From 973d06d96f93e9af22182465e750d055907d0cc8 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 5 Dec 2023 17:17:12 +0000 Subject: [PATCH 001/675] chore(release): Bumped to Version 14.53.0 # [14.53.0](https://github.com/frappe/erpnext/compare/v14.52.1...v14.53.0) (2023-12-05) ### Bug Fixes * `OperationalError` while selecting Serial No in `Warranty Claim` (backport [#38394](https://github.com/frappe/erpnext/issues/38394)) ([#38413](https://github.com/frappe/erpnext/issues/38413)) ([49296cd](https://github.com/frappe/erpnext/commit/49296cd5cb97836156c29d644acd147c36c30c50)) * better overlap logic for job card (backport [#38432](https://github.com/frappe/erpnext/issues/38432)) ([#38521](https://github.com/frappe/erpnext/issues/38521)) ([6db63c9](https://github.com/frappe/erpnext/commit/6db63c971eb88ad68b916c1947e81806c0440638)) * consider the `Valuation Method` while picking incorrect SLE (backport [#38592](https://github.com/frappe/erpnext/issues/38592)) ([#38594](https://github.com/frappe/erpnext/issues/38594)) ([01b2a1f](https://github.com/frappe/erpnext/commit/01b2a1ffebe31f63741e98d4e4c07fc09716546e)) * debit credit mismatch in multi-currecy asset purchase receipt ([#38342](https://github.com/frappe/erpnext/issues/38342)) ([fd6a23c](https://github.com/frappe/erpnext/commit/fd6a23cf41997e1ada5adb7d28dcc091979aabcf)) * don't consider cancelled entries ([#38401](https://github.com/frappe/erpnext/issues/38401)) ([9345334](https://github.com/frappe/erpnext/commit/9345334196484f3d89c1be11bcb0598cf38cfab7)) * don't show non-stock items in Stock Analytics report (backport [#38543](https://github.com/frappe/erpnext/issues/38543)) ([#38544](https://github.com/frappe/erpnext/issues/38544)) ([2e333e6](https://github.com/frappe/erpnext/commit/2e333e6802ad880700920e5df70b87495a67a657)) * don't update previous doc on rate change (backport [#38493](https://github.com/frappe/erpnext/issues/38493)) ([#38523](https://github.com/frappe/erpnext/issues/38523)) ([b88c7d6](https://github.com/frappe/erpnext/commit/b88c7d63deebb331f49512191565f359221b75e7)) * exclude `invoice_doctypes` from party advance ([1042d4c](https://github.com/frappe/erpnext/commit/1042d4cfeb158a224ea722816fec346a940f2c98)) * exploded items in Subcontracting Receipt (backport [#38441](https://github.com/frappe/erpnext/issues/38441)) ([#38444](https://github.com/frappe/erpnext/issues/38444)) ([2908d96](https://github.com/frappe/erpnext/commit/2908d966b620c4462b96d79e46f08e6bed3ff1df)) * get dynamic link with parenttype contact ([12d3cda](https://github.com/frappe/erpnext/commit/12d3cda2d9bf2d8b5fd1c8b74db3011ecace487a)) * incorrect customer outstanding amount ([#38475](https://github.com/frappe/erpnext/issues/38475)) ([c07723d](https://github.com/frappe/erpnext/commit/c07723d49d065a29e9b6985c746c82cce1815d6d)) * incorrect material request quantity in Production Plan (backport [#38566](https://github.com/frappe/erpnext/issues/38566)) ([#38578](https://github.com/frappe/erpnext/issues/38578)) ([70965ef](https://github.com/frappe/erpnext/commit/70965efd941389b03280a39a41448800e0747a02)) * incorrect ordered qty for Subcontracting Order ([#38415](https://github.com/frappe/erpnext/issues/38415)) ([8e37087](https://github.com/frappe/erpnext/commit/8e370876cc14ec7ec5099b1fad003049680ed8b0)) * incorrect requested quantity for the subcontracting order (backport [#38455](https://github.com/frappe/erpnext/issues/38455)) ([#38471](https://github.com/frappe/erpnext/issues/38471)) ([497049b](https://github.com/frappe/erpnext/commit/497049b3fd7139c59b0f117ddaea9fbd2df98e18)) * item group filter in sales person wise report ([eee9062](https://github.com/frappe/erpnext/commit/eee906281480fbed66e3ad823bd4e0386ed07bd3)) * make create button translatable ([#38165](https://github.com/frappe/erpnext/issues/38165)) ([c71b31e](https://github.com/frappe/erpnext/commit/c71b31e3b8d577d99bfd904faa02c9b6022f6c6a)) * no fstring in translation ([#38381](https://github.com/frappe/erpnext/issues/38381)) ([cec2574](https://github.com/frappe/erpnext/commit/cec25740eae25dec8519af756636a521faf3e6b5)) * **pe:** show split alert only on splitting ([8d6d74c](https://github.com/frappe/erpnext/commit/8d6d74c23700dc22e27e2cfce4af0227a4ab3be3)) * **regional:** use net figures for sales calc ([#38260](https://github.com/frappe/erpnext/issues/38260)) ([d337533](https://github.com/frappe/erpnext/commit/d337533907a17ada35bfe0da070e0ee6ae13a24d)) * remove hardcoded, implicit rounding loss allowance ([419943a](https://github.com/frappe/erpnext/commit/419943a9d9ea748088702935844ed616f616400a)) * set cwip account before asset tests ([ecde1d5](https://github.com/frappe/erpnext/commit/ecde1d58b5f3dbc0bbbf2fc96a867b7e25ae5776)) * show item name as title instead of item group in BOM (backport [#38478](https://github.com/frappe/erpnext/issues/38478)) ([#38480](https://github.com/frappe/erpnext/issues/38480)) ([0df52d2](https://github.com/frappe/erpnext/commit/0df52d2c4f900e2110798347eac306c16536f8e7)) * SO ordered qty on PO item removal ([#38378](https://github.com/frappe/erpnext/issues/38378)) ([b7fe163](https://github.com/frappe/erpnext/commit/b7fe163de5ece72a91862695537274c6aeb7eb06)) * sql error while filtering on finance book in GL ([25ec1fc](https://github.com/frappe/erpnext/commit/25ec1fce6105249defd835601e0132735cdd189b)) * unset discount amount based on coupon code ([7fa97fa](https://github.com/frappe/erpnext/commit/7fa97fa7b58a83266c669c6fd7d170f66f162154)) * use `docstatus` instead of `status` (backport [#38439](https://github.com/frappe/erpnext/issues/38439)) ([#38442](https://github.com/frappe/erpnext/issues/38442)) ([2dc4b02](https://github.com/frappe/erpnext/commit/2dc4b02fc853ec1cf40ab1da1126811e0f9e2862)) * use predefined onload property `load_after_mapping` (backport [#38209](https://github.com/frappe/erpnext/issues/38209)) ([#38518](https://github.com/frappe/erpnext/issues/38518)) ([9f23b7e](https://github.com/frappe/erpnext/commit/9f23b7e46f52390317b13b29ca5ddbe9842effe1)) * **ux:** make valuation field read only when it can't be modified (backport [#38450](https://github.com/frappe/erpnext/issues/38450)) ([#38463](https://github.com/frappe/erpnext/issues/38463)) ([bec7fb5](https://github.com/frappe/erpnext/commit/bec7fb54936867f438bea60f42637089eedf9f8b)) * validate finance_books table length in asset ([#38584](https://github.com/frappe/erpnext/issues/38584)) ([14b908f](https://github.com/frappe/erpnext/commit/14b908f8ab243c94cf1bd0ecde52055dcdbc5e7b)) ### Features * `Company` filter in `Stock Ledger Variance` report (backport [#38553](https://github.com/frappe/erpnext/issues/38553)) ([#38573](https://github.com/frappe/erpnext/issues/38573)) ([6c89f35](https://github.com/frappe/erpnext/commit/6c89f351f46c7d1ba1d0d5129b0f59e093d91a59)) * shift depreciation for assets [v14] [backport of [#38327](https://github.com/frappe/erpnext/issues/38327)] ([#38404](https://github.com/frappe/erpnext/issues/38404)) ([d8c3935](https://github.com/frappe/erpnext/commit/d8c3935e64f240b938f90558554ef57d6b9f46f0)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index ca5062d835e..8406ce635b9 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.52.1" +__version__ = "14.53.0" def get_default_company(user=None): From 214861d68f81158d1c4dcac493dfbd2243c3e29e Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 12 Dec 2023 16:12:27 +0000 Subject: [PATCH 002/675] chore(release): Bumped to Version 14.54.0 # [14.54.0](https://github.com/frappe/erpnext/compare/v14.53.0...v14.54.0) (2023-12-12) ### Bug Fixes * 1st row depr. sch. value of asset put to less than 180 days acc. to I.T. S. 32 ([#38697](https://github.com/frappe/erpnext/issues/38697)) ([753a1b5](https://github.com/frappe/erpnext/commit/753a1b511d2696d241625f285a7db59e29092bd3)) * calc monthly_repayment_amount correctly in regenerate_repayment_schedule ([#38636](https://github.com/frappe/erpnext/issues/38636)) ([008400d](https://github.com/frappe/erpnext/commit/008400d287c26f78909abb1b09bb52a5ea94c1b3)) * format only if searched text contain link value text ([b28deaa](https://github.com/frappe/erpnext/commit/b28deaae2c0e3784c7cbe585ded11d5354d65c9c)) * limit end date to current date ([cbe15e1](https://github.com/frappe/erpnext/commit/cbe15e159a3f97f2e057df74370dbd35b0c661b1)) * typo in unittest ([#38673](https://github.com/frappe/erpnext/issues/38673)) ([c87a3c7](https://github.com/frappe/erpnext/commit/c87a3c7793d577a6544b65148f59899a14e19b8b)) * **ux:** don't update qty blindly (backport [#38608](https://github.com/frappe/erpnext/issues/38608)) ([#38638](https://github.com/frappe/erpnext/issues/38638)) ([29cd474](https://github.com/frappe/erpnext/commit/29cd474f44844ace9409cf4f304ef950a84f5616)) ### Features * add employee number to client user bootinfo (backport [#38477](https://github.com/frappe/erpnext/issues/38477)) ([#38602](https://github.com/frappe/erpnext/issues/38602)) ([c2a137f](https://github.com/frappe/erpnext/commit/c2a137f0fb48e9322436ada3e3cce849a236d231)) ### Reverts * Revert "refactor: bank transaction (backport #38182)" (#38653) ([7813efa](https://github.com/frappe/erpnext/commit/7813efad356a56de595f887896562818f43cae23)), closes [#38182](https://github.com/frappe/erpnext/issues/38182) [#38653](https://github.com/frappe/erpnext/issues/38653) [#38182](https://github.com/frappe/erpnext/issues/38182) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 8406ce635b9..0cdb9d54802 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.53.0" +__version__ = "14.54.0" def get_default_company(user=None): From 4908d03a885eb5cfb5e2f82d770938dcac42dfcb Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 14 Dec 2023 00:30:36 +0530 Subject: [PATCH 003/675] Revert "fix(ux): don't update qty blindly" (backport #38728) (backport #38729) (#38733) Revert "fix(ux): don't update qty blindly" (backport #38728) (#38729) Revert "fix(ux): don't update qty blindly" (#38728) (cherry picked from commit 6851c5042fc5e2d3f45d070af18a33c24fc10794) Co-authored-by: Ankush Menat (cherry picked from commit 0d49dd364858cba27623f2ea92c6b07e704b4a9a) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- erpnext/public/js/controllers/transaction.js | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index ff00a3ade16..050b9dcd3db 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -441,6 +441,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe item.pricing_rules = '' return this.frm.call({ method: "erpnext.stock.get_item_details.get_item_details", + child: item, args: { doc: me.frm.doc, args: { @@ -489,19 +490,6 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe callback: function(r) { if(!r.exc) { frappe.run_serially([ - () => { - var child = locals[cdt][cdn]; - var std_field_list = ["doctype"] - .concat(frappe.model.std_fields_list) - .concat(frappe.model.child_table_field_list); - - for (var key in r.message) { - if (std_field_list.indexOf(key) === -1) { - if (key === "qty" && child[key]) continue; - child[key] = r.message[key]; - } - } - }, () => { var d = locals[cdt][cdn]; me.add_taxes_from_item_tax_template(d.item_tax_rate); From d53a9f6530aef2d6096aa5a070d5165dfc4ef834 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 14 Dec 2023 00:38:47 +0530 Subject: [PATCH 004/675] fix: supplier removed on selection of item (backport #38712) (backport #38732) (#38735) fix: supplier removed on selection of item (backport #38712) (#38732) fix: supplier removed on selection of item (#38712) (cherry picked from commit db24e2488247eef326ef52a88dbae8e828893e7e) Co-authored-by: rohitwaghchaure (cherry picked from commit dda95ea8922c0f6073d36edf3c0a525fbfc7295e) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- erpnext/stock/get_item_details.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index daa7becc1f4..e47971d03e8 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -391,7 +391,6 @@ def get_basic_details(args, item, overwrite_warehouse=True): "net_amount": 0.0, "discount_percentage": 0.0, "discount_amount": flt(args.discount_amount) or 0.0, - "supplier": get_default_supplier(args, item_defaults, item_group_defaults, brand_defaults), "update_stock": args.get("update_stock") if args.get("doctype") in ["Sales Invoice", "Purchase Invoice"] else 0, @@ -411,6 +410,10 @@ def get_basic_details(args, item, overwrite_warehouse=True): } ) + default_supplier = get_default_supplier(args, item_defaults, item_group_defaults, brand_defaults) + if default_supplier: + out.supplier = default_supplier + if item.get("enable_deferred_revenue") or item.get("enable_deferred_expense"): out.update(calculate_service_end_date(args, item)) From b7fd24074dad093a96123398b249c1f73e93019a Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 13 Dec 2023 19:09:56 +0000 Subject: [PATCH 005/675] chore(release): Bumped to Version 14.54.1 ## [14.54.1](https://github.com/frappe/erpnext/compare/v14.54.0...v14.54.1) (2023-12-13) ### Bug Fixes * supplier removed on selection of item (backport [#38712](https://github.com/frappe/erpnext/issues/38712)) (backport [#38732](https://github.com/frappe/erpnext/issues/38732)) ([#38735](https://github.com/frappe/erpnext/issues/38735)) ([d53a9f6](https://github.com/frappe/erpnext/commit/d53a9f6530aef2d6096aa5a070d5165dfc4ef834)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 0cdb9d54802..380cd4477b0 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.54.0" +__version__ = "14.54.1" def get_default_company(user=None): From cdef0bd503cd6683283c2776f24daa070320297b Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 13 Dec 2023 11:25:21 +0530 Subject: [PATCH 006/675] fix: fetch exc rate of multi currency journals (cherry picked from commit 1b3ba25220ca4ef96df5416331ee2f78376da64a) --- .../payment_reconciliation.py | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index a907dc7acf5..6ce79ee3ea6 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -539,6 +539,27 @@ class PaymentReconciliation(Document): invoice_exchange_map.update(purchase_invoice_map) + journals = [ + d.get("invoice_number") for d in invoices if d.get("invoice_type") == "Journal Entry" + ] + journals.extend( + [d.get("reference_name") for d in payments if d.get("reference_type") == "Journal Entry"] + ) + if journals: + journals = list(set(journals)) + journals_map = frappe._dict( + frappe.db.get_all( + "Journal Entry Account", + filters={"parent": ("in", journals), "account": ("in", [self.receivable_payable_account])}, + fields=[ + "parent as `name`", + "exchange_rate", + ], + as_list=1, + ) + ) + invoice_exchange_map.update(journals_map) + return invoice_exchange_map def validate_allocation(self): From 20a26c3dd44a2a28af16ccd486594982498a9c3a Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 14 Dec 2023 06:20:30 +0000 Subject: [PATCH 007/675] chore(release): Bumped to Version 14.54.2 ## [14.54.2](https://github.com/frappe/erpnext/compare/v14.54.1...v14.54.2) (2023-12-14) ### Bug Fixes * fetch exc rate of multi currency journals ([cdef0bd](https://github.com/frappe/erpnext/commit/cdef0bd503cd6683283c2776f24daa070320297b)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 380cd4477b0..585f8126daf 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.54.1" +__version__ = "14.54.2" def get_default_company(user=None): From 98198d35eaa8909396a6ced5ad274931b7f90dcf Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Fri, 15 Dec 2023 12:30:15 +0530 Subject: [PATCH 008/675] fix: skip jvs against bank accounts (cherry picked from commit f7b2380ec1cbe5e58755f88ca08cb052b92e05c7) --- .../tds_payable_monthly/tds_payable_monthly.py | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py index f6c7bd3db70..e4953bb1815 100644 --- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py +++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py @@ -345,21 +345,16 @@ def get_tds_docs_query(filters, bank_accounts, tds_accounts): if filters.get("party"): party = [filters.get("party")] - query = query.where( - ((gle.account.isin(tds_accounts) & gle.against.isin(party))) - | ((gle.voucher_type == "Journal Entry") & (gle.party == filters.get("party"))) - | gle.party.isin(party) + jv_condition = gle.against.isin(party) | ( + (gle.voucher_type == "Journal Entry") & (gle.party == filters.get("party")) ) else: party = frappe.get_all(filters.get("party_type"), pluck="name") - query = query.where( - ((gle.account.isin(tds_accounts) & gle.against.isin(party))) - | ( - (gle.voucher_type == "Journal Entry") - & ((gle.party_type == filters.get("party_type")) | (gle.party_type == "")) - ) - | gle.party.isin(party) + jv_condition = gle.against.isin(party) | ( + (gle.voucher_type == "Journal Entry") + & ((gle.party_type == filters.get("party_type")) | (gle.party_type == "")) ) + query = query.where((gle.account.isin(tds_accounts) & jv_condition) | gle.party.isin(party)) return query From 3c717db3fd2d88f3fbad053c0b71b9a99b62c9b6 Mon Sep 17 00:00:00 2001 From: Richard Case Date: Mon, 30 Oct 2023 00:15:05 +0000 Subject: [PATCH 009/675] feat: in_party_currency option for AR/AP reports --- .../accounts_payable/accounts_payable.js | 7 +++++- .../accounts_receivable.js | 8 +++++-- .../accounts_receivable.py | 22 +++++++++++-------- 3 files changed, 25 insertions(+), 12 deletions(-) mode change 100755 => 100644 erpnext/accounts/report/accounts_receivable/accounts_receivable.py diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.js b/erpnext/accounts/report/accounts_payable/accounts_payable.js index 0979cffbf3c..dc2e8a8518b 100644 --- a/erpnext/accounts/report/accounts_payable/accounts_payable.js +++ b/erpnext/accounts/report/accounts_payable/accounts_payable.js @@ -149,11 +149,16 @@ frappe.query_reports["Accounts Payable"] = { "label": __("Revaluation Journals"), "fieldtype": "Check", }, + { + "fieldname": "in_party_currency", + "label": __("In Party Currency"), + "fieldtype": "Check", + }, { "fieldname": "ignore_accounts", "label": __("Group by Voucher"), "fieldtype": "Check", - } + }, ], diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js index d6e3098e171..aa492bf51d9 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js @@ -181,13 +181,17 @@ frappe.query_reports["Accounts Receivable"] = { "label": __("Revaluation Journals"), "fieldtype": "Check", }, + { + "fieldname": "in_party_currency", + "label": __("In Party Currency"), + "fieldtype": "Check", + }, { "fieldname": "ignore_accounts", - "label": __("Group by Voucher"), + "label": __("Ignore by Voucher"), "fieldtype": "Check", } - ], "formatter": function(value, row, column, data, default_formatter) { diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py old mode 100755 new mode 100644 index f1abc1d4ddb..4d082dc3354 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -28,8 +28,8 @@ from erpnext.accounts.utils import get_currency_precision # 6. Configurable Ageing Groups (0-30, 30-60 etc) can be set via filters # 7. For overpayment against an invoice with payment terms, there will be an additional row # 8. Invoice details like Sales Persons, Delivery Notes are also fetched comma separated -# 9. Report amounts are in "Party Currency" if party is selected, or company currency for multi-party -# 10. This reports is based on all GL Entries that are made against account_type "Receivable" or "Payable" +# 9. Report amounts are in party currency if in_party_currency is selected, otherwise company currency +# 10. This report is based on Payment Ledger Entries def execute(filters=None): @@ -84,6 +84,9 @@ class ReceivablePayableReport(object): self.total_row_map = {} self.skip_total_row = 1 + if self.filters.get("in_party_currency"): + self.skip_total_row = 1 + def get_data(self): self.get_ple_entries() self.get_sales_invoices_or_customers_based_on_sales_person() @@ -145,7 +148,7 @@ class ReceivablePayableReport(object): if self.filters.get("group_by_party"): self.init_subtotal_row(ple.party) - if self.filters.get("group_by_party"): + if self.filters.get("group_by_party") and not self.filters.get("in_party_currency"): self.init_subtotal_row("Total") def get_invoices(self, ple): @@ -224,8 +227,7 @@ class ReceivablePayableReport(object): if not row: return - # amount in "Party Currency", if its supplied. If not, amount in company currency - if self.filters.get("party_type") and self.filters.get("party"): + if self.filters.get("in_party_currency"): amount = ple.amount_in_account_currency else: amount = ple.amount @@ -256,8 +258,10 @@ class ReceivablePayableReport(object): def update_sub_total_row(self, row, party): total_row = self.total_row_map.get(party) - for field in self.get_currency_fields(): - total_row[field] += row.get(field, 0.0) + if total_row: + for field in self.get_currency_fields(): + total_row[field] += row.get(field, 0.0) + total_row["currency"] = row.get("currency", "") def append_subtotal_row(self, party): sub_total_row = self.total_row_map.get(party) @@ -318,7 +322,7 @@ class ReceivablePayableReport(object): if self.filters.get("group_by_party"): self.append_subtotal_row(self.previous_party) if self.data: - self.data.append(self.total_row_map.get("Total")) + self.data.append(self.total_row_map.get("Total", {})) def append_row(self, row): self.allocate_future_payments(row) @@ -449,7 +453,7 @@ class ReceivablePayableReport(object): party_details = self.get_party_details(row.party) or {} row.update(party_details) - if self.filters.get("party_type") and self.filters.get("party"): + if self.filters.get("in_party_currency"): row.currency = row.account_currency else: row.currency = self.company_currency From 08940226950a65cc29161b866ab7920b7e1f6304 Mon Sep 17 00:00:00 2001 From: Richard Case Date: Mon, 30 Oct 2023 00:45:02 +0000 Subject: [PATCH 010/675] chore: update tests --- .../accounts/report/accounts_payable/test_accounts_payable.py | 1 + .../report/accounts_receivable/test_accounts_receivable.py | 1 + 2 files changed, 2 insertions(+) diff --git a/erpnext/accounts/report/accounts_payable/test_accounts_payable.py b/erpnext/accounts/report/accounts_payable/test_accounts_payable.py index 9f03d92cd50..b4cb25ff1b8 100644 --- a/erpnext/accounts/report/accounts_payable/test_accounts_payable.py +++ b/erpnext/accounts/report/accounts_payable/test_accounts_payable.py @@ -40,6 +40,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): "range2": 60, "range3": 90, "range4": 120, + "in_party_currency": 1, } data = execute(filters) diff --git a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py index f83285a1a72..dd0842df041 100644 --- a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py @@ -581,6 +581,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): "range2": 60, "range3": 90, "range4": 120, + "in_party_currency": 1, } si = self.create_sales_invoice(no_payment_schedule=True, do_not_submit=True) From 7eee573fcb37bd0da4cc0b7457e552cb23e8d47f Mon Sep 17 00:00:00 2001 From: Devin Slauenwhite Date: Sat, 16 Dec 2023 16:29:01 +0000 Subject: [PATCH 011/675] fixup! feat: in_party_currency option for AR/AP reports fix: merge conflict typo --- .../accounts/report/accounts_receivable/accounts_receivable.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js index aa492bf51d9..28aa7bf9ecf 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js @@ -188,7 +188,7 @@ frappe.query_reports["Accounts Receivable"] = { }, { "fieldname": "ignore_accounts", - "label": __("Ignore by Voucher"), + "label": __("Group by Voucher"), "fieldtype": "Check", } From 39aa36e44b66123443c66e3503c1d6bb8c277447 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Sat, 16 Dec 2023 21:11:54 +0100 Subject: [PATCH 012/675] fix: groups for current accounts in German CoAs (cherry picked from commit 259f313af75c3cbdcc1af75a8e7d2ad1a3a90c0c) --- .../verified/de_kontenplan_SKR03_gnucash.json | 14 +++++++++++-- ..._kontenplan_SKR04_with_account_number.json | 20 +++++++------------ 2 files changed, 19 insertions(+), 15 deletions(-) diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR03_gnucash.json b/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR03_gnucash.json index 741d4283e2f..daf2e21d78c 100644 --- a/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR03_gnucash.json +++ b/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR03_gnucash.json @@ -53,8 +53,13 @@ }, "II. Forderungen und sonstige Vermögensgegenstände": { "is_group": 1, - "Ford. a. Lieferungen und Leistungen": { + "Forderungen aus Lieferungen und Leistungen mit Kontokorrent": { "account_number": "1400", + "account_type": "Receivable", + "is_group": 1 + }, + "Forderungen aus Lieferungen und Leistungen ohne Kontokorrent": { + "account_number": "1410", "account_type": "Receivable" }, "Durchlaufende Posten": { @@ -180,8 +185,13 @@ }, "IV. Verbindlichkeiten aus Lieferungen und Leistungen": { "is_group": 1, - "Verbindlichkeiten aus Lieferungen u. Leistungen": { + "Verbindlichkeiten aus Lieferungen und Leistungen mit Kontokorrent": { "account_number": "1600", + "account_type": "Payable", + "is_group": 1 + }, + "Verbindlichkeiten aus Lieferungen und Leistungen ohne Kontokorrent": { + "account_number": "1610", "account_type": "Payable" } }, diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR04_with_account_number.json b/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR04_with_account_number.json index 2bf55cfcd04..000ef80ee38 100644 --- a/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR04_with_account_number.json +++ b/erpnext/accounts/doctype/account/chart_of_accounts/verified/de_kontenplan_SKR04_with_account_number.json @@ -407,13 +407,10 @@ "Bewertungskorrektur zu Forderungen aus Lieferungen und Leistungen": { "account_number": "9960" }, - "Debitoren": { - "is_group": 1, - "account_number": "10000" - }, - "Forderungen aus Lieferungen und Leistungen": { + "Forderungen aus Lieferungen und Leistungen mit Kontokorrent": { "account_number": "1200", - "account_type": "Receivable" + "account_type": "Receivable", + "is_group": 1 }, "Forderungen aus Lieferungen und Leistungen ohne Kontokorrent": { "account_number": "1210" @@ -1138,18 +1135,15 @@ "Bewertungskorrektur zu Verb. aus Lieferungen und Leistungen": { "account_number": "9964" }, - "Kreditoren": { - "account_number": "70000", + "Verb. aus Lieferungen und Leistungen mit Kontokorrent": { + "account_number": "3300", + "account_type": "Payable", "is_group": 1, - "Wareneingangs-­Verrechnungskonto" : { + "Wareneingangs-Verrechnungskonto" : { "account_number": "70001", "account_type": "Stock Received But Not Billed" } }, - "Verb. aus Lieferungen und Leistungen": { - "account_number": "3300", - "account_type": "Payable" - }, "Verb. aus Lieferungen und Leistungen ohne Kontokorrent": { "account_number": "3310" }, From 226d0e0196f8c5defddc600348364d39fa703f27 Mon Sep 17 00:00:00 2001 From: Devin Slauenwhite Date: Thu, 23 Nov 2023 05:46:43 +0000 Subject: [PATCH 013/675] fix: display all item rate stop messages --- erpnext/utilities/transaction_base.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/utilities/transaction_base.py b/erpnext/utilities/transaction_base.py index 7eba35dedd9..b083614a5f7 100644 --- a/erpnext/utilities/transaction_base.py +++ b/erpnext/utilities/transaction_base.py @@ -98,6 +98,7 @@ class TransactionBase(StatusUpdater): "Selling Settings", "None", ["maintain_same_rate_action", "role_to_override_stop_action"] ) + stop_actions = [] for ref_dt, ref_dn_field, ref_link_field in ref_details: reference_names = [d.get(ref_link_field) for d in self.get("items") if d.get(ref_link_field)] reference_details = self.get_reference_details(reference_names, ref_dt + " Item") @@ -108,7 +109,7 @@ class TransactionBase(StatusUpdater): if abs(flt(d.rate - ref_rate, d.precision("rate"))) >= 0.01: if action == "Stop": if role_allowed_to_override not in frappe.get_roles(): - frappe.throw( + stop_actions.append( _("Row #{0}: Rate must be same as {1}: {2} ({3} / {4})").format( d.idx, ref_dt, d.get(ref_dn_field), d.rate, ref_rate ) @@ -121,6 +122,8 @@ class TransactionBase(StatusUpdater): title=_("Warning"), indicator="orange", ) + if stop_actions: + frappe.throw(stop_actions, as_list=True) def get_reference_details(self, reference_names, reference_doctype): return frappe._dict( From 37a18299d00cade770e79c2702279b0084a58e0a Mon Sep 17 00:00:00 2001 From: Gursheen Kaur Anand <40693548+GursheenK@users.noreply.github.com> Date: Tue, 19 Dec 2023 15:35:35 +0530 Subject: [PATCH 014/675] Revert "fix: unset discount amount based on coupon code (backport #38250)" --- erpnext/public/js/utils/sales_common.js | 431 ------------------------ 1 file changed, 431 deletions(-) delete mode 100644 erpnext/public/js/utils/sales_common.js diff --git a/erpnext/public/js/utils/sales_common.js b/erpnext/public/js/utils/sales_common.js deleted file mode 100644 index 5514963c966..00000000000 --- a/erpnext/public/js/utils/sales_common.js +++ /dev/null @@ -1,431 +0,0 @@ -// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -// License: GNU General Public License v3. See license.txt - -frappe.provide("erpnext.selling"); - -erpnext.sales_common = { - setup_selling_controller:function() { - erpnext.selling.SellingController = class SellingController extends erpnext.TransactionController { - setup() { - super.setup(); - this.toggle_enable_for_stock_uom("allow_to_edit_stock_uom_qty_for_sales"); - this.frm.email_field = "contact_email"; - } - - onload() { - super.onload(); - this.setup_queries(); - this.frm.set_query('shipping_rule', function() { - return { - filters: { - "shipping_rule_type": "Selling" - } - }; - }); - } - - setup_queries() { - var me = this; - - $.each([["customer", "customer"], - ["lead", "lead"]], - function(i, opts) { - if(me.frm.fields_dict[opts[0]]) - me.frm.set_query(opts[0], erpnext.queries[opts[1]]); - }); - - me.frm.set_query('contact_person', erpnext.queries.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); - - erpnext.accounts.dimensions.setup_dimension_filters(me.frm, me.frm.doctype); - - if(this.frm.fields_dict.selling_price_list) { - this.frm.set_query("selling_price_list", function() { - return { filters: { selling: 1 } }; - }); - } - - if(this.frm.fields_dict.tc_name) { - this.frm.set_query("tc_name", function() { - return { filters: { selling: 1 } }; - }); - } - - if(!this.frm.fields_dict["items"]) { - return; - } - - if(this.frm.fields_dict["items"].grid.get_field('item_code')) { - this.frm.set_query("item_code", "items", function() { - return { - query: "erpnext.controllers.queries.item_query", - filters: {'is_sales_item': 1, 'customer': me.frm.doc.customer, 'has_variants': 0} - } - }); - } - - if(this.frm.fields_dict["packed_items"] && - this.frm.fields_dict["packed_items"].grid.get_field('batch_no')) { - this.frm.set_query("batch_no", "packed_items", function(doc, cdt, cdn) { - return me.set_query_for_batch(doc, cdt, cdn) - }); - } - - if(this.frm.fields_dict["items"].grid.get_field('item_code')) { - this.frm.set_query("item_tax_template", "items", function(doc, cdt, cdn) { - return me.set_query_for_item_tax_template(doc, cdt, cdn) - }); - } - - } - - refresh() { - super.refresh(); - - frappe.dynamic_link = {doc: this.frm.doc, fieldname: 'customer', doctype: 'Customer'} - - this.frm.toggle_display("customer_name", - (this.frm.doc.customer_name && this.frm.doc.customer_name!==this.frm.doc.customer)); - - this.toggle_editable_price_list_rate(); - } - - customer() { - var me = this; - erpnext.utils.get_party_details(this.frm, null, null, function() { - me.apply_price_list(); - }); - } - - customer_address() { - erpnext.utils.get_address_display(this.frm, "customer_address"); - erpnext.utils.set_taxes_from_address(this.frm, "customer_address", "customer_address", "shipping_address_name"); - } - - shipping_address_name() { - erpnext.utils.get_address_display(this.frm, "shipping_address_name", "shipping_address"); - erpnext.utils.set_taxes_from_address(this.frm, "shipping_address_name", "customer_address", "shipping_address_name"); - } - - dispatch_address_name() { - erpnext.utils.get_address_display(this.frm, "dispatch_address_name", "dispatch_address"); - } - - sales_partner() { - this.apply_pricing_rule(); - } - - campaign() { - this.apply_pricing_rule(); - } - - selling_price_list() { - this.apply_price_list(); - this.set_dynamic_labels(); - } - - discount_percentage(doc, cdt, cdn) { - var item = frappe.get_doc(cdt, cdn); - item.discount_amount = 0.0; - this.apply_discount_on_item(doc, cdt, cdn, 'discount_percentage'); - } - - discount_amount(doc, cdt, cdn) { - - if(doc.name === cdn) { - return; - } - - var item = frappe.get_doc(cdt, cdn); - item.discount_percentage = 0.0; - this.apply_discount_on_item(doc, cdt, cdn, 'discount_amount'); - } - - commission_rate() { - this.calculate_commission(); - } - - total_commission() { - frappe.model.round_floats_in(this.frm.doc, ["amount_eligible_for_commission", "total_commission"]); - - const { amount_eligible_for_commission } = this.frm.doc; - if(!amount_eligible_for_commission) return; - - this.frm.set_value( - "commission_rate", flt( - this.frm.doc.total_commission * 100.0 / amount_eligible_for_commission - ) - ); - } - - allocated_percentage(doc, cdt, cdn) { - var sales_person = frappe.get_doc(cdt, cdn); - if(sales_person.allocated_percentage) { - - sales_person.allocated_percentage = flt(sales_person.allocated_percentage, - precision("allocated_percentage", sales_person)); - - sales_person.allocated_amount = flt(this.frm.doc.amount_eligible_for_commission * - sales_person.allocated_percentage / 100.0, - precision("allocated_amount", sales_person)); - refresh_field(["allocated_amount"], sales_person); - - this.calculate_incentive(sales_person); - refresh_field(["allocated_percentage", "allocated_amount", "commission_rate","incentives"], sales_person.name, - sales_person.parentfield); - } - } - - sales_person(doc, cdt, cdn) { - var row = frappe.get_doc(cdt, cdn); - this.calculate_incentive(row); - refresh_field("incentives",row.name,row.parentfield); - } - - toggle_editable_price_list_rate() { - var df = frappe.meta.get_docfield(this.frm.doc.doctype + " Item", "price_list_rate", this.frm.doc.name); - var editable_price_list_rate = cint(frappe.defaults.get_default("editable_price_list_rate")); - - if(df && editable_price_list_rate) { - const parent_field = frappe.meta.get_parentfield(this.frm.doc.doctype, this.frm.doc.doctype + " Item"); - if (!this.frm.fields_dict[parent_field]) return; - - this.frm.fields_dict[parent_field].grid.update_docfield_property( - 'price_list_rate', 'read_only', 0 - ); - } - } - - calculate_commission() { - if(!this.frm.fields_dict.commission_rate || this.frm.doc.docstatus === 1) return; - - if(this.frm.doc.commission_rate > 100) { - this.frm.set_value("commission_rate", 100); - frappe.throw(`${__(frappe.meta.get_label( - this.frm.doc.doctype, "commission_rate", this.frm.doc.name - ))} ${__("cannot be greater than 100")}`); - } - - this.frm.doc.amount_eligible_for_commission = this.frm.doc.items.reduce( - (sum, item) => item.grant_commission ? sum + item.base_net_amount : sum, 0 - ) - - this.frm.doc.total_commission = flt( - this.frm.doc.amount_eligible_for_commission * this.frm.doc.commission_rate / 100.0, - precision("total_commission") - ); - - refresh_field(["amount_eligible_for_commission", "total_commission"]); - } - - calculate_contribution() { - var me = this; - $.each(this.frm.doc.doctype.sales_team || [], function(i, sales_person) { - frappe.model.round_floats_in(sales_person); - if (!sales_person.allocated_percentage) return; - - sales_person.allocated_amount = flt( - me.frm.doc.amount_eligible_for_commission - * sales_person.allocated_percentage - / 100.0, - precision("allocated_amount", sales_person) - ); - }); - } - - calculate_incentive(row) { - if(row.allocated_amount) - { - row.incentives = flt( - row.allocated_amount * row.commission_rate / 100.0, - precision("incentives", row)); - } - } - - set_dynamic_labels() { - super.set_dynamic_labels(); - this.set_product_bundle_help(this.frm.doc); - } - - set_product_bundle_help(doc) { - if(!this.frm.fields_dict.packing_list) return; - if ((doc.packed_items || []).length) { - $(this.frm.fields_dict.packing_list.row.wrapper).toggle(true); - - if (in_list(['Delivery Note', 'Sales Invoice'], doc.doctype)) { - var help_msg = "
" + - __("For 'Product Bundle' items, Warehouse, Serial No and Batch No will be considered from the 'Packing List' table. If Warehouse and Batch No are same for all packing items for any 'Product Bundle' item, those values can be entered in the main Item table, values will be copied to 'Packing List' table.")+ - "
"; - frappe.meta.get_docfield(doc.doctype, 'product_bundle_help', doc.name).options = help_msg; - } - } else { - $(this.frm.fields_dict.packing_list.row.wrapper).toggle(false); - if (in_list(['Delivery Note', 'Sales Invoice'], doc.doctype)) { - frappe.meta.get_docfield(doc.doctype, 'product_bundle_help', doc.name).options = ''; - } - } - refresh_field('product_bundle_help'); - } - - company_address() { - var me = this; - if(this.frm.doc.company_address) { - frappe.call({ - method: "frappe.contacts.doctype.address.address.get_address_display", - args: {"address_dict": this.frm.doc.company_address }, - callback: function(r) { - if(r.message) { - me.frm.set_value("company_address_display", r.message) - } - } - }) - } else { - this.frm.set_value("company_address_display", ""); - } - } - - conversion_factor(doc, cdt, cdn, dont_fetch_price_list_rate) { - super.conversion_factor(doc, cdt, cdn, dont_fetch_price_list_rate); - } - - qty(doc, cdt, cdn) { - super.qty(doc, cdt, cdn); - } - - pick_serial_and_batch(doc, cdt, cdn) { - let item = locals[cdt][cdn]; - let me = this; - let path = "assets/erpnext/js/utils/serial_no_batch_selector.js"; - - frappe.db.get_value("Item", item.item_code, ["has_batch_no", "has_serial_no"]) - .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; - item.type_of_transaction = item.qty > 0 ? "Outward":"Inward"; - - item.title = item.has_serial_no ? - __("Select Serial No") : __("Select Batch No"); - - if (item.has_serial_no && item.has_batch_no) { - item.title = __("Select Serial and Batch"); - } - - frappe.require(path, function() { - new erpnext.SerialBatchPackageSelector( - me.frm, item, (r) => { - if (r) { - frappe.model.set_value(item.doctype, item.name, { - "serial_and_batch_bundle": r.name, - "qty": Math.abs(r.total_qty) - }); - } - } - ); - }); - } - }); - } - - update_auto_repeat_reference(doc) { - if (doc.auto_repeat) { - frappe.call({ - method:"frappe.automation.doctype.auto_repeat.auto_repeat.update_reference", - args:{ - docname: doc.auto_repeat, - reference:doc.name - }, - callback: function(r){ - if (r.message=="success") { - frappe.show_alert({message:__("Auto repeat document updated"), indicator:'green'}); - } else { - frappe.show_alert({message:__("An error occurred during the update process"), indicator:'red'}); - } - } - }) - } - } - - project() { - let me = this; - if(in_list(["Delivery Note", "Sales Invoice", "Sales Order"], this.frm.doc.doctype)) { - if(this.frm.doc.project) { - frappe.call({ - method:'erpnext.projects.doctype.project.project.get_cost_center_name' , - args: {project: this.frm.doc.project}, - callback: function(r, rt) { - if(!r.exc) { - $.each(me.frm.doc["items"] || [], function(i, row) { - if(r.message) { - frappe.model.set_value(row.doctype, row.name, "cost_center", r.message); - frappe.msgprint(__("Cost Center For Item with Item Code {0} has been Changed to {1}", [row.item_name, r.message])); - } - }) - } - } - }) - } - } - } - - coupon_code() { - this.frm.set_value("discount_amount", 0); - this.frm.set_value("additional_discount_percentage", 0); - } - }; - } -} - -erpnext.pre_sales = { - set_as_lost: function(doctype) { - frappe.ui.form.on(doctype, { - set_as_lost_dialog: function(frm) { - var dialog = new frappe.ui.Dialog({ - title: __("Set as Lost"), - fields: [ - { - "fieldtype": "Table MultiSelect", - "label": __("Lost Reasons"), - "fieldname": "lost_reason", - "options": frm.doctype === 'Opportunity' ? 'Opportunity Lost Reason Detail': 'Quotation Lost Reason Detail', - "reqd": 1 - }, - { - "fieldtype": "Table MultiSelect", - "label": __("Competitors"), - "fieldname": "competitors", - "options": "Competitor Detail" - }, - { - "fieldtype": "Small Text", - "label": __("Detailed Reason"), - "fieldname": "detailed_reason" - }, - ], - primary_action: function() { - let values = dialog.get_values(); - - frm.call({ - doc: frm.doc, - method: 'declare_enquiry_lost', - args: { - 'lost_reasons_list': values.lost_reason, - 'competitors': values.competitors ? values.competitors : [], - 'detailed_reason': values.detailed_reason - }, - callback: function(r) { - dialog.hide(); - frm.reload_doc(); - }, - }); - }, - primary_action_label: __('Declare Lost') - }); - - dialog.show(); - } - }); - } -} \ No newline at end of file From 857843bc29628122ab1a5fd1618bab5b0ea5128a Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 19 Dec 2023 15:54:29 +0530 Subject: [PATCH 015/675] chore: fix test case (#38856) --- erpnext/stock/doctype/item/test_item.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index 22015e8a65d..7c665b973d8 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -524,7 +524,7 @@ class TestItem(FrappeTestCase): def test_item_variant_by_manufacturer(self): template = make_item( "_Test Item Variant By Manufacturer", {"has_variants": 1, "variant_based_on": "Manufacturer"} - ) + ).name for manufacturer in ["DFSS", "DASA", "ASAAS"]: if not frappe.db.exists("Manufacturer", manufacturer): From 87e8dd0d63068b93c713c4636e97cc3edda411fd Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Tue, 19 Dec 2023 15:56:59 +0530 Subject: [PATCH 016/675] fix: unset discount amount from coupon discounts --- erpnext/selling/sales_common.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index 80a6b7712fc..3470aee8d8c 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -428,6 +428,11 @@ erpnext.selling.SellingController = class SellingController extends erpnext.Tran }) } } + + coupon_code() { + this.frm.set_value("discount_amount", 0); + this.frm.set_value("additional_discount_percentage", 0); + } }; frappe.ui.form.on(cur_frm.doctype,"project", function(frm) { From 2184e8ef58379f53ef8f1d069afa26e64796b073 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 19 Dec 2023 17:20:41 +0530 Subject: [PATCH 017/675] fix: on closed unreserved the production plan qty (#38848) --- .../production_plan/production_plan.py | 4 ++ .../production_plan/test_production_plan.py | 41 +++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 7778f060146..b546594a4ed 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -508,6 +508,7 @@ class ProductionPlan(Document): if close: self.db_set("status", "Closed") + self.update_bin_qty() return if self.total_produced_qty > 0: @@ -522,6 +523,9 @@ class ProductionPlan(Document): if close is not None: self.db_set("status", self.status) + if self.docstatus == 1 and self.status != "Completed": + self.update_bin_qty() + def update_ordered_status(self): update_status = False for d in self.po_items: diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py index 6a50a10d07a..d546306f080 100644 --- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py @@ -1447,6 +1447,47 @@ class TestProductionPlan(FrappeTestCase): self.assertEqual(row.get("uom"), "Nos") self.assertEqual(row.get("conversion_factor"), 10.0) + def test_unreserve_qty_on_closing_of_pp(self): + from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse + from erpnext.stock.utils import get_or_make_bin + + fg_item = make_item(properties={"is_stock_item": 1, "stock_uom": "_Test UOM 1"}).name + rm_item = make_item(properties={"is_stock_item": 1, "stock_uom": "_Test UOM 1"}).name + + store_warehouse = create_warehouse("Store Warehouse", company="_Test Company") + rm_warehouse = create_warehouse("RM Warehouse", company="_Test Company") + + make_bom(item=fg_item, raw_materials=[rm_item], source_warehouse="_Test Warehouse - _TC") + + pln = create_production_plan( + item_code=fg_item, planned_qty=10, stock_uom="_Test UOM 1", do_not_submit=1 + ) + + pln.for_warehouse = rm_warehouse + mr_items = get_items_for_material_requests(pln.as_dict()) + for d in mr_items: + pln.append("mr_items", d) + + pln.save() + pln.submit() + + bin_name = get_or_make_bin(rm_item, rm_warehouse) + before_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan")) + + pln.reload() + pln.set_status(close=True) + + bin_name = get_or_make_bin(rm_item, rm_warehouse) + after_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan")) + self.assertAlmostEqual(after_qty, before_qty - 10) + + pln.reload() + pln.set_status(close=False) + + bin_name = get_or_make_bin(rm_item, rm_warehouse) + after_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan")) + self.assertAlmostEqual(after_qty, before_qty) + def create_production_plan(**args): """ From 7fdac62393ee1e96969cca38a4ce0c07993dce7e Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 19 Dec 2023 18:01:50 +0530 Subject: [PATCH 018/675] fix: incoming rate for sales return with Moving Average valuation method (#38849) --- erpnext/controllers/selling_controller.py | 8 +-- .../delivery_note/test_delivery_note.py | 50 +++++++++++++++++++ 2 files changed, 55 insertions(+), 3 deletions(-) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 7cef623148e..bac94f98af4 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -12,7 +12,7 @@ from erpnext.controllers.sales_and_purchase_return import get_rate_for_return from erpnext.controllers.stock_controller import StockController from erpnext.stock.doctype.item.item import set_item_default from erpnext.stock.get_item_details import get_bin_details, get_conversion_factor -from erpnext.stock.utils import get_incoming_rate +from erpnext.stock.utils import get_incoming_rate, get_valuation_method class SellingController(StockController): @@ -422,11 +422,13 @@ class SellingController(StockController): items = self.get("items") + (self.get("packed_items") or []) for d in items: - if not self.get("return_against"): + if not self.get("return_against") or ( + get_valuation_method(d.item_code) == "Moving Average" and self.get("is_return") + ): # Get incoming rate based on original item cost based on valuation method qty = flt(d.get("stock_qty") or d.get("actual_qty")) - if not (self.get("is_return") and d.incoming_rate): + if not d.incoming_rate: d.incoming_rate = get_incoming_rate( { "item_code": d.item_code, diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index 65828f3a4ac..476b4959813 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -1332,6 +1332,56 @@ class TestDeliveryNote(FrappeTestCase): dn.reload() self.assertFalse(dn.items[0].target_warehouse) + def test_sales_return_valuation_for_moving_average(self): + item_code = make_item( + "_Test Item Sales Return with MA", {"is_stock_item": 1, "valuation_method": "Moving Average"} + ).name + + make_stock_entry( + item_code=item_code, + target="_Test Warehouse - _TC", + qty=5, + basic_rate=100.0, + posting_date=add_days(nowdate(), -5), + ) + dn = create_delivery_note( + item_code=item_code, qty=5, rate=500, posting_date=add_days(nowdate(), -4) + ) + self.assertEqual(dn.items[0].incoming_rate, 100.0) + + make_stock_entry( + item_code=item_code, + target="_Test Warehouse - _TC", + qty=5, + basic_rate=200.0, + posting_date=add_days(nowdate(), -3), + ) + make_stock_entry( + item_code=item_code, + target="_Test Warehouse - _TC", + qty=5, + basic_rate=300.0, + posting_date=add_days(nowdate(), -2), + ) + + dn1 = create_delivery_note( + is_return=1, + item_code=item_code, + return_against=dn.name, + qty=-5, + rate=500, + company=dn.company, + expense_account="Cost of Goods Sold - _TC", + cost_center="Main - _TC", + do_not_submit=1, + posting_date=add_days(nowdate(), -1), + ) + + # (300 * 5) + (200 * 5) = 2500 + # 2500 / 10 = 250 + + self.assertAlmostEqual(dn1.items[0].incoming_rate, 250.0) + def create_delivery_note(**args): dn = frappe.new_doc("Delivery Note") From d375164100158db9b974742caa3e05062c481d7d Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 19 Dec 2023 18:19:20 +0530 Subject: [PATCH 019/675] fix: if not budget then don't validate (#38861) --- erpnext/stock/doctype/material_request/material_request.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index 659bc42f0a6..b84ccf770b2 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -123,7 +123,9 @@ class MaterialRequest(BuyingController): def on_submit(self): self.update_requested_qty_in_production_plan() self.update_requested_qty() - if self.material_request_type == "Purchase": + if self.material_request_type == "Purchase" and frappe.db.exists( + "Budget", {"applicable_on_material_request": 1, "docstatus": 1} + ): self.validate_budget() def before_save(self): From f7bfbd82dd9eb82780bfad60194a4348ba2f03fa Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 20 Dec 2023 09:14:03 +0530 Subject: [PATCH 020/675] fix(ux): don't override Item Name and Description in MR (backport #38720) (#38762) fix(ux): don't override Item Name and Description in MR (cherry picked from commit 726ac6bda1ee3b25c1d62b312d96aa32466ba11e) Co-authored-by: s-aga-r --- erpnext/stock/doctype/material_request/material_request.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index ec075bb6bad..5922af25879 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -199,9 +199,8 @@ frappe.ui.form.on('Material Request', { get_item_data: function(frm, item, overwrite_warehouse=false) { if (item && !item.item_code) { return; } - frm.call({ + frappe.call({ method: "erpnext.stock.get_item_details.get_item_details", - child: item, args: { args: { item_code: item.item_code, From 22b442d593182184cb98a994520109737b8e479c Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 20 Dec 2023 09:14:54 +0530 Subject: [PATCH 021/675] fix: close PO on SCO close (backport #38667) (#38680) fix: close PO on SCO close (cherry picked from commit b023e5d6b3af376a22397c51f4ec8939da463686) Co-authored-by: s-aga-r --- .../doctype/subcontracting_order/subcontracting_order.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py index c0064996099..4ed8a0eab16 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py +++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py @@ -7,6 +7,7 @@ from frappe.model.mapper import get_mapped_doc from frappe.utils import flt from erpnext.buying.doctype.purchase_order.purchase_order import is_subcontracting_order_created +from erpnext.buying.doctype.purchase_order.purchase_order import update_status as update_po_status from erpnext.controllers.subcontracting_controller import SubcontractingController from erpnext.stock.stock_balance import update_bin_qty from erpnext.stock.utils import get_bin @@ -234,6 +235,9 @@ class SubcontractingOrder(SubcontractingController): "Subcontracting Order", self.name, "status", status, update_modified=update_modified ) + if status == "Closed": + update_po_status("Closed", self.purchase_order) + @frappe.whitelist() def make_subcontracting_receipt(source_name, target_doc=None): From 8657ba374a9f7e43279eb71104e29b0be53e3bf0 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 20 Dec 2023 04:46:08 +0000 Subject: [PATCH 022/675] chore(release): Bumped to Version 14.55.0 # [14.55.0](https://github.com/frappe/erpnext/compare/v14.54.2...v14.55.0) (2023-12-20) ### Bug Fixes * close PO on SCO close (backport [#38667](https://github.com/frappe/erpnext/issues/38667)) ([#38680](https://github.com/frappe/erpnext/issues/38680)) ([22b442d](https://github.com/frappe/erpnext/commit/22b442d593182184cb98a994520109737b8e479c)) * **customer:** contact creation for companies ([#38055](https://github.com/frappe/erpnext/issues/38055)) ([ed9b38b](https://github.com/frappe/erpnext/commit/ed9b38bc8d32dd7b03a221b92828dc05d7cc2df9)) * **customer:** quick form and integration fixes ([#37386](https://github.com/frappe/erpnext/issues/37386)) ([6ed7c66](https://github.com/frappe/erpnext/commit/6ed7c6610acf328b1378597d4361f9bc707cb2fa)) * error while filtering on name on reconciliation tool ([30b6321](https://github.com/frappe/erpnext/commit/30b6321fd5f471f0678552e5f728c4ec5f1bf746)) * fetch exc rate of multi currency journals ([bf585de](https://github.com/frappe/erpnext/commit/bf585de558ca7ac1ba08230177fbd1429b120568)) * fetch item_tax_template values if fields with fetch_from exisit ([24ae45c](https://github.com/frappe/erpnext/commit/24ae45ce84b98e68759fb285b9856dc4f1ac72f7)) * get customers for leaderboard ([9dfe342](https://github.com/frappe/erpnext/commit/9dfe3428383e8b7cbfc0cce343d98cddcde039a8)) * get items for leaderboard ([812b301](https://github.com/frappe/erpnext/commit/812b301aaba35f7bcb490da11047662f46077927)) * get sales partner for leaderboard ([c63f873](https://github.com/frappe/erpnext/commit/c63f873a6f731731785b921351f35cc56a00f98a)) * get sales person for leaderboard ([df93447](https://github.com/frappe/erpnext/commit/df93447d3039c2141c0d088383a10cc53f389777)) * get suppliers for leaderboard ([7477ff3](https://github.com/frappe/erpnext/commit/7477ff3b5cb7ef49c1642b901e32fc5fa295c855)) * if not budget then don't validate ([#38861](https://github.com/frappe/erpnext/issues/38861)) ([d375164](https://github.com/frappe/erpnext/commit/d375164100158db9b974742caa3e05062c481d7d)) * incoming rate for sales return with Moving Average valuation method ([#38849](https://github.com/frappe/erpnext/issues/38849)) ([7fdac62](https://github.com/frappe/erpnext/commit/7fdac62393ee1e96969cca38a4ce0c07993dce7e)) * incorrect limit ([#38818](https://github.com/frappe/erpnext/issues/38818)) ([e18dc5c](https://github.com/frappe/erpnext/commit/e18dc5cea37302aec374bb34cbd980a01ced6b35)) * Init internal child table values ([d593f81](https://github.com/frappe/erpnext/commit/d593f81a1604e408128f7c031d11bf21ffa42a13)) * item variant with manufacturer ([#38845](https://github.com/frappe/erpnext/issues/38845)) ([e0c8ff1](https://github.com/frappe/erpnext/commit/e0c8ff10daeed0829266aea9142805f68ceedb2b)) * not able to cancel SCR with Batch ([#38817](https://github.com/frappe/erpnext/issues/38817)) ([fb5090f](https://github.com/frappe/erpnext/commit/fb5090fd3f23ada507fe8abc5a899f4b06e48d7e)) * not able to make inter-company po from so ([#38826](https://github.com/frappe/erpnext/issues/38826)) ([23042df](https://github.com/frappe/erpnext/commit/23042dfc3c0d02374c5710ed679731b1910f9b9a)) * on closed unreserved the production plan qty ([#38848](https://github.com/frappe/erpnext/issues/38848)) ([2184e8e](https://github.com/frappe/erpnext/commit/2184e8ef58379f53ef8f1d069afa26e64796b073)) * Reset SLA on issue doesn't work (backport [#38789](https://github.com/frappe/erpnext/issues/38789)) ([#38790](https://github.com/frappe/erpnext/issues/38790)) ([942f34a](https://github.com/frappe/erpnext/commit/942f34a73456530ce6bd3723ed7adb10cbb83731)) * supplier removed on selection of item (backport [#38712](https://github.com/frappe/erpnext/issues/38712)) ([#38732](https://github.com/frappe/erpnext/issues/38732)) ([dda95ea](https://github.com/frappe/erpnext/commit/dda95ea8922c0f6073d36edf3c0a525fbfc7295e)) * timezone aware SLA banner (backport [#38745](https://github.com/frappe/erpnext/issues/38745)) ([#38746](https://github.com/frappe/erpnext/issues/38746)) ([73d525e](https://github.com/frappe/erpnext/commit/73d525e84da63a4392501b76111d895fdef6498c)) * **ux:** don't override Item Name and Description in MR (backport [#38720](https://github.com/frappe/erpnext/issues/38720)) ([#38762](https://github.com/frappe/erpnext/issues/38762)) ([f7bfbd8](https://github.com/frappe/erpnext/commit/f7bfbd82dd9eb82780bfad60194a4348ba2f03fa)) * validation error on reconciling PE to Journals as Invoice ([9836205](https://github.com/frappe/erpnext/commit/9836205725ae8891a76a462937700322177fe1dd)) * wrong currency in Stock Balance report (backport [#38778](https://github.com/frappe/erpnext/issues/38778)) ([#38779](https://github.com/frappe/erpnext/issues/38779)) ([e05b23c](https://github.com/frappe/erpnext/commit/e05b23c38825039c85648557c572c26a3fc913a4)) * wrong paid and cn amount on pos invoice ([77da0da](https://github.com/frappe/erpnext/commit/77da0da945583e153ef2b7b5a55e28a322d072d3)) ### Features * **RFQ:** special properties in print preview (backport [#38725](https://github.com/frappe/erpnext/issues/38725)) ([#38726](https://github.com/frappe/erpnext/issues/38726)) ([2290750](https://github.com/frappe/erpnext/commit/229075048bd17d945da6ef2b8af71f500db7e8ea)) * set lead name from email ([1469ca7](https://github.com/frappe/erpnext/commit/1469ca7d0b8d7b60da991a8e3b682517113481f3)) ### Performance Improvements * index `return_against` on delivery note (backport [#38827](https://github.com/frappe/erpnext/issues/38827)) ([#38831](https://github.com/frappe/erpnext/issues/38831)) ([4114760](https://github.com/frappe/erpnext/commit/4114760efd0628a2502cff4f4e0a81c54e4eeeb4)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 585f8126daf..634a41ce6e3 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.54.2" +__version__ = "14.55.0" def get_default_company(user=None): From d9e0d55b88e428f80a9e84eb4309f2c153b7028f Mon Sep 17 00:00:00 2001 From: Devin Slauenwhite Date: Mon, 18 Dec 2023 18:03:53 +0000 Subject: [PATCH 023/675] fix: use party account currency when party account is specified (cherry picked from commit c7b961ffa27c611a1e0a45750b38f1f23b0b0c7f) --- .../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 4a4577720b0..8f7b99aef16 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -227,7 +227,7 @@ class ReceivablePayableReport(object): if not row: return - if self.filters.get("in_party_currency"): + if self.filters.get("in_party_currency") or self.filters.get("party_account"): amount = ple.amount_in_account_currency else: amount = ple.amount @@ -457,7 +457,7 @@ class ReceivablePayableReport(object): party_details = self.get_party_details(row.party) or {} row.update(party_details) - if self.filters.get("in_party_currency"): + if self.filters.get("in_party_currency") or self.filters.get("party_account"): row.currency = row.account_currency else: row.currency = self.company_currency From 30cb2186380f0eafce20fcdbcaf3b209dfd6b46b Mon Sep 17 00:00:00 2001 From: Devin Slauenwhite Date: Mon, 18 Dec 2023 19:09:25 +0000 Subject: [PATCH 024/675] fix(test): expect account currency when party account is specified. (cherry picked from commit a09241e3c763882a0a0e06b21ccaa0b06f60bc75) --- .../report/accounts_receivable/test_accounts_receivable.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py index fbfaed6dfda..976935b99f6 100644 --- a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py @@ -579,7 +579,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): filters.update({"party_account": self.debtors_usd}) report = execute(filters)[1] self.assertEqual(len(report), 1) - expected_data = [8000.0, 8000.0, self.debtors_usd, si2.currency] + expected_data = [100.0, 100.0, self.debtors_usd, si2.currency] row = report[0] self.assertEqual( expected_data, [row.invoiced, row.outstanding, row.party_account, row.account_currency] From c2f692a4e4f3dd5089fe4949c6cd74574282fdb1 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 20 Dec 2023 17:53:48 +0530 Subject: [PATCH 025/675] fix: allow to set rate manually for service item in BOM (#38880) --- erpnext/manufacturing/doctype/bom/bom.py | 3 ++ erpnext/manufacturing/doctype/bom/test_bom.py | 29 +++++++++++++++++++ .../doctype/bom_item/bom_item.json | 13 +++++++-- erpnext/patches.txt | 1 + .../v14_0/set_maintain_stock_for_bom_item.py | 19 ++++++++++++ 5 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 erpnext/patches/v14_0/set_maintain_stock_for_bom_item.py diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 6151af7e3ef..c22af3f22cb 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -661,6 +661,9 @@ 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 d.rate = self.get_rm_rate( { diff --git a/erpnext/manufacturing/doctype/bom/test_bom.py b/erpnext/manufacturing/doctype/bom/test_bom.py index 051b475bcc3..2debf9191ef 100644 --- a/erpnext/manufacturing/doctype/bom/test_bom.py +++ b/erpnext/manufacturing/doctype/bom/test_bom.py @@ -698,6 +698,35 @@ class TestBOM(FrappeTestCase): bom.update_cost() self.assertFalse(bom.flags.cost_updated) + def test_bom_with_service_item_cost(self): + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry + + rm_item = make_item(properties={"is_stock_item": 1, "valuation_rate": 1000.0}).name + + service_item = make_item(properties={"is_stock_item": 0}).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, service_item], do_not_save=True) + bom.rm_cost_as_per = "Valuation Rate" + + for row in bom.items: + if row.item_code == service_item: + row.rate = 566.00 + else: + row.rate = 800.00 + + bom.save() + + for row in bom.items: + if row.item_code == service_item: + self.assertEqual(row.is_stock_item, 0) + self.assertEqual(row.rate, 566.00) + else: + self.assertEqual(row.is_stock_item, 1) + def test_do_not_include_manufacturing_and_fixed_items(self): from erpnext.manufacturing.doctype.bom.bom import item_query diff --git a/erpnext/manufacturing/doctype/bom_item/bom_item.json b/erpnext/manufacturing/doctype/bom_item/bom_item.json index cb58af1f29a..dfd66120984 100644 --- a/erpnext/manufacturing/doctype/bom_item/bom_item.json +++ b/erpnext/manufacturing/doctype/bom_item/bom_item.json @@ -14,6 +14,7 @@ "bom_no", "source_warehouse", "allow_alternative_item", + "is_stock_item", "section_break_5", "description", "col_break1", @@ -185,7 +186,7 @@ "in_list_view": 1, "label": "Rate", "options": "currency", - "read_only": 1, + "read_only_depends_on": "eval:doc.is_stock_item == 1", "reqd": 1 }, { @@ -284,13 +285,21 @@ "fieldname": "do_not_explode", "fieldtype": "Check", "label": "Do Not Explode" + }, + { + "default": "0", + "fetch_from": "item_code.is_stock_item", + "fieldname": "is_stock_item", + "fieldtype": "Check", + "label": "Is Stock Item", + "read_only": 1 } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-11-14 18:35:51.378513", + "modified": "2023-12-20 16:21:55.477883", "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM Item", diff --git a/erpnext/patches.txt b/erpnext/patches.txt index d96f45fd676..e0130c57e14 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -355,3 +355,4 @@ erpnext.patches.v14_0.clear_reconciliation_values_from_singles # 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 +erpnext.patches.v14_0.set_maintain_stock_for_bom_item \ No newline at end of file diff --git a/erpnext/patches/v14_0/set_maintain_stock_for_bom_item.py b/erpnext/patches/v14_0/set_maintain_stock_for_bom_item.py new file mode 100644 index 00000000000..f0b618f32d4 --- /dev/null +++ b/erpnext/patches/v14_0/set_maintain_stock_for_bom_item.py @@ -0,0 +1,19 @@ +import frappe + + +def execute(): + if not frappe.db.exists("BOM", {"docstatus": 1}): + return + + # Added is_stock_item to handle Read Only based on condition for the rate field + frappe.db.sql( + """ + UPDATE + `tabBOM Item` boi, + `tabItem` i + SET + boi.is_stock_item = i.is_stock_item + WHERE + boi.item_code = i.name + """ + ) From 4d1ccd9e27302d161e899fc6b6b02f33a1743844 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 20 Dec 2023 18:17:41 +0530 Subject: [PATCH 026/675] perf: use estimated rows instead of actual rows (backport #38830) (#38875) * perf: use estimated rows instead of actual rows (#38830) (cherry picked from commit 9983283f95753e7523cf30cc258df0572f88081d) # Conflicts: # erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py * chore: conflicts --------- Co-authored-by: Ankush Menat --- .../batch_wise_balance_history.py | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) 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 b38dba8bb17..31ea576ec1d 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 @@ -4,7 +4,7 @@ import frappe from frappe import _ -from frappe.utils import cint, flt, getdate +from frappe.utils import cint, flt, get_table_name, getdate from pypika import functions as fn from erpnext.stock.doctype.warehouse.warehouse import apply_warehouse_filter @@ -12,11 +12,22 @@ from erpnext.stock.doctype.warehouse.warehouse import apply_warehouse_filter SLE_COUNT_LIMIT = 10_000 +def _estimate_table_row_count(doctype: str): + table = get_table_name(doctype) + return cint( + frappe.db.sql( + f"""select table_rows + from information_schema.tables + where table_name = '{table}' ;""" + )[0][0] + ) + + def execute(filters=None): if not filters: filters = {} - sle_count = frappe.db.count("Stock Ledger Entry") + sle_count = _estimate_table_row_count("Stock Ledger Entry") if sle_count > SLE_COUNT_LIMIT and not filters.get("item_code") and not filters.get("warehouse"): frappe.throw(_("Please select either the Item or Warehouse filter to generate the report.")) From afc049154d468abc056b628105680852d513b696 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 20 Dec 2023 18:34:31 +0530 Subject: [PATCH 027/675] fix: allow to set rate manually for service item in BOM (backport #38880) (#38881) fix: allow to set rate manually for service item in BOM (#38880) (cherry picked from commit c2f692a4e4f3dd5089fe4949c6cd74574282fdb1) Co-authored-by: rohitwaghchaure --- erpnext/manufacturing/doctype/bom/bom.py | 3 ++ erpnext/manufacturing/doctype/bom/test_bom.py | 29 +++++++++++++++++++ .../doctype/bom_item/bom_item.json | 13 +++++++-- erpnext/patches.txt | 1 + .../v14_0/set_maintain_stock_for_bom_item.py | 19 ++++++++++++ 5 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 erpnext/patches/v14_0/set_maintain_stock_for_bom_item.py diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 6151af7e3ef..c22af3f22cb 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -661,6 +661,9 @@ 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 d.rate = self.get_rm_rate( { diff --git a/erpnext/manufacturing/doctype/bom/test_bom.py b/erpnext/manufacturing/doctype/bom/test_bom.py index 051b475bcc3..2debf9191ef 100644 --- a/erpnext/manufacturing/doctype/bom/test_bom.py +++ b/erpnext/manufacturing/doctype/bom/test_bom.py @@ -698,6 +698,35 @@ class TestBOM(FrappeTestCase): bom.update_cost() self.assertFalse(bom.flags.cost_updated) + def test_bom_with_service_item_cost(self): + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry + + rm_item = make_item(properties={"is_stock_item": 1, "valuation_rate": 1000.0}).name + + service_item = make_item(properties={"is_stock_item": 0}).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, service_item], do_not_save=True) + bom.rm_cost_as_per = "Valuation Rate" + + for row in bom.items: + if row.item_code == service_item: + row.rate = 566.00 + else: + row.rate = 800.00 + + bom.save() + + for row in bom.items: + if row.item_code == service_item: + self.assertEqual(row.is_stock_item, 0) + self.assertEqual(row.rate, 566.00) + else: + self.assertEqual(row.is_stock_item, 1) + def test_do_not_include_manufacturing_and_fixed_items(self): from erpnext.manufacturing.doctype.bom.bom import item_query diff --git a/erpnext/manufacturing/doctype/bom_item/bom_item.json b/erpnext/manufacturing/doctype/bom_item/bom_item.json index cb58af1f29a..dfd66120984 100644 --- a/erpnext/manufacturing/doctype/bom_item/bom_item.json +++ b/erpnext/manufacturing/doctype/bom_item/bom_item.json @@ -14,6 +14,7 @@ "bom_no", "source_warehouse", "allow_alternative_item", + "is_stock_item", "section_break_5", "description", "col_break1", @@ -185,7 +186,7 @@ "in_list_view": 1, "label": "Rate", "options": "currency", - "read_only": 1, + "read_only_depends_on": "eval:doc.is_stock_item == 1", "reqd": 1 }, { @@ -284,13 +285,21 @@ "fieldname": "do_not_explode", "fieldtype": "Check", "label": "Do Not Explode" + }, + { + "default": "0", + "fetch_from": "item_code.is_stock_item", + "fieldname": "is_stock_item", + "fieldtype": "Check", + "label": "Is Stock Item", + "read_only": 1 } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-11-14 18:35:51.378513", + "modified": "2023-12-20 16:21:55.477883", "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM Item", diff --git a/erpnext/patches.txt b/erpnext/patches.txt index d96f45fd676..e0130c57e14 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -355,3 +355,4 @@ erpnext.patches.v14_0.clear_reconciliation_values_from_singles # 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 +erpnext.patches.v14_0.set_maintain_stock_for_bom_item \ No newline at end of file diff --git a/erpnext/patches/v14_0/set_maintain_stock_for_bom_item.py b/erpnext/patches/v14_0/set_maintain_stock_for_bom_item.py new file mode 100644 index 00000000000..f0b618f32d4 --- /dev/null +++ b/erpnext/patches/v14_0/set_maintain_stock_for_bom_item.py @@ -0,0 +1,19 @@ +import frappe + + +def execute(): + if not frappe.db.exists("BOM", {"docstatus": 1}): + return + + # Added is_stock_item to handle Read Only based on condition for the rate field + frappe.db.sql( + """ + UPDATE + `tabBOM Item` boi, + `tabItem` i + SET + boi.is_stock_item = i.is_stock_item + WHERE + boi.item_code = i.name + """ + ) From eefac19cc824a714bcd9b30bc80c8683596b6e96 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 20 Dec 2023 13:06:21 +0000 Subject: [PATCH 028/675] chore(release): Bumped to Version 14.55.1 ## [14.55.1](https://github.com/frappe/erpnext/compare/v14.55.0...v14.55.1) (2023-12-20) ### Bug Fixes * allow to set rate manually for service item in BOM (backport [#38880](https://github.com/frappe/erpnext/issues/38880)) ([#38881](https://github.com/frappe/erpnext/issues/38881)) ([afc0491](https://github.com/frappe/erpnext/commit/afc049154d468abc056b628105680852d513b696)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 634a41ce6e3..479473d991f 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.55.0" +__version__ = "14.55.1" def get_default_company(user=None): From 8169c7de0d06ce031f44c30016d1f17744655bf5 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 20 Dec 2023 22:57:37 +0530 Subject: [PATCH 029/675] feat: total_asset_cost field (backport #38879) (#38886) * feat: total_asset_cost field (#38879) (cherry picked from commit d370c60a6c840be23ec4094593b9bbf1d1dca88b) # Conflicts: # erpnext/assets/doctype/asset/asset.json # erpnext/assets/doctype/asset/asset.py # erpnext/patches.txt * chore: resolve conflicts in asset.json * chore: resolve conflicts in asset.py * chore: resolve conflicts in patches.txt --------- Co-authored-by: Anand Baburajan --- erpnext/assets/doctype/asset/asset.json | 13 +++++++++++-- .../assets/doctype/asset_repair/asset_repair.py | 6 ++++++ erpnext/patches.txt | 3 ++- .../v14_0/update_total_asset_cost_field.py | 17 +++++++++++++++++ 4 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 erpnext/patches/v14_0/update_total_asset_cost_field.py diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json index 3b0d5d5e7fb..3dc8d29542f 100644 --- a/erpnext/assets/doctype/asset/asset.json +++ b/erpnext/assets/doctype/asset/asset.json @@ -35,6 +35,7 @@ "purchase_receipt", "purchase_invoice", "available_for_use_date", + "total_asset_cost", "column_break_23", "gross_purchase_amount", "asset_quantity", @@ -532,6 +533,14 @@ "label": "Capitalized In", "options": "Asset Capitalization", "read_only": 1 + }, + { + "depends_on": "eval:doc.docstatus > 0", + "fieldname": "total_asset_cost", + "fieldtype": "Currency", + "label": "Total Asset Cost", + "options": "Company:company:default_currency", + "read_only": 1 } ], "idx": 72, @@ -565,7 +574,7 @@ "link_fieldname": "target_asset" } ], - "modified": "2023-11-20 21:05:45.216899", + "modified": "2023-12-20 16:50:21.128595", "modified_by": "Administrator", "module": "Assets", "name": "Asset", @@ -609,4 +618,4 @@ "states": [], "title_field": "asset_name", "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index 47453e30308..f978e768af4 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -46,6 +46,9 @@ class AssetRepair(AccountsController): self.increase_asset_value() + if self.capitalize_repair_cost: + self.asset_doc.total_asset_cost += self.repair_cost + if self.get("stock_consumption"): self.check_for_stock_items_and_warehouse() self.decrease_stock_quantity() @@ -68,6 +71,9 @@ class AssetRepair(AccountsController): self.decrease_asset_value() + if self.capitalize_repair_cost: + self.asset_doc.total_asset_cost -= self.repair_cost + if self.get("stock_consumption"): self.increase_stock_quantity() if self.get("capitalize_repair_cost"): diff --git a/erpnext/patches.txt b/erpnext/patches.txt index e0130c57e14..c755a90818d 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -352,7 +352,8 @@ erpnext.patches.v14_0.create_accounting_dimensions_in_supplier_quotation erpnext.patches.v14_0.update_zero_asset_quantity_field execute:frappe.db.set_single_value("Buying Settings", "project_update_frequency", "Each Transaction") erpnext.patches.v14_0.clear_reconciliation_values_from_singles +erpnext.patches.v14_0.update_total_asset_cost_field # 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 -erpnext.patches.v14_0.set_maintain_stock_for_bom_item \ No newline at end of file +erpnext.patches.v14_0.set_maintain_stock_for_bom_item diff --git a/erpnext/patches/v14_0/update_total_asset_cost_field.py b/erpnext/patches/v14_0/update_total_asset_cost_field.py new file mode 100644 index 00000000000..57cf71b6134 --- /dev/null +++ b/erpnext/patches/v14_0/update_total_asset_cost_field.py @@ -0,0 +1,17 @@ +import frappe + + +def execute(): + asset = frappe.qb.DocType("Asset") + frappe.qb.update(asset).set(asset.total_asset_cost, asset.gross_purchase_amount).run() + + asset_repair_list = frappe.db.get_all( + "Asset Repair", + filters={"docstatus": 1, "repair_status": "Completed", "capitalize_repair_cost": 1}, + fields=["asset", "repair_cost"], + ) + + for asset_repair in asset_repair_list: + frappe.qb.update(asset).set( + asset.total_asset_cost, asset.total_asset_cost + asset_repair.repair_cost + ).where(asset.name == asset_repair.asset).run() From f2f410093aa688d004637cb3a7a0052498351d8e Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Thu, 21 Dec 2023 09:31:03 +0530 Subject: [PATCH 030/675] fix: remove rows with zero consumed qty --- erpnext/controllers/subcontracting_controller.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py index 34d3e700ccc..772fbd54512 100644 --- a/erpnext/controllers/subcontracting_controller.py +++ b/erpnext/controllers/subcontracting_controller.py @@ -433,8 +433,11 @@ class SubcontractingController(StockController): self.__set_batch_no_as_per_qty(item_row, new_rm_obj, batch_no, batch_qty) self.available_materials[key]["batch_no"][batch_no] = 0 - if abs(qty) > 0 and not new_rm_obj: + if new_rm_obj: + self.remove(rm_obj) + elif abs(qty) > 0: self.__set_consumed_qty(rm_obj, qty) + else: self.__set_consumed_qty(rm_obj, qty, bom_item.required_qty or qty) self.__set_serial_nos(item_row, rm_obj) @@ -525,6 +528,10 @@ class SubcontractingController(StockController): (row.item_code, row.get(self.subcontract_data.order_field)) ] -= row.qty + def __reset_idx(self): + for idx, item in enumerate(self.get(self.raw_material_table)): + item.idx = idx + 1 + def __prepare_supplied_items(self): self.initialized_fields() self.__get_subcontract_orders() @@ -532,6 +539,7 @@ class SubcontractingController(StockController): self.get_available_materials() self.__remove_changed_rows() self.__set_supplied_items() + self.__reset_idx() def __validate_batch_no(self, row, key): if row.get("batch_no") and row.get("batch_no") not in self.__transferred_items.get(key).get( From 3a668bbe9694fdd6e8265869c6943e42f889ac41 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 21 Dec 2023 14:13:34 +0530 Subject: [PATCH 031/675] =?UTF-8?q?fix:=20reposting=20not=20fixing=20valua?= =?UTF-8?q?tion=20rate=20for=20sales=20return=20using=20movin=E2=80=A6=20(?= =?UTF-8?q?#38895)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix: reposting not fixing valuation rate for sales return using moving average method --- erpnext/controllers/selling_controller.py | 4 +- .../delivery_note/test_delivery_note.py | 53 +++++++++++++++++++ erpnext/stock/stock_ledger.py | 19 ++++++- 3 files changed, 74 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index bac94f98af4..d3157e7037b 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -428,7 +428,9 @@ class SellingController(StockController): # Get incoming rate based on original item cost based on valuation method qty = flt(d.get("stock_qty") or d.get("actual_qty")) - if not d.incoming_rate: + if not d.incoming_rate or ( + get_valuation_method(d.item_code) == "Moving Average" and self.get("is_return") + ): d.incoming_rate = get_incoming_rate( { "item_code": d.item_code, diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index 476b4959813..a931ee2c254 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -1382,6 +1382,59 @@ class TestDeliveryNote(FrappeTestCase): self.assertAlmostEqual(dn1.items[0].incoming_rate, 250.0) + def test_sales_return_valuation_for_moving_average_case2(self): + # Make DN return + # Make Bakcdated Purchase Receipt and check DN return valuation rate + # The rate should be recalculate based on the backdated purchase receipt + frappe.flags.print_debug_messages = False + item_code = make_item( + "_Test Item Sales Return with MA Case2", + {"is_stock_item": 1, "valuation_method": "Moving Average", "stock_uom": "Nos"}, + ).name + + make_stock_entry( + item_code=item_code, + target="_Test Warehouse - _TC", + qty=5, + basic_rate=100.0, + posting_date=add_days(nowdate(), -5), + ) + + dn = create_delivery_note( + item_code=item_code, + warehouse="_Test Warehouse - _TC", + qty=5, + rate=500, + posting_date=add_days(nowdate(), -4), + ) + + returned_dn = create_delivery_note( + is_return=1, + item_code=item_code, + return_against=dn.name, + qty=-5, + rate=500, + company=dn.company, + warehouse="_Test Warehouse - _TC", + expense_account="Cost of Goods Sold - _TC", + cost_center="Main - _TC", + posting_date=add_days(nowdate(), -1), + ) + + self.assertAlmostEqual(returned_dn.items[0].incoming_rate, 100.0) + + # Make backdated purchase receipt + make_stock_entry( + item_code=item_code, + target="_Test Warehouse - _TC", + qty=5, + basic_rate=200.0, + posting_date=add_days(nowdate(), -3), + ) + + returned_dn.reload() + self.assertAlmostEqual(returned_dn.items[0].incoming_rate, 200.0) + 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 8a262497926..df1f544d7b1 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -16,6 +16,7 @@ from erpnext.stock.doctype.bin.bin import update_qty as update_bin_qty from erpnext.stock.doctype.inventory_dimension.inventory_dimension import get_inventory_dimensions from erpnext.stock.utils import ( get_incoming_outgoing_rate_for_cancel, + get_incoming_rate, get_or_make_bin, get_valuation_method, ) @@ -701,7 +702,23 @@ class update_entries_after(object): ) if self.valuation_method == "Moving Average": - rate = flt(self.data[self.args.warehouse].previous_sle.valuation_rate) + rate = get_incoming_rate( + { + "item_code": sle.item_code, + "warehouse": sle.warehouse, + "posting_date": sle.posting_date, + "posting_time": sle.posting_time, + "qty": sle.actual_qty, + "serial_no": sle.get("serial_no"), + "batch_no": sle.get("batch_no"), + "company": sle.company, + "voucher_type": sle.voucher_type, + "voucher_no": sle.voucher_no, + "allow_zero_valuation": self.allow_zero_rate, + "sle": sle.name, + } + ) + else: rate = get_rate_for_return( sle.voucher_type, From 98bfcc4c758b61f714c53ea0f00246731a30fdaf Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 21 Dec 2023 14:13:55 +0530 Subject: [PATCH 032/675] fix: do not reset the basic rate for the material receipt stock entry (#38896) --- erpnext/stock/doctype/stock_entry/stock_entry.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index 70771e77461..0786ce6be2a 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -517,7 +517,12 @@ frappe.ui.form.on('Stock Entry', { }, callback: function(r) { if (!r.exc) { - ["actual_qty", "basic_rate"].forEach((field) => { + let fields = ["actual_qty", "basic_rate"]; + if (frm.doc.purpose == "Material Receipt") { + fields = ["actual_qty"]; + } + + fields.forEach((field) => { frappe.model.set_value(cdt, cdn, field, (r.message[field] || 0.0)); }); frm.events.calculate_basic_amount(frm, child); From 8aba707b6aaa7e89355d2b5afe81f625804f6506 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 21 Dec 2023 15:34:49 +0530 Subject: [PATCH 033/675] =?UTF-8?q?fix:=20reposting=20not=20fixing=20valua?= =?UTF-8?q?tion=20rate=20for=20sales=20return=20using=20movin=E2=80=A6=20(?= =?UTF-8?q?backport=20#38895)=20(#38900)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix: reposting not fixing valuation rate for sales return using movin… (#38895) fix: reposting not fixing valuation rate for sales return using moving average method (cherry picked from commit 3a668bbe9694fdd6e8265869c6943e42f889ac41) Co-authored-by: rohitwaghchaure --- erpnext/controllers/selling_controller.py | 4 +- .../delivery_note/test_delivery_note.py | 53 +++++++++++++++++++ erpnext/stock/stock_ledger.py | 19 ++++++- 3 files changed, 74 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index bac94f98af4..d3157e7037b 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -428,7 +428,9 @@ class SellingController(StockController): # Get incoming rate based on original item cost based on valuation method qty = flt(d.get("stock_qty") or d.get("actual_qty")) - if not d.incoming_rate: + if not d.incoming_rate or ( + get_valuation_method(d.item_code) == "Moving Average" and self.get("is_return") + ): d.incoming_rate = get_incoming_rate( { "item_code": d.item_code, diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index 476b4959813..a931ee2c254 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -1382,6 +1382,59 @@ class TestDeliveryNote(FrappeTestCase): self.assertAlmostEqual(dn1.items[0].incoming_rate, 250.0) + def test_sales_return_valuation_for_moving_average_case2(self): + # Make DN return + # Make Bakcdated Purchase Receipt and check DN return valuation rate + # The rate should be recalculate based on the backdated purchase receipt + frappe.flags.print_debug_messages = False + item_code = make_item( + "_Test Item Sales Return with MA Case2", + {"is_stock_item": 1, "valuation_method": "Moving Average", "stock_uom": "Nos"}, + ).name + + make_stock_entry( + item_code=item_code, + target="_Test Warehouse - _TC", + qty=5, + basic_rate=100.0, + posting_date=add_days(nowdate(), -5), + ) + + dn = create_delivery_note( + item_code=item_code, + warehouse="_Test Warehouse - _TC", + qty=5, + rate=500, + posting_date=add_days(nowdate(), -4), + ) + + returned_dn = create_delivery_note( + is_return=1, + item_code=item_code, + return_against=dn.name, + qty=-5, + rate=500, + company=dn.company, + warehouse="_Test Warehouse - _TC", + expense_account="Cost of Goods Sold - _TC", + cost_center="Main - _TC", + posting_date=add_days(nowdate(), -1), + ) + + self.assertAlmostEqual(returned_dn.items[0].incoming_rate, 100.0) + + # Make backdated purchase receipt + make_stock_entry( + item_code=item_code, + target="_Test Warehouse - _TC", + qty=5, + basic_rate=200.0, + posting_date=add_days(nowdate(), -3), + ) + + returned_dn.reload() + self.assertAlmostEqual(returned_dn.items[0].incoming_rate, 200.0) + 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 8a262497926..df1f544d7b1 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -16,6 +16,7 @@ from erpnext.stock.doctype.bin.bin import update_qty as update_bin_qty from erpnext.stock.doctype.inventory_dimension.inventory_dimension import get_inventory_dimensions from erpnext.stock.utils import ( get_incoming_outgoing_rate_for_cancel, + get_incoming_rate, get_or_make_bin, get_valuation_method, ) @@ -701,7 +702,23 @@ class update_entries_after(object): ) if self.valuation_method == "Moving Average": - rate = flt(self.data[self.args.warehouse].previous_sle.valuation_rate) + rate = get_incoming_rate( + { + "item_code": sle.item_code, + "warehouse": sle.warehouse, + "posting_date": sle.posting_date, + "posting_time": sle.posting_time, + "qty": sle.actual_qty, + "serial_no": sle.get("serial_no"), + "batch_no": sle.get("batch_no"), + "company": sle.company, + "voucher_type": sle.voucher_type, + "voucher_no": sle.voucher_no, + "allow_zero_valuation": self.allow_zero_rate, + "sle": sle.name, + } + ) + else: rate = get_rate_for_return( sle.voucher_type, From 0c13cb829cc95f2dff30b2276ada489ab74e39cc Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 21 Dec 2023 10:05:58 +0000 Subject: [PATCH 034/675] chore(release): Bumped to Version 14.55.2 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## [14.55.2](https://github.com/frappe/erpnext/compare/v14.55.1...v14.55.2) (2023-12-21) ### Bug Fixes * reposting not fixing valuation rate for sales return using movin… (backport [#38895](https://github.com/frappe/erpnext/issues/38895)) ([#38900](https://github.com/frappe/erpnext/issues/38900)) ([8aba707](https://github.com/frappe/erpnext/commit/8aba707b6aaa7e89355d2b5afe81f625804f6506)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 479473d991f..ee200eeba9e 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.55.1" +__version__ = "14.55.2" def get_default_company(user=None): From 3b9feffc00ec6c1f0e99bd4670030efc71509595 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 21 Dec 2023 16:56:32 +0530 Subject: [PATCH 035/675] chore: additional_asset_cost field (backport #38904) (#38905) * chore: additional_asset_cost field (#38904) (cherry picked from commit 283763dfb2affa6a0b7bb29e19123c3e1fb27f30) # Conflicts: # erpnext/assets/doctype/asset/asset.py * chore: resolve conflicts in asset.py --------- Co-authored-by: Anand Baburajan --- erpnext/assets/doctype/asset/asset.json | 11 ++++++++++- erpnext/assets/doctype/asset/asset.py | 1 + erpnext/assets/doctype/asset_repair/asset_repair.py | 2 ++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json index 3dc8d29542f..91ae62bfad7 100644 --- a/erpnext/assets/doctype/asset/asset.json +++ b/erpnext/assets/doctype/asset/asset.json @@ -36,6 +36,7 @@ "purchase_invoice", "available_for_use_date", "total_asset_cost", + "additional_asset_cost", "column_break_23", "gross_purchase_amount", "asset_quantity", @@ -541,6 +542,14 @@ "label": "Total Asset Cost", "options": "Company:company:default_currency", "read_only": 1 + }, + { + "depends_on": "eval:doc.docstatus > 0", + "fieldname": "additional_asset_cost", + "fieldtype": "Currency", + "label": "Additional Asset Cost", + "options": "Company:company:default_currency", + "read_only": 1 } ], "idx": 72, @@ -574,7 +583,7 @@ "link_fieldname": "target_asset" } ], - "modified": "2023-12-20 16:50:21.128595", + "modified": "2023-12-21 16:46:20.732869", "modified_by": "Administrator", "module": "Assets", "name": "Asset", diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 92a349abb3e..f83d7071aa7 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -48,6 +48,7 @@ class Asset(AccountsController): if self.get("schedules"): self.validate_expected_value_after_useful_life() + self.total_asset_cost = self.gross_purchase_amount self.status = self.get_status() def on_submit(self): diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index f978e768af4..b348e13ca83 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -48,6 +48,7 @@ class AssetRepair(AccountsController): if self.capitalize_repair_cost: self.asset_doc.total_asset_cost += self.repair_cost + self.asset_doc.additional_asset_cost += self.repair_cost if self.get("stock_consumption"): self.check_for_stock_items_and_warehouse() @@ -73,6 +74,7 @@ class AssetRepair(AccountsController): if self.capitalize_repair_cost: self.asset_doc.total_asset_cost -= self.repair_cost + self.asset_doc.additional_asset_cost -= self.repair_cost if self.get("stock_consumption"): self.increase_stock_quantity() From a63b8df86722bf51b2dd534522331a24cd3f3340 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 21 Dec 2023 17:10:04 +0530 Subject: [PATCH 036/675] feat: total_asset_cost field (backport #38879) (backport #38886) (#38907) feat: total_asset_cost field (backport #38879) (#38886) * feat: total_asset_cost field (#38879) (cherry picked from commit d370c60a6c840be23ec4094593b9bbf1d1dca88b) # Conflicts: # erpnext/assets/doctype/asset/asset.json # erpnext/assets/doctype/asset/asset.py # erpnext/patches.txt * chore: resolve conflicts in asset.json * chore: resolve conflicts in asset.py * chore: resolve conflicts in patches.txt --------- Co-authored-by: Anand Baburajan (cherry picked from commit 8169c7de0d06ce031f44c30016d1f17744655bf5) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- erpnext/assets/doctype/asset/asset.json | 13 +++++++++++-- .../assets/doctype/asset_repair/asset_repair.py | 6 ++++++ erpnext/patches.txt | 3 ++- .../v14_0/update_total_asset_cost_field.py | 17 +++++++++++++++++ 4 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 erpnext/patches/v14_0/update_total_asset_cost_field.py diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json index 3b0d5d5e7fb..3dc8d29542f 100644 --- a/erpnext/assets/doctype/asset/asset.json +++ b/erpnext/assets/doctype/asset/asset.json @@ -35,6 +35,7 @@ "purchase_receipt", "purchase_invoice", "available_for_use_date", + "total_asset_cost", "column_break_23", "gross_purchase_amount", "asset_quantity", @@ -532,6 +533,14 @@ "label": "Capitalized In", "options": "Asset Capitalization", "read_only": 1 + }, + { + "depends_on": "eval:doc.docstatus > 0", + "fieldname": "total_asset_cost", + "fieldtype": "Currency", + "label": "Total Asset Cost", + "options": "Company:company:default_currency", + "read_only": 1 } ], "idx": 72, @@ -565,7 +574,7 @@ "link_fieldname": "target_asset" } ], - "modified": "2023-11-20 21:05:45.216899", + "modified": "2023-12-20 16:50:21.128595", "modified_by": "Administrator", "module": "Assets", "name": "Asset", @@ -609,4 +618,4 @@ "states": [], "title_field": "asset_name", "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index 47453e30308..f978e768af4 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -46,6 +46,9 @@ class AssetRepair(AccountsController): self.increase_asset_value() + if self.capitalize_repair_cost: + self.asset_doc.total_asset_cost += self.repair_cost + if self.get("stock_consumption"): self.check_for_stock_items_and_warehouse() self.decrease_stock_quantity() @@ -68,6 +71,9 @@ class AssetRepair(AccountsController): self.decrease_asset_value() + if self.capitalize_repair_cost: + self.asset_doc.total_asset_cost -= self.repair_cost + if self.get("stock_consumption"): self.increase_stock_quantity() if self.get("capitalize_repair_cost"): diff --git a/erpnext/patches.txt b/erpnext/patches.txt index e0130c57e14..c755a90818d 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -352,7 +352,8 @@ erpnext.patches.v14_0.create_accounting_dimensions_in_supplier_quotation erpnext.patches.v14_0.update_zero_asset_quantity_field execute:frappe.db.set_single_value("Buying Settings", "project_update_frequency", "Each Transaction") erpnext.patches.v14_0.clear_reconciliation_values_from_singles +erpnext.patches.v14_0.update_total_asset_cost_field # 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 -erpnext.patches.v14_0.set_maintain_stock_for_bom_item \ No newline at end of file +erpnext.patches.v14_0.set_maintain_stock_for_bom_item diff --git a/erpnext/patches/v14_0/update_total_asset_cost_field.py b/erpnext/patches/v14_0/update_total_asset_cost_field.py new file mode 100644 index 00000000000..57cf71b6134 --- /dev/null +++ b/erpnext/patches/v14_0/update_total_asset_cost_field.py @@ -0,0 +1,17 @@ +import frappe + + +def execute(): + asset = frappe.qb.DocType("Asset") + frappe.qb.update(asset).set(asset.total_asset_cost, asset.gross_purchase_amount).run() + + asset_repair_list = frappe.db.get_all( + "Asset Repair", + filters={"docstatus": 1, "repair_status": "Completed", "capitalize_repair_cost": 1}, + fields=["asset", "repair_cost"], + ) + + for asset_repair in asset_repair_list: + frappe.qb.update(asset).set( + asset.total_asset_cost, asset.total_asset_cost + asset_repair.repair_cost + ).where(asset.name == asset_repair.asset).run() From 3abb91b2c4299ad96330ba66e992bd9ba7d60763 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 21 Dec 2023 11:41:51 +0000 Subject: [PATCH 037/675] chore(release): Bumped to Version 14.56.0 # [14.56.0](https://github.com/frappe/erpnext/compare/v14.55.2...v14.56.0) (2023-12-21) ### Features * total_asset_cost field (backport [#38879](https://github.com/frappe/erpnext/issues/38879)) (backport [#38886](https://github.com/frappe/erpnext/issues/38886)) ([#38907](https://github.com/frappe/erpnext/issues/38907)) ([a63b8df](https://github.com/frappe/erpnext/commit/a63b8df86722bf51b2dd534522331a24cd3f3340)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index ee200eeba9e..85dbe99af84 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.55.2" +__version__ = "14.56.0" def get_default_company(user=None): From 0d7cb1a9be9d76bc29dd88eca18b3731b106ec57 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 21 Dec 2023 17:12:01 +0530 Subject: [PATCH 038/675] chore: additional_asset_cost field (backport #38904) (backport #38905) (#38908) chore: additional_asset_cost field (backport #38904) (#38905) * chore: additional_asset_cost field (#38904) (cherry picked from commit 283763dfb2affa6a0b7bb29e19123c3e1fb27f30) # Conflicts: # erpnext/assets/doctype/asset/asset.py * chore: resolve conflicts in asset.py --------- Co-authored-by: Anand Baburajan (cherry picked from commit 3b9feffc00ec6c1f0e99bd4670030efc71509595) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- erpnext/assets/doctype/asset/asset.json | 11 ++++++++++- erpnext/assets/doctype/asset/asset.py | 1 + erpnext/assets/doctype/asset_repair/asset_repair.py | 2 ++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json index 3dc8d29542f..91ae62bfad7 100644 --- a/erpnext/assets/doctype/asset/asset.json +++ b/erpnext/assets/doctype/asset/asset.json @@ -36,6 +36,7 @@ "purchase_invoice", "available_for_use_date", "total_asset_cost", + "additional_asset_cost", "column_break_23", "gross_purchase_amount", "asset_quantity", @@ -541,6 +542,14 @@ "label": "Total Asset Cost", "options": "Company:company:default_currency", "read_only": 1 + }, + { + "depends_on": "eval:doc.docstatus > 0", + "fieldname": "additional_asset_cost", + "fieldtype": "Currency", + "label": "Additional Asset Cost", + "options": "Company:company:default_currency", + "read_only": 1 } ], "idx": 72, @@ -574,7 +583,7 @@ "link_fieldname": "target_asset" } ], - "modified": "2023-12-20 16:50:21.128595", + "modified": "2023-12-21 16:46:20.732869", "modified_by": "Administrator", "module": "Assets", "name": "Asset", diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 92a349abb3e..f83d7071aa7 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -48,6 +48,7 @@ class Asset(AccountsController): if self.get("schedules"): self.validate_expected_value_after_useful_life() + self.total_asset_cost = self.gross_purchase_amount self.status = self.get_status() def on_submit(self): diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py index f978e768af4..b348e13ca83 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.py +++ b/erpnext/assets/doctype/asset_repair/asset_repair.py @@ -48,6 +48,7 @@ class AssetRepair(AccountsController): if self.capitalize_repair_cost: self.asset_doc.total_asset_cost += self.repair_cost + self.asset_doc.additional_asset_cost += self.repair_cost if self.get("stock_consumption"): self.check_for_stock_items_and_warehouse() @@ -73,6 +74,7 @@ class AssetRepair(AccountsController): if self.capitalize_repair_cost: self.asset_doc.total_asset_cost -= self.repair_cost + self.asset_doc.additional_asset_cost -= self.repair_cost if self.get("stock_consumption"): self.increase_stock_quantity() From 2770ca1b659a3217e249b60155434052d0a62730 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 21 Dec 2023 18:49:36 +0530 Subject: [PATCH 039/675] fix: reset the incoming rate on changing of the warehouse (#38909) --- erpnext/selling/sales_common.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index 3470aee8d8c..b2a64a5d461 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -200,6 +200,10 @@ erpnext.selling.SellingController = class SellingController extends erpnext.Tran item.serial_no = null; } + if (doc.docstatus === 0 && doc.is_return && !doc.return_against) { + item.incoming_rate = 0.0; + } + var has_batch_no; frappe.db.get_value('Item', {'item_code': item.item_code}, 'has_batch_no', (r) => { has_batch_no = r && r.has_batch_no; From b254a72d41bd701cf93752771681b0f55e93a6f7 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 21 Dec 2023 21:40:59 +0530 Subject: [PATCH 040/675] perf: Drop unused/duplicate/sub-optimal indexes (backport #38884) (#38912) * perf: Drop unused/duplicate/sub-optimal indexes (#38884) * ci: enable more checks * perf: Drop unused/duplicate indexes (cherry picked from commit 787333896c3710d16b1fa72432db0fe59c74dfa8) # Conflicts: # erpnext/accounts/doctype/gl_entry/gl_entry.json # erpnext/patches.txt * chore: conflicts --------- Co-authored-by: Ankush Menat --- .pre-commit-config.yaml | 6 +++- .../accounts/doctype/gl_entry/gl_entry.json | 8 ++---- .../purchase_invoice/purchase_invoice.py | 4 --- .../doctype/sales_invoice/sales_invoice.py | 4 --- .../purchase_order_item.json | 3 +- erpnext/patches.txt | 2 +- erpnext/stock/doctype/bin/bin.json | 3 +- .../drop_unused_return_against_index.py | 28 +++++++++++++------ erpnext/tests/test_perf.py | 24 ++++++++++++++++ 9 files changed, 55 insertions(+), 27 deletions(-) create mode 100644 erpnext/tests/test_perf.py diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index d70977c07e2..fba2f74a5b9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -5,7 +5,7 @@ fail_fast: false repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.0.1 + rev: v4.3.0 hooks: - id: trailing-whitespace files: "erpnext.*" @@ -15,6 +15,10 @@ repos: args: ['--branch', 'develop'] - id: check-merge-conflict - id: check-ast + - id: check-json + - id: check-toml + - id: check-yaml + - id: debug-statements - repo: https://github.com/PyCQA/flake8 rev: 5.0.4 diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.json b/erpnext/accounts/doctype/gl_entry/gl_entry.json index e6d97a1fb26..592eaecc1c5 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.json +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.json @@ -138,8 +138,7 @@ "label": "Against Voucher Type", "oldfieldname": "against_voucher_type", "oldfieldtype": "Data", - "options": "DocType", - "search_index": 1 + "options": "DocType" }, { "fieldname": "against_voucher", @@ -158,8 +157,7 @@ "label": "Voucher Type", "oldfieldname": "voucher_type", "oldfieldtype": "Select", - "options": "DocType", - "search_index": 1 + "options": "DocType" }, { "fieldname": "voucher_no", @@ -291,4 +289,4 @@ "search_fields": "voucher_no,account,posting_date,against_voucher", "sort_field": "modified", "sort_order": "DESC" -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 944a8fb2364..7171bddac4b 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1660,10 +1660,6 @@ def make_inter_company_sales_invoice(source_name, target_doc=None): return make_inter_company_transaction("Purchase Invoice", source_name, target_doc) -def on_doctype_update(): - frappe.db.add_index("Purchase Invoice", ["supplier", "is_return", "return_against"]) - - @frappe.whitelist() def make_purchase_receipt(source_name, target_doc=None): def update_item(obj, target, source_parent): diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 3d18a860361..63a576b5ba8 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -2388,10 +2388,6 @@ def get_loyalty_programs(customer): return lp_details -def on_doctype_update(): - frappe.db.add_index("Sales Invoice", ["customer", "is_return", "return_against"]) - - @frappe.whitelist() def create_invoice_discounting(source_name, target_doc=None): invoice = frappe.get_doc("Sales Invoice", source_name) diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json index 45790b80fcb..1a9035c3327 100644 --- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json +++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json @@ -123,8 +123,7 @@ "oldfieldname": "item_code", "oldfieldtype": "Link", "options": "Item", - "reqd": 1, - "search_index": 1 + "reqd": 1 }, { "fieldname": "supplier_part_no", diff --git a/erpnext/patches.txt b/erpnext/patches.txt index c755a90818d..e464552fa0c 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -355,5 +355,5 @@ erpnext.patches.v14_0.clear_reconciliation_values_from_singles erpnext.patches.v14_0.update_total_asset_cost_field # 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 +erpnext.stock.doctype.delivery_note.patches.drop_unused_return_against_index # 2023-12-20 erpnext.patches.v14_0.set_maintain_stock_for_bom_item diff --git a/erpnext/stock/doctype/bin/bin.json b/erpnext/stock/doctype/bin/bin.json index 02684a72419..5284bc410c1 100644 --- a/erpnext/stock/doctype/bin/bin.json +++ b/erpnext/stock/doctype/bin/bin.json @@ -51,8 +51,7 @@ "oldfieldtype": "Link", "options": "Item", "read_only": 1, - "reqd": 1, - "search_index": 1 + "reqd": 1 }, { "default": "0.00", diff --git a/erpnext/stock/doctype/delivery_note/patches/drop_unused_return_against_index.py b/erpnext/stock/doctype/delivery_note/patches/drop_unused_return_against_index.py index 8fe4ffb58f1..cc29e67fa7b 100644 --- a/erpnext/stock/doctype/delivery_note/patches/drop_unused_return_against_index.py +++ b/erpnext/stock/doctype/delivery_note/patches/drop_unused_return_against_index.py @@ -1,15 +1,27 @@ +import click import frappe +UNUSED_INDEXES = [ + ("Delivery Note", ["customer", "is_return", "return_against"]), + ("Sales Invoice", ["customer", "is_return", "return_against"]), + ("Purchase Invoice", ["supplier", "is_return", "return_against"]), + ("Purchase Receipt", ["supplier", "is_return", "return_against"]), +] + def execute(): - """Drop unused return_against index""" + for doctype, index_fields in UNUSED_INDEXES: + table = f"tab{doctype}" + index_name = frappe.db.get_index_name(index_fields) + drop_index_if_exists(table, index_name) + + +def drop_index_if_exists(table: str, index: str): + if not frappe.db.has_index(table, index): + return try: - frappe.db.sql_ddl( - "ALTER TABLE `tabDelivery Note` DROP INDEX `customer_is_return_return_against_index`" - ) - frappe.db.sql_ddl( - "ALTER TABLE `tabPurchase Receipt` DROP INDEX `supplier_is_return_return_against_index`" - ) + 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 unused index") + frappe.log_error("Failed to drop index") diff --git a/erpnext/tests/test_perf.py b/erpnext/tests/test_perf.py new file mode 100644 index 00000000000..fc17b1dcbda --- /dev/null +++ b/erpnext/tests/test_perf.py @@ -0,0 +1,24 @@ +import frappe +from frappe.tests.utils import FrappeTestCase + +INDEXED_FIELDS = { + "Bin": ["item_code"], + "GL Entry": ["voucher_type", "against_voucher_type"], + "Purchase Order Item": ["item_code"], + "Stock Ledger Entry": ["warehouse"], +} + + +class TestPerformance(FrappeTestCase): + def test_ensure_indexes(self): + # These fields are not explicitly indexed BUT they are prefix in some + # other composite index. If those are removed this test should be + # updated accordingly. + for doctype, fields in INDEXED_FIELDS.items(): + for field in fields: + self.assertTrue( + frappe.db.sql( + f"""SHOW INDEX FROM `tab{doctype}` + WHERE Column_name = "{field}" AND Seq_in_index = 1""" + ) + ) From 28f052d586c2781136b2e6497b7f3731a5b5227e Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sun, 24 Dec 2023 16:12:13 +0530 Subject: [PATCH 041/675] fix: first name error on customer creation (#38927) fix: error on customer creation --- erpnext/selling/doctype/customer/customer.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 4bbce9b2f00..56388a594a7 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -702,6 +702,7 @@ def make_contact(args, is_primary_contact=1): else: values.update( { + "first_name": args.get("customer_name"), "company_name": args.get("customer_name"), } ) From 81b5e6c5f13da686d4472168b731e7e284a4e9c2 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 24 Dec 2023 16:24:31 +0530 Subject: [PATCH 042/675] fix: german translations for Lost Quotations (#38435) * fix: german translations for Lost Quotations (#38435) (cherry picked from commit 5952cfa673837e09167591e593e5691f1f121b6e) # Conflicts: # erpnext/translations/de.csv * chore: resolve conflicts --------- Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com> --- erpnext/translations/de.csv | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index 105021cfa75..3a8b7fe1a4a 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -9104,3 +9104,7 @@ Select an item from each set to be used in the Sales Order.,"Wählen Sie aus den Is Alternative,Ist Alternative, Alternative Items,Alternativpositionen, Component Type,Komponententyp, +Lost Quotations,Verlorene Angebote, +Lost Quotations %,Verlorene Angebote %, +Lost Value,Verlorener Wert, +Lost Value %,Verlorener Wert %, From 7577706a9dc0a050cc3ed401f44f15a528b8d573 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 20 Dec 2023 17:22:30 +0530 Subject: [PATCH 043/675] fix: incorrect price list in customer-wise item price report (cherry picked from commit 9a00edb03115b72afb4274e6fcf2dc7d5b431657) --- .../customer_wise_item_price.py | 50 +++++++++++++++++-- 1 file changed, 47 insertions(+), 3 deletions(-) diff --git a/erpnext/selling/report/customer_wise_item_price/customer_wise_item_price.py b/erpnext/selling/report/customer_wise_item_price/customer_wise_item_price.py index a58f40362ba..40aa9acc3c6 100644 --- a/erpnext/selling/report/customer_wise_item_price/customer_wise_item_price.py +++ b/erpnext/selling/report/customer_wise_item_price/customer_wise_item_price.py @@ -3,11 +3,11 @@ import frappe -from frappe import _ +from frappe import _, qb +from frappe.query_builder import Criterion from erpnext import get_default_company from erpnext.accounts.party import get_party_details -from erpnext.stock.get_item_details import get_price_list_rate_for def execute(filters=None): @@ -50,6 +50,42 @@ def get_columns(filters=None): ] +def fetch_item_prices( + customer: str = None, price_list: str = None, selling_price_list: str = None, items: list = None +): + price_list_map = frappe._dict() + ip = qb.DocType("Item Price") + and_conditions = [] + or_conditions = [] + if items: + and_conditions.append(ip.item_code.isin([x.item_code for x in items])) + and_conditions.append(ip.selling == True) + + or_conditions.append(ip.customer == None) + or_conditions.append(ip.price_list == None) + + if customer: + or_conditions.append(ip.customer == customer) + + if price_list: + or_conditions.append(ip.price_list == price_list) + + if selling_price_list: + or_conditions.append(ip.price_list == selling_price_list) + + res = ( + qb.from_(ip) + .select(ip.item_code, ip.price_list, ip.price_list_rate) + .where(Criterion.all(and_conditions)) + .where(Criterion.any(or_conditions)) + .run(as_dict=True) + ) + for x in res: + price_list_map.update({(x.item_code, x.price_list): x.price_list_rate}) + + return price_list_map + + def get_data(filters=None): data = [] customer_details = get_customer_details(filters) @@ -59,9 +95,17 @@ def get_data(filters=None): "Bin", fields=["item_code", "sum(actual_qty) AS available"], group_by="item_code" ) item_stock_map = {item.item_code: item.available for item in item_stock_map} + price_list_map = fetch_item_prices( + customer_details.customer, + customer_details.price_list, + customer_details.selling_price_list, + items, + ) for item in items: - price_list_rate = get_price_list_rate_for(customer_details, item.item_code) or 0.0 + price_list_rate = price_list_map.get( + (item.item_code, customer_details.price_list or customer_details.selling_price_list), 0.0 + ) available_stock = item_stock_map.get(item.item_code) data.append( From 1eee203f7fb840e3aeb23b5a07231764cb2afef3 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 26 Dec 2023 14:45:54 +0530 Subject: [PATCH 044/675] fix(ux): make PR and PI Item rate field readonly based on `Maintain Same Rate` (backport #38942) (#38943) * fix(ux): make PI Item rate field editable (cherry picked from commit eb5bb9f9a99e54be71c7ae5e24235b5b44692444) * fix(ux): make PI Item rate field readonly based on `Maintain Same Rate` (cherry picked from commit cb9114442b936f03c9c975900282b3b9db62e453) * fix(ux): make PR Item rate field readonly based on `Maintain Same Rate` (cherry picked from commit b1ba2103323c8bfe066c288b4c285cf1f3c5b20b) --------- Co-authored-by: s-aga-r --- .../doctype/purchase_invoice/purchase_invoice.js | 12 ++++++++++++ .../purchase_invoice_item.json | 3 +-- .../doctype/purchase_receipt/purchase_receipt.js | 14 ++++++++++++++ .../purchase_receipt_item.json | 3 +-- 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index c2aa1d936e5..3b77614607a 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -164,6 +164,18 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying. } }) }, __("Get Items From")); + + if (!this.frm.doc.is_return) { + frappe.db.get_single_value("Buying Settings", "maintain_same_rate").then((value) => { + if (value) { + this.frm.doc.items.forEach((item) => { + this.frm.fields_dict.items.grid.update_docfield_property( + "rate", "read_only", (item.purchase_receipt && item.pr_detail) + ); + }); + } + }); + } } this.frm.toggle_reqd("supplier_warehouse", this.frm.doc.is_subcontracted); 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 bc56132c60f..3d59d288e4d 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -286,7 +286,6 @@ "oldfieldname": "import_rate", "oldfieldtype": "Currency", "options": "currency", - "read_only_depends_on": "eval: (!parent.is_return && doc.purchase_receipt && doc.pr_detail)", "reqd": 1 }, { @@ -894,7 +893,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2023-11-30 16:26:05.629780", + "modified": "2023-12-25 22:00:28.043555", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js index 8966fbcbb3c..314b65ee4f8 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js @@ -78,6 +78,20 @@ frappe.ui.form.on("Purchase Receipt", { }, __('Create')); } + if (frm.doc.docstatus === 0) { + if (!frm.doc.is_return) { + frappe.db.get_single_value("Buying Settings", "maintain_same_rate").then((value) => { + if (value) { + frm.doc.items.forEach((item) => { + frm.fields_dict.items.grid.update_docfield_property( + "rate", "read_only", (item.purchase_order && item.purchase_order_item) + ); + }); + } + }); + } + } + frm.events.add_custom_buttons(frm); }, diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index 6d89e9897af..1a2bb8e4c8e 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -352,7 +352,6 @@ "oldfieldtype": "Currency", "options": "currency", "print_width": "100px", - "read_only_depends_on": "eval: (!parent.is_return && doc.purchase_order && doc.purchase_order_item)", "width": "100px" }, { @@ -1055,7 +1054,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2023-11-30 16:12:02.364608", + "modified": "2023-12-25 22:32:09.801965", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", From f6f2712db0cd8cec3940e7e31678d909307887b9 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 26 Dec 2023 21:59:09 +0530 Subject: [PATCH 045/675] fix: min order qty optional in production plan (backport #38956) (#38957) * fix: min order qty optional in production plan (#38956) * fix: min order qty optional in production plan * fix: test cases (cherry picked from commit b09c9354fb621c4283d6ebde91f3d061ea88f7f6) # Conflicts: # erpnext/manufacturing/doctype/bom/bom.json # erpnext/manufacturing/doctype/production_plan/production_plan.py * chore: fix conflicts * chore: fix conflicts --------- Co-authored-by: rohitwaghchaure --- erpnext/manufacturing/doctype/bom/bom.json | 5 ++-- .../production_plan/production_plan.js | 2 ++ .../production_plan/production_plan.json | 9 +++++++- .../production_plan/production_plan.py | 18 +++++++++++++-- .../production_plan/test_production_plan.py | 23 +++++++++++++++++++ 5 files changed, 52 insertions(+), 5 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.json b/erpnext/manufacturing/doctype/bom/bom.json index d02402299e3..28c33e47fb9 100644 --- a/erpnext/manufacturing/doctype/bom/bom.json +++ b/erpnext/manufacturing/doctype/bom/bom.json @@ -214,6 +214,7 @@ "options": "\nWork Order\nJob Card" }, { + "default": "1", "fieldname": "conversion_rate", "fieldtype": "Float", "label": "Conversion Rate", @@ -606,7 +607,7 @@ "image_field": "image", "is_submittable": 1, "links": [], - "modified": "2023-04-06 12:47:58.514795", + "modified": "2023-12-26 19:34:08.159312", "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM", @@ -645,4 +646,4 @@ "sort_order": "DESC", "states": [], "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js index dd102b0fae0..cd92263543b 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.js +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js @@ -305,6 +305,8 @@ frappe.ui.form.on('Production Plan', { frappe.throw(__("Select the Warehouse")); } + frm.set_value("consider_minimum_order_qty", 0); + if (frm.doc.ignore_existing_ordered_qty) { frm.events.get_items_for_material_requests(frm); } else { diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json index 49386c4ebc4..257b60c4869 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.json +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json @@ -48,6 +48,7 @@ "material_request_planning", "include_non_stock_items", "include_subcontracted_items", + "consider_minimum_order_qty", "include_safety_stock", "ignore_existing_ordered_qty", "column_break_25", @@ -423,13 +424,19 @@ "fieldtype": "Link", "label": "Sub Assembly Warehouse", "options": "Warehouse" + }, + { + "default": "0", + "fieldname": "consider_minimum_order_qty", + "fieldtype": "Check", + "label": "Consider Minimum Order Qty" } ], "icon": "fa fa-calendar", "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2023-11-03 14:08:11.928027", + "modified": "2023-12-26 16:31:13.740777", "modified_by": "Administrator", "module": "Manufacturing", "name": "Production Plan", diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index b546594a4ed..ee2e58bfb46 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -1135,7 +1135,14 @@ def get_subitems( def get_material_request_items( - row, sales_order, company, ignore_existing_ordered_qty, include_safety_stock, warehouse, bin_dict + doc, + row, + sales_order, + company, + ignore_existing_ordered_qty, + include_safety_stock, + warehouse, + bin_dict, ): total_qty = row["qty"] @@ -1144,8 +1151,14 @@ def get_material_request_items( required_qty = total_qty elif total_qty > bin_dict.get("projected_qty", 0): required_qty = total_qty - bin_dict.get("projected_qty", 0) - if required_qty > 0 and required_qty < row["min_order_qty"]: + + if ( + doc.get("consider_minimum_order_qty") + and required_qty > 0 + and required_qty < row["min_order_qty"] + ): required_qty = row["min_order_qty"] + item_group_defaults = get_item_group_defaults(row.item_code, company) if not row["purchase_uom"]: @@ -1483,6 +1496,7 @@ def get_items_for_material_requests(doc, warehouses=None, get_parent_warehouse_d if details.qty > 0: items = get_material_request_items( + doc, details, sales_order, company, diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py index d546306f080..8c8e6efd92d 100644 --- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py @@ -1488,6 +1488,29 @@ class TestProductionPlan(FrappeTestCase): after_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan")) self.assertAlmostEqual(after_qty, before_qty) + def test_min_order_qty_in_pp(self): + from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse + from erpnext.stock.utils import get_or_make_bin + + fg_item = make_item(properties={"is_stock_item": 1}).name + rm_item = make_item(properties={"is_stock_item": 1, "min_order_qty": 1000}).name + + rm_warehouse = create_warehouse("RM Warehouse", company="_Test Company") + + make_bom(item=fg_item, raw_materials=[rm_item], source_warehouse="_Test Warehouse - _TC") + + pln = create_production_plan(item_code=fg_item, planned_qty=10, do_not_submit=1) + + pln.for_warehouse = rm_warehouse + mr_items = get_items_for_material_requests(pln.as_dict()) + for d in mr_items: + self.assertEqual(d.get("quantity"), 10.0) + + pln.consider_minimum_order_qty = 1 + mr_items = get_items_for_material_requests(pln.as_dict()) + for d in mr_items: + self.assertEqual(d.get("quantity"), 1000.0) + def create_production_plan(**args): """ From 6e479f918d900175bac25bb878d2ebed58c52cf8 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 27 Dec 2023 08:40:26 +0000 Subject: [PATCH 046/675] chore(release): Bumped to Version 14.57.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # [14.57.0](https://github.com/frappe/erpnext/compare/v14.56.0...v14.57.0) (2023-12-27) ### Bug Fixes * allow to set rate manually for service item in BOM ([#38880](https://github.com/frappe/erpnext/issues/38880)) ([c2f692a](https://github.com/frappe/erpnext/commit/c2f692a4e4f3dd5089fe4949c6cd74574282fdb1)) * display all item rate stop messages ([226d0e0](https://github.com/frappe/erpnext/commit/226d0e0196f8c5defddc600348364d39fa703f27)) * do not reset the basic rate for the material receipt stock entry ([#38896](https://github.com/frappe/erpnext/issues/38896)) ([98bfcc4](https://github.com/frappe/erpnext/commit/98bfcc4c758b61f714c53ea0f00246731a30fdaf)) * first name error on customer creation ([#38927](https://github.com/frappe/erpnext/issues/38927)) ([28f052d](https://github.com/frappe/erpnext/commit/28f052d586c2781136b2e6497b7f3731a5b5227e)) * german translations for Lost Quotations ([#38435](https://github.com/frappe/erpnext/issues/38435)) ([81b5e6c](https://github.com/frappe/erpnext/commit/81b5e6c5f13da686d4472168b731e7e284a4e9c2)) * groups for current accounts in German CoAs ([39aa36e](https://github.com/frappe/erpnext/commit/39aa36e44b66123443c66e3503c1d6bb8c277447)) * incorrect price list in customer-wise item price report ([7577706](https://github.com/frappe/erpnext/commit/7577706a9dc0a050cc3ed401f44f15a528b8d573)) * min order qty optional in production plan (backport [#38956](https://github.com/frappe/erpnext/issues/38956)) ([#38957](https://github.com/frappe/erpnext/issues/38957)) ([f6f2712](https://github.com/frappe/erpnext/commit/f6f2712db0cd8cec3940e7e31678d909307887b9)) * remove rows with zero consumed qty ([f2f4100](https://github.com/frappe/erpnext/commit/f2f410093aa688d004637cb3a7a0052498351d8e)) * reposting not fixing valuation rate for sales return using movin… ([#38895](https://github.com/frappe/erpnext/issues/38895)) ([3a668bb](https://github.com/frappe/erpnext/commit/3a668bbe9694fdd6e8265869c6943e42f889ac41)) * reset the incoming rate on changing of the warehouse ([#38909](https://github.com/frappe/erpnext/issues/38909)) ([2770ca1](https://github.com/frappe/erpnext/commit/2770ca1b659a3217e249b60155434052d0a62730)) * skip jvs against bank accounts ([98198d3](https://github.com/frappe/erpnext/commit/98198d35eaa8909396a6ced5ad274931b7f90dcf)) * **test:** expect account currency when party account is specified. ([30cb218](https://github.com/frappe/erpnext/commit/30cb2186380f0eafce20fcdbcaf3b209dfd6b46b)) * unset discount amount from coupon discounts ([87e8dd0](https://github.com/frappe/erpnext/commit/87e8dd0d63068b93c713c4636e97cc3edda411fd)) * use party account currency when party account is specified ([d9e0d55](https://github.com/frappe/erpnext/commit/d9e0d55b88e428f80a9e84eb4309f2c153b7028f)) * **ux:** make PR and PI Item rate field readonly based on `Maintain Same Rate` (backport [#38942](https://github.com/frappe/erpnext/issues/38942)) ([#38943](https://github.com/frappe/erpnext/issues/38943)) ([1eee203](https://github.com/frappe/erpnext/commit/1eee203f7fb840e3aeb23b5a07231764cb2afef3)) ### Features * in_party_currency option for AR/AP reports ([3c717db](https://github.com/frappe/erpnext/commit/3c717db3fd2d88f3fbad053c0b71b9a99b62c9b6)) * total_asset_cost field (backport [#38879](https://github.com/frappe/erpnext/issues/38879)) ([#38886](https://github.com/frappe/erpnext/issues/38886)) ([8169c7d](https://github.com/frappe/erpnext/commit/8169c7de0d06ce031f44c30016d1f17744655bf5)) ### Performance Improvements * Drop unused/duplicate/sub-optimal indexes (backport [#38884](https://github.com/frappe/erpnext/issues/38884)) ([#38912](https://github.com/frappe/erpnext/issues/38912)) ([b254a72](https://github.com/frappe/erpnext/commit/b254a72d41bd701cf93752771681b0f55e93a6f7)) * use estimated rows instead of actual rows (backport [#38830](https://github.com/frappe/erpnext/issues/38830)) ([#38875](https://github.com/frappe/erpnext/issues/38875)) ([4d1ccd9](https://github.com/frappe/erpnext/commit/4d1ccd9e27302d161e899fc6b6b02f33a1743844)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 85dbe99af84..8386b715981 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.56.0" +__version__ = "14.57.0" def get_default_company(user=None): From 92d61eb19efd937517b604daaca0cce0115cf3f6 Mon Sep 17 00:00:00 2001 From: VihangT <151990347+VihangT@users.noreply.github.com> Date: Wed, 27 Dec 2023 16:11:14 +0530 Subject: [PATCH 047/675] fix : correct logic for overlap error (#38798) * fix : correct logic for overlap error * fix : Updated as per lintels * fix : changes as per linters * fix : correct logic for overlap error fixing overlap error logic with taking care of sequential time job cards in overlap job card list Added Provision if time_logs list is empty --- .../doctype/job_card/job_card.py | 58 ++++++++++--------- 1 file changed, 31 insertions(+), 27 deletions(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index 455aa7e5766..f47858ba9ff 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -227,35 +227,39 @@ class JobCard(Document): def has_overlap(self, production_capacity, time_logs): overlap = False - if production_capacity == 1 and len(time_logs) > 0: + if production_capacity == 1 and len(time_logs) >= 1: return True + if not len(time_logs): + return False - # Check overlap exists or not between the overlapping time logs with the current Job Card - for row in time_logs: - count = 1 - for next_row in time_logs: - if row.name == next_row.name: - continue - - if ( - ( - get_datetime(next_row.from_time) >= get_datetime(row.from_time) - and get_datetime(next_row.from_time) <= get_datetime(row.to_time) - ) - or ( - get_datetime(next_row.to_time) >= get_datetime(row.from_time) - and get_datetime(next_row.to_time) <= get_datetime(row.to_time) - ) - or ( - get_datetime(next_row.from_time) <= get_datetime(row.from_time) - and get_datetime(next_row.to_time) >= get_datetime(row.to_time) - ) - ): - count += 1 - - if count > production_capacity: - return True - + # sorting overlapping job cards as per from_time + time_logs = sorted(time_logs, key=lambda x: x.get("from_time")) + # alloted_capacity has key number starting from 1. Key number will increment by 1 if non sequential job card found + # if key number reaches/crosses to production_capacity means capacity is full and overlap error generated + # this will store last to_time of sequential job cards + alloted_capacity = {1: time_logs[0]["to_time"]} + # flag for sequential Job card found + sequential_job_card_found = False + for i in range(1, len(time_logs)): + # scanning for all Existing keys + for key in alloted_capacity.keys(): + # if current Job Card from time is greater than last to_time in that key means these job card are sequential + if alloted_capacity[key] <= time_logs[i]["from_time"]: + # So update key's value with last to_time + alloted_capacity[key] = time_logs[i]["to_time"] + # flag is true as we get sequential Job Card for that key + sequential_job_card_found = True + # Immediately break so that job card to time is not added with any other key except this + break + # if sequential job card not found above means it is overlapping so increment key number to alloted_capacity + if not sequential_job_card_found: + # increment key number + key = key + 1 + # for that key last to time is assigned. + alloted_capacity[key] = time_logs[i]["to_time"] + if len(alloted_capacity) >= production_capacity: + # if number of keys greater or equal to production caoacity means full capacity is utilized and we should throw overlap error + return True return overlap def get_workstation_based_on_available_slot(self, existing) -> Optional[str]: From c74e6aaebb0b7900a92c53a53740c4f4d3540a1b Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 28 Dec 2023 10:03:26 +0530 Subject: [PATCH 048/675] feat: Merge taxes from mapped docs (#38346) * feat: Merge taxes from mapped docs (#38346) * feat: Merge taxes from mapped docs * chore: ci failures (cherry picked from commit 9b1c22250f1da37a563ba0885fbbd732b176816c) * chore: ci failures --------- Co-authored-by: Deepesh Garg --- .../doctype/journal_entry/test_records.json | 1 - erpnext/public/js/utils.js | 12 +++--- .../purchase_receipt/purchase_receipt.py | 43 ++++++++++++++++++- 3 files changed, 48 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/test_records.json b/erpnext/accounts/doctype/journal_entry/test_records.json index 5077305cf22..73777d4008c 100644 --- a/erpnext/accounts/doctype/journal_entry/test_records.json +++ b/erpnext/accounts/doctype/journal_entry/test_records.json @@ -81,7 +81,6 @@ }, { "account": "Sales - _TC", - "cost_center": "_Test Cost Center - _TC", "credit_in_account_currency": 400.0, "debit_in_account_currency": 0.0, "doctype": "Journal Entry Account", diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 4c7b2534fcc..35c3828d5b9 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -737,7 +737,7 @@ erpnext.utils.map_current_doc = function(opts) { }, callback: function(r) { if(!r.exc) { - var doc = frappe.model.sync(r.message); + frappe.model.sync(r.message); cur_frm.dirty(); cur_frm.refresh(); } @@ -764,6 +764,11 @@ erpnext.utils.map_current_doc = function(opts) { target: opts.target, date_field: opts.date_field || undefined, setters: opts.setters, + data_fields: [{ + fieldname: 'merge_taxes', + fieldtype: 'Check', + label: __('Merge taxes from multiple documents'), + }], get_query: opts.get_query, add_filters_group: 1, allow_child_item_selection: opts.allow_child_item_selection, @@ -777,10 +782,7 @@ erpnext.utils.map_current_doc = function(opts) { return; } opts.source_name = values; - if (opts.allow_child_item_selection) { - // args contains filtered child docnames - opts.args = args; - } + opts.args = args; d.dialog.hide(); _map(); }, diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index f35dc136990..032e2fec822 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -970,8 +970,39 @@ def get_item_wise_returned_qty(pr_doc): ) +def merge_taxes(source_taxes, target_doc): + from erpnext.accounts.doctype.pos_invoice_merge_log.pos_invoice_merge_log import ( + update_item_wise_tax_detail, + ) + + existing_taxes = target_doc.get("taxes") or [] + idx = 1 + for tax in source_taxes: + found = False + for t in existing_taxes: + if t.account_head == tax.account_head and t.cost_center == tax.cost_center: + t.tax_amount = flt(t.tax_amount) + flt(tax.tax_amount_after_discount_amount) + t.base_tax_amount = flt(t.base_tax_amount) + flt(tax.base_tax_amount_after_discount_amount) + update_item_wise_tax_detail(t, tax) + found = True + + if not found: + tax.charge_type = "Actual" + tax.idx = idx + idx += 1 + tax.included_in_print_rate = 0 + tax.dont_recompute_tax = 1 + tax.row_id = "" + tax.tax_amount = tax.tax_amount_after_discount_amount + tax.base_tax_amount = tax.base_tax_amount_after_discount_amount + tax.item_wise_tax_detail = tax.item_wise_tax_detail + existing_taxes.append(tax) + + target_doc.set("taxes", existing_taxes) + + @frappe.whitelist() -def make_purchase_invoice(source_name, target_doc=None): +def make_purchase_invoice(source_name, target_doc=None, args=None): from erpnext.accounts.party import get_payment_terms_template doc = frappe.get_doc("Purchase Receipt", source_name) @@ -988,6 +1019,10 @@ def make_purchase_invoice(source_name, target_doc=None): ) doc.run_method("onload") doc.run_method("set_missing_values") + + if args and args.get("merge_taxes"): + merge_taxes(source.get("taxes") or [], doc) + doc.run_method("calculate_taxes_and_totals") doc.set_payment_schedule() @@ -1051,7 +1086,11 @@ def make_purchase_invoice(source_name, target_doc=None): if not doc.get("is_return") else get_pending_qty(d)[0] > 0, }, - "Purchase Taxes and Charges": {"doctype": "Purchase Taxes and Charges", "add_if_empty": True}, + "Purchase Taxes and Charges": { + "doctype": "Purchase Taxes and Charges", + "add_if_empty": True, + "ignore": args.get("merge_taxes") if args else 0, + }, }, target_doc, set_missing_values, From 7ad42ec95749c0055a0dc6072024b497a1545d80 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 28 Dec 2023 11:59:47 +0530 Subject: [PATCH 049/675] fix: use `Stock Qty` while getting `POS Reserved Qty` (backport #38962) (#38982) fix: use `Stock Qty` while getting `POS Reserved Qty` (cherry picked from commit 722310641700ce767cc2dd132e5ee41610d2e525) Co-authored-by: s-aga-r --- 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 df5e221e195..2405aba38a3 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -705,7 +705,7 @@ def get_pos_reserved_qty(item_code, warehouse): reserved_qty = ( frappe.qb.from_(p_inv) .from_(p_item) - .select(Sum(p_item.qty).as_("qty")) + .select(Sum(p_item.stock_qty).as_("stock_qty")) .where( (p_inv.name == p_item.parent) & (IfNull(p_inv.consolidated_invoice, "") == "") @@ -716,7 +716,7 @@ def get_pos_reserved_qty(item_code, warehouse): ) ).run(as_dict=True) - return reserved_qty[0].qty or 0 if reserved_qty else 0 + return flt(reserved_qty[0].stock_qty) if reserved_qty else 0 @frappe.whitelist() From e4d6df39ff273402096c32ed0a1bc359d26bfee2 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Thu, 28 Dec 2023 12:54:31 +0530 Subject: [PATCH 050/675] fix(Hierarchy Chart): check if company is set before loading children (#38985) * fix(Org Chart): check if company is set before loading children * refactor: avoid assigning undefined values, use empty strings, null instead * fix: returning to org chart view from employee master does not render chart - the check for company field is truthy from the employee doctype, so it does not render company field again. Explicitly check for company field on the same page * fix(Org Chart Mobile): check if company is set before loading children --- .../hierarchy_chart_desktop.js | 26 +++++++++++-------- .../hierarchy_chart/hierarchy_chart_mobile.js | 26 +++++++++++-------- 2 files changed, 30 insertions(+), 22 deletions(-) diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index a585aa614fb..516c98a143d 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js @@ -68,7 +68,7 @@ erpnext.HierarchyChart = class { show() { this.setup_actions(); - if ($(`[data-fieldname="company"]`).length) return; + if (this.page.main.find('[data-fieldname="company"]').length) return; let me = this; let company = this.page.add_field({ @@ -80,7 +80,7 @@ erpnext.HierarchyChart = class { only_select: true, reqd: 1, change: () => { - me.company = undefined; + me.company = ''; $('#hierarchy-chart-wrapper').remove(); if (company.get_value()) { @@ -219,8 +219,8 @@ erpnext.HierarchyChart = class { } }).then(r => { if (r.message.length) { - let expand_node = undefined; - let node = undefined; + let expand_node; + let node; $.each(r.message, (_i, data) => { if ($(`[id="${data.id}"]`).length) @@ -229,7 +229,7 @@ erpnext.HierarchyChart = class { node = new me.Node({ id: data.id, parent: $('
  • ').appendTo(me.$hierarchy.find('.node-children')), - parent_id: undefined, + parent_id: '', image: data.image, name: data.name, title: data.title, @@ -286,6 +286,10 @@ erpnext.HierarchyChart = class { } load_children(node, deep=false) { + if (!this.company) { + frappe.throw(__('Please select a company first.')); + } + if (!deep) { frappe.run_serially([ () => this.get_child_nodes(node.id), @@ -367,8 +371,8 @@ erpnext.HierarchyChart = class { } render_children_of_all_nodes(data_list) { - let entry = undefined; - let node = undefined; + let entry; + let node; while (data_list.length) { // to avoid overlapping connectors @@ -423,7 +427,7 @@ erpnext.HierarchyChart = class { title: data.title, expandable: data.expandable, connections: data.connections, - children: undefined + children: null, }); } @@ -519,7 +523,7 @@ erpnext.HierarchyChart = class { collapse_previous_level_nodes(node) { let node_parent = $(`[id="${node.parent_id}"]`); let previous_level_nodes = node_parent.parent().parent().children('li'); - let node_card = undefined; + let node_card; previous_level_nodes.each(function() { node_card = $(this).find('.node-card'); @@ -582,12 +586,12 @@ erpnext.HierarchyChart = class { level.nextAll('li').remove(); let nodes = level.find('.node-card'); - let node_object = undefined; + let node_object; $.each(nodes, (_i, element) => { node_object = this.nodes[element.id]; node_object.expanded = 0; - node_object.$children = undefined; + node_object.$children = null; }); nodes.removeClass('collapsed active-path'); diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js index 52236e7df96..512ea508a9d 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js @@ -59,8 +59,8 @@ erpnext.HierarchyChartMobile = class { } show() { + if (this.page.main.find('[data-fieldname="company"]').length) return; let me = this; - if ($(`[data-fieldname="company"]`).length) return; let company = this.page.add_field({ fieldtype: 'Link', @@ -71,7 +71,7 @@ erpnext.HierarchyChartMobile = class { only_select: true, reqd: 1, change: () => { - me.company = undefined; + me.company = ''; if (company.get_value() && me.company != company.get_value()) { me.company = company.get_value(); @@ -154,7 +154,7 @@ erpnext.HierarchyChartMobile = class { return new me.Node({ id: data.id, parent: root_level, - parent_id: undefined, + parent_id: '', image: data.image, name: data.name, title: data.title, @@ -174,7 +174,7 @@ erpnext.HierarchyChartMobile = class { if (this.$sibling_group) { const sibling_parent = this.$sibling_group.find('.node-group').attr('data-parent'); - if (node.parent_id !== undefined && node.parent_id != sibling_parent) + if (node.parent_id != '' && node.parent_id != sibling_parent) this.$sibling_group.empty(); } @@ -225,6 +225,10 @@ erpnext.HierarchyChartMobile = class { } load_children(node) { + if (!this.company) { + frappe.throw(__('Please select a company first')); + } + frappe.run_serially([ () => this.get_child_nodes(node.id), (child_nodes) => this.render_child_nodes(node, child_nodes) @@ -281,7 +285,7 @@ erpnext.HierarchyChartMobile = class { title: data.title, expandable: data.expandable, connections: data.connections, - children: undefined + children: null }); } @@ -291,7 +295,7 @@ erpnext.HierarchyChartMobile = class { const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); - let connector = undefined; + let connector = null; if ($(`[id="${parent_id}"]`).hasClass('active')) { connector = this.get_connector_for_active_node(parent_node, child_node); @@ -377,7 +381,7 @@ erpnext.HierarchyChartMobile = class { let node_element = $(`[id="${node.id}"]`); node_element.click(function() { - let el = undefined; + let el = null; if (node.is_root) { el = $(this).detach(); @@ -411,7 +415,7 @@ erpnext.HierarchyChartMobile = class { $('.node-group').on('click', function() { let parent = $(this).attr('data-parent'); - if (parent === 'undefined') { + if (parent == '') { me.setup_hierarchy(); me.render_root_nodes(); } else { @@ -427,7 +431,7 @@ erpnext.HierarchyChartMobile = class { let node_object = this.nodes[node.id]; node_object.expanded = 0; - node_object.$children = undefined; + node_object.$children = null; this.nodes[node.id] = node_object; } @@ -484,7 +488,7 @@ erpnext.HierarchyChartMobile = class { node.removeClass('active-child active-path'); node_object.expanded = 0; - node_object.$children = undefined; + node_object.$children = null; this.nodes[node.id] = node_object; // show parent's siblings and expand parent node @@ -523,7 +527,7 @@ erpnext.HierarchyChartMobile = class { current_node.removeClass('active-child active-path'); node_object.expanded = 0; - node_object.$children = undefined; + node_object.$children = null; level.empty().append(current_node); } From 29d383ad8b76179d977be9b78c891738fd9a3364 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 28 Dec 2023 14:22:23 +0530 Subject: [PATCH 051/675] fix: remove bad defaults (backport #38986) (#38987) fix: remove bad defaults (#38986) Child tables can't have a default. (cherry picked from commit b71b0d599775cd445acb555f7df6f849056a7a9b) Co-authored-by: Ankush Menat --- .../cashier_closing/cashier_closing.json | 371 ++---------------- .../selling/doctype/customer/customer.json | 3 +- erpnext/stock/tests/test_valuation.py | 1 - 3 files changed, 34 insertions(+), 341 deletions(-) diff --git a/erpnext/accounts/doctype/cashier_closing/cashier_closing.json b/erpnext/accounts/doctype/cashier_closing/cashier_closing.json index 1b38f0d36d7..051b44b5868 100644 --- a/erpnext/accounts/doctype/cashier_closing/cashier_closing.json +++ b/erpnext/accounts/doctype/cashier_closing/cashier_closing.json @@ -1,457 +1,152 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, + "actions": [], "autoname": "naming_series:", - "beta": 0, "creation": "2018-06-18 16:51:49.994750", - "custom": 0, - "docstatus": 0, "doctype": "DocType", - "document_type": "", "editable_grid": 1, "engine": "InnoDB", + "field_order": [ + "naming_series", + "user", + "date", + "from_time", + "time", + "expense", + "custody", + "returns", + "outstanding_amount", + "payments", + "net_amount", + "amended_from" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "POS-CLO-", "fieldname": "naming_series", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, "in_filter": 1, "in_global_search": 1, - "in_list_view": 0, "in_standard_filter": 1, "label": "Series", - "length": 0, - "no_copy": 0, "options": "POS-CLO-", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "user", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, "in_filter": 1, - "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 1, "label": "User", - "length": 0, - "no_copy": 0, "options": "User", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "Today", "fieldname": "date", "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, "in_filter": 1, - "in_global_search": 0, - "in_list_view": 0, "in_standard_filter": 1, "label": "Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "from_time", "fieldtype": "Time", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, "in_filter": 1, - "in_global_search": 0, - "in_list_view": 0, "in_standard_filter": 1, "label": "From Time", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", "fieldname": "time", "fieldtype": "Time", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, "in_filter": 1, - "in_global_search": 0, - "in_list_view": 0, "in_standard_filter": 1, "label": "To Time", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "0.00", "fieldname": "expense", "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, "in_filter": 1, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Expense", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Expense" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "0.00", "fieldname": "custody", "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, "in_filter": 1, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Custody", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Custody" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "0.00", "fieldname": "returns", "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, "in_filter": 1, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Returns", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "2", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "precision": "2" }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "default": "0.00", "fieldname": "outstanding_amount", "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Outstanding Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "0.0", "fieldname": "payments", "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, "in_filter": 1, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Payments", - "length": 0, - "no_copy": 0, - "options": "Cashier Closing Payments", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Cashier Closing Payments" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "net_amount", "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, "in_filter": 1, - "in_global_search": 0, "in_list_view": 1, "in_standard_filter": 1, "label": "Net Amount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "amended_from", "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Amended From", - "length": 0, "no_copy": 1, "options": "Cashier Closing", - "permlevel": 0, "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2019-02-19 08:35:24.157327", + "links": [], + "modified": "2023-12-28 13:15:46.858427", "modified_by": "Administrator", "module": "Accounts", "name": "Cashier Closing", - "name_case": "", + "naming_rule": "By \"Naming Series\" field", "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "System Manager", - "set_user_permissions": 0, "share": 1, "submit": 1, "write": 1 } ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 -} + "states": [], + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json index c3c7766b9f5..9844e0902c4 100644 --- a/erpnext/selling/doctype/customer/customer.json +++ b/erpnext/selling/doctype/customer/customer.json @@ -447,7 +447,6 @@ "report_hide": 1 }, { - "default": "0", "fieldname": "credit_limits", "fieldtype": "Table", "label": "Credit Limit", @@ -568,7 +567,7 @@ "link_fieldname": "party" } ], - "modified": "2023-10-19 16:56:27.327035", + "modified": "2023-12-28 13:15:36.298369", "modified_by": "Administrator", "module": "Selling", "name": "Customer", diff --git a/erpnext/stock/tests/test_valuation.py b/erpnext/stock/tests/test_valuation.py index 05f153b4a0c..4d8990ae40b 100644 --- a/erpnext/stock/tests/test_valuation.py +++ b/erpnext/stock/tests/test_valuation.py @@ -195,7 +195,6 @@ class TestFIFOValuation(unittest.TestCase): total_value -= sum(q * r for q, r in consumed) self.assertTotalQty(total_qty) self.assertTotalValue(total_value) - self.assertGreaterEqual(total_value, 0) class TestLIFOValuation(unittest.TestCase): From a2cba1bf237dd26e9cf9be7a4d903afc2441cdf6 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 28 Dec 2023 17:53:27 +0530 Subject: [PATCH 052/675] fix: Opening balance in bank reconciliation tool (#38977) fix: Opening balance in bank reconciliation tool (#38977) (cherry picked from commit bbee9b56377f24109217515360789558566f6aa1) Co-authored-by: Deepesh Garg --- .../bank_reconciliation_tool/bank_reconciliation_tool.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js index d9cf1bb3b34..f2380bde125 100644 --- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js +++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js @@ -137,7 +137,7 @@ frappe.ui.form.on("Bank Reconciliation Tool", { "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_account_balance", args: { bank_account: frm.doc.bank_account, - till_date: frm.doc.bank_statement_from_date, + till_date: frappe.datetime.add_days(frm.doc.bank_statement_from_date, -1) }, callback: (response) => { frm.set_value("account_opening_balance", response.message); From 04fb215ff51a7d36d90d3b3d9f9375035cee496e Mon Sep 17 00:00:00 2001 From: Gursheen Kaur Anand <40693548+GursheenK@users.noreply.github.com> Date: Fri, 29 Dec 2023 13:02:17 +0530 Subject: [PATCH 053/675] feat: Show Ledger view for Purchase & Sales Register (#38801) * feat: fetch payments and journals * refactor: use single qb query for PE and PI * feat: fetch PE along with SI * refactor: move repeating code to common controller * fix: fetch cost center for PE * feat: fetch JV with PE * fix: validate party filter for fetching payments * fix: linting issues * refactor: use qb to fetch PE JV and Inv * refactor: move fn to fetch advance taxes to utils & use qb * fix: filtering through accounting dimensions * chore: remove debugging print statements * fix: modify rows and columns for ledger view * refactor: filter accounting dimensions using qb * fix: show additional table cols from india compliance api call * chore: fix typo * test: purchase register and ledger view * fix: filter by party in opening row calculation * fix: exclude cancelled gl entries for opening balance * chore: change column format for report * fix: check gl entry status using is_cancelled * fix: running balance after sorting * fix: gst itemised registers for india compliance api call * fix: additional query cols for gst itemised registers * fix: additional query cols for sales register * fix: PE in sales register * fix: tax accounts in sales register * fix: Sales/Purchase register showing duplicate records * fix: avoid fetching advance account for party * refactor: remove unnecessary condition * fix: remove checking for advance accounts in payments --------- Co-authored-by: Deepesh Garg --- .../item_wise_purchase_register.py | 3 +- .../item_wise_sales_register.py | 3 +- .../purchase_register/purchase_register.js | 6 + .../purchase_register/purchase_register.py | 504 +++++++++++------ .../test_purchase_register.py | 128 +++++ .../report/sales_register/sales_register.js | 6 + .../report/sales_register/sales_register.py | 509 ++++++++++-------- erpnext/accounts/report/utils.py | 210 +++++++- 8 files changed, 982 insertions(+), 387 deletions(-) create mode 100644 erpnext/accounts/report/purchase_register/test_purchase_register.py 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 ce1a62d0065..5d3d4d74978 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,8 @@ def get_conditions(filters): def get_items(filters, additional_query_columns): conditions = get_conditions(filters) - + if additional_query_columns: + additional_query_columns = "," + ",".join(additional_query_columns) return frappe.db.sql( """ select diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py index 19bb449cd94..ce22d7566c1 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -381,7 +381,8 @@ def get_group_by_conditions(filters, doctype): def get_items(filters, additional_query_columns, additional_conditions=None): conditions = get_conditions(filters, additional_conditions) - + if additional_query_columns: + additional_query_columns = "," + ",".join(additional_query_columns) return frappe.db.sql( """ select diff --git a/erpnext/accounts/report/purchase_register/purchase_register.js b/erpnext/accounts/report/purchase_register/purchase_register.js index aaf76c42997..57cb703baea 100644 --- a/erpnext/accounts/report/purchase_register/purchase_register.js +++ b/erpnext/accounts/report/purchase_register/purchase_register.js @@ -52,6 +52,12 @@ frappe.query_reports["Purchase Register"] = { "label": __("Item Group"), "fieldtype": "Link", "options": "Item Group" + }, + { + "fieldname": "include_payments", + "label": __("Show Ledger View"), + "fieldtype": "Check", + "default": 0 } ] } diff --git a/erpnext/accounts/report/purchase_register/purchase_register.py b/erpnext/accounts/report/purchase_register/purchase_register.py index 69827aca694..4eb135b0487 100644 --- a/erpnext/accounts/report/purchase_register/purchase_register.py +++ b/erpnext/accounts/report/purchase_register/purchase_register.py @@ -4,13 +4,22 @@ import frappe from frappe import _, msgprint -from frappe.utils import flt +from frappe.query_builder.custom import ConstantColumn +from frappe.utils import flt, getdate +from pypika import Order -from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( - get_accounting_dimensions, - get_dimension_with_children, +from erpnext.accounts.party import get_party_account +from erpnext.accounts.report.utils import ( + apply_common_conditions, + get_advance_taxes_and_charges, + get_journal_entries, + get_opening_row, + get_party_details, + get_payment_entries, + get_query_columns, + get_taxes_query, + get_values_for_columns, ) -from erpnext.accounts.report.utils import get_query_columns, get_values_for_columns def execute(filters=None): @@ -21,9 +30,15 @@ def _execute(filters=None, additional_table_columns=None): if not filters: filters = {} + include_payments = filters.get("include_payments") + if filters.get("include_payments") and not filters.get("supplier"): + frappe.throw(_("Please select a supplier for fetching payments.")) invoice_list = get_invoices(filters, get_query_columns(additional_table_columns)) + if filters.get("include_payments"): + invoice_list += get_payments(filters) + columns, expense_accounts, tax_accounts, unrealized_profit_loss_accounts = get_columns( - invoice_list, additional_table_columns + invoice_list, additional_table_columns, include_payments ) if not invoice_list: @@ -33,14 +48,28 @@ def _execute(filters=None, additional_table_columns=None): invoice_expense_map = get_invoice_expense_map(invoice_list) internal_invoice_map = get_internal_invoice_map(invoice_list) invoice_expense_map, invoice_tax_map = get_invoice_tax_map( - invoice_list, invoice_expense_map, expense_accounts + invoice_list, invoice_expense_map, expense_accounts, include_payments ) invoice_po_pr_map = get_invoice_po_pr_map(invoice_list) suppliers = list(set(d.supplier for d in invoice_list)) - supplier_details = get_supplier_details(suppliers) + supplier_details = get_party_details("Supplier", suppliers) company_currency = frappe.get_cached_value("Company", filters.company, "default_currency") + res = [] + if include_payments: + opening_row = get_opening_row( + "Supplier", filters.supplier, getdate(filters.from_date), filters.company + )[0] + res.append( + { + "payable_account": opening_row.account, + "debit": flt(opening_row.debit), + "credit": flt(opening_row.credit), + "balance": flt(opening_row.balance), + } + ) + data = [] for inv in invoice_list: # invoice details @@ -48,24 +77,23 @@ def _execute(filters=None, additional_table_columns=None): purchase_receipt = list(set(invoice_po_pr_map.get(inv.name, {}).get("purchase_receipt", []))) project = list(set(invoice_po_pr_map.get(inv.name, {}).get("project", []))) - row = [ - inv.name, - inv.posting_date, - inv.supplier, - inv.supplier_name, - *get_values_for_columns(additional_table_columns, inv).values(), - supplier_details.get(inv.supplier), # supplier_group - inv.tax_id, - inv.credit_to, - inv.mode_of_payment, - ", ".join(project), - inv.bill_no, - inv.bill_date, - inv.remarks, - ", ".join(purchase_order), - ", ".join(purchase_receipt), - company_currency, - ] + row = { + "voucher_type": inv.doctype, + "voucher_no": inv.name, + "posting_date": inv.posting_date, + "supplier_id": inv.supplier, + "supplier_name": inv.supplier_name, + **get_values_for_columns(additional_table_columns, inv), + "supplier_group": supplier_details.get(inv.supplier).get("supplier_group"), # supplier_group + "tax_id": supplier_details.get(inv.supplier).get("tax_id"), + "payable_account": inv.credit_to, + "mode_of_payment": inv.mode_of_payment, + "project": ", ".join(project) if inv.doctype == "Purchase Invoice" else inv.project, + "remarks": inv.remarks, + "purchase_order": ", ".join(purchase_order), + "purchase_receipt": ", ".join(purchase_receipt), + "currency": company_currency, + } # map expense values base_net_total = 0 @@ -75,14 +103,16 @@ def _execute(filters=None, additional_table_columns=None): else: expense_amount = flt(invoice_expense_map.get(inv.name, {}).get(expense_acc)) base_net_total += expense_amount - row.append(expense_amount) + row.update({frappe.scrub(expense_acc): expense_amount}) # Add amount in unrealized account for account in unrealized_profit_loss_accounts: - row.append(flt(internal_invoice_map.get((inv.name, account)))) + row.update( + {frappe.scrub(account + "_unrealized"): flt(internal_invoice_map.get((inv.name, account)))} + ) # net total - row.append(base_net_total or inv.base_net_total) + row.update({"net_total": base_net_total or inv.base_net_total}) # tax account total_tax = 0 @@ -90,45 +120,190 @@ def _execute(filters=None, additional_table_columns=None): if tax_acc not in expense_accounts: tax_amount = flt(invoice_tax_map.get(inv.name, {}).get(tax_acc)) total_tax += tax_amount - row.append(tax_amount) + row.update({frappe.scrub(tax_acc): tax_amount}) # total tax, grand total, rounded total & outstanding amount - row += [total_tax, inv.base_grand_total, flt(inv.base_grand_total, 0), inv.outstanding_amount] + row.update( + { + "total_tax": total_tax, + "grand_total": inv.base_grand_total, + "rounded_total": inv.base_rounded_total, + "outstanding_amount": inv.outstanding_amount, + } + ) + + if inv.doctype == "Purchase Invoice": + row.update({"debit": inv.base_grand_total, "credit": 0.0}) + else: + row.update({"debit": 0.0, "credit": inv.base_grand_total}) data.append(row) - return columns, data + res += sorted(data, key=lambda x: x["posting_date"]) + + if include_payments: + running_balance = flt(opening_row.balance) + for row in range(1, len(res)): + running_balance += res[row]["debit"] - res[row]["credit"] + res[row].update({"balance": running_balance}) + + return columns, res, None, None, None, include_payments -def get_columns(invoice_list, additional_table_columns): +def get_columns(invoice_list, additional_table_columns, include_payments=False): """return columns based on filters""" columns = [ - _("Invoice") + ":Link/Purchase Invoice:120", - _("Posting Date") + ":Date:80", - _("Supplier Id") + "::120", - _("Supplier Name") + "::120", + { + "label": _("Voucher Type"), + "fieldname": "voucher_type", + "width": 120, + }, + { + "label": _("Voucher"), + "fieldname": "voucher_no", + "fieldtype": "Dynamic Link", + "options": "voucher_type", + "width": 120, + }, + {"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 80}, + { + "label": _("Supplier"), + "fieldname": "supplier_id", + "fieldtype": "Link", + "options": "Supplier", + "width": 120, + }, + {"label": _("Supplier Name"), "fieldname": "supplier_name", "fieldtype": "Data", "width": 120}, ] - if additional_table_columns: + if additional_table_columns and not include_payments: columns += additional_table_columns - columns += [ - _("Supplier Group") + ":Link/Supplier Group:120", - _("Tax Id") + "::80", - _("Payable Account") + ":Link/Account:120", - _("Mode of Payment") + ":Link/Mode of Payment:80", - _("Project") + ":Link/Project:80", - _("Bill No") + "::120", - _("Bill Date") + ":Date:80", - _("Remarks") + "::150", - _("Purchase Order") + ":Link/Purchase Order:100", - _("Purchase Receipt") + ":Link/Purchase Receipt:100", - {"fieldname": "currency", "label": _("Currency"), "fieldtype": "Data", "width": 80}, - ] + if not include_payments: + columns += [ + { + "label": _("Supplier Group"), + "fieldname": "supplier_group", + "fieldtype": "Link", + "options": "Supplier Group", + "width": 120, + }, + {"label": _("Tax Id"), "fieldname": "tax_id", "fieldtype": "Data", "width": 80}, + { + "label": _("Payable Account"), + "fieldname": "payable_account", + "fieldtype": "Link", + "options": "Account", + "width": 100, + }, + { + "label": _("Mode Of Payment"), + "fieldname": "mode_of_payment", + "fieldtype": "Data", + "width": 120, + }, + { + "label": _("Project"), + "fieldname": "project", + "fieldtype": "Link", + "options": "Project", + "width": 80, + }, + {"label": _("Bill No"), "fieldname": "bill_no", "fieldtype": "Data", "width": 120}, + {"label": _("Bill Date"), "fieldname": "bill_date", "fieldtype": "Date", "width": 80}, + { + "label": _("Purchase Order"), + "fieldname": "purchase_order", + "fieldtype": "Link", + "options": "Purchase Order", + "width": 100, + }, + { + "label": _("Purchase Receipt"), + "fieldname": "purchase_receipt", + "fieldtype": "Link", + "options": "Purchase Receipt", + "width": 100, + }, + {"fieldname": "currency", "label": _("Currency"), "fieldtype": "Data", "width": 80}, + ] + else: + columns += [ + { + "fieldname": "payable_account", + "label": _("Payable Account"), + "fieldtype": "Link", + "options": "Account", + "width": 120, + }, + {"fieldname": "debit", "label": _("Debit"), "fieldtype": "Currency", "width": 120}, + {"fieldname": "credit", "label": _("Credit"), "fieldtype": "Currency", "width": 120}, + {"fieldname": "balance", "label": _("Balance"), "fieldtype": "Currency", "width": 120}, + ] + account_columns, accounts = get_account_columns(invoice_list, include_payments) + + columns = ( + columns + + account_columns[0] + + account_columns[1] + + [ + { + "label": _("Net Total"), + "fieldname": "net_total", + "fieldtype": "Currency", + "options": "currency", + "width": 120, + } + ] + + account_columns[2] + + [ + { + "label": _("Total Tax"), + "fieldname": "total_tax", + "fieldtype": "Currency", + "options": "currency", + "width": 120, + } + ] + ) + + if not include_payments: + columns += [ + { + "label": _("Grand Total"), + "fieldname": "grand_total", + "fieldtype": "Currency", + "options": "currency", + "width": 120, + }, + { + "label": _("Rounded Total"), + "fieldname": "rounded_total", + "fieldtype": "Currency", + "options": "currency", + "width": 120, + }, + { + "label": _("Outstanding Amount"), + "fieldname": "outstanding_amount", + "fieldtype": "Currency", + "options": "currency", + "width": 120, + }, + ] + columns += [{"label": _("Remarks"), "fieldname": "remarks", "fieldtype": "Data", "width": 120}] + return columns, accounts[0], accounts[2], accounts[1] + + +def get_account_columns(invoice_list, include_payments): expense_accounts = [] tax_accounts = [] unrealized_profit_loss_accounts = [] + expense_columns = [] + tax_columns = [] + unrealized_profit_loss_account_columns = [] + if invoice_list: expense_accounts = frappe.db.sql_list( """select distinct expense_account @@ -139,15 +314,18 @@ def get_columns(invoice_list, additional_table_columns): tuple([inv.name for inv in invoice_list]), ) - tax_accounts = frappe.db.sql_list( - """select distinct account_head - from `tabPurchase Taxes and Charges` where parenttype = 'Purchase Invoice' - and docstatus = 1 and (account_head is not null and account_head != '') - and category in ('Total', 'Valuation and Total') - and parent in (%s) order by account_head""" - % ", ".join(["%s"] * len(invoice_list)), - tuple(inv.name for inv in invoice_list), + purchase_taxes_query = get_taxes_query( + invoice_list, "Purchase Taxes and Charges", "Purchase Invoice" ) + purchase_tax_accounts = purchase_taxes_query.run(as_dict=True, pluck="account_head") + tax_accounts = purchase_tax_accounts + + if include_payments: + advance_taxes_query = get_taxes_query( + invoice_list, "Advance Taxes and Charges", "Payment Entry" + ) + advance_tax_accounts = advance_taxes_query.run(as_dict=True, pluck="account_head") + tax_accounts = set(tax_accounts + advance_tax_accounts) unrealized_profit_loss_accounts = frappe.db.sql_list( """SELECT distinct unrealized_profit_loss_account @@ -158,108 +336,109 @@ def get_columns(invoice_list, additional_table_columns): tuple(inv.name for inv in invoice_list), ) - expense_columns = [(account + ":Currency/currency:120") for account in expense_accounts] - unrealized_profit_loss_account_columns = [ - (account + ":Currency/currency:120") for account in unrealized_profit_loss_accounts - ] - tax_columns = [ - (account + ":Currency/currency:120") - for account in tax_accounts - if account not in expense_accounts - ] + for account in expense_accounts: + expense_columns.append( + { + "label": account, + "fieldname": frappe.scrub(account), + "fieldtype": "Currency", + "options": "currency", + "width": 120, + } + ) - columns = ( - columns - + expense_columns - + unrealized_profit_loss_account_columns - + [_("Net Total") + ":Currency/currency:120"] - + tax_columns - + [ - _("Total Tax") + ":Currency/currency:120", - _("Grand Total") + ":Currency/currency:120", - _("Rounded Total") + ":Currency/currency:120", - _("Outstanding Amount") + ":Currency/currency:120", - ] - ) + for account in tax_accounts: + if account not in expense_accounts: + tax_columns.append( + { + "label": account, + "fieldname": frappe.scrub(account), + "fieldtype": "Currency", + "options": "currency", + "width": 120, + } + ) - return columns, expense_accounts, tax_accounts, unrealized_profit_loss_accounts + for account in unrealized_profit_loss_accounts: + unrealized_profit_loss_account_columns.append( + { + "label": account, + "fieldname": frappe.scrub(account), + "fieldtype": "Currency", + "options": "currency", + "width": 120, + } + ) + columns = [expense_columns, unrealized_profit_loss_account_columns, tax_columns] + accounts = [expense_accounts, unrealized_profit_loss_accounts, tax_accounts] -def get_conditions(filters): - conditions = "" - - if filters.get("company"): - conditions += " and company=%(company)s" - if filters.get("supplier"): - conditions += " and supplier = %(supplier)s" - - if filters.get("from_date"): - conditions += " and posting_date>=%(from_date)s" - if filters.get("to_date"): - conditions += " and posting_date<=%(to_date)s" - - if filters.get("mode_of_payment"): - conditions += " and ifnull(mode_of_payment, '') = %(mode_of_payment)s" - - if filters.get("cost_center"): - conditions += """ and exists(select name from `tabPurchase Invoice Item` - where parent=`tabPurchase Invoice`.name - and ifnull(`tabPurchase Invoice Item`.cost_center, '') = %(cost_center)s)""" - - if filters.get("warehouse"): - conditions += """ and exists(select name from `tabPurchase Invoice Item` - where parent=`tabPurchase Invoice`.name - and ifnull(`tabPurchase Invoice Item`.warehouse, '') = %(warehouse)s)""" - - if filters.get("item_group"): - conditions += """ and exists(select name from `tabPurchase Invoice Item` - where parent=`tabPurchase Invoice`.name - and ifnull(`tabPurchase Invoice Item`.item_group, '') = %(item_group)s)""" - - accounting_dimensions = get_accounting_dimensions(as_list=False) - - if accounting_dimensions: - common_condition = """ - and exists(select name from `tabPurchase Invoice Item` - where parent=`tabPurchase Invoice`.name - """ - for dimension in accounting_dimensions: - if filters.get(dimension.fieldname): - if frappe.get_cached_value("DocType", dimension.document_type, "is_tree"): - filters[dimension.fieldname] = get_dimension_with_children( - dimension.document_type, filters.get(dimension.fieldname) - ) - - conditions += ( - common_condition - + "and ifnull(`tabPurchase Invoice`.{0}, '') in %({0})s)".format(dimension.fieldname) - ) - else: - conditions += ( - common_condition - + "and ifnull(`tabPurchase Invoice`.{0}, '') in %({0})s)".format(dimension.fieldname) - ) - - return conditions + return columns, accounts def get_invoices(filters, additional_query_columns): - conditions = get_conditions(filters) - return frappe.db.sql( - """ - select - name, posting_date, credit_to, supplier, supplier_name, tax_id, bill_no, bill_date, - remarks, base_net_total, base_grand_total, outstanding_amount, - mode_of_payment {0} - from `tabPurchase Invoice` - where docstatus = 1 {1} - order by posting_date desc, name desc""".format( - additional_query_columns, conditions - ), - filters, - as_dict=1, + pi = frappe.qb.DocType("Purchase Invoice") + query = ( + frappe.qb.from_(pi) + .select( + ConstantColumn("Purchase Invoice").as_("doctype"), + pi.name, + pi.posting_date, + pi.credit_to, + pi.supplier, + pi.supplier_name, + pi.tax_id, + pi.bill_no, + pi.bill_date, + pi.remarks, + pi.base_net_total, + pi.base_grand_total, + pi.base_rounded_total, + pi.outstanding_amount, + pi.mode_of_payment, + ) + .where((pi.docstatus == 1)) + .orderby(pi.posting_date, pi.name, order=Order.desc) ) + if additional_query_columns: + for col in additional_query_columns: + query = query.select(col) + + if filters.get("supplier"): + query = query.where(pi.supplier == filters.supplier) + + query = get_conditions(filters, query, "Purchase Invoice") + + query = apply_common_conditions( + filters, query, doctype="Purchase Invoice", child_doctype="Purchase Invoice Item" + ) + + invoices = query.run(as_dict=True) + return invoices + + +def get_conditions(filters, query, doctype): + parent_doc = frappe.qb.DocType(doctype) + + if filters.get("mode_of_payment"): + query = query.where(parent_doc.mode_of_payment == filters.mode_of_payment) + + return query + + +def get_payments(filters): + args = frappe._dict( + account="credit_to", + account_fieldname="paid_to", + party="supplier", + party_name="supplier_name", + party_account=get_party_account("Supplier", filters.supplier, filters.company), + ) + payment_entries = get_payment_entries(filters, args) + journal_entries = get_journal_entries(filters, args) + return payment_entries + journal_entries + def get_invoice_expense_map(invoice_list): expense_details = frappe.db.sql( @@ -300,7 +479,9 @@ def get_internal_invoice_map(invoice_list): return internal_invoice_map -def get_invoice_tax_map(invoice_list, invoice_expense_map, expense_accounts): +def get_invoice_tax_map( + invoice_list, invoice_expense_map, expense_accounts, include_payments=False +): tax_details = frappe.db.sql( """ select parent, account_head, case add_deduct_tax when "Add" then sum(base_tax_amount_after_discount_amount) @@ -315,6 +496,9 @@ def get_invoice_tax_map(invoice_list, invoice_expense_map, expense_accounts): as_dict=1, ) + if include_payments: + tax_details += get_advance_taxes_and_charges(invoice_list) + invoice_tax_map = {} for d in tax_details: if d.account_head in expense_accounts: @@ -382,17 +566,3 @@ def get_account_details(invoice_list): account_map[acc.name] = acc.parent_account return account_map - - -def get_supplier_details(suppliers): - supplier_details = {} - for supp in frappe.db.sql( - """select name, supplier_group from `tabSupplier` - where name in (%s)""" - % ", ".join(["%s"] * len(suppliers)), - tuple(suppliers), - as_dict=1, - ): - supplier_details.setdefault(supp.name, supp.supplier_group) - - return supplier_details diff --git a/erpnext/accounts/report/purchase_register/test_purchase_register.py b/erpnext/accounts/report/purchase_register/test_purchase_register.py new file mode 100644 index 00000000000..6903662e687 --- /dev/null +++ b/erpnext/accounts/report/purchase_register/test_purchase_register.py @@ -0,0 +1,128 @@ +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors +# MIT License. See license.txt + +import frappe +from frappe.tests.utils import FrappeTestCase +from frappe.utils import add_months, getdate, today + +from erpnext.accounts.report.purchase_register.purchase_register import execute + + +class TestPurchaseRegister(FrappeTestCase): + def test_purchase_register(self): + frappe.db.sql("delete from `tabPurchase Invoice` where company='_Test Company 6'") + frappe.db.sql("delete from `tabGL Entry` where company='_Test Company 6'") + + filters = frappe._dict( + company="_Test Company 6", from_date=add_months(today(), -1), to_date=today() + ) + + pi = make_purchase_invoice() + + report_results = execute(filters) + first_row = frappe._dict(report_results[1][0]) + self.assertEqual(first_row.voucher_type, "Purchase Invoice") + self.assertEqual(first_row.voucher_no, pi.name) + self.assertEqual(first_row.payable_account, "Creditors - _TC6") + self.assertEqual(first_row.net_total, 1000) + self.assertEqual(first_row.total_tax, 100) + self.assertEqual(first_row.grand_total, 1100) + + def test_purchase_register_ledger_view(self): + frappe.db.sql("delete from `tabPurchase Invoice` where company='_Test Company 6'") + frappe.db.sql("delete from `tabGL Entry` where company='_Test Company 6'") + + filters = frappe._dict( + company="_Test Company 6", + from_date=add_months(today(), -1), + to_date=today(), + include_payments=True, + supplier="_Test Supplier", + ) + + pi = make_purchase_invoice() + pe = make_payment_entry() + + report_results = execute(filters) + first_row = frappe._dict(report_results[1][2]) + self.assertEqual(first_row.voucher_type, "Payment Entry") + self.assertEqual(first_row.voucher_no, pe.name) + self.assertEqual(first_row.payable_account, "Creditors - _TC6") + self.assertEqual(first_row.debit, 0) + self.assertEqual(first_row.credit, 600) + self.assertEqual(first_row.balance, 500) + + +def make_purchase_invoice(): + from erpnext.accounts.doctype.account.test_account import create_account + from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center + from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse + + gst_acc = create_account( + account_name="GST", + account_type="Tax", + parent_account="Duties and Taxes - _TC6", + company="_Test Company 6", + account_currency="INR", + ) + create_warehouse(warehouse_name="_Test Warehouse - _TC6", company="_Test Company 6") + create_cost_center(cost_center_name="_Test Cost Center", company="_Test Company 6") + pi = create_purchase_invoice_with_taxes() + pi.submit() + return pi + + +def create_purchase_invoice_with_taxes(): + return frappe.get_doc( + { + "doctype": "Purchase Invoice", + "posting_date": today(), + "supplier": "_Test Supplier", + "company": "_Test Company 6", + "cost_center": "_Test Cost Center - _TC6", + "taxes_and_charges": "", + "currency": "INR", + "credit_to": "Creditors - _TC6", + "items": [ + { + "doctype": "Purchase Invoice Item", + "cost_center": "_Test Cost Center - _TC6", + "item_code": "_Test Item", + "qty": 1, + "rate": 1000, + "expense_account": "Stock Received But Not Billed - _TC6", + } + ], + "taxes": [ + { + "account_head": "GST - _TC6", + "cost_center": "_Test Cost Center - _TC6", + "add_deduct_tax": "Add", + "category": "Valuation and Total", + "charge_type": "Actual", + "description": "Shipping Charges", + "doctype": "Purchase Taxes and Charges", + "parentfield": "taxes", + "rate": 100, + "tax_amount": 100.0, + } + ], + } + ) + + +def make_payment_entry(): + frappe.set_user("Administrator") + from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry + + return create_payment_entry( + company="_Test Company 6", + party_type="Supplier", + party="_Test Supplier", + payment_type="Pay", + paid_from="Cash - _TC6", + paid_to="Creditors - _TC6", + paid_amount=600, + save=1, + submit=1, + ) diff --git a/erpnext/accounts/report/sales_register/sales_register.js b/erpnext/accounts/report/sales_register/sales_register.js index 2c9b01bbaa3..1a41172a970 100644 --- a/erpnext/accounts/report/sales_register/sales_register.js +++ b/erpnext/accounts/report/sales_register/sales_register.js @@ -64,6 +64,12 @@ frappe.query_reports["Sales Register"] = { "label": __("Item Group"), "fieldtype": "Link", "options": "Item Group" + }, + { + "fieldname": "include_payments", + "label": __("Show Ledger View"), + "fieldtype": "Check", + "default": 0 } ] } diff --git a/erpnext/accounts/report/sales_register/sales_register.py b/erpnext/accounts/report/sales_register/sales_register.py index 291c7d976e4..61b1fe2293c 100644 --- a/erpnext/accounts/report/sales_register/sales_register.py +++ b/erpnext/accounts/report/sales_register/sales_register.py @@ -5,13 +5,22 @@ import frappe from frappe import _, msgprint from frappe.model.meta import get_field_precision -from frappe.utils import flt +from frappe.query_builder.custom import ConstantColumn +from frappe.utils import flt, getdate +from pypika import Order -from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( - get_accounting_dimensions, - get_dimension_with_children, +from erpnext.accounts.party import get_party_account +from erpnext.accounts.report.utils import ( + apply_common_conditions, + get_advance_taxes_and_charges, + get_journal_entries, + get_opening_row, + get_party_details, + get_payment_entries, + get_query_columns, + get_taxes_query, + get_values_for_columns, ) -from erpnext.accounts.report.utils import get_query_columns, get_values_for_columns def execute(filters=None): @@ -22,9 +31,15 @@ def _execute(filters, additional_table_columns=None): if not filters: filters = frappe._dict({}) + include_payments = filters.get("include_payments") + if filters.get("include_payments") and not filters.get("customer"): + frappe.throw(_("Please select a customer for fetching payments.")) invoice_list = get_invoices(filters, get_query_columns(additional_table_columns)) - columns, income_accounts, tax_accounts, unrealized_profit_loss_accounts = get_columns( - invoice_list, additional_table_columns + if filters.get("include_payments"): + invoice_list += get_payments(filters) + + columns, income_accounts, unrealized_profit_loss_accounts, tax_accounts = get_columns( + invoice_list, additional_table_columns, include_payments ) if not invoice_list: @@ -34,13 +49,29 @@ def _execute(filters, additional_table_columns=None): invoice_income_map = get_invoice_income_map(invoice_list) internal_invoice_map = get_internal_invoice_map(invoice_list) invoice_income_map, invoice_tax_map = get_invoice_tax_map( - invoice_list, invoice_income_map, income_accounts + invoice_list, invoice_income_map, income_accounts, include_payments ) # Cost Center & Warehouse Map invoice_cc_wh_map = get_invoice_cc_wh_map(invoice_list) invoice_so_dn_map = get_invoice_so_dn_map(invoice_list) company_currency = frappe.get_cached_value("Company", filters.get("company"), "default_currency") mode_of_payments = get_mode_of_payments([inv.name for inv in invoice_list]) + customers = list(set(d.customer for d in invoice_list)) + customer_details = get_party_details("Customer", customers) + + res = [] + if include_payments: + opening_row = get_opening_row( + "Customer", filters.customer, getdate(filters.from_date), filters.company + )[0] + res.append( + { + "receivable_account": opening_row.account, + "debit": flt(opening_row.debit), + "credit": flt(opening_row.credit), + "balance": flt(opening_row.balance), + } + ) data = [] for inv in invoice_list: @@ -51,14 +82,15 @@ def _execute(filters, additional_table_columns=None): warehouse = list(set(invoice_cc_wh_map.get(inv.name, {}).get("warehouse", []))) row = { - "invoice": inv.name, + "voucher_type": inv.doctype, + "voucher_no": inv.name, "posting_date": inv.posting_date, "customer": inv.customer, "customer_name": inv.customer_name, **get_values_for_columns(additional_table_columns, inv), - "customer_group": inv.get("customer_group"), - "territory": inv.get("territory"), - "tax_id": inv.get("tax_id"), + "customer_group": customer_details.get(inv.customer).get("customer_group"), + "territory": customer_details.get(inv.customer).get("territory"), + "tax_id": customer_details.get(inv.customer).get("tax_id"), "receivable_account": inv.debit_to, "mode_of_payment": ", ".join(mode_of_payments.get(inv.name, [])), "project": inv.project, @@ -66,7 +98,7 @@ def _execute(filters, additional_table_columns=None): "remarks": inv.remarks, "sales_order": ", ".join(sales_order), "delivery_note": ", ".join(delivery_note), - "cost_center": ", ".join(cost_center), + "cost_center": ", ".join(cost_center) if inv.doctype == "Sales Invoice" else inv.cost_center, "warehouse": ", ".join(warehouse), "currency": company_currency, } @@ -116,19 +148,36 @@ def _execute(filters, additional_table_columns=None): } ) + if inv.doctype == "Sales Invoice": + row.update({"debit": inv.base_grand_total, "credit": 0.0}) + else: + row.update({"debit": 0.0, "credit": inv.base_grand_total}) data.append(row) - return columns, data + res += sorted(data, key=lambda x: x["posting_date"]) + + if include_payments: + running_balance = flt(opening_row.balance) + for row in range(1, len(res)): + running_balance += res[row]["debit"] - res[row]["credit"] + res[row].update({"balance": running_balance}) + + return columns, res, None, None, None, include_payments -def get_columns(invoice_list, additional_table_columns): +def get_columns(invoice_list, additional_table_columns, include_payments=False): """return columns based on filters""" columns = [ { - "label": _("Invoice"), - "fieldname": "invoice", - "fieldtype": "Link", - "options": "Sales Invoice", + "label": _("Voucher Type"), + "fieldname": "voucher_type", + "width": 120, + }, + { + "label": _("Voucher"), + "fieldname": "voucher_no", + "fieldtype": "Dynamic Link", + "options": "voucher_type", "width": 120, }, {"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 80}, @@ -142,83 +191,156 @@ def get_columns(invoice_list, additional_table_columns): {"label": _("Customer Name"), "fieldname": "customer_name", "fieldtype": "Data", "width": 120}, ] - if additional_table_columns: + if additional_table_columns and not include_payments: columns += additional_table_columns - columns += [ + if not include_payments: + columns += [ + { + "label": _("Customer Group"), + "fieldname": "customer_group", + "fieldtype": "Link", + "options": "Customer Group", + "width": 120, + }, + { + "label": _("Territory"), + "fieldname": "territory", + "fieldtype": "Link", + "options": "Territory", + "width": 80, + }, + {"label": _("Tax Id"), "fieldname": "tax_id", "fieldtype": "Data", "width": 80}, + { + "label": _("Receivable Account"), + "fieldname": "receivable_account", + "fieldtype": "Link", + "options": "Account", + "width": 100, + }, + { + "label": _("Mode Of Payment"), + "fieldname": "mode_of_payment", + "fieldtype": "Data", + "width": 120, + }, + { + "label": _("Project"), + "fieldname": "project", + "fieldtype": "Link", + "options": "Project", + "width": 80, + }, + {"label": _("Owner"), "fieldname": "owner", "fieldtype": "Data", "width": 100}, + { + "label": _("Sales Order"), + "fieldname": "sales_order", + "fieldtype": "Link", + "options": "Sales Order", + "width": 100, + }, + { + "label": _("Delivery Note"), + "fieldname": "delivery_note", + "fieldtype": "Link", + "options": "Delivery Note", + "width": 100, + }, + { + "label": _("Cost Center"), + "fieldname": "cost_center", + "fieldtype": "Link", + "options": "Cost Center", + "width": 100, + }, + { + "label": _("Warehouse"), + "fieldname": "warehouse", + "fieldtype": "Link", + "options": "Warehouse", + "width": 100, + }, + {"fieldname": "currency", "label": _("Currency"), "fieldtype": "Data", "width": 80}, + ] + else: + columns += [ + { + "fieldname": "receivable_account", + "label": _("Receivable Account"), + "fieldtype": "Link", + "options": "Account", + "width": 120, + }, + {"fieldname": "debit", "label": _("Debit"), "fieldtype": "Currency", "width": 120}, + {"fieldname": "credit", "label": _("Credit"), "fieldtype": "Currency", "width": 120}, + {"fieldname": "balance", "label": _("Balance"), "fieldtype": "Currency", "width": 120}, + ] + + account_columns, accounts = get_account_columns(invoice_list, include_payments) + + net_total_column = [ { - "label": _("Customer Group"), - "fieldname": "customer_group", - "fieldtype": "Link", - "options": "Customer Group", + "label": _("Net Total"), + "fieldname": "net_total", + "fieldtype": "Currency", + "options": "currency", "width": 120, - }, - { - "label": _("Territory"), - "fieldname": "territory", - "fieldtype": "Link", - "options": "Territory", - "width": 80, - }, - {"label": _("Tax Id"), "fieldname": "tax_id", "fieldtype": "Data", "width": 120}, - { - "label": _("Receivable Account"), - "fieldname": "receivable_account", - "fieldtype": "Link", - "options": "Account", - "width": 80, - }, - { - "label": _("Mode Of Payment"), - "fieldname": "mode_of_payment", - "fieldtype": "Data", - "width": 120, - }, - { - "label": _("Project"), - "fieldname": "project", - "fieldtype": "Link", - "options": "Project", - "width": 80, - }, - {"label": _("Owner"), "fieldname": "owner", "fieldtype": "Data", "width": 150}, - {"label": _("Remarks"), "fieldname": "remarks", "fieldtype": "Data", "width": 150}, - { - "label": _("Sales Order"), - "fieldname": "sales_order", - "fieldtype": "Link", - "options": "Sales Order", - "width": 100, - }, - { - "label": _("Delivery Note"), - "fieldname": "delivery_note", - "fieldtype": "Link", - "options": "Delivery Note", - "width": 100, - }, - { - "label": _("Cost Center"), - "fieldname": "cost_center", - "fieldtype": "Link", - "options": "Cost Center", - "width": 100, - }, - { - "label": _("Warehouse"), - "fieldname": "warehouse", - "fieldtype": "Link", - "options": "Warehouse", - "width": 100, - }, - {"fieldname": "currency", "label": _("Currency"), "fieldtype": "Data", "width": 80}, + } ] + total_columns = [ + { + "label": _("Tax Total"), + "fieldname": "tax_total", + "fieldtype": "Currency", + "options": "currency", + "width": 120, + } + ] + if not include_payments: + total_columns += [ + { + "label": _("Grand Total"), + "fieldname": "grand_total", + "fieldtype": "Currency", + "options": "currency", + "width": 120, + }, + { + "label": _("Rounded Total"), + "fieldname": "rounded_total", + "fieldtype": "Currency", + "options": "currency", + "width": 120, + }, + { + "label": _("Outstanding Amount"), + "fieldname": "outstanding_amount", + "fieldtype": "Currency", + "options": "currency", + "width": 120, + }, + ] + + columns = ( + columns + + account_columns[0] + + account_columns[2] + + net_total_column + + account_columns[1] + + total_columns + ) + columns += [{"label": _("Remarks"), "fieldname": "remarks", "fieldtype": "Data", "width": 150}] + return columns, accounts[0], accounts[1], accounts[2] + + +def get_account_columns(invoice_list, include_payments): income_accounts = [] tax_accounts = [] + unrealized_profit_loss_accounts = [] + income_columns = [] tax_columns = [] - unrealized_profit_loss_accounts = [] unrealized_profit_loss_account_columns = [] if invoice_list: @@ -230,14 +352,16 @@ def get_columns(invoice_list, additional_table_columns): tuple(inv.name for inv in invoice_list), ) - tax_accounts = frappe.db.sql_list( - """select distinct account_head - from `tabSales Taxes and Charges` where parenttype = 'Sales Invoice' - and docstatus = 1 and base_tax_amount_after_discount_amount != 0 - and parent in (%s) order by account_head""" - % ", ".join(["%s"] * len(invoice_list)), - tuple(inv.name for inv in invoice_list), - ) + sales_taxes_query = get_taxes_query(invoice_list, "Sales Taxes and Charges", "Sales Invoice") + sales_tax_accounts = sales_taxes_query.run(as_dict=True, pluck="account_head") + tax_accounts = sales_tax_accounts + + if include_payments: + advance_taxes_query = get_taxes_query( + invoice_list, "Advance Taxes and Charges", "Payment Entry" + ) + advance_tax_accounts = advance_taxes_query.run(as_dict=True, pluck="account_head") + tax_accounts = set(tax_accounts + advance_tax_accounts) unrealized_profit_loss_accounts = frappe.db.sql_list( """SELECT distinct unrealized_profit_loss_account @@ -283,134 +407,82 @@ def get_columns(invoice_list, additional_table_columns): } ) - net_total_column = [ - { - "label": _("Net Total"), - "fieldname": "net_total", - "fieldtype": "Currency", - "options": "currency", - "width": 120, - } - ] + columns = [income_columns, unrealized_profit_loss_account_columns, tax_columns] + accounts = [income_accounts, unrealized_profit_loss_accounts, tax_accounts] - total_columns = [ - { - "label": _("Tax Total"), - "fieldname": "tax_total", - "fieldtype": "Currency", - "options": "currency", - "width": 120, - }, - { - "label": _("Grand Total"), - "fieldname": "grand_total", - "fieldtype": "Currency", - "options": "currency", - "width": 120, - }, - { - "label": _("Rounded Total"), - "fieldname": "rounded_total", - "fieldtype": "Currency", - "options": "currency", - "width": 120, - }, - { - "label": _("Outstanding Amount"), - "fieldname": "outstanding_amount", - "fieldtype": "Currency", - "options": "currency", - "width": 120, - }, - ] - - columns = ( - columns - + income_columns - + unrealized_profit_loss_account_columns - + net_total_column - + tax_columns - + total_columns - ) - - return columns, income_accounts, tax_accounts, unrealized_profit_loss_accounts - - -def get_conditions(filters): - conditions = "" - - accounting_dimensions = get_accounting_dimensions(as_list=False) or [] - accounting_dimensions_list = [d.fieldname for d in accounting_dimensions] - - if filters.get("company"): - conditions += " and company=%(company)s" - - if filters.get("customer") and "customer" not in accounting_dimensions_list: - conditions += " and customer = %(customer)s" - - if filters.get("from_date"): - conditions += " and posting_date >= %(from_date)s" - if filters.get("to_date"): - conditions += " and posting_date <= %(to_date)s" - - if filters.get("owner"): - conditions += " and owner = %(owner)s" - - def get_sales_invoice_item_field_condition(field, table="Sales Invoice Item") -> str: - if not filters.get(field) or field in accounting_dimensions_list: - return "" - return f""" and exists(select name from `tab{table}` - where parent=`tabSales Invoice`.name - and ifnull(`tab{table}`.{field}, '') = %({field})s)""" - - conditions += get_sales_invoice_item_field_condition("mode_of_payment", "Sales Invoice Payment") - conditions += get_sales_invoice_item_field_condition("cost_center") - conditions += get_sales_invoice_item_field_condition("warehouse") - conditions += get_sales_invoice_item_field_condition("brand") - conditions += get_sales_invoice_item_field_condition("item_group") - - if accounting_dimensions: - common_condition = """ - and exists(select name from `tabSales Invoice Item` - where parent=`tabSales Invoice`.name - """ - for dimension in accounting_dimensions: - if filters.get(dimension.fieldname): - if frappe.get_cached_value("DocType", dimension.document_type, "is_tree"): - filters[dimension.fieldname] = get_dimension_with_children( - dimension.document_type, filters.get(dimension.fieldname) - ) - - conditions += ( - common_condition - + "and ifnull(`tabSales Invoice`.{0}, '') in %({0})s)".format(dimension.fieldname) - ) - else: - conditions += ( - common_condition - + "and ifnull(`tabSales Invoice`.{0}, '') in %({0})s)".format(dimension.fieldname) - ) - - return conditions + return columns, accounts def get_invoices(filters, additional_query_columns): - conditions = get_conditions(filters) - return frappe.db.sql( - """ - select name, posting_date, debit_to, project, customer, - customer_name, owner, remarks, territory, tax_id, customer_group, - base_net_total, base_grand_total, base_rounded_total, outstanding_amount, - is_internal_customer, represents_company, company {0} - from `tabSales Invoice` - where docstatus = 1 {1} - order by posting_date desc, name desc""".format( - additional_query_columns, conditions - ), - filters, - as_dict=1, + si = frappe.qb.DocType("Sales Invoice") + query = ( + frappe.qb.from_(si) + .select( + ConstantColumn("Sales Invoice").as_("doctype"), + si.name, + si.posting_date, + si.debit_to, + si.project, + si.customer, + si.customer_name, + si.owner, + si.remarks, + si.territory, + si.tax_id, + si.customer_group, + si.base_net_total, + si.base_grand_total, + si.base_rounded_total, + si.outstanding_amount, + si.is_internal_customer, + si.represents_company, + si.company, + ) + .where((si.docstatus == 1)) + .orderby(si.posting_date, si.name, order=Order.desc) ) + if additional_query_columns: + for col in additional_query_columns: + query = query.select(col) + + if filters.get("customer"): + query = query.where(si.customer == filters.customer) + + query = get_conditions(filters, query, "Sales Invoice") + query = apply_common_conditions( + filters, query, doctype="Sales Invoice", child_doctype="Sales Invoice Item" + ) + + invoices = query.run(as_dict=True) + return invoices + + +def get_conditions(filters, query, doctype): + parent_doc = frappe.qb.DocType(doctype) + if filters.get("owner"): + query = query.where(parent_doc.owner == filters.owner) + + if filters.get("mode_of_payment"): + payment_doc = frappe.qb.DocType("Sales Invoice Payment") + query = query.inner_join(payment_doc).on(parent_doc.name == payment_doc.parent) + query = query.where(payment_doc.mode_of_payment == filters.mode_of_payment).distinct() + + return query + + +def get_payments(filters): + args = frappe._dict( + account="debit_to", + account_fieldname="paid_from", + party="customer", + party_name="customer_name", + party_account=get_party_account("Customer", filters.customer, filters.company), + ) + payment_entries = get_payment_entries(filters, args) + journal_entries = get_journal_entries(filters, args) + return payment_entries + journal_entries + def get_invoice_income_map(invoice_list): income_details = frappe.db.sql( @@ -447,7 +519,7 @@ def get_internal_invoice_map(invoice_list): return internal_invoice_map -def get_invoice_tax_map(invoice_list, invoice_income_map, income_accounts): +def get_invoice_tax_map(invoice_list, invoice_income_map, income_accounts, include_payments=False): tax_details = frappe.db.sql( """select parent, account_head, sum(base_tax_amount_after_discount_amount) as tax_amount @@ -457,6 +529,9 @@ def get_invoice_tax_map(invoice_list, invoice_income_map, income_accounts): as_dict=1, ) + if include_payments: + tax_details += get_advance_taxes_and_charges(invoice_list) + invoice_tax_map = {} for d in tax_details: if d.account_head in income_accounts: diff --git a/erpnext/accounts/report/utils.py b/erpnext/accounts/report/utils.py index 7ea1fac1056..7dd61d2c64c 100644 --- a/erpnext/accounts/report/utils.py +++ b/erpnext/accounts/report/utils.py @@ -1,8 +1,16 @@ import frappe +from frappe.query_builder.custom import ConstantColumn +from frappe.query_builder.functions import Sum from frappe.utils import flt, formatdate, get_datetime_str, get_table_name +from pypika import Order from erpnext import get_company_currency, get_default_company +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( + get_accounting_dimensions, + get_dimension_with_children, +) from erpnext.accounts.doctype.fiscal_year.fiscal_year import get_from_and_to_date +from erpnext.accounts.party import get_party_account from erpnext.setup.utils import get_exchange_rate __exchange_rates = {} @@ -165,7 +173,7 @@ def get_query_columns(report_columns): else: columns.append(fieldname) - return ", " + ", ".join(columns) + return columns def get_values_for_columns(report_columns, report_row): @@ -179,3 +187,203 @@ def get_values_for_columns(report_columns, report_row): values[fieldname] = report_row.get(fieldname) return values + + +def get_party_details(party_type, party_list): + party_details = {} + party = frappe.qb.DocType(party_type) + query = frappe.qb.from_(party).select(party.name, party.tax_id).where(party.name.isin(party_list)) + if party_type == "Supplier": + query = query.select(party.supplier_group) + else: + query = query.select(party.customer_group, party.territory) + + party_detail_list = query.run(as_dict=True) + for party_dict in party_detail_list: + party_details[party_dict.name] = party_dict + return party_details + + +def get_taxes_query(invoice_list, doctype, parenttype): + taxes = frappe.qb.DocType(doctype) + + query = ( + frappe.qb.from_(taxes) + .select(taxes.account_head) + .distinct() + .where( + (taxes.parenttype == parenttype) + & (taxes.docstatus == 1) + & (taxes.account_head.isnotnull()) + & (taxes.parent.isin([inv.name for inv in invoice_list])) + ) + .orderby(taxes.account_head) + ) + + if doctype == "Purchase Taxes and Charges": + return query.where(taxes.category.isin(["Total", "Valuation and Total"])) + elif doctype == "Sales Taxes and Charges": + return query + return query.where(taxes.charge_type.isin(["On Paid Amount", "Actual"])) + + +def get_journal_entries(filters, args): + je = frappe.qb.DocType("Journal Entry") + journal_account = frappe.qb.DocType("Journal Entry Account") + query = ( + frappe.qb.from_(je) + .inner_join(journal_account) + .on(je.name == journal_account.parent) + .select( + je.voucher_type.as_("doctype"), + je.name, + je.posting_date, + journal_account.account.as_(args.account), + journal_account.party.as_(args.party), + journal_account.party.as_(args.party_name), + je.bill_no, + je.bill_date, + je.remark.as_("remarks"), + je.total_amount.as_("base_net_total"), + je.total_amount.as_("base_grand_total"), + je.mode_of_payment, + journal_account.project, + ) + .where( + (je.voucher_type == "Journal Entry") + & (journal_account.party == filters.get(args.party)) + & (journal_account.account == args.party_account) + ) + .orderby(je.posting_date, je.name, order=Order.desc) + ) + query = apply_common_conditions(filters, query, doctype="Journal Entry", payments=True) + + journal_entries = query.run(as_dict=True) + return journal_entries + + +def get_payment_entries(filters, args): + pe = frappe.qb.DocType("Payment Entry") + query = ( + frappe.qb.from_(pe) + .select( + ConstantColumn("Payment Entry").as_("doctype"), + pe.name, + pe.posting_date, + pe[args.account_fieldname].as_(args.account), + pe.party.as_(args.party), + pe.party_name.as_(args.party_name), + pe.remarks, + pe.paid_amount.as_("base_net_total"), + pe.paid_amount_after_tax.as_("base_grand_total"), + pe.mode_of_payment, + pe.project, + pe.cost_center, + ) + .where( + (pe.party == filters.get(args.party)) & (pe[args.account_fieldname] == args.party_account) + ) + .orderby(pe.posting_date, pe.name, order=Order.desc) + ) + query = apply_common_conditions(filters, query, doctype="Payment Entry", payments=True) + payment_entries = query.run(as_dict=True) + return payment_entries + + +def apply_common_conditions(filters, query, doctype, child_doctype=None, payments=False): + parent_doc = frappe.qb.DocType(doctype) + if child_doctype: + child_doc = frappe.qb.DocType(child_doctype) + + join_required = False + + if filters.get("company"): + query = query.where(parent_doc.company == filters.company) + if filters.get("from_date"): + query = query.where(parent_doc.posting_date >= filters.from_date) + if filters.get("to_date"): + query = query.where(parent_doc.posting_date <= filters.to_date) + + if payments: + if filters.get("cost_center"): + query = query.where(parent_doc.cost_center == filters.cost_center) + else: + if filters.get("cost_center"): + query = query.where(child_doc.cost_center == filters.cost_center) + join_required = True + if filters.get("warehouse"): + query = query.where(child_doc.warehouse == filters.warehouse) + join_required = True + if filters.get("item_group"): + query = query.where(child_doc.item_group == filters.item_group) + join_required = True + + if not payments: + if filters.get("brand"): + query = query.where(child_doc.brand == filters.brand) + join_required = True + + if join_required: + query = query.inner_join(child_doc).on(parent_doc.name == child_doc.parent) + query = query.distinct() + + if parent_doc.get_table_name() != "tabJournal Entry": + query = filter_invoices_based_on_dimensions(filters, query, parent_doc) + + return query + + +def get_advance_taxes_and_charges(invoice_list): + adv_taxes = frappe.qb.DocType("Advance Taxes and Charges") + return ( + frappe.qb.from_(adv_taxes) + .select( + adv_taxes.parent, + adv_taxes.account_head, + ( + frappe.qb.terms.Case() + .when(adv_taxes.add_deduct_tax == "Add", Sum(adv_taxes.base_tax_amount)) + .else_(Sum(adv_taxes.base_tax_amount) * -1) + ).as_("tax_amount"), + ) + .where( + (adv_taxes.parent.isin([inv.name for inv in invoice_list])) + & (adv_taxes.charge_type.isin(["On Paid Amount", "Actual"])) + & (adv_taxes.base_tax_amount != 0) + ) + .groupby(adv_taxes.parent, adv_taxes.account_head, adv_taxes.add_deduct_tax) + ).run(as_dict=True) + + +def filter_invoices_based_on_dimensions(filters, query, parent_doc): + accounting_dimensions = get_accounting_dimensions(as_list=False) + if accounting_dimensions: + for dimension in accounting_dimensions: + if filters.get(dimension.fieldname): + if frappe.get_cached_value("DocType", dimension.document_type, "is_tree"): + filters[dimension.fieldname] = get_dimension_with_children( + dimension.document_type, filters.get(dimension.fieldname) + ) + fieldname = dimension.fieldname + query = query.where(parent_doc[fieldname] == filters.fieldname) + return query + + +def get_opening_row(party_type, party, from_date, company): + party_account = get_party_account(party_type, party, company) + gle = frappe.qb.DocType("GL Entry") + return ( + frappe.qb.from_(gle) + .select( + ConstantColumn("Opening").as_("account"), + Sum(gle.debit).as_("debit"), + Sum(gle.credit).as_("credit"), + (Sum(gle.debit) - Sum(gle.credit)).as_("balance"), + ) + .where( + (gle.account == party_account) + & (gle.party == party) + & (gle.posting_date < from_date) + & (gle.is_cancelled == 0) + ) + ).run(as_dict=True) From c0b598074c3defaca233231646fb5d558236bbc8 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 29 Dec 2023 16:15:45 +0530 Subject: [PATCH 054/675] fix: Validate account in Sales/Purchase Taxes and Charges Template (#39013) fix: Validate account in Sales/Purchase Taxes and Charges Template (#39013) (cherry picked from commit cd37fd790b9d7a28b9df8697c9b40c5477a580ec) Co-authored-by: Deepesh Garg --- 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 47aba077b49..e580748866d 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -2365,6 +2365,7 @@ def validate_taxes_and_charges(tax): def validate_account_head(idx, account, company, context=""): account_company = frappe.get_cached_value("Account", account, "company") + is_group = frappe.get_cached_value("Account", account, "is_group") if account_company != company: frappe.throw( @@ -2374,6 +2375,12 @@ def validate_account_head(idx, account, company, context=""): title=_("Invalid Account"), ) + if is_group: + frappe.throw( + _("Row {0}: Account {1} is a Group Account").format(idx, frappe.bold(account)), + title=_("Invalid Account"), + ) + def validate_cost_center(tax, doc): if not tax.cost_center: From 88e5c9e61b27e15e75e3c8c1d060d8ae252ecd1b Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sat, 30 Dec 2023 11:14:23 +0530 Subject: [PATCH 055/675] fix: work order with multi level, fetch operting cost from sub-assembly (backport #38992) (#39027) * fix: work order with multi level, fetch operting cost from sub-assembly (#38992) (cherry picked from commit 70abedc57ad2147719c23537ab9aaf348d6e9182) # Conflicts: # erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.py # erpnext/manufacturing/doctype/work_order/test_work_order.py * chore: fix conflicts * chore: fix conflicts * chore: fix conflicts --------- Co-authored-by: rohitwaghchaure --- erpnext/manufacturing/doctype/bom/bom.py | 44 +++++++++ .../manufacturing_settings.json | 11 ++- .../production_plan/test_production_plan.py | 4 + .../doctype/work_order/test_work_order.py | 89 ++++++++++++++++++- .../stock/doctype/stock_entry/stock_entry.py | 37 ++++++-- 5 files changed, 177 insertions(+), 8 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index c22af3f22cb..e281fbc1ec9 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -1395,3 +1395,47 @@ def make_variant_bom(source_name, bom_no, item, variant_items, target_doc=None): ) return doc + + +def get_op_cost_from_sub_assemblies(bom_no, op_cost=0): + # Get operating cost from sub-assemblies + + bom_items = frappe.get_all( + "BOM Item", filters={"parent": bom_no, "docstatus": 1}, fields=["bom_no"], order_by="idx asc" + ) + + for row in bom_items: + if not row.bom_no: + continue + + if cost := frappe.get_cached_value("BOM", row.bom_no, "operating_cost_per_bom_quantity"): + op_cost += flt(cost) + get_op_cost_from_sub_assemblies(row.bom_no, op_cost) + + return op_cost + + +def get_scrap_items_from_sub_assemblies(bom_no, company, qty, scrap_items=None): + if not scrap_items: + scrap_items = {} + + bom_items = frappe.get_all( + "BOM Item", + filters={"parent": bom_no, "docstatus": 1}, + fields=["bom_no", "qty"], + order_by="idx asc", + ) + + for row in bom_items: + if not row.bom_no: + continue + + qty = flt(row.qty) * flt(qty) + items = get_bom_items_as_dict( + row.bom_no, company, qty=qty, fetch_exploded=0, fetch_scrap_items=1 + ) + scrap_items.update(items) + + get_scrap_items_from_sub_assemblies(row.bom_no, company, qty, scrap_items) + + return scrap_items diff --git a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json index 01647d56c91..d3ad51f7236 100644 --- a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json +++ b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json @@ -31,6 +31,7 @@ "job_card_excess_transfer", "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" ], @@ -194,13 +195,20 @@ "fieldname": "job_card_excess_transfer", "fieldtype": "Check", "label": "Allow Excess Material Transfer" + }, + { + "default": "0", + "description": "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.", + "fieldname": "set_op_cost_and_scrape_from_sub_assemblies", + "fieldtype": "Check", + "label": "Set Operating Cost / Scrape Items From Sub-assemblies" } ], "icon": "icon-wrench", "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2021-09-13 22:09:09.401559", + "modified": "2023-12-28 16:37:44.874096", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing Settings", @@ -216,5 +224,6 @@ ], "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py index 8c8e6efd92d..3695ae9d9d8 100644 --- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py @@ -1591,6 +1591,10 @@ def make_bom(**args): } ) + if args.operating_cost_per_bom_quantity: + bom.fg_based_operating_cost = 1 + bom.operating_cost_per_bom_quantity = args.operating_cost_per_bom_quantity + for item in args.raw_materials: item_doc = frappe.get_doc("Item", item) bom.append( diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index b77e4e08735..74c2ece6e7e 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -1726,6 +1726,93 @@ class TestWorkOrder(FrappeTestCase): frappe.db.set_single_value("Manufacturing Settings", "make_serial_no_batch_from_work_order", 0) + def test_op_cost_and_scrap_based_on_sub_assemblies(self): + # Make Sub Assembly BOM 1 + + frappe.db.set_single_value( + "Manufacturing Settings", "set_op_cost_and_scrape_from_sub_assemblies", 1 + ) + + items = { + "Test Final FG Item": 0, + "Test Final SF Item 1": 0, + "Test Final SF Item 2": 0, + "Test Final RM Item 1": 100, + "Test Final RM Item 2": 200, + "Test Final Scrap Item 1": 50, + "Test Final Scrap Item 2": 60, + } + + for item in items: + if not frappe.db.exists("Item", item): + item_properties = {"is_stock_item": 1, "valuation_rate": items[item]} + + make_item(item_code=item, properties=item_properties), + + prepare_boms_for_sub_assembly_test() + + wo_order = make_wo_order_test_record( + production_item="Test Final FG Item", + qty=10, + use_multi_level_bom=1, + skip_transfer=1, + from_wip_warehouse=1, + ) + + se_doc = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 10)) + se_doc.save() + + self.assertTrue(se_doc.additional_costs) + scrap_items = [] + for item in se_doc.items: + if item.is_scrap_item: + scrap_items.append(item.item_code) + + self.assertEqual( + sorted(scrap_items), sorted(["Test Final Scrap Item 1", "Test Final Scrap Item 2"]) + ) + for row in se_doc.additional_costs: + self.assertEqual(row.amount, 3000) + + frappe.db.set_single_value( + "Manufacturing Settings", "set_op_cost_and_scrape_from_sub_assemblies", 0 + ) + + +def prepare_boms_for_sub_assembly_test(): + if not frappe.db.exists("BOM", {"item": "Test Final SF Item 1"}): + bom = make_bom( + item="Test Final SF Item 1", + source_warehouse="Stores - _TC", + raw_materials=["Test Final RM Item 1"], + operating_cost_per_bom_quantity=100, + do_not_submit=True, + ) + + bom.append("scrap_items", {"item_code": "Test Final Scrap Item 1", "qty": 1}) + + bom.submit() + + if not frappe.db.exists("BOM", {"item": "Test Final SF Item 2"}): + bom = make_bom( + item="Test Final SF Item 2", + source_warehouse="Stores - _TC", + raw_materials=["Test Final RM Item 2"], + operating_cost_per_bom_quantity=200, + do_not_submit=True, + ) + + bom.append("scrap_items", {"item_code": "Test Final Scrap Item 2", "qty": 1}) + + bom.submit() + + if not frappe.db.exists("BOM", {"item": "Test Final FG Item"}): + bom = make_bom( + item="Test Final FG Item", + source_warehouse="Stores - _TC", + raw_materials=["Test Final SF Item 1", "Test Final SF Item 2"], + ) + def prepare_data_for_workstation_type_check(): from erpnext.manufacturing.doctype.operation.test_operation import make_operation @@ -1955,7 +2042,7 @@ def make_wo_order_test_record(**args): wo_order.sales_order = args.sales_order or None wo_order.planned_start_date = args.planned_start_date or now() wo_order.transfer_material_against = args.transfer_material_against or "Work Order" - wo_order.from_wip_warehouse = args.from_wip_warehouse or None + wo_order.from_wip_warehouse = args.from_wip_warehouse or 0 if args.source_warehouse: for item in wo_order.get("required_items"): diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 5031625a6b8..61f99ee715a 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -24,7 +24,12 @@ from frappe.utils import ( import erpnext from erpnext.accounts.general_ledger import process_gl_map from erpnext.controllers.taxes_and_totals import init_landed_taxes_and_totals -from erpnext.manufacturing.doctype.bom.bom import add_additional_cost, validate_bom_no +from erpnext.manufacturing.doctype.bom.bom import ( + add_additional_cost, + get_op_cost_from_sub_assemblies, + get_scrap_items_from_sub_assemblies, + validate_bom_no, +) from erpnext.setup.doctype.brand.brand import get_brand_defaults from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults from erpnext.stock.doctype.batch.batch import get_batch_no, get_batch_qty, set_batch_nos @@ -1767,11 +1772,22 @@ class StockEntry(StockController): def get_bom_scrap_material(self, qty): from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict - # item dict = { item_code: {qty, description, stock_uom} } - item_dict = ( - get_bom_items_as_dict(self.bom_no, self.company, qty=qty, fetch_exploded=0, fetch_scrap_items=1) - or {} - ) + if ( + frappe.db.get_single_value( + "Manufacturing Settings", "set_op_cost_and_scrape_from_sub_assemblies" + ) + and self.work_order + and frappe.get_cached_value("Work Order", self.work_order, "use_multi_level_bom") + ): + item_dict = get_scrap_items_from_sub_assemblies(self.bom_no, self.company, qty) + else: + # item dict = { item_code: {qty, description, stock_uom} } + item_dict = ( + get_bom_items_as_dict( + self.bom_no, self.company, qty=qty, fetch_exploded=0, fetch_scrap_items=1 + ) + or {} + ) for item in item_dict.values(): item.from_warehouse = "" @@ -2527,6 +2543,15 @@ def get_work_order_details(work_order, company): def get_operating_cost_per_unit(work_order=None, bom_no=None): operating_cost_per_unit = 0 if work_order: + if ( + bom_no + and frappe.db.get_single_value( + "Manufacturing Settings", "set_op_cost_and_scrape_from_sub_assemblies" + ) + and frappe.get_cached_value("Work Order", work_order, "use_multi_level_bom") + ): + return get_op_cost_from_sub_assemblies(bom_no) + if not bom_no: bom_no = work_order.bom_no From 81ef7b4c00c02226b2c1f87add9fd022e40a24c6 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 31 Dec 2023 18:06:47 +0530 Subject: [PATCH 056/675] fix: issue occured when creating supplier with contact details (backport #38147) (#39046) * fix: issue occured when creating supplier with contact details (cherry picked from commit 7842c9fba8b521b9ba7f65281c4f2384f62b06e1) # Conflicts: # erpnext/selling/doctype/customer/customer.py * fix: Suppier name was not taken when creating address from supplier (cherry picked from commit 545ef3c23491e895bf288ddf7666ea80251b41e7) * chore: fix conflicts * chore: fix linter issues --------- Co-authored-by: kunhi Co-authored-by: rohitwaghchaure --- erpnext/selling/doctype/customer/customer.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 56388a594a7..fea27da61b2 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -690,8 +690,12 @@ def make_contact(args, is_primary_contact=1): "is_primary_contact": is_primary_contact, "links": [{"link_doctype": args.get("doctype"), "link_name": args.get("name")}], } - if args.customer_type == "Individual": - first, middle, last = parse_full_name(args.get("customer_name")) + + party_type = args.customer_type if args.doctype == "Customer" else args.supplier_type + party_name_key = "customer_name" if args.doctype == "Customer" else "supplier_name" + + if party_type == "Individual": + first, middle, last = parse_full_name(args.get(party_name_key)) values.update( { "first_name": first, @@ -703,9 +707,10 @@ def make_contact(args, is_primary_contact=1): values.update( { "first_name": args.get("customer_name"), - "company_name": args.get("customer_name"), + "company_name": args.get(party_name_key), } ) + contact = frappe.get_doc(values) if args.get("email_id"): @@ -734,10 +739,12 @@ def make_address(args, is_primary_address=1, is_shipping_address=1): title=_("Missing Values Required"), ) + party_name_key = "customer_name" if args.doctype == "Customer" else "supplier_name" + address = frappe.get_doc( { "doctype": "Address", - "address_title": args.get("customer_name"), + "address_title": args.get(party_name_key), "address_line1": args.get("address_line1"), "address_line2": args.get("address_line2"), "city": args.get("city"), From 1cbe1e894d1fe4d760e75e72b0b8792f78741f5b Mon Sep 17 00:00:00 2001 From: anandbaburajan Date: Sun, 31 Dec 2023 23:34:15 +0530 Subject: [PATCH 057/675] fix: asset WDV depreciation calc according to IT act --- erpnext/assets/doctype/asset/asset.py | 60 +++++++++++-------- erpnext/assets/doctype/asset/depreciation.py | 8 +++ .../asset_finance_book.json | 3 +- 3 files changed, 44 insertions(+), 27 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index f83d7071aa7..2120535a295 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -428,10 +428,7 @@ class Asset(AccountsController): n == 0 and (has_pro_rata or has_wdv_or_dd_non_yearly_pro_rata) and not self.opening_accumulated_depreciation - and get_updated_rate_of_depreciation_for_wdv_and_dd( - self, value_after_depreciation, finance_book, False - ) - == finance_book.rate_of_depreciation + and not self.flags.wdv_it_act_applied ): from_date = add_days( self.available_for_use_date, -1 @@ -1378,26 +1375,16 @@ def get_depreciation_amount( asset, fb_row, schedule_idx, number_of_pending_depreciations ) else: - rate_of_depreciation = get_updated_rate_of_depreciation_for_wdv_and_dd( - asset, depreciable_value, fb_row - ) return get_wdv_or_dd_depr_amount( + asset, + fb_row, depreciable_value, - rate_of_depreciation, - fb_row.frequency_of_depreciation, schedule_idx, prev_depreciation_amount, has_wdv_or_dd_non_yearly_pro_rata, ) -@erpnext.allow_regional -def get_updated_rate_of_depreciation_for_wdv_and_dd( - asset, depreciable_value, fb_row, show_msg=True -): - return fb_row.rate_of_depreciation - - def get_straight_line_or_manual_depr_amount( asset, row, schedule_idx, number_of_pending_depreciations ): @@ -1532,30 +1519,53 @@ def get_asset_shift_factors_map(): return dict(frappe.db.get_all("Asset Shift Factor", ["shift_name", "shift_factor"], as_list=True)) +@erpnext.allow_regional def get_wdv_or_dd_depr_amount( + asset, + fb_row, depreciable_value, - rate_of_depreciation, - frequency_of_depreciation, schedule_idx, prev_depreciation_amount, has_wdv_or_dd_non_yearly_pro_rata, ): - if cint(frequency_of_depreciation) == 12: - return flt(depreciable_value) * (flt(rate_of_depreciation) / 100) + return get_default_wdv_or_dd_depr_amount( + asset, + fb_row, + depreciable_value, + schedule_idx, + prev_depreciation_amount, + has_wdv_or_dd_non_yearly_pro_rata, + ) + + +def get_default_wdv_or_dd_depr_amount( + asset, + fb_row, + depreciable_value, + schedule_idx, + prev_depreciation_amount, + has_wdv_or_dd_non_yearly_pro_rata, +): + if cint(fb_row.frequency_of_depreciation) == 12: + return flt(depreciable_value) * (flt(fb_row.rate_of_depreciation) / 100) else: if has_wdv_or_dd_non_yearly_pro_rata: if schedule_idx == 0: - return flt(depreciable_value) * (flt(rate_of_depreciation) / 100) - elif schedule_idx % (12 / cint(frequency_of_depreciation)) == 1: + return flt(depreciable_value) * (flt(fb_row.rate_of_depreciation) / 100) + elif schedule_idx % (12 / cint(fb_row.frequency_of_depreciation)) == 1: return ( - flt(depreciable_value) * flt(frequency_of_depreciation) * (flt(rate_of_depreciation) / 1200) + flt(depreciable_value) + * flt(fb_row.frequency_of_depreciation) + * (flt(fb_row.rate_of_depreciation) / 1200) ) else: return prev_depreciation_amount else: - if schedule_idx % (12 / cint(frequency_of_depreciation)) == 0: + if schedule_idx % (12 / cint(fb_row.frequency_of_depreciation)) == 0: return ( - flt(depreciable_value) * flt(frequency_of_depreciation) * (flt(rate_of_depreciation) / 1200) + flt(depreciable_value) + * flt(fb_row.frequency_of_depreciation) + * (flt(fb_row.rate_of_depreciation) / 1200) ) else: return prev_depreciation_amount diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py index 6f76955e12d..7eb600025e0 100644 --- a/erpnext/assets/doctype/asset/depreciation.py +++ b/erpnext/assets/doctype/asset/depreciation.py @@ -19,6 +19,7 @@ from frappe.utils import ( from frappe.utils.data import get_link_to_form from frappe.utils.user import get_users_with_role +import erpnext from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( get_checks_for_pl_and_bs_accounts, ) @@ -473,6 +474,13 @@ def depreciate_asset(asset, date): make_depreciation_entry(asset.name, date) + cancel_depreciation_entries(asset, date) + + +@erpnext.allow_regional +def cancel_depreciation_entries(asset, date): + pass + def reset_depreciation_schedule(asset, date): if not asset.calculate_depreciation: diff --git a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json index 172b81d98b0..9e5dd3e1a68 100644 --- a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json +++ b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json @@ -90,7 +90,6 @@ }, { "default": "0", - "depends_on": "eval:doc.depreciation_method == \"Straight Line\" || doc.depreciation_method == \"Manual\"", "fieldname": "daily_prorata_based", "fieldtype": "Check", "label": "Depreciate based on daily pro-rata" @@ -106,7 +105,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-11-29 03:53:03.591098", + "modified": "2023-12-29 08:49:39.876439", "modified_by": "Administrator", "module": "Assets", "name": "Asset Finance Book", From b8dce3eeac8c8e6586e29aa7ab0b6eead318a7e2 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 1 Jan 2024 11:27:46 +0530 Subject: [PATCH 058/675] fix: take quantity into account when setting asset's gross purchase amt (backport #39056) (#39057) fix: take quantity into account when setting asset's gross purchase amt (#39056) fix: take quantity into account when setting asset's gross purchase amount (cherry picked from commit 0346f47c1d25e1d60de21dd0dc04e8c7c0b23938) Co-authored-by: Anand Baburajan --- .../doctype/purchase_invoice/purchase_invoice.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 7171bddac4b..cc1109b7dc8 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -976,11 +976,17 @@ class PurchaseInvoice(BuyingController): ) assets = frappe.db.get_all( - "Asset", filters={"purchase_invoice": self.name, "item_code": item.item_code} + "Asset", + filters={"purchase_invoice": self.name, "item_code": item.item_code}, + fields=["name", "asset_quantity"], ) for asset in assets: - frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate)) - frappe.db.set_value("Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate)) + frappe.db.set_value( + "Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate) * asset.asset_quantity + ) + frappe.db.set_value( + "Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate) * asset.asset_quantity + ) def make_stock_adjustment_entry( self, gl_entries, item, voucher_wise_stock_value, account_currency From fe9acc898ec51073a4a75e3cf20457f0cd161909 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 1 Jan 2024 15:38:22 +0530 Subject: [PATCH 059/675] fix(DX): capture tracebacks with context (backport #39060) (#39062) * fix(DX): capture tracebacks with context (#39060) (cherry picked from commit 510fdf7bf651788e549184c23d89c2387c1c2f10) # Conflicts: # erpnext/manufacturing/doctype/bom_creator/bom_creator.py * Delete erpnext/manufacturing/doctype/bom_creator/bom_creator.py --------- Co-authored-by: Ankush Menat --- .../process_payment_reconciliation.py | 2 +- .../doctype/repost_payment_ledger/repost_payment_ledger.py | 2 +- .../doctype/closing_stock_balance/closing_stock_balance.py | 4 +--- .../doctype/repost_item_valuation/repost_item_valuation.py | 2 +- erpnext/stock/reorder_item.py | 2 +- erpnext/utilities/bulk_transaction.py | 4 ++-- 6 files changed, 7 insertions(+), 9 deletions(-) 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 31660306db0..a93fd46caf6 100644 --- a/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.py +++ b/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.py @@ -415,7 +415,7 @@ def reconcile(doc: None | str = None) -> None: # Update the parent doc about the exception frappe.db.rollback() - traceback = frappe.get_traceback() + traceback = frappe.get_traceback(with_context=True) if traceback: message = "Traceback:
    " + traceback frappe.db.set_value("Process Payment Reconciliation Log", log, "error_log", message) diff --git a/erpnext/accounts/doctype/repost_payment_ledger/repost_payment_ledger.py b/erpnext/accounts/doctype/repost_payment_ledger/repost_payment_ledger.py index 209cad4f905..d603635ac5a 100644 --- a/erpnext/accounts/doctype/repost_payment_ledger/repost_payment_ledger.py +++ b/erpnext/accounts/doctype/repost_payment_ledger/repost_payment_ledger.py @@ -43,7 +43,7 @@ def start_payment_ledger_repost(docname=None): except Exception as e: frappe.db.rollback() - traceback = frappe.get_traceback() + traceback = frappe.get_traceback(with_context=True) if traceback: message = "Traceback:
    " + traceback frappe.db.set_value(repost_doc.doctype, repost_doc.name, "repost_error_log", message) diff --git a/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.py b/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.py index 295d979b835..2f7a2dd70b7 100644 --- a/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.py +++ b/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.py @@ -128,6 +128,4 @@ def prepare_closing_stock_balance(name): doc.db_set("status", "Completed") except Exception as e: doc.db_set("status", "Failed") - traceback = frappe.get_traceback() - - frappe.log_error("Closing Stock Balance Failed", traceback, doc.doctype, doc.name) + doc.log_error(title="Closing Stock Balance Failed") 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 9f3074a24ca..6ffb34dc135 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py @@ -248,7 +248,7 @@ def repost(doc): raise frappe.db.rollback() - traceback = frappe.get_traceback() + traceback = frappe.get_traceback(with_context=True) doc.log_error("Unable to repost item valuation") message = frappe.message_log.pop() if frappe.message_log else "" diff --git a/erpnext/stock/reorder_item.py b/erpnext/stock/reorder_item.py index 136c78ff586..3c429a2c22d 100644 --- a/erpnext/stock/reorder_item.py +++ b/erpnext/stock/reorder_item.py @@ -141,7 +141,7 @@ def create_material_request(material_requests): exceptions_list.extend(frappe.local.message_log) frappe.local.message_log = [] else: - exceptions_list.append(frappe.get_traceback()) + exceptions_list.append(frappe.get_traceback(with_context=True)) mr.log_error("Unable to create material request") diff --git a/erpnext/utilities/bulk_transaction.py b/erpnext/utilities/bulk_transaction.py index 5c43bbe41da..7577f0a379e 100644 --- a/erpnext/utilities/bulk_transaction.py +++ b/erpnext/utilities/bulk_transaction.py @@ -62,7 +62,7 @@ def retry_failed_transactions(failed_docs: list | None): task(log.transaction_name, log.from_doctype, log.to_doctype) except Exception as e: frappe.db.rollback(save_point="before_creation_state") - update_log(log.name, "Failed", 1, str(frappe.get_traceback())) + update_log(log.name, "Failed", 1, str(frappe.get_traceback(with_context=True))) else: update_log(log.name, "Success", 1) @@ -86,7 +86,7 @@ def job(deserialized_data, from_doctype, to_doctype): fail_count += 1 create_log( doc_name, - str(frappe.get_traceback()), + str(frappe.get_traceback(with_context=True)), from_doctype, to_doctype, status="Failed", From c7d17d21e868815874bcee2b6d6898ca1ad50f80 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 29 Dec 2023 13:35:18 +0530 Subject: [PATCH 060/675] refactor(perf): replace account subquery with 'in' condition (cherry picked from commit a517125d64a48b61a9b2d943b3a01bfe8ae87f41) --- .../customer_ledger_summary.py | 34 +++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py b/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py index d870a1aaf83..b2222c2d6a7 100644 --- a/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py +++ b/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py @@ -3,7 +3,7 @@ import frappe -from frappe import _, scrub +from frappe import _, qb, scrub from frappe.utils import getdate, nowdate @@ -38,7 +38,6 @@ class PartyLedgerSummaryReport(object): """ Additional Columns for 'User Permission' based access control """ - from frappe import qb if self.filters.party_type == "Customer": self.territories = frappe._dict({}) @@ -365,13 +364,29 @@ class PartyLedgerSummaryReport(object): def get_party_adjustment_amounts(self): conditions = self.prepare_conditions() - income_or_expense = ( - "Expense Account" if self.filters.party_type == "Customer" else "Income Account" + account_type = "Expense Account" if self.filters.party_type == "Customer" else "Income Account" + income_or_expense_accounts = frappe.db.get_all( + "Account", filters={"account_type": account_type, "company": self.filters.company}, pluck="name" ) invoice_dr_or_cr = "debit" if self.filters.party_type == "Customer" else "credit" reverse_dr_or_cr = "credit" if self.filters.party_type == "Customer" else "debit" round_off_account = frappe.get_cached_value("Company", self.filters.company, "round_off_account") + gl = qb.DocType("GL Entry") + if not income_or_expense_accounts: + # prevent empty 'in' condition + income_or_expense_accounts.append("") + + accounts_query = ( + qb.from_(gl) + .select(gl.voucher_type, gl.voucher_no) + .where( + (gl.account.isin(income_or_expense_accounts)) + & (gl.posting_date.gte(self.filters.from_date)) + & (gl.posting_date.lte(self.filters.to_date)) + ) + ) + gl_entries = frappe.db.sql( """ select @@ -381,16 +396,15 @@ class PartyLedgerSummaryReport(object): where docstatus < 2 and is_cancelled = 0 and (voucher_type, voucher_no) in ( - select voucher_type, voucher_no from `tabGL Entry` gle, `tabAccount` acc - where acc.name = gle.account and acc.account_type = '{income_or_expense}' - and gle.posting_date between %(from_date)s and %(to_date)s and gle.docstatus < 2 + {accounts_query} ) and (voucher_type, voucher_no) in ( select voucher_type, voucher_no from `tabGL Entry` gle where gle.party_type=%(party_type)s and ifnull(party, '') != '' and gle.posting_date between %(from_date)s and %(to_date)s and gle.docstatus < 2 {conditions} ) - """.format( - conditions=conditions, income_or_expense=income_or_expense + """.format( + accounts_query=accounts_query, + conditions=conditions, ), self.filters, as_dict=True, @@ -414,7 +428,7 @@ class PartyLedgerSummaryReport(object): elif gle.party: parties.setdefault(gle.party, 0) parties[gle.party] += gle.get(reverse_dr_or_cr) - gle.get(invoice_dr_or_cr) - elif frappe.get_cached_value("Account", gle.account, "account_type") == income_or_expense: + elif frappe.get_cached_value("Account", gle.account, "account_type") == account_type: accounts.setdefault(gle.account, 0) accounts[gle.account] += gle.get(invoice_dr_or_cr) - gle.get(reverse_dr_or_cr) else: From cad15cdec25d630dc14819647f745d8936c8f908 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 1 Jan 2024 14:46:15 +0530 Subject: [PATCH 061/675] fix: undefined error in Budget Variance and Profitability report 'Budget' and 'Budget Account' doesn't have support for dynamic dimension. It only supports hard-coded ones - Project and Cost Center (cherry picked from commit 92bc962f60adb3967a2ef378511822d53ff2f4a8) # Conflicts: # erpnext/accounts/report/profitability_analysis/profitability_analysis.js --- .../budget_variance_report/budget_variance_report.js | 4 ---- .../profitability_analysis/profitability_analysis.js | 10 ++++++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.js b/erpnext/accounts/report/budget_variance_report/budget_variance_report.js index 5955c2e0fc9..2c00bd738ce 100644 --- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.js +++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.js @@ -88,7 +88,3 @@ frappe.query_reports["Budget Variance Report"] = { return value; } } - -erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Budget Variance Report"].filters[4].options.push(dimension["document_type"]); -}); diff --git a/erpnext/accounts/report/profitability_analysis/profitability_analysis.js b/erpnext/accounts/report/profitability_analysis/profitability_analysis.js index c4054a977a8..7a430077022 100644 --- a/erpnext/accounts/report/profitability_analysis/profitability_analysis.js +++ b/erpnext/accounts/report/profitability_analysis/profitability_analysis.js @@ -119,8 +119,18 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "initial_depth": 3 } +<<<<<<< HEAD erpnext.dimension_filters.forEach((dimension) => { frappe.query_reports["Profitability Analysis"].filters[1].options.push(dimension["document_type"]); }); }); +======= + frappe.set_route("query-report", "Profit and Loss Statement"); + }, + "tree": true, + "name_field": "account", + "parent_field": "parent_account", + "initial_depth": 3 +} +>>>>>>> 92bc962f60 (fix: undefined error in Budget Variance and Profitability report) From bfc94cf284cd50af0a5f038b3ab1dfb4000d010d Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 1 Jan 2024 17:32:33 +0530 Subject: [PATCH 062/675] fix: select options should dynamically load dimensions (cherry picked from commit 1a9e091d12f9149638f3774b9def0fa80370b59b) --- .../budget_variance_report.js | 63 ++++++++++++++----- .../profitability_analysis.js | 14 ----- 2 files changed, 46 insertions(+), 31 deletions(-) diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.js b/erpnext/accounts/report/budget_variance_report/budget_variance_report.js index 2c00bd738ce..ca1cca13115 100644 --- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.js +++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.js @@ -2,7 +2,44 @@ // License: GNU General Public License v3. See license.txt frappe.query_reports["Budget Variance Report"] = { - "filters": [ + "filters": get_filters(), + "formatter": function (value, row, column, data, default_formatter) { + value = default_formatter(value, row, column, data); + + if (column.fieldname.includes(__("variance"))) { + + if (data[column.fieldname] < 0) { + value = "" + value + ""; + } + else if (data[column.fieldname] > 0) { + value = "" + value + ""; + } + } + + return value; + } +} +function get_filters() { + function get_dimensions() { + let result = []; + frappe.call({ + method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.get_dimensions", + args: { + 'with_cost_center_and_project': true + }, + async: false, + callback: function(r) { + if(!r.exc) { + result = r.message[0].map(elem => elem.document_type); + } + } + }); + return result; + } + + let budget_against_options = get_dimensions(); + + let filters = [ { fieldname: "from_fiscal_year", label: __("From Fiscal Year"), @@ -44,9 +81,13 @@ frappe.query_reports["Budget Variance Report"] = { fieldname: "budget_against", label: __("Budget Against"), fieldtype: "Select", - options: ["Cost Center", "Project"], + options: budget_against_options, default: "Cost Center", reqd: 1, + get_data: function() { + console.log(this.options); + return ["Emacs", "Rocks"]; + }, on_change: function() { frappe.query_report.set_filter_value("budget_against_filter", []); frappe.query_report.refresh(); @@ -71,20 +112,8 @@ frappe.query_reports["Budget Variance Report"] = { fieldtype: "Check", default: 0, }, - ], - "formatter": function (value, row, column, data, default_formatter) { - value = default_formatter(value, row, column, data); + ] - if (column.fieldname.includes(__("variance"))) { - - if (data[column.fieldname] < 0) { - value = "" + value + ""; - } - else if (data[column.fieldname] > 0) { - value = "" + value + ""; - } - } - - return value; - } + return filters; } + diff --git a/erpnext/accounts/report/profitability_analysis/profitability_analysis.js b/erpnext/accounts/report/profitability_analysis/profitability_analysis.js index 7a430077022..4dbc687366d 100644 --- a/erpnext/accounts/report/profitability_analysis/profitability_analysis.js +++ b/erpnext/accounts/report/profitability_analysis/profitability_analysis.js @@ -119,18 +119,4 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "initial_depth": 3 } -<<<<<<< HEAD - erpnext.dimension_filters.forEach((dimension) => { - frappe.query_reports["Profitability Analysis"].filters[1].options.push(dimension["document_type"]); - }); - }); -======= - frappe.set_route("query-report", "Profit and Loss Statement"); - }, - "tree": true, - "name_field": "account", - "parent_field": "parent_account", - "initial_depth": 3 -} ->>>>>>> 92bc962f60 (fix: undefined error in Budget Variance and Profitability report) From 2c90ee23f9dd1f3011b063c7fc074e6d36f1e5f6 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 2 Jan 2024 13:51:43 +0530 Subject: [PATCH 063/675] fix: Add missing french translations (backport #38368) (#38513) * fix: Add missing french translations (#38368) fix: Add missing french translation Co-authored-by: barredterra <14891507+barredterra@users.noreply.github.com> (cherry picked from commit a1e0197a8bc569a334503b6c3ba024feb3572d0f) # Conflicts: # erpnext/translations/fr.csv * chore: resolve merge conflicts --------- Co-authored-by: noec764 <58433943+noec764@users.noreply.github.com> Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com> --- erpnext/translations/fr.csv | 136 +++++++++++++++++++++++++++++++++--- 1 file changed, 126 insertions(+), 10 deletions(-) diff --git a/erpnext/translations/fr.csv b/erpnext/translations/fr.csv index d24c9972ce1..44d0b902397 100644 --- a/erpnext/translations/fr.csv +++ b/erpnext/translations/fr.csv @@ -151,7 +151,7 @@ Advance amount cannot be greater than {0} {1},Montant de l'avance ne peut être Advertising,Publicité, Aerospace,Aérospatial, Against,Contre, -Against Account,Pour le Compte, +Against Account,Contrepartie, Against Journal Entry {0} does not have any unmatched {1} entry,L'Écriture de Journal {0} n'a pas d'entrée non associée {1}, Against Journal Entry {0} is already adjusted against some other voucher,L'Écriture de Journal {0} est déjà ajustée par un autre bon, Against Supplier Invoice {0} dated {1},Pour la Facture Fournisseur {0} datée {1}, @@ -3275,7 +3275,7 @@ Import Successful,Importation réussie, Please save first,S'il vous plaît enregistrer en premier, Price not found for item {0} in price list {1},Prix non trouvé pour l'article {0} dans la liste de prix {1}, Warehouse Type,Type d'entrepôt, -'Date' is required,'Date' est requis, +'Date' is required,La 'date' est obligatoire, Budgets,Budgets, Bundle Qty,Quantité de paquet, Company GSTIN,GSTIN de la Société, @@ -3329,7 +3329,7 @@ Account: {0} is capital Work in progress and can not be updated by Journa Account: {0} is not permitted under Payment Entry,Compte: {0} n'est pas autorisé sous Saisie du paiement., Accounting Dimension {0} is required for 'Balance Sheet' account {1}.,La dimension de comptabilité {0} est requise pour le compte "Bilan" {1}., Accounting Dimension {0} is required for 'Profit and Loss' account {1}.,La dimension de comptabilité {0} est requise pour le compte 'Bénéfices et pertes' {1}., -Accounting Masters,Maîtres Comptables, +Accounting Masters,Données de base, Accounting Period overlaps with {0},La période comptable chevauche avec {0}, Activity,Activité, Add / Manage Email Accounts.,Ajouter / Gérer les Comptes de Messagerie., @@ -4214,7 +4214,7 @@ Mandatory For Profit and Loss Account,Compte de résultat obligatoire, Accounting Period,Période comptable, Period Name,Nom de période, Closed Documents,Documents fermés, -Accounts Settings,Paramètres des Comptes, +Accounts Settings,Paramètres de comptabilité, Settings for Accounts,Paramètres des Comptes, Make Accounting Entry For Every Stock Movement,Faites une Écriture Comptable Pour Chaque Mouvement du Stock, Users with this role are allowed to set frozen accounts and create / modify accounting entries against frozen accounts,Les utilisateurs ayant ce rôle sont autorisés à définir les comptes gelés et à créer / modifier des écritures comptables sur des comptes gelés, @@ -4231,7 +4231,7 @@ Show Payment Schedule in Print,Afficher le calendrier de paiement dans Imprimer, Currency Exchange Settings,Paramètres d'échange de devises, Allow Stale Exchange Rates,Autoriser les Taux de Change Existants, Stale Days,Journées Passées, -Report Settings,Paramètres de rapport, +Report Settings,Paramètres des rapports, Use Custom Cash Flow Format,Utiliser le format de flux de trésorerie personnalisé, Allowed To Transact With,Autorisé à faire affaire avec, SWIFT number,Numéro rapide, @@ -4837,7 +4837,7 @@ Redemption Account,Compte pour l'échange, Redemption Cost Center,Centre de coûts pour l'échange, In Words will be visible once you save the Sales Invoice.,En Toutes Lettres. Sera visible une fois que vous enregistrerez la Facture., Allocate Advances Automatically (FIFO),Allouer automatiquement les avances (FIFO), -Get Advances Received,Obtenir Acomptes Reçus, +Get Advances Received,Obtenir les paiements des avances, Base Change Amount (Company Currency),Montant de Base à Rendre (Devise de la Société), Write Off Outstanding Amount,Encours de Reprise, Terms and Conditions Details,Détails des Termes et Conditions, @@ -4845,7 +4845,7 @@ Is Internal Customer,Est un client interne, Is Discounted,Est réduit, Unpaid and Discounted,Non payé et à prix réduit, Overdue and Discounted,En retard et à prix réduit, -Accounting Details,Détails Comptabilité, +Accounting Details,Détails Comptable, Debit To,Débit Pour, Is Opening Entry,Est Écriture Ouverte, C-Form Applicable,Formulaire-C Applicable, @@ -8742,7 +8742,7 @@ Import Chart of Accounts from CSV / Excel files,Importer un plan comptable à pa Completed Qty cannot be greater than 'Qty to Manufacture',La quantité terminée ne peut pas être supérieure à la `` quantité à fabriquer '', "Row {0}: For Supplier {1}, Email Address is Required to send an email","Ligne {0}: pour le fournisseur {1}, l'adresse e-mail est obligatoire pour envoyer un e-mail", "If enabled, the system will post accounting entries for inventory automatically","Si activé, le système enregistrera automatiquement les écritures comptables pour l'inventaire", -Accounts Frozen Till Date,Comptes gelés jusqu'à la date, +Accounts Frozen Till Date,Comptes gelés jusqu'au, Accounting entries are frozen up to this date. Nobody can create or modify entries except users with the role specified below,Les écritures comptables sont gelées jusqu'à cette date. Personne ne peut créer ou modifier des entrées sauf les utilisateurs avec le rôle spécifié ci-dessous, Role Allowed to Set Frozen Accounts and Edit Frozen Entries,Rôle autorisé à définir des comptes gelés et à modifier les entrées gelées, Address used to determine Tax Category in transactions,Adresse utilisée pour déterminer la catégorie de taxe dans les transactions, @@ -9053,7 +9053,7 @@ Unit of Measure (UOM),Unité de mesure (UdM), Unit Of Measure (UOM),Unité de mesure (UdM), CRM Settings,Paramètres CRM, Do Not Explode,Ne pas décomposer, -Quick Access, Accés rapides, +Quick Access, Accès rapides, {} Available,{} Disponible.s, {} Pending,{} En attente.s, {} To Bill,{} à facturer, @@ -9109,4 +9109,120 @@ Receivable Accounts,Compte de débit Mention if a non-standard receivable account,Veuillez mentionner s'il s'agit d'un compte débiteur non standard Allow Purchase,Autoriser à l'achat Inventory Settings,Paramétrage de l'inventaire -Component Type,Type de composant, +Add Comment,Ajouter un Commentaire, +More...,Plus..., +Notes,Remarques, +Payment Gateway,Passerelle de Paiement, +Payment Gateway Name,Nom de la passerelle de paiement, +Payments,Paiements, +Plan Name,Nom du Plan, +Portal,Portail, +Scan Barcode,Scan Code Barre, +Some information is missing,Certaines informations sont manquantes, +Successful,Réussi, +Tools,Outils, +Use Sandbox,Utiliser Sandbox, +Busy,Occupé, +Completed By,Effectué par, +Payment Failed,Le Paiement a Échoué, +Column {0},Colonne {0}, +Field Mapping,Cartographie des champs, +Not Specified,Non précisé, +Update Type,Type de mise à jour, +Dr,Dr, +End Time,Heure de Fin, +Fetching...,Aller chercher..., +"It seems that there is an issue with the server's stripe configuration. In case of failure, the amount will get refunded to your account.","Il semble qu'il y a un problème avec la configuration de Stripe sur le serveur. En cas d'erreur, le montant est remboursé sur votre compte.", +Looks like someone sent you to an incomplete URL. Please ask them to look into it.,On dirait que quelqu'un vous a envoyé vers URL incomplète. Veuillez leur demander d’analyser l’erreur., +Master,Maître, +Pay,Payer, +You can also copy-paste this link in your browser,Vous pouvez également copier-coller ce lien dans votre navigateur, +Verified By,Vérifié Par, +Invalid naming series (. missing) for {0},Masque de numérotation non valide (. Manquante) pour {0}, +Phone Number,Numéro de téléphone, +Account SID,Compte SID, +Global Defaults,Valeurs par Défaut Globales, +Is Mandatory,Est obligatoire, +WhatsApp,WhatsApp, +Make a call,Passer un coup de téléphone, +No of Employees,Nb de salarié(e)s +No. of Employees,Nb de salarié(e)s +Annual Revenue,CA annuel +Qualified By,Qualifié par +Qualified on,Qualifié le +Open Tasks,Tâche à faire ouverte +No open task,Pas de Tâche à faire ouverte +Open Events,Evénements ouvert +No open event,Pas Evénements ouvert +New Task,Nv. Tâche à faire +No Notes,Pas de note +New Note,Nouvelle Note +Prospect Owner,Resp. du Prospect +Deal Owner,Resp. de l'opportunité +Stage,Etape +Probability,Probabilité +Closing,Clôture +Allow Sales,Autoriser à la vente +Approve,Approuver, +Reject,Rejeter, +'Account' in the Accounting section of Customer {0},'Compte' dans la section comptabilité du client {0}, +Accounting Entry Number,Numéro d'écriture comptable, +Accounting Ledger,Grand livre, +Accounts Closing,Clôture, +Accounts Frozen Upto,Comptes gelés jusqu'au, +Accounts Manager,Responsable comptable, +Active Customers,Clients actifs, +Against Account,Contrepartie, +All the Comments and Emails will be copied from one document to another newly created document(Lead -> Opportunity -> Quotation) throughout the CRM documents.,Tous les commentaires et les courriels seront copiés d'un document à un autre document nouvellement créé (Lead -> Opportunité -> Devis) dans l'ensemble des documents CRM., +Allow multi-currency invoices against single party account ,Autoriser les factures multi-devises en contrepartie d'un seul compte de tiers, +Allow Sales Order Creation For Expired Quotation,Autoriser la création de commandes client pour les devis expirés, +Allow Continuous Material Consumption,Autoriser la consommation continue de matériel, +Allow Lead Duplication based on Emails,Autoriser la duplication des pistes sur la base des courriels, +Asset Settings,Paramètres des actifs, +Auto close Opportunity Replied after the no. of days mentioned above,Fermeture automatique de l'opportunité de réponse après le nombre de jours mentionné ci-dessus., +Auto Creation of Contact,Création automatique d'un contact, +Automatically Fetch Payment Terms from Order,Récupération automatique des conditions de paiement de la commande, +Bill for Rejected Quantity in Purchase Invoice,Facturation de la quantité rejetée dans la facture d'achat, +Credit Limit Settings,Paramètres de la limite de crédit, +Create Ledger Entries for Change Amount,Créer des écritures de grand livre pour modifier le montant, +Customer Defaults,Valeurs par défaut des clients, +Calculate Product Bundle Price based on Child Items' Rates,Calculer le prix des ensembles de produits en fonction des tarifs des articles enfants, +Configure the action to stop the transaction or just warn if the same rate is not maintained.,Configurez une action pour stopper la transaction ou alertez simplement su le prix unitaie n'est pas maintenu., +Close Replied Opportunity After Days,Fermer l'opportunité répliquée après des jours, +Carry Forward Communication and Comments,Reprendre les communications et commentaires, +Default Down Payment Payable Account,Compte d'acompte fournisseur par défaut, +Default Down Payment Receivable Account,Compte d'acompte client par défaut, +Disable Last Purchase Rate,Désactiver le dernier prix d'achat, +'Default {0} Account' in Company {1},'Compte {0} par défaut' dans la société {1}, +Enable Custom Cash Flow Format,Activation du format de flux de trésorerie personnalisé, +Enabling ensure each Purchase Invoice has a unique value in Supplier Invoice No. field,Garanti que chaque facture d'achat est associée à un numéro de facture fournisseur unique, +Enable Common Party Accounting,Activer la comptabilité des tiers communs, +Enabling this will allow creation of multi-currency invoices against single party account in company currency,L'activation de cette option va permettre la création de factures multi-devises en contrepartie d'un seul compte de tiers en devise de la société, +Enable Discount Accounting for Selling,Activation de la comptabilité d'escompte pour la vente, +'Expected Start Date' can not be greater than 'Expected End Date','Date de Début Prévue' ne peut pas être postérieure à 'Date de Fin Prévue', +Get Advances Received, Obtenir les paiements des avances, +"If enabled, ledger entries will be posted for change amount in POS transactions","Si cette option est activée, des écritures de grand livre seront enregistrées pour le montant de la modification dans les transactions POS.", +"If enabled, additional ledger entries will be made for discounts in a separate Discount Account","Si cette option est activée, des écritures de grand livre supplémentaires seront effectuées pour les remises dans un compte de remise séparé.", +Item Price Settings,Paramètres du prix de l'article, +Invoice and Billing,Facturation, +Invoice Cancellation,Annulation de facture, +Invoicing Features,Caractéristiques de la facturation, +Journals,Journaux, +"Learn about Common Party","En savoir plus Tiers communs", +Naming Series and Price Defaults,Nom de série et Tarifs, +Over Order Allowance (%),Tolérance de sur-commande (%), +Payment Terms from orders will be fetched into the invoices as is,Les termes de paiement des commandes seront récupérées dans les factures telles quelles, +Percentage you are allowed to order more against the Blanket Order Quantity. For example: If you have a Blanket Order of Quantity 100 units. and your Allowance is 10% then you are allowed to order 110 units.,"Percentage de commande autorisée en plus de la quantité prévue dans la commande ouverte. Par exemple: Si vous avez une commande avec une quantité de 100 unités et une tolérance de 10%, alors vous pourrez commander jusqu'à 110 unités.", +Period Closing Settings,Paramètres de clôture de la période, +Quick Access,Accès rapide, +Report Setting,Réglage des rapports, +Record all transactions against an accounting journal,Comptabiliser toutes les transactions dans un journal comptable, +Rows with Same Account heads will be merged on Ledger,Les lignes associées aux mêmes comptes comptables seront fusionnées dans le grand livre, +Role Allowed to Over Bill ,Rôle autorisé à sur-facturer, +Role allowed to bypass Credit Limit,Rôle autorisé à contourner la limite de crédit, +Role Allowed to Override Stop Action,Rôle autorisé à outrepasser l'action Stop, +Show Balances in Chart Of Accounts,Afficher les soldes dans le plan comptable, +Sales Update Frequency in Company and Project,Fréquence de mise à jour des ventes dans la société et le projet, +Transaction Settings,Paramètres des transactions, +Subcontracting Settings,Paramètres de sous-traitance, +Users with this role are allowed to over bill above the allowance percentage,Les utilisateurs avec ce rôle sont autorisés à sur-facturer au delà du pourcentage de tolérance, From 558861b6341febc7e5499bffc691b05b18e72c7f Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 2 Jan 2024 14:43:23 +0530 Subject: [PATCH 064/675] perf: index item_code in bom explosion item (backport #39085) (#39087) perf: index item_code in bom explosion item (#39085) (cherry picked from commit 739434b72779fe12aed86a0a61d352948c81b680) Co-authored-by: Ankush Menat --- .../doctype/bom_explosion_item/bom_explosion_item.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom_explosion_item/bom_explosion_item.json b/erpnext/manufacturing/doctype/bom_explosion_item/bom_explosion_item.json index c75ac32cd12..27ecd57b873 100644 --- a/erpnext/manufacturing/doctype/bom_explosion_item/bom_explosion_item.json +++ b/erpnext/manufacturing/doctype/bom_explosion_item/bom_explosion_item.json @@ -37,7 +37,8 @@ "oldfieldname": "item_code", "oldfieldtype": "Link", "options": "Item", - "read_only": 1 + "read_only": 1, + "search_index": 1 }, { "fieldname": "item_name", @@ -170,7 +171,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-11-14 18:35:40.856895", + "modified": "2024-01-02 13:49:36.211586", "modified_by": "Administrator", "module": "Manufacturing", "name": "BOM Explosion Item", From 9d256e131df0a3e73993ef6b8faffc7b883ca89f Mon Sep 17 00:00:00 2001 From: RJPvT <48353029+RJPvT@users.noreply.github.com> Date: Tue, 2 Jan 2024 22:01:51 +0100 Subject: [PATCH 065/675] fix: wrong file name file name is actually reversed. In [1]: from premailer import Premailer ...: ...: from frappe.utils.jinja_globals import bundled_asset ...: ...: # get email css files from hooks ...: css_files = frappe.get_hooks("email_css") ...: css_files = [frappe.utils.jinja_globals.bundled_asset(path) for path in ...: css_files] ...: css_files = [path.lstrip("/") for path in css_files] In [2]: css_files Out[2]: ['assets/frappe/dist/css/email.bundle.ELTO33N3.css', 'email_erpnext.bundle.css', 'assets/css/email.css'] In [3]: css_files = [css_file for css_file in css_files if os.path.exists(os.pat ...: h.abspath(css_file))] In [4]: css_files Out[4]: ['assets/frappe/dist/css/email.bundle.ELTO33N3.css', 'assets/css/email.css'] --- erpnext/hooks.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 054f9dedabe..884e6bfcabe 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -18,7 +18,7 @@ app_include_js = "erpnext.bundle.js" app_include_css = "erpnext.bundle.css" web_include_js = "erpnext-web.bundle.js" web_include_css = "erpnext-web.bundle.css" -email_css = "email_erpnext.bundle.css" +email_css = "erpnext_email.bundle.scss" doctype_js = { "Address": "public/js/address.js", From b6a36270b2b7196e72180e39bc32277683633673 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 3 Jan 2024 06:01:14 +0000 Subject: [PATCH 066/675] chore(release): Bumped to Version 14.58.0 # [14.58.0](https://github.com/frappe/erpnext/compare/v14.57.0...v14.58.0) (2024-01-03) ### Bug Fixes * Add missing french translations (backport [#38368](https://github.com/frappe/erpnext/issues/38368)) ([#38513](https://github.com/frappe/erpnext/issues/38513)) ([2c90ee2](https://github.com/frappe/erpnext/commit/2c90ee23f9dd1f3011b063c7fc074e6d36f1e5f6)) * **DX:** capture tracebacks with context (backport [#39060](https://github.com/frappe/erpnext/issues/39060)) ([#39062](https://github.com/frappe/erpnext/issues/39062)) ([fe9acc8](https://github.com/frappe/erpnext/commit/fe9acc898ec51073a4a75e3cf20457f0cd161909)) * **Hierarchy Chart:** check if company is set before loading children ([#38985](https://github.com/frappe/erpnext/issues/38985)) ([e4d6df3](https://github.com/frappe/erpnext/commit/e4d6df39ff273402096c32ed0a1bc359d26bfee2)) * issue occured when creating supplier with contact details (backport [#38147](https://github.com/frappe/erpnext/issues/38147)) ([#39046](https://github.com/frappe/erpnext/issues/39046)) ([81ef7b4](https://github.com/frappe/erpnext/commit/81ef7b4c00c02226b2c1f87add9fd022e40a24c6)) * Opening balance in bank reconciliation tool ([#38977](https://github.com/frappe/erpnext/issues/38977)) ([a2cba1b](https://github.com/frappe/erpnext/commit/a2cba1bf237dd26e9cf9be7a4d903afc2441cdf6)) * remove bad defaults (backport [#38986](https://github.com/frappe/erpnext/issues/38986)) ([#38987](https://github.com/frappe/erpnext/issues/38987)) ([29d383a](https://github.com/frappe/erpnext/commit/29d383ad8b76179d977be9b78c891738fd9a3364)) * select options should dynamically load dimensions ([bfc94cf](https://github.com/frappe/erpnext/commit/bfc94cf284cd50af0a5f038b3ab1dfb4000d010d)) * take quantity into account when setting asset's gross purchase amt (backport [#39056](https://github.com/frappe/erpnext/issues/39056)) ([#39057](https://github.com/frappe/erpnext/issues/39057)) ([b8dce3e](https://github.com/frappe/erpnext/commit/b8dce3eeac8c8e6586e29aa7ab0b6eead318a7e2)) * undefined error in Budget Variance and Profitability report ([cad15cd](https://github.com/frappe/erpnext/commit/cad15cdec25d630dc14819647f745d8936c8f908)) * use `Stock Qty` while getting `POS Reserved Qty` (backport [#38962](https://github.com/frappe/erpnext/issues/38962)) ([#38982](https://github.com/frappe/erpnext/issues/38982)) ([7ad42ec](https://github.com/frappe/erpnext/commit/7ad42ec95749c0055a0dc6072024b497a1545d80)) * Validate account in Sales/Purchase Taxes and Charges Template ([#39013](https://github.com/frappe/erpnext/issues/39013)) ([c0b5980](https://github.com/frappe/erpnext/commit/c0b598074c3defaca233231646fb5d558236bbc8)) * work order with multi level, fetch operting cost from sub-assembly (backport [#38992](https://github.com/frappe/erpnext/issues/38992)) ([#39027](https://github.com/frappe/erpnext/issues/39027)) ([88e5c9e](https://github.com/frappe/erpnext/commit/88e5c9e61b27e15e75e3c8c1d060d8ae252ecd1b)) ### Features * Merge taxes from mapped docs ([#38346](https://github.com/frappe/erpnext/issues/38346)) ([c74e6aa](https://github.com/frappe/erpnext/commit/c74e6aaebb0b7900a92c53a53740c4f4d3540a1b)) * Show Ledger view for Purchase & Sales Register ([#38801](https://github.com/frappe/erpnext/issues/38801)) ([04fb215](https://github.com/frappe/erpnext/commit/04fb215ff51a7d36d90d3b3d9f9375035cee496e)) ### Performance Improvements * index item_code in bom explosion item (backport [#39085](https://github.com/frappe/erpnext/issues/39085)) ([#39087](https://github.com/frappe/erpnext/issues/39087)) ([558861b](https://github.com/frappe/erpnext/commit/558861b6341febc7e5499bffc691b05b18e72c7f)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 8386b715981..e5d05ff717f 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.57.0" +__version__ = "14.58.0" def get_default_company(user=None): From 7d64df05bc3a265023a27d92ea6fdc5e444a6864 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 3 Jan 2024 13:00:44 +0530 Subject: [PATCH 067/675] fix: Ignore UP on "allowed to transact with" (backport #39103) (#39104) fix: Ignore UP on "allowed to transact with" (#39103) If a customer is allowed to transact with some company it usually doesn't imply that customer is somehow "linked with" that company. (cherry picked from commit 6401908f419a7d34d7d42d2083fe9f7cd82e7b87) Co-authored-by: Ankush Menat --- .../allowed_to_transact_with/allowed_to_transact_with.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/allowed_to_transact_with/allowed_to_transact_with.json b/erpnext/accounts/doctype/allowed_to_transact_with/allowed_to_transact_with.json index e3f2d59c065..234ffc8a870 100644 --- a/erpnext/accounts/doctype/allowed_to_transact_with/allowed_to_transact_with.json +++ b/erpnext/accounts/doctype/allowed_to_transact_with/allowed_to_transact_with.json @@ -11,6 +11,7 @@ { "fieldname": "company", "fieldtype": "Link", + "ignore_user_permissions": 1, "in_list_view": 1, "label": "Company", "options": "Company", @@ -19,7 +20,7 @@ ], "istable": 1, "links": [], - "modified": "2020-05-01 12:32:34.044911", + "modified": "2024-01-03 11:13:02.669632", "modified_by": "Administrator", "module": "Accounts", "name": "Allowed To Transact With", @@ -28,5 +29,6 @@ "quick_entry": 1, "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file From 9277b0255748f425cf7eec51ed1ef0f351924bf9 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 2 Jan 2024 15:18:15 +0530 Subject: [PATCH 068/675] refactor: flag to control loyalty point creation at invoice level (cherry picked from commit 1bc74bde291f92f450497fef6d30c86a0c0209d2) --- .../doctype/sales_invoice/sales_invoice.json | 14 +++++++++++--- .../doctype/sales_invoice/sales_invoice.py | 7 ++++++- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index 0c337aa62ec..83b7da94110 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -138,6 +138,7 @@ "loyalty_amount", "column_break_77", "loyalty_program", + "dont_create_loyalty_points", "loyalty_redemption_account", "loyalty_redemption_cost_center", "contact_and_address_tab", @@ -1039,8 +1040,7 @@ "label": "Loyalty Program", "no_copy": 1, "options": "Loyalty Program", - "print_hide": 1, - "read_only": 1 + "print_hide": 1 }, { "allow_on_submit": 1, @@ -2153,6 +2153,14 @@ "fieldname": "update_billed_amount_in_delivery_note", "fieldtype": "Check", "label": "Update Billed Amount in Delivery Note" + }, + { + "default": "0", + "depends_on": "loyalty_program", + "fieldname": "dont_create_loyalty_points", + "fieldtype": "Check", + "label": "Don't Create Loyalty Points", + "no_copy": 1 } ], "icon": "fa fa-file-text", @@ -2165,7 +2173,7 @@ "link_fieldname": "consolidated_invoice" } ], - "modified": "2023-11-23 16:56:29.679499", + "modified": "2024-01-02 17:25:46.027523", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 63a576b5ba8..66d9022d08f 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -300,7 +300,12 @@ class SalesInvoice(SellingController): update_linked_doc(self.doctype, self.name, self.inter_company_invoice_reference) # create the loyalty point ledger entry if the customer is enrolled in any loyalty program - if not self.is_return and not self.is_consolidated and self.loyalty_program: + if ( + not self.is_return + and not self.is_consolidated + and self.loyalty_program + and not self.dont_create_loyalty_points + ): self.make_loyalty_point_entry() elif ( self.is_return and self.return_against and not self.is_consolidated and self.loyalty_program From 26ae708d6d59ea04f862a20dcc08555f7bf553ab Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 3 Jan 2024 20:32:55 +0530 Subject: [PATCH 069/675] fix(UX): dont override framework's permission check messages (backport #39118) (#39119) fix(UX): dont override framework's permission check messages (#39118) (cherry picked from commit e84c9f7c51fc6e8c5c631c74131f9290624b2a05) Co-authored-by: Ankush Menat --- erpnext/accounts/party.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index c24d28b3249..c83969b34bc 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -114,14 +114,12 @@ def _get_party_details( set_account_and_due_date(party, account, party_type, company, posting_date, bill_date, doctype) ) party = party_details[party_type.lower()] - - if not ignore_permissions and not ( - frappe.has_permission(party_type, "read", party) - or frappe.has_permission(party_type, "select", party) - ): - frappe.throw(_("Not permitted for {0}").format(party), frappe.PermissionError) - party = frappe.get_doc(party_type, party) + + if not ignore_permissions: + ptype = "select" if frappe.only_has_select_perm(party_type) else "read" + frappe.has_permission(party_type, ptype, party, throw=True) + currency = party.get("default_currency") or currency or get_company_currency(company) party_address, shipping_address = set_address_details( From 71ecf081c312ffca15059de9e3dc446d43815411 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 3 Jan 2024 17:59:15 +0530 Subject: [PATCH 070/675] fix: typerror on multi select dialog (cherry picked from commit 7da9ffa3bd072fbfc9627e6eb5afb3dd3078f560) --- erpnext/public/js/utils.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 35c3828d5b9..682762678ba 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -759,16 +759,20 @@ erpnext.utils.map_current_doc = function(opts) { } if (opts.source_doctype) { + let data_fields = []; + if(opts.source_doctype == "Purchase Receipt") { + data_fields.push({ + fieldname: 'merge_taxes', + fieldtype: 'Check', + label: __('Merge taxes from multiple documents'), + }); + } const d = new frappe.ui.form.MultiSelectDialog({ doctype: opts.source_doctype, target: opts.target, date_field: opts.date_field || undefined, setters: opts.setters, - data_fields: [{ - fieldname: 'merge_taxes', - fieldtype: 'Check', - label: __('Merge taxes from multiple documents'), - }], + data_fields: data_fields, get_query: opts.get_query, add_filters_group: 1, allow_child_item_selection: opts.allow_child_item_selection, @@ -782,7 +786,10 @@ erpnext.utils.map_current_doc = function(opts) { return; } opts.source_name = values; - opts.args = args; + if (opts.allow_child_item_selection || opts.source_doctype == "Purchase Receipt") { + // args contains filtered child docnames + opts.args = args; + } d.dialog.hide(); _map(); }, From 43cc7e4a59c3a84fc748110c06cdfb3bab4721e8 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 3 Jan 2024 17:59:15 +0530 Subject: [PATCH 071/675] fix: typerror on multi select dialog (cherry picked from commit 7da9ffa3bd072fbfc9627e6eb5afb3dd3078f560) --- erpnext/public/js/utils.js | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 35c3828d5b9..682762678ba 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -759,16 +759,20 @@ erpnext.utils.map_current_doc = function(opts) { } if (opts.source_doctype) { + let data_fields = []; + if(opts.source_doctype == "Purchase Receipt") { + data_fields.push({ + fieldname: 'merge_taxes', + fieldtype: 'Check', + label: __('Merge taxes from multiple documents'), + }); + } const d = new frappe.ui.form.MultiSelectDialog({ doctype: opts.source_doctype, target: opts.target, date_field: opts.date_field || undefined, setters: opts.setters, - data_fields: [{ - fieldname: 'merge_taxes', - fieldtype: 'Check', - label: __('Merge taxes from multiple documents'), - }], + data_fields: data_fields, get_query: opts.get_query, add_filters_group: 1, allow_child_item_selection: opts.allow_child_item_selection, @@ -782,7 +786,10 @@ erpnext.utils.map_current_doc = function(opts) { return; } opts.source_name = values; - opts.args = args; + if (opts.allow_child_item_selection || opts.source_doctype == "Purchase Receipt") { + // args contains filtered child docnames + opts.args = args; + } d.dialog.hide(); _map(); }, From feaa16d74855e8eda28b8289a65b7677a825edef Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 3 Jan 2024 15:37:55 +0000 Subject: [PATCH 072/675] chore(release): Bumped to Version 14.58.1 ## [14.58.1](https://github.com/frappe/erpnext/compare/v14.58.0...v14.58.1) (2024-01-03) ### Bug Fixes * typerror on multi select dialog ([43cc7e4](https://github.com/frappe/erpnext/commit/43cc7e4a59c3a84fc748110c06cdfb3bab4721e8)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index e5d05ff717f..c6e5405c7db 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.58.0" +__version__ = "14.58.1" def get_default_company(user=None): From 36b8e972f11cb860c06ca3ca1726103be3217bb4 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 3 Jan 2024 20:56:12 +0530 Subject: [PATCH 073/675] fix: ignore cancelled payments in Sales/Purchase Register (cherry picked from commit 0f1be03faf96aaf83063e28dc57a5e016944683c) --- erpnext/accounts/report/utils.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/utils.py b/erpnext/accounts/report/utils.py index 7dd61d2c64c..01bc29fedf3 100644 --- a/erpnext/accounts/report/utils.py +++ b/erpnext/accounts/report/utils.py @@ -251,6 +251,7 @@ def get_journal_entries(filters, args): ) .where( (je.voucher_type == "Journal Entry") + & (je.docstatus == 1) & (journal_account.party == filters.get(args.party)) & (journal_account.account == args.party_account) ) @@ -281,7 +282,9 @@ def get_payment_entries(filters, args): pe.cost_center, ) .where( - (pe.party == filters.get(args.party)) & (pe[args.account_fieldname] == args.party_account) + (pe.docstatus == 1) + & (pe.party == filters.get(args.party)) + & (pe[args.account_fieldname].isin(args.party_account)) ) .orderby(pe.posting_date, pe.name, order=Order.desc) ) From 080a742725f37c0b4514f534a7fc4362c5276db0 Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Thu, 4 Jan 2024 13:33:28 +0100 Subject: [PATCH 074/675] fix(Employee): treeview (#39126) (cherry picked from commit e912e9597dbd83724afbcd00f3b2acd75b50a8f0) --- erpnext/setup/doctype/employee/employee.json | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/erpnext/setup/doctype/employee/employee.json b/erpnext/setup/doctype/employee/employee.json index 1143ccb7b10..daf2df5a590 100644 --- a/erpnext/setup/doctype/employee/employee.json +++ b/erpnext/setup/doctype/employee/employee.json @@ -616,8 +616,8 @@ "fieldname": "relieving_date", "fieldtype": "Date", "label": "Relieving Date", - "no_copy": 1, "mandatory_depends_on": "eval:doc.status == \"Left\"", + "no_copy": 1, "oldfieldname": "relieving_date", "oldfieldtype": "Date" }, @@ -822,12 +822,14 @@ "icon": "fa fa-user", "idx": 24, "image_field": "image", + "is_tree": 1, "links": [], - "modified": "2023-10-04 10:57:05.174592", + "modified": "2024-01-03 17:36:20.984421", "modified_by": "Administrator", "module": "Setup", "name": "Employee", "naming_rule": "By \"Naming Series\" field", + "nsm_parent_field": "reports_to", "owner": "Administrator", "permissions": [ { @@ -860,7 +862,6 @@ "read": 1, "report": 1, "role": "HR Manager", - "set_user_permissions": 1, "share": 1, "write": 1 } @@ -871,4 +872,4 @@ "sort_order": "DESC", "states": [], "title_field": "employee_name" -} +} \ No newline at end of file From 2866f7c44169098b691a5fa31cef5cb2b6ce6e81 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 5 Jan 2024 05:03:22 +0530 Subject: [PATCH 075/675] fix: inventory dimension negative stock validation (backport #39149) (#39150) fix: inventory dimension negative stock validation (#39149) (cherry picked from commit bae7c6496402466df3af15c6bffcf51b97ae2037) Co-authored-by: rohitwaghchaure --- .../inventory_dimension/inventory_dimension.py | 2 +- .../test_inventory_dimension.py | 8 ++++++++ .../stock_ledger_entry/stock_ledger_entry.py | 14 +++++++++----- 3 files changed, 18 insertions(+), 6 deletions(-) diff --git a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py index 257d18fc33a..a7ead064c97 100644 --- a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py +++ b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py @@ -281,7 +281,7 @@ def get_evaluated_inventory_dimension(doc, sl_dict, parent_doc=None): dimensions = get_document_wise_inventory_dimensions(doc.doctype) filter_dimensions = [] for row in dimensions: - if row.type_of_transaction: + if row.type_of_transaction and row.type_of_transaction != "Both": if ( row.type_of_transaction == "Inward" if doc.docstatus == 1 diff --git a/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py b/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py index 531bc3f109f..59389c7d7df 100644 --- a/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py +++ b/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py @@ -429,6 +429,14 @@ class TestInventoryDimension(FrappeTestCase): ) warehouse = create_warehouse("Negative Stock Warehouse") + + 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) + doc.reload() + if doc.docstatus == 1: + doc.cancel() + doc = make_stock_entry(item_code=item_code, target=warehouse, qty=10, do_not_submit=True) doc.items[0].to_inv_site = "Site 1" 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 921b04aab8c..1d52b17149b 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py @@ -71,16 +71,20 @@ class StockLedgerEntry(Document): "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: - flt_precision = cint(frappe.db.get_default("float_precision")) or 2 - diff = sle.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) + qty_after_transaction = sle.qty_after_transaction + + 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) def throw_validation_error(self, diff, dimensions): dimension_msg = _(", with the inventory {0}: {1}").format( From f236a2c081d8d10522848af417c7a061811991ee Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 5 Jan 2024 14:32:05 +0530 Subject: [PATCH 076/675] refactor: prevent permissions by always processing in background (cherry picked from commit 15dc5c7e9966e3bf06ec0aa951d7185fb6cf6d0e) --- erpnext/utilities/bulk_transaction.py | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/erpnext/utilities/bulk_transaction.py b/erpnext/utilities/bulk_transaction.py index 7577f0a379e..b519617435f 100644 --- a/erpnext/utilities/bulk_transaction.py +++ b/erpnext/utilities/bulk_transaction.py @@ -15,18 +15,15 @@ def transaction_processing(data, from_doctype, to_doctype): length_of_data = len(deserialized_data) - if length_of_data > 10: - frappe.msgprint( - _("Started a background job to create {1} {0}").format(to_doctype, length_of_data) - ) - frappe.enqueue( - job, - deserialized_data=deserialized_data, - from_doctype=from_doctype, - to_doctype=to_doctype, - ) - else: - job(deserialized_data, from_doctype, to_doctype) + frappe.msgprint( + _("Started a background job to create {1} {0}").format(to_doctype, length_of_data) + ) + frappe.enqueue( + job, + deserialized_data=deserialized_data, + from_doctype=from_doctype, + to_doctype=to_doctype, + ) @frappe.whitelist() From 06d193ad87ebb98bbfad1d79fa0c9afaf5f860d2 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 8 Jan 2024 11:44:52 +0530 Subject: [PATCH 077/675] fix: don't set rate for non-stock item in Internal Transfer (backport #39140) (#39168) * fix: don't set rate for non-stock item in Internal Transfer (cherry picked from commit e1b0fffd0c934d376562e3ba8707b38426ea09a1) * test: internal transfer for non-stock item (cherry picked from commit 57b6a987034522266caca853d4ee26316526e526) # Conflicts: # erpnext/stock/doctype/delivery_note/test_delivery_note.py * chore: `conflicts` --------- Co-authored-by: s-aga-r --- erpnext/controllers/selling_controller.py | 3 +++ .../delivery_note/test_delivery_note.py | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index d3157e7037b..d55aeeacc08 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -422,6 +422,9 @@ class SellingController(StockController): items = self.get("items") + (self.get("packed_items") or []) for d in items: + if not frappe.get_cached_value("Item", d.item_code, "is_stock_item"): + continue + if not self.get("return_against") or ( get_valuation_method(d.item_code) == "Moving Average" and self.get("is_return") ): diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index a931ee2c254..9f2c0be75d0 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -1435,6 +1435,25 @@ class TestDeliveryNote(FrappeTestCase): returned_dn.reload() self.assertAlmostEqual(returned_dn.items[0].incoming_rate, 200.0) + def test_internal_transfer_for_non_stock_item(self): + from erpnext.selling.doctype.customer.test_customer import create_internal_customer + from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note + + item = make_item(properties={"is_stock_item": 0}).name + warehouse = "_Test Warehouse - _TC" + target = "Stores - _TC" + company = "_Test Company" + customer = create_internal_customer(represents_company=company) + rate = 100 + + so = make_sales_order(item_code=item, qty=1, rate=rate, customer=customer, warehouse=warehouse) + dn = make_delivery_note(so.name) + dn.items[0].target_warehouse = target + dn.save().submit() + + self.assertEqual(so.items[0].rate, rate) + self.assertEqual(dn.items[0].rate, so.items[0].rate) + def create_delivery_note(**args): dn = frappe.new_doc("Delivery Note") From 69c460c7562103771d5238c1f02af04493651dd8 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Mon, 8 Jan 2024 11:36:37 +0530 Subject: [PATCH 078/675] fix: set `First Name` in Supplier Contact --- erpnext/selling/doctype/customer/customer.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index fea27da61b2..b7fcadb1eca 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -706,7 +706,9 @@ def make_contact(args, is_primary_contact=1): else: values.update( { - "first_name": args.get("customer_name"), + "first_name": args.get("customer_name") + if args.doctype == "Customer" + else args.get("supplier_name"), "company_name": args.get(party_name_key), } ) From 580e9f6e10e21dab07080ac2884d0095412239b1 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 5 Jan 2024 17:12:08 +0530 Subject: [PATCH 079/675] fix: improved validation message (cherry picked from commit fe43dab4d78091e626ddfb6432b2d69f84793d92) --- erpnext/assets/doctype/asset_category/asset_category.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/assets/doctype/asset_category/asset_category.py b/erpnext/assets/doctype/asset_category/asset_category.py index 8d351412ca8..2a204e11477 100644 --- a/erpnext/assets/doctype/asset_category/asset_category.py +++ b/erpnext/assets/doctype/asset_category/asset_category.py @@ -67,12 +67,12 @@ class AssetCategory(Document): if selected_key_type not in expected_key_types: frappe.throw( _( - "Row #{}: {} of {} should be {}. Please modify the account or select a different account." + "Row #{0}: {1} of {2} should be {3}. Please update the {1} or select a different account." ).format( d.idx, frappe.unscrub(key_to_match), frappe.bold(selected_account), - frappe.bold(expected_key_types), + frappe.bold(" or ".join(expected_key_types)), ), title=_("Invalid Account"), ) From 0f6477a253844d6e1fc530cbc5d0f7a38eaf0aa4 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 5 Jan 2024 17:38:51 +0530 Subject: [PATCH 080/675] fix: Purchase date and amount is not mandatory for composite asset creation (cherry picked from commit c34f09c503b367f6ea1eee5e24f8aa6e94976e01) # Conflicts: # erpnext/assets/doctype/asset/asset.py --- erpnext/assets/doctype/asset/asset.json | 12 ++-- erpnext/assets/doctype/asset/asset.py | 91 ++++++++++++++++++++++++- 2 files changed, 96 insertions(+), 7 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json index 91ae62bfad7..cdccf81507b 100644 --- a/erpnext/assets/doctype/asset/asset.json +++ b/erpnext/assets/doctype/asset/asset.json @@ -202,9 +202,9 @@ "fieldname": "purchase_date", "fieldtype": "Date", "label": "Purchase Date", + "mandatory_depends_on": "eval:!doc.is_existing_asset", "read_only": 1, - "read_only_depends_on": "eval:!doc.is_existing_asset && !doc.is_composite_asset", - "reqd": 1 + "read_only_depends_on": "eval:!doc.is_existing_asset && !doc.is_composite_asset" }, { "fieldname": "disposal_date", @@ -227,15 +227,15 @@ "fieldname": "gross_purchase_amount", "fieldtype": "Currency", "label": "Gross Purchase Amount", + "mandatory_depends_on": "eval:(!doc.is_composite_asset || doc.docstatus==1)", "options": "Company:company:default_currency", - "read_only_depends_on": "eval:!doc.is_existing_asset", - "reqd": 1 + "read_only_depends_on": "eval:!doc.is_existing_asset" }, { "fieldname": "available_for_use_date", "fieldtype": "Date", "label": "Available-for-use Date", - "reqd": 1 + "mandatory_depends_on": "eval:(!doc.is_composite_asset || doc.docstatus==1)" }, { "default": "0", @@ -583,7 +583,7 @@ "link_fieldname": "target_asset" } ], - "modified": "2023-12-21 16:46:20.732869", + "modified": "2024-01-05 17:36:53.131512", "modified_by": "Administrator", "module": "Assets", "name": "Asset", diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index f83d7071aa7..300d95d37ee 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -34,6 +34,86 @@ from erpnext.controllers.accounts_controller import AccountsController class Asset(AccountsController): +<<<<<<< HEAD +======= + # 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.assets.doctype.asset_finance_book.asset_finance_book import AssetFinanceBook + + additional_asset_cost: DF.Currency + amended_from: DF.Link | None + asset_category: DF.Link | None + asset_name: DF.Data + asset_owner: DF.Literal["", "Company", "Supplier", "Customer"] + asset_owner_company: DF.Link | None + asset_quantity: DF.Int + available_for_use_date: DF.Date | None + booked_fixed_asset: DF.Check + calculate_depreciation: DF.Check + capitalized_in: DF.Link | None + company: DF.Link + comprehensive_insurance: DF.Data | None + cost_center: DF.Link | None + custodian: DF.Link | None + customer: DF.Link | None + default_finance_book: DF.Link | None + department: DF.Link | None + depr_entry_posting_status: DF.Literal["", "Successful", "Failed"] + depreciation_method: DF.Literal["", "Straight Line", "Double Declining Balance", "Manual"] + disposal_date: DF.Date | None + finance_books: DF.Table[AssetFinanceBook] + frequency_of_depreciation: DF.Int + gross_purchase_amount: DF.Currency + image: DF.AttachImage | None + insurance_end_date: DF.Date | None + insurance_start_date: DF.Date | None + insured_value: DF.Data | None + insurer: DF.Data | None + is_composite_asset: DF.Check + is_existing_asset: DF.Check + is_fully_depreciated: DF.Check + item_code: DF.Link + item_name: DF.ReadOnly | None + journal_entry_for_scrap: DF.Link | None + location: DF.Link + maintenance_required: DF.Check + naming_series: DF.Literal["ACC-ASS-.YYYY.-"] + next_depreciation_date: DF.Date | None + number_of_depreciations_booked: DF.Int + opening_accumulated_depreciation: DF.Currency + policy_number: DF.Data | None + purchase_date: DF.Date | None + purchase_invoice: DF.Link | None + purchase_receipt: DF.Link | None + purchase_receipt_amount: DF.Currency + split_from: DF.Link | None + status: DF.Literal[ + "Draft", + "Submitted", + "Partially Depreciated", + "Fully Depreciated", + "Sold", + "Scrapped", + "In Maintenance", + "Out of Order", + "Issue", + "Receipt", + "Capitalized", + "Decapitalized", + ] + supplier: DF.Link | None + total_asset_cost: DF.Currency + total_number_of_depreciations: DF.Int + value_after_depreciation: DF.Currency + # end: auto-generated types + +>>>>>>> c34f09c503 (fix: Purchase date and amount is not mandatory for composite asset creation) def validate(self): self.validate_asset_values() self.validate_asset_and_reference() @@ -247,7 +327,16 @@ class Asset(AccountsController): frappe.throw(_("Gross Purchase Amount is mandatory"), frappe.MandatoryError) if is_cwip_accounting_enabled(self.asset_category): +<<<<<<< HEAD if not self.is_existing_asset and not (self.purchase_receipt or self.purchase_invoice): +======= + if ( + not self.is_existing_asset + and not self.is_composite_asset + and not self.purchase_receipt + and not self.purchase_invoice + ): +>>>>>>> c34f09c503 (fix: Purchase date and amount is not mandatory for composite asset creation) frappe.throw( _("Please create purchase receipt or purchase invoice for the item {0}").format( self.item_code @@ -260,7 +349,7 @@ class Asset(AccountsController): and not frappe.db.get_value("Purchase Invoice", self.purchase_invoice, "update_stock") ): frappe.throw( - _("Update stock must be enable for the purchase invoice {0}").format(self.purchase_invoice) + _("Update stock must be enabled for the purchase invoice {0}").format(self.purchase_invoice) ) if not self.calculate_depreciation: From 2ea2146b343d13e4cc230e4d1ab8924f997e2b15 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 8 Jan 2024 16:27:19 +0530 Subject: [PATCH 081/675] fix: update Maintenance Schedule status on Maintenance Visit submit (backport #39167) (#39185) * fix: make `Sales Person` non-mandatory (cherry picked from commit 4d56f725fe23495fa2392be01545c5d2fa8889b3) * fix: update Maintenance Schedule status on Maintenance Visit submit (cherry picked from commit cd293a5173aaec693afee23e9b874b1db649a267) # Conflicts: # erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py # erpnext/maintenance/doctype/maintenance_visit_purpose/maintenance_visit_purpose.py * chore: `conflicts` --------- Co-authored-by: s-aga-r --- .../maintenance_schedule.py | 48 +++++++++------ .../maintenance_visit/maintenance_visit.py | 59 +++++++++++++++---- .../maintenance_visit_purpose.json | 15 ++++- 3 files changed, 89 insertions(+), 33 deletions(-) diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py index 95e2d694a58..8e4af3ac172 100644 --- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py +++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py @@ -80,7 +80,7 @@ class MaintenanceSchedule(TransactionBase): self.update_amc_date(serial_nos, d.end_date) no_email_sp = [] - if d.sales_person not in email_map: + if d.sales_person and d.sales_person not in email_map: sp = frappe.get_doc("Sales Person", d.sales_person) try: email_map[d.sales_person] = sp.get_email_id() @@ -94,12 +94,11 @@ class MaintenanceSchedule(TransactionBase): ).format(self.owner, "
    " + "
    ".join(no_email_sp)) ) - scheduled_date = frappe.db.sql( - """select scheduled_date from - `tabMaintenance Schedule Detail` where sales_person=%s and item_code=%s and - parent=%s""", - (d.sales_person, d.item_code, self.name), - as_dict=1, + scheduled_date = frappe.db.get_all( + "Maintenance Schedule Detail", + {"parent": self.name, "item_code": d.item_code}, + ["scheduled_date"], + as_list=False, ) for key in scheduled_date: @@ -195,8 +194,6 @@ class MaintenanceSchedule(TransactionBase): throw(_("Please select Start Date and End Date for Item {0}").format(d.item_code)) elif not d.no_of_visits: throw(_("Please mention no of visits required")) - elif not d.sales_person: - throw(_("Please select a Sales Person for item: {0}").format(d.item_name)) if getdate(d.start_date) >= getdate(d.end_date): throw(_("Start date should be less than end date for Item {0}").format(d.item_code)) @@ -392,16 +389,28 @@ def get_serial_nos_from_schedule(item_code, schedule=None): def make_maintenance_visit(source_name, target_doc=None, item_name=None, s_id=None): from frappe.model.mapper import get_mapped_doc + def condition(doc): + if s_id: + return doc.name == s_id + elif item_name: + return doc.item_name == item_name + + return True + def update_status_and_detail(source, target, parent): target.maintenance_type = "Scheduled" - target.maintenance_schedule_detail = s_id def update_serial(source, target, parent): - serial_nos = get_serial_nos(target.serial_no) - if len(serial_nos) == 1: - target.serial_no = serial_nos[0] - else: - target.serial_no = "" + if source.item_reference: + if serial_nos := frappe.db.get_value( + "Maintenance Schedule Item", source.item_reference, "serial_no" + ): + serial_nos = serial_nos.split("\n") + + if len(serial_nos) == 1: + target.serial_no = serial_nos[0] + else: + target.serial_no = "" doclist = get_mapped_doc( "Maintenance Schedule", @@ -413,10 +422,13 @@ def make_maintenance_visit(source_name, target_doc=None, item_name=None, s_id=No "validation": {"docstatus": ["=", 1]}, "postprocess": update_status_and_detail, }, - "Maintenance Schedule Item": { + "Maintenance Schedule Detail": { "doctype": "Maintenance Visit Purpose", - "condition": lambda doc: doc.item_name == item_name if item_name else True, - "field_map": {"sales_person": "service_person"}, + "condition": condition, + "field_map": { + "sales_person": "service_person", + "name": "maintenance_schedule_detail", + }, "postprocess": update_serial, }, }, diff --git a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py index 0d319bf7424..a0974e99040 100644 --- a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py +++ b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py @@ -23,20 +23,39 @@ class MaintenanceVisit(TransactionBase): frappe.throw(_("Add Items in the Purpose Table"), title=_("Purposes Required")) def validate_maintenance_date(self): - if self.maintenance_type == "Scheduled" and self.maintenance_schedule_detail: - item_ref = frappe.db.get_value( - "Maintenance Schedule Detail", self.maintenance_schedule_detail, "item_reference" - ) - if item_ref: - start_date, end_date = frappe.db.get_value( - "Maintenance Schedule Item", item_ref, ["start_date", "end_date"] + if self.maintenance_type == "Scheduled": + if self.maintenance_schedule_detail: + item_ref = frappe.db.get_value( + "Maintenance Schedule Detail", self.maintenance_schedule_detail, "item_reference" ) - if get_datetime(self.mntc_date) < get_datetime(start_date) or get_datetime( - self.mntc_date - ) > get_datetime(end_date): - frappe.throw( - _("Date must be between {0} and {1}").format(format_date(start_date), format_date(end_date)) + if item_ref: + start_date, end_date = frappe.db.get_value( + "Maintenance Schedule Item", item_ref, ["start_date", "end_date"] ) + if get_datetime(self.mntc_date) < get_datetime(start_date) or get_datetime( + self.mntc_date + ) > get_datetime(end_date): + frappe.throw( + _("Date must be between {0} and {1}").format(format_date(start_date), format_date(end_date)) + ) + else: + for purpose in self.purposes: + if purpose.maintenance_schedule_detail: + item_ref = frappe.db.get_value( + "Maintenance Schedule Detail", purpose.maintenance_schedule_detail, "item_reference" + ) + if item_ref: + start_date, end_date = frappe.db.get_value( + "Maintenance Schedule Item", item_ref, ["start_date", "end_date"] + ) + if get_datetime(self.mntc_date) < get_datetime(start_date) or get_datetime( + self.mntc_date + ) > get_datetime(end_date): + frappe.throw( + _("Date must be between {0} and {1}").format( + format_date(start_date), format_date(end_date) + ) + ) def validate(self): self.validate_serial_no() @@ -49,6 +68,7 @@ class MaintenanceVisit(TransactionBase): if not cancel: status = self.completion_status actual_date = self.mntc_date + if self.maintenance_schedule_detail: frappe.db.set_value( "Maintenance Schedule Detail", self.maintenance_schedule_detail, "completion_status", status @@ -56,6 +76,21 @@ class MaintenanceVisit(TransactionBase): frappe.db.set_value( "Maintenance Schedule Detail", self.maintenance_schedule_detail, "actual_date", actual_date ) + else: + for purpose in self.purposes: + if purpose.maintenance_schedule_detail: + frappe.db.set_value( + "Maintenance Schedule Detail", + purpose.maintenance_schedule_detail, + "completion_status", + status, + ) + frappe.db.set_value( + "Maintenance Schedule Detail", + purpose.maintenance_schedule_detail, + "actual_date", + actual_date, + ) def update_customer_issue(self, flag): if not self.maintenance_schedule: diff --git a/erpnext/maintenance/doctype/maintenance_visit_purpose/maintenance_visit_purpose.json b/erpnext/maintenance/doctype/maintenance_visit_purpose/maintenance_visit_purpose.json index ba053555531..a5a63c4c4de 100644 --- a/erpnext/maintenance/doctype/maintenance_visit_purpose/maintenance_visit_purpose.json +++ b/erpnext/maintenance/doctype/maintenance_visit_purpose/maintenance_visit_purpose.json @@ -17,7 +17,8 @@ "work_details", "work_done", "prevdoc_doctype", - "prevdoc_docname" + "prevdoc_docname", + "maintenance_schedule_detail" ], "fields": [ { @@ -49,6 +50,8 @@ "options": "Serial No" }, { + "fetch_from": "item_code.description", + "fetch_if_empty": 1, "fieldname": "description", "fieldtype": "Text Editor", "in_list_view": 1, @@ -56,7 +59,6 @@ "oldfieldname": "description", "oldfieldtype": "Small Text", "print_width": "300px", - "reqd": 1, "width": "300px" }, { @@ -103,12 +105,19 @@ { "fieldname": "section_break_6", "fieldtype": "Section Break" + }, + { + "fieldname": "maintenance_schedule_detail", + "fieldtype": "Data", + "hidden": 1, + "label": "Maintenance Schedule Detail", + "options": "Maintenance Schedule Detail" } ], "idx": 1, "istable": 1, "links": [], - "modified": "2023-02-27 11:09:33.114458", + "modified": "2024-01-05 21:46:53.239830", "modified_by": "Administrator", "module": "Maintenance", "name": "Maintenance Visit Purpose", From 5e517cfc775184681a4549b617e7ccaf56f1bbfc Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 8 Jan 2024 17:43:36 +0530 Subject: [PATCH 082/675] fix: FG Item incorrect qty in the work order (backport #39200) (#39210) fix: FG Item incorrect qty in the work order (#39200) (cherry picked from commit 466625213b6355b53b7af3ee1682cee6fc8649a4) Co-authored-by: rohitwaghchaure --- .../production_plan/production_plan.py | 11 ++--- .../production_plan/test_production_plan.py | 41 ++++++++++++++++++- 2 files changed, 46 insertions(+), 6 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index ee2e58bfb46..767b4ccbb1a 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -570,6 +570,10 @@ class ProductionPlan(Document): "project": self.project, } + key = (d.item_code, d.sales_order, d.warehouse) + if not d.sales_order: + key = (d.name, d.item_code, d.warehouse) + if not item_details["project"] and d.sales_order: item_details["project"] = frappe.get_cached_value("Sales Order", d.sales_order, "project") @@ -578,12 +582,9 @@ class ProductionPlan(Document): item_dict[(d.item_code, d.material_request_item, d.warehouse)] = item_details else: item_details.update( - { - "qty": flt(item_dict.get((d.item_code, d.sales_order, d.warehouse), {}).get("qty")) - + (flt(d.planned_qty) - flt(d.ordered_qty)) - } + {"qty": flt(item_dict.get(key, {}).get("qty")) + (flt(d.planned_qty) - flt(d.ordered_qty))} ) - item_dict[(d.item_code, d.sales_order, d.warehouse)] = item_details + item_dict[key] = item_details return item_dict diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py index 3695ae9d9d8..4f6280ade8f 100644 --- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py @@ -661,7 +661,7 @@ class TestProductionPlan(FrappeTestCase): items_data = pln.get_production_items() # Update qty - items_data[(item, None, None)]["qty"] = qty + items_data[(pln.po_items[0].name, item, None)]["qty"] = qty # Create and Submit Work Order for each item in items_data for key, item in items_data.items(): @@ -1511,6 +1511,45 @@ class TestProductionPlan(FrappeTestCase): for d in mr_items: self.assertEqual(d.get("quantity"), 1000.0) + def test_fg_item_quantity(self): + from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse + from erpnext.stock.utils import get_or_make_bin + + fg_item = make_item(properties={"is_stock_item": 1}).name + rm_item = make_item(properties={"is_stock_item": 1}).name + + make_bom(item=fg_item, raw_materials=[rm_item], source_warehouse="_Test Warehouse - _TC") + + pln = create_production_plan(item_code=fg_item, planned_qty=10, do_not_submit=1) + + pln.append( + "po_items", + { + "item_code": rm_item, + "planned_qty": 20, + "bom_no": pln.po_items[0].bom_no, + "warehouse": pln.po_items[0].warehouse, + "planned_start_date": add_to_date(nowdate(), days=1), + }, + ) + pln.submit() + wo_qty = {} + + for row in pln.po_items: + wo_qty[row.name] = row.planned_qty + + pln.make_work_order() + + work_orders = frappe.get_all( + "Work Order", + fields=["qty", "production_plan_item as name"], + filters={"production_plan": pln.name}, + ) + self.assertEqual(len(work_orders), 2) + + for row in work_orders: + self.assertEqual(row.qty, wo_qty[row.name]) + def create_production_plan(**args): """ From 7b4b630b2c441481b7121df90e4640654e9aed2e Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 8 Jan 2024 18:15:55 +0530 Subject: [PATCH 083/675] fix: Show maintain-stock and is-fixed-asset checkbox in item quick entry dialog (cherry picked from commit c14986f9e6e617b67e97c8d25843a4dc33b77dcf) --- erpnext/stock/doctype/item/item.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index 7afc031d87d..b9256c8ee56 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -203,6 +203,7 @@ "label": "Allow Alternative Item" }, { + "allow_in_quick_entry": 1, "bold": 1, "default": "1", "depends_on": "eval:!doc.is_fixed_asset", @@ -240,6 +241,7 @@ "label": "Standard Selling Rate" }, { + "allow_in_quick_entry": 1, "default": "0", "fieldname": "is_fixed_asset", "fieldtype": "Check", @@ -247,6 +249,7 @@ "set_only_once": 1 }, { + "allow_in_quick_entry": 1, "depends_on": "is_fixed_asset", "fieldname": "asset_category", "fieldtype": "Link", @@ -897,7 +900,7 @@ "index_web_pages_for_search": 1, "links": [], "make_attachments_public": 1, - "modified": "2023-09-18 15:41:32.688051", + "modified": "2024-01-08 18:09:30.225085", "modified_by": "Administrator", "module": "Stock", "name": "Item", From d0e1162c964bb08c96d6b9a2ed726761d8bc2488 Mon Sep 17 00:00:00 2001 From: Kevin Shenk Date: Fri, 20 Jan 2023 10:09:37 -0800 Subject: [PATCH 084/675] feat: Copy project_name, from_time, to_time from timesheet details to sales invoice (#33726) feat: Copy project_name, from_time, to_time from timesheet details to sales invoice (cherry picked from commit e4bceaaf6627fafce12bdf5effce95b9088a7898) --- erpnext/projects/doctype/timesheet/timesheet.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py index e63ac144f64..9e52befd22e 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -389,6 +389,9 @@ def make_sales_invoice(source_name, item_code=None, customer=None, currency=None "timesheets", { "time_sheet": timesheet.name, + "project_name": time_log.project_name, + "from_time": time_log.from_time, + "to_time": time_log.to_time, "billing_hours": time_log.billing_hours, "billing_amount": time_log.billing_amount, "timesheet_detail": time_log.name, From 71f9b7f67508ef04ad5f1f463e4df6c4dca83554 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 9 Jan 2024 12:40:41 +0530 Subject: [PATCH 085/675] fix: total allocated percentage for sales team issue (cherry picked from commit b498094a9759bb95a3c77c6ec6877aafc6daa0ea) --- .../selling/doctype/quotation/quotation.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 88e24ad542a..539960a508f 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -288,15 +288,16 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False): ) # 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 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.delivery_date = nowdate() From c2eeeecac8c934098d289eb32bfbe995587f6987 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 9 Jan 2024 15:59:24 +0530 Subject: [PATCH 086/675] fix: BOM replace tool does not update exploded items of root (backport #39244) (#39249) fix: BOM replace tool does not update exploded items of root (#39244) (cherry picked from commit 5e0d017497ef7afaaa70abffbd3d3924f9e56063) Co-authored-by: rohitwaghchaure --- .../bom_update_log/bom_updation_utils.py | 6 +- .../bom_update_log/test_bom_update_log.py | 62 +++++++++++++++++++ 2 files changed, 66 insertions(+), 2 deletions(-) diff --git a/erpnext/manufacturing/doctype/bom_update_log/bom_updation_utils.py b/erpnext/manufacturing/doctype/bom_update_log/bom_updation_utils.py index a2919b79b80..f013b88e946 100644 --- a/erpnext/manufacturing/doctype/bom_update_log/bom_updation_utils.py +++ b/erpnext/manufacturing/doctype/bom_update_log/bom_updation_utils.py @@ -86,10 +86,12 @@ def get_ancestor_boms(new_bom: str, bom_list: Optional[List] = None) -> List: if new_bom == d.parent: frappe.throw(_("BOM recursion: {0} cannot be child of {1}").format(new_bom, d.parent)) - bom_list.append(d.parent) + if d.parent not in tuple(bom_list): + bom_list.append(d.parent) + get_ancestor_boms(d.parent, bom_list) - return list(set(bom_list)) + return bom_list def update_new_bom_in_bom_items(unit_cost: float, current_bom: str, new_bom: str) -> None: diff --git a/erpnext/manufacturing/doctype/bom_update_log/test_bom_update_log.py b/erpnext/manufacturing/doctype/bom_update_log/test_bom_update_log.py index b38fc8976b2..30e6f5e2091 100644 --- a/erpnext/manufacturing/doctype/bom_update_log/test_bom_update_log.py +++ b/erpnext/manufacturing/doctype/bom_update_log/test_bom_update_log.py @@ -57,6 +57,68 @@ class TestBOMUpdateLog(FrappeTestCase): log.reload() self.assertEqual(log.status, "Completed") + def test_bom_replace_for_root_bom(self): + """ + - B-Item A (Root Item) + - B-Item B + - B-Item C + - B-Item D + - B-Item E + - B-Item F + + Create New BOM for B-Item E with B-Item G and replace it in the above BOM. + """ + + from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom + from erpnext.stock.doctype.item.test_item import make_item + + items = ["B-Item A", "B-Item B", "B-Item C", "B-Item D", "B-Item E", "B-Item F", "B-Item G"] + + for item_code in items: + if not frappe.db.exists("Item", item_code): + make_item(item_code) + + for item_code in items: + remove_bom(item_code) + + bom_tree = { + "B-Item A": {"B-Item B": {"B-Item C": {}}, "B-Item D": {"B-Item E": {"B-Item F": {}}}} + } + + root_bom = create_nested_bom(bom_tree, prefix="") + + exploded_items = frappe.get_all( + "BOM Explosion Item", filters={"parent": root_bom.name}, fields=["item_code"] + ) + + exploded_items = [item.item_code for item in exploded_items] + expected_exploded_items = ["B-Item C", "B-Item F"] + self.assertEqual(sorted(exploded_items), sorted(expected_exploded_items)) + + old_bom = frappe.db.get_value("BOM", {"item": "B-Item E"}, "name") + bom_tree = {"B-Item E": {"B-Item G": {}}} + + new_bom = create_nested_bom(bom_tree, prefix="") + enqueue_replace_bom(boms=frappe._dict(current_bom=old_bom, new_bom=new_bom.name)) + + exploded_items = frappe.get_all( + "BOM Explosion Item", filters={"parent": root_bom.name}, fields=["item_code"] + ) + + exploded_items = [item.item_code for item in exploded_items] + expected_exploded_items = ["B-Item C", "B-Item G"] + self.assertEqual(sorted(exploded_items), sorted(expected_exploded_items)) + + +def remove_bom(item_code): + boms = frappe.get_all("BOM", fields=["docstatus", "name"], filters={"item": item_code}) + + for row in boms: + if row.docstatus == 1: + frappe.get_doc("BOM", row.name).cancel() + + frappe.delete_doc("BOM", row.name) + def update_cost_in_all_boms_in_test(): """ From 36ba33c5009e1526a25cc55ed39de5208aa5f090 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 9 Jan 2024 17:42:04 +0530 Subject: [PATCH 087/675] fix: TypeError is pricing rules (backport #39252) (#39259) fix: TypeError is pricing rules (#39252) (cherry picked from commit 274c65c451ccc71f11f29a2abc6d13b2b49b776c) 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 65d4595fcd5..8396783ffa1 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -521,7 +521,7 @@ def get_qty_amount_data_for_cumulative(pr_doc, doc, items=None): values.extend(warehouses) if items: - condition = " and `tab{child_doc}`.{apply_on} in ({items})".format( + condition += " and `tab{child_doc}`.{apply_on} in ({items})".format( child_doc=child_doctype, apply_on=apply_on, items=",".join(["%s"] * len(items)) ) From 8c496fbf2b0653a4c2ef1cb23c9b07cab98b8ebe Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 9 Jan 2024 17:43:18 +0530 Subject: [PATCH 088/675] fix: incorrect indicator title for portal sales order (backport #39247) (#39254) fix: incorrect indicator title for portal sales order (#39247) (cherry picked from commit 2d2ff7cf52a548b8e880cd212cbbe6ef4569ea28) Co-authored-by: rohitwaghchaure --- .../doctype/sales_order/sales_order.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index cbc11ed02af..bec24fe5e99 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -432,17 +432,17 @@ class SalesOrder(SellingController): def set_indicator(self): """Set indicator for portal""" - if self.per_billed < 100 and self.per_delivered < 100: - self.indicator_color = "orange" - self.indicator_title = _("Not Paid and Not Delivered") + self.indicator_color = { + "Draft": "red", + "On Hold": "orange", + "To Deliver and Bill": "orange", + "To Bill": "orange", + "To Deliver": "orange", + "Completed": "green", + "Cancelled": "red", + }.get(self.status, "blue") - elif self.per_billed == 100 and self.per_delivered < 100: - self.indicator_color = "orange" - self.indicator_title = _("Paid and Not Delivered") - - else: - self.indicator_color = "green" - self.indicator_title = _("Paid") + self.indicator_title = _(self.status) def on_recurring(self, reference_doc, auto_repeat_doc): def _get_delivery_date(ref_doc_delivery_date, red_doc_transaction_date, transaction_date): From a15ad804d008613afdf92e9dcd2b8ac415780304 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 9 Jan 2024 19:09:06 +0530 Subject: [PATCH 089/675] fix: add read permission to Buying Settings (backport #39158) (#39257) * fix: add read permission to Buying Settings (cherry picked from commit e05bf9d32a00aac55021faaacbfa914ee9f7275a) # Conflicts: # erpnext/buying/doctype/buying_settings/buying_settings.json * chore: resolve merge conflicts --------- Co-authored-by: s-aga-r Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com> --- .../buying_settings/buying_settings.json | 39 ++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.json b/erpnext/buying/doctype/buying_settings/buying_settings.json index 0af93bfc902..5be70288c56 100644 --- a/erpnext/buying/doctype/buying_settings/buying_settings.json +++ b/erpnext/buying/doctype/buying_settings/buying_settings.json @@ -188,7 +188,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2023-11-24 10:55:51.287327", + "modified": "2024-01-05 15:26:02.320942", "modified_by": "Administrator", "module": "Buying", "name": "Buying Settings", @@ -212,10 +212,45 @@ "role": "Purchase Manager", "share": 1, "write": 1 + }, + { + "email": 1, + "print": 1, + "read": 1, + "role": "Accounts User", + "share": 1 + }, + { + "email": 1, + "print": 1, + "read": 1, + "role": "Accounts Manager", + "share": 1 + }, + { + "email": 1, + "print": 1, + "read": 1, + "role": "Stock Manager", + "share": 1 + }, + { + "email": 1, + "print": 1, + "read": 1, + "role": "Stock User", + "share": 1 + }, + { + "email": 1, + "print": 1, + "read": 1, + "role": "Purchase User", + "share": 1 } ], "sort_field": "modified", "sort_order": "DESC", "states": [], "track_changes": 1 -} \ No newline at end of file +} From a3146c39dd36d8edb7b99cbd57ade2472f475ee2 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 9 Jan 2024 20:37:49 +0530 Subject: [PATCH 090/675] fix: Duplicate Closing Stock Balance (backport #39262) (#39263) fix: Duplicate Closing Stock Balance (cherry picked from commit b15795392bdf8165e7ddfb60451353ebefa177d2) Co-authored-by: s-aga-r --- .../doctype/closing_stock_balance/closing_stock_balance.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.py b/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.py index 2f7a2dd70b7..e905f673365 100644 --- a/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.py +++ b/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.py @@ -44,7 +44,7 @@ class ClosingStockBalance(Document): & ( (table.from_date.between(self.from_date, self.to_date)) | (table.to_date.between(self.from_date, self.to_date)) - | (table.from_date >= self.from_date and table.to_date <= self.to_date) + | (table.from_date >= self.from_date and table.to_date >= self.to_date) ) ) ) From f42e93bf6c43edeec19fc8f23b9ecab8c9c385ae Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 10 Jan 2024 10:53:09 +0530 Subject: [PATCH 091/675] fix: skip rate validation for return `DN Items` with `Moving Average` valuation (backport #39242) (#39265) * fix: skip rate validation for return `DN Items` with `Moving Average` valuation (cherry picked from commit e0ad52b50036e689247f23090a5de89a7fb4abfa) # Conflicts: # erpnext/controllers/sales_and_purchase_return.py * chore: `conflicts` --------- Co-authored-by: s-aga-r --- erpnext/controllers/sales_and_purchase_return.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index efeedc14db6..f1d1e7eaebd 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -8,7 +8,7 @@ from frappe.model.meta import get_field_precision from frappe.utils import flt, format_datetime, get_datetime import erpnext -from erpnext.stock.utils import get_incoming_rate +from erpnext.stock.utils import get_incoming_rate, get_valuation_method class StockOverReturnError(frappe.ValidationError): @@ -116,7 +116,12 @@ def validate_returned_items(doc): ref = valid_items.get(d.item_code, frappe._dict()) validate_quantity(doc, d, ref, valid_items, already_returned_items) - if ref.rate and doc.doctype in ("Delivery Note", "Sales Invoice") and flt(d.rate) > ref.rate: + if ( + ref.rate + and flt(d.rate) > ref.rate + and doc.doctype in ("Delivery Note", "Sales Invoice") + and get_valuation_method(ref.item_code) != "Moving Average" + ): frappe.throw( _("Row # {0}: Rate cannot be greater than the rate used in {1} {2}").format( d.idx, doc.doctype, doc.return_against From 41e384326eb11f6dc18097f035007678b1689c5e Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 9 Jan 2024 12:51:57 +0530 Subject: [PATCH 092/675] fix: Set asset purchase amount based on qty and valuation_rate (cherry picked from commit 135e19d0aab3b1f4ae2361b327c579468d71d6d6) --- .../purchase_invoice/purchase_invoice.py | 26 ++++++++----------- erpnext/assets/doctype/asset/asset.js | 12 ++++++--- erpnext/controllers/buying_controller.py | 9 +++---- .../purchase_receipt/purchase_receipt.py | 13 ++++++---- 4 files changed, 31 insertions(+), 29 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index cc1109b7dc8..4a9153a1ebc 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -925,17 +925,6 @@ class PurchaseInvoice(BuyingController): item=item, ) ) - - # update gross amount of asset bought through this document - assets = frappe.db.get_all( - "Asset", filters={"purchase_invoice": self.name, "item_code": item.item_code} - ) - for asset in assets: - frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate)) - frappe.db.set_value( - "Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate) - ) - if ( self.auto_accounting_for_stock and self.is_opening == "No" @@ -975,17 +964,24 @@ class PurchaseInvoice(BuyingController): item.item_tax_amount, item.precision("item_tax_amount") ) + if item.is_fixed_asset and item.landed_cost_voucher_amount: + self.update_gross_purchase_amount_for_linked_assets(item) + + 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}, fields=["name", "asset_quantity"], ) for asset in assets: + purchase_amount = flt(item.valuation_rate) * asset.asset_quantity frappe.db.set_value( - "Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate) * asset.asset_quantity - ) - frappe.db.set_value( - "Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate) * asset.asset_quantity + "Asset", + asset.name, + { + "gross_purchase_amount": purchase_amount, + "purchase_receipt_amount": purchase_amount, + }, ) def make_stock_adjustment_entry( diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index 37fbd184fe1..f711577abbe 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -519,10 +519,16 @@ frappe.ui.form.on('Asset', { indicator: 'red' }); } - frm.set_value('gross_purchase_amount', item.base_net_rate + item.item_tax_amount); - frm.set_value('purchase_receipt_amount', item.base_net_rate + item.item_tax_amount); - item.asset_location && frm.set_value('location', item.asset_location); + var is_grouped_asset = frappe.db.get_value('Item', item.item_code, 'is_grouped_asset'); + var asset_quantity = is_grouped_asset ? item.qty : 1; + var purchase_amount = flt(item.valuation_rate * asset_quantity, precision('gross_purchase_amount')); + + frm.set_value('gross_purchase_amount', purchase_amount); + frm.set_value('purchase_receipt_amount', purchase_amount); + frm.set_value('asset_quantity', asset_quantity); frm.set_value('cost_center', item.cost_center || purchase_doc.cost_center); + if(item.asset_location) { frm.set_value('location', item.asset_location); } + }, set_depreciation_rate: function(frm, row) { diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 8e9b2289079..a2b7a65fce0 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -730,11 +730,8 @@ class BuyingController(SubcontractingController): item_data = frappe.db.get_value( "Item", row.item_code, ["asset_naming_series", "asset_category"], as_dict=1 ) - - if is_grouped_asset: - purchase_amount = flt(row.base_amount + row.item_tax_amount) - else: - purchase_amount = flt(row.base_rate + row.item_tax_amount) + asset_quantity = row.qty if is_grouped_asset else 1 + purchase_amount = flt(row.valuation_rate) * asset_quantity asset = frappe.get_doc( { @@ -750,7 +747,7 @@ class BuyingController(SubcontractingController): "calculate_depreciation": 1, "purchase_receipt_amount": purchase_amount, "gross_purchase_amount": purchase_amount, - "asset_quantity": row.qty if is_grouped_asset else 1, + "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, "cost_center": row.cost_center, diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 032e2fec822..d7cefe36473 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -606,7 +606,7 @@ class PurchaseReceipt(BuyingController): ): warehouse_with_no_account.append(d.warehouse or d.rejected_warehouse) - if d.is_fixed_asset: + if d.is_fixed_asset and d.landed_cost_voucher_amount: self.update_assets(d, d.valuation_rate) if warehouse_with_no_account: @@ -738,11 +738,14 @@ class PurchaseReceipt(BuyingController): ) for asset in assets: + purchase_amount = flt(valuation_rate) * asset.asset_quantity frappe.db.set_value( - "Asset", asset.name, "gross_purchase_amount", flt(valuation_rate) * asset.asset_quantity - ) - frappe.db.set_value( - "Asset", asset.name, "purchase_receipt_amount", flt(valuation_rate) * asset.asset_quantity + "Asset", + asset.name, + { + "gross_purchase_amount": purchase_amount, + "purchase_receipt_amount": purchase_amount, + }, ) def update_status(self, status): From 952cee3d6a625561c2d39252c2dcfd3d67a7be1a Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 8 Jan 2024 12:45:42 +0530 Subject: [PATCH 093/675] fix: Ignore asset qty and status validation while cancelling LCV (cherry picked from commit e9d36242ce3e09ee423f917eede2275bded10d6d) --- .../landed_cost_voucher/landed_cost_voucher.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py index 7f0dc2df9f3..3511cec2fd7 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py @@ -167,7 +167,8 @@ class LandedCostVoucher(Document): for d in self.get("purchase_receipts"): doc = frappe.get_doc(d.receipt_document_type, d.receipt_document) # check if there are {qty} assets created and linked to this receipt document - self.validate_asset_qty_and_status(d.receipt_document_type, doc) + if self.docstatus != 2: + self.validate_asset_qty_and_status(d.receipt_document_type, doc) # set landed cost voucher amount in pr item doc.set_landed_cost_voucher_amount() @@ -208,20 +209,20 @@ class LandedCostVoucher(Document): filters={receipt_document_type: item.receipt_document, "item_code": item.item_code}, fields=["name", "docstatus"], ) - if not docs or len(docs) != item.qty: + if not docs or len(docs) < item.qty: frappe.throw( _( - "There are not enough asset created or linked to {0}. Please create or link {1} Assets with respective document." - ).format(item.receipt_document, item.qty) + "There are only {0} asset created or linked to {1}. Please create or link {2} Assets with respective document." + ).format(len(docs), item.receipt_document, item.qty) ) if docs: for d in docs: if d.docstatus == 1: frappe.throw( _( - "{2} {0} has submitted Assets. Remove Item {1} from table to continue." + "{0} {1} has submitted Assets. Remove Item {2} from table to continue." ).format( - item.receipt_document, item.item_code, item.receipt_document_type + item.receipt_document_type, item.receipt_document, item.item_code ) ) From 8fe346aef887acfe4939549acc69d380a60b80e1 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 10 Jan 2024 11:34:20 +0530 Subject: [PATCH 094/675] fix: possible typeerror on transaction.js (cherry picked from commit 9f27ac142b6239bb01fb8304056776f1fae24694) --- 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 0f291e1eba7..48bde9dbc98 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -739,7 +739,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe if (me.frm.doc.price_list_currency == company_currency) { me.frm.set_value('plc_conversion_rate', 1.0); } - if (company_doc.default_letter_head) { + if (company_doc && company_doc.default_letter_head) { if(me.frm.fields_dict.letter_head) { me.frm.set_value("letter_head", company_doc.default_letter_head); } From b52988389750e6d18eb5407ec18bf84b63d783d4 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 10 Jan 2024 12:01:32 +0530 Subject: [PATCH 095/675] fix: resolved conflict --- erpnext/assets/doctype/asset/asset.py | 84 --------------------------- 1 file changed, 84 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 300d95d37ee..3397f7708f5 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -34,86 +34,6 @@ from erpnext.controllers.accounts_controller import AccountsController class Asset(AccountsController): -<<<<<<< HEAD -======= - # 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.assets.doctype.asset_finance_book.asset_finance_book import AssetFinanceBook - - additional_asset_cost: DF.Currency - amended_from: DF.Link | None - asset_category: DF.Link | None - asset_name: DF.Data - asset_owner: DF.Literal["", "Company", "Supplier", "Customer"] - asset_owner_company: DF.Link | None - asset_quantity: DF.Int - available_for_use_date: DF.Date | None - booked_fixed_asset: DF.Check - calculate_depreciation: DF.Check - capitalized_in: DF.Link | None - company: DF.Link - comprehensive_insurance: DF.Data | None - cost_center: DF.Link | None - custodian: DF.Link | None - customer: DF.Link | None - default_finance_book: DF.Link | None - department: DF.Link | None - depr_entry_posting_status: DF.Literal["", "Successful", "Failed"] - depreciation_method: DF.Literal["", "Straight Line", "Double Declining Balance", "Manual"] - disposal_date: DF.Date | None - finance_books: DF.Table[AssetFinanceBook] - frequency_of_depreciation: DF.Int - gross_purchase_amount: DF.Currency - image: DF.AttachImage | None - insurance_end_date: DF.Date | None - insurance_start_date: DF.Date | None - insured_value: DF.Data | None - insurer: DF.Data | None - is_composite_asset: DF.Check - is_existing_asset: DF.Check - is_fully_depreciated: DF.Check - item_code: DF.Link - item_name: DF.ReadOnly | None - journal_entry_for_scrap: DF.Link | None - location: DF.Link - maintenance_required: DF.Check - naming_series: DF.Literal["ACC-ASS-.YYYY.-"] - next_depreciation_date: DF.Date | None - number_of_depreciations_booked: DF.Int - opening_accumulated_depreciation: DF.Currency - policy_number: DF.Data | None - purchase_date: DF.Date | None - purchase_invoice: DF.Link | None - purchase_receipt: DF.Link | None - purchase_receipt_amount: DF.Currency - split_from: DF.Link | None - status: DF.Literal[ - "Draft", - "Submitted", - "Partially Depreciated", - "Fully Depreciated", - "Sold", - "Scrapped", - "In Maintenance", - "Out of Order", - "Issue", - "Receipt", - "Capitalized", - "Decapitalized", - ] - supplier: DF.Link | None - total_asset_cost: DF.Currency - total_number_of_depreciations: DF.Int - value_after_depreciation: DF.Currency - # end: auto-generated types - ->>>>>>> c34f09c503 (fix: Purchase date and amount is not mandatory for composite asset creation) def validate(self): self.validate_asset_values() self.validate_asset_and_reference() @@ -327,16 +247,12 @@ class Asset(AccountsController): frappe.throw(_("Gross Purchase Amount is mandatory"), frappe.MandatoryError) if is_cwip_accounting_enabled(self.asset_category): -<<<<<<< HEAD - if not self.is_existing_asset and not (self.purchase_receipt or self.purchase_invoice): -======= if ( not self.is_existing_asset and not self.is_composite_asset and not self.purchase_receipt and not self.purchase_invoice ): ->>>>>>> c34f09c503 (fix: Purchase date and amount is not mandatory for composite asset creation) frappe.throw( _("Please create purchase receipt or purchase invoice for the item {0}").format( self.item_code From a2a85586778dfb31f907a37d6a7c3cc616aebc81 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 10 Jan 2024 12:27:13 +0530 Subject: [PATCH 096/675] fix: set parent doctype on chart (backport #39286) (#39287) fix: set parent doctype on chart (#39286) (cherry picked from commit 38c5ecf007751b0e3b152ee5089e7a8e00f17f41) Co-authored-by: Ankush Menat --- .../completed_operation/completed_operation.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/dashboard_chart/completed_operation/completed_operation.json b/erpnext/manufacturing/dashboard_chart/completed_operation/completed_operation.json index d74ae2faf4d..146214af429 100644 --- a/erpnext/manufacturing/dashboard_chart/completed_operation/completed_operation.json +++ b/erpnext/manufacturing/dashboard_chart/completed_operation/completed_operation.json @@ -12,12 +12,13 @@ "is_public": 1, "is_standard": 1, "last_synced_on": "2020-07-21 16:57:09.767009", - "modified": "2020-07-21 16:57:55.719802", + "modified": "2024-01-10 12:21:25.134075", "modified_by": "Administrator", "module": "Manufacturing", "name": "Completed Operation", "number_of_groups": 0, "owner": "Administrator", + "parent_document_type": "Work Order", "time_interval": "Quarterly", "timeseries": 1, "timespan": "Last Year", From ee7474ba203d57f0835d31eb80dfdd8f6608af23 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 10 Jan 2024 12:31:59 +0530 Subject: [PATCH 097/675] fix: remove global _("translation") calls (backport #32828) (#39231) fix: remove global _("translation") calls (#32828) This is not how it works. Translations are dynamic based on language sets during request (using header, user's preferences etc) Calling them on global variables makes no sense. Ref: https://github.com/frappe/frappe/pull/18733 (cherry picked from commit 75983ce80912821dccc1432ca6d23cb60bccea92) Co-authored-by: Ankush Menat --- erpnext/hooks.py | 52 +++++++++++++++++++++++------------------------- 1 file changed, 25 insertions(+), 27 deletions(-) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 054f9dedabe..97d71a5f2a3 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -1,5 +1,3 @@ -from frappe import _ - app_name = "erpnext" app_title = "ERPNext" app_publisher = "Frappe Technologies Pvt. Ltd." @@ -92,7 +90,7 @@ website_route_rules = [ { "from_route": "/orders/", "to_route": "order", - "defaults": {"doctype": "Sales Order", "parents": [{"label": _("Orders"), "route": "orders"}]}, + "defaults": {"doctype": "Sales Order", "parents": [{"label": "Orders", "route": "orders"}]}, }, {"from_route": "/invoices", "to_route": "Sales Invoice"}, { @@ -100,7 +98,7 @@ website_route_rules = [ "to_route": "order", "defaults": { "doctype": "Sales Invoice", - "parents": [{"label": _("Invoices"), "route": "invoices"}], + "parents": [{"label": "Invoices", "route": "invoices"}], }, }, {"from_route": "/supplier-quotations", "to_route": "Supplier Quotation"}, @@ -109,7 +107,7 @@ website_route_rules = [ "to_route": "order", "defaults": { "doctype": "Supplier Quotation", - "parents": [{"label": _("Supplier Quotation"), "route": "supplier-quotations"}], + "parents": [{"label": "Supplier Quotation", "route": "supplier-quotations"}], }, }, {"from_route": "/purchase-orders", "to_route": "Purchase Order"}, @@ -118,7 +116,7 @@ website_route_rules = [ "to_route": "order", "defaults": { "doctype": "Purchase Order", - "parents": [{"label": _("Purchase Order"), "route": "purchase-orders"}], + "parents": [{"label": "Purchase Order", "route": "purchase-orders"}], }, }, {"from_route": "/purchase-invoices", "to_route": "Purchase Invoice"}, @@ -127,7 +125,7 @@ website_route_rules = [ "to_route": "order", "defaults": { "doctype": "Purchase Invoice", - "parents": [{"label": _("Purchase Invoice"), "route": "purchase-invoices"}], + "parents": [{"label": "Purchase Invoice", "route": "purchase-invoices"}], }, }, {"from_route": "/quotations", "to_route": "Quotation"}, @@ -136,7 +134,7 @@ website_route_rules = [ "to_route": "order", "defaults": { "doctype": "Quotation", - "parents": [{"label": _("Quotations"), "route": "quotations"}], + "parents": [{"label": "Quotations", "route": "quotations"}], }, }, {"from_route": "/shipments", "to_route": "Delivery Note"}, @@ -145,7 +143,7 @@ website_route_rules = [ "to_route": "order", "defaults": { "doctype": "Delivery Note", - "parents": [{"label": _("Shipments"), "route": "shipments"}], + "parents": [{"label": "Shipments", "route": "shipments"}], }, }, {"from_route": "/rfq", "to_route": "Request for Quotation"}, @@ -154,14 +152,14 @@ website_route_rules = [ "to_route": "rfq", "defaults": { "doctype": "Request for Quotation", - "parents": [{"label": _("Request for Quotation"), "route": "rfq"}], + "parents": [{"label": "Request for Quotation", "route": "rfq"}], }, }, {"from_route": "/addresses", "to_route": "Address"}, { "from_route": "/addresses/", "to_route": "addresses", - "defaults": {"doctype": "Address", "parents": [{"label": _("Addresses"), "route": "addresses"}]}, + "defaults": {"doctype": "Address", "parents": [{"label": "Addresses", "route": "addresses"}]}, }, {"from_route": "/boms", "to_route": "BOM"}, {"from_route": "/timesheets", "to_route": "Timesheet"}, @@ -171,78 +169,78 @@ website_route_rules = [ "to_route": "material_request_info", "defaults": { "doctype": "Material Request", - "parents": [{"label": _("Material Request"), "route": "material-requests"}], + "parents": [{"label": "Material Request", "route": "material-requests"}], }, }, {"from_route": "/project", "to_route": "Project"}, ] standard_portal_menu_items = [ - {"title": _("Projects"), "route": "/project", "reference_doctype": "Project"}, + {"title": "Projects", "route": "/project", "reference_doctype": "Project"}, { - "title": _("Request for Quotations"), + "title": "Request for Quotations", "route": "/rfq", "reference_doctype": "Request for Quotation", "role": "Supplier", }, { - "title": _("Supplier Quotation"), + "title": "Supplier Quotation", "route": "/supplier-quotations", "reference_doctype": "Supplier Quotation", "role": "Supplier", }, { - "title": _("Purchase Orders"), + "title": "Purchase Orders", "route": "/purchase-orders", "reference_doctype": "Purchase Order", "role": "Supplier", }, { - "title": _("Purchase Invoices"), + "title": "Purchase Invoices", "route": "/purchase-invoices", "reference_doctype": "Purchase Invoice", "role": "Supplier", }, { - "title": _("Quotations"), + "title": "Quotations", "route": "/quotations", "reference_doctype": "Quotation", "role": "Customer", }, { - "title": _("Orders"), + "title": "Orders", "route": "/orders", "reference_doctype": "Sales Order", "role": "Customer", }, { - "title": _("Invoices"), + "title": "Invoices", "route": "/invoices", "reference_doctype": "Sales Invoice", "role": "Customer", }, { - "title": _("Shipments"), + "title": "Shipments", "route": "/shipments", "reference_doctype": "Delivery Note", "role": "Customer", }, - {"title": _("Issues"), "route": "/issues", "reference_doctype": "Issue", "role": "Customer"}, - {"title": _("Addresses"), "route": "/addresses", "reference_doctype": "Address"}, + {"title": "Issues", "route": "/issues", "reference_doctype": "Issue", "role": "Customer"}, + {"title": "Addresses", "route": "/addresses", "reference_doctype": "Address"}, { - "title": _("Timesheets"), + "title": "Timesheets", "route": "/timesheets", "reference_doctype": "Timesheet", "role": "Customer", }, - {"title": _("Newsletter"), "route": "/newsletters", "reference_doctype": "Newsletter"}, + {"title": "Newsletter", "route": "/newsletters", "reference_doctype": "Newsletter"}, { - "title": _("Material Request"), + "title": "Material Request", "route": "/material-requests", "reference_doctype": "Material Request", "role": "Customer", }, - {"title": _("Appointment Booking"), "route": "/book_appointment"}, + {"title": "Appointment Booking", "route": "/book_appointment"}, ] default_roles = [ From 1cc887a997ee0c2990bff9f14aaa51a24452308a Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 10 Jan 2024 12:33:28 +0530 Subject: [PATCH 098/675] fix: Show timesheet table after fetching data from timesheet (backport #39275) (#39280) fix: Show timesheet table after fetching data from timesheet (cherry picked from commit e1ba5878a3c61d44006dbc5f1f269e6a2542583c) Co-authored-by: Nabin Hait --- erpnext/accounts/doctype/sales_invoice/sales_invoice.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 1a04841fd52..b236b447d57 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -890,8 +890,8 @@ frappe.ui.form.on('Sales Invoice', { frm.events.append_time_log(frm, timesheet, 1.0); } }); - frm.refresh_field("timesheets"); frm.trigger("calculate_timesheet_totals"); + frm.refresh(); }, async get_exchange_rate(frm, from_currency, to_currency) { From 2e6f1378f1fd079026e52ebc39991952977fa1de Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 10 Jan 2024 10:35:32 +0000 Subject: [PATCH 099/675] chore(release): Bumped to Version 14.59.0 # [14.59.0](https://github.com/frappe/erpnext/compare/v14.58.1...v14.59.0) (2024-01-10) ### Bug Fixes * add read permission to Buying Settings (backport [#39158](https://github.com/frappe/erpnext/issues/39158)) ([#39257](https://github.com/frappe/erpnext/issues/39257)) ([a15ad80](https://github.com/frappe/erpnext/commit/a15ad804d008613afdf92e9dcd2b8ac415780304)) * asset WDV depreciation calc according to IT act ([1cbe1e8](https://github.com/frappe/erpnext/commit/1cbe1e894d1fe4d760e75e72b0b8792f78741f5b)) * BOM replace tool does not update exploded items of root (backport [#39244](https://github.com/frappe/erpnext/issues/39244)) ([#39249](https://github.com/frappe/erpnext/issues/39249)) ([c2eeeec](https://github.com/frappe/erpnext/commit/c2eeeecac8c934098d289eb32bfbe995587f6987)) * don't set rate for non-stock item in Internal Transfer (backport [#39140](https://github.com/frappe/erpnext/issues/39140)) ([#39168](https://github.com/frappe/erpnext/issues/39168)) ([06d193a](https://github.com/frappe/erpnext/commit/06d193ad87ebb98bbfad1d79fa0c9afaf5f860d2)) * Duplicate Closing Stock Balance (backport [#39262](https://github.com/frappe/erpnext/issues/39262)) ([#39263](https://github.com/frappe/erpnext/issues/39263)) ([a3146c3](https://github.com/frappe/erpnext/commit/a3146c39dd36d8edb7b99cbd57ade2472f475ee2)) * **Employee:** treeview ([#39126](https://github.com/frappe/erpnext/issues/39126)) ([080a742](https://github.com/frappe/erpnext/commit/080a742725f37c0b4514f534a7fc4362c5276db0)) * FG Item incorrect qty in the work order (backport [#39200](https://github.com/frappe/erpnext/issues/39200)) ([#39210](https://github.com/frappe/erpnext/issues/39210)) ([5e517cf](https://github.com/frappe/erpnext/commit/5e517cfc775184681a4549b617e7ccaf56f1bbfc)) * Ignore asset qty and status validation while cancelling LCV ([952cee3](https://github.com/frappe/erpnext/commit/952cee3d6a625561c2d39252c2dcfd3d67a7be1a)) * Ignore UP on "allowed to transact with" (backport [#39103](https://github.com/frappe/erpnext/issues/39103)) ([#39104](https://github.com/frappe/erpnext/issues/39104)) ([7d64df0](https://github.com/frappe/erpnext/commit/7d64df05bc3a265023a27d92ea6fdc5e444a6864)) * improved validation message ([580e9f6](https://github.com/frappe/erpnext/commit/580e9f6e10e21dab07080ac2884d0095412239b1)) * incorrect indicator title for portal sales order (backport [#39247](https://github.com/frappe/erpnext/issues/39247)) ([#39254](https://github.com/frappe/erpnext/issues/39254)) ([8c496fb](https://github.com/frappe/erpnext/commit/8c496fbf2b0653a4c2ef1cb23c9b07cab98b8ebe)) * inventory dimension negative stock validation (backport [#39149](https://github.com/frappe/erpnext/issues/39149)) ([#39150](https://github.com/frappe/erpnext/issues/39150)) ([2866f7c](https://github.com/frappe/erpnext/commit/2866f7c44169098b691a5fa31cef5cb2b6ce6e81)) * possible typeerror on transaction.js ([8fe346a](https://github.com/frappe/erpnext/commit/8fe346aef887acfe4939549acc69d380a60b80e1)) * Purchase date and amount is not mandatory for composite asset creation ([0f6477a](https://github.com/frappe/erpnext/commit/0f6477a253844d6e1fc530cbc5d0f7a38eaf0aa4)) * remove global _("translation") calls (backport [#32828](https://github.com/frappe/erpnext/issues/32828)) ([#39231](https://github.com/frappe/erpnext/issues/39231)) ([ee7474b](https://github.com/frappe/erpnext/commit/ee7474ba203d57f0835d31eb80dfdd8f6608af23)) * resolved conflict ([b529883](https://github.com/frappe/erpnext/commit/b52988389750e6d18eb5407ec18bf84b63d783d4)) * set `First Name` in Supplier Contact ([69c460c](https://github.com/frappe/erpnext/commit/69c460c7562103771d5238c1f02af04493651dd8)) * Set asset purchase amount based on qty and valuation_rate ([41e3843](https://github.com/frappe/erpnext/commit/41e384326eb11f6dc18097f035007678b1689c5e)) * set parent doctype on chart (backport [#39286](https://github.com/frappe/erpnext/issues/39286)) ([#39287](https://github.com/frappe/erpnext/issues/39287)) ([a2a8558](https://github.com/frappe/erpnext/commit/a2a85586778dfb31f907a37d6a7c3cc616aebc81)) * Show maintain-stock and is-fixed-asset checkbox in item quick entry dialog ([7b4b630](https://github.com/frappe/erpnext/commit/7b4b630b2c441481b7121df90e4640654e9aed2e)) * Show timesheet table after fetching data from timesheet (backport [#39275](https://github.com/frappe/erpnext/issues/39275)) ([#39280](https://github.com/frappe/erpnext/issues/39280)) ([1cc887a](https://github.com/frappe/erpnext/commit/1cc887a997ee0c2990bff9f14aaa51a24452308a)) * skip rate validation for return `DN Items` with `Moving Average` valuation (backport [#39242](https://github.com/frappe/erpnext/issues/39242)) ([#39265](https://github.com/frappe/erpnext/issues/39265)) ([f42e93b](https://github.com/frappe/erpnext/commit/f42e93bf6c43edeec19fc8f23b9ecab8c9c385ae)) * total allocated percentage for sales team issue ([71f9b7f](https://github.com/frappe/erpnext/commit/71f9b7f67508ef04ad5f1f463e4df6c4dca83554)) * TypeError is pricing rules (backport [#39252](https://github.com/frappe/erpnext/issues/39252)) ([#39259](https://github.com/frappe/erpnext/issues/39259)) ([36ba33c](https://github.com/frappe/erpnext/commit/36ba33c5009e1526a25cc55ed39de5208aa5f090)) * typerror on multi select dialog ([71ecf08](https://github.com/frappe/erpnext/commit/71ecf081c312ffca15059de9e3dc446d43815411)) * update Maintenance Schedule status on Maintenance Visit submit (backport [#39167](https://github.com/frappe/erpnext/issues/39167)) ([#39185](https://github.com/frappe/erpnext/issues/39185)) ([2ea2146](https://github.com/frappe/erpnext/commit/2ea2146b343d13e4cc230e4d1ab8924f997e2b15)) * **UX:** dont override framework's permission check messages (backport [#39118](https://github.com/frappe/erpnext/issues/39118)) ([#39119](https://github.com/frappe/erpnext/issues/39119)) ([26ae708](https://github.com/frappe/erpnext/commit/26ae708d6d59ea04f862a20dcc08555f7bf553ab)) ### Features * Copy project_name, from_time, to_time from timesheet details to sales invoice ([#33726](https://github.com/frappe/erpnext/issues/33726)) ([d0e1162](https://github.com/frappe/erpnext/commit/d0e1162c964bb08c96d6b9a2ed726761d8bc2488)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index c6e5405c7db..6aa2b45fa6b 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.58.1" +__version__ = "14.59.0" def get_default_company(user=None): From d42db1174d3f5ffd2f0517e2a39ae10ad05e4eec Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Wed, 10 Jan 2024 21:31:03 +0530 Subject: [PATCH 100/675] feat: provision to select the qty field for Product Page (#39292) feat: provision to select the qty field to be shown as `In Stock` in product page --- .../e_commerce_settings.json | 10 ++++++- erpnext/patches.txt | 1 + erpnext/utilities/product.py | 29 +++++++++++++------ 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.json b/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.json index e6f08f708a8..31b3197e12d 100644 --- a/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.json +++ b/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.json @@ -31,6 +31,7 @@ "column_break_21", "default_customer_group", "quotation_series", + "show_actual_qty", "checkout_settings_section", "enable_checkout", "show_price_in_quotation", @@ -366,12 +367,19 @@ "fieldtype": "Check", "label": "Enable Redisearch", "read_only_depends_on": "eval:!doc.is_redisearch_loaded" + }, + { + "default": "1", + "description": "If enabled Actual Qty will be shown as In Stock on the product page instead of Projected Qty.", + "fieldname": "show_actual_qty", + "fieldtype": "Check", + "label": "Show Actual Qty" } ], "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2022-04-01 18:35:56.106756", + "modified": "2024-01-10 21:06:45.386977", "modified_by": "Administrator", "module": "E-commerce", "name": "E Commerce Settings", diff --git a/erpnext/patches.txt b/erpnext/patches.txt index e464552fa0c..6b7b13ff46e 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -357,3 +357,4 @@ erpnext.patches.v14_0.update_total_asset_cost_field erpnext.patches.v14_0.migrate_gl_to_payment_ledger erpnext.stock.doctype.delivery_note.patches.drop_unused_return_against_index # 2023-12-20 erpnext.patches.v14_0.set_maintain_stock_for_bom_item +execute:frappe.db.set_single_value('E Commerce Settings', 'show_actual_qty', 1) diff --git a/erpnext/utilities/product.py b/erpnext/utilities/product.py index 5b8b6c67c79..49066cd0c8a 100644 --- a/erpnext/utilities/product.py +++ b/erpnext/utilities/product.py @@ -2,6 +2,7 @@ # License: GNU General Public License v3. See license.txt import frappe +from frappe.query_builder.functions import IfNull from frappe.utils import cint, flt, fmt_money, getdate, nowdate from erpnext.accounts.doctype.pricing_rule.pricing_rule import get_pricing_rule_for_item @@ -30,16 +31,26 @@ def get_web_item_qty_in_stock(item_code, item_warehouse_field, warehouse=None): total_stock = 0.0 if warehouses: + qty_field = ( + "actual_qty" + if frappe.db.get_single_value("E Commerce Settings", "show_actual_qty") + else "projected_qty" + ) + + BIN = frappe.qb.DocType("Bin") + ITEM = frappe.qb.DocType("Item") + UOM = frappe.qb.DocType("UOM Conversion Detail") + for warehouse in warehouses: - stock_qty = frappe.db.sql( - """ - select S.actual_qty / IFNULL(C.conversion_factor, 1) - from tabBin S - inner join `tabItem` I on S.item_code = I.Item_code - left join `tabUOM Conversion Detail` C on I.sales_uom = C.uom and C.parent = I.Item_code - where S.item_code=%s and S.warehouse=%s""", - (item_code, warehouse), - ) + stock_qty = ( + frappe.qb.from_(BIN) + .select(BIN[qty_field] / IfNull(UOM.conversion_factor, 1)) + .inner_join(ITEM) + .on(BIN.item_code == ITEM.item_code) + .left_join(UOM) + .on((ITEM.sales_uom == UOM.uom) & (UOM.parent == ITEM.item_code)) + .where((BIN.item_code == item_code) & (BIN.warehouse == warehouse)) + ).run() if stock_qty: total_stock += adjust_qty_for_expired_items(item_code, stock_qty, warehouse) From 760af497ca551a034e6b73e12a7764da051cedb9 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 10 Jan 2024 21:51:34 +0530 Subject: [PATCH 101/675] feat: provision to select the qty field for Product Page (backport #39292) (#39300) feat: provision to select the qty field for Product Page (#39292) feat: provision to select the qty field to be shown as `In Stock` in product page (cherry picked from commit d42db1174d3f5ffd2f0517e2a39ae10ad05e4eec) Co-authored-by: s-aga-r --- .../e_commerce_settings.json | 10 ++++++- erpnext/patches.txt | 1 + erpnext/utilities/product.py | 29 +++++++++++++------ 3 files changed, 30 insertions(+), 10 deletions(-) diff --git a/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.json b/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.json index e6f08f708a8..31b3197e12d 100644 --- a/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.json +++ b/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.json @@ -31,6 +31,7 @@ "column_break_21", "default_customer_group", "quotation_series", + "show_actual_qty", "checkout_settings_section", "enable_checkout", "show_price_in_quotation", @@ -366,12 +367,19 @@ "fieldtype": "Check", "label": "Enable Redisearch", "read_only_depends_on": "eval:!doc.is_redisearch_loaded" + }, + { + "default": "1", + "description": "If enabled Actual Qty will be shown as In Stock on the product page instead of Projected Qty.", + "fieldname": "show_actual_qty", + "fieldtype": "Check", + "label": "Show Actual Qty" } ], "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2022-04-01 18:35:56.106756", + "modified": "2024-01-10 21:06:45.386977", "modified_by": "Administrator", "module": "E-commerce", "name": "E Commerce Settings", diff --git a/erpnext/patches.txt b/erpnext/patches.txt index e464552fa0c..6b7b13ff46e 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -357,3 +357,4 @@ erpnext.patches.v14_0.update_total_asset_cost_field erpnext.patches.v14_0.migrate_gl_to_payment_ledger erpnext.stock.doctype.delivery_note.patches.drop_unused_return_against_index # 2023-12-20 erpnext.patches.v14_0.set_maintain_stock_for_bom_item +execute:frappe.db.set_single_value('E Commerce Settings', 'show_actual_qty', 1) diff --git a/erpnext/utilities/product.py b/erpnext/utilities/product.py index 5b8b6c67c79..49066cd0c8a 100644 --- a/erpnext/utilities/product.py +++ b/erpnext/utilities/product.py @@ -2,6 +2,7 @@ # License: GNU General Public License v3. See license.txt import frappe +from frappe.query_builder.functions import IfNull from frappe.utils import cint, flt, fmt_money, getdate, nowdate from erpnext.accounts.doctype.pricing_rule.pricing_rule import get_pricing_rule_for_item @@ -30,16 +31,26 @@ def get_web_item_qty_in_stock(item_code, item_warehouse_field, warehouse=None): total_stock = 0.0 if warehouses: + qty_field = ( + "actual_qty" + if frappe.db.get_single_value("E Commerce Settings", "show_actual_qty") + else "projected_qty" + ) + + BIN = frappe.qb.DocType("Bin") + ITEM = frappe.qb.DocType("Item") + UOM = frappe.qb.DocType("UOM Conversion Detail") + for warehouse in warehouses: - stock_qty = frappe.db.sql( - """ - select S.actual_qty / IFNULL(C.conversion_factor, 1) - from tabBin S - inner join `tabItem` I on S.item_code = I.Item_code - left join `tabUOM Conversion Detail` C on I.sales_uom = C.uom and C.parent = I.Item_code - where S.item_code=%s and S.warehouse=%s""", - (item_code, warehouse), - ) + stock_qty = ( + frappe.qb.from_(BIN) + .select(BIN[qty_field] / IfNull(UOM.conversion_factor, 1)) + .inner_join(ITEM) + .on(BIN.item_code == ITEM.item_code) + .left_join(UOM) + .on((ITEM.sales_uom == UOM.uom) & (UOM.parent == ITEM.item_code)) + .where((BIN.item_code == item_code) & (BIN.warehouse == warehouse)) + ).run() if stock_qty: total_stock += adjust_qty_for_expired_items(item_code, stock_qty, warehouse) From 5fd68f720415e8a178f455b671f269ece60a557f Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 10 Jan 2024 16:24:39 +0000 Subject: [PATCH 102/675] chore(release): Bumped to Version 14.60.0 # [14.60.0](https://github.com/frappe/erpnext/compare/v14.59.0...v14.60.0) (2024-01-10) ### Features * provision to select the qty field for Product Page (backport [#39292](https://github.com/frappe/erpnext/issues/39292)) ([#39300](https://github.com/frappe/erpnext/issues/39300)) ([760af49](https://github.com/frappe/erpnext/commit/760af497ca551a034e6b73e12a7764da051cedb9)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 6aa2b45fa6b..9b7f9f2ba53 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.59.0" +__version__ = "14.60.0" def get_default_company(user=None): From dc7c9e7affa89dab7daf94f68a7205e07b38763f Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 10 Jan 2024 22:22:16 +0530 Subject: [PATCH 103/675] fix: performance issue related to stock entry (backport #39301) (#39302) fix: performance issue related to stock entry (#39301) (cherry picked from commit c67b0a3a6408075785211da20603fbcd829825bb) Co-authored-by: rohitwaghchaure --- .../manufacturing/doctype/production_plan/production_plan.js | 2 +- .../manufacturing/doctype/production_plan/production_plan.py | 4 ++-- .../doctype/production_plan/test_production_plan.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js index cd92263543b..c9c474db7f0 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.js +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js @@ -173,7 +173,7 @@ frappe.ui.form.on('Production Plan', { method: "set_status", freeze: true, doc: frm.doc, - args: {close : close}, + args: {close : close, update_bin: true}, callback: function() { frm.reload_doc(); } diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 767b4ccbb1a..c98d639663a 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -503,7 +503,7 @@ class ProductionPlan(Document): frappe.delete_doc("Work Order", d.name) @frappe.whitelist() - def set_status(self, close=None): + def set_status(self, close=None, update_bin=False): self.status = {0: "Draft", 1: "Submitted", 2: "Cancelled"}.get(self.docstatus) if close: @@ -523,7 +523,7 @@ class ProductionPlan(Document): if close is not None: self.db_set("status", self.status) - if self.docstatus == 1 and self.status != "Completed": + if update_bin and self.docstatus == 1 and self.status != "Completed": self.update_bin_qty() def update_ordered_status(self): diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py index 4f6280ade8f..2b9751926a2 100644 --- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py @@ -1475,14 +1475,14 @@ class TestProductionPlan(FrappeTestCase): before_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan")) pln.reload() - pln.set_status(close=True) + pln.set_status(close=True, update_bin=True) bin_name = get_or_make_bin(rm_item, rm_warehouse) after_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan")) self.assertAlmostEqual(after_qty, before_qty - 10) pln.reload() - pln.set_status(close=False) + pln.set_status(close=False, update_bin=True) bin_name = get_or_make_bin(rm_item, rm_warehouse) after_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan")) From 49d914bdb678a8bff7e425d80daee8ee1c03b3f5 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 10 Jan 2024 22:22:16 +0530 Subject: [PATCH 104/675] fix: performance issue related to stock entry (backport #39301) (#39302) fix: performance issue related to stock entry (#39301) (cherry picked from commit c67b0a3a6408075785211da20603fbcd829825bb) Co-authored-by: rohitwaghchaure (cherry picked from commit dc7c9e7affa89dab7daf94f68a7205e07b38763f) --- .../manufacturing/doctype/production_plan/production_plan.js | 2 +- .../manufacturing/doctype/production_plan/production_plan.py | 4 ++-- .../doctype/production_plan/test_production_plan.py | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js index cd92263543b..c9c474db7f0 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.js +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js @@ -173,7 +173,7 @@ frappe.ui.form.on('Production Plan', { method: "set_status", freeze: true, doc: frm.doc, - args: {close : close}, + args: {close : close, update_bin: true}, callback: function() { frm.reload_doc(); } diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 767b4ccbb1a..c98d639663a 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -503,7 +503,7 @@ class ProductionPlan(Document): frappe.delete_doc("Work Order", d.name) @frappe.whitelist() - def set_status(self, close=None): + def set_status(self, close=None, update_bin=False): self.status = {0: "Draft", 1: "Submitted", 2: "Cancelled"}.get(self.docstatus) if close: @@ -523,7 +523,7 @@ class ProductionPlan(Document): if close is not None: self.db_set("status", self.status) - if self.docstatus == 1 and self.status != "Completed": + if update_bin and self.docstatus == 1 and self.status != "Completed": self.update_bin_qty() def update_ordered_status(self): diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py index 4f6280ade8f..2b9751926a2 100644 --- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py @@ -1475,14 +1475,14 @@ class TestProductionPlan(FrappeTestCase): before_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan")) pln.reload() - pln.set_status(close=True) + pln.set_status(close=True, update_bin=True) bin_name = get_or_make_bin(rm_item, rm_warehouse) after_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan")) self.assertAlmostEqual(after_qty, before_qty - 10) pln.reload() - pln.set_status(close=False) + pln.set_status(close=False, update_bin=True) bin_name = get_or_make_bin(rm_item, rm_warehouse) after_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan")) From 5ae050ecd4a9f733b1ed56de2a0a03c37b8acc5d Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 10 Jan 2024 16:55:11 +0000 Subject: [PATCH 105/675] chore(release): Bumped to Version 14.60.1 ## [14.60.1](https://github.com/frappe/erpnext/compare/v14.60.0...v14.60.1) (2024-01-10) ### Bug Fixes * performance issue related to stock entry (backport [#39301](https://github.com/frappe/erpnext/issues/39301)) ([#39302](https://github.com/frappe/erpnext/issues/39302)) ([49d914b](https://github.com/frappe/erpnext/commit/49d914bdb678a8bff7e425d80daee8ee1c03b3f5)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 9b7f9f2ba53..0cfb47a1284 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.60.0" +__version__ = "14.60.1" def get_default_company(user=None): From dfcb7467741184da20d0f32d62433732a1acbec4 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 10 Jan 2024 20:26:51 +0530 Subject: [PATCH 106/675] fix: circular dependency error while deleting QC (cherry picked from commit 7cc324e31ebf533b0ffde8a2a3c4013756959d7a) # Conflicts: # erpnext/stock/doctype/quality_inspection/test_quality_inspection.py --- .../quality_inspection/quality_inspection.py | 3 + .../test_quality_inspection.py | 64 +++++++++++++++++++ 2 files changed, 67 insertions(+) diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.py b/erpnext/stock/doctype/quality_inspection/quality_inspection.py index 2a9f091bd09..002f7bf8a8c 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.py +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.py @@ -68,6 +68,9 @@ class QualityInspection(Document): def on_cancel(self): self.update_qc_reference() + def on_trash(self): + self.update_qc_reference() + def validate_readings_status_mandatory(self): for reading in self.readings: if not reading.status: diff --git a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py index 4f19643ad52..5c734744bae 100644 --- a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py +++ b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py @@ -216,6 +216,70 @@ class TestQualityInspection(FrappeTestCase): qa.save() self.assertEqual(qa.status, "Accepted") +<<<<<<< HEAD +======= + @change_settings("System Settings", {"number_format": "#.###,##"}) + def test_diff_number_format(self): + self.assertEqual(frappe.db.get_default("number_format"), "#.###,##") # sanity check + + # Test QI based on acceptance values (Non formula) + dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True) + readings = [ + { + "specification": "Iron Content", # numeric reading + "min_value": 60, + "max_value": 100, + "reading_1": "70,000", + }, + { + "specification": "Iron Content", # numeric reading + "min_value": 60, + "max_value": 100, + "reading_1": "1.100,00", + }, + ] + + qa = create_quality_inspection( + reference_type="Delivery Note", reference_name=dn.name, readings=readings, do_not_save=True + ) + + qa.save() + + # status must be auto set as per formula + self.assertEqual(qa.readings[0].status, "Accepted") + self.assertEqual(qa.readings[1].status, "Rejected") + + qa.delete() + dn.delete() + + def test_delete_quality_inspection_linked_with_stock_entry(self): + item_code = create_item("_Test Cicuular Dependecy Item with QA").name + + se = make_stock_entry( + item_code=item_code, target="_Test Warehouse - _TC", qty=1, basic_rate=100, do_not_submit=True + ) + + se.inspection_required = 1 + se.save() + + qa = create_quality_inspection( + item_code=item_code, reference_type="Stock Entry", reference_name=se.name, do_not_submit=True + ) + + se.reload() + se.items[0].quality_inspection = qa.name + se.save() + + qa.delete() + + se.reload() + + qc = se.items[0].quality_inspection + self.assertFalse(qc) + + se.delete() + +>>>>>>> 7cc324e31e (fix: circular dependency error while deleting QC) def create_quality_inspection(**args): args = frappe._dict(args) From 453700d0abb59ca67234617d35301daa0640a2ab Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 8 Jan 2024 18:17:01 +0530 Subject: [PATCH 107/675] fix: incorrect percentage received in purchase invoice (cherry picked from commit 8d2c78867e1d563aafa740b3dc5f8a4fae68e52f) --- .../doctype/purchase_invoice/purchase_invoice.py | 12 ++++++++++++ .../doctype/purchase_receipt/purchase_receipt.py | 1 + 2 files changed, 13 insertions(+) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 4a9153a1ebc..2af11159298 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -131,6 +131,18 @@ class PurchaseInvoice(BuyingController): self.reset_default_field_value("set_warehouse", "items", "warehouse") self.reset_default_field_value("rejected_warehouse", "items", "rejected_warehouse") self.reset_default_field_value("set_from_warehouse", "items", "from_warehouse") + self.set_percentage_received() + + def set_percentage_received(self): + total_billed_qty = 0.0 + total_received_qty = 0.0 + for row in self.items: + if row.purchase_receipt and row.pr_detail and row.received_qty: + total_billed_qty += row.qty + total_received_qty += row.received_qty + + if total_billed_qty and total_received_qty: + self.per_received = total_received_qty / total_billed_qty * 100 def validate_release_date(self): if self.release_date and getdate(nowdate()) >= getdate(self.release_date): diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index d7cefe36473..d44bb4d26d8 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -1077,6 +1077,7 @@ def make_purchase_invoice(source_name, target_doc=None, args=None): "field_map": { "name": "pr_detail", "parent": "purchase_receipt", + "qty": "received_qty", "purchase_order_item": "po_detail", "purchase_order": "purchase_order", "is_fixed_asset": "is_fixed_asset", From daf0e435e2522e54c815f9912ceca36e99e17f5d Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 11 Jan 2024 14:53:21 +0530 Subject: [PATCH 108/675] chore: fix conflicts --- .../test_quality_inspection.py | 37 ------------------- 1 file changed, 37 deletions(-) diff --git a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py index 5c734744bae..9eeb4602ab6 100644 --- a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py +++ b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py @@ -216,42 +216,6 @@ class TestQualityInspection(FrappeTestCase): qa.save() self.assertEqual(qa.status, "Accepted") -<<<<<<< HEAD -======= - @change_settings("System Settings", {"number_format": "#.###,##"}) - def test_diff_number_format(self): - self.assertEqual(frappe.db.get_default("number_format"), "#.###,##") # sanity check - - # Test QI based on acceptance values (Non formula) - dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True) - readings = [ - { - "specification": "Iron Content", # numeric reading - "min_value": 60, - "max_value": 100, - "reading_1": "70,000", - }, - { - "specification": "Iron Content", # numeric reading - "min_value": 60, - "max_value": 100, - "reading_1": "1.100,00", - }, - ] - - qa = create_quality_inspection( - reference_type="Delivery Note", reference_name=dn.name, readings=readings, do_not_save=True - ) - - qa.save() - - # status must be auto set as per formula - self.assertEqual(qa.readings[0].status, "Accepted") - self.assertEqual(qa.readings[1].status, "Rejected") - - qa.delete() - dn.delete() - def test_delete_quality_inspection_linked_with_stock_entry(self): item_code = create_item("_Test Cicuular Dependecy Item with QA").name @@ -279,7 +243,6 @@ class TestQualityInspection(FrappeTestCase): se.delete() ->>>>>>> 7cc324e31e (fix: circular dependency error while deleting QC) def create_quality_inspection(**args): args = frappe._dict(args) From 298cdf5f0efe4e0a0e76157ad0719bee10822c4a Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 11 Jan 2024 16:24:43 +0530 Subject: [PATCH 109/675] fix: broken dimension filters in Sales/Purchase register (cherry picked from commit 7b3f9386d7af54181a59c42e9e07ee3d6f54903e) --- erpnext/accounts/report/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/utils.py b/erpnext/accounts/report/utils.py index 7dd61d2c64c..e27e45d439c 100644 --- a/erpnext/accounts/report/utils.py +++ b/erpnext/accounts/report/utils.py @@ -365,7 +365,7 @@ def filter_invoices_based_on_dimensions(filters, query, parent_doc): dimension.document_type, filters.get(dimension.fieldname) ) fieldname = dimension.fieldname - query = query.where(parent_doc[fieldname] == filters.fieldname) + query = query.where(parent_doc[fieldname].isin(filters[fieldname])) return query From 9395f7535b32b02806e7c36d4beb6b44a246a134 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 11 Jan 2024 16:45:49 +0530 Subject: [PATCH 110/675] fix: possible typeerror in consolidated report (cherry picked from commit 268731aec4fb7a8bf9bc698773894d8fa722eb1a) --- .../consolidated_financial_statement.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js index b7d25c41982..cb1083b06c1 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js @@ -129,7 +129,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { } value = default_formatter(value, row, column, data); - if (!data.parent_account) { + if (data && !data.parent_account) { value = $(`${value}`); var $value = $(value).css("font-weight", "bold"); From 520cdb6f32f4477aa9fd0456c6b7c6328c5ccfe1 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 8 Jan 2024 17:11:26 +0530 Subject: [PATCH 111/675] fix: project filters on Delivery Note and Sales Order (cherry picked from commit 9ba6ff67d5727ee97f12a9cbee120e78c20d6cec) --- erpnext/public/js/utils/dimension_tree_filter.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/public/js/utils/dimension_tree_filter.js b/erpnext/public/js/utils/dimension_tree_filter.js index bb23f1512b9..3f70c09f667 100644 --- a/erpnext/public/js/utils/dimension_tree_filter.js +++ b/erpnext/public/js/utils/dimension_tree_filter.js @@ -16,6 +16,8 @@ erpnext.accounts.dimensions = { }, callback: function(r) { me.accounting_dimensions = r.message[0]; + // Ignoring "Project" as it is already handled specifically in Sales Order and Delivery Note + me.accounting_dimensions = me.accounting_dimensions.filter(x=>{return x.document_type != "Project"}); me.default_dimensions = r.message[1]; me.setup_filters(frm, doctype); } From 670d61547fb6a7ee86050c2f1020cd247f12df52 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Wed, 10 Jan 2024 22:05:02 +0530 Subject: [PATCH 112/675] fix: date in master document for dictionary condition (cherry picked from commit d96a777edd9cad698b9d9bb90863c1080eacb36f) # Conflicts: # erpnext/accounts/utils.py --- erpnext/accounts/utils.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 52b0c34673a..fd1e2914a4f 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -1222,8 +1222,18 @@ def get_autoname_with_number(number_value, doc_title, company): def parse_naming_series_variable(doc, variable): if variable == "FY": +<<<<<<< HEAD date = doc.get("posting_date") or doc.get("transaction_date") or getdate() return get_fiscal_year(date=date, company=doc.get("company"))[0] +======= + if doc: + date = doc.get("posting_date") or doc.get("transaction_date") or getdate() + company = doc.get("company") + else: + date = getdate() + company = None + return get_fiscal_year(date=date, company=company)[0] +>>>>>>> d96a777edd (fix: date in master document for dictionary condition) @frappe.whitelist() From d95b14d5b8f03c646a90186ce013af0cf42225b9 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Wed, 10 Jan 2024 22:06:06 +0530 Subject: [PATCH 113/675] test: naming series variable parsing (cherry picked from commit bbdf98a8f0a53b0b4c5a73eb1842401d67314760) --- erpnext/accounts/test/test_utils.py | 35 +++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/erpnext/accounts/test/test_utils.py b/erpnext/accounts/test/test_utils.py index 3d5e5fc4ec7..3243e3dcc63 100644 --- a/erpnext/accounts/test/test_utils.py +++ b/erpnext/accounts/test/test_utils.py @@ -22,6 +22,9 @@ class TestUtils(unittest.TestCase): super(TestUtils, cls).setUpClass() make_test_objects("Address", ADDRESS_RECORDS) + def tearDown(self): + frappe.db.rollback() + def test_get_party_shipping_address(self): address = get_party_shipping_address("Customer", "_Test Customer 1") self.assertEqual(address, "_Test Billing Address 2 Title-Billing") @@ -125,6 +128,38 @@ class TestUtils(unittest.TestCase): self.assertEqual(len(payment_entry.references), 1) self.assertEqual(payment_entry.difference_amount, 0) + def test_naming_series_variable_parsing(self): + """ + Tests parsing utility used by Naming Series Variable hook for FY + """ + from frappe.custom.doctype.property_setter.property_setter import make_property_setter + from frappe.utils import nowdate + + from erpnext.accounts.utils import get_fiscal_year + from erpnext.buying.doctype.supplier.test_supplier import create_supplier + + # Configure Supplier Naming in Buying Settings + frappe.db.set_default("supp_master_name", "Auto Name") + + # Configure Autoname in Supplier DocType + make_property_setter( + "Supplier", None, "naming_rule", "Expression", "Data", for_doctype="Doctype" + ) + make_property_setter( + "Supplier", None, "autoname", "SUP-.FY.-.#####", "Data", for_doctype="Doctype" + ) + + # Create Fiscal Year for Current Year + fiscal_year = get_fiscal_year(nowdate())[0] + + # Create Supplier + supplier = create_supplier() + + # Check Naming Series in generated Supplier ID + doc_name = supplier.name.split("-") + self.assertEqual(len(doc_name), 3) + self.assertSequenceEqual(doc_name[0:2], ("SUP", fiscal_year)) + ADDRESS_RECORDS = [ { From 6bd01f227eea82f434efa1e3bdb4f9ab2a3e0f26 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Thu, 11 Jan 2024 12:22:08 +0530 Subject: [PATCH 114/675] fix: reset default after test (cherry picked from commit 813b7a96fbb0ff048c112dec5551edf5980d0955) --- erpnext/accounts/test/test_utils.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/test/test_utils.py b/erpnext/accounts/test/test_utils.py index 3243e3dcc63..660cb62d2c5 100644 --- a/erpnext/accounts/test/test_utils.py +++ b/erpnext/accounts/test/test_utils.py @@ -22,7 +22,8 @@ class TestUtils(unittest.TestCase): super(TestUtils, cls).setUpClass() make_test_objects("Address", ADDRESS_RECORDS) - def tearDown(self): + @classmethod + def tearDownClass(cls): frappe.db.rollback() def test_get_party_shipping_address(self): @@ -149,7 +150,6 @@ class TestUtils(unittest.TestCase): "Supplier", None, "autoname", "SUP-.FY.-.#####", "Data", for_doctype="Doctype" ) - # Create Fiscal Year for Current Year fiscal_year = get_fiscal_year(nowdate())[0] # Create Supplier @@ -159,6 +159,7 @@ class TestUtils(unittest.TestCase): doc_name = supplier.name.split("-") self.assertEqual(len(doc_name), 3) self.assertSequenceEqual(doc_name[0:2], ("SUP", fiscal_year)) + frappe.db.set_default("supp_master_name", "Supplier Name") ADDRESS_RECORDS = [ From 318026615036dfdc6bb169788e25ae0f9a4520e1 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Wed, 3 Jan 2024 13:39:28 +0100 Subject: [PATCH 115/675] fix: consider all years in holiday list (cherry picked from commit 300aaa39fecf5b65fadf9ec18d122b6d8e327fca) --- erpnext/setup/doctype/holiday_list/holiday_list.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/setup/doctype/holiday_list/holiday_list.py b/erpnext/setup/doctype/holiday_list/holiday_list.py index 526bc2ba4ac..4593cbda5ee 100644 --- a/erpnext/setup/doctype/holiday_list/holiday_list.py +++ b/erpnext/setup/doctype/holiday_list/holiday_list.py @@ -63,7 +63,7 @@ class HolidayList(Document): for holiday_date, holiday_name in country_holidays( self.country, subdiv=self.subdivision, - years=[from_date.year, to_date.year], + years=list(range(from_date.year, to_date.year + 1)), language=frappe.local.lang, ).items(): if holiday_date in existing_holidays: From f3d8d273f4daba82ee61bf6b4a60707e77a5ed11 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Thu, 4 Jan 2024 11:04:25 +0100 Subject: [PATCH 116/675] test: improve test for local holidays (cherry picked from commit 60329ade9e4377741ef1578b4a9cc9529e3766c1) --- .../doctype/holiday_list/test_holiday_list.py | 51 +++++++++++++++++-- 1 file changed, 46 insertions(+), 5 deletions(-) diff --git a/erpnext/setup/doctype/holiday_list/test_holiday_list.py b/erpnext/setup/doctype/holiday_list/test_holiday_list.py index 7eeb27d864e..c0e71f5d254 100644 --- a/erpnext/setup/doctype/holiday_list/test_holiday_list.py +++ b/erpnext/setup/doctype/holiday_list/test_holiday_list.py @@ -48,17 +48,58 @@ class TestHolidayList(unittest.TestCase): def test_local_holidays(self): holiday_list = frappe.new_doc("Holiday List") - holiday_list.from_date = "2023-04-01" - holiday_list.to_date = "2023-04-30" + holiday_list.from_date = "2022-01-01" + holiday_list.to_date = "2024-12-31" holiday_list.country = "DE" holiday_list.subdivision = "SN" holiday_list.get_local_holidays() - holidays = [holiday.holiday_date for holiday in holiday_list.holidays] - self.assertNotIn(date(2023, 1, 1), holidays) + holidays = holiday_list.get_holidays() + self.assertIn(date(2022, 1, 1), holidays) + self.assertIn(date(2022, 4, 15), holidays) + self.assertIn(date(2022, 4, 18), holidays) + self.assertIn(date(2022, 5, 1), holidays) + self.assertIn(date(2022, 5, 26), holidays) + self.assertIn(date(2022, 6, 6), holidays) + self.assertIn(date(2022, 10, 3), holidays) + self.assertIn(date(2022, 10, 31), holidays) + self.assertIn(date(2022, 11, 16), holidays) + self.assertIn(date(2022, 12, 25), holidays) + self.assertIn(date(2022, 12, 26), holidays) + self.assertIn(date(2023, 1, 1), holidays) self.assertIn(date(2023, 4, 7), holidays) self.assertIn(date(2023, 4, 10), holidays) - self.assertNotIn(date(2023, 5, 1), holidays) + self.assertIn(date(2023, 5, 1), holidays) + self.assertIn(date(2023, 5, 18), holidays) + self.assertIn(date(2023, 5, 29), holidays) + self.assertIn(date(2023, 10, 3), holidays) + self.assertIn(date(2023, 10, 31), holidays) + self.assertIn(date(2023, 11, 22), holidays) + self.assertIn(date(2023, 12, 25), holidays) + self.assertIn(date(2023, 12, 26), holidays) + self.assertIn(date(2024, 1, 1), holidays) + self.assertIn(date(2024, 3, 29), holidays) + self.assertIn(date(2024, 4, 1), holidays) + self.assertIn(date(2024, 5, 1), holidays) + self.assertIn(date(2024, 5, 9), holidays) + self.assertIn(date(2024, 5, 20), holidays) + self.assertIn(date(2024, 10, 3), holidays) + self.assertIn(date(2024, 10, 31), holidays) + self.assertIn(date(2024, 11, 20), holidays) + self.assertIn(date(2024, 12, 25), holidays) + self.assertIn(date(2024, 12, 26), holidays) + + # check some random dates that should not be local holidays + self.assertNotIn(date(2022, 1, 2), holidays) + self.assertNotIn(date(2023, 4, 16), holidays) + self.assertNotIn(date(2024, 4, 19), holidays) + self.assertNotIn(date(2022, 5, 2), holidays) + self.assertNotIn(date(2023, 5, 27), holidays) + self.assertNotIn(date(2024, 6, 7), holidays) + self.assertNotIn(date(2022, 10, 4), holidays) + self.assertNotIn(date(2023, 10, 30), holidays) + self.assertNotIn(date(2024, 11, 17), holidays) + self.assertNotIn(date(2022, 12, 24), holidays) def test_localized_country_names(self): lang = frappe.local.lang From 755576bd78be12ffc0c6584cd659cc7279624fb0 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Wed, 27 Dec 2023 23:05:37 +0100 Subject: [PATCH 117/675] fix: unreconcile Bank Transaction on cancel of payment voucher (cherry picked from commit 0a95b38166e36946f89286d39081b0f529e88cb6) # Conflicts: # erpnext/accounts/doctype/bank_transaction/bank_transaction.py # erpnext/accounts/doctype/journal_entry/journal_entry.js --- .../bank_transaction/bank_transaction.py | 23 +++++++++++++++++++ .../doctype/journal_entry/journal_entry.js | 4 ++++ .../doctype/payment_entry/payment_entry.js | 2 +- .../purchase_invoice/purchase_invoice.js | 2 +- .../doctype/sales_invoice/sales_invoice.js | 2 +- erpnext/controllers/accounts_controller.py | 5 ++++ 6 files changed, 35 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py index 256bde5c719..54c4655d264 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py @@ -2,6 +2,11 @@ # For license information, please see license.txt import frappe +<<<<<<< HEAD +======= +from frappe import _ +from frappe.model.docstatus import DocStatus +>>>>>>> 0a95b38166 (fix: unreconcile Bank Transaction on cancel of payment voucher) from frappe.utils import flt from erpnext.controllers.status_updater import StatusUpdater @@ -393,3 +398,21 @@ def unclear_reference_payment(doctype, docname, bt_name): bt = frappe.get_doc("Bank Transaction", bt_name) set_voucher_clearance(doctype, docname, None, bt) return docname + + +def remove_from_bank_transaction(doctype, docname): + """Remove a (cancelled) voucher from all Bank Transactions.""" + for bt_name in get_reconciled_bank_transactions(doctype, docname): + bt = frappe.get_doc("Bank Transaction", bt_name) + if bt.docstatus == DocStatus.cancelled(): + continue + + modified = False + + for pe in bt.payment_entries: + if pe.payment_document == doctype and pe.payment_entry == docname: + bt.remove(pe) + modified = True + + if modified: + bt.save() diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index c59643280e8..bf71a4c6fa5 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -8,7 +8,11 @@ frappe.provide("erpnext.journal_entry"); frappe.ui.form.on("Journal Entry", { setup: function(frm) { frm.add_fetch("bank_account", "account", "account"); +<<<<<<< HEAD frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Journal Entry', "Repost Payment Ledger", 'Asset', 'Asset Movement', "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries"]; +======= + frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Journal Entry', "Repost Payment Ledger", 'Asset', 'Asset Movement', 'Asset Depreciation Schedule', "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries", "Bank Transaction"]; +>>>>>>> 0a95b38166 (fix: unreconcile Bank Transaction on cancel of payment voucher) }, refresh: function(frm) { diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index d5eeae5ab34..8ed2654c7f9 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -7,7 +7,7 @@ cur_frm.cscript.tax_table = "Advance Taxes and Charges"; frappe.ui.form.on('Payment Entry', { onload: function(frm) { - frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Journal Entry', 'Repost Payment Ledger','Repost Accounting Ledger', 'Unreconcile Payment', 'Unreconcile Payment Entries']; + frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Journal Entry', 'Repost Payment Ledger','Repost Accounting Ledger', 'Unreconcile Payment', 'Unreconcile Payment Entries', "Bank Transaction"]; if(frm.doc.__islocal) { if (!frm.doc.paid_from) frm.set_value("paid_from_account_currency", null); diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 3b77614607a..0add6c57da6 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -31,7 +31,7 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying. super.onload(); // Ignore linked advances - this.frm.ignore_doctypes_on_cancel_all = ['Journal Entry', 'Payment Entry', 'Purchase Invoice', "Repost Payment Ledger", "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries"]; + this.frm.ignore_doctypes_on_cancel_all = ['Journal Entry', 'Payment Entry', 'Purchase Invoice', "Repost Payment Ledger", "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries", "Bank Transaction"]; if(!this.frm.doc.__islocal) { // show credit_to in print format diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index b236b447d57..1e8428d9e29 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -34,7 +34,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e super.onload(); this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice', 'Timesheet', 'POS Invoice Merge Log', - 'POS Closing Entry', 'Journal Entry', 'Payment Entry', "Repost Payment Ledger", "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries"]; + 'POS Closing Entry', 'Journal Entry', 'Payment Entry', "Repost Payment Ledger", "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries", "Bank Transaction"]; if(!this.frm.doc.__islocal && !this.frm.doc.customer && this.frm.doc.debit_to) { // show debit_to in print format diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index e580748866d..cc8ee9226a4 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1347,11 +1347,16 @@ class AccountsController(TransactionBase): reconcile_against_document(lst) def on_cancel(self): + from erpnext.accounts.doctype.bank_transaction.bank_transaction import ( + remove_from_bank_transaction, + ) from erpnext.accounts.utils import ( cancel_exchange_gain_loss_journal, unlink_ref_doc_from_payment_entries, ) + remove_from_bank_transaction(self.doctype, self.name) + if self.doctype in ["Sales Invoice", "Purchase Invoice", "Payment Entry", "Journal Entry"]: # Cancel Exchange Gain/Loss Journal before unlinking cancel_exchange_gain_loss_journal(self) From 4a1a4b06dd320f9f76ed6f2374258815fd1add7e Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Thu, 28 Dec 2023 00:00:04 +0100 Subject: [PATCH 118/675] test: cancel voucher linked to Bank Transaction (cherry picked from commit 517bedeb7ee68d7794d09a5f49b97ab64f652abf) --- .../bank_transaction/test_bank_transaction.py | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py index f900e0775ce..eb0dc74825d 100644 --- a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py +++ b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py @@ -2,10 +2,10 @@ # See license.txt import json -import unittest import frappe from frappe import utils +from frappe.model.docstatus import DocStatus from frappe.tests.utils import FrappeTestCase from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import ( @@ -81,6 +81,29 @@ class TestBankTransaction(FrappeTestCase): clearance_date = frappe.db.get_value("Payment Entry", payment.name, "clearance_date") self.assertFalse(clearance_date) + def test_cancel_voucher(self): + bank_transaction = frappe.get_doc( + "Bank Transaction", + dict(description="1512567 BG/000003025 OPSKATTUZWXXX AT776000000098709849 Herr G"), + ) + payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1700)) + vouchers = json.dumps( + [ + { + "payment_doctype": "Payment Entry", + "payment_name": payment.name, + "amount": bank_transaction.unallocated_amount, + } + ] + ) + reconcile_vouchers(bank_transaction.name, vouchers) + payment.reload() + payment.cancel() + bank_transaction.reload() + self.assertEqual(bank_transaction.docstatus, DocStatus.submitted()) + self.assertEqual(bank_transaction.unallocated_amount, 1700) + self.assertEqual(bank_transaction.payment_entries, []) + # Check if ERPNext can correctly filter a linked payments based on the debit/credit amount def test_debit_credit_output(self): bank_transaction = frappe.get_doc( From 4998d68564c5eb1d99bd39a1f7ce21e7f519683d Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Thu, 11 Jan 2024 15:04:01 +0100 Subject: [PATCH 119/675] chore: resolve merge confilcts --- .../accounts/doctype/bank_transaction/bank_transaction.py | 6 +----- erpnext/accounts/doctype/journal_entry/journal_entry.js | 6 +----- erpnext/accounts/doctype/payment_entry/payment_entry.js | 2 +- 3 files changed, 3 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py index 54c4655d264..86c3a8a9336 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py @@ -2,11 +2,7 @@ # For license information, please see license.txt import frappe -<<<<<<< HEAD -======= -from frappe import _ from frappe.model.docstatus import DocStatus ->>>>>>> 0a95b38166 (fix: unreconcile Bank Transaction on cancel of payment voucher) from frappe.utils import flt from erpnext.controllers.status_updater import StatusUpdater @@ -73,7 +69,7 @@ class BankTransaction(StatusUpdater): "payment_entry": voucher["payment_name"], "allocated_amount": 0.0, # Temporary } - child = self.append("payment_entries", pe) + self.append("payment_entries", pe) added = True # runs on_update_after_submit diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index bf71a4c6fa5..f86320d917a 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -8,11 +8,7 @@ frappe.provide("erpnext.journal_entry"); frappe.ui.form.on("Journal Entry", { setup: function(frm) { frm.add_fetch("bank_account", "account", "account"); -<<<<<<< HEAD - frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Journal Entry', "Repost Payment Ledger", 'Asset', 'Asset Movement', "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries"]; -======= - frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Journal Entry', "Repost Payment Ledger", 'Asset', 'Asset Movement', 'Asset Depreciation Schedule', "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries", "Bank Transaction"]; ->>>>>>> 0a95b38166 (fix: unreconcile Bank Transaction on cancel of payment voucher) + frm.ignore_doctypes_on_cancel_all = ["Sales Invoice", "Purchase Invoice", "Journal Entry", "Repost Payment Ledger", "Asset", "Asset Movement", "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries", "Bank Transaction"]; }, refresh: function(frm) { diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 8ed2654c7f9..6b3f46d3833 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -7,7 +7,7 @@ cur_frm.cscript.tax_table = "Advance Taxes and Charges"; frappe.ui.form.on('Payment Entry', { onload: function(frm) { - frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Journal Entry', 'Repost Payment Ledger','Repost Accounting Ledger', 'Unreconcile Payment', 'Unreconcile Payment Entries', "Bank Transaction"]; + frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Journal Entry', 'Repost Payment Ledger', 'Repost Accounting Ledger', 'Unreconcile Payment', 'Unreconcile Payment Entries', 'Bank Transaction']; if(frm.doc.__islocal) { if (!frm.doc.paid_from) frm.set_value("paid_from_account_currency", null); From 7ae1df60be0a0e1e9af292e9abed0e2cb7a3ec63 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 12 Jan 2024 16:48:50 +0530 Subject: [PATCH 120/675] chore: remove share, print and email permissions from Buying Settings (backport #39337) (#39338) chore: remove share, print and email permissions from Buying Settings (cherry picked from commit 3c46abca6c362f8ddd7d269d8f28ee07ee557cc8) Co-authored-by: s-aga-r --- .../buying_settings/buying_settings.json | 27 +++++-------------- 1 file changed, 6 insertions(+), 21 deletions(-) diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.json b/erpnext/buying/doctype/buying_settings/buying_settings.json index 5be70288c56..f070c40a589 100644 --- a/erpnext/buying/doctype/buying_settings/buying_settings.json +++ b/erpnext/buying/doctype/buying_settings/buying_settings.json @@ -188,7 +188,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2024-01-05 15:26:02.320942", + "modified": "2024-01-12 16:42:01.894346", "modified_by": "Administrator", "module": "Buying", "name": "Buying Settings", @@ -214,39 +214,24 @@ "write": 1 }, { - "email": 1, - "print": 1, "read": 1, - "role": "Accounts User", - "share": 1 + "role": "Accounts User" }, { - "email": 1, - "print": 1, "read": 1, - "role": "Accounts Manager", - "share": 1 + "role": "Accounts Manager" }, { - "email": 1, - "print": 1, "read": 1, - "role": "Stock Manager", - "share": 1 + "role": "Stock Manager" }, { - "email": 1, - "print": 1, "read": 1, - "role": "Stock User", - "share": 1 + "role": "Stock User" }, { - "email": 1, - "print": 1, "read": 1, - "role": "Purchase User", - "share": 1 + "role": "Purchase User" } ], "sort_field": "modified", From d21fc6055cc25a1d4af6c290c2584e3de30fffc5 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Wed, 27 Dec 2023 19:32:48 +0530 Subject: [PATCH 121/675] fix: use child table values instead of global min max (cherry picked from commit 43fed29514c0d839312f33e1d7d490ae73d9830f) --- erpnext/stock/doctype/item/item.js | 26 ++++++-------------------- 1 file changed, 6 insertions(+), 20 deletions(-) diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js index b306a41bb83..1b71017bd4d 100644 --- a/erpnext/stock/doctype/item/item.js +++ b/erpnext/stock/doctype/item/item.js @@ -630,26 +630,12 @@ $.extend(erpnext.item, { } }); } else { - frappe.call({ - method: "frappe.client.get", - args: { - doctype: "Item Attribute", - name: d.attribute - } - }).then((r) => { - if(r.message) { - const from = r.message.from_range; - const to = r.message.to_range; - const increment = r.message.increment; - - let values = []; - for(var i = from; i <= to; i = flt(i + increment, 6)) { - values.push(i); - } - attr_val_fields[d.attribute] = values; - resolve(); - } - }); + 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(); } }); From d9f7070f92216c2422f4e1ad262e9397fb940969 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 12 Jan 2024 14:11:37 +0530 Subject: [PATCH 122/675] fix: added indexing to improve performance (cherry picked from commit ac81323fec7287a986feaf7ba496395bf3f6a424) # Conflicts: # erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py --- .../doctype/stock_entry/stock_entry.json | 8 ++- .../stock_entry_detail.json | 5 +- .../stock_entry_detail/stock_entry_detail.py | 63 +++++++++++++++++++ 3 files changed, 71 insertions(+), 5 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json index 564c380017b..d45296f1310 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.json +++ b/erpnext/stock/doctype/stock_entry/stock_entry.json @@ -104,7 +104,8 @@ "in_standard_filter": 1, "label": "Stock Entry Type", "options": "Stock Entry Type", - "reqd": 1 + "reqd": 1, + "search_index": 1 }, { "depends_on": "eval:doc.purpose == 'Material Transfer'", @@ -546,7 +547,8 @@ "label": "Job Card", "options": "Job Card", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "search_index": 1 }, { "fieldname": "amended_from", @@ -679,7 +681,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2023-06-19 18:23:40.748114", + "modified": "2024-01-12 11:56:58.644882", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry", diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json index 6b1a8efc997..5e523aef999 100644 --- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json +++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json @@ -557,7 +557,8 @@ "label": "Job Card Item", "no_copy": 1, "print_hide": 1, - "read_only": 1 + "read_only": 1, + "search_index": 1 }, { "default": "0", @@ -572,7 +573,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-05-09 12:41:18.210864", + "modified": "2024-01-12 11:56:04.626103", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry Detail", diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py index 000ff2dcf8b..d7250b23069 100644 --- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py +++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py @@ -6,4 +6,67 @@ from frappe.model.document import Document class StockEntryDetail(Document): +<<<<<<< HEAD +======= + # 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 + + actual_qty: DF.Float + additional_cost: DF.Currency + against_stock_entry: DF.Link | None + allow_alternative_item: DF.Check + allow_zero_valuation_rate: DF.Check + amount: DF.Currency + barcode: DF.Data | None + basic_amount: DF.Currency + basic_rate: DF.Currency + batch_no: DF.Link | None + bom_no: DF.Link | None + conversion_factor: DF.Float + cost_center: DF.Link | None + description: DF.TextEditor | None + expense_account: DF.Link | None + has_item_scanned: DF.Check + image: DF.Attach | None + is_finished_item: DF.Check + is_scrap_item: DF.Check + item_code: DF.Link + item_group: DF.Data | None + item_name: DF.Data | None + job_card_item: DF.Data | None + material_request: DF.Link | None + material_request_item: DF.Link | None + original_item: DF.Link | None + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + po_detail: DF.Data | None + project: DF.Link | None + putaway_rule: DF.Link | None + qty: DF.Float + quality_inspection: DF.Link | None + reference_purchase_receipt: DF.Link | None + retain_sample: DF.Check + s_warehouse: DF.Link | None + sample_quantity: DF.Int + sco_rm_detail: DF.Data | None + serial_and_batch_bundle: DF.Link | None + serial_no: DF.SmallText | None + set_basic_rate_manually: DF.Check + ste_detail: DF.Data | None + stock_uom: DF.Link + subcontracted_item: DF.Link | None + t_warehouse: DF.Link | None + transfer_qty: DF.Float + transferred_qty: DF.Float + uom: DF.Link + valuation_rate: DF.Currency + # end: auto-generated types + +>>>>>>> ac81323fec (fix: added indexing to improve performance) pass From c2fee6c25dc16cf44b3da930254c70ad01d9e9a3 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Fri, 12 Jan 2024 20:56:40 +0530 Subject: [PATCH 123/675] chore: fix conflicts --- .../stock_entry_detail/stock_entry_detail.py | 63 ------------------- 1 file changed, 63 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py index d7250b23069..000ff2dcf8b 100644 --- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py +++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.py @@ -6,67 +6,4 @@ from frappe.model.document import Document class StockEntryDetail(Document): -<<<<<<< HEAD -======= - # 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 - - actual_qty: DF.Float - additional_cost: DF.Currency - against_stock_entry: DF.Link | None - allow_alternative_item: DF.Check - allow_zero_valuation_rate: DF.Check - amount: DF.Currency - barcode: DF.Data | None - basic_amount: DF.Currency - basic_rate: DF.Currency - batch_no: DF.Link | None - bom_no: DF.Link | None - conversion_factor: DF.Float - cost_center: DF.Link | None - description: DF.TextEditor | None - expense_account: DF.Link | None - has_item_scanned: DF.Check - image: DF.Attach | None - is_finished_item: DF.Check - is_scrap_item: DF.Check - item_code: DF.Link - item_group: DF.Data | None - item_name: DF.Data | None - job_card_item: DF.Data | None - material_request: DF.Link | None - material_request_item: DF.Link | None - original_item: DF.Link | None - parent: DF.Data - parentfield: DF.Data - parenttype: DF.Data - po_detail: DF.Data | None - project: DF.Link | None - putaway_rule: DF.Link | None - qty: DF.Float - quality_inspection: DF.Link | None - reference_purchase_receipt: DF.Link | None - retain_sample: DF.Check - s_warehouse: DF.Link | None - sample_quantity: DF.Int - sco_rm_detail: DF.Data | None - serial_and_batch_bundle: DF.Link | None - serial_no: DF.SmallText | None - set_basic_rate_manually: DF.Check - ste_detail: DF.Data | None - stock_uom: DF.Link - subcontracted_item: DF.Link | None - t_warehouse: DF.Link | None - transfer_qty: DF.Float - transferred_qty: DF.Float - uom: DF.Link - valuation_rate: DF.Currency - # end: auto-generated types - ->>>>>>> ac81323fec (fix: added indexing to improve performance) pass From 1a26c70df285176dc282426bc715b83c470075e5 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 12 Jan 2024 18:28:52 +0530 Subject: [PATCH 124/675] fix: incorrect active serial nos due to backdated transactions --- .../purchase_receipt/test_purchase_receipt.py | 79 ---------------- .../stock_reconciliation.py | 58 ++++++++---- .../test_stock_reconciliation.py | 68 ++++++++++++++ erpnext/stock/stock_ledger.py | 94 +++++++++++-------- erpnext/stock/utils.py | 4 + 5 files changed, 164 insertions(+), 139 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 404758ce94f..b444e864e3d 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -13,7 +13,6 @@ from erpnext.stock.doctype.item.test_item import create_item, make_item from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice from erpnext.stock.doctype.serial_no.serial_no import SerialNoDuplicateError, get_serial_nos from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse -from erpnext.stock.stock_ledger import SerialNoExistsInFutureTransaction class TestPurchaseReceipt(FrappeTestCase): @@ -197,84 +196,6 @@ class TestPurchaseReceipt(FrappeTestCase): self.assertFalse(frappe.db.get_value("Batch", {"item": item.name, "reference_name": pr.name})) self.assertFalse(frappe.db.get_all("Serial No", {"batch_no": batch_no})) - def test_duplicate_serial_nos(self): - from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note - - item = frappe.db.exists("Item", {"item_name": "Test Serialized Item 123"}) - if not item: - item = create_item("Test Serialized Item 123") - item.has_serial_no = 1 - item.serial_no_series = "TSI123-.####" - item.save() - else: - item = frappe.get_doc("Item", {"item_name": "Test Serialized Item 123"}) - - # First make purchase receipt - pr = make_purchase_receipt(item_code=item.name, qty=2, rate=500) - pr.load_from_db() - - serial_nos = frappe.db.get_value( - "Stock Ledger Entry", - {"voucher_type": "Purchase Receipt", "voucher_no": pr.name, "item_code": item.name}, - "serial_no", - ) - - serial_nos = get_serial_nos(serial_nos) - - self.assertEquals(get_serial_nos(pr.items[0].serial_no), serial_nos) - - # Then tried to receive same serial nos in difference company - pr_different_company = make_purchase_receipt( - item_code=item.name, - qty=2, - rate=500, - serial_no="\n".join(serial_nos), - company="_Test Company 1", - do_not_submit=True, - warehouse="Stores - _TC1", - ) - - self.assertRaises(SerialNoDuplicateError, pr_different_company.submit) - - # Then made delivery note to remove the serial nos from stock - dn = create_delivery_note(item_code=item.name, qty=2, rate=1500, serial_no="\n".join(serial_nos)) - dn.load_from_db() - self.assertEquals(get_serial_nos(dn.items[0].serial_no), serial_nos) - - posting_date = add_days(today(), -3) - - # Try to receive same serial nos again in the same company with backdated. - pr1 = make_purchase_receipt( - item_code=item.name, - qty=2, - rate=500, - posting_date=posting_date, - serial_no="\n".join(serial_nos), - do_not_submit=True, - ) - - self.assertRaises(SerialNoExistsInFutureTransaction, pr1.submit) - - # Try to receive same serial nos with different company with backdated. - pr2 = make_purchase_receipt( - item_code=item.name, - qty=2, - rate=500, - posting_date=posting_date, - serial_no="\n".join(serial_nos), - company="_Test Company 1", - do_not_submit=True, - warehouse="Stores - _TC1", - ) - - self.assertRaises(SerialNoExistsInFutureTransaction, pr2.submit) - - # Receive the same serial nos after the delivery note posting date and time - make_purchase_receipt(item_code=item.name, qty=2, rate=500, serial_no="\n".join(serial_nos)) - - # Raise the error for backdated deliver note entry cancel - self.assertRaises(SerialNoExistsInFutureTransaction, dn.cancel) - def test_purchase_receipt_gl_entry(self): pr = make_purchase_receipt( company="_Test Company with perpetual inventory", diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index dd39f103cd7..9a46ae71ad2 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -634,30 +634,48 @@ class StockReconciliation(StockController): if voucher_detail_no != row.name: continue - current_qty = get_batch_qty_for_stock_reco( - row.item_code, row.warehouse, row.batch_no, self.posting_date, self.posting_time, self.name - ) + if row.serial_no: + item_dict = get_stock_balance_for( + row.item_code, + row.warehouse, + self.posting_date, + self.posting_time, + voucher_no=self.name, + ) + + current_qty = item_dict.get("qty") + row.current_serial_no = item_dict.get("serial_nos") + row.current_valuation_rate = item_dict.get("rate") + else: + current_qty = get_batch_qty_for_stock_reco( + row.item_code, row.warehouse, row.batch_no, self.posting_date, self.posting_time, self.name + ) precesion = row.precision("current_qty") if flt(current_qty, precesion) != flt(row.current_qty, precesion): - val_rate = get_valuation_rate( - row.item_code, - row.warehouse, - self.doctype, - self.name, - company=self.company, - batch_no=row.batch_no, - ) + 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, + ) + + row.current_valuation_rate = val_rate - row.current_valuation_rate = val_rate row.current_qty = current_qty - row.db_set( - { - "current_qty": row.current_qty, - "current_valuation_rate": row.current_valuation_rate, - "current_amount": flt(row.current_qty * row.current_valuation_rate), - } - ) + values_to_update = { + "current_qty": row.current_qty, + "current_valuation_rate": row.current_valuation_rate, + "current_amount": flt(row.current_qty * row.current_valuation_rate), + } + + if row.current_serial_no: + values_to_update["current_serial_no"] = row.current_serial_no + + row.db_set(values_to_update) if ( add_new_sle @@ -880,6 +898,7 @@ def get_stock_balance_for( batch_no: Optional[str] = None, with_valuation_rate: bool = True, inventory_dimensions_dict=None, + voucher_no=None, ): frappe.has_permission("Stock Reconciliation", "write", throw=True) @@ -910,6 +929,7 @@ def get_stock_balance_for( with_serial_no=has_serial_no, inventory_dimensions_dict=inventory_dimensions_dict, batch_no=batch_no, + voucher_no=voucher_no, ) if has_serial_no: diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py index 05c60175f51..19c04afe909 100644 --- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py @@ -1055,6 +1055,74 @@ class TestStockReconciliation(FrappeTestCase, StockTestMixin): self.assertEqual(sr.items[0].current_qty, se2.items[0].qty) self.assertEqual(len(sr.items[0].current_serial_no.split("\n")), sr.items[0].current_qty) + def test_backdated_purchase_receipt_with_stock_reco(self): + item_code = self.make_item( + properties={ + "is_stock_item": 1, + "has_serial_no": 1, + "serial_no_series": "TEST-SERIAL-.###", + } + ).name + + warehouse = "_Test Warehouse - _TC" + + # Step - 1: Create a Backdated Purchase Receipt + + pr1 = make_purchase_receipt( + item_code=item_code, warehouse=warehouse, qty=10, rate=100, posting_date=add_days(nowdate(), -3) + ) + pr1.reload() + + serial_nos = sorted(get_serial_nos(pr1.items[0].serial_no))[:5] + + # Step - 2: Create a Stock Reconciliation + sr1 = create_stock_reconciliation( + item_code=item_code, + warehouse=warehouse, + qty=5, + serial_no="\n".join(serial_nos), + ) + + data = frappe.get_all( + "Stock Ledger Entry", + fields=["serial_no", "actual_qty", "stock_value_difference"], + filters={"voucher_no": sr1.name, "is_cancelled": 0}, + order_by="creation", + ) + + for d in data: + if d.actual_qty < 0: + self.assertEqual(d.actual_qty, -10.0) + self.assertAlmostEqual(d.stock_value_difference, -1000.0) + else: + self.assertEqual(d.actual_qty, 5.0) + self.assertAlmostEqual(d.stock_value_difference, 500.0) + + # Step - 3: Create a Purchase Receipt before the first Purchase Receipt + make_purchase_receipt( + item_code=item_code, warehouse=warehouse, qty=10, rate=200, posting_date=add_days(nowdate(), -5) + ) + + data = frappe.get_all( + "Stock Ledger Entry", + fields=["serial_no", "actual_qty", "stock_value_difference"], + filters={"voucher_no": sr1.name, "is_cancelled": 0}, + order_by="creation", + ) + + for d in data: + if d.actual_qty < 0: + self.assertEqual(d.actual_qty, -20.0) + self.assertAlmostEqual(d.stock_value_difference, -3000.0) + else: + self.assertEqual(d.actual_qty, 5.0) + self.assertAlmostEqual(d.stock_value_difference, 500.0) + + active_serial_no = frappe.get_all( + "Serial No", filters={"status": "Active", "item_code": item_code} + ) + self.assertEqual(len(active_serial_no), 5) + 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 df1f544d7b1..ef1b0cda4ff 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1,7 +1,6 @@ # Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt -import copy import json from typing import Optional, Set, Tuple @@ -27,10 +26,6 @@ class NegativeStockError(frappe.ValidationError): pass -class SerialNoExistsInFutureTransaction(frappe.ValidationError): - pass - - def make_sl_entries(sl_entries, allow_negative_stock=False, via_landed_cost_voucher=False): """Create SL entries from SL entry dicts @@ -54,9 +49,6 @@ def make_sl_entries(sl_entries, allow_negative_stock=False, via_landed_cost_vouc future_sle_exists(args, sl_entries) for sle in sl_entries: - if sle.serial_no and not via_landed_cost_voucher: - validate_serial_no(sle) - if cancel: sle["actual_qty"] = -flt(sle.get("actual_qty")) @@ -133,35 +125,6 @@ def get_args_for_future_sle(row): ) -def validate_serial_no(sle): - from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos - - for sn in get_serial_nos(sle.serial_no): - args = copy.deepcopy(sle) - args.serial_no = sn - args.warehouse = "" - - vouchers = [] - for row in get_stock_ledger_entries(args, ">"): - voucher_type = frappe.bold(row.voucher_type) - voucher_no = frappe.bold(get_link_to_form(row.voucher_type, row.voucher_no)) - vouchers.append(f"{voucher_type} {voucher_no}") - - if vouchers: - serial_no = frappe.bold(sn) - msg = ( - f"""The serial no {serial_no} has been used in the future transactions so you need to cancel them first. - The list of the transactions are as below.""" - + "

    • " - ) - - msg += "
    • ".join(vouchers) - msg += "
    " - - title = "Cannot Submit" if not sle.get("is_cancelled") else "Cannot Cancel" - frappe.throw(_(msg), title=_(title), exc=SerialNoExistsInFutureTransaction) - - def validate_cancellation(args): if args[0].get("is_cancelled"): repost_entry = frappe.db.get_value( @@ -573,7 +536,12 @@ class update_entries_after(object): if not self.args.get("sle_id"): self.get_dynamic_incoming_outgoing_rate(sle) - if sle.voucher_type == "Stock Reconciliation" and sle.batch_no and sle.voucher_detail_no: + if ( + sle.voucher_type == "Stock Reconciliation" + and not self.args.get("sle_id") + and sle.voucher_detail_no + and (sle.batch_no or sle.serial_no) + ): self.reset_actual_qty_for_stock_reco(sle) if ( @@ -651,11 +619,52 @@ class update_entries_after(object): doc.recalculate_current_qty(sle.voucher_detail_no, sle.creation, sle.actual_qty >= 0) if sle.actual_qty < 0: - sle.actual_qty = ( - flt(frappe.db.get_value("Stock Reconciliation Item", sle.voucher_detail_no, "current_qty")) - * -1 + stock_reco_details = frappe.db.get_value( + "Stock Reconciliation Item", + sle.voucher_detail_no, + ["current_qty", "current_serial_no as sn_no"], + as_dict=True, ) + sle.actual_qty = flt(stock_reco_details.current_qty) * -1 + + if stock_reco_details.sn_no: + sle.serial_no = stock_reco_details.sn_no + sle.qty_after_transaction = 0.0 + + if sle.serial_no: + self.update_serial_no_status(sle) + + def update_serial_no_status(self, sle): + from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos + + serial_nos = get_serial_nos(sle.serial_no) + warehouse = None + status = "Delivered" + if sle.actual_qty > 0: + warehouse = sle.warehouse + status = "Active" + + sn_table = frappe.qb.DocType("Serial No") + + query = ( + frappe.qb.update(sn_table) + .set(sn_table.warehouse, warehouse) + .set(sn_table.status, status) + .where(sn_table.name.isin(serial_nos)) + ) + + if sle.actual_qty > 0: + query = query.set(sn_table.purchase_document_type, sle.voucher_type) + query = query.set(sn_table.purchase_document_no, sle.voucher_no) + query = query.set(sn_table.delivery_document_type, None) + query = query.set(sn_table.delivery_document_no, None) + else: + query = query.set(sn_table.delivery_document_type, sle.voucher_type) + query = query.set(sn_table.delivery_document_no, sle.voucher_no) + + query.run() + def validate_negative_stock(self, sle): """ validate negative stock for entries current datetime onwards @@ -1282,6 +1291,9 @@ def get_stock_ledger_entries( if operator in (">", "<=") and previous_sle.get("name"): conditions += " and name!=%(name)s" + if operator in (">", "<=") and previous_sle.get("voucher_no"): + conditions += " and voucher_no!=%(voucher_no)s" + if extra_cond: conditions += f"{extra_cond}" diff --git a/erpnext/stock/utils.py b/erpnext/stock/utils.py index e019c572daf..2b57a1be8fa 100644 --- a/erpnext/stock/utils.py +++ b/erpnext/stock/utils.py @@ -96,6 +96,7 @@ def get_stock_balance( with_serial_no=False, inventory_dimensions_dict=None, batch_no=None, + voucher_no=None, ): """Returns stock balance quantity at given warehouse on given posting date or current date. @@ -115,6 +116,9 @@ def get_stock_balance( "posting_time": posting_time, } + if voucher_no: + args["voucher_no"] = voucher_no + extra_cond = "" if inventory_dimensions_dict: for field, value in inventory_dimensions_dict.items(): From 1acaa20ee11056c9f79cab333474bf13dfb56bf6 Mon Sep 17 00:00:00 2001 From: RJPvT <48353029+RJPvT@users.noreply.github.com> Date: Sat, 13 Jan 2024 16:09:11 +0100 Subject: [PATCH 125/675] fix: empty category in Plaid --- .../doctype/plaid_settings/plaid_settings.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py index a462141a86b..5354d0d6c13 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py @@ -239,11 +239,12 @@ def new_bank_transaction(transaction): withdrawal = 0.0 tags = [] - try: - tags += transaction["category"] - tags += [f'Plaid Cat. {transaction["category_id"]}'] - except KeyError: - pass + if transaction["category"]: + try: + tags += transaction["category"] + tags += [f'Plaid Cat. {transaction["category_id"]}'] + except KeyError: + pass if not frappe.db.exists( "Bank Transaction", dict(transaction_id=transaction["transaction_id"]) From 5336cd49c90ede14fd3cb525444da2f7dcb4b3f5 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Sun, 14 Jan 2024 09:41:48 +0530 Subject: [PATCH 126/675] chore: fix conflicts --- erpnext/accounts/utils.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index fd1e2914a4f..d9fb75a86d2 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -1222,10 +1222,6 @@ def get_autoname_with_number(number_value, doc_title, company): def parse_naming_series_variable(doc, variable): if variable == "FY": -<<<<<<< HEAD - date = doc.get("posting_date") or doc.get("transaction_date") or getdate() - return get_fiscal_year(date=date, company=doc.get("company"))[0] -======= if doc: date = doc.get("posting_date") or doc.get("transaction_date") or getdate() company = doc.get("company") @@ -1233,7 +1229,6 @@ def parse_naming_series_variable(doc, variable): date = getdate() company = None return get_fiscal_year(date=date, company=company)[0] ->>>>>>> d96a777edd (fix: date in master document for dictionary condition) @frappe.whitelist() From 664abc628742e9fb4e17eabb5b32f685bdce620c Mon Sep 17 00:00:00 2001 From: mahsem <137205921+mahsem@users.noreply.github.com> Date: Mon, 8 Jan 2024 18:47:44 +0100 Subject: [PATCH 127/675] Update sales_taxes_and_charges.json Change Rate label to existing Tax Rate label so it can be correctly translated in other languages (cherry picked from commit 2b93be1139bcb963a89c10f261a1dd2c83f01980) --- .../sales_taxes_and_charges/sales_taxes_and_charges.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json b/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json index e236577e118..527e4c866ba 100644 --- a/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json +++ b/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json @@ -108,7 +108,7 @@ "fieldname": "rate", "fieldtype": "Float", "in_list_view": 1, - "label": "Rate", + "label": "Tax Rate", "oldfieldname": "rate", "oldfieldtype": "Currency" }, @@ -227,4 +227,4 @@ "sort_field": "modified", "sort_order": "ASC", "states": [] -} \ No newline at end of file +} From cb6757437ed72377ece6c0ded858019ce2b087be Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Sun, 14 Jan 2024 09:48:05 +0530 Subject: [PATCH 128/675] fix: modified date was not updated (cherry picked from commit f567af49a697568ba1d5dc3507953c332cb1a60a) --- .../sales_taxes_and_charges/sales_taxes_and_charges.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json b/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json index 527e4c866ba..f9e5f4129c9 100644 --- a/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json +++ b/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json @@ -218,7 +218,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2022-10-17 13:08:17.776528", + "modified": "2022-10-18 13:08:17.776528", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Taxes and Charges", From c69a59c3c6c72ba69a8347b2c2940e2aaa49428e Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sun, 14 Jan 2024 10:05:26 +0530 Subject: [PATCH 129/675] fix: added item group in stock reco (cherry picked from commit 116ff8241caeb98df28c91143a076dfa9e3179d6) # Conflicts: # erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json --- .../stock_reconciliation_item.json | 40 ++++++++++++++++++- 1 file changed, 39 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json index a04309ad48e..0c0d7e87379 100644 --- a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json +++ b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json @@ -10,8 +10,9 @@ "has_item_scanned", "item_code", "item_name", - "warehouse", + "item_group", "column_break_6", + "warehouse", "qty", "valuation_rate", "amount", @@ -49,6 +50,7 @@ "reqd": 1 }, { + "fetch_from": "item_code.item_name", "fieldname": "item_name", "fieldtype": "Data", "in_global_search": 1, @@ -186,11 +188,47 @@ "fieldtype": "Data", "label": "Has Item Scanned", "read_only": 1 +<<<<<<< HEAD +======= + }, + { + "fieldname": "serial_and_batch_bundle", + "fieldtype": "Link", + "label": "Serial / Batch Bundle", + "no_copy": 1, + "options": "Serial and Batch Bundle", + "print_hide": 1, + "search_index": 1 + }, + { + "fieldname": "current_serial_and_batch_bundle", + "fieldtype": "Link", + "label": "Current Serial / Batch Bundle", + "no_copy": 1, + "options": "Serial and Batch Bundle", + "read_only": 1 + }, + { + "fieldname": "add_serial_batch_bundle", + "fieldtype": "Button", + "label": "Add Serial / Batch No" + }, + { + "fetch_from": "item_code.item_group", + "fieldname": "item_group", + "fieldtype": "Link", + "label": "Item Group", + "options": "Item Group" +>>>>>>> 116ff8241c (fix: added item group in stock reco) } ], "istable": 1, "links": [], +<<<<<<< HEAD "modified": "2023-07-25 11:58:44.992419", +======= + "modified": "2024-01-14 10:04:23.599951", +>>>>>>> 116ff8241c (fix: added item group in stock reco) "modified_by": "Administrator", "module": "Stock", "name": "Stock Reconciliation Item", From 4d11a9c884f2b6d0940e43de4f1ee75add03a202 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Sun, 14 Jan 2024 10:32:10 +0530 Subject: [PATCH 130/675] chore: fix conflicts --- .../stock_reconciliation_item.json | 31 +------------------ 1 file changed, 1 insertion(+), 30 deletions(-) diff --git a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json index 0c0d7e87379..e3cd34725b8 100644 --- a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json +++ b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json @@ -188,30 +188,6 @@ "fieldtype": "Data", "label": "Has Item Scanned", "read_only": 1 -<<<<<<< HEAD -======= - }, - { - "fieldname": "serial_and_batch_bundle", - "fieldtype": "Link", - "label": "Serial / Batch Bundle", - "no_copy": 1, - "options": "Serial and Batch Bundle", - "print_hide": 1, - "search_index": 1 - }, - { - "fieldname": "current_serial_and_batch_bundle", - "fieldtype": "Link", - "label": "Current Serial / Batch Bundle", - "no_copy": 1, - "options": "Serial and Batch Bundle", - "read_only": 1 - }, - { - "fieldname": "add_serial_batch_bundle", - "fieldtype": "Button", - "label": "Add Serial / Batch No" }, { "fetch_from": "item_code.item_group", @@ -219,16 +195,11 @@ "fieldtype": "Link", "label": "Item Group", "options": "Item Group" ->>>>>>> 116ff8241c (fix: added item group in stock reco) } ], "istable": 1, "links": [], -<<<<<<< HEAD - "modified": "2023-07-25 11:58:44.992419", -======= "modified": "2024-01-14 10:04:23.599951", ->>>>>>> 116ff8241c (fix: added item group in stock reco) "modified_by": "Administrator", "module": "Stock", "name": "Stock Reconciliation Item", @@ -239,4 +210,4 @@ "sort_order": "DESC", "states": [], "track_changes": 1 -} \ No newline at end of file +} From 28434d101b381b03475f61b222043df17a2aea51 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Sun, 14 Jan 2024 10:44:18 +0530 Subject: [PATCH 131/675] fix: modified date --- .../sales_taxes_and_charges/sales_taxes_and_charges.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json b/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json index f9e5f4129c9..9e0a7983b74 100644 --- a/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json +++ b/erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json @@ -218,7 +218,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2022-10-18 13:08:17.776528", + "modified": "2024-01-14 10:08:17.776528", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Taxes and Charges", From e9af0c6e675922e4534fe51a2d7b8266283f3c51 Mon Sep 17 00:00:00 2001 From: mahsem <137205921+mahsem@users.noreply.github.com> Date: Mon, 8 Jan 2024 18:45:39 +0100 Subject: [PATCH 132/675] Update purchase_taxes_and_charges.json label Rate to Tax Rate Change Rate label to existing Tax Rate label so it can be correctly translated in other languages (cherry picked from commit bd464197c41329ddf3cff50cd3eb876df9c6c382) --- .../purchase_taxes_and_charges.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json b/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json index 347cae05b72..249e7518f0b 100644 --- a/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json +++ b/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json @@ -126,7 +126,7 @@ "fieldname": "rate", "fieldtype": "Float", "in_list_view": 1, - "label": "Rate", + "label": "Tax Rate", "oldfieldname": "rate", "oldfieldtype": "Currency" }, @@ -239,4 +239,4 @@ "sort_field": "modified", "sort_order": "DESC", "track_changes": 1 -} \ No newline at end of file +} From 724c934fbbf246c181f40535f037827e39e60c74 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Sun, 14 Jan 2024 10:15:50 +0530 Subject: [PATCH 133/675] fix: modified date was not set (cherry picked from commit 566876ae7a9ac4ce70f0c72dd69c933ed022bf46) --- .../purchase_taxes_and_charges/purchase_taxes_and_charges.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json b/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json index 249e7518f0b..f10e9842a49 100644 --- a/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json +++ b/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json @@ -230,7 +230,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2021-08-05 20:04:36.618240", + "modified": "2021-08-06 20:04:36.618240", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Taxes and Charges", From e9d2437c7ac0a547191658d4694370af863cd450 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Sun, 14 Jan 2024 10:45:43 +0530 Subject: [PATCH 134/675] fix: modified date (cherry picked from commit 6827edb2c51ffe44f5af32fc63e065481bb85662) --- .../purchase_taxes_and_charges/purchase_taxes_and_charges.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json b/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json index f10e9842a49..adab54b3756 100644 --- a/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json +++ b/erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json @@ -230,7 +230,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2021-08-06 20:04:36.618240", + "modified": "2024-01-14 10:04:36.618240", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Taxes and Charges", From d0e3458c8c696fcf5be2b2cfbea47dfb1a260193 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sun, 14 Jan 2024 15:52:37 +0530 Subject: [PATCH 135/675] fix: incorrect sql error if account name has '%' (cherry picked from commit 641c3de0caf3dd542a353edd78c8c18f686b8cae) --- .../report/customer_ledger_summary/customer_ledger_summary.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py b/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py index b2222c2d6a7..2da6d18f005 100644 --- a/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py +++ b/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.py @@ -376,6 +376,10 @@ class PartyLedgerSummaryReport(object): if not income_or_expense_accounts: # prevent empty 'in' condition income_or_expense_accounts.append("") + else: + # escape '%' in account name + # ignoring frappe.db.escape as it replaces single quotes with double quotes + income_or_expense_accounts = [x.replace("%", "%%") for x in income_or_expense_accounts] accounts_query = ( qb.from_(gl) From 4af3159f6245b414d498e8f40d75eaffbbdbdaaa Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 15 Jan 2024 12:11:17 +0530 Subject: [PATCH 136/675] ci: bump node in release workflow (backport #39377) (#39379) * ci: bump node in release workflow (cherry picked from commit aef87cced7da0524c7ca2dadfcd111aaf13ef0c2) # Conflicts: # .github/workflows/release.yml * chore: `conflicts` --------- Co-authored-by: s-aga-r --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ccd712065dc..e6a7f85f81b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,7 +17,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v2 with: - node-version: 18 + node-version: 20 - name: Setup dependencies run: | From 3caf11472d7d253a8cc25b07f95a5a74789418d4 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 15 Jan 2024 12:15:15 +0530 Subject: [PATCH 137/675] ci: bump node in release workflow (backport #39377) (backport #39379) (#39381) ci: bump node in release workflow (backport #39377) (#39379) * ci: bump node in release workflow (cherry picked from commit aef87cced7da0524c7ca2dadfcd111aaf13ef0c2) # Conflicts: # .github/workflows/release.yml * chore: `conflicts` --------- Co-authored-by: s-aga-r (cherry picked from commit 4af3159f6245b414d498e8f40d75eaffbbdbdaaa) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index ccd712065dc..e6a7f85f81b 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,7 +17,7 @@ jobs: - name: Setup Node.js uses: actions/setup-node@v2 with: - node-version: 18 + node-version: 20 - name: Setup dependencies run: | From a6bc5cae90465d6d517c59c61769623724f8eacd Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 15 Jan 2024 14:10:55 +0530 Subject: [PATCH 138/675] fix: pass accounts as list to query --- erpnext/accounts/report/purchase_register/purchase_register.py | 2 +- erpnext/accounts/report/sales_register/sales_register.py | 2 +- erpnext/accounts/report/utils.py | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/report/purchase_register/purchase_register.py b/erpnext/accounts/report/purchase_register/purchase_register.py index 4eb135b0487..b4ab5b8fa4e 100644 --- a/erpnext/accounts/report/purchase_register/purchase_register.py +++ b/erpnext/accounts/report/purchase_register/purchase_register.py @@ -433,7 +433,7 @@ def get_payments(filters): account_fieldname="paid_to", party="supplier", party_name="supplier_name", - party_account=get_party_account("Supplier", filters.supplier, filters.company), + party_account=[get_party_account("Supplier", filters.supplier, filters.company)], ) payment_entries = get_payment_entries(filters, args) journal_entries = get_journal_entries(filters, args) diff --git a/erpnext/accounts/report/sales_register/sales_register.py b/erpnext/accounts/report/sales_register/sales_register.py index 61b1fe2293c..1cb72f8d2d6 100644 --- a/erpnext/accounts/report/sales_register/sales_register.py +++ b/erpnext/accounts/report/sales_register/sales_register.py @@ -477,7 +477,7 @@ def get_payments(filters): account_fieldname="paid_from", party="customer", party_name="customer_name", - party_account=get_party_account("Customer", filters.customer, filters.company), + party_account=[get_party_account("Customer", filters.customer, filters.company)], ) payment_entries = get_payment_entries(filters, args) journal_entries = get_journal_entries(filters, args) diff --git a/erpnext/accounts/report/utils.py b/erpnext/accounts/report/utils.py index 01bc29fedf3..79ab799ccd3 100644 --- a/erpnext/accounts/report/utils.py +++ b/erpnext/accounts/report/utils.py @@ -253,7 +253,7 @@ def get_journal_entries(filters, args): (je.voucher_type == "Journal Entry") & (je.docstatus == 1) & (journal_account.party == filters.get(args.party)) - & (journal_account.account == args.party_account) + & (journal_account.account.isin(args.party_account)) ) .orderby(je.posting_date, je.name, order=Order.desc) ) From 4ea72f4b692a846bbd27f75054c229c3f7c167dd Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 15 Jan 2024 20:22:30 +0530 Subject: [PATCH 139/675] fix: possible typerror in utils.js and remove unwanted debugging statements (cherry picked from commit 60b26ad8b262752e7d491b3fe21b398c0928bfaf) --- .../report/budget_variance_report/budget_variance_report.js | 4 ---- erpnext/public/js/utils.js | 2 +- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.js b/erpnext/accounts/report/budget_variance_report/budget_variance_report.js index ca1cca13115..c9ddde9b5fb 100644 --- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.js +++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.js @@ -84,10 +84,6 @@ function get_filters() { options: budget_against_options, default: "Cost Center", reqd: 1, - get_data: function() { - console.log(this.options); - return ["Emacs", "Rocks"]; - }, on_change: function() { frappe.query_report.set_filter_value("budget_against_filter", []); frappe.query_report.refresh(); diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 682762678ba..4c76e2a869e 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -21,7 +21,7 @@ $.extend(erpnext, { }, toggle_naming_series: function() { - if(cur_frm.fields_dict.naming_series) { + if(cur_frm && cur_frm.fields_dict.naming_series) { cur_frm.toggle_display("naming_series", cur_frm.doc.__islocal?true:false); } }, From 4b197920c1e3e816f76f153fc8d755820a63eaf6 Mon Sep 17 00:00:00 2001 From: ljain112 Date: Tue, 16 Jan 2024 15:53:53 +0530 Subject: [PATCH 140/675] fix: show bill_date and bill_no in Purchase Register --- erpnext/accounts/report/purchase_register/purchase_register.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/accounts/report/purchase_register/purchase_register.py b/erpnext/accounts/report/purchase_register/purchase_register.py index b4ab5b8fa4e..f745c87a00a 100644 --- a/erpnext/accounts/report/purchase_register/purchase_register.py +++ b/erpnext/accounts/report/purchase_register/purchase_register.py @@ -89,6 +89,8 @@ def _execute(filters=None, additional_table_columns=None): "payable_account": inv.credit_to, "mode_of_payment": inv.mode_of_payment, "project": ", ".join(project) if inv.doctype == "Purchase Invoice" else inv.project, + "bill_no": inv.bill_no, + "bill_date": inv.bill_date, "remarks": inv.remarks, "purchase_order": ", ".join(purchase_order), "purchase_receipt": ", ".join(purchase_receipt), From f2e577bec749fcbba74c224e2869433f24d7a4bb Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 16 Jan 2024 13:38:53 +0530 Subject: [PATCH 141/675] fix: project query controller logic (cherry picked from commit 4eefb445a748100f3c36094188e38c127ad80051) # Conflicts: # erpnext/controllers/queries.py --- erpnext/controllers/queries.py | 59 +++++++++++++++++----------------- 1 file changed, 30 insertions(+), 29 deletions(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index b73ebf53ae8..aa06dad4598 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -6,9 +6,15 @@ import json from collections import defaultdict import frappe -from frappe import scrub +from frappe import qb, scrub from frappe.desk.reportview import get_filters_cond, get_match_cond +<<<<<<< HEAD from frappe.utils import nowdate, unique +======= +from frappe.query_builder import Criterion +from frappe.query_builder.functions import Concat, Sum +from frappe.utils import nowdate, today, unique +>>>>>>> 4eefb445a7 (fix: project query controller logic) import erpnext from erpnext.stock.get_item_details import _get_item_tax_template @@ -329,37 +335,32 @@ def bom(doctype, txt, searchfield, start, page_len, filters): @frappe.whitelist() @frappe.validate_and_sanitize_search_inputs def get_project_name(doctype, txt, searchfield, start, page_len, filters): - doctype = "Project" - cond = "" + proj = qb.DocType("Project") + qb_filter_and_conditions = [] + qb_filter_or_conditions = [] if filters and filters.get("customer"): - cond = """(`tabProject`.customer = %s or - ifnull(`tabProject`.customer,"")="") and""" % ( - frappe.db.escape(filters.get("customer")) - ) + qb_filter_and_conditions.append(proj.customer == filters.get("customer")) - fields = get_fields(doctype, ["name", "project_name"]) - searchfields = frappe.get_meta(doctype).get_search_fields() - searchfields = " or ".join(["`tabProject`." + field + " like %(txt)s" for field in searchfields]) + qb_filter_and_conditions.append(proj.status.notin(["Completed", "Cancelled"])) - return frappe.db.sql( - """select {fields} from `tabProject` - where - `tabProject`.status not in ('Completed', 'Cancelled') - and {cond} {scond} {match_cond} - order by - (case when locate(%(_txt)s, `tabProject`.name) > 0 then locate(%(_txt)s, `tabProject`.name) else 99999 end), - `tabProject`.idx desc, - `tabProject`.name asc - limit {page_len} offset {start}""".format( - fields=", ".join(["`tabProject`.{0}".format(f) for f in fields]), - cond=cond, - scond=searchfields, - match_cond=get_match_cond(doctype), - start=start, - page_len=page_len, - ), - {"txt": "%{0}%".format(txt), "_txt": txt.replace("%", "")}, - ) + q = qb.from_(proj) + + fields = get_fields("Project", ["name", "project_name"]) + for x in fields: + q = q.select(proj[x]) + + # ignore 'customer' and 'status' on searchfields as they must be exactly matched + searchfields = [ + x for x in frappe.get_meta(doctype).get_search_fields() if x not in ["customer", "status"] + ] + if txt: + for x in searchfields: + qb_filter_or_conditions.append(proj[x].like(f"%{txt}%")) + + q = q.where(Criterion.all(qb_filter_and_conditions)).where(Criterion.any(qb_filter_or_conditions)) + if page_len: + q = q.limit(page_len) + return q.run() @frappe.whitelist() From 98967ed58487cc63e0ed345c50b6f0fb1be97eea Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 16 Jan 2024 14:28:09 +0530 Subject: [PATCH 142/675] fix(test): test case for project query (cherry picked from commit 3349dde5e2914bd9e2dbe0ce4de94023bfee2e7f) --- erpnext/controllers/queries.py | 2 +- erpnext/controllers/tests/test_queries.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index aa06dad4598..c6285fc7344 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -345,7 +345,7 @@ def get_project_name(doctype, txt, searchfield, start, page_len, filters): q = qb.from_(proj) - fields = get_fields("Project", ["name", "project_name"]) + fields = get_fields(doctype, ["name", "project_name"]) for x in fields: q = q.select(proj[x]) diff --git a/erpnext/controllers/tests/test_queries.py b/erpnext/controllers/tests/test_queries.py index 60d1733021c..3a3bc1cd725 100644 --- a/erpnext/controllers/tests/test_queries.py +++ b/erpnext/controllers/tests/test_queries.py @@ -68,7 +68,7 @@ class TestQueries(unittest.TestCase): self.assertGreaterEqual(len(query(txt="_Test Item Home Desktop Manufactured")), 1) def test_project_query(self): - query = add_default_params(queries.get_project_name, "BOM") + query = add_default_params(queries.get_project_name, "Project") self.assertGreaterEqual(len(query(txt="_Test Project")), 1) From b35a83ee47f0d8d37112a458d79cfe6c2f452e39 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 16 Jan 2024 14:35:06 +0530 Subject: [PATCH 143/675] refactor: better ordering of query result (cherry picked from commit bfe42fdccb13ab797ac7252ada58df49af43ad54) # Conflicts: # erpnext/controllers/queries.py --- erpnext/controllers/queries.py | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index c6285fc7344..f581fb429bd 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -9,12 +9,19 @@ import frappe from frappe import qb, scrub from frappe.desk.reportview import get_filters_cond, get_match_cond <<<<<<< HEAD +<<<<<<< HEAD from frappe.utils import nowdate, unique ======= from frappe.query_builder import Criterion from frappe.query_builder.functions import Concat, Sum from frappe.utils import nowdate, today, unique >>>>>>> 4eefb445a7 (fix: project query controller logic) +======= +from frappe.query_builder import Criterion, CustomFunction +from frappe.query_builder.functions import Concat, Locate, Sum +from frappe.utils import nowdate, today, unique +from pypika import Order +>>>>>>> bfe42fdccb (refactor: better ordering of query result) import erpnext from erpnext.stock.get_item_details import _get_item_tax_template @@ -338,6 +345,8 @@ def get_project_name(doctype, txt, searchfield, start, page_len, filters): proj = qb.DocType("Project") qb_filter_and_conditions = [] qb_filter_or_conditions = [] + ifelse = CustomFunction("IF", ["condition", "then", "else"]) + if filters and filters.get("customer"): qb_filter_and_conditions.append(proj.customer == filters.get("customer")) @@ -349,17 +358,29 @@ def get_project_name(doctype, txt, searchfield, start, page_len, filters): for x in fields: q = q.select(proj[x]) - # ignore 'customer' and 'status' on searchfields as they must be exactly matched + # don't consider 'customer' and 'status' fields for pattern search, as they must be exactly matched searchfields = [ x for x in frappe.get_meta(doctype).get_search_fields() if x not in ["customer", "status"] ] + + # pattern search if txt: for x in searchfields: qb_filter_or_conditions.append(proj[x].like(f"%{txt}%")) q = q.where(Criterion.all(qb_filter_and_conditions)).where(Criterion.any(qb_filter_or_conditions)) + + # ordering + if txt: + # project_name containing search string 'txt' will be given higher precedence + q = q.orderby(ifelse(Locate(txt, proj.project_name) > 0, Locate(txt, proj.project_name), 99999)) + q = q.orderby(proj.idx, order=Order.desc).orderby(proj.name) + if page_len: q = q.limit(page_len) + + if start: + q = q.offset(start) return q.run() From 42c1de640ce5686b41eb5937e64d6f1351d808c8 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 17 Jan 2024 10:36:29 +0530 Subject: [PATCH 144/675] chore: resolve conflict --- erpnext/controllers/queries.py | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index f581fb429bd..06ea8336bd6 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -8,20 +8,10 @@ from collections import defaultdict import frappe from frappe import qb, scrub from frappe.desk.reportview import get_filters_cond, get_match_cond -<<<<<<< HEAD -<<<<<<< HEAD -from frappe.utils import nowdate, unique -======= -from frappe.query_builder import Criterion -from frappe.query_builder.functions import Concat, Sum -from frappe.utils import nowdate, today, unique ->>>>>>> 4eefb445a7 (fix: project query controller logic) -======= from frappe.query_builder import Criterion, CustomFunction -from frappe.query_builder.functions import Concat, Locate, Sum -from frappe.utils import nowdate, today, unique +from frappe.query_builder.functions import Locate +from frappe.utils import nowdate, unique from pypika import Order ->>>>>>> bfe42fdccb (refactor: better ordering of query result) import erpnext from erpnext.stock.get_item_details import _get_item_tax_template From 3989b9757968e4cc5f9676dab5e034ea08b0191e Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 15 Jan 2024 16:54:19 +0530 Subject: [PATCH 145/675] fix: WDV as per IT Act: calculate yearly amount first and then split it based on months (cherry picked from commit 22bd6a54b24129403e0b399938bddcaa9d630cae) # Conflicts: # erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py --- .../asset_depreciation_schedule.py | 1020 +++++++++++++++++ 1 file changed, 1020 insertions(+) create mode 100644 erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py new file mode 100644 index 00000000000..ffb50ebe452 --- /dev/null +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py @@ -0,0 +1,1020 @@ +# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +import frappe +from frappe import _ +from frappe.model.document import Document +from frappe.utils import ( + add_days, + add_months, + add_years, + cint, + date_diff, + flt, + get_first_day, + get_last_day, + getdate, + is_last_day_of_the_month, + month_diff, +) + +import erpnext +from erpnext.accounts.utils import get_fiscal_year + + +class AssetDepreciationSchedule(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.assets.doctype.depreciation_schedule.depreciation_schedule import ( + DepreciationSchedule, + ) + + amended_from: DF.Link | None + asset: DF.Link + company: DF.Link | None + daily_prorata_based: DF.Check + depreciation_method: DF.Literal[ + "", "Straight Line", "Double Declining Balance", "Written Down Value", "Manual" + ] + depreciation_schedule: DF.Table[DepreciationSchedule] + expected_value_after_useful_life: DF.Currency + finance_book: DF.Link | None + finance_book_id: DF.Int + frequency_of_depreciation: DF.Int + gross_purchase_amount: DF.Currency + naming_series: DF.Literal["ACC-ADS-.YYYY.-"] + notes: DF.SmallText | None + number_of_depreciations_booked: DF.Int + opening_accumulated_depreciation: DF.Currency + rate_of_depreciation: DF.Percent + shift_based: DF.Check + status: DF.Literal["Draft", "Active", "Cancelled"] + total_number_of_depreciations: DF.Int + # end: auto-generated types + + def before_save(self): + if not self.finance_book_id: + self.prepare_draft_asset_depr_schedule_data_from_asset_name_and_fb_name( + self.asset, self.finance_book + ) + self.update_shift_depr_schedule() + + def validate(self): + self.validate_another_asset_depr_schedule_does_not_exist() + + def validate_another_asset_depr_schedule_does_not_exist(self): + finance_book_filter = ["finance_book", "is", "not set"] + if self.finance_book: + finance_book_filter = ["finance_book", "=", self.finance_book] + + asset_depr_schedule = frappe.db.exists( + "Asset Depreciation Schedule", + [ + ["asset", "=", self.asset], + finance_book_filter, + ["docstatus", "<", 2], + ], + ) + + if asset_depr_schedule and asset_depr_schedule != self.name: + if self.finance_book: + frappe.throw( + _( + "Asset Depreciation Schedule {0} for Asset {1} and Finance Book {2} already exists." + ).format(asset_depr_schedule, self.asset, self.finance_book) + ) + else: + frappe.throw( + _("Asset Depreciation Schedule {0} for Asset {1} already exists.").format( + asset_depr_schedule, self.asset + ) + ) + + def on_submit(self): + self.db_set("status", "Active") + + def before_cancel(self): + if not self.flags.should_not_cancel_depreciation_entries: + self.cancel_depreciation_entries() + + def cancel_depreciation_entries(self): + for d in self.get("depreciation_schedule"): + if d.journal_entry: + frappe.get_doc("Journal Entry", d.journal_entry).cancel() + + def on_cancel(self): + self.db_set("status", "Cancelled") + + def update_shift_depr_schedule(self): + if not self.shift_based or self.docstatus != 0: + return + + asset_doc = frappe.get_doc("Asset", self.asset) + fb_row = asset_doc.finance_books[self.finance_book_id - 1] + + self.make_depr_schedule(asset_doc, fb_row) + self.set_accumulated_depreciation(asset_doc, fb_row) + + def prepare_draft_asset_depr_schedule_data_from_asset_name_and_fb_name(self, asset_name, fb_name): + asset_doc = frappe.get_doc("Asset", asset_name) + + finance_book_filter = ["finance_book", "is", "not set"] + if fb_name: + finance_book_filter = ["finance_book", "=", fb_name] + + asset_finance_book_name = frappe.db.get_value( + doctype="Asset Finance Book", + filters=[["parent", "=", asset_name], finance_book_filter], + ) + asset_finance_book_doc = frappe.get_doc("Asset Finance Book", asset_finance_book_name) + + self.prepare_draft_asset_depr_schedule_data(asset_doc, asset_finance_book_doc) + + def prepare_draft_asset_depr_schedule_data( + self, + asset_doc, + row, + date_of_disposal=None, + date_of_return=None, + update_asset_finance_book_row=True, + ): + have_asset_details_been_modified = self.have_asset_details_been_modified(asset_doc) + not_manual_depr_or_have_manual_depr_details_been_modified = ( + self.not_manual_depr_or_have_manual_depr_details_been_modified(row) + ) + + self.set_draft_asset_depr_schedule_details(asset_doc, row) + + if self.should_prepare_depreciation_schedule( + have_asset_details_been_modified, not_manual_depr_or_have_manual_depr_details_been_modified + ): + self.make_depr_schedule(asset_doc, row, date_of_disposal, update_asset_finance_book_row) + self.set_accumulated_depreciation(asset_doc, row, date_of_disposal, date_of_return) + + def have_asset_details_been_modified(self, asset_doc): + return ( + asset_doc.gross_purchase_amount != self.gross_purchase_amount + or asset_doc.opening_accumulated_depreciation != self.opening_accumulated_depreciation + or asset_doc.number_of_depreciations_booked != self.number_of_depreciations_booked + ) + + def not_manual_depr_or_have_manual_depr_details_been_modified(self, row): + return ( + self.depreciation_method != "Manual" + or row.total_number_of_depreciations != self.total_number_of_depreciations + or row.frequency_of_depreciation != self.frequency_of_depreciation + or getdate(row.depreciation_start_date) != self.get("depreciation_schedule")[0].schedule_date + or row.expected_value_after_useful_life != self.expected_value_after_useful_life + ) + + def should_prepare_depreciation_schedule( + self, have_asset_details_been_modified, not_manual_depr_or_have_manual_depr_details_been_modified + ): + if not self.get("depreciation_schedule"): + return True + + old_asset_depr_schedule_doc = self.get_doc_before_save() + + if self.docstatus != 0 and not old_asset_depr_schedule_doc: + return True + + if have_asset_details_been_modified or not_manual_depr_or_have_manual_depr_details_been_modified: + return True + + return False + + def set_draft_asset_depr_schedule_details(self, asset_doc, row): + self.asset = asset_doc.name + self.finance_book = row.finance_book + self.finance_book_id = row.idx + self.opening_accumulated_depreciation = asset_doc.opening_accumulated_depreciation or 0 + self.number_of_depreciations_booked = asset_doc.number_of_depreciations_booked or 0 + self.gross_purchase_amount = asset_doc.gross_purchase_amount + self.depreciation_method = row.depreciation_method + self.total_number_of_depreciations = row.total_number_of_depreciations + self.frequency_of_depreciation = row.frequency_of_depreciation + self.rate_of_depreciation = row.rate_of_depreciation + self.expected_value_after_useful_life = row.expected_value_after_useful_life + self.daily_prorata_based = row.daily_prorata_based + self.shift_based = row.shift_based + self.status = "Draft" + + def make_depr_schedule( + self, + asset_doc, + row, + date_of_disposal=None, + update_asset_finance_book_row=True, + value_after_depreciation=None, + ): + if not self.get("depreciation_schedule"): + self.depreciation_schedule = [] + + if not asset_doc.available_for_use_date: + return + + start = self.clear_depr_schedule() + + self._make_depr_schedule( + asset_doc, row, start, date_of_disposal, update_asset_finance_book_row, value_after_depreciation + ) + + def clear_depr_schedule(self): + start = 0 + num_of_depreciations_completed = 0 + depr_schedule = [] + + self.schedules_before_clearing = self.get("depreciation_schedule") + + for schedule in self.get("depreciation_schedule"): + if schedule.journal_entry: + num_of_depreciations_completed += 1 + depr_schedule.append(schedule) + else: + start = num_of_depreciations_completed + break + + self.depreciation_schedule = depr_schedule + + return start + + def _make_depr_schedule( + self, + asset_doc, + row, + start, + date_of_disposal, + update_asset_finance_book_row, + value_after_depreciation, + ): + asset_doc.validate_asset_finance_books(row) + + if not value_after_depreciation: + value_after_depreciation = _get_value_after_depreciation_for_making_schedule(asset_doc, row) + row.value_after_depreciation = value_after_depreciation + + if update_asset_finance_book_row: + row.db_update() + + final_number_of_depreciations = cint(row.total_number_of_depreciations) - cint( + self.number_of_depreciations_booked + ) + + has_pro_rata = _check_is_pro_rata(asset_doc, row) + if has_pro_rata: + final_number_of_depreciations += 1 + + has_wdv_or_dd_non_yearly_pro_rata = False + if ( + row.depreciation_method in ("Written Down Value", "Double Declining Balance") + and cint(row.frequency_of_depreciation) != 12 + ): + has_wdv_or_dd_non_yearly_pro_rata = _check_is_pro_rata( + asset_doc, row, wdv_or_dd_non_yearly=True + ) + + skip_row = False + should_get_last_day = is_last_day_of_the_month(row.depreciation_start_date) + + depreciation_amount = 0 + + number_of_pending_depreciations = final_number_of_depreciations - start + yearly_opening_wdv = value_after_depreciation + current_fiscal_year_end_date = None + for n in range(start, final_number_of_depreciations): + # If depreciation is already completed (for double declining balance) + if skip_row: + continue + + schedule_date = add_months(row.depreciation_start_date, n * cint(row.frequency_of_depreciation)) + if not current_fiscal_year_end_date: + current_fiscal_year_end_date = get_fiscal_year(row.depreciation_start_date)[2] + elif getdate(schedule_date) > getdate(current_fiscal_year_end_date): + current_fiscal_year_end_date = add_years(current_fiscal_year_end_date, 1) + yearly_opening_wdv = value_after_depreciation + + if n > 0 and len(self.get("depreciation_schedule")) > n - 1: + prev_depreciation_amount = self.get("depreciation_schedule")[n - 1].depreciation_amount + else: + prev_depreciation_amount = 0 + + depreciation_amount = get_depreciation_amount( + self, + asset_doc, + value_after_depreciation, + yearly_opening_wdv, + row, + n, + prev_depreciation_amount, + has_wdv_or_dd_non_yearly_pro_rata, + number_of_pending_depreciations, + ) + + if not has_pro_rata or ( + n < (cint(final_number_of_depreciations) - 1) or final_number_of_depreciations == 2 + ): + schedule_date = add_months( + row.depreciation_start_date, n * cint(row.frequency_of_depreciation) + ) + + if should_get_last_day: + schedule_date = get_last_day(schedule_date) + + # if asset is being sold or scrapped + if date_of_disposal: + from_date = add_months( + getdate(asset_doc.available_for_use_date), + (asset_doc.number_of_depreciations_booked * row.frequency_of_depreciation), + ) + if self.depreciation_schedule: + from_date = self.depreciation_schedule[-1].schedule_date + + depreciation_amount, days, months = _get_pro_rata_amt( + row, + depreciation_amount, + from_date, + date_of_disposal, + ) + + if depreciation_amount > 0: + self.add_depr_schedule_row(date_of_disposal, depreciation_amount, n) + + break + + # For first row + if ( + n == 0 + and (has_pro_rata or has_wdv_or_dd_non_yearly_pro_rata) + and not self.opening_accumulated_depreciation + and not self.flags.wdv_it_act_applied + ): + from_date = add_days( + asset_doc.available_for_use_date, -1 + ) # needed to calc depr amount for available_for_use_date too + depreciation_amount, days, months = _get_pro_rata_amt( + row, + depreciation_amount, + from_date, + row.depreciation_start_date, + has_wdv_or_dd_non_yearly_pro_rata, + ) + elif n == 0 and has_wdv_or_dd_non_yearly_pro_rata and self.opening_accumulated_depreciation: + if not is_first_day_of_the_month(getdate(asset_doc.available_for_use_date)): + from_date = get_last_day( + add_months( + getdate(asset_doc.available_for_use_date), + ((self.number_of_depreciations_booked - 1) * row.frequency_of_depreciation), + ) + ) + else: + from_date = add_months( + getdate(add_days(asset_doc.available_for_use_date, -1)), + (self.number_of_depreciations_booked * row.frequency_of_depreciation), + ) + depreciation_amount, days, months = _get_pro_rata_amt( + row, + depreciation_amount, + from_date, + row.depreciation_start_date, + has_wdv_or_dd_non_yearly_pro_rata, + ) + + # For last row + elif has_pro_rata and n == cint(final_number_of_depreciations) - 1: + if not asset_doc.flags.increase_in_asset_life: + # In case of increase_in_asset_life, the asset.to_date is already set on asset_repair submission + asset_doc.to_date = add_months( + asset_doc.available_for_use_date, + (n + self.number_of_depreciations_booked) * cint(row.frequency_of_depreciation), + ) + + depreciation_amount_without_pro_rata = depreciation_amount + + depreciation_amount, days, months = _get_pro_rata_amt( + row, + depreciation_amount, + schedule_date, + asset_doc.to_date, + has_wdv_or_dd_non_yearly_pro_rata, + ) + + depreciation_amount = self.get_adjusted_depreciation_amount( + depreciation_amount_without_pro_rata, depreciation_amount + ) + + schedule_date = add_days(schedule_date, days) + + if not depreciation_amount: + continue + value_after_depreciation = flt( + value_after_depreciation - flt(depreciation_amount), + asset_doc.precision("gross_purchase_amount"), + ) + + # Adjust depreciation amount in the last period based on the expected value after useful life + if row.expected_value_after_useful_life and ( + ( + n == cint(final_number_of_depreciations) - 1 + and value_after_depreciation != row.expected_value_after_useful_life + ) + or value_after_depreciation < row.expected_value_after_useful_life + ): + depreciation_amount += value_after_depreciation - row.expected_value_after_useful_life + skip_row = True + + if flt(depreciation_amount, asset_doc.precision("gross_purchase_amount")) > 0: + self.add_depr_schedule_row(schedule_date, depreciation_amount, n) + + # to ensure that final accumulated depreciation amount is accurate + def get_adjusted_depreciation_amount( + self, depreciation_amount_without_pro_rata, depreciation_amount_for_last_row + ): + if not self.opening_accumulated_depreciation: + depreciation_amount_for_first_row = self.get_depreciation_amount_for_first_row() + + if ( + depreciation_amount_for_first_row + depreciation_amount_for_last_row + != depreciation_amount_without_pro_rata + ): + depreciation_amount_for_last_row = ( + depreciation_amount_without_pro_rata - depreciation_amount_for_first_row + ) + + return depreciation_amount_for_last_row + + def get_depreciation_amount_for_first_row(self): + return self.get("depreciation_schedule")[0].depreciation_amount + + def add_depr_schedule_row(self, schedule_date, depreciation_amount, schedule_idx): + if self.shift_based: + shift = ( + self.schedules_before_clearing[schedule_idx].shift + if self.schedules_before_clearing and len(self.schedules_before_clearing) > schedule_idx + else frappe.get_cached_value("Asset Shift Factor", {"default": 1}, "shift_name") + ) + else: + shift = None + + self.append( + "depreciation_schedule", + { + "schedule_date": schedule_date, + "depreciation_amount": depreciation_amount, + "shift": shift, + }, + ) + + def set_accumulated_depreciation( + self, + asset_doc, + row, + date_of_disposal=None, + date_of_return=None, + ignore_booked_entry=False, + ): + straight_line_idx = [ + d.idx + for d in self.get("depreciation_schedule") + if self.depreciation_method == "Straight Line" or self.depreciation_method == "Manual" + ] + + accumulated_depreciation = None + value_after_depreciation = flt(row.value_after_depreciation) + + for i, d in enumerate(self.get("depreciation_schedule")): + if ignore_booked_entry and d.journal_entry: + continue + + if not accumulated_depreciation: + if i > 0 and asset_doc.flags.decrease_in_asset_value_due_to_value_adjustment: + accumulated_depreciation = self.get("depreciation_schedule")[ + i - 1 + ].accumulated_depreciation_amount + else: + accumulated_depreciation = flt(self.opening_accumulated_depreciation) + + depreciation_amount = flt(d.depreciation_amount, d.precision("depreciation_amount")) + value_after_depreciation -= flt(depreciation_amount) + + # for the last row, if depreciation method = Straight Line + if ( + straight_line_idx + and i == max(straight_line_idx) - 1 + and not date_of_disposal + and not date_of_return + and not row.shift_based + ): + depreciation_amount += flt( + value_after_depreciation - flt(row.expected_value_after_useful_life), + d.precision("depreciation_amount"), + ) + + d.depreciation_amount = depreciation_amount + accumulated_depreciation += d.depreciation_amount + d.accumulated_depreciation_amount = flt( + accumulated_depreciation, d.precision("accumulated_depreciation_amount") + ) + + +def _get_value_after_depreciation_for_making_schedule(asset_doc, fb_row): + if asset_doc.docstatus == 1 and fb_row.value_after_depreciation: + value_after_depreciation = flt(fb_row.value_after_depreciation) + else: + value_after_depreciation = flt(asset_doc.gross_purchase_amount) - flt( + asset_doc.opening_accumulated_depreciation + ) + + return value_after_depreciation + + +# if it returns True, depreciation_amount will not be equal for the first and last rows +def _check_is_pro_rata(asset_doc, row, wdv_or_dd_non_yearly=False): + has_pro_rata = False + + # if not existing asset, from_date = available_for_use_date + # otherwise, if number_of_depreciations_booked = 2, available_for_use_date = 01/01/2020 and frequency_of_depreciation = 12 + # from_date = 01/01/2022 + from_date = _get_modified_available_for_use_date(asset_doc, row, wdv_or_dd_non_yearly) + days = date_diff(row.depreciation_start_date, from_date) + 1 + + if wdv_or_dd_non_yearly: + total_days = get_total_days(row.depreciation_start_date, 12) + else: + # if frequency_of_depreciation is 12 months, total_days = 365 + total_days = get_total_days(row.depreciation_start_date, row.frequency_of_depreciation) + + if days < total_days: + has_pro_rata = True + + return has_pro_rata + + +def _get_modified_available_for_use_date(asset_doc, row, wdv_or_dd_non_yearly=False): + if wdv_or_dd_non_yearly: + return add_months( + asset_doc.available_for_use_date, + (asset_doc.number_of_depreciations_booked * 12), + ) + else: + return add_months( + asset_doc.available_for_use_date, + (asset_doc.number_of_depreciations_booked * row.frequency_of_depreciation), + ) + + +def _get_pro_rata_amt( + row, depreciation_amount, from_date, to_date, has_wdv_or_dd_non_yearly_pro_rata=False +): + days = date_diff(to_date, from_date) + months = month_diff(to_date, from_date) + if has_wdv_or_dd_non_yearly_pro_rata: + total_days = get_total_days(to_date, 12) + else: + total_days = get_total_days(to_date, row.frequency_of_depreciation) + + return (depreciation_amount * flt(days)) / flt(total_days), days, months + + +def get_total_days(date, frequency): + period_start_date = add_months(date, cint(frequency) * -1) + + if is_last_day_of_the_month(date): + period_start_date = get_last_day(period_start_date) + + return date_diff(date, period_start_date) + + +def get_depreciation_amount( + asset_depr_schedule, + asset, + depreciable_value, + yearly_opening_wdv, + fb_row, + schedule_idx=0, + prev_depreciation_amount=0, + has_wdv_or_dd_non_yearly_pro_rata=False, + number_of_pending_depreciations=0, +): + if fb_row.depreciation_method in ("Straight Line", "Manual"): + return get_straight_line_or_manual_depr_amount( + asset_depr_schedule, asset, fb_row, schedule_idx, number_of_pending_depreciations + ) + else: + return get_wdv_or_dd_depr_amount( + asset, + fb_row, + depreciable_value, + yearly_opening_wdv, + schedule_idx, + prev_depreciation_amount, + has_wdv_or_dd_non_yearly_pro_rata, + asset_depr_schedule, + ) + + +def get_straight_line_or_manual_depr_amount( + asset_depr_schedule, asset, row, schedule_idx, number_of_pending_depreciations +): + if row.shift_based: + return get_shift_depr_amount(asset_depr_schedule, asset, row, schedule_idx) + + # if the Depreciation Schedule is being modified after Asset Repair due to increase in asset life and value + if asset.flags.increase_in_asset_life: + return (flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life)) / ( + date_diff(asset.to_date, asset.available_for_use_date) / 365 + ) + # if the Depreciation Schedule is being modified after Asset Repair due to increase in asset value + elif asset.flags.increase_in_asset_value_due_to_repair: + return (flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life)) / flt( + row.total_number_of_depreciations + ) + # if the Depreciation Schedule is being modified after Asset Value Adjustment due to decrease in asset value + elif asset.flags.decrease_in_asset_value_due_to_value_adjustment: + if row.daily_prorata_based: + daily_depr_amount = ( + flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life) + ) / date_diff( + get_last_day( + add_months( + row.depreciation_start_date, + flt(row.total_number_of_depreciations - asset.number_of_depreciations_booked - 1) + * row.frequency_of_depreciation, + ) + ), + add_days( + get_last_day( + add_months( + row.depreciation_start_date, + flt( + row.total_number_of_depreciations + - asset.number_of_depreciations_booked + - number_of_pending_depreciations + - 1 + ) + * row.frequency_of_depreciation, + ) + ), + 1, + ), + ) + + to_date = get_last_day( + add_months(row.depreciation_start_date, schedule_idx * row.frequency_of_depreciation) + ) + from_date = add_days( + get_last_day( + add_months(row.depreciation_start_date, (schedule_idx - 1) * row.frequency_of_depreciation) + ), + 1, + ) + + return daily_depr_amount * (date_diff(to_date, from_date) + 1) + else: + return ( + flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life) + ) / number_of_pending_depreciations + # if the Depreciation Schedule is being prepared for the first time + else: + if row.daily_prorata_based: + daily_depr_amount = ( + flt(asset.gross_purchase_amount) + - flt(asset.opening_accumulated_depreciation) + - flt(row.expected_value_after_useful_life) + ) / date_diff( + get_last_day( + add_months( + row.depreciation_start_date, + flt(row.total_number_of_depreciations - asset.number_of_depreciations_booked - 1) + * row.frequency_of_depreciation, + ) + ), + add_days( + get_last_day(add_months(row.depreciation_start_date, -1 * row.frequency_of_depreciation)), 1 + ), + ) + + to_date = get_last_day( + add_months(row.depreciation_start_date, schedule_idx * row.frequency_of_depreciation) + ) + from_date = add_days( + get_last_day( + add_months(row.depreciation_start_date, (schedule_idx - 1) * row.frequency_of_depreciation) + ), + 1, + ) + + return daily_depr_amount * (date_diff(to_date, from_date) + 1) + else: + return ( + flt(asset.gross_purchase_amount) + - flt(asset.opening_accumulated_depreciation) + - flt(row.expected_value_after_useful_life) + ) / flt(row.total_number_of_depreciations - asset.number_of_depreciations_booked) + + +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 ( + flt(asset.gross_purchase_amount) + - flt(asset.opening_accumulated_depreciation) + - flt(row.expected_value_after_useful_life) + ) / flt(row.total_number_of_depreciations - asset.number_of_depreciations_booked) + + asset_shift_factors_map = get_asset_shift_factors_map() + shift = ( + asset_depr_schedule.schedules_before_clearing[schedule_idx].shift + if len(asset_depr_schedule.schedules_before_clearing) > schedule_idx + else None + ) + shift_factor = asset_shift_factors_map.get(shift) if shift else 0 + + shift_factors_sum = sum( + flt(asset_shift_factors_map.get(schedule.shift)) + for schedule in asset_depr_schedule.schedules_before_clearing + ) + + return ( + ( + flt(asset.gross_purchase_amount) + - flt(asset.opening_accumulated_depreciation) + - flt(row.expected_value_after_useful_life) + ) + / flt(shift_factors_sum) + ) * shift_factor + + +def get_asset_shift_factors_map(): + return dict(frappe.db.get_all("Asset Shift Factor", ["shift_name", "shift_factor"], as_list=True)) + + +@erpnext.allow_regional +def get_wdv_or_dd_depr_amount( + asset, + fb_row, + depreciable_value, + yearly_opening_wdv, + schedule_idx, + prev_depreciation_amount, + has_wdv_or_dd_non_yearly_pro_rata, + asset_depr_schedule, +): + return ( + get_default_wdv_or_dd_depr_amount( + asset, + fb_row, + depreciable_value, + schedule_idx, + prev_depreciation_amount, + has_wdv_or_dd_non_yearly_pro_rata, + asset_depr_schedule, + ), + None, + ) + + +def get_default_wdv_or_dd_depr_amount( + asset, + fb_row, + depreciable_value, + schedule_idx, + prev_depreciation_amount, + has_wdv_or_dd_non_yearly_pro_rata, + asset_depr_schedule, +): + if cint(fb_row.frequency_of_depreciation) == 12: + return flt(depreciable_value) * (flt(fb_row.rate_of_depreciation) / 100) + else: + if has_wdv_or_dd_non_yearly_pro_rata: + if schedule_idx == 0: + return flt(depreciable_value) * (flt(fb_row.rate_of_depreciation) / 100) + elif schedule_idx % (12 / cint(fb_row.frequency_of_depreciation)) == 1: + return ( + flt(depreciable_value) + * flt(fb_row.frequency_of_depreciation) + * (flt(fb_row.rate_of_depreciation) / 1200) + ) + else: + return prev_depreciation_amount + else: + if schedule_idx % (12 / cint(fb_row.frequency_of_depreciation)) == 0: + return ( + flt(depreciable_value) + * flt(fb_row.frequency_of_depreciation) + * (flt(fb_row.rate_of_depreciation) / 1200) + ) + else: + return prev_depreciation_amount + + +def make_draft_asset_depr_schedules_if_not_present(asset_doc): + asset_depr_schedules_names = [] + + for row in asset_doc.get("finance_books"): + draft_asset_depr_schedule_name = get_asset_depr_schedule_name( + asset_doc.name, "Draft", row.finance_book + ) + + active_asset_depr_schedule_name = get_asset_depr_schedule_name( + asset_doc.name, "Active", row.finance_book + ) + + if not draft_asset_depr_schedule_name and not active_asset_depr_schedule_name: + name = make_draft_asset_depr_schedule(asset_doc, row) + asset_depr_schedules_names.append(name) + + return asset_depr_schedules_names + + +def make_draft_asset_depr_schedules(asset_doc): + asset_depr_schedules_names = [] + + for row in asset_doc.get("finance_books"): + name = make_draft_asset_depr_schedule(asset_doc, row) + asset_depr_schedules_names.append(name) + + return asset_depr_schedules_names + + +def make_draft_asset_depr_schedule(asset_doc, row): + asset_depr_schedule_doc = frappe.new_doc("Asset Depreciation Schedule") + + asset_depr_schedule_doc.prepare_draft_asset_depr_schedule_data(asset_doc, row) + + asset_depr_schedule_doc.insert() + + return asset_depr_schedule_doc.name + + +def update_draft_asset_depr_schedules(asset_doc): + for row in asset_doc.get("finance_books"): + asset_depr_schedule_doc = get_asset_depr_schedule_doc(asset_doc.name, "Draft", row.finance_book) + + if not asset_depr_schedule_doc: + continue + + asset_depr_schedule_doc.prepare_draft_asset_depr_schedule_data(asset_doc, row) + + asset_depr_schedule_doc.save() + + +def convert_draft_asset_depr_schedules_into_active(asset_doc): + for row in asset_doc.get("finance_books"): + asset_depr_schedule_doc = get_asset_depr_schedule_doc(asset_doc.name, "Draft", row.finance_book) + + if not asset_depr_schedule_doc: + continue + + asset_depr_schedule_doc.submit() + + +def cancel_asset_depr_schedules(asset_doc): + for row in asset_doc.get("finance_books"): + asset_depr_schedule_doc = get_asset_depr_schedule_doc(asset_doc.name, "Active", row.finance_book) + + if not asset_depr_schedule_doc: + continue + + asset_depr_schedule_doc.cancel() + + +def make_new_active_asset_depr_schedules_and_cancel_current_ones( + asset_doc, + notes, + date_of_disposal=None, + date_of_return=None, + value_after_depreciation=None, + ignore_booked_entry=False, +): + for row in asset_doc.get("finance_books"): + current_asset_depr_schedule_doc = get_asset_depr_schedule_doc( + asset_doc.name, "Active", row.finance_book + ) + + if not current_asset_depr_schedule_doc: + frappe.throw( + _("Asset Depreciation Schedule not found for Asset {0} and Finance Book {1}").format( + asset_doc.name, row.finance_book + ) + ) + + new_asset_depr_schedule_doc = frappe.copy_doc(current_asset_depr_schedule_doc) + + if asset_doc.flags.increase_in_asset_value_due_to_repair and row.depreciation_method in ( + "Written Down Value", + "Double Declining Balance", + ): + new_rate_of_depreciation = flt( + asset_doc.get_depreciation_rate(row), row.precision("rate_of_depreciation") + ) + row.rate_of_depreciation = new_rate_of_depreciation + new_asset_depr_schedule_doc.rate_of_depreciation = new_rate_of_depreciation + + new_asset_depr_schedule_doc.make_depr_schedule( + asset_doc, row, date_of_disposal, value_after_depreciation=value_after_depreciation + ) + new_asset_depr_schedule_doc.set_accumulated_depreciation( + asset_doc, row, date_of_disposal, date_of_return, ignore_booked_entry + ) + + new_asset_depr_schedule_doc.notes = notes + + current_asset_depr_schedule_doc.flags.should_not_cancel_depreciation_entries = True + current_asset_depr_schedule_doc.cancel() + + new_asset_depr_schedule_doc.submit() + + +def get_temp_asset_depr_schedule_doc( + asset_doc, + row, + date_of_disposal=None, + date_of_return=None, + update_asset_finance_book_row=False, + new_depr_schedule=None, +): + current_asset_depr_schedule_doc = get_asset_depr_schedule_doc( + asset_doc.name, "Active", row.finance_book + ) + + if not current_asset_depr_schedule_doc: + frappe.throw( + _("Asset Depreciation Schedule not found for Asset {0} and Finance Book {1}").format( + asset_doc.name, row.finance_book + ) + ) + + temp_asset_depr_schedule_doc = frappe.copy_doc(current_asset_depr_schedule_doc) + + if new_depr_schedule: + temp_asset_depr_schedule_doc.depreciation_schedule = [] + + for schedule in new_depr_schedule: + temp_asset_depr_schedule_doc.append( + "depreciation_schedule", + { + "schedule_date": schedule.schedule_date, + "depreciation_amount": schedule.depreciation_amount, + "accumulated_depreciation_amount": schedule.accumulated_depreciation_amount, + "journal_entry": schedule.journal_entry, + "shift": schedule.shift, + }, + ) + + temp_asset_depr_schedule_doc.prepare_draft_asset_depr_schedule_data( + asset_doc, + row, + date_of_disposal, + date_of_return, + update_asset_finance_book_row, + ) + + return temp_asset_depr_schedule_doc + + +@frappe.whitelist() +def get_depr_schedule(asset_name, status, finance_book=None): + asset_depr_schedule_doc = get_asset_depr_schedule_doc(asset_name, status, finance_book) + + if not asset_depr_schedule_doc: + return + + return asset_depr_schedule_doc.get("depreciation_schedule") + + +@frappe.whitelist() +def get_asset_depr_schedule_doc(asset_name, status, finance_book=None): + asset_depr_schedule_name = get_asset_depr_schedule_name(asset_name, status, finance_book) + + if not asset_depr_schedule_name: + return + + asset_depr_schedule_doc = frappe.get_doc("Asset Depreciation Schedule", asset_depr_schedule_name) + + return asset_depr_schedule_doc + + +def get_asset_depr_schedule_name(asset_name, status, finance_book=None): + finance_book_filter = ["finance_book", "is", "not set"] + if finance_book: + finance_book_filter = ["finance_book", "=", finance_book] + + return frappe.db.get_value( + doctype="Asset Depreciation Schedule", + filters=[ + ["asset", "=", asset_name], + finance_book_filter, + ["status", "=", status], + ], + ) + + +def is_first_day_of_the_month(date): + first_day_of_the_month = get_first_day(date) + + return getdate(first_day_of_the_month) == getdate(date) From 1dff96057c72991bfc579184d09edc58739fe01a Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 15 Jan 2024 17:54:47 +0530 Subject: [PATCH 146/675] fix: Cancel asset capitalisation record on cancellation of asset and vice-versa (cherry picked from commit efe9f6656f01c46a6ac02e3bb61851564670d6bc) # Conflicts: # erpnext/assets/doctype/asset_capitalization/asset_capitalization.py --- erpnext/assets/doctype/asset/asset.json | 5 ++--- erpnext/assets/doctype/asset/asset.py | 11 +++++++++++ .../asset_capitalization.py | 17 +++++++++++++++++ 3 files changed, 30 insertions(+), 3 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json index cdccf81507b..d8b8bf18a15 100644 --- a/erpnext/assets/doctype/asset/asset.json +++ b/erpnext/assets/doctype/asset/asset.json @@ -202,8 +202,7 @@ "fieldname": "purchase_date", "fieldtype": "Date", "label": "Purchase Date", - "mandatory_depends_on": "eval:!doc.is_existing_asset", - "read_only": 1, + "mandatory_depends_on": "eval:!doc.is_existing_asset && !doc.is_composite_asset", "read_only_depends_on": "eval:!doc.is_existing_asset && !doc.is_composite_asset" }, { @@ -583,7 +582,7 @@ "link_fieldname": "target_asset" } ], - "modified": "2024-01-05 17:36:53.131512", + "modified": "2024-01-15 17:35:49.226603", "modified_by": "Administrator", "module": "Assets", "name": "Asset", diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 3db4a8d18bd..dc1ab378eee 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -61,6 +61,7 @@ class Asset(AccountsController): def on_cancel(self): self.validate_cancellation() self.cancel_movement_entries() + self.cancel_capitalization() self.delete_depreciation_entries() self.set_status() self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry") @@ -831,6 +832,16 @@ class Asset(AccountsController): movement = frappe.get_doc("Asset Movement", movement.get("name")) movement.cancel() + def cancel_capitalization(self): + asset_capitalization = frappe.db.get_value( + "Asset Capitalization", + {"target_asset": self.name, "docstatus": 1, "entry_type": "Capitalization"}, + ) + + if asset_capitalization: + asset_capitalization = frappe.get_doc("Asset Capitalization", asset_capitalization) + asset_capitalization.cancel() + def delete_depreciation_entries(self): if self.calculate_depreciation: for d in self.get("schedules"): diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index 92cb85d1b7c..38c8de2beea 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -72,11 +72,28 @@ class AssetCapitalization(StockController): self.update_target_asset() def on_cancel(self): +<<<<<<< HEAD self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Repost Item Valuation") +======= + self.ignore_linked_doctypes = ( + "GL Entry", + "Stock Ledger Entry", + "Repost Item Valuation", + "Serial and Batch Bundle", + "Asset", + ) + self.cancel_target_asset() +>>>>>>> efe9f6656f (fix: Cancel asset capitalisation record on cancellation of asset and vice-versa) self.update_stock_ledger() self.make_gl_entries() self.restore_consumed_asset_items() + def cancel_target_asset(self): + if self.entry_type == "Capitalization" and self.target_asset: + asset_doc = frappe.get_doc("Asset", self.target_asset) + if asset_doc.docstatus == 1: + asset_doc.cancel() + def set_title(self): self.title = self.target_asset_name or self.target_item_name or self.target_item_code From 8014839795d0e28ec53134f0225df97fed635985 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 17 Jan 2024 17:32:14 +0530 Subject: [PATCH 147/675] Revert "fix: WDV as per IT Act: calculate yearly amount first and then split it based on months (backport #39385)" --- .../asset_depreciation_schedule.py | 1020 ----------------- 1 file changed, 1020 deletions(-) delete mode 100644 erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py deleted file mode 100644 index ffb50ebe452..00000000000 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py +++ /dev/null @@ -1,1020 +0,0 @@ -# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -import frappe -from frappe import _ -from frappe.model.document import Document -from frappe.utils import ( - add_days, - add_months, - add_years, - cint, - date_diff, - flt, - get_first_day, - get_last_day, - getdate, - is_last_day_of_the_month, - month_diff, -) - -import erpnext -from erpnext.accounts.utils import get_fiscal_year - - -class AssetDepreciationSchedule(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.assets.doctype.depreciation_schedule.depreciation_schedule import ( - DepreciationSchedule, - ) - - amended_from: DF.Link | None - asset: DF.Link - company: DF.Link | None - daily_prorata_based: DF.Check - depreciation_method: DF.Literal[ - "", "Straight Line", "Double Declining Balance", "Written Down Value", "Manual" - ] - depreciation_schedule: DF.Table[DepreciationSchedule] - expected_value_after_useful_life: DF.Currency - finance_book: DF.Link | None - finance_book_id: DF.Int - frequency_of_depreciation: DF.Int - gross_purchase_amount: DF.Currency - naming_series: DF.Literal["ACC-ADS-.YYYY.-"] - notes: DF.SmallText | None - number_of_depreciations_booked: DF.Int - opening_accumulated_depreciation: DF.Currency - rate_of_depreciation: DF.Percent - shift_based: DF.Check - status: DF.Literal["Draft", "Active", "Cancelled"] - total_number_of_depreciations: DF.Int - # end: auto-generated types - - def before_save(self): - if not self.finance_book_id: - self.prepare_draft_asset_depr_schedule_data_from_asset_name_and_fb_name( - self.asset, self.finance_book - ) - self.update_shift_depr_schedule() - - def validate(self): - self.validate_another_asset_depr_schedule_does_not_exist() - - def validate_another_asset_depr_schedule_does_not_exist(self): - finance_book_filter = ["finance_book", "is", "not set"] - if self.finance_book: - finance_book_filter = ["finance_book", "=", self.finance_book] - - asset_depr_schedule = frappe.db.exists( - "Asset Depreciation Schedule", - [ - ["asset", "=", self.asset], - finance_book_filter, - ["docstatus", "<", 2], - ], - ) - - if asset_depr_schedule and asset_depr_schedule != self.name: - if self.finance_book: - frappe.throw( - _( - "Asset Depreciation Schedule {0} for Asset {1} and Finance Book {2} already exists." - ).format(asset_depr_schedule, self.asset, self.finance_book) - ) - else: - frappe.throw( - _("Asset Depreciation Schedule {0} for Asset {1} already exists.").format( - asset_depr_schedule, self.asset - ) - ) - - def on_submit(self): - self.db_set("status", "Active") - - def before_cancel(self): - if not self.flags.should_not_cancel_depreciation_entries: - self.cancel_depreciation_entries() - - def cancel_depreciation_entries(self): - for d in self.get("depreciation_schedule"): - if d.journal_entry: - frappe.get_doc("Journal Entry", d.journal_entry).cancel() - - def on_cancel(self): - self.db_set("status", "Cancelled") - - def update_shift_depr_schedule(self): - if not self.shift_based or self.docstatus != 0: - return - - asset_doc = frappe.get_doc("Asset", self.asset) - fb_row = asset_doc.finance_books[self.finance_book_id - 1] - - self.make_depr_schedule(asset_doc, fb_row) - self.set_accumulated_depreciation(asset_doc, fb_row) - - def prepare_draft_asset_depr_schedule_data_from_asset_name_and_fb_name(self, asset_name, fb_name): - asset_doc = frappe.get_doc("Asset", asset_name) - - finance_book_filter = ["finance_book", "is", "not set"] - if fb_name: - finance_book_filter = ["finance_book", "=", fb_name] - - asset_finance_book_name = frappe.db.get_value( - doctype="Asset Finance Book", - filters=[["parent", "=", asset_name], finance_book_filter], - ) - asset_finance_book_doc = frappe.get_doc("Asset Finance Book", asset_finance_book_name) - - self.prepare_draft_asset_depr_schedule_data(asset_doc, asset_finance_book_doc) - - def prepare_draft_asset_depr_schedule_data( - self, - asset_doc, - row, - date_of_disposal=None, - date_of_return=None, - update_asset_finance_book_row=True, - ): - have_asset_details_been_modified = self.have_asset_details_been_modified(asset_doc) - not_manual_depr_or_have_manual_depr_details_been_modified = ( - self.not_manual_depr_or_have_manual_depr_details_been_modified(row) - ) - - self.set_draft_asset_depr_schedule_details(asset_doc, row) - - if self.should_prepare_depreciation_schedule( - have_asset_details_been_modified, not_manual_depr_or_have_manual_depr_details_been_modified - ): - self.make_depr_schedule(asset_doc, row, date_of_disposal, update_asset_finance_book_row) - self.set_accumulated_depreciation(asset_doc, row, date_of_disposal, date_of_return) - - def have_asset_details_been_modified(self, asset_doc): - return ( - asset_doc.gross_purchase_amount != self.gross_purchase_amount - or asset_doc.opening_accumulated_depreciation != self.opening_accumulated_depreciation - or asset_doc.number_of_depreciations_booked != self.number_of_depreciations_booked - ) - - def not_manual_depr_or_have_manual_depr_details_been_modified(self, row): - return ( - self.depreciation_method != "Manual" - or row.total_number_of_depreciations != self.total_number_of_depreciations - or row.frequency_of_depreciation != self.frequency_of_depreciation - or getdate(row.depreciation_start_date) != self.get("depreciation_schedule")[0].schedule_date - or row.expected_value_after_useful_life != self.expected_value_after_useful_life - ) - - def should_prepare_depreciation_schedule( - self, have_asset_details_been_modified, not_manual_depr_or_have_manual_depr_details_been_modified - ): - if not self.get("depreciation_schedule"): - return True - - old_asset_depr_schedule_doc = self.get_doc_before_save() - - if self.docstatus != 0 and not old_asset_depr_schedule_doc: - return True - - if have_asset_details_been_modified or not_manual_depr_or_have_manual_depr_details_been_modified: - return True - - return False - - def set_draft_asset_depr_schedule_details(self, asset_doc, row): - self.asset = asset_doc.name - self.finance_book = row.finance_book - self.finance_book_id = row.idx - self.opening_accumulated_depreciation = asset_doc.opening_accumulated_depreciation or 0 - self.number_of_depreciations_booked = asset_doc.number_of_depreciations_booked or 0 - self.gross_purchase_amount = asset_doc.gross_purchase_amount - self.depreciation_method = row.depreciation_method - self.total_number_of_depreciations = row.total_number_of_depreciations - self.frequency_of_depreciation = row.frequency_of_depreciation - self.rate_of_depreciation = row.rate_of_depreciation - self.expected_value_after_useful_life = row.expected_value_after_useful_life - self.daily_prorata_based = row.daily_prorata_based - self.shift_based = row.shift_based - self.status = "Draft" - - def make_depr_schedule( - self, - asset_doc, - row, - date_of_disposal=None, - update_asset_finance_book_row=True, - value_after_depreciation=None, - ): - if not self.get("depreciation_schedule"): - self.depreciation_schedule = [] - - if not asset_doc.available_for_use_date: - return - - start = self.clear_depr_schedule() - - self._make_depr_schedule( - asset_doc, row, start, date_of_disposal, update_asset_finance_book_row, value_after_depreciation - ) - - def clear_depr_schedule(self): - start = 0 - num_of_depreciations_completed = 0 - depr_schedule = [] - - self.schedules_before_clearing = self.get("depreciation_schedule") - - for schedule in self.get("depreciation_schedule"): - if schedule.journal_entry: - num_of_depreciations_completed += 1 - depr_schedule.append(schedule) - else: - start = num_of_depreciations_completed - break - - self.depreciation_schedule = depr_schedule - - return start - - def _make_depr_schedule( - self, - asset_doc, - row, - start, - date_of_disposal, - update_asset_finance_book_row, - value_after_depreciation, - ): - asset_doc.validate_asset_finance_books(row) - - if not value_after_depreciation: - value_after_depreciation = _get_value_after_depreciation_for_making_schedule(asset_doc, row) - row.value_after_depreciation = value_after_depreciation - - if update_asset_finance_book_row: - row.db_update() - - final_number_of_depreciations = cint(row.total_number_of_depreciations) - cint( - self.number_of_depreciations_booked - ) - - has_pro_rata = _check_is_pro_rata(asset_doc, row) - if has_pro_rata: - final_number_of_depreciations += 1 - - has_wdv_or_dd_non_yearly_pro_rata = False - if ( - row.depreciation_method in ("Written Down Value", "Double Declining Balance") - and cint(row.frequency_of_depreciation) != 12 - ): - has_wdv_or_dd_non_yearly_pro_rata = _check_is_pro_rata( - asset_doc, row, wdv_or_dd_non_yearly=True - ) - - skip_row = False - should_get_last_day = is_last_day_of_the_month(row.depreciation_start_date) - - depreciation_amount = 0 - - number_of_pending_depreciations = final_number_of_depreciations - start - yearly_opening_wdv = value_after_depreciation - current_fiscal_year_end_date = None - for n in range(start, final_number_of_depreciations): - # If depreciation is already completed (for double declining balance) - if skip_row: - continue - - schedule_date = add_months(row.depreciation_start_date, n * cint(row.frequency_of_depreciation)) - if not current_fiscal_year_end_date: - current_fiscal_year_end_date = get_fiscal_year(row.depreciation_start_date)[2] - elif getdate(schedule_date) > getdate(current_fiscal_year_end_date): - current_fiscal_year_end_date = add_years(current_fiscal_year_end_date, 1) - yearly_opening_wdv = value_after_depreciation - - if n > 0 and len(self.get("depreciation_schedule")) > n - 1: - prev_depreciation_amount = self.get("depreciation_schedule")[n - 1].depreciation_amount - else: - prev_depreciation_amount = 0 - - depreciation_amount = get_depreciation_amount( - self, - asset_doc, - value_after_depreciation, - yearly_opening_wdv, - row, - n, - prev_depreciation_amount, - has_wdv_or_dd_non_yearly_pro_rata, - number_of_pending_depreciations, - ) - - if not has_pro_rata or ( - n < (cint(final_number_of_depreciations) - 1) or final_number_of_depreciations == 2 - ): - schedule_date = add_months( - row.depreciation_start_date, n * cint(row.frequency_of_depreciation) - ) - - if should_get_last_day: - schedule_date = get_last_day(schedule_date) - - # if asset is being sold or scrapped - if date_of_disposal: - from_date = add_months( - getdate(asset_doc.available_for_use_date), - (asset_doc.number_of_depreciations_booked * row.frequency_of_depreciation), - ) - if self.depreciation_schedule: - from_date = self.depreciation_schedule[-1].schedule_date - - depreciation_amount, days, months = _get_pro_rata_amt( - row, - depreciation_amount, - from_date, - date_of_disposal, - ) - - if depreciation_amount > 0: - self.add_depr_schedule_row(date_of_disposal, depreciation_amount, n) - - break - - # For first row - if ( - n == 0 - and (has_pro_rata or has_wdv_or_dd_non_yearly_pro_rata) - and not self.opening_accumulated_depreciation - and not self.flags.wdv_it_act_applied - ): - from_date = add_days( - asset_doc.available_for_use_date, -1 - ) # needed to calc depr amount for available_for_use_date too - depreciation_amount, days, months = _get_pro_rata_amt( - row, - depreciation_amount, - from_date, - row.depreciation_start_date, - has_wdv_or_dd_non_yearly_pro_rata, - ) - elif n == 0 and has_wdv_or_dd_non_yearly_pro_rata and self.opening_accumulated_depreciation: - if not is_first_day_of_the_month(getdate(asset_doc.available_for_use_date)): - from_date = get_last_day( - add_months( - getdate(asset_doc.available_for_use_date), - ((self.number_of_depreciations_booked - 1) * row.frequency_of_depreciation), - ) - ) - else: - from_date = add_months( - getdate(add_days(asset_doc.available_for_use_date, -1)), - (self.number_of_depreciations_booked * row.frequency_of_depreciation), - ) - depreciation_amount, days, months = _get_pro_rata_amt( - row, - depreciation_amount, - from_date, - row.depreciation_start_date, - has_wdv_or_dd_non_yearly_pro_rata, - ) - - # For last row - elif has_pro_rata and n == cint(final_number_of_depreciations) - 1: - if not asset_doc.flags.increase_in_asset_life: - # In case of increase_in_asset_life, the asset.to_date is already set on asset_repair submission - asset_doc.to_date = add_months( - asset_doc.available_for_use_date, - (n + self.number_of_depreciations_booked) * cint(row.frequency_of_depreciation), - ) - - depreciation_amount_without_pro_rata = depreciation_amount - - depreciation_amount, days, months = _get_pro_rata_amt( - row, - depreciation_amount, - schedule_date, - asset_doc.to_date, - has_wdv_or_dd_non_yearly_pro_rata, - ) - - depreciation_amount = self.get_adjusted_depreciation_amount( - depreciation_amount_without_pro_rata, depreciation_amount - ) - - schedule_date = add_days(schedule_date, days) - - if not depreciation_amount: - continue - value_after_depreciation = flt( - value_after_depreciation - flt(depreciation_amount), - asset_doc.precision("gross_purchase_amount"), - ) - - # Adjust depreciation amount in the last period based on the expected value after useful life - if row.expected_value_after_useful_life and ( - ( - n == cint(final_number_of_depreciations) - 1 - and value_after_depreciation != row.expected_value_after_useful_life - ) - or value_after_depreciation < row.expected_value_after_useful_life - ): - depreciation_amount += value_after_depreciation - row.expected_value_after_useful_life - skip_row = True - - if flt(depreciation_amount, asset_doc.precision("gross_purchase_amount")) > 0: - self.add_depr_schedule_row(schedule_date, depreciation_amount, n) - - # to ensure that final accumulated depreciation amount is accurate - def get_adjusted_depreciation_amount( - self, depreciation_amount_without_pro_rata, depreciation_amount_for_last_row - ): - if not self.opening_accumulated_depreciation: - depreciation_amount_for_first_row = self.get_depreciation_amount_for_first_row() - - if ( - depreciation_amount_for_first_row + depreciation_amount_for_last_row - != depreciation_amount_without_pro_rata - ): - depreciation_amount_for_last_row = ( - depreciation_amount_without_pro_rata - depreciation_amount_for_first_row - ) - - return depreciation_amount_for_last_row - - def get_depreciation_amount_for_first_row(self): - return self.get("depreciation_schedule")[0].depreciation_amount - - def add_depr_schedule_row(self, schedule_date, depreciation_amount, schedule_idx): - if self.shift_based: - shift = ( - self.schedules_before_clearing[schedule_idx].shift - if self.schedules_before_clearing and len(self.schedules_before_clearing) > schedule_idx - else frappe.get_cached_value("Asset Shift Factor", {"default": 1}, "shift_name") - ) - else: - shift = None - - self.append( - "depreciation_schedule", - { - "schedule_date": schedule_date, - "depreciation_amount": depreciation_amount, - "shift": shift, - }, - ) - - def set_accumulated_depreciation( - self, - asset_doc, - row, - date_of_disposal=None, - date_of_return=None, - ignore_booked_entry=False, - ): - straight_line_idx = [ - d.idx - for d in self.get("depreciation_schedule") - if self.depreciation_method == "Straight Line" or self.depreciation_method == "Manual" - ] - - accumulated_depreciation = None - value_after_depreciation = flt(row.value_after_depreciation) - - for i, d in enumerate(self.get("depreciation_schedule")): - if ignore_booked_entry and d.journal_entry: - continue - - if not accumulated_depreciation: - if i > 0 and asset_doc.flags.decrease_in_asset_value_due_to_value_adjustment: - accumulated_depreciation = self.get("depreciation_schedule")[ - i - 1 - ].accumulated_depreciation_amount - else: - accumulated_depreciation = flt(self.opening_accumulated_depreciation) - - depreciation_amount = flt(d.depreciation_amount, d.precision("depreciation_amount")) - value_after_depreciation -= flt(depreciation_amount) - - # for the last row, if depreciation method = Straight Line - if ( - straight_line_idx - and i == max(straight_line_idx) - 1 - and not date_of_disposal - and not date_of_return - and not row.shift_based - ): - depreciation_amount += flt( - value_after_depreciation - flt(row.expected_value_after_useful_life), - d.precision("depreciation_amount"), - ) - - d.depreciation_amount = depreciation_amount - accumulated_depreciation += d.depreciation_amount - d.accumulated_depreciation_amount = flt( - accumulated_depreciation, d.precision("accumulated_depreciation_amount") - ) - - -def _get_value_after_depreciation_for_making_schedule(asset_doc, fb_row): - if asset_doc.docstatus == 1 and fb_row.value_after_depreciation: - value_after_depreciation = flt(fb_row.value_after_depreciation) - else: - value_after_depreciation = flt(asset_doc.gross_purchase_amount) - flt( - asset_doc.opening_accumulated_depreciation - ) - - return value_after_depreciation - - -# if it returns True, depreciation_amount will not be equal for the first and last rows -def _check_is_pro_rata(asset_doc, row, wdv_or_dd_non_yearly=False): - has_pro_rata = False - - # if not existing asset, from_date = available_for_use_date - # otherwise, if number_of_depreciations_booked = 2, available_for_use_date = 01/01/2020 and frequency_of_depreciation = 12 - # from_date = 01/01/2022 - from_date = _get_modified_available_for_use_date(asset_doc, row, wdv_or_dd_non_yearly) - days = date_diff(row.depreciation_start_date, from_date) + 1 - - if wdv_or_dd_non_yearly: - total_days = get_total_days(row.depreciation_start_date, 12) - else: - # if frequency_of_depreciation is 12 months, total_days = 365 - total_days = get_total_days(row.depreciation_start_date, row.frequency_of_depreciation) - - if days < total_days: - has_pro_rata = True - - return has_pro_rata - - -def _get_modified_available_for_use_date(asset_doc, row, wdv_or_dd_non_yearly=False): - if wdv_or_dd_non_yearly: - return add_months( - asset_doc.available_for_use_date, - (asset_doc.number_of_depreciations_booked * 12), - ) - else: - return add_months( - asset_doc.available_for_use_date, - (asset_doc.number_of_depreciations_booked * row.frequency_of_depreciation), - ) - - -def _get_pro_rata_amt( - row, depreciation_amount, from_date, to_date, has_wdv_or_dd_non_yearly_pro_rata=False -): - days = date_diff(to_date, from_date) - months = month_diff(to_date, from_date) - if has_wdv_or_dd_non_yearly_pro_rata: - total_days = get_total_days(to_date, 12) - else: - total_days = get_total_days(to_date, row.frequency_of_depreciation) - - return (depreciation_amount * flt(days)) / flt(total_days), days, months - - -def get_total_days(date, frequency): - period_start_date = add_months(date, cint(frequency) * -1) - - if is_last_day_of_the_month(date): - period_start_date = get_last_day(period_start_date) - - return date_diff(date, period_start_date) - - -def get_depreciation_amount( - asset_depr_schedule, - asset, - depreciable_value, - yearly_opening_wdv, - fb_row, - schedule_idx=0, - prev_depreciation_amount=0, - has_wdv_or_dd_non_yearly_pro_rata=False, - number_of_pending_depreciations=0, -): - if fb_row.depreciation_method in ("Straight Line", "Manual"): - return get_straight_line_or_manual_depr_amount( - asset_depr_schedule, asset, fb_row, schedule_idx, number_of_pending_depreciations - ) - else: - return get_wdv_or_dd_depr_amount( - asset, - fb_row, - depreciable_value, - yearly_opening_wdv, - schedule_idx, - prev_depreciation_amount, - has_wdv_or_dd_non_yearly_pro_rata, - asset_depr_schedule, - ) - - -def get_straight_line_or_manual_depr_amount( - asset_depr_schedule, asset, row, schedule_idx, number_of_pending_depreciations -): - if row.shift_based: - return get_shift_depr_amount(asset_depr_schedule, asset, row, schedule_idx) - - # if the Depreciation Schedule is being modified after Asset Repair due to increase in asset life and value - if asset.flags.increase_in_asset_life: - return (flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life)) / ( - date_diff(asset.to_date, asset.available_for_use_date) / 365 - ) - # if the Depreciation Schedule is being modified after Asset Repair due to increase in asset value - elif asset.flags.increase_in_asset_value_due_to_repair: - return (flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life)) / flt( - row.total_number_of_depreciations - ) - # if the Depreciation Schedule is being modified after Asset Value Adjustment due to decrease in asset value - elif asset.flags.decrease_in_asset_value_due_to_value_adjustment: - if row.daily_prorata_based: - daily_depr_amount = ( - flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life) - ) / date_diff( - get_last_day( - add_months( - row.depreciation_start_date, - flt(row.total_number_of_depreciations - asset.number_of_depreciations_booked - 1) - * row.frequency_of_depreciation, - ) - ), - add_days( - get_last_day( - add_months( - row.depreciation_start_date, - flt( - row.total_number_of_depreciations - - asset.number_of_depreciations_booked - - number_of_pending_depreciations - - 1 - ) - * row.frequency_of_depreciation, - ) - ), - 1, - ), - ) - - to_date = get_last_day( - add_months(row.depreciation_start_date, schedule_idx * row.frequency_of_depreciation) - ) - from_date = add_days( - get_last_day( - add_months(row.depreciation_start_date, (schedule_idx - 1) * row.frequency_of_depreciation) - ), - 1, - ) - - return daily_depr_amount * (date_diff(to_date, from_date) + 1) - else: - return ( - flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life) - ) / number_of_pending_depreciations - # if the Depreciation Schedule is being prepared for the first time - else: - if row.daily_prorata_based: - daily_depr_amount = ( - flt(asset.gross_purchase_amount) - - flt(asset.opening_accumulated_depreciation) - - flt(row.expected_value_after_useful_life) - ) / date_diff( - get_last_day( - add_months( - row.depreciation_start_date, - flt(row.total_number_of_depreciations - asset.number_of_depreciations_booked - 1) - * row.frequency_of_depreciation, - ) - ), - add_days( - get_last_day(add_months(row.depreciation_start_date, -1 * row.frequency_of_depreciation)), 1 - ), - ) - - to_date = get_last_day( - add_months(row.depreciation_start_date, schedule_idx * row.frequency_of_depreciation) - ) - from_date = add_days( - get_last_day( - add_months(row.depreciation_start_date, (schedule_idx - 1) * row.frequency_of_depreciation) - ), - 1, - ) - - return daily_depr_amount * (date_diff(to_date, from_date) + 1) - else: - return ( - flt(asset.gross_purchase_amount) - - flt(asset.opening_accumulated_depreciation) - - flt(row.expected_value_after_useful_life) - ) / flt(row.total_number_of_depreciations - asset.number_of_depreciations_booked) - - -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 ( - flt(asset.gross_purchase_amount) - - flt(asset.opening_accumulated_depreciation) - - flt(row.expected_value_after_useful_life) - ) / flt(row.total_number_of_depreciations - asset.number_of_depreciations_booked) - - asset_shift_factors_map = get_asset_shift_factors_map() - shift = ( - asset_depr_schedule.schedules_before_clearing[schedule_idx].shift - if len(asset_depr_schedule.schedules_before_clearing) > schedule_idx - else None - ) - shift_factor = asset_shift_factors_map.get(shift) if shift else 0 - - shift_factors_sum = sum( - flt(asset_shift_factors_map.get(schedule.shift)) - for schedule in asset_depr_schedule.schedules_before_clearing - ) - - return ( - ( - flt(asset.gross_purchase_amount) - - flt(asset.opening_accumulated_depreciation) - - flt(row.expected_value_after_useful_life) - ) - / flt(shift_factors_sum) - ) * shift_factor - - -def get_asset_shift_factors_map(): - return dict(frappe.db.get_all("Asset Shift Factor", ["shift_name", "shift_factor"], as_list=True)) - - -@erpnext.allow_regional -def get_wdv_or_dd_depr_amount( - asset, - fb_row, - depreciable_value, - yearly_opening_wdv, - schedule_idx, - prev_depreciation_amount, - has_wdv_or_dd_non_yearly_pro_rata, - asset_depr_schedule, -): - return ( - get_default_wdv_or_dd_depr_amount( - asset, - fb_row, - depreciable_value, - schedule_idx, - prev_depreciation_amount, - has_wdv_or_dd_non_yearly_pro_rata, - asset_depr_schedule, - ), - None, - ) - - -def get_default_wdv_or_dd_depr_amount( - asset, - fb_row, - depreciable_value, - schedule_idx, - prev_depreciation_amount, - has_wdv_or_dd_non_yearly_pro_rata, - asset_depr_schedule, -): - if cint(fb_row.frequency_of_depreciation) == 12: - return flt(depreciable_value) * (flt(fb_row.rate_of_depreciation) / 100) - else: - if has_wdv_or_dd_non_yearly_pro_rata: - if schedule_idx == 0: - return flt(depreciable_value) * (flt(fb_row.rate_of_depreciation) / 100) - elif schedule_idx % (12 / cint(fb_row.frequency_of_depreciation)) == 1: - return ( - flt(depreciable_value) - * flt(fb_row.frequency_of_depreciation) - * (flt(fb_row.rate_of_depreciation) / 1200) - ) - else: - return prev_depreciation_amount - else: - if schedule_idx % (12 / cint(fb_row.frequency_of_depreciation)) == 0: - return ( - flt(depreciable_value) - * flt(fb_row.frequency_of_depreciation) - * (flt(fb_row.rate_of_depreciation) / 1200) - ) - else: - return prev_depreciation_amount - - -def make_draft_asset_depr_schedules_if_not_present(asset_doc): - asset_depr_schedules_names = [] - - for row in asset_doc.get("finance_books"): - draft_asset_depr_schedule_name = get_asset_depr_schedule_name( - asset_doc.name, "Draft", row.finance_book - ) - - active_asset_depr_schedule_name = get_asset_depr_schedule_name( - asset_doc.name, "Active", row.finance_book - ) - - if not draft_asset_depr_schedule_name and not active_asset_depr_schedule_name: - name = make_draft_asset_depr_schedule(asset_doc, row) - asset_depr_schedules_names.append(name) - - return asset_depr_schedules_names - - -def make_draft_asset_depr_schedules(asset_doc): - asset_depr_schedules_names = [] - - for row in asset_doc.get("finance_books"): - name = make_draft_asset_depr_schedule(asset_doc, row) - asset_depr_schedules_names.append(name) - - return asset_depr_schedules_names - - -def make_draft_asset_depr_schedule(asset_doc, row): - asset_depr_schedule_doc = frappe.new_doc("Asset Depreciation Schedule") - - asset_depr_schedule_doc.prepare_draft_asset_depr_schedule_data(asset_doc, row) - - asset_depr_schedule_doc.insert() - - return asset_depr_schedule_doc.name - - -def update_draft_asset_depr_schedules(asset_doc): - for row in asset_doc.get("finance_books"): - asset_depr_schedule_doc = get_asset_depr_schedule_doc(asset_doc.name, "Draft", row.finance_book) - - if not asset_depr_schedule_doc: - continue - - asset_depr_schedule_doc.prepare_draft_asset_depr_schedule_data(asset_doc, row) - - asset_depr_schedule_doc.save() - - -def convert_draft_asset_depr_schedules_into_active(asset_doc): - for row in asset_doc.get("finance_books"): - asset_depr_schedule_doc = get_asset_depr_schedule_doc(asset_doc.name, "Draft", row.finance_book) - - if not asset_depr_schedule_doc: - continue - - asset_depr_schedule_doc.submit() - - -def cancel_asset_depr_schedules(asset_doc): - for row in asset_doc.get("finance_books"): - asset_depr_schedule_doc = get_asset_depr_schedule_doc(asset_doc.name, "Active", row.finance_book) - - if not asset_depr_schedule_doc: - continue - - asset_depr_schedule_doc.cancel() - - -def make_new_active_asset_depr_schedules_and_cancel_current_ones( - asset_doc, - notes, - date_of_disposal=None, - date_of_return=None, - value_after_depreciation=None, - ignore_booked_entry=False, -): - for row in asset_doc.get("finance_books"): - current_asset_depr_schedule_doc = get_asset_depr_schedule_doc( - asset_doc.name, "Active", row.finance_book - ) - - if not current_asset_depr_schedule_doc: - frappe.throw( - _("Asset Depreciation Schedule not found for Asset {0} and Finance Book {1}").format( - asset_doc.name, row.finance_book - ) - ) - - new_asset_depr_schedule_doc = frappe.copy_doc(current_asset_depr_schedule_doc) - - if asset_doc.flags.increase_in_asset_value_due_to_repair and row.depreciation_method in ( - "Written Down Value", - "Double Declining Balance", - ): - new_rate_of_depreciation = flt( - asset_doc.get_depreciation_rate(row), row.precision("rate_of_depreciation") - ) - row.rate_of_depreciation = new_rate_of_depreciation - new_asset_depr_schedule_doc.rate_of_depreciation = new_rate_of_depreciation - - new_asset_depr_schedule_doc.make_depr_schedule( - asset_doc, row, date_of_disposal, value_after_depreciation=value_after_depreciation - ) - new_asset_depr_schedule_doc.set_accumulated_depreciation( - asset_doc, row, date_of_disposal, date_of_return, ignore_booked_entry - ) - - new_asset_depr_schedule_doc.notes = notes - - current_asset_depr_schedule_doc.flags.should_not_cancel_depreciation_entries = True - current_asset_depr_schedule_doc.cancel() - - new_asset_depr_schedule_doc.submit() - - -def get_temp_asset_depr_schedule_doc( - asset_doc, - row, - date_of_disposal=None, - date_of_return=None, - update_asset_finance_book_row=False, - new_depr_schedule=None, -): - current_asset_depr_schedule_doc = get_asset_depr_schedule_doc( - asset_doc.name, "Active", row.finance_book - ) - - if not current_asset_depr_schedule_doc: - frappe.throw( - _("Asset Depreciation Schedule not found for Asset {0} and Finance Book {1}").format( - asset_doc.name, row.finance_book - ) - ) - - temp_asset_depr_schedule_doc = frappe.copy_doc(current_asset_depr_schedule_doc) - - if new_depr_schedule: - temp_asset_depr_schedule_doc.depreciation_schedule = [] - - for schedule in new_depr_schedule: - temp_asset_depr_schedule_doc.append( - "depreciation_schedule", - { - "schedule_date": schedule.schedule_date, - "depreciation_amount": schedule.depreciation_amount, - "accumulated_depreciation_amount": schedule.accumulated_depreciation_amount, - "journal_entry": schedule.journal_entry, - "shift": schedule.shift, - }, - ) - - temp_asset_depr_schedule_doc.prepare_draft_asset_depr_schedule_data( - asset_doc, - row, - date_of_disposal, - date_of_return, - update_asset_finance_book_row, - ) - - return temp_asset_depr_schedule_doc - - -@frappe.whitelist() -def get_depr_schedule(asset_name, status, finance_book=None): - asset_depr_schedule_doc = get_asset_depr_schedule_doc(asset_name, status, finance_book) - - if not asset_depr_schedule_doc: - return - - return asset_depr_schedule_doc.get("depreciation_schedule") - - -@frappe.whitelist() -def get_asset_depr_schedule_doc(asset_name, status, finance_book=None): - asset_depr_schedule_name = get_asset_depr_schedule_name(asset_name, status, finance_book) - - if not asset_depr_schedule_name: - return - - asset_depr_schedule_doc = frappe.get_doc("Asset Depreciation Schedule", asset_depr_schedule_name) - - return asset_depr_schedule_doc - - -def get_asset_depr_schedule_name(asset_name, status, finance_book=None): - finance_book_filter = ["finance_book", "is", "not set"] - if finance_book: - finance_book_filter = ["finance_book", "=", finance_book] - - return frappe.db.get_value( - doctype="Asset Depreciation Schedule", - filters=[ - ["asset", "=", asset_name], - finance_book_filter, - ["status", "=", status], - ], - ) - - -def is_first_day_of_the_month(date): - first_day_of_the_month = get_first_day(date) - - return getdate(first_day_of_the_month) == getdate(date) From 4edb73d398d921d76e860cb072b00af20c272cca Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 17 Jan 2024 17:36:42 +0530 Subject: [PATCH 148/675] fix: resolved merge conflict --- .../doctype/asset_capitalization/asset_capitalization.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index 38c8de2beea..d0e77f8868a 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -72,18 +72,13 @@ class AssetCapitalization(StockController): self.update_target_asset() def on_cancel(self): -<<<<<<< HEAD - self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Repost Item Valuation") -======= self.ignore_linked_doctypes = ( "GL Entry", "Stock Ledger Entry", "Repost Item Valuation", - "Serial and Batch Bundle", "Asset", ) self.cancel_target_asset() ->>>>>>> efe9f6656f (fix: Cancel asset capitalisation record on cancellation of asset and vice-versa) self.update_stock_ledger() self.make_gl_entries() self.restore_consumed_asset_items() From ac6020a940335747e25fd10f1ba0dcac7ad0e3b5 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 17 Jan 2024 16:17:53 +0530 Subject: [PATCH 149/675] fix: composite asset capitalization using asset components (cherry picked from commit 5df40661d2d857eeb987e51ea6f4b465aa3d502e) --- erpnext/assets/doctype/asset/asset.py | 2 ++ .../asset_capitalization.js | 27 ++++++++++++------- .../asset_capitalization.py | 22 ++++++++++++--- 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index dc1ab378eee..e5e022802d2 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -1362,6 +1362,8 @@ def is_cwip_accounting_enabled(asset_category): @frappe.whitelist() def get_asset_value_after_depreciation(asset_name, finance_book=None): asset = frappe.get_doc("Asset", asset_name) + if not asset.calculate_depreciation: + return flt(asset.value_after_depreciation) return asset.get_value_after_depreciation(finance_book) diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js index 304bdf26dee..67f8421fbde 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js @@ -20,10 +20,10 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s this.show_stock_ledger(); } - if (this.frm.doc.stock_items && !this.frm.doc.stock_items.length && this.frm.doc.target_asset && this.frm.doc.capitalization_method === "Choose a WIP composite asset") { - this.set_consumed_stock_items_tagged_to_wip_composite_asset(this.frm.doc.target_asset); - this.get_target_asset_details(); - } + // if (this.frm.doc.stock_items && !this.frm.doc.stock_items.length && this.frm.doc.target_asset && this.frm.doc.capitalization_method === "Choose a WIP composite asset") { + // this.set_consumed_stock_items_tagged_to_wip_composite_asset(this.frm.doc.target_asset); + // this.get_target_asset_details(); + // } } setup_queries() { @@ -119,13 +119,20 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s }, callback: function (r) { if (!r.exc && r.message) { - me.frm.clear_table("stock_items"); - - for (let item of r.message) { - me.frm.add_child("stock_items", item); + if(r.message[0] && r.message[0].length) { + me.frm.clear_table("stock_items"); + for (let item of r.message[0]) { + me.frm.add_child("stock_items", item); + } + refresh_field("stock_items"); + } + if (r.message[1] && r.message[1].length) { + me.frm.clear_table("asset_items"); + for (let item of r.message[1]) { + me.frm.add_child("asset_items", item); + } + me.frm.refresh_field("asset_items"); } - - refresh_field("stock_items"); me.calculate_totals(); } diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index d0e77f8868a..d1bf15eb459 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -794,7 +794,6 @@ def get_consumed_asset_details(args): out.cost_center = get_default_cost_center( args, item_defaults, item_group_defaults, brand_defaults ) - return out @@ -842,10 +841,27 @@ def get_items_tagged_to_wip_composite_asset(asset): "qty", "valuation_rate", "amount", + "is_fixed_asset", + "parent", ] pr_items = frappe.get_all( - "Purchase Receipt Item", filters={"wip_composite_asset": asset}, fields=fields + "Purchase Receipt Item", filters={"wip_composite_asset": asset, "docstatus": 1}, fields=fields ) - return pr_items + stock_items = [] + asset_items = [] + for d in pr_items: + if not d.is_fixed_asset: + stock_items.append(frappe._dict(d)) + else: + asset_details = frappe.db.get_value( + "Asset", + {"item_code": d.item_code, "purchase_receipt": d.parent}, + ["name as asset", "asset_name"], + as_dict=1, + ) + d.update(asset_details) + asset_items.append(frappe._dict(d)) + + return stock_items, asset_items From 9aa1e7444e018df71b7ae3a3d9af850819ee42bb Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 17 Jan 2024 17:05:44 +0000 Subject: [PATCH 150/675] chore(release): Bumped to Version 14.61.0 # [14.61.0](https://github.com/frappe/erpnext/compare/v14.60.1...v14.61.0) (2024-01-17) ### Bug Fixes * added indexing to improve performance ([d9f7070](https://github.com/frappe/erpnext/commit/d9f7070f92216c2422f4e1ad262e9397fb940969)) * added item group in stock reco ([c69a59c](https://github.com/frappe/erpnext/commit/c69a59c3c6c72ba69a8347b2c2940e2aaa49428e)) * broken dimension filters in Sales/Purchase register ([298cdf5](https://github.com/frappe/erpnext/commit/298cdf5f0efe4e0a0e76157ad0719bee10822c4a)) * Cancel asset capitalisation record on cancellation of asset and vice-versa ([1dff960](https://github.com/frappe/erpnext/commit/1dff96057c72991bfc579184d09edc58739fe01a)) * circular dependency error while deleting QC ([dfcb746](https://github.com/frappe/erpnext/commit/dfcb7467741184da20d0f32d62433732a1acbec4)) * composite asset capitalization using asset components ([ac6020a](https://github.com/frappe/erpnext/commit/ac6020a940335747e25fd10f1ba0dcac7ad0e3b5)) * consider all years in holiday list ([3180266](https://github.com/frappe/erpnext/commit/318026615036dfdc6bb169788e25ae0f9a4520e1)) * date in master document for dictionary condition ([670d615](https://github.com/frappe/erpnext/commit/670d61547fb6a7ee86050c2f1020cd247f12df52)) * empty category in Plaid ([1acaa20](https://github.com/frappe/erpnext/commit/1acaa20ee11056c9f79cab333474bf13dfb56bf6)) * ignore cancelled payments in Sales/Purchase Register ([36b8e97](https://github.com/frappe/erpnext/commit/36b8e972f11cb860c06ca3ca1726103be3217bb4)) * incorrect active serial nos due to backdated transactions ([1a26c70](https://github.com/frappe/erpnext/commit/1a26c70df285176dc282426bc715b83c470075e5)) * incorrect percentage received in purchase invoice ([453700d](https://github.com/frappe/erpnext/commit/453700d0abb59ca67234617d35301daa0640a2ab)) * incorrect sql error if account name has '%' ([d0e3458](https://github.com/frappe/erpnext/commit/d0e3458c8c696fcf5be2b2cfbea47dfb1a260193)) * modified date ([e9d2437](https://github.com/frappe/erpnext/commit/e9d2437c7ac0a547191658d4694370af863cd450)) * modified date ([28434d1](https://github.com/frappe/erpnext/commit/28434d101b381b03475f61b222043df17a2aea51)) * modified date was not set ([724c934](https://github.com/frappe/erpnext/commit/724c934fbbf246c181f40535f037827e39e60c74)) * modified date was not updated ([cb67574](https://github.com/frappe/erpnext/commit/cb6757437ed72377ece6c0ded858019ce2b087be)) * pass accounts as list to query ([a6bc5ca](https://github.com/frappe/erpnext/commit/a6bc5cae90465d6d517c59c61769623724f8eacd)) * performance issue related to stock entry (backport [#39301](https://github.com/frappe/erpnext/issues/39301)) ([#39302](https://github.com/frappe/erpnext/issues/39302)) ([dc7c9e7](https://github.com/frappe/erpnext/commit/dc7c9e7affa89dab7daf94f68a7205e07b38763f)) * possible typeerror in consolidated report ([9395f75](https://github.com/frappe/erpnext/commit/9395f7535b32b02806e7c36d4beb6b44a246a134)) * possible typerror in utils.js ([4ea72f4](https://github.com/frappe/erpnext/commit/4ea72f4b692a846bbd27f75054c229c3f7c167dd)) * project filters on Delivery Note and Sales Order ([520cdb6](https://github.com/frappe/erpnext/commit/520cdb6f32f4477aa9fd0456c6b7c6328c5ccfe1)) * project query controller logic ([f2e577b](https://github.com/frappe/erpnext/commit/f2e577bec749fcbba74c224e2869433f24d7a4bb)) * reset default after test ([6bd01f2](https://github.com/frappe/erpnext/commit/6bd01f227eea82f434efa1e3bdb4f9ab2a3e0f26)) * resolved merge conflict ([4edb73d](https://github.com/frappe/erpnext/commit/4edb73d398d921d76e860cb072b00af20c272cca)) * show bill_date and bill_no in Purchase Register ([4b19792](https://github.com/frappe/erpnext/commit/4b197920c1e3e816f76f153fc8d755820a63eaf6)) * **test:** test case for project query ([98967ed](https://github.com/frappe/erpnext/commit/98967ed58487cc63e0ed345c50b6f0fb1be97eea)) * unreconcile Bank Transaction on cancel of payment voucher ([755576b](https://github.com/frappe/erpnext/commit/755576bd78be12ffc0c6584cd659cc7279624fb0)) * use child table values instead of global min max ([d21fc60](https://github.com/frappe/erpnext/commit/d21fc6055cc25a1d4af6c290c2584e3de30fffc5)) * WDV as per IT Act: calculate yearly amount first and then split it based on months ([3989b97](https://github.com/frappe/erpnext/commit/3989b9757968e4cc5f9676dab5e034ea08b0191e)) * wrong file name ([9d256e1](https://github.com/frappe/erpnext/commit/9d256e131df0a3e73993ef6b8faffc7b883ca89f)) ### Features * provision to select the qty field for Product Page ([#39292](https://github.com/frappe/erpnext/issues/39292)) ([d42db11](https://github.com/frappe/erpnext/commit/d42db1174d3f5ffd2f0517e2a39ae10ad05e4eec)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 0cfb47a1284..e640a99fdf8 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.60.1" +__version__ = "14.61.0" def get_default_company(user=None): From f7ba7361ca6ebfc0876334e3ecdd54da564cf352 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Thu, 18 Jan 2024 14:33:10 +0530 Subject: [PATCH 151/675] fix: set unallocated amount after base tax (cherry picked from commit e9bc63aacf20dd83cb7e15494bb8f2f025491991) --- 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 6b3f46d3833..6d9d6920105 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -919,7 +919,7 @@ frappe.ui.form.on('Payment Entry', { 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) + 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 From 6895b74ecc952f059e4eb05e67863bafd7139ce0 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Thu, 18 Jan 2024 15:05:19 +0530 Subject: [PATCH 152/675] fix: linting issue (cherry picked from commit 99b94af49ffd34bccb76b73c2f06187540444a44) --- .../gross_and_net_profit_report/gross_and_net_profit_report.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py index f0ca405401d..5ccd4f0f16f 100644 --- a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py +++ b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py @@ -134,7 +134,7 @@ def get_revenue(data, period_list, include_in_gross=1): def remove_parent_with_no_child(data): data_to_be_removed = False - for parent in data: + for parent in list(data): if "is_group" in parent and parent.get("is_group") == 1: have_child = False for child in data: From b840eb90ebf558d91471c8bd1c629bc15214b94c Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 18 Jan 2024 19:06:41 +0530 Subject: [PATCH 153/675] fix: Asset Depreciation WDV as per Income Tax Act --- erpnext/assets/doctype/asset/asset.py | 21 +++++++++++++++++-- .../create_custom_field_for_finance_book.py | 2 +- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index e5e022802d2..b90bf19b429 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -10,6 +10,7 @@ from frappe import _ from frappe.utils import ( add_days, add_months, + add_years, cint, date_diff, flt, @@ -23,6 +24,7 @@ from frappe.utils import ( import erpnext from erpnext.accounts.general_ledger import make_reverse_gl_entries +from erpnext.accounts.utils import get_fiscal_year from erpnext.assets.doctype.asset.depreciation import ( get_depreciation_accounts, get_disposal_account_and_cost_center, @@ -381,14 +383,22 @@ class Asset(AccountsController): should_get_last_day = is_last_day_of_the_month(finance_book.depreciation_start_date) depreciation_amount = 0 - number_of_pending_depreciations = final_number_of_depreciations - start[finance_book.idx - 1] + yearly_opening_wdv = value_after_depreciation + current_fiscal_year_end_date = None for n in range(start[finance_book.idx - 1], final_number_of_depreciations): # If depreciation is already completed (for double declining balance) if skip_row: continue + schedule_date = add_months(finance_book.depreciation_start_date, n * cint(finance_book.frequency_of_depreciation)) + if not current_fiscal_year_end_date: + current_fiscal_year_end_date = get_fiscal_year(finance_book.depreciation_start_date)[2] + elif getdate(schedule_date) > getdate(current_fiscal_year_end_date): + current_fiscal_year_end_date = add_years(current_fiscal_year_end_date, 1) + yearly_opening_wdv = value_after_depreciation + if n > 0 and len(self.get("schedules")) > n - 1: prev_depreciation_amount = self.get("schedules")[n - 1].depreciation_amount else: @@ -397,6 +407,7 @@ class Asset(AccountsController): depreciation_amount = get_depreciation_amount( self, value_after_depreciation, + yearly_opening_wdv, finance_book, n, prev_depreciation_amount, @@ -494,7 +505,10 @@ class Asset(AccountsController): if not depreciation_amount: continue - value_after_depreciation -= flt(depreciation_amount, self.precision("gross_purchase_amount")) + value_after_depreciation = flt( + value_after_depreciation - flt(depreciation_amount), + self.precision("gross_purchase_amount"), + ) # Adjust depreciation amount in the last period based on the expected value after useful life if finance_book.expected_value_after_useful_life and ( @@ -1380,6 +1394,7 @@ def get_total_days(date, frequency): def get_depreciation_amount( asset, depreciable_value, + yearly_opening_wdv, fb_row, schedule_idx=0, prev_depreciation_amount=0, @@ -1397,6 +1412,7 @@ def get_depreciation_amount( asset, fb_row, depreciable_value, + yearly_opening_wdv, schedule_idx, prev_depreciation_amount, has_wdv_or_dd_non_yearly_pro_rata, @@ -1542,6 +1558,7 @@ def get_wdv_or_dd_depr_amount( asset, fb_row, depreciable_value, + yearly_opening_wdv, schedule_idx, prev_depreciation_amount, has_wdv_or_dd_non_yearly_pro_rata, diff --git a/erpnext/patches/v13_0/create_custom_field_for_finance_book.py b/erpnext/patches/v13_0/create_custom_field_for_finance_book.py index 2b8666d21b6..e0292098e7a 100644 --- a/erpnext/patches/v13_0/create_custom_field_for_finance_book.py +++ b/erpnext/patches/v13_0/create_custom_field_for_finance_book.py @@ -14,7 +14,7 @@ def execute(): "label": "For Income Tax", "fieldtype": "Check", "insert_after": "finance_book_name", - "description": "If the asset is put to use for less than 180 days, the first Depreciation Rate will be reduced by 50%.", + "description": "If the asset is put to use for less than 180 days in the first year, the first year's depreciation rate will be reduced by 50%.", } ] } From f604798a45fce39cd5e41c6a0aca8d7240848cdb Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 19 Jan 2024 11:29:26 +0530 Subject: [PATCH 154/675] fix: asset module test cases --- erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py | 2 +- erpnext/assets/doctype/asset/test_asset.py | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py b/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py index 6e946f74660..82147f4f6ab 100644 --- a/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py +++ b/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py @@ -41,7 +41,7 @@ def test_record_generator(): ] start = 2012 - end = now_datetime().year + 5 + end = now_datetime().year + 25 for year in range(start, end): test_records.append( { diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 21afd5df851..708172b2980 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -845,7 +845,7 @@ class TestDepreciationMethods(AssetSetup): ["2030-12-31", 28630.14, 28630.14], ["2031-12-31", 35684.93, 64315.07], ["2032-12-31", 17842.47, 82157.54], - ["2033-06-06", 5342.46, 87500.0], + ["2033-06-06", 5342.47, 87500.01], ] schedules = [ @@ -957,7 +957,7 @@ class TestDepreciationBasics(AssetSetup): }, ) - depreciation_amount = get_depreciation_amount(asset, 100000, asset.finance_books[0]) + depreciation_amount = get_depreciation_amount(asset, 100000, 100000, asset.finance_books[0]) self.assertEqual(depreciation_amount, 30000) def test_make_depreciation_schedule(self): From 837fff4533cb6bdf49b2debded62f98aafa6ad2c Mon Sep 17 00:00:00 2001 From: Gursheen Kaur Anand <40693548+GursheenK@users.noreply.github.com> Date: Wed, 17 Jan 2024 12:25:03 +0530 Subject: [PATCH 155/675] Revert "fix(minor): financial statements period end date" (cherry picked from commit 73625a2622f29a83c98179c0b5048a29e61ce243) --- erpnext/accounts/report/financial_statements.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 7355c4b8a16..096bb107069 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -8,17 +8,7 @@ import re import frappe from frappe import _ -from frappe.utils import ( - add_days, - add_months, - cint, - cstr, - flt, - formatdate, - get_first_day, - getdate, - today, -) +from frappe.utils import add_days, add_months, cint, cstr, flt, formatdate, get_first_day, getdate from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( get_accounting_dimensions, @@ -53,8 +43,6 @@ def get_period_list( year_start_date = getdate(period_start_date) year_end_date = getdate(period_end_date) - year_end_date = getdate(today()) if year_end_date > getdate(today()) else year_end_date - months_to_add = {"Yearly": 12, "Half-Yearly": 6, "Quarterly": 3, "Monthly": 1}[periodicity] period_list = [] From 3bdff18467a575e4378fd625eebf6a2a666031cb Mon Sep 17 00:00:00 2001 From: David Arnold Date: Mon, 8 Jan 2024 22:07:46 +0100 Subject: [PATCH 156/675] fix: use most reliable section reference per report line (cherry picked from commit b5be17c6dfc9ab10e9eb57669f697fbb5576489c) --- .../tds_payable_monthly.py | 106 ++++++++++-------- 1 file changed, 60 insertions(+), 46 deletions(-) diff --git a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py index e4953bb1815..b96bfbeb817 100644 --- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py +++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py @@ -46,12 +46,10 @@ def get_result( out = [] for name, details in gle_map.items(): - tax_amount, total_amount, grand_total, base_total = 0, 0, 0, 0 - bill_no, bill_date = "", "" - tax_withholding_category = tax_category_map.get(name) - rate = tax_rate_map.get(tax_withholding_category) - for entry in details: + tax_amount, total_amount, grand_total, base_total = 0, 0, 0, 0 + tax_withholding_category, rate = None, None + bill_no, bill_date = "", "" party = entry.party or entry.against posting_date = entry.posting_date voucher_type = entry.voucher_type @@ -61,12 +59,19 @@ def get_result( if party_list: party = party_list[0] - if not tax_withholding_category: - tax_withholding_category = party_map.get(party, {}).get("tax_withholding_category") - rate = tax_rate_map.get(tax_withholding_category) - - if entry.account in tds_accounts: + if entry.account in tds_accounts.keys(): tax_amount += entry.credit - entry.debit + # infer tax withholding category from the account if it's the single account for this category + tax_withholding_category = tds_accounts.get(entry.account) + rate = tax_rate_map.get(tax_withholding_category) + # or else the consolidated value from the voucher document + if not tax_withholding_category: + # or else from the party default + tax_withholding_category = tax_category_map.get(name) + rate = tax_rate_map.get(tax_withholding_category) + if not tax_withholding_category: + tax_withholding_category = party_map.get(party, {}).get("tax_withholding_category") + rate = tax_rate_map.get(tax_withholding_category) if net_total_map.get(name): if voucher_type == "Journal Entry" and tax_amount and rate: @@ -80,41 +85,41 @@ def get_result( else: total_amount += entry.credit - if tax_amount: - if party_map.get(party, {}).get("party_type") == "Supplier": - party_name = "supplier_name" - party_type = "supplier_type" - else: - party_name = "customer_name" - party_type = "customer_type" + if tax_amount: + if party_map.get(party, {}).get("party_type") == "Supplier": + party_name = "supplier_name" + party_type = "supplier_type" + else: + party_name = "customer_name" + party_type = "customer_type" - row = { - "pan" - if frappe.db.has_column(filters.party_type, "pan") - else "tax_id": party_map.get(party, {}).get("pan"), - "party": party_map.get(party, {}).get("name"), - } - - if filters.naming_series == "Naming Series": - row.update({"party_name": party_map.get(party, {}).get(party_name)}) - - row.update( - { - "section_code": tax_withholding_category or "", - "entity_type": party_map.get(party, {}).get(party_type), - "rate": rate, - "total_amount": total_amount, - "grand_total": grand_total, - "base_total": base_total, - "tax_amount": tax_amount, - "transaction_date": posting_date, - "transaction_type": voucher_type, - "ref_no": name, - "supplier_invoice_no": bill_no, - "supplier_invoice_date": bill_date, + row = { + "pan" + if frappe.db.has_column(filters.party_type, "pan") + else "tax_id": party_map.get(party, {}).get("pan"), + "party": party_map.get(party, {}).get("name"), } - ) - out.append(row) + + if filters.naming_series == "Naming Series": + row.update({"party_name": party_map.get(party, {}).get(party_name)}) + + row.update( + { + "section_code": tax_withholding_category or "", + "entity_type": party_map.get(party, {}).get(party_type), + "rate": rate, + "total_amount": total_amount, + "grand_total": grand_total, + "base_total": base_total, + "tax_amount": tax_amount, + "transaction_date": posting_date, + "transaction_type": voucher_type, + "ref_no": name, + "supplier_invoice_no": bill_no, + "supplier_invoice_date": bill_date, + } + ) + out.append(row) out.sort(key=lambda x: x["section_code"]) @@ -282,11 +287,20 @@ def get_tds_docs(filters): journal_entry_party_map = frappe._dict() bank_accounts = frappe.get_all("Account", {"is_group": 0, "account_type": "Bank"}, pluck="name") - tds_accounts = frappe.get_all( - "Tax Withholding Account", {"company": filters.get("company")}, pluck="account" + _tds_accounts = frappe.get_all( + "Tax Withholding Account", + {"company": filters.get("company")}, + ["account", "parent"], ) + tds_accounts = {} + for tds_acc in _tds_accounts: + # if it turns out not to be the only tax withholding category, then don't include in the map + if tds_accounts.get(tds_acc["account"]): + tds_accounts[tds_acc["account"]] = None + else: + tds_accounts[tds_acc["account"]] = tds_acc["parent"] - tds_docs = get_tds_docs_query(filters, bank_accounts, tds_accounts).run(as_dict=True) + tds_docs = get_tds_docs_query(filters, bank_accounts, list(tds_accounts.keys())).run(as_dict=True) for d in tds_docs: if d.voucher_type == "Purchase Invoice": From a19b41d8c85a0f364d7f3704851b79bd9b88da5a Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Fri, 19 Jan 2024 17:08:02 +0530 Subject: [PATCH 157/675] fix: party field in pdf html (cherry picked from commit b2d9380596dd8ca134777148337ef039f10cc2ca) --- .../accounts_receivable/accounts_receivable.html | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.html b/erpnext/accounts/report/accounts_receivable/accounts_receivable.html index ed3b9915591..7d8d33c46b4 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.html +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.html @@ -10,10 +10,8 @@

    {%= __(report.report_name) %}

    - {% if (filters.customer_name) { %} - {%= filters.customer_name %} - {% } else { %} - {%= filters.customer || filters.supplier %} + {% if (filters.party) { %} + {%= __(filters.party) %} {% } %}

    @@ -141,7 +139,7 @@ {%= __("Reference") %} {% } %} {% if(!filters.show_future_payments) { %} - {%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %} + {%= (filters.party) ? __("Remarks"): __("Party") %} {% } %} {%= __("Invoiced Amount") %} {% if(!filters.show_future_payments) { %} @@ -158,7 +156,7 @@ {%= __("Remaining Balance") %} {% } %} {% } else { %} - {%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %} + {%= (filters.party) ? __("Remarks"): __("Party") %} {%= __("Total Invoiced Amount") %} {%= __("Total Paid Amount") %} {%= report.report_name === "Accounts Receivable Summary" ? __('Credit Note Amount') : __('Debit Note Amount') %} @@ -187,7 +185,7 @@ {% if(!filters.show_future_payments) { %} - {% if(!(filters.customer || filters.supplier)) { %} + {% if(!(filters.party)) { %} {%= data[i]["party"] %} {% if(data[i]["customer_name"] && data[i]["customer_name"] != data[i]["party"]) { %}
    {%= data[i]["customer_name"] %} @@ -260,7 +258,7 @@ {% if(data[i]["party"]|| " ") { %} {% if(!data[i]["is_total_row"]) { %} - {% if(!(filters.customer || filters.supplier)) { %} + {% if(!(filters.party)) { %} {%= data[i]["party"] %} {% if(data[i]["customer_name"] && data[i]["customer_name"] != data[i]["party"]) { %}
    {%= data[i]["customer_name"] %} From 55c9cc3f26409faf80298a6e69305db145d6bd01 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 22 Jan 2024 16:43:54 +0530 Subject: [PATCH 158/675] fix: UOM needs to be whole number not being checked in quotations (cherry picked from commit aaf83da3e9a28244c0f57f01a86b98257bdf503b) --- erpnext/selling/doctype/quotation/quotation.py | 3 ++- .../selling/doctype/quotation/test_quotation.py | 16 ++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 539960a508f..d3832172545 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -24,7 +24,8 @@ class Quotation(SellingController): def validate(self): super(Quotation, self).validate() self.set_status() - self.validate_uom_is_integer("stock_uom", "qty") + self.validate_uom_is_integer("stock_uom", "stock_qty") + self.validate_uom_is_integer("uom", "qty") self.validate_valid_till() self.validate_shopping_cart_items() self.set_customer_name() diff --git a/erpnext/selling/doctype/quotation/test_quotation.py b/erpnext/selling/doctype/quotation/test_quotation.py index 5623a12cdda..691c5b03d2d 100644 --- a/erpnext/selling/doctype/quotation/test_quotation.py +++ b/erpnext/selling/doctype/quotation/test_quotation.py @@ -590,6 +590,22 @@ class TestQuotation(FrappeTestCase): quotation.reload() self.assertEqual(quotation.status, "Ordered") + def test_uom_validation(self): + from erpnext.stock.doctype.item.test_item import make_item + + item = "_Test Item FOR UOM Validation" + make_item(item, {"is_stock_item": 1}) + + if not frappe.db.exists("UOM", "lbs"): + frappe.get_doc({"doctype": "UOM", "uom_name": "lbs", "must_be_whole_number": 1}).insert() + else: + frappe.db.set_value("UOM", "lbs", "must_be_whole_number", 1) + + quotation = make_quotation(item_code=item, qty=1, rate=100, do_not_submit=1) + quotation.items[0].uom = "lbs" + quotation.items[0].conversion_factor = 2.23 + self.assertRaises(frappe.ValidationError, quotation.save) + test_records = frappe.get_test_records("Quotation") From 0a6af795c48439cf10fc3b08b7ab531f38b586d8 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 4 May 2023 16:27:43 +0530 Subject: [PATCH 159/675] refactor: cr notes will post for itself (cherry picked from commit db76e8a277f4f62dde079b2429f02689ea3fc25b) --- erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py | 4 +--- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 4 +--- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 2af11159298..25e1e59f094 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -671,9 +671,7 @@ class PurchaseInvoice(BuyingController): "credit_in_account_currency": base_grand_total if self.party_account_currency == self.company_currency else grand_total, - "against_voucher": self.return_against - if cint(self.is_return) and self.return_against - else self.name, + "against_voucher": self.name, "against_voucher_type": self.doctype, "project": self.project, "cost_center": self.cost_center, diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 66d9022d08f..a6cf9646b4d 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1069,9 +1069,7 @@ class SalesInvoice(SellingController): "debit_in_account_currency": base_grand_total if self.party_account_currency == self.company_currency else grand_total, - "against_voucher": self.return_against - if cint(self.is_return) and self.return_against - else self.name, + "against_voucher": self.name, "against_voucher_type": self.doctype, "cost_center": self.cost_center, "project": self.project, From 014fcfa611ebdac821594d7958c7034da5289bd9 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 4 Jul 2023 15:59:57 +0530 Subject: [PATCH 160/675] refactor: remove return_against for cr/dr note filter (cherry picked from commit 00878707aecb9ee7efae49d1dea81150b9adbb1a) --- .../payment_reconciliation/payment_reconciliation.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 2b98baf2210..442df539f74 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -228,15 +228,12 @@ class PaymentReconciliation(Document): self.common_filter_conditions.append(ple.account == self.receivable_payable_account) self.get_return_invoices() - return_invoices = [ - x for x in self.return_invoices if x.return_against == None or x.return_against == "" - ] outstanding_dr_or_cr = [] - if return_invoices: + if self.return_invoices: ple_query = QueryPaymentLedger() return_outstanding = ple_query.get_voucher_outstandings( - vouchers=return_invoices, + vouchers=self.return_invoices, common_filter=self.common_filter_conditions, posting_date=self.ple_posting_date_filter, min_outstanding=-(self.minimum_payment_amount) if self.minimum_payment_amount else None, From 43b40d92e57413950667788b05b9574bb58306d3 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sun, 13 Aug 2023 15:48:25 +0530 Subject: [PATCH 161/675] refactor(test): return invoice will have -ve outstanding (cherry picked from commit b30c1e1abfcb45950709e7b12481d9e187b845f4) --- erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index d050299912d..45a01ab04aa 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1528,8 +1528,8 @@ class TestSalesInvoice(FrappeTestCase): self.assertEqual(party_credited, 1000) # Check outstanding amount - self.assertFalse(si1.outstanding_amount) - self.assertEqual(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"), 1500) + self.assertEqual(frappe.db.get_value("Sales Invoice", si1.name, "outstanding_amount"), -1000) + self.assertEqual(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"), 2500) def test_gle_made_when_asset_is_returned(self): create_asset_data() From 03040c1c7f16ffbbb79fa3c8123a88c43f8703ba Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 15 Aug 2023 09:02:41 +0530 Subject: [PATCH 162/675] refactor(test): ledger entries will be against itself (cherry picked from commit 0e2fb1188a66d6f1e6fe89564106c024e940107f) --- .../test_payment_ledger_entry.py | 34 +++++++++++++++---- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/payment_ledger_entry/test_payment_ledger_entry.py b/erpnext/accounts/doctype/payment_ledger_entry/test_payment_ledger_entry.py index fc6dbba7e7f..ce9579ed613 100644 --- a/erpnext/accounts/doctype/payment_ledger_entry/test_payment_ledger_entry.py +++ b/erpnext/accounts/doctype/payment_ledger_entry/test_payment_ledger_entry.py @@ -294,7 +294,7 @@ class TestPaymentLedgerEntry(FrappeTestCase): cr_note1.return_against = si3.name cr_note1 = cr_note1.save().submit() - pl_entries = ( + pl_entries_si3 = ( qb.from_(ple) .select( ple.voucher_type, @@ -309,7 +309,24 @@ class TestPaymentLedgerEntry(FrappeTestCase): .run(as_dict=True) ) - expected_values = [ + pl_entries_cr_note1 = ( + qb.from_(ple) + .select( + ple.voucher_type, + ple.voucher_no, + ple.against_voucher_type, + ple.against_voucher_no, + ple.amount, + ple.delinked, + ) + .where( + (ple.against_voucher_type == cr_note1.doctype) & (ple.against_voucher_no == cr_note1.name) + ) + .orderby(ple.creation) + .run(as_dict=True) + ) + + expected_values_for_si3 = [ { "voucher_type": si3.doctype, "voucher_no": si3.name, @@ -317,18 +334,21 @@ class TestPaymentLedgerEntry(FrappeTestCase): "against_voucher_no": si3.name, "amount": amount, "delinked": 0, - }, + } + ] + # credit/debit notes post ledger entries against itself + expected_values_for_cr_note1 = [ { "voucher_type": cr_note1.doctype, "voucher_no": cr_note1.name, - "against_voucher_type": si3.doctype, - "against_voucher_no": si3.name, + "against_voucher_type": cr_note1.doctype, + "against_voucher_no": cr_note1.name, "amount": -amount, "delinked": 0, }, ] - self.assertEqual(pl_entries[0], expected_values[0]) - self.assertEqual(pl_entries[1], expected_values[1]) + self.assertEqual(pl_entries_si3, expected_values_for_si3) + self.assertEqual(pl_entries_cr_note1, expected_values_for_cr_note1) def test_je_against_inv_and_note(self): ple = self.ple From fecab1338e40a57c304ef1445ab6c2a6b1edec98 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 4 Jul 2023 16:31:10 +0530 Subject: [PATCH 163/675] refactor(test): payments to invoice with -ve outstanding (cherry picked from commit f6e4ac2b6267ac0b88f399ada1482fc1366a126a) --- .../payment_entry/test_payment_entry.py | 45 ++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index a7f1f08cbc2..d035149ff93 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -705,7 +705,50 @@ class TestPaymentEntry(FrappeTestCase): pe2.submit() # create return entry against si1 - create_sales_invoice(is_return=1, return_against=si1.name, qty=-1) + cr_note = create_sales_invoice(is_return=1, return_against=si1.name, qty=-1) + si1_outstanding = frappe.db.get_value("Sales Invoice", si1.name, "outstanding_amount") + + # create JE(credit note) manually against si1 and cr_note + je = frappe.get_doc( + { + "doctype": "Journal Entry", + "company": si1.company, + "voucher_type": "Credit Note", + "posting_date": nowdate(), + } + ) + je.append( + "accounts", + { + "account": si1.debit_to, + "party_type": "Customer", + "party": si1.customer, + "debit": 0, + "credit": 100, + "debit_in_account_currency": 0, + "credit_in_account_currency": 100, + "reference_type": si1.doctype, + "reference_name": si1.name, + "cost_center": si1.items[0].cost_center, + }, + ) + je.append( + "accounts", + { + "account": cr_note.debit_to, + "party_type": "Customer", + "party": cr_note.customer, + "debit": 100, + "credit": 0, + "debit_in_account_currency": 100, + "credit_in_account_currency": 0, + "reference_type": cr_note.doctype, + "reference_name": cr_note.name, + "cost_center": cr_note.items[0].cost_center, + }, + ) + je.save().submit() + si1_outstanding = frappe.db.get_value("Sales Invoice", si1.name, "outstanding_amount") self.assertEqual(si1_outstanding, -100) From 1aeeac4d0623110d642f378578df910e840d1886 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 18 Aug 2023 10:10:42 +0530 Subject: [PATCH 164/675] refactor: criteria for `Credit Note Issued` and `Debit Note Issued` (cherry picked from commit 8f695123cd1e1e931b9ccac314687e010db1524d) --- .../accounts/doctype/purchase_invoice/purchase_invoice.py | 8 ++------ erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 8 ++------ 2 files changed, 4 insertions(+), 12 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 25e1e59f094..802ac8080a9 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -1538,12 +1538,8 @@ class PurchaseInvoice(BuyingController): elif outstanding_amount > 0 and getdate(self.due_date) >= getdate(): self.status = "Unpaid" # Check if outstanding amount is 0 due to debit note issued against invoice - elif ( - outstanding_amount <= 0 - and self.is_return == 0 - and frappe.db.get_value( - "Purchase Invoice", {"is_return": 1, "return_against": self.name, "docstatus": 1} - ) + elif self.is_return == 0 and frappe.db.get_value( + "Purchase Invoice", {"is_return": 1, "return_against": self.name, "docstatus": 1} ): self.status = "Debit Note Issued" elif self.is_return == 1: diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index a6cf9646b4d..fc37e5ec67f 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1696,12 +1696,8 @@ class SalesInvoice(SellingController): elif outstanding_amount > 0 and getdate(self.due_date) >= getdate(): self.status = "Unpaid" # Check if outstanding amount is 0 due to credit note issued against invoice - elif ( - outstanding_amount <= 0 - and self.is_return == 0 - and frappe.db.get_value( - "Sales Invoice", {"is_return": 1, "return_against": self.name, "docstatus": 1} - ) + elif self.is_return == 0 and frappe.db.get_value( + "Sales Invoice", {"is_return": 1, "return_against": self.name, "docstatus": 1} ): self.status = "Credit Note Issued" elif self.is_return == 1: From 956f05238a18d7efdf64adcc16ba9b4f01581540 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 23 Aug 2023 17:58:15 +0530 Subject: [PATCH 165/675] refactor: Payment btn criteria for Cr/Dr notes (cherry picked from commit 60eee564bfaa473ecb1afcf16d6e6f65b9928e34) --- erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js | 3 +-- erpnext/accounts/doctype/sales_invoice/sales_invoice.js | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 0add6c57da6..665fc6edcc9 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -99,8 +99,7 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying. } } - if(doc.docstatus == 1 && doc.outstanding_amount != 0 - && !(doc.is_return && doc.return_against) && !doc.on_hold) { + if(doc.docstatus == 1 && doc.outstanding_amount != 0 && !doc.on_hold) { this.frm.add_custom_button( __('Payment'), () => this.make_payment_entry(), diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 1e8428d9e29..2ac7370f4ef 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -91,8 +91,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e if(doc.update_stock) this.show_stock_ledger(); - if (doc.docstatus == 1 && doc.outstanding_amount!=0 - && !(cint(doc.is_return) && doc.return_against)) { + if (doc.docstatus == 1 && doc.outstanding_amount!=0) { this.frm.add_custom_button( __('Payment'), () => this.make_payment_entry(), From 26ca27a431dcd34e568b28c2407ad2599f59179d Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 23 Jan 2024 11:54:25 +0000 Subject: [PATCH 166/675] chore(release): Bumped to Version 14.61.1 ## [14.61.1](https://github.com/frappe/erpnext/compare/v14.61.0...v14.61.1) (2024-01-23) ### Bug Fixes * linting issue ([6895b74](https://github.com/frappe/erpnext/commit/6895b74ecc952f059e4eb05e67863bafd7139ce0)) * party field in pdf html ([a19b41d](https://github.com/frappe/erpnext/commit/a19b41d8c85a0f364d7f3704851b79bd9b88da5a)) * set unallocated amount after base tax ([f7ba736](https://github.com/frappe/erpnext/commit/f7ba7361ca6ebfc0876334e3ecdd54da564cf352)) * UOM needs to be whole number not being checked in quotations ([55c9cc3](https://github.com/frappe/erpnext/commit/55c9cc3f26409faf80298a6e69305db145d6bd01)) * use most reliable section reference per report line ([3bdff18](https://github.com/frappe/erpnext/commit/3bdff18467a575e4378fd625eebf6a2a666031cb)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index e640a99fdf8..c4d42e2b11c 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.61.0" +__version__ = "14.61.1" def get_default_company(user=None): From 5c9ad21a3f993ee8127947ac9cde5da8c8611ab6 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 30 Nov 2023 13:11:11 +0530 Subject: [PATCH 167/675] refactor: dimensions section in allocation table in reconciliation (cherry picked from commit 1cde804c773de41520a6148e7d99ab0c23c39ae1) --- .../payment_reconciliation_allocation.json | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json b/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json index 491c67818df..cbfd9b2d8b9 100644 --- a/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json +++ b/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json @@ -24,6 +24,7 @@ "difference_account", "exchange_rate", "currency", + "accounting_dimensions_section", "cost_center" ], "fields": [ @@ -157,12 +158,17 @@ "fieldname": "gain_loss_posting_date", "fieldtype": "Date", "label": "Difference Posting Date" + }, + { + "fieldname": "accounting_dimensions_section", + "fieldtype": "Section Break", + "label": "Accounting Dimensions" } ], "is_virtual": 1, "istable": 1, "links": [], - "modified": "2023-11-17 17:33:38.612615", + "modified": "2023-12-14 12:32:45.554730", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Reconciliation Allocation", From b25e8ae14c5b3225fdb4cff7436f17a03ce2103b Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 14 Dec 2023 12:16:50 +0530 Subject: [PATCH 168/675] refactor: update dimension doctypes in hooks (cherry picked from commit cfb3d872673844f04f5c9dd3f7d7f56288e5dd22) --- erpnext/hooks.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 481b740d775..5a4e8539bf6 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -548,6 +548,8 @@ accounting_dimension_doctypes = [ "Account Closing Balance", "Supplier Quotation", "Supplier Quotation Item", + "Payment Reconciliation", + "Payment Reconciliation Allocation", ] # get matching queries for Bank Reconciliation From 4114c0e85415480e44909ceccb855e84b2312a3c Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 14 Dec 2023 12:39:13 +0530 Subject: [PATCH 169/675] refactor: dimensions filter section in payment reconciliation (cherry picked from commit 20e0acc20a218029d7101a1ba6ff3c1ae03fac02) --- .../payment_reconciliation.json | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json index e8985de4e1e..a14a74a1f47 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json @@ -24,6 +24,7 @@ "invoice_limit", "payment_limit", "bank_cash_account", + "accounting_dimensions_section", "cost_center", "sec_break1", "invoice_name", @@ -199,6 +200,14 @@ "fieldname": "payment_name", "fieldtype": "Data", "label": "Filter on Payment" + }, + { + "collapsible": 1, + "collapsible_depends_on": "eval: doc.invoices.length == 0", + "depends_on": "eval:doc.receivable_payable_account", + "fieldname": "accounting_dimensions_section", + "fieldtype": "Section Break", + "label": "Accounting Dimensions Filter" } ], "hide_toolbar": 1, @@ -206,7 +215,7 @@ "is_virtual": 1, "issingle": 1, "links": [], - "modified": "2023-11-17 17:33:55.701726", + "modified": "2023-12-14 12:38:44.910625", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Reconciliation", From 81b87ef2e27089805b9069a715f08952f7e8884f Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 14 Dec 2023 13:33:24 +0530 Subject: [PATCH 170/675] refactor: column break in dimension section (cherry picked from commit 20576e0f47ba3c4937121bfab1e0d8d395a590ce) --- .../payment_reconciliation/payment_reconciliation.json | 7 ++++++- .../payment_reconciliation_allocation.json | 9 +++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json index a14a74a1f47..948bae3dfe6 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.json @@ -26,6 +26,7 @@ "bank_cash_account", "accounting_dimensions_section", "cost_center", + "dimension_col_break", "sec_break1", "invoice_name", "invoices", @@ -208,6 +209,10 @@ "fieldname": "accounting_dimensions_section", "fieldtype": "Section Break", "label": "Accounting Dimensions Filter" + }, + { + "fieldname": "dimension_col_break", + "fieldtype": "Column Break" } ], "hide_toolbar": 1, @@ -215,7 +220,7 @@ "is_virtual": 1, "issingle": 1, "links": [], - "modified": "2023-12-14 12:38:44.910625", + "modified": "2023-12-14 13:38:16.264013", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Reconciliation", diff --git a/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json b/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json index cbfd9b2d8b9..3f85b213500 100644 --- a/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json +++ b/erpnext/accounts/doctype/payment_reconciliation_allocation/payment_reconciliation_allocation.json @@ -25,7 +25,8 @@ "exchange_rate", "currency", "accounting_dimensions_section", - "cost_center" + "cost_center", + "dimension_col_break" ], "fields": [ { @@ -163,12 +164,16 @@ "fieldname": "accounting_dimensions_section", "fieldtype": "Section Break", "label": "Accounting Dimensions" + }, + { + "fieldname": "dimension_col_break", + "fieldtype": "Column Break" } ], "is_virtual": 1, "istable": 1, "links": [], - "modified": "2023-12-14 12:32:45.554730", + "modified": "2023-12-14 13:38:26.104150", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Reconciliation Allocation", From 66cadb8b9f2afd5938401d2cb9134f6bb823f5e2 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 15 Dec 2023 16:02:12 +0530 Subject: [PATCH 171/675] refactor: handle dimension filters (cherry picked from commit c1fe4bcc64775507a3bd8e02b61274d8dc2d6447) --- .../payment_reconciliation/payment_reconciliation.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 2b98baf2210..b8096843d6a 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -10,6 +10,7 @@ from frappe.query_builder.custom import ConstantColumn from frappe.utils import flt, fmt_money, get_link_to_form, getdate, nowdate, today import erpnext +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_dimensions from erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation import ( is_any_doc_running, ) @@ -592,6 +593,14 @@ class PaymentReconciliation(Document): if not invoices_to_reconcile: frappe.throw(_("No records found in Allocation table")) + def build_dimensions_filter_conditions(self): + ple = qb.DocType("Payment Ledger Entry") + dimensions_and_defaults = get_dimensions() + for x in dimensions_and_defaults[0]: + dimension = x.fieldname + if self.get(dimension): + self.accounting_dimension_filter_conditions.append(ple[dimension] == self.get(dimension)) + def build_qb_filter_conditions(self, get_invoices=False, get_return_invoices=False): self.common_filter_conditions.clear() self.accounting_dimension_filter_conditions.clear() @@ -615,6 +624,8 @@ class PaymentReconciliation(Document): if self.to_payment_date: self.ple_posting_date_filter.append(ple.posting_date.lte(self.to_payment_date)) + self.build_dimensions_filter_conditions() + def get_conditions(self, get_payments=False): condition = " and company = '{0}' ".format(self.company) From 3d62bce885611ae31ddf69d1af12527da82ddd15 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 15 Dec 2023 17:25:02 +0530 Subject: [PATCH 172/675] refactor: pass dimension filters to query (cherry picked from commit ff60ec85b85d5548886e247b72cf1262587feba3) # Conflicts: # erpnext/controllers/accounts_controller.py --- .../payment_reconciliation.py | 9 ++++ erpnext/controllers/accounts_controller.py | 45 +++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index b8096843d6a..f0e381b822d 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -113,6 +113,15 @@ class PaymentReconciliation(Document): order_doctype = "Sales Order" if self.party_type == "Customer" else "Purchase Order" condition = self.get_conditions(get_payments=True) + # pass dynamic dimension filter values to query builder + dimensions = {} + dimensions_and_defaults = get_dimensions() + for x in dimensions_and_defaults[0]: + dimension = x.fieldname + if self.get(dimension): + dimensions.update({dimension: self.get(dimension)}) + condition.update({"accounting_dimensions": dimensions}) + payment_entries = get_advance_payment_entries_for_regional( self.party_type, self.party, diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index cc8ee9226a4..d5bc69fe5db 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -7,6 +7,11 @@ import json import frappe from frappe import _, bold, qb, throw from frappe.model.workflow import get_workflow_name, is_transition_condition_satisfied +<<<<<<< HEAD +======= +from frappe.query_builder import Criterion +from frappe.query_builder.custom import ConstantColumn +>>>>>>> ff60ec85b8 (refactor: pass dimension filters to query) from frappe.query_builder.functions import Abs, Sum from frappe.utils import ( add_days, @@ -2542,6 +2547,7 @@ def get_advance_payment_entries( payment_entries_against_order, unallocated_payment_entries = [], [] limit_cond = "limit %s" % limit if limit else "" +<<<<<<< HEAD if order_list or against_all_orders: if order_list: reference_condition = " and t2.reference_name in ({0})".format( @@ -2550,6 +2556,45 @@ def get_advance_payment_entries( else: reference_condition = "" order_list = [] +======= + if payment_type == "Receive": + q = q.select((payment_entry.source_exchange_rate).as_("exchange_rate")) + else: + q = q.select((payment_entry.target_exchange_rate).as_("exchange_rate")) + + if condition: + # conditions should be built as an array and passed as Criterion + common_filter_conditions = [] + + common_filter_conditions.append(payment_entry.company == condition["company"]) + if condition.get("name", None): + common_filter_conditions.append(payment_entry.name.like(f"%{condition.get('name')}%")) + + if condition.get("from_payment_date"): + common_filter_conditions.append(payment_entry.posting_date.gte(condition["from_payment_date"])) + + if condition.get("to_payment_date"): + common_filter_conditions.append(payment_entry.posting_date.lte(condition["to_payment_date"])) + + if condition.get("get_payments") == True: + if condition.get("cost_center"): + common_filter_conditions.append(payment_entry.cost_center == condition["cost_center"]) + + if condition.get("accounting_dimensions"): + for field, val in condition.get("accounting_dimensions").items(): + common_filter_conditions.append(payment_entry[field] == val) + + if condition.get("minimum_payment_amount"): + common_filter_conditions.append( + payment_entry.unallocated_amount.gte(condition["minimum_payment_amount"]) + ) + + if condition.get("maximum_payment_amount"): + common_filter_conditions.append( + payment_entry.unallocated_amount.lte(condition["maximum_payment_amount"]) + ) + q = q.where(Criterion.all(common_filter_conditions)) +>>>>>>> ff60ec85b8 (refactor: pass dimension filters to query) payment_name_filter = "" if payment_name: From b2db6d054658e26dea6f3576a2b6fb1cc2872c9b Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 19 Dec 2023 17:07:54 +0530 Subject: [PATCH 173/675] refactor: set query filters for dimensions (cherry picked from commit ad8475cb8b24d40b04f86903feee08ecac6aa1f1) --- .../payment_reconciliation.js | 21 +++++++++++++++++++ .../payment_reconciliation.py | 17 +++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js index 5cdedb73c09..16d1839679c 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js @@ -83,6 +83,8 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo this.frm.change_custom_button_type('Allocate', null, 'default'); } + this.frm.trigger("set_query_for_dimension_filters"); + // check for any running reconciliation jobs if (this.frm.doc.receivable_payable_account) { this.frm.call({ @@ -113,6 +115,25 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo } } + set_query_for_dimension_filters() { + frappe.call({ + method: "erpnext.accounts.doctype.payment_reconciliation.payment_reconciliation.get_queries_for_dimension_filters", + args: { + company: this.frm.doc.company, + }, + callback: (r) => { + if (!r.exc && r.message) { + r.message.forEach(x => { + this.frm.set_query(x.fieldname, () => { + return { + 'filters': x.filters + }; + }); + }); + } + } + }); + } company() { this.frm.set_value('party', ''); diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index f0e381b822d..45620bae59f 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -757,3 +757,20 @@ def reconcile_dr_cr_note(dr_cr_notes, company): @erpnext.allow_regional def adjust_allocations_for_taxes(doc): pass + + +@frappe.whitelist() +def get_queries_for_dimension_filters(company: str = None): + dimensions_with_filters = [] + for d in get_dimensions()[0]: + filters = {} + meta = frappe.get_meta(d.document_type) + if meta.has_field("company") and company: + filters.update({"company": company}) + + if meta.is_tree: + filters.update({"is_group": 0}) + + dimensions_with_filters.append({"fieldname": d.fieldname, "filters": filters}) + + return dimensions_with_filters From dac422a0e149905ee739c18b5840ec096d54c1f0 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 20 Dec 2023 17:19:27 +0530 Subject: [PATCH 174/675] refactor: pass dimension details to query (cherry picked from commit 5dc22e1811bb1841bb8c790cc3a1e1315cef6074) # Conflicts: # erpnext/accounts/utils.py --- .../payment_reconciliation.py | 17 ++++++++++++----- erpnext/accounts/utils.py | 6 ++++++ erpnext/controllers/accounts_controller.py | 2 ++ 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 45620bae59f..b7be45a7ebd 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -29,6 +29,7 @@ class PaymentReconciliation(Document): self.common_filter_conditions = [] self.accounting_dimension_filter_conditions = [] self.ple_posting_date_filter = [] + self.dimensions = get_dimensions()[0] def load_from_db(self): # 'modified' attribute is required for `run_doc_method` to work properly. @@ -115,8 +116,7 @@ class PaymentReconciliation(Document): # pass dynamic dimension filter values to query builder dimensions = {} - dimensions_and_defaults = get_dimensions() - for x in dimensions_and_defaults[0]: + for x in self.dimensions: dimension = x.fieldname if self.get(dimension): dimensions.update({dimension: self.get(dimension)}) @@ -472,7 +472,7 @@ class PaymentReconciliation(Document): self.get_unreconciled_entries() def get_payment_details(self, row, dr_or_cr): - return frappe._dict( + payment_details = frappe._dict( { "voucher_type": row.get("reference_type"), "voucher_no": row.get("reference_name"), @@ -495,6 +495,14 @@ class PaymentReconciliation(Document): } ) + dimensions_dict = {} + for x in self.dimensions: + if row.get(x.fieldname): + dimensions_dict.update({x.fieldname: row.get(x.fieldname)}) + + payment_details.update({"dimensions": dimensions_dict}) + return payment_details + def check_mandatory_to_fetch(self): for fieldname in ["company", "party_type", "party", "receivable_payable_account"]: if not self.get(fieldname): @@ -604,8 +612,7 @@ class PaymentReconciliation(Document): def build_dimensions_filter_conditions(self): ple = qb.DocType("Payment Ledger Entry") - dimensions_and_defaults = get_dimensions() - for x in dimensions_and_defaults[0]: + for x in self.dimensions: dimension = x.fieldname if self.get(dimension): self.accounting_dimension_filter_conditions.append(ple[dimension] == self.get(dimension)) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index d9fb75a86d2..74284d9c1d9 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -630,6 +630,11 @@ def update_reference_in_payment_entry( if d.difference_amount is not None else payment_entry.get_exchange_rate(), "exchange_gain_loss": d.difference_amount, +<<<<<<< HEAD +======= + "account": d.account, + "dimensions": d.dimensions, +>>>>>>> 5dc22e1811 (refactor: pass dimension details to query) } if d.voucher_detail_no: @@ -1991,6 +1996,7 @@ def create_gain_loss_journal( ref2_dn, ref2_detail_no, cost_center, + dimensions, ) -> str: journal_entry = frappe.new_doc("Journal Entry") journal_entry.voucher_type = "Exchange Gain Or Loss" diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index d5bc69fe5db..9da71654227 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1178,6 +1178,7 @@ class AccountsController(TransactionBase): self.name, arg.get("referenced_row"), arg.get("cost_center"), + {}, ) frappe.msgprint( _("Exchange Gain/Loss amount has been booked through {0}").format( @@ -1258,6 +1259,7 @@ class AccountsController(TransactionBase): self.name, d.idx, self.cost_center, + {}, ) frappe.msgprint( _("Exchange Gain/Loss amount has been booked through {0}").format( From 8f87c588ecec027019e66809c73a7eef2f19cea2 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 29 Dec 2023 17:42:41 +0530 Subject: [PATCH 175/675] refactor: replace sql with query builder for Jourals query (cherry picked from commit 9c5a79209eb014c90aac46a5dd5ed0d9b7cb8f87) --- .../payment_reconciliation.py | 133 ++++++++---------- 1 file changed, 61 insertions(+), 72 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index b7be45a7ebd..5fcb35e7846 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -136,66 +136,67 @@ class PaymentReconciliation(Document): return payment_entries def get_jv_entries(self): - condition = self.get_conditions() + je = qb.DocType("Journal Entry") + jea = qb.DocType("Journal Entry Account") + conditions = self.get_journal_filter_conditions() + + # Dimension filters + for x in self.dimensions: + dimension = x.fieldname + if self.get(dimension): + conditions.append(jea[dimension] == self.get(dimension)) if self.payment_name: - condition += f" and t1.name like '%%{self.payment_name}%%'" + conditions.append(je.name.like(f"%%{self.payent_name}%%")) if self.get("cost_center"): - condition += f" and t2.cost_center = '{self.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)) - bank_account_condition = ( - "t2.against_account like %(bank_cash_account)s" if self.bank_cash_account else "1=1" + if self.bank_cash_account: + conditions.append(jea.against_account.like(f"%%{self.bank_cash_account}%%")) + + journal_query = ( + qb.from_(je) + .inner_join(jea) + .on(jea.parent == je.name) + .select( + ConstantColumn("Journal Entry").as_("reference_type"), + je.name.as_("reference_name"), + je.posting_date, + je.remark.as_("remarks"), + jea.name.as_("reference_row"), + jea[dr_or_cr].as_("amount"), + jea.is_advance, + jea.exchange_rate, + jea.account_currency.as_("currency"), + jea.cost_center.as_("cost_center"), + ) + .where( + (je.docstatus == 1) + & (jea.party_type == self.party_type) + & (jea.party == self.party) + & (jea.account == self.receivable_payable_account) + & ( + (jea.reference_type == "") + | (jea.reference_type.isnull()) + | (jea.reference_type.isin(("Sales Order", "Purchase Order"))) + ) + ) + .where(Criterion.all(conditions)) + .orderby(je.posting_date) ) - limit = f"limit {self.payment_limit}" if self.payment_limit else " " + if self.payment_limit: + journal_query = journal_query.limit(self.payment_limit) - # nosemgrep - journal_entries = frappe.db.sql( - """ - select - "Journal Entry" as reference_type, t1.name as reference_name, - t1.posting_date, t1.remark as remarks, t2.name as reference_row, - {dr_or_cr} as amount, t2.is_advance, t2.exchange_rate, - t2.account_currency as currency, t2.cost_center as cost_center - from - `tabJournal Entry` t1, `tabJournal Entry Account` t2 - where - t1.name = t2.parent and t1.docstatus = 1 and t2.docstatus = 1 - and t2.party_type = %(party_type)s and t2.party = %(party)s - and t2.account = %(account)s and {dr_or_cr} > 0 {condition} - and (t2.reference_type is null or t2.reference_type = '' or - (t2.reference_type in ('Sales Order', 'Purchase Order') - and t2.reference_name is not null and t2.reference_name != '')) - and (CASE - WHEN t1.voucher_type in ('Debit Note', 'Credit Note') - THEN 1=1 - ELSE {bank_account_condition} - END) - order by t1.posting_date - {limit} - """.format( - **{ - "dr_or_cr": dr_or_cr, - "bank_account_condition": bank_account_condition, - "condition": condition, - "limit": limit, - } - ), - { - "party_type": self.party_type, - "party": self.party, - "account": self.receivable_payable_account, - "bank_cash_account": "%%%s%%" % self.bank_cash_account, - }, - as_dict=1, - ) + journal_entries = journal_query.run(as_dict=True) return list(journal_entries) @@ -642,37 +643,25 @@ class PaymentReconciliation(Document): self.build_dimensions_filter_conditions() - def get_conditions(self, get_payments=False): - condition = " and company = '{0}' ".format(self.company) + def get_journal_filter_conditions(self): + conditions = [] + je = qb.DocType("Journal Entry") + jea = qb.DocType("Journal Entry Account") + conditions.append(je.company == self.company) - if self.get("cost_center") and get_payments: - condition = " and cost_center = '{0}' ".format(self.cost_center) + if self.from_payment_date: + conditions.append(je.posting_date.gte(self.from_payment_date)) - condition += ( - " and posting_date >= {0}".format(frappe.db.escape(self.from_payment_date)) - if self.from_payment_date - else "" - ) - condition += ( - " and posting_date <= {0}".format(frappe.db.escape(self.to_payment_date)) - if self.to_payment_date - else "" - ) + if self.to_payment_date: + conditions.append(je.posting_date.lte(self.to_payment_date)) if self.minimum_payment_amount: - condition += ( - " and unallocated_amount >= {0}".format(flt(self.minimum_payment_amount)) - if get_payments - else " and total_debit >= {0}".format(flt(self.minimum_payment_amount)) - ) - if self.maximum_payment_amount: - condition += ( - " and unallocated_amount <= {0}".format(flt(self.maximum_payment_amount)) - if get_payments - else " and total_debit <= {0}".format(flt(self.maximum_payment_amount)) - ) + conditions.append(je.total_debit.gte(self.minimum_payment_amount)) - return condition + if self.maximum_payment_amount: + conditions.append(je.total_debit.lte(self.maximumb_payment_amount)) + + return conditions def reconcile_dr_cr_note(dr_cr_notes, company): From ecd36501afea1a413b2ef7a7389303ce3baf883c Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 2 Jan 2024 11:33:02 +0530 Subject: [PATCH 176/675] refactor: partial change on outstanding invoice popup (cherry picked from commit 2154502955166243e354897d7dcb22d1987c4693) --- .../doctype/payment_entry/payment_entry.js | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 6d9d6920105..b7a4e854ff2 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -629,6 +629,20 @@ frappe.ui.form.on('Payment Entry', { frm.events.set_unallocated_amount(frm); }, + get_dimensions: function(frm) { + let result = []; + frappe.call({ + method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.get_dimensions", + async: false, + callback: function(r) { + if(!r.exc) { + result = r.message[0].map(elem => elem.document_type); + } + } + }); + return result; + }, + get_outstanding_invoices_or_orders: function(frm, get_outstanding_invoices, get_orders_to_be_billed) { const today = frappe.datetime.get_today(); const fields = [ From 41c074d0bbca82b21fcd27b66adc84fa8a03c6b0 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 15 Jan 2024 14:56:51 +0530 Subject: [PATCH 177/675] fix: typo's and parameter changes (cherry picked from commit 0ec17590ae062fbda0c14a2806ec1ac07c638593) --- .../doctype/payment_reconciliation/payment_reconciliation.py | 5 +++-- erpnext/accounts/utils.py | 2 ++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 5fcb35e7846..30250caddb2 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -147,7 +147,7 @@ class PaymentReconciliation(Document): conditions.append(jea[dimension] == self.get(dimension)) if self.payment_name: - conditions.append(je.name.like(f"%%{self.payent_name}%%")) + conditions.append(je.name.like(f"%%{self.payment_name}%%")) if self.get("cost_center"): conditions.append(jea.cost_center == self.cost_center) @@ -659,7 +659,7 @@ class PaymentReconciliation(Document): conditions.append(je.total_debit.gte(self.minimum_payment_amount)) if self.maximum_payment_amount: - conditions.append(je.total_debit.lte(self.maximumb_payment_amount)) + conditions.append(je.total_debit.lte(self.maximum_payment_amount)) return conditions @@ -747,6 +747,7 @@ def reconcile_dr_cr_note(dr_cr_notes, company): inv.against_voucher, None, inv.cost_center, + frappe._dict(), ) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 74284d9c1d9..9af81168e59 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -1998,6 +1998,8 @@ def create_gain_loss_journal( cost_center, dimensions, ) -> str: + # TODO: pass dimensions to Journal + journal_entry = frappe.new_doc("Journal Entry") journal_entry.voucher_type = "Exchange Gain Or Loss" journal_entry.company = company From 937262b57299f63e9207637a777adee5d5974325 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 15 Jan 2024 16:13:26 +0530 Subject: [PATCH 178/675] refactor: Credit Note and its Exc gain/loss JE inherits dimensions (cherry picked from commit ab939cc6e8ab3669f1e9b0f007e9459be180ac32) --- .../payment_reconciliation.py | 31 ++++++++++++++----- erpnext/accounts/utils.py | 6 ++-- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 30250caddb2..f8408b4b13d 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -402,8 +402,15 @@ class PaymentReconciliation(Document): row = self.append("allocation", {}) row.update(entry) + def update_dimension_values_in_allocated_entries(self, res): + for x in self.dimensions: + dimension = x.fieldname + if self.get(dimension): + res[dimension] = self.get(dimension) + return res + def get_allocated_entry(self, pay, inv, allocated_amount): - return frappe._dict( + res = frappe._dict( { "reference_type": pay.get("reference_type"), "reference_name": pay.get("reference_name"), @@ -419,6 +426,9 @@ class PaymentReconciliation(Document): } ) + res = self.update_dimension_values_in_allocated_entries(res) + return res + def reconcile_allocations(self, skip_ref_details_update_for_pe=False): adjust_allocations_for_taxes(self) dr_or_cr = ( @@ -444,7 +454,7 @@ class PaymentReconciliation(Document): reconcile_against_document(entry_list, skip_ref_details_update_for_pe) if dr_or_cr_notes: - reconcile_dr_cr_note(dr_or_cr_notes, self.company) + reconcile_dr_cr_note(dr_or_cr_notes, self.company, self.dimensions) @frappe.whitelist() def reconcile(self): @@ -496,12 +506,10 @@ class PaymentReconciliation(Document): } ) - dimensions_dict = {} for x in self.dimensions: if row.get(x.fieldname): - dimensions_dict.update({x.fieldname: row.get(x.fieldname)}) + payment_details[x.fieldname] = row.get(x.fieldname) - payment_details.update({"dimensions": dimensions_dict}) return payment_details def check_mandatory_to_fetch(self): @@ -664,7 +672,7 @@ class PaymentReconciliation(Document): return conditions -def reconcile_dr_cr_note(dr_cr_notes, company): +def reconcile_dr_cr_note(dr_cr_notes, company, active_dimensions=None): for inv in dr_cr_notes: voucher_type = "Credit Note" if inv.voucher_type == "Sales Invoice" else "Debit Note" @@ -714,6 +722,15 @@ def reconcile_dr_cr_note(dr_cr_notes, company): } ) + # Credit Note(JE) will inherit the same dimension values as payment + dimensions_dict = frappe._dict() + if active_dimensions: + for dim in active_dimensions: + dimensions_dict[dim.fieldname] = inv.get(dim.fieldname) + + jv.accounts[0].update(dimensions_dict) + jv.accounts[1].update(dimensions_dict) + jv.flags.ignore_mandatory = True jv.flags.skip_remarks_creation = True jv.flags.ignore_exchange_rate = True @@ -747,7 +764,7 @@ def reconcile_dr_cr_note(dr_cr_notes, company): inv.against_voucher, None, inv.cost_center, - frappe._dict(), + dimensions_dict, ) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 9af81168e59..07ba01fe529 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -1998,8 +1998,6 @@ def create_gain_loss_journal( cost_center, dimensions, ) -> str: - # TODO: pass dimensions to Journal - journal_entry = frappe.new_doc("Journal Entry") journal_entry.voucher_type = "Exchange Gain Or Loss" journal_entry.company = company @@ -2032,7 +2030,7 @@ def create_gain_loss_journal( dr_or_cr + "_in_account_currency": 0, } ) - + journal_account.update(dimensions) journal_entry.append("accounts", journal_account) journal_account = frappe._dict( @@ -2048,7 +2046,7 @@ def create_gain_loss_journal( reverse_dr_or_cr: abs(exc_gain_loss), } ) - + journal_account.update(dimensions) journal_entry.append("accounts", journal_account) journal_entry.save() From c3ffb7a4c4acee7a962def5feef735aec557aef7 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 17 Jan 2024 12:37:43 +0530 Subject: [PATCH 179/675] refactor: apply dimension filters on cr/dr notes (cherry picked from commit 188ff8cde794bb1ef1043f0e47469d65944aac1e) --- .../doctype/payment_reconciliation/payment_reconciliation.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index f8408b4b13d..d400dcdceb9 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -253,6 +253,7 @@ class PaymentReconciliation(Document): min_outstanding=-(self.minimum_payment_amount) if self.minimum_payment_amount else None, max_outstanding=-(self.maximum_payment_amount) if self.maximum_payment_amount else None, get_payments=True, + accounting_dimensions=self.accounting_dimension_filter_conditions, ) for inv in return_outstanding: From c1591ec8e1a504d31936e88ed16e5a5b6cc5e38b Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 17 Jan 2024 12:50:05 +0530 Subject: [PATCH 180/675] chore: test dimension filter output (cherry picked from commit e3c44231abbbe389a1f815ab77f2d6ff0c614e1b) --- .../tests/test_accounts_controller.py | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) diff --git a/erpnext/controllers/tests/test_accounts_controller.py b/erpnext/controllers/tests/test_accounts_controller.py index 97d3c5c32de..3a3e6def48a 100644 --- a/erpnext/controllers/tests/test_accounts_controller.py +++ b/erpnext/controllers/tests/test_accounts_controller.py @@ -56,6 +56,7 @@ class TestAccountsController(FrappeTestCase): 20 series - Sales Invoice against Journals 30 series - Sales Invoice against Credit Notes 40 series - Company default Cost center is unset + 50 series - Dimension inheritence """ def setUp(self): @@ -1255,3 +1256,88 @@ class TestAccountsController(FrappeTestCase): ) frappe.db.set_value("Company", self.company, "cost_center", cc) + + def setup_dimensions(self): + # create dimension + from erpnext.accounts.doctype.accounting_dimension.test_accounting_dimension import ( + create_dimension, + ) + + create_dimension() + # make it non-mandatory + loc = frappe.get_doc("Accounting Dimension", "Location") + for x in loc.dimension_defaults: + x.mandatory_for_bs = False + x.mandatory_for_pl = False + loc.save() + + def test_50_dimensions_filter(self): + """ + Gain/Loss JE should inherit its dimension from payment + """ + self.setup_dimensions() + rate_in_account_currency = 1 + + # Invoices + si1 = self.create_sales_invoice(qty=1, rate=rate_in_account_currency, do_not_submit=True) + si1.department = "Management" + si1.save().submit() + + si2 = self.create_sales_invoice(qty=1, rate=rate_in_account_currency, do_not_submit=True) + si2.department = "Operations" + si2.save().submit() + + # Payments + cr_note1 = self.create_sales_invoice(qty=-1, conversion_rate=75, rate=1, do_not_save=True) + cr_note1.department = "Management" + cr_note1.is_return = 1 + cr_note1.save().submit() + + cr_note2 = self.create_sales_invoice(qty=-1, conversion_rate=75, rate=1, do_not_save=True) + cr_note2.department = "Legal" + cr_note2.is_return = 1 + cr_note2.save().submit() + + pe1 = get_payment_entry(si1.doctype, si1.name) + pe1.references = [] + pe1.department = "Research & Development" + pe1.save().submit() + + pe2 = get_payment_entry(si1.doctype, si1.name) + pe2.references = [] + pe2.department = "Management" + pe2.save().submit() + + je1 = self.create_journal_entry( + acc1=self.debit_usd, + acc1_exc_rate=75, + acc2=self.cash, + acc1_amount=-1, + acc2_amount=-75, + acc2_exc_rate=1, + ) + je1.accounts[0].party_type = "Customer" + je1.accounts[0].party = self.customer + je1.accounts[0].department = "Management" + je1.save().submit() + + # assert dimension filter's result + pr = self.create_payment_reconciliation() + pr.get_unreconciled_entries() + self.assertEqual(len(pr.invoices), 2) + self.assertEqual(len(pr.payments), 5) + + pr.department = "Legal" + pr.get_unreconciled_entries() + self.assertEqual(len(pr.invoices), 0) + self.assertEqual(len(pr.payments), 1) + + pr.department = "Management" + pr.get_unreconciled_entries() + self.assertEqual(len(pr.invoices), 1) + self.assertEqual(len(pr.payments), 3) + + pr.department = "Research & Development" + pr.get_unreconciled_entries() + self.assertEqual(len(pr.invoices), 0) + self.assertEqual(len(pr.payments), 1) From de948f23c130d95936309be1a790e41878f6e059 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 18 Jan 2024 13:20:06 +0530 Subject: [PATCH 181/675] test: dimension inheritance for cr note reconciliation (cherry picked from commit ba5a7c8cd8ee6fc09b0d81ffbe8b364e584f1f1b) --- .../tests/test_accounts_controller.py | 44 ++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/tests/test_accounts_controller.py b/erpnext/controllers/tests/test_accounts_controller.py index 3a3e6def48a..a448ad4a572 100644 --- a/erpnext/controllers/tests/test_accounts_controller.py +++ b/erpnext/controllers/tests/test_accounts_controller.py @@ -1273,7 +1273,7 @@ class TestAccountsController(FrappeTestCase): def test_50_dimensions_filter(self): """ - Gain/Loss JE should inherit its dimension from payment + Test workings of dimension filters """ self.setup_dimensions() rate_in_account_currency = 1 @@ -1341,3 +1341,45 @@ class TestAccountsController(FrappeTestCase): pr.get_unreconciled_entries() self.assertEqual(len(pr.invoices), 0) self.assertEqual(len(pr.payments), 1) + + def test_51_cr_note_should_inherit_dimension_from_payment(self): + self.setup_dimensions() + rate_in_account_currency = 1 + + # Invoice + si = self.create_sales_invoice(qty=1, rate=rate_in_account_currency, do_not_submit=True) + si.department = "Management" + si.save().submit() + + # Payment + cr_note = self.create_sales_invoice(qty=-1, conversion_rate=75, rate=1, do_not_save=True) + cr_note.department = "Management" + cr_note.is_return = 1 + cr_note.save().submit() + + pr = self.create_payment_reconciliation() + pr.department = "Management" + pr.get_unreconciled_entries() + self.assertEqual(len(pr.invoices), 1) + self.assertEqual(len(pr.payments), 1) + invoices = [x.as_dict() for x in pr.invoices] + payments = [x.as_dict() for x in pr.payments] + pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments})) + pr.reconcile() + self.assertEqual(len(pr.invoices), 0) + self.assertEqual(len(pr.payments), 0) + + # There should be 2 journals, JE(Cr Note) and JE(Exchange Gain/Loss) + exc_je_for_si = self.get_journals_for(si.doctype, si.name) + exc_je_for_cr_note = self.get_journals_for(cr_note.doctype, cr_note.name) + self.assertNotEqual(exc_je_for_si, []) + self.assertEqual(len(exc_je_for_si), 2) + self.assertEqual(len(exc_je_for_cr_note), 2) + self.assertEqual(exc_je_for_si, exc_je_for_cr_note) + + for x in exc_je_for_si + exc_je_for_cr_note: + with self.subTest(x=x): + self.assertEqual( + [cr_note.department, cr_note.department], + frappe.db.get_all("Journal Entry Account", filters={"parent": x.parent}, pluck="department"), + ) From a9197023199547351f5cdd59e80e6b2ebda013ae Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 18 Jan 2024 14:35:06 +0530 Subject: [PATCH 182/675] refactor: pass dimension values to Gain/Loss journal (cherry picked from commit c44eb432a59fb3ffb3748e47356068499f1129b1) # Conflicts: # erpnext/accounts/utils.py --- .../payment_reconciliation.py | 2 +- erpnext/accounts/utils.py | 32 ++++++++++++++++--- erpnext/controllers/accounts_controller.py | 8 +++-- 3 files changed, 34 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index d400dcdceb9..79897e0803a 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -452,7 +452,7 @@ class PaymentReconciliation(Document): reconciled_entry.append(payment_details) if entry_list: - reconcile_against_document(entry_list, skip_ref_details_update_for_pe) + reconcile_against_document(entry_list, skip_ref_details_update_for_pe, self.dimensions) if dr_or_cr_notes: reconcile_dr_cr_note(dr_or_cr_notes, self.company, self.dimensions) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 07ba01fe529..c207d9338e6 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -434,7 +434,19 @@ def add_cc(args=None): return cc.name -def reconcile_against_document(args, skip_ref_details_update_for_pe=False): # nosemgrep +def _build_dimensions_dict_for_exc_gain_loss( + entry: dict | object = None, active_dimensions: list = None +): + dimensions_dict = frappe._dict() + if entry and active_dimensions: + for dim in active_dimensions: + dimensions_dict[dim.fieldname] = entry.get(dim.fieldname) + return dimensions_dict + + +def reconcile_against_document( + args, skip_ref_details_update_for_pe=False, active_dimensions=None +): # nosemgrep """ Cancel PE or JV, Update against document, split if required and resubmit """ @@ -459,6 +471,8 @@ def reconcile_against_document(args, skip_ref_details_update_for_pe=False): # n check_if_advance_entry_modified(entry) validate_allocated_amount(entry) + dimensions_dict = _build_dimensions_dict_for_exc_gain_loss(entry, active_dimensions) + # update ref in advance entry if voucher_type == "Journal Entry": referenced_row = update_reference_in_journal_entry(entry, doc, do_not_save=False) @@ -466,10 +480,19 @@ def reconcile_against_document(args, skip_ref_details_update_for_pe=False): # n # amount and account in args # referenced_row is used to deduplicate gain/loss journal entry.update({"referenced_row": referenced_row}) - doc.make_exchange_gain_loss_journal([entry]) + doc.make_exchange_gain_loss_journal([entry], dimensions_dict) else: +<<<<<<< HEAD update_reference_in_payment_entry( entry, doc, do_not_save=True, skip_ref_details_update_for_pe=skip_ref_details_update_for_pe +======= + referenced_row = update_reference_in_payment_entry( + entry, + doc, + do_not_save=True, + skip_ref_details_update_for_pe=skip_ref_details_update_for_pe, + dimensions_dict=dimensions_dict, +>>>>>>> c44eb432a5 (refactor: pass dimension values to Gain/Loss journal) ) doc.save(ignore_permissions=True) @@ -618,7 +641,7 @@ def update_reference_in_journal_entry(d, journal_entry, do_not_save=False): def update_reference_in_payment_entry( - d, payment_entry, do_not_save=False, skip_ref_details_update_for_pe=False + d, payment_entry, do_not_save=False, skip_ref_details_update_for_pe=False, dimensions_dict=None ): reference_details = { "reference_doctype": d.against_voucher_type, @@ -666,8 +689,9 @@ def update_reference_in_payment_entry( if not skip_ref_details_update_for_pe: payment_entry.set_missing_ref_details() payment_entry.set_amounts() + payment_entry.make_exchange_gain_loss_journal( - frappe._dict({"difference_posting_date": d.difference_posting_date}) + frappe._dict({"difference_posting_date": d.difference_posting_date}), dimensions_dict ) if not do_not_save: diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 9da71654227..a7baddb9b0a 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1123,7 +1123,9 @@ class AccountsController(TransactionBase): return True return False - def make_exchange_gain_loss_journal(self, args: dict = None) -> None: + def make_exchange_gain_loss_journal( + self, args: dict = None, dimensions_dict: dict = None + ) -> None: """ Make Exchange Gain/Loss journal for Invoices and Payments """ @@ -1178,7 +1180,7 @@ class AccountsController(TransactionBase): self.name, arg.get("referenced_row"), arg.get("cost_center"), - {}, + dimensions_dict, ) frappe.msgprint( _("Exchange Gain/Loss amount has been booked through {0}").format( @@ -1259,7 +1261,7 @@ class AccountsController(TransactionBase): self.name, d.idx, self.cost_center, - {}, + dimensions_dict, ) frappe.msgprint( _("Exchange Gain/Loss amount has been booked through {0}").format( From ec58c309d24c38866481c55310c7748f5b16c281 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 18 Jan 2024 17:08:30 +0530 Subject: [PATCH 183/675] test: dimension inheritance in PE reconciliation (cherry picked from commit 6148fb024b7157d637aa2308e7c856969858468d) --- .../tests/test_accounts_controller.py | 38 ++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/tests/test_accounts_controller.py b/erpnext/controllers/tests/test_accounts_controller.py index a448ad4a572..331599f7877 100644 --- a/erpnext/controllers/tests/test_accounts_controller.py +++ b/erpnext/controllers/tests/test_accounts_controller.py @@ -1342,7 +1342,7 @@ class TestAccountsController(FrappeTestCase): self.assertEqual(len(pr.invoices), 0) self.assertEqual(len(pr.payments), 1) - def test_51_cr_note_should_inherit_dimension_from_payment(self): + def test_51_cr_note_should_inherit_dimension(self): self.setup_dimensions() rate_in_account_currency = 1 @@ -1383,3 +1383,39 @@ class TestAccountsController(FrappeTestCase): [cr_note.department, cr_note.department], frappe.db.get_all("Journal Entry Account", filters={"parent": x.parent}, pluck="department"), ) + + def test_52_dimension_inhertiance_exc_gain_loss(self): + # Sales Invoice in Foreign Currency + self.setup_dimensions() + rate = 80 + rate_in_account_currency = 1 + dimension = "Research & Development" + + si = self.create_sales_invoice(qty=1, rate=rate_in_account_currency, do_not_save=True) + si.department = dimension + si.save().submit() + + pe = self.create_payment_entry(amount=1, source_exc_rate=82).save() + pe.department = dimension + pe = pe.save().submit() + + pr = self.create_payment_reconciliation() + pr.department = dimension + pr.get_unreconciled_entries() + self.assertEqual(len(pr.invoices), 1) + self.assertEqual(len(pr.payments), 1) + invoices = [x.as_dict() for x in pr.invoices] + payments = [x.as_dict() for x in pr.payments] + pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments})) + pr.reconcile() + self.assertEqual(len(pr.invoices), 0) + self.assertEqual(len(pr.payments), 0) + journals = self.get_journals_for(si.doctype, si.name) + self.assertEqual( + [dimension, dimension], + frappe.db.get_all( + "Journal Entry Account", + filters={"parent": ("in", [x.parent for x in journals])}, + pluck="department", + ), + ) From d4828f3cf58fca98651dd2b4904e80ac867e98f8 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 19 Jan 2024 16:44:20 +0530 Subject: [PATCH 184/675] refactor: pass dimensions on advance allocation (cherry picked from commit cbd443a78afbc7c58055881e534a8aa56ca4bea6) --- erpnext/controllers/accounts_controller.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index a7baddb9b0a..c3de4e1241a 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -31,6 +31,7 @@ from frappe.utils import ( import erpnext from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( get_accounting_dimensions, + get_dimensions, ) from erpnext.accounts.doctype.pricing_rule.utils import ( apply_pricing_rule_for_free_items, @@ -1353,7 +1354,13 @@ class AccountsController(TransactionBase): if lst: from erpnext.accounts.utils import reconcile_against_document - reconcile_against_document(lst) + # pass dimension values to utility method + active_dimensions = get_dimensions()[0] + for x in lst: + for dim in active_dimensions: + if self.get(dim.fieldname): + x.update({dim.fieldname: self.get(dim.fieldname)}) + reconcile_against_document(lst, active_dimensions=active_dimensions) def on_cancel(self): from erpnext.accounts.doctype.bank_transaction.bank_transaction import ( From 15db7b8ae492d7a9e1adaf035697368bf2b4bff0 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 19 Jan 2024 16:50:54 +0530 Subject: [PATCH 185/675] test: dimension inheritance on adv allocation (cherry picked from commit fcf4687c523202436234814af3da4c4d84f5eba9) --- .../tests/test_accounts_controller.py | 58 +++++++++++++++++-- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/erpnext/controllers/tests/test_accounts_controller.py b/erpnext/controllers/tests/test_accounts_controller.py index 331599f7877..fad216d5a43 100644 --- a/erpnext/controllers/tests/test_accounts_controller.py +++ b/erpnext/controllers/tests/test_accounts_controller.py @@ -1389,18 +1389,18 @@ class TestAccountsController(FrappeTestCase): self.setup_dimensions() rate = 80 rate_in_account_currency = 1 - dimension = "Research & Development" + dpt = "Research & Development" si = self.create_sales_invoice(qty=1, rate=rate_in_account_currency, do_not_save=True) - si.department = dimension + si.department = dpt si.save().submit() pe = self.create_payment_entry(amount=1, source_exc_rate=82).save() - pe.department = dimension + pe.department = dpt pe = pe.save().submit() pr = self.create_payment_reconciliation() - pr.department = dimension + pr.department = dpt pr.get_unreconciled_entries() self.assertEqual(len(pr.invoices), 1) self.assertEqual(len(pr.payments), 1) @@ -1410,9 +1410,57 @@ class TestAccountsController(FrappeTestCase): pr.reconcile() self.assertEqual(len(pr.invoices), 0) self.assertEqual(len(pr.payments), 0) + + # Exc Gain/Loss journals should inherit dimension from parent journals = self.get_journals_for(si.doctype, si.name) self.assertEqual( - [dimension, dimension], + [dpt, dpt], + frappe.db.get_all( + "Journal Entry Account", + filters={"parent": ("in", [x.parent for x in journals])}, + pluck="department", + ), + ) + + def test_53_dimension_inheritance_on_advance(self): + self.setup_dimensions() + dpt = "Research & Development" + + adv = self.create_payment_entry(amount=1, source_exc_rate=85) + adv.department = dpt + adv.save().submit() + adv.reload() + + # Sales Invoices in different exchange rates + si = self.create_sales_invoice(qty=1, conversion_rate=82, rate=1, do_not_submit=True) + si.department = dpt + advances = si.get_advance_entries() + self.assertEqual(len(advances), 1) + self.assertEqual(advances[0].reference_name, adv.name) + si.append( + "advances", + { + "doctype": "Sales Invoice Advance", + "reference_type": advances[0].reference_type, + "reference_name": advances[0].reference_name, + "reference_row": advances[0].reference_row, + "advance_amount": 1, + "allocated_amount": 1, + "ref_exchange_rate": advances[0].exchange_rate, + "remarks": advances[0].remarks, + }, + ) + si = si.save().submit() + + # Outstanding in both currencies should be '0' + adv.reload() + self.assertEqual(si.outstanding_amount, 0) + self.assert_ledger_outstanding(si.doctype, si.name, 0.0, 0.0) + + # Exc Gain/Loss journals should inherit dimension from parent + journals = self.get_journals_for(si.doctype, si.name) + self.assertEqual( + [dpt, dpt], frappe.db.get_all( "Journal Entry Account", filters={"parent": ("in", [x.parent for x in journals])}, From 51bc225fe550b470fad324c6a656da7300c23969 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 19 Jan 2024 18:00:31 +0530 Subject: [PATCH 186/675] refactor: dynamic dimension filters in pop up (cherry picked from commit f8bbb0619cbbbaace8f54a9f8758c3962ebe4725) --- .../doctype/payment_entry/payment_entry.js | 45 +++++++++---------- .../doctype/payment_entry/payment_entry.py | 8 ++++ .../public/js/utils/dimension_tree_filter.js | 4 ++ 3 files changed, 33 insertions(+), 24 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index b7a4e854ff2..309141fe4c2 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -629,23 +629,9 @@ frappe.ui.form.on('Payment Entry', { frm.events.set_unallocated_amount(frm); }, - get_dimensions: function(frm) { - let result = []; - frappe.call({ - method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.get_dimensions", - async: false, - callback: function(r) { - if(!r.exc) { - result = r.message[0].map(elem => elem.document_type); - } - } - }); - return result; - }, - get_outstanding_invoices_or_orders: function(frm, get_outstanding_invoices, get_orders_to_be_billed) { const today = frappe.datetime.get_today(); - const fields = [ + let fields = [ {fieldtype:"Section Break", label: __("Posting Date")}, {fieldtype:"Date", label: __("From Date"), fieldname:"from_posting_date", default:frappe.datetime.add_days(today, -30)}, @@ -660,18 +646,29 @@ frappe.ui.form.on('Payment Entry', { fieldname:"outstanding_amt_greater_than", default: 0}, {fieldtype:"Column Break"}, {fieldtype:"Float", label: __("Less Than Amount"), fieldname:"outstanding_amt_less_than"}, - {fieldtype:"Section Break"}, - {fieldtype:"Link", label:__("Cost Center"), fieldname:"cost_center", options:"Cost Center", - "get_query": function() { - return { - "filters": {"company": frm.doc.company} - } + ]; + + if (frm.dimension_filters) { + let column_break_insertion_point = Math.ceil((frm.dimension_filters.length)/2); + + fields.push({fieldtype:"Section Break"}); + frm.dimension_filters.map((elem, idx)=>{ + fields.push({ + fieldtype: "Link", + label: elem.document_type == "Cost Center" ? "Cost Center" : elem.label, + options: elem.document_type, + fieldname: elem.fieldname || elem.document_type + }); + if(idx+1 == column_break_insertion_point) { + fields.push({fieldtype:"Column Break"}); } - }, - {fieldtype:"Column Break"}, + }); + } + + fields = fields.concat([ {fieldtype:"Section Break"}, {fieldtype:"Check", label: __("Allocate Payment Amount"), fieldname:"allocate_payment_amount", default:1}, - ]; + ]); let btn_text = ""; diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index a3ebf045fc9..acb6c924408 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -13,6 +13,7 @@ from pypika import Case from pypika.functions import Coalesce, Sum import erpnext +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_dimensions from erpnext.accounts.doctype.bank_account.bank_account import ( get_bank_account_details, get_party_bank_account, @@ -1453,6 +1454,13 @@ def get_outstanding_reference_documents(args): condition += " and cost_center='%s'" % args.get("cost_center") accounting_dimensions_filter.append(ple.cost_center == args.get("cost_center")) + # dynamic dimension filters + active_dimensions = get_dimensions()[0] + for dim in active_dimensions: + if args.get(dim.fieldname): + condition += " and {0}='{1}'".format(dim.fieldname, args.get(dim.fieldname)) + accounting_dimensions_filter.append(ple[dim.fieldname] == args.get(dim.fieldname)) + date_fields_dict = { "posting_date": ["from_posting_date", "to_posting_date"], "due_date": ["from_due_date", "to_due_date"], diff --git a/erpnext/public/js/utils/dimension_tree_filter.js b/erpnext/public/js/utils/dimension_tree_filter.js index 3f70c09f667..27d00bacb88 100644 --- a/erpnext/public/js/utils/dimension_tree_filter.js +++ b/erpnext/public/js/utils/dimension_tree_filter.js @@ -25,6 +25,10 @@ erpnext.accounts.dimensions = { }, setup_filters(frm, doctype) { + if (doctype == 'Payment Entry' && this.accounting_dimensions) { + frm.dimension_filters = this.accounting_dimensions + } + if (this.accounting_dimensions) { this.accounting_dimensions.forEach((dimension) => { frappe.model.with_doctype(dimension['document_type'], () => { From 9a3bde93508ba5765184a61e87860fc537532be9 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 22 Jan 2024 11:59:20 +0530 Subject: [PATCH 187/675] refactor: update dimensions, only if provided (cherry picked from commit ec0f17ca8bd810e41ae73f5a45f304ba38c63d0a) --- erpnext/accounts/utils.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index c207d9338e6..2c0d370c04c 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -2054,7 +2054,8 @@ def create_gain_loss_journal( dr_or_cr + "_in_account_currency": 0, } ) - journal_account.update(dimensions) + if dimensions: + journal_account.update(dimensions) journal_entry.append("accounts", journal_account) journal_account = frappe._dict( @@ -2070,7 +2071,8 @@ def create_gain_loss_journal( reverse_dr_or_cr: abs(exc_gain_loss), } ) - journal_account.update(dimensions) + if dimensions: + journal_account.update(dimensions) journal_entry.append("accounts", journal_account) journal_entry.save() From e70f0f6d8d1665c99d1d2e69b04de6d538b33b32 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 22 Jan 2024 12:06:07 +0530 Subject: [PATCH 188/675] refactor: handle dynamic dimension in order query (cherry picked from commit 7c2cb70387d7dbb7f976d28919ce21f25a0b6acd) --- 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 acb6c924408..66c82be596c 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1688,6 +1688,12 @@ def get_orders_to_be_billed( if doc and hasattr(doc, "cost_center") and doc.cost_center: condition = " and cost_center='%s'" % cost_center + # dynamic dimension filters + active_dimensions = get_dimensions()[0] + for dim in active_dimensions: + if filters.get(dim.fieldname): + condition += " and {0}='{1}'".format(dim.fieldname, filters.get(dim.fieldname)) + if party_account_currency == company_currency: grand_total_field = "base_grand_total" rounded_total_field = "base_rounded_total" From c759406ebbe225fc4f1bb7d7b4050e0386055cb8 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Tue, 23 Jan 2024 14:57:23 +0100 Subject: [PATCH 189/675] fix(Batch): reload doc after splitting to show updated qty --- erpnext/stock/doctype/batch/batch.js | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/erpnext/stock/doctype/batch/batch.js b/erpnext/stock/doctype/batch/batch.js index 3b07e4e80c1..3b5b7b03cc5 100644 --- a/erpnext/stock/doctype/batch/batch.js +++ b/erpnext/stock/doctype/batch/batch.js @@ -128,19 +128,16 @@ frappe.ui.form.on('Batch', { fieldtype: 'Data', }], (data) => { - frappe.call({ - method: 'erpnext.stock.doctype.batch.batch.split_batch', - args: { + frappe.xcall( + 'erpnext.stock.doctype.batch.batch.split_batch', + { item_code: frm.doc.item, batch_no: frm.doc.name, qty: data.qty, warehouse: $btn.attr('data-warehouse'), new_batch_id: data.new_batch_id - }, - callback: (r) => { - frm.refresh(); - }, - }); + } + ).then(() => frm.reload_doc()); }, __('Split Batch'), __('Split') From dd3b77ae288b53736583575cf687e1a7cc95d64d Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Tue, 23 Jan 2024 15:02:42 +0100 Subject: [PATCH 190/675] refactor(Batch): use const instead of var --- erpnext/stock/doctype/batch/batch.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/batch/batch.js b/erpnext/stock/doctype/batch/batch.js index 3b5b7b03cc5..38d3d1cc376 100644 --- a/erpnext/stock/doctype/batch/batch.js +++ b/erpnext/stock/doctype/batch/batch.js @@ -52,7 +52,7 @@ frappe.ui.form.on('Batch', { // sort by qty r.message.sort(function(a, b) { a.qty > b.qty ? 1 : -1 }); - var rows = $('
    ').appendTo(section); + const rows = $('
    ').appendTo(section); // show (r.message || []).forEach(function(d) { @@ -76,7 +76,7 @@ frappe.ui.form.on('Batch', { // move - ask for target warehouse and make stock entry rows.find('.btn-move').on('click', function() { - var $btn = $(this); + const $btn = $(this); const fields = [ { fieldname: 'to_warehouse', @@ -115,7 +115,7 @@ frappe.ui.form.on('Batch', { // split - ask for new qty and batch ID (optional) // and make stock entry via batch.batch_split rows.find('.btn-split').on('click', function() { - var $btn = $(this); + const $btn = $(this); frappe.prompt([{ fieldname: 'qty', label: __('New Batch Qty'), From b310a55727e16ce511173eef1acd6db49f720227 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 24 Jan 2024 10:29:55 +0530 Subject: [PATCH 191/675] fix: not able to edit address through portal (cherry picked from commit b046d980ad83b906d46515082bd32038daadcb3c) --- erpnext/utilities/web_form/addresses/addresses.json | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/erpnext/utilities/web_form/addresses/addresses.json b/erpnext/utilities/web_form/addresses/addresses.json index 2f5e180731a..4e2d8e36c2c 100644 --- a/erpnext/utilities/web_form/addresses/addresses.json +++ b/erpnext/utilities/web_form/addresses/addresses.json @@ -8,26 +8,29 @@ "allow_print": 0, "amount": 0.0, "amount_based_on_field": 0, + "anonymous": 0, + "apply_document_permissions": 1, + "condition_json": "[]", "creation": "2016-06-24 15:50:33.196990", "doc_type": "Address", "docstatus": 0, "doctype": "Web Form", "idx": 0, "is_standard": 1, + "list_columns": [], + "list_title": "", "login_required": 1, "max_attachment_size": 0, - "modified": "2019-10-15 06:55:30.405119", - "modified_by": "Administrator", + "modified": "2024-01-24 10:28:35.026064", + "modified_by": "rohitw1991@gmail.com", "module": "Utilities", "name": "addresses", "owner": "Administrator", "published": 1, "route": "address", - "route_to_success_link": 0, "show_attachments": 0, - "show_in_grid": 0, + "show_list": 1, "show_sidebar": 0, - "sidebar_items": [], "success_url": "/addresses", "title": "Address", "web_form_fields": [ From e3fdb6f55cabd34ee97b47b7f50c4aabac59ac06 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 24 Jan 2024 12:15:55 +0530 Subject: [PATCH 192/675] fix: not able to edit / change address from portal --- .../templates/includes/cart/address_card.html | 4 +- .../includes/cart/address_picker_card.html | 2 +- .../templates/includes/cart/cart_address.html | 77 +++++++++++++++++++ erpnext/templates/pages/cart.js | 73 ------------------ 4 files changed, 80 insertions(+), 76 deletions(-) diff --git a/erpnext/templates/includes/cart/address_card.html b/erpnext/templates/includes/cart/address_card.html index 830ed649f5f..a4f10cc643e 100644 --- a/erpnext/templates/includes/cart/address_card.html +++ b/erpnext/templates/includes/cart/address_card.html @@ -7,11 +7,11 @@
    {{ address.display }}
    - + {{ _('Edit') }} - + \ No newline at end of file diff --git a/erpnext/templates/includes/cart/address_picker_card.html b/erpnext/templates/includes/cart/address_picker_card.html index 646210e65f1..dd9adad66f2 100644 --- a/erpnext/templates/includes/cart/address_picker_card.html +++ b/erpnext/templates/includes/cart/address_picker_card.html @@ -7,6 +7,6 @@

    {{ address.display }}

    - {{ _('Edit') }} + {{ _('Edit') }} diff --git a/erpnext/templates/includes/cart/cart_address.html b/erpnext/templates/includes/cart/cart_address.html index cf600173731..525a56af1f0 100644 --- a/erpnext/templates/includes/cart/cart_address.html +++ b/erpnext/templates/includes/cart/cart_address.html @@ -186,3 +186,80 @@ frappe.ready(() => { } }); + + \ No newline at end of file diff --git a/erpnext/templates/pages/cart.js b/erpnext/templates/pages/cart.js index fb2d159dcf9..1cf8a1a28e8 100644 --- a/erpnext/templates/pages/cart.js +++ b/erpnext/templates/pages/cart.js @@ -12,7 +12,6 @@ $.extend(shopping_cart, { }, bind_events: function() { - shopping_cart.bind_address_picker_dialog(); shopping_cart.bind_place_order(); shopping_cart.bind_request_quotation(); shopping_cart.bind_change_qty(); @@ -21,78 +20,6 @@ $.extend(shopping_cart, { shopping_cart.bind_coupon_code(); }, - bind_address_picker_dialog: function() { - const d = this.get_update_address_dialog(); - this.parent.find('.btn-change-address').on('click', (e) => { - const type = $(e.currentTarget).parents('.address-container').attr('data-address-type'); - $(d.get_field('address_picker').wrapper).html( - this.get_address_template(type) - ); - d.show(); - }); - }, - - get_update_address_dialog() { - let d = new frappe.ui.Dialog({ - title: "Select Address", - fields: [{ - 'fieldtype': 'HTML', - 'fieldname': 'address_picker', - }], - primary_action_label: __('Set Address'), - primary_action: () => { - const $card = d.$wrapper.find('.address-card.active'); - const address_type = $card.closest('[data-address-type]').attr('data-address-type'); - const address_name = $card.closest('[data-address-name]').attr('data-address-name'); - frappe.call({ - type: "POST", - method: "erpnext.e_commerce.shopping_cart.cart.update_cart_address", - freeze: true, - args: { - address_type, - address_name - }, - callback: function(r) { - d.hide(); - if (!r.exc) { - $(".cart-tax-items").html(r.message.total); - shopping_cart.parent.find( - `.address-container[data-address-type="${address_type}"]` - ).html(r.message.address); - } - } - }); - } - }); - - return d; - }, - - get_address_template(type) { - return { - shipping: `
    -
    - {% for address in shipping_addresses %} -
    - {% include "templates/includes/cart/address_picker_card.html" %} -
    - {% endfor %} -
    -
    `, - billing: `
    -
    - {% for address in billing_addresses %} -
    - {% include "templates/includes/cart/address_picker_card.html" %} -
    - {% endfor %} -
    -
    `, - }[type]; - }, - bind_place_order: function() { $(".btn-place-order").on("click", function() { shopping_cart.place_order(this); From 780c069268084e6cb1b4bacde8cad98485e925d1 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 24 Jan 2024 11:45:15 +0530 Subject: [PATCH 193/675] fix: email list for auto reorder material request (cherry picked from commit 764f3422a0826e22d116616daa3849d02cc1e117) --- .../material_request/test_material_request.py | 56 +++++++++++++++++ erpnext/stock/reorder_item.py | 61 +++++++++++++++---- 2 files changed, 106 insertions(+), 11 deletions(-) diff --git a/erpnext/stock/doctype/material_request/test_material_request.py b/erpnext/stock/doctype/material_request/test_material_request.py index 03f58c664d3..b5817c750e2 100644 --- a/erpnext/stock/doctype/material_request/test_material_request.py +++ b/erpnext/stock/doctype/material_request/test_material_request.py @@ -762,6 +762,62 @@ class TestMaterialRequest(FrappeTestCase): self.assertEqual(mr.per_ordered, 100) self.assertEqual(existing_requested_qty, current_requested_qty) + def test_auto_email_users_with_company_user_permissions(self): + from erpnext.stock.reorder_item import get_email_list + + comapnywise_users = { + "_Test Company": "test_auto_email_@example.com", + "_Test Company 1": "test_auto_email_1@example.com", + } + + permissions = [] + + for company, user in comapnywise_users.items(): + if not frappe.db.exists("User", user): + frappe.get_doc( + { + "doctype": "User", + "email": user, + "first_name": user, + "send_notifications": 0, + "enabled": 1, + "user_type": "System User", + "roles": [{"role": "Purchase Manager"}], + } + ).insert(ignore_permissions=True) + + if not frappe.db.exists( + "User Permission", {"user": user, "allow": "Company", "for_value": company} + ): + perm_doc = frappe.get_doc( + { + "doctype": "User Permission", + "user": user, + "allow": "Company", + "for_value": company, + "apply_to_all_doctypes": 1, + } + ).insert(ignore_permissions=True) + + permissions.append(perm_doc) + + comapnywise_mr_list = frappe._dict({}) + mr1 = make_material_request() + comapnywise_mr_list.setdefault(mr1.company, []).append(mr1.name) + + mr2 = make_material_request( + company="_Test Company 1", warehouse="Stores - _TC1", cost_center="Main - _TC1" + ) + comapnywise_mr_list.setdefault(mr2.company, []).append(mr2.name) + + for company, mr_list in comapnywise_mr_list.items(): + emails = get_email_list(company) + + self.assertTrue(comapnywise_users[company] in emails) + + for perm in permissions: + perm.delete() + def get_in_transit_warehouse(company): if not frappe.db.exists("Warehouse Type", "Transit"): diff --git a/erpnext/stock/reorder_item.py b/erpnext/stock/reorder_item.py index 3c429a2c22d..e172cecb236 100644 --- a/erpnext/stock/reorder_item.py +++ b/erpnext/stock/reorder_item.py @@ -145,6 +145,7 @@ def create_material_request(material_requests): mr.log_error("Unable to create material request") + company_wise_mr = frappe._dict({}) for request_type in material_requests: for company in material_requests[request_type]: try: @@ -206,17 +207,19 @@ def create_material_request(material_requests): mr.submit() mr_list.append(mr) + company_wise_mr.setdefault(company, []).append(mr) + except Exception: _log_exception(mr) - if mr_list: + if company_wise_mr: if getattr(frappe.local, "reorder_email_notify", None) is None: frappe.local.reorder_email_notify = cint( frappe.db.get_value("Stock Settings", None, "reorder_email_notify") ) if frappe.local.reorder_email_notify: - send_email_notification(mr_list) + send_email_notification(company_wise_mr) if exceptions_list: notify_errors(exceptions_list) @@ -224,20 +227,56 @@ def create_material_request(material_requests): return mr_list -def send_email_notification(mr_list): +def send_email_notification(company_wise_mr): """Notify user about auto creation of indent""" - email_list = frappe.db.sql_list( - """select distinct r.parent - from `tabHas Role` r, tabUser p - where p.name = r.parent and p.enabled = 1 and p.docstatus < 2 - and r.role in ('Purchase Manager','Stock Manager') - and p.name not in ('Administrator', 'All', 'Guest')""" + for company, mr_list in company_wise_mr.items(): + email_list = get_email_list(company) + + if not email_list: + continue + + msg = frappe.render_template("templates/emails/reorder_item.html", {"mr_list": mr_list}) + + frappe.sendmail( + recipients=email_list, subject=_("Auto Material Requests Generated"), message=msg + ) + + +def get_email_list(company): + users = get_comapny_wise_users(company) + user_table = frappe.qb.DocType("User") + role_table = frappe.qb.DocType("Has Role") + + query = ( + frappe.qb.from_(user_table) + .inner_join(role_table) + .on(user_table.name == role_table.parent) + .select(user_table.email) + .where( + (role_table.role.isin(["Purchase Manager", "Stock Manager"])) + & (user_table.name.notin(["Administrator", "All", "Guest"])) + & (user_table.enabled == 1) + & (user_table.docstatus < 2) + ) ) - msg = frappe.render_template("templates/emails/reorder_item.html", {"mr_list": mr_list}) + if users: + query = query.where(user_table.name.isin(users)) - frappe.sendmail(recipients=email_list, subject=_("Auto Material Requests Generated"), message=msg) + emails = query.run(as_dict=True) + + return list(set([email.email for email in emails])) + + +def get_comapny_wise_users(company): + users = frappe.get_all( + "User Permission", + filters={"allow": "Company", "for_value": company, "apply_to_all_doctypes": 1}, + fields=["user"], + ) + + return [user.user for user in users] def notify_errors(exceptions_list): From 2c431f394eea0a496c12dafc28f96377ac4d37cb Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 24 Jan 2024 16:01:17 +0530 Subject: [PATCH 194/675] chore: resolve conflicts --- erpnext/accounts/utils.py | 8 ---- erpnext/controllers/accounts_controller.py | 45 ---------------------- 2 files changed, 53 deletions(-) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 2c0d370c04c..b3e9996b515 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -482,17 +482,12 @@ def reconcile_against_document( entry.update({"referenced_row": referenced_row}) doc.make_exchange_gain_loss_journal([entry], dimensions_dict) else: -<<<<<<< HEAD - update_reference_in_payment_entry( - entry, doc, do_not_save=True, skip_ref_details_update_for_pe=skip_ref_details_update_for_pe -======= referenced_row = update_reference_in_payment_entry( entry, doc, do_not_save=True, skip_ref_details_update_for_pe=skip_ref_details_update_for_pe, dimensions_dict=dimensions_dict, ->>>>>>> c44eb432a5 (refactor: pass dimension values to Gain/Loss journal) ) doc.save(ignore_permissions=True) @@ -653,11 +648,8 @@ def update_reference_in_payment_entry( if d.difference_amount is not None else payment_entry.get_exchange_rate(), "exchange_gain_loss": d.difference_amount, -<<<<<<< HEAD -======= "account": d.account, "dimensions": d.dimensions, ->>>>>>> 5dc22e1811 (refactor: pass dimension details to query) } if d.voucher_detail_no: diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index c3de4e1241a..cd7b7b69529 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -7,11 +7,6 @@ import json import frappe from frappe import _, bold, qb, throw from frappe.model.workflow import get_workflow_name, is_transition_condition_satisfied -<<<<<<< HEAD -======= -from frappe.query_builder import Criterion -from frappe.query_builder.custom import ConstantColumn ->>>>>>> ff60ec85b8 (refactor: pass dimension filters to query) from frappe.query_builder.functions import Abs, Sum from frappe.utils import ( add_days, @@ -2558,7 +2553,6 @@ def get_advance_payment_entries( payment_entries_against_order, unallocated_payment_entries = [], [] limit_cond = "limit %s" % limit if limit else "" -<<<<<<< HEAD if order_list or against_all_orders: if order_list: reference_condition = " and t2.reference_name in ({0})".format( @@ -2567,45 +2561,6 @@ def get_advance_payment_entries( else: reference_condition = "" order_list = [] -======= - if payment_type == "Receive": - q = q.select((payment_entry.source_exchange_rate).as_("exchange_rate")) - else: - q = q.select((payment_entry.target_exchange_rate).as_("exchange_rate")) - - if condition: - # conditions should be built as an array and passed as Criterion - common_filter_conditions = [] - - common_filter_conditions.append(payment_entry.company == condition["company"]) - if condition.get("name", None): - common_filter_conditions.append(payment_entry.name.like(f"%{condition.get('name')}%")) - - if condition.get("from_payment_date"): - common_filter_conditions.append(payment_entry.posting_date.gte(condition["from_payment_date"])) - - if condition.get("to_payment_date"): - common_filter_conditions.append(payment_entry.posting_date.lte(condition["to_payment_date"])) - - if condition.get("get_payments") == True: - if condition.get("cost_center"): - common_filter_conditions.append(payment_entry.cost_center == condition["cost_center"]) - - if condition.get("accounting_dimensions"): - for field, val in condition.get("accounting_dimensions").items(): - common_filter_conditions.append(payment_entry[field] == val) - - if condition.get("minimum_payment_amount"): - common_filter_conditions.append( - payment_entry.unallocated_amount.gte(condition["minimum_payment_amount"]) - ) - - if condition.get("maximum_payment_amount"): - common_filter_conditions.append( - payment_entry.unallocated_amount.lte(condition["maximum_payment_amount"]) - ) - q = q.where(Criterion.all(common_filter_conditions)) ->>>>>>> ff60ec85b8 (refactor: pass dimension filters to query) payment_name_filter = "" if payment_name: From 295395918c3515f1ce6757a9ababab42a87d4800 Mon Sep 17 00:00:00 2001 From: Florian HENRY Date: Thu, 11 Jan 2024 16:00:48 +0100 Subject: [PATCH 195/675] fix: Payment Terms Status for Sales Order report should show all payment terms from order not only this comming from template (cherry picked from commit 6c8f52b26f0601135ec0dd1be94835951936cd11) --- .../payment_terms_status_for_sales_order.py | 1 - 1 file changed, 1 deletion(-) 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 3682c5fd62e..c6e46475387 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 @@ -210,7 +210,6 @@ def get_so_with_invoices(filters): .where( (so.docstatus == 1) & (so.status.isin(["To Deliver and Bill", "To Bill"])) - & (so.payment_terms_template != "NULL") & (so.company == conditions.company) & (so.transaction_date[conditions.start_date : conditions.end_date]) ) From 941f8824e5f31f42b0f54182d790e8178c9511ab Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 25 Jan 2024 11:40:44 +0530 Subject: [PATCH 196/675] fix: linter issue --- erpnext/assets/doctype/asset/asset.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index b90bf19b429..3fcb9720dd1 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -392,7 +392,9 @@ class Asset(AccountsController): if skip_row: continue - schedule_date = add_months(finance_book.depreciation_start_date, n * cint(finance_book.frequency_of_depreciation)) + schedule_date = add_months( + finance_book.depreciation_start_date, n * cint(finance_book.frequency_of_depreciation) + ) if not current_fiscal_year_end_date: current_fiscal_year_end_date = get_fiscal_year(finance_book.depreciation_start_date)[2] elif getdate(schedule_date) > getdate(current_fiscal_year_end_date): From 9a5995a3e5f1ff806855f8b88a4dc0b0a3ce73a8 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 25 Jan 2024 11:27:14 +0530 Subject: [PATCH 197/675] fix: do not delete batches implicitly --- erpnext/controllers/stock_controller.py | 5 ----- .../purchase_receipt/test_purchase_receipt.py | 14 +++++++++++++- .../stock/doctype/stock_entry/test_stock_entry.py | 2 +- .../test_stock_reconciliation.py | 2 +- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 403f71be5eb..5768026df2b 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -402,11 +402,6 @@ class StockController(AccountsController): d.batch_no = None d.db_set("batch_no", None) - for data in frappe.get_all( - "Batch", {"reference_name": self.name, "reference_doctype": self.doctype} - ): - frappe.delete_doc("Batch", data.name) - def get_sl_entries(self, d, args): sl_dict = 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 b444e864e3d..3a81c4a5462 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -193,7 +193,6 @@ class TestPurchaseReceipt(FrappeTestCase): batch_no = pr.items[0].batch_no pr.cancel() - self.assertFalse(frappe.db.get_value("Batch", {"item": item.name, "reference_name": pr.name})) self.assertFalse(frappe.db.get_all("Serial No", {"batch_no": batch_no})) def test_purchase_receipt_gl_entry(self): @@ -2172,6 +2171,19 @@ class TestPurchaseReceipt(FrappeTestCase): pr_doc.reload() self.assertFalse(pr_doc.items[0].from_warehouse) + def test_do_not_delete_batch_implicitly(self): + item = make_item( + "_Test Item With Delete Batch", + {"has_batch_no": 1, "create_new_batch": 1, "batch_number_series": "TBWDB.#####"}, + ).name + + pr = make_purchase_receipt(item_code=item, qty=10, rate=100) + batch_no = pr.items[0].batch_no + self.assertTrue(frappe.db.exists("Batch", batch_no)) + + pr.cancel() + self.assertTrue(frappe.db.exists("Batch", batch_no)) + 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_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index c4f26c4baaf..8afe23a1d7a 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -737,7 +737,7 @@ class TestStockEntry(FrappeTestCase): self.assertEqual(batch_in_serial_no, None) self.assertEqual(frappe.db.get_value("Serial No", serial_no, "status"), "Inactive") - self.assertEqual(frappe.db.exists("Batch", batch_no), None) + self.assertTrue(frappe.db.exists("Batch", batch_no)) def test_serial_batch_item_qty_deduction(self): """ diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py index 19c04afe909..9499566f341 100644 --- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py @@ -312,7 +312,7 @@ class TestStockReconciliation(FrappeTestCase, StockTestMixin): sr.cancel() self.assertEqual(frappe.db.get_value("Serial No", serial_nos[0], "status"), "Inactive") - self.assertEqual(frappe.db.exists("Batch", batch_no), None) + self.assertTrue(frappe.db.exists("Batch", batch_no)) def test_stock_reco_balance_qty_for_serial_and_batch_item(self): from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry From 1e32c6207e2d344b2ba43f8be42f952340eacba5 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 25 Jan 2024 12:51:48 +0530 Subject: [PATCH 198/675] fix: default enable closing stock balance (backport #39551) (#39553) fix: default enable closing stock balance (#39551) (cherry picked from commit d1fb90edffbbbd48722fddda1ed3d9144783b774) Co-authored-by: rohitwaghchaure --- erpnext/stock/report/stock_balance/stock_balance.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/report/stock_balance/stock_balance.js b/erpnext/stock/report/stock_balance/stock_balance.js index 6de5f00ece8..fe6e83eddad 100644 --- a/erpnext/stock/report/stock_balance/stock_balance.js +++ b/erpnext/stock/report/stock_balance/stock_balance.js @@ -99,7 +99,7 @@ frappe.query_reports["Stock Balance"] = { "fieldname": 'ignore_closing_balance', "label": __('Ignore Closing Balance'), "fieldtype": 'Check', - "default": 1 + "default": 0 }, ], From 1dacb794415cdf138be3707dd95cabe5bff3e9f2 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 22 Jan 2024 16:58:31 +0530 Subject: [PATCH 199/675] fix: fetch correct quantity and amount for grouped asset (cherry picked from commit 06f48c678be51c85a98bd0c6175fa210a47788f0) --- erpnext/assets/doctype/asset/asset.js | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index f711577abbe..120ca44cd23 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -519,16 +519,16 @@ frappe.ui.form.on('Asset', { indicator: 'red' }); } - var is_grouped_asset = frappe.db.get_value('Item', item.item_code, 'is_grouped_asset'); - var asset_quantity = is_grouped_asset ? item.qty : 1; - var purchase_amount = flt(item.valuation_rate * asset_quantity, precision('gross_purchase_amount')); - - frm.set_value('gross_purchase_amount', purchase_amount); - frm.set_value('purchase_receipt_amount', purchase_amount); - frm.set_value('asset_quantity', asset_quantity); - frm.set_value('cost_center', item.cost_center || purchase_doc.cost_center); - if(item.asset_location) { frm.set_value('location', item.asset_location); } + frappe.db.get_value('Item', item.item_code, 'is_grouped_asset', (r) => { + var asset_quantity = r.is_grouped_asset ? item.qty : 1; + var purchase_amount = flt(item.valuation_rate * asset_quantity, precision('gross_purchase_amount')); + frm.set_value('gross_purchase_amount', purchase_amount); + frm.set_value('purchase_receipt_amount', purchase_amount); + frm.set_value('asset_quantity', asset_quantity); + frm.set_value('cost_center', item.cost_center || purchase_doc.cost_center); + if(item.asset_location) { frm.set_value('location', item.asset_location); } + }); }, set_depreciation_rate: function(frm, row) { From c26f7bbed0160a533c782d44410001c5fcab9913 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 25 Jan 2024 17:22:15 +0530 Subject: [PATCH 200/675] fix: incorrect amount in the material request item (backport #39567) (#39568) fix: incorrect amount in the material request item (#39567) fix: incoorect amount in the material request (cherry picked from commit 2bdfdeeb9a5f2fd98cf67fc983a920790e56e1e1) Co-authored-by: rohitwaghchaure --- erpnext/stock/doctype/material_request/material_request.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index 5922af25879..e501fbba0e4 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -514,6 +514,13 @@ erpnext.buying.MaterialRequestController = class MaterialRequestController exten schedule_date() { set_schedule_date(this.frm); } + + qty(doc, cdt, cdn) { + var row = frappe.get_doc(cdt, cdn); + row.amount = flt(row.qty) * flt(row.rate); + frappe.model.set_value(cdt, cdn, "amount", row.amount); + refresh_field("amount", row.name, row.parentfield); + } }; // for backward compatibility: combine new and previous states From 9fd1692db2c1cf45b34273bdd984c37bab13d403 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Thu, 25 Jan 2024 19:08:39 +0530 Subject: [PATCH 201/675] fix: RM valuation rate in SCR (#39541) --- .../controllers/subcontracting_controller.py | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py index 772fbd54512..5c069b1e275 100644 --- a/erpnext/controllers/subcontracting_controller.py +++ b/erpnext/controllers/subcontracting_controller.py @@ -343,7 +343,7 @@ class SubcontractingController(StockController): i += 1 def __get_materials_from_bom(self, item_code, bom_no, exploded_item=0): - doctype = "BOM Item" if not exploded_item else "BOM Explosion Item" + doctype = "BOM Explosion Item" if exploded_item else "BOM Item" fields = [f"`tab{doctype}`.`stock_qty` / `tabBOM`.`quantity` as qty_consumed_per_unit"] alias_dict = { @@ -447,6 +447,16 @@ class SubcontractingController(StockController): rm_obj = self.append(self.raw_material_table, bom_item) rm_obj.reference_name = item_row.name + if self.doctype == self.subcontract_data.order_doctype: + rm_obj.required_qty = qty + rm_obj.amount = rm_obj.required_qty * rm_obj.rate + else: + rm_obj.consumed_qty = 0 + setattr( + rm_obj, self.subcontract_data.order_field, item_row.get(self.subcontract_data.order_field) + ) + self.__set_batch_nos(bom_item, item_row, rm_obj, qty) + if self.doctype == "Subcontracting Receipt": args = frappe._dict( { @@ -465,16 +475,6 @@ class SubcontractingController(StockController): ) rm_obj.rate = get_incoming_rate(args) - if self.doctype == self.subcontract_data.order_doctype: - rm_obj.required_qty = qty - rm_obj.amount = rm_obj.required_qty * rm_obj.rate - else: - rm_obj.consumed_qty = 0 - setattr( - rm_obj, self.subcontract_data.order_field, item_row.get(self.subcontract_data.order_field) - ) - self.__set_batch_nos(bom_item, item_row, rm_obj, qty) - def __get_qty_based_on_material_transfer(self, item_row, transfer_item): key = (item_row.item_code, item_row.get(self.subcontract_data.order_field)) From a072cfbf3fb8a8f704039072316bf433874a32f1 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Tue, 23 Jan 2024 13:11:06 +0100 Subject: [PATCH 202/675] refactor(Sales Invoice): set account for mode of payment (cherry picked from commit 3815f07c33d9c84230304d53a735d8f580f3288e) --- .../accounts/doctype/pos_invoice/pos_invoice.py | 7 ------- .../doctype/sales_invoice/sales_invoice.py | 17 +++++++---------- 2 files changed, 7 insertions(+), 17 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index 2405aba38a3..383b9dab24a 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -13,7 +13,6 @@ from erpnext.accounts.doctype.loyalty_program.loyalty_program import validate_lo from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request from erpnext.accounts.doctype.sales_invoice.sales_invoice import ( SalesInvoice, - get_bank_cash_account, get_mode_of_payment_info, update_multi_mode_option, ) @@ -52,7 +51,6 @@ class POSInvoice(SalesInvoice): self.validate_stock_availablility() self.validate_return_items_qty() self.set_status() - self.set_account_for_mode_of_payment() self.validate_pos() self.validate_payment_amount() self.validate_loyalty_transaction() @@ -584,11 +582,6 @@ class POSInvoice(SalesInvoice): update_multi_mode_option(self, pos_profile) self.paid_amount = 0 - def set_account_for_mode_of_payment(self): - for pay in self.payments: - if not pay.account: - pay.account = get_bank_cash_account(pay.mode_of_payment, self.company).get("account") - @frappe.whitelist() def create_payment_request(self): for pay in self.payments: diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index fc37e5ec67f..19f52ad1e77 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -245,7 +245,8 @@ class SalesInvoice(SellingController): self.calculate_taxes_and_totals() def before_save(self): - set_account_for_mode_of_payment(self) + self.set_account_for_mode_of_payment() + self.set_paid_amount() def on_submit(self): self.validate_pos_paid_amount() @@ -538,9 +539,6 @@ class SalesInvoice(SellingController): ): data.sales_invoice = sales_invoice - def on_update(self): - self.set_paid_amount() - def on_update_after_submit(self): if hasattr(self, "repost_required"): fields_to_check = [ @@ -571,6 +569,11 @@ class SalesInvoice(SellingController): self.paid_amount = paid_amount self.base_paid_amount = base_paid_amount + def set_account_for_mode_of_payment(self): + for payment in self.payments: + if not payment.account: + payment.account = get_bank_cash_account(payment.mode_of_payment, self.company).get("account") + def validate_time_sheets_are_submitted(self): for data in self.timesheets: if data.time_sheet: @@ -1943,12 +1946,6 @@ def make_sales_return(source_name, target_doc=None): return make_return_doc("Sales Invoice", source_name, target_doc) -def set_account_for_mode_of_payment(self): - for data in self.payments: - if not data.account: - data.account = get_bank_cash_account(data.mode_of_payment, self.company).get("account") - - def get_inter_company_details(doc, doctype): if doctype in ["Sales Invoice", "Sales Order", "Delivery Note"]: parties = frappe.db.get_all( From 47c591ccf16565ad20ac88d7ba7a45bd6c990690 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Fri, 26 Jan 2024 14:25:05 +0530 Subject: [PATCH 203/675] fix(ecom): do not create a new contact if a contact already exists (#39290) --- erpnext/e_commerce/shopping_cart/cart.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/erpnext/e_commerce/shopping_cart/cart.py b/erpnext/e_commerce/shopping_cart/cart.py index 030b439ae4b..029506b7a1e 100644 --- a/erpnext/e_commerce/shopping_cart/cart.py +++ b/erpnext/e_commerce/shopping_cart/cart.py @@ -501,6 +501,7 @@ def get_party(user=None): contact_name = get_contact_name(user) party = None + contact = None if contact_name: contact = frappe.get_doc("Contact", contact_name) if contact.links: @@ -538,11 +539,15 @@ def get_party(user=None): customer.flags.ignore_mandatory = True customer.insert(ignore_permissions=True) - contact = frappe.new_doc("Contact") - contact.update({"first_name": fullname, "email_ids": [{"email_id": user, "is_primary": 1}]}) + if not contact: + contact = frappe.new_doc("Contact") + contact.update({"first_name": fullname, "email_ids": [{"email_id": user, "is_primary": 1}]}) + contact.insert(ignore_permissions=True) + contact.reload() + contact.append("links", dict(link_doctype="Customer", link_name=customer.name)) contact.flags.ignore_mandatory = True - contact.insert(ignore_permissions=True) + contact.save(ignore_permissions=True) return customer From e729972987ddcb2e78edad2ae729b765fda12266 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sat, 27 Jan 2024 10:12:37 +0530 Subject: [PATCH 204/675] fix: incorrect amount in the material request item (backport #39567) (backport #39568) (#39586) fix: incorrect amount in the material request item (backport #39567) (#39568) fix: incorrect amount in the material request item (#39567) fix: incoorect amount in the material request (cherry picked from commit 2bdfdeeb9a5f2fd98cf67fc983a920790e56e1e1) Co-authored-by: rohitwaghchaure (cherry picked from commit c26f7bbed0160a533c782d44410001c5fcab9913) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- erpnext/stock/doctype/material_request/material_request.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index 5922af25879..e501fbba0e4 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -514,6 +514,13 @@ erpnext.buying.MaterialRequestController = class MaterialRequestController exten schedule_date() { set_schedule_date(this.frm); } + + qty(doc, cdt, cdn) { + var row = frappe.get_doc(cdt, cdn); + row.amount = flt(row.qty) * flt(row.rate); + frappe.model.set_value(cdt, cdn, "amount", row.amount); + refresh_field("amount", row.name, row.parentfield); + } }; // for backward compatibility: combine new and previous states From 33b21a54f7cf6990a965a91736b58c2bef99e65f Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Sat, 27 Jan 2024 04:43:52 +0000 Subject: [PATCH 205/675] chore(release): Bumped to Version 14.61.2 ## [14.61.2](https://github.com/frappe/erpnext/compare/v14.61.1...v14.61.2) (2024-01-27) ### Bug Fixes * incorrect amount in the material request item (backport [#39567](https://github.com/frappe/erpnext/issues/39567)) (backport [#39568](https://github.com/frappe/erpnext/issues/39568)) ([#39586](https://github.com/frappe/erpnext/issues/39586)) ([e729972](https://github.com/frappe/erpnext/commit/e729972987ddcb2e78edad2ae729b765fda12266)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index c4d42e2b11c..15f872b033d 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.61.1" +__version__ = "14.61.2" def get_default_company(user=None): From 4626ea79ef2cf1b0b0a13874f1e548bd51ceb0f2 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 27 Jan 2024 09:55:26 +0530 Subject: [PATCH 206/675] refactor: do currency conversion on future amount columns (cherry picked from commit 0de4197c886f6dfa500c0b3f1869c13cc3807aaf) --- .../accounts_receivable.py | 37 +++++++++++++++---- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 8f7b99aef16..9219cc5e383 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -5,7 +5,7 @@ from collections import OrderedDict import frappe -from frappe import _, qb, scrub +from frappe import _, qb, query_builder, scrub from frappe.query_builder import Criterion from frappe.query_builder.functions import Date, Substring, Sum from frappe.utils import cint, cstr, flt, getdate, nowdate @@ -578,6 +578,8 @@ class ReceivablePayableReport(object): def get_future_payments_from_payment_entry(self): pe = frappe.qb.DocType("Payment Entry") pe_ref = frappe.qb.DocType("Payment Entry Reference") + ifelse = query_builder.CustomFunction("IF", ["condition", "then", "else"]) + return ( frappe.qb.from_(pe) .inner_join(pe_ref) @@ -589,6 +591,11 @@ class ReceivablePayableReport(object): (pe.posting_date).as_("future_date"), (pe_ref.allocated_amount).as_("future_amount"), (pe.reference_no).as_("future_ref"), + ifelse( + pe.payment_type == "Receive", + pe.source_exchange_rate * pe_ref.allocated_amount, + pe.target_exchange_rate * pe_ref.allocated_amount, + ).as_("future_amount_in_base_currency"), ) .where( (pe.docstatus < 2) @@ -625,13 +632,24 @@ class ReceivablePayableReport(object): query = query.select( Sum(jea.debit_in_account_currency - jea.credit_in_account_currency).as_("future_amount") ) + query = query.select(Sum(jea.debit - jea.credit).as_("future_amount_in_base_currency")) else: query = query.select( Sum(jea.credit_in_account_currency - jea.debit_in_account_currency).as_("future_amount") ) + query = query.select(Sum(jea.credit - jea.debit).as_("future_amount_in_base_currency")) else: query = query.select( - Sum(jea.debit if self.account_type == "Payable" else jea.credit).as_("future_amount") + Sum(jea.debit if self.account_type == "Payable" else jea.credit).as_( + "future_amount_in_base_currency" + ) + ) + query = query.select( + Sum( + jea.debit_in_account_currency + if self.account_type == "Payable" + else jea.credit_in_account_currency + ).as_("future_amount") ) query = query.having(qb.Field("future_amount") > 0) @@ -647,14 +665,19 @@ class ReceivablePayableReport(object): row.remaining_balance = row.outstanding row.future_amount = 0.0 for future in self.future_payments.get((row.voucher_no, row.party), []): - if row.remaining_balance > 0 and future.future_amount: - if future.future_amount > row.outstanding: + if self.filters.in_party_currency: + future_amount_field = "future_amount" + else: + future_amount_field = "future_amount_in_base_currency" + + if row.remaining_balance > 0 and future.get(future_amount_field): + if future.get(future_amount_field) > row.outstanding: row.future_amount = row.outstanding - future.future_amount = future.future_amount - row.outstanding + future[future_amount_field] = future.get(future_amount_field) - row.outstanding row.remaining_balance = 0 else: - row.future_amount += future.future_amount - future.future_amount = 0 + row.future_amount += future.get(future_amount_field) + future[future_amount_field] = 0 row.remaining_balance = row.outstanding - row.future_amount row.setdefault("future_ref", []).append( From 12ac371b2267832e4bd686c0ffbbcac0a04be24d Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 27 Jan 2024 11:14:29 +0530 Subject: [PATCH 207/675] test: future payment with foreign currency (cherry picked from commit 7b37389115cd10aac154c292e7b34fcefd4d7ba2) --- .../test_accounts_receivable.py | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py index 976935b99f6..6ff81be0ab7 100644 --- a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py @@ -772,3 +772,92 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): # post sorting output should be [[Additional Debtors, ...], [Debtors, ...]] report_output = sorted(report_output, key=lambda x: x[0]) self.assertEqual(expected_data, report_output) + + def test_future_payments_on_foreign_currency(self): + self.customer2 = ( + frappe.get_doc( + { + "doctype": "Customer", + "customer_name": "Jane Doe", + "type": "Individual", + "default_currency": "USD", + } + ) + .insert() + .submit() + ) + + si = self.create_sales_invoice(do_not_submit=True) + si.posting_date = add_days(today(), -1) + si.customer = self.customer2 + si.currency = "USD" + si.conversion_rate = 80 + si.debit_to = self.debtors_usd + si.save().submit() + + # full payment in USD + pe = get_payment_entry(si.doctype, si.name) + pe.posting_date = add_days(today(), 1) + pe.base_received_amount = 7500 + pe.received_amount = 7500 + pe.source_exchange_rate = 75 + pe.save().submit() + + filters = frappe._dict( + { + "company": self.company, + "report_date": today(), + "range1": 30, + "range2": 60, + "range3": 90, + "range4": 120, + "show_future_payments": True, + "in_party_currency": False, + } + ) + report = execute(filters)[1] + self.assertEqual(len(report), 1) + + expected_data = [8000.0, 8000.0, 500.0, 7500.0] + row = report[0] + self.assertEqual( + expected_data, [row.invoiced, row.outstanding, row.remaining_balance, row.future_amount] + ) + + filters.in_party_currency = True + report = execute(filters)[1] + self.assertEqual(len(report), 1) + expected_data = [100.0, 100.0, 0.0, 100.0] + row = report[0] + self.assertEqual( + expected_data, [row.invoiced, row.outstanding, row.remaining_balance, row.future_amount] + ) + + pe.cancel() + # partial payment in USD on a future date + pe = get_payment_entry(si.doctype, si.name) + pe.posting_date = add_days(today(), 1) + pe.base_received_amount = 6750 + pe.received_amount = 6750 + pe.source_exchange_rate = 75 + pe.paid_amount = 90 # in USD + pe.references[0].allocated_amount = 90 + pe.save().submit() + + filters.in_party_currency = False + report = execute(filters)[1] + self.assertEqual(len(report), 1) + expected_data = [8000.0, 8000.0, 1250.0, 6750.0] + row = report[0] + self.assertEqual( + expected_data, [row.invoiced, row.outstanding, row.remaining_balance, row.future_amount] + ) + + filters.in_party_currency = True + report = execute(filters)[1] + self.assertEqual(len(report), 1) + expected_data = [100.0, 100.0, 10.0, 90.0] + row = report[0] + self.assertEqual( + expected_data, [row.invoiced, row.outstanding, row.remaining_balance, row.future_amount] + ) From bf61030dab65e8fff4d6752e320257dacc555f75 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sat, 27 Jan 2024 23:25:04 +0530 Subject: [PATCH 208/675] fix: prevent extra transfer against inter transfer transaction (backport #39213) (#39595) * fix: prevent extra transfer against inter transfer transaction (#39213) * fix: prevent extra transfer against inter transfer transaction * fix: internal transfer dashboard (cherry picked from commit 8fdc244e16c26a74944a3a67613f8b64009a69b0) # Conflicts: # erpnext/controllers/stock_controller.py * chore: fix conflicts --------- Co-authored-by: rohitwaghchaure --- erpnext/controllers/stock_controller.py | 115 +++++++++++++++++- .../delivery_note/delivery_note_dashboard.py | 6 +- .../purchase_receipt/test_purchase_receipt.py | 3 +- 3 files changed, 121 insertions(+), 3 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 5768026df2b..f6ccb824aea 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -6,7 +6,7 @@ from collections import defaultdict from typing import List, Tuple import frappe -from frappe import _ +from frappe import _, bold from frappe.utils import cint, cstr, flt, get_link_to_form, getdate import erpnext @@ -668,6 +668,9 @@ class StockController(AccountsController): self.validate_in_transit_warehouses() self.validate_multi_currency() self.validate_packed_items() + + if self.get("is_internal_supplier"): + self.validate_internal_transfer_qty() else: self.validate_internal_transfer_warehouse() @@ -706,6 +709,116 @@ class StockController(AccountsController): if self.doctype in ("Sales Invoice", "Delivery Note Item") and self.get("packed_items"): frappe.throw(_("Packed Items cannot be transferred internally")) + def validate_internal_transfer_qty(self): + if self.doctype not in ["Purchase Invoice", "Purchase Receipt"]: + return + + item_wise_transfer_qty = self.get_item_wise_inter_transfer_qty() + if not item_wise_transfer_qty: + return + + item_wise_received_qty = self.get_item_wise_inter_received_qty() + precision = frappe.get_precision(self.doctype + " Item", "qty") + + over_receipt_allowance = frappe.db.get_single_value( + "Stock Settings", "over_delivery_receipt_allowance" + ) + + parent_doctype = { + "Purchase Receipt": "Delivery Note", + "Purchase Invoice": "Sales Invoice", + }.get(self.doctype) + + for key, transferred_qty in item_wise_transfer_qty.items(): + recevied_qty = flt(item_wise_received_qty.get(key), precision) + if over_receipt_allowance: + transferred_qty = transferred_qty + flt( + transferred_qty * over_receipt_allowance / 100, precision + ) + + if recevied_qty > flt(transferred_qty, precision): + frappe.throw( + _("For Item {0} cannot be received more than {1} qty against the {2} {3}").format( + bold(key[1]), + bold(flt(transferred_qty, precision)), + bold(parent_doctype), + get_link_to_form(parent_doctype, self.get("inter_company_reference")), + ) + ) + + def get_item_wise_inter_transfer_qty(self): + reference_field = "inter_company_reference" + if self.doctype == "Purchase Invoice": + reference_field = "inter_company_invoice_reference" + + parent_doctype = { + "Purchase Receipt": "Delivery Note", + "Purchase Invoice": "Sales Invoice", + }.get(self.doctype) + + child_doctype = parent_doctype + " Item" + + parent_tab = frappe.qb.DocType(parent_doctype) + child_tab = frappe.qb.DocType(child_doctype) + + query = ( + frappe.qb.from_(parent_doctype) + .inner_join(child_tab) + .on(child_tab.parent == parent_tab.name) + .select( + child_tab.name, + child_tab.item_code, + child_tab.qty, + ) + .where((parent_tab.name == self.get(reference_field)) & (parent_tab.docstatus == 1)) + ) + + data = query.run(as_dict=True) + item_wise_transfer_qty = defaultdict(float) + for row in data: + item_wise_transfer_qty[(row.name, row.item_code)] += flt(row.qty) + + return item_wise_transfer_qty + + def get_item_wise_inter_received_qty(self): + child_doctype = self.doctype + " Item" + + parent_tab = frappe.qb.DocType(self.doctype) + child_tab = frappe.qb.DocType(child_doctype) + + query = ( + frappe.qb.from_(self.doctype) + .inner_join(child_tab) + .on(child_tab.parent == parent_tab.name) + .select( + child_tab.item_code, + child_tab.qty, + ) + .where(parent_tab.docstatus < 2) + ) + + if self.doctype == "Purchase Invoice": + query = query.select( + child_tab.sales_invoice_item.as_("name"), + ) + + query = query.where( + parent_tab.inter_company_invoice_reference == self.inter_company_invoice_reference + ) + else: + query = query.select( + child_tab.delivery_note_item.as_("name"), + ) + + query = query.where(parent_tab.inter_company_reference == self.inter_company_reference) + + data = query.run(as_dict=True) + item_wise_transfer_qty = defaultdict(float) + for row in data: + item_wise_transfer_qty[(row.name, row.item_code)] += flt(row.qty) + + return item_wise_transfer_qty + def validate_putaway_capacity(self): # if over receipt is attempted while 'apply putaway rule' is disabled # and if rule was applied on the transaction, validate it. diff --git a/erpnext/stock/doctype/delivery_note/delivery_note_dashboard.py b/erpnext/stock/doctype/delivery_note/delivery_note_dashboard.py index d4a574da73f..2440701af98 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note_dashboard.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note_dashboard.py @@ -8,6 +8,7 @@ def get_data(): "Stock Entry": "delivery_note_no", "Quality Inspection": "reference_name", "Auto Repeat": "reference_document", + "Purchase Receipt": "inter_company_reference", }, "internal_links": { "Sales Order": ["items", "against_sales_order"], @@ -22,6 +23,9 @@ def get_data(): {"label": _("Reference"), "items": ["Sales Order", "Shipment", "Quality Inspection"]}, {"label": _("Returns"), "items": ["Stock Entry"]}, {"label": _("Subscription"), "items": ["Auto Repeat"]}, - {"label": _("Internal Transfer"), "items": ["Material Request", "Purchase Order"]}, + { + "label": _("Internal Transfer"), + "items": ["Material Request", "Purchase Order", "Purchase Receipt"], + }, ], } diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 3a81c4a5462..0fc04c5f83e 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -1625,7 +1625,7 @@ class TestPurchaseReceipt(FrappeTestCase): pr.items[0].rejected_warehouse = from_warehouse pr.save() - self.assertRaises(OverAllowanceError, pr.submit) + self.assertRaises(frappe.ValidationError, pr.submit) # Step 5: Test Over Receipt Allowance frappe.db.set_single_value("Stock Settings", "over_delivery_receipt_allowance", 50) @@ -1639,6 +1639,7 @@ class TestPurchaseReceipt(FrappeTestCase): to_warehouse=target_warehouse, ) + pr.reload() pr.submit() frappe.db.set_single_value("Stock Settings", "over_delivery_receipt_allowance", 0) From 66be3c551f4666651b32d78a4fb15cdeed75b189 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Thu, 25 Jan 2024 16:13:24 +0530 Subject: [PATCH 209/675] fix: enqueue JV submission when more than 100 accounts (cherry picked from commit 53b44ccf2907ee15ca4dd6291400272e7cc2acf3) --- .../doctype/journal_entry/journal_entry.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 82a9c0f1524..d6400c52b95 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -78,6 +78,20 @@ class JournalEntry(AccountsController): if not self.title: self.title = self.get_title() + def submit(self): + if len(self.accounts) > 100: + msgprint(_("The task has been enqueued as a background job."), alert=True) + self.queue_action("submit", timeout=4600) + else: + self._submit() + + def cancel(self): + if len(self.accounts) > 100: + msgprint(_("The task has been enqueued as a background job."), alert=True) + self.queue_action("cancel", timeout=4600) + else: + self._cancel() + def on_submit(self): self.validate_cheque_info() self.check_credit_limit() From 04728792f588cf84cdf53082b92cf0eff4a19f37 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Fri, 26 Jan 2024 20:03:21 +0530 Subject: [PATCH 210/675] fix: return doc obj after submit (cherry picked from commit fc677811b7d896879d9c2537b0527635a8ff3855) --- erpnext/accounts/doctype/journal_entry/journal_entry.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index d6400c52b95..6e3019bb8f0 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -83,14 +83,14 @@ class JournalEntry(AccountsController): msgprint(_("The task has been enqueued as a background job."), alert=True) self.queue_action("submit", timeout=4600) else: - self._submit() + return self._submit() def cancel(self): if len(self.accounts) > 100: msgprint(_("The task has been enqueued as a background job."), alert=True) self.queue_action("cancel", timeout=4600) else: - self._cancel() + return self._cancel() def on_submit(self): self.validate_cheque_info() From 2389b41f51d61bd3b79b6b2014be20865737e130 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 29 Jan 2024 13:36:10 +0530 Subject: [PATCH 211/675] fix amount not updated when change rate in material request (backport #39606) (#39614) fix amount not updated when change rate in material request (#39606) * fix amount not updated when change rate in material request * make code consistent (cherry picked from commit efade9b9aee13c4702c69841cdb6766f305d5b30) Co-authored-by: Jeffry Suryadharma <41689493+jeffrysurya@users.noreply.github.com> --- erpnext/stock/doctype/material_request/material_request.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index e501fbba0e4..675a3e978c0 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -429,6 +429,9 @@ frappe.ui.form.on("Material Request Item", { rate: function(frm, doctype, name) { const item = locals[doctype][name]; + item.amount = flt(item.qty) * flt(item.rate); + frappe.model.set_value(doctype, name, "amount", item.amount); + refresh_field("amount", item.name, item.parentfield); frm.events.get_item_data(frm, item, false); }, From ea779fcad9b7dd64b9c39ec38644632fb66a6138 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 29 Jan 2024 14:44:16 +0530 Subject: [PATCH 212/675] refactor: build payment entry query separately --- .../payment_reconciliation.py | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 79897e0803a..3246327864d 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -112,7 +112,7 @@ class PaymentReconciliation(Document): def get_payment_entries(self): order_doctype = "Sales Order" if self.party_type == "Customer" else "Purchase Order" - condition = self.get_conditions(get_payments=True) + condition = self.get_payment_entry_conditions() # pass dynamic dimension filter values to query builder dimensions = {} @@ -652,6 +652,29 @@ class PaymentReconciliation(Document): self.build_dimensions_filter_conditions() + def get_payment_entry_conditions(self): + condition = " and company = '{0}' ".format(self.company) + + if self.get("cost_center"): + condition = " and cost_center = '{0}' ".format(self.cost_center) + + condition += ( + " and posting_date >= {0}".format(frappe.db.escape(self.from_payment_date)) + if self.from_payment_date + else "" + ) + condition += ( + " and posting_date <= {0}".format(frappe.db.escape(self.to_payment_date)) + if self.to_payment_date + else "" + ) + + if self.minimum_payment_amount: + condition += " and unallocated_amount >= {0}".format(flt(self.minimum_payment_amount)) + if self.maximum_payment_amount: + condition += " and unallocated_amount <= {0}".format(flt(self.maximum_payment_amount)) + return condition + def get_journal_filter_conditions(self): conditions = [] je = qb.DocType("Journal Entry") From 1e341f0ff62bfcef778d4f792da6c611ac041372 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Mon, 29 Jan 2024 13:15:00 +0530 Subject: [PATCH 213/675] fix: do not auto-populate item delivery date (cherry picked from commit 49cb11c1f357bd73afb0b150a502a418d5560067) --- erpnext/selling/doctype/quotation/quotation.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index d3832172545..de2cef90929 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -309,7 +309,6 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False): balance_qty = obj.qty - ordered_items.get(obj.item_code, 0.0) target.qty = balance_qty if balance_qty > 0 else 0 target.stock_qty = flt(target.qty) * flt(obj.conversion_factor) - target.delivery_date = nowdate() if obj.against_blanket_order: target.against_blanket_order = obj.against_blanket_order From c50988b1bc8b6dbfbec80763e4ecb8a5bb45c38d Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Mon, 29 Jan 2024 14:19:25 +0530 Subject: [PATCH 214/675] fix: qtn tests using delivery date (cherry picked from commit 079cd30b9c80739f6beec910b838b62f077e236b) --- erpnext/selling/doctype/quotation/test_quotation.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/erpnext/selling/doctype/quotation/test_quotation.py b/erpnext/selling/doctype/quotation/test_quotation.py index 691c5b03d2d..4dba030c20e 100644 --- a/erpnext/selling/doctype/quotation/test_quotation.py +++ b/erpnext/selling/doctype/quotation/test_quotation.py @@ -87,7 +87,6 @@ class TestQuotation(FrappeTestCase): self.assertEqual(sales_order.get("items")[0].prevdoc_docname, quotation.name) self.assertEqual(sales_order.customer, "_Test Customer") - sales_order.delivery_date = "2014-01-01" sales_order.naming_series = "_T-Quotation-" sales_order.transaction_date = nowdate() sales_order.insert() @@ -120,7 +119,6 @@ class TestQuotation(FrappeTestCase): self.assertEqual(sales_order.get("items")[0].prevdoc_docname, quotation.name) self.assertEqual(sales_order.customer, "_Test Customer") - sales_order.delivery_date = "2014-01-01" sales_order.naming_series = "_T-Quotation-" sales_order.transaction_date = nowdate() sales_order.insert() From ff0daedd52bf60e17093f5ed5adbb3e5b35acf94 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 29 Jan 2024 16:34:42 +0530 Subject: [PATCH 215/675] refactor: convert sql to query builder on Payments query --- .../payment_reconciliation.py | 43 +++--- erpnext/controllers/accounts_controller.py | 125 +++++++++--------- 2 files changed, 84 insertions(+), 84 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 3246327864d..435837f4460 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -114,14 +114,6 @@ class PaymentReconciliation(Document): order_doctype = "Sales Order" if self.party_type == "Customer" else "Purchase Order" condition = self.get_payment_entry_conditions() - # pass dynamic dimension filter values to query builder - dimensions = {} - for x in self.dimensions: - dimension = x.fieldname - if self.get(dimension): - dimensions.update({dimension: self.get(dimension)}) - condition.update({"accounting_dimensions": dimensions}) - payment_entries = get_advance_payment_entries_for_regional( self.party_type, self.party, @@ -653,27 +645,32 @@ class PaymentReconciliation(Document): self.build_dimensions_filter_conditions() def get_payment_entry_conditions(self): - condition = " and company = '{0}' ".format(self.company) + conditions = [] + pe = qb.DocType("Payment Entry") + conditions.append(pe.company == self.company) if self.get("cost_center"): - condition = " and cost_center = '{0}' ".format(self.cost_center) + conditions.append(pe.cost_center == self.cost_center) - condition += ( - " and posting_date >= {0}".format(frappe.db.escape(self.from_payment_date)) - if self.from_payment_date - else "" - ) - condition += ( - " and posting_date <= {0}".format(frappe.db.escape(self.to_payment_date)) - if self.to_payment_date - else "" - ) + if self.from_payment_date: + conditions.append(pe.posting_date.gte(self.from_payment_date)) + + if self.to_payment_date: + conditions.append(pe.posting_date.lte(self.to_payment_date)) if self.minimum_payment_amount: - condition += " and unallocated_amount >= {0}".format(flt(self.minimum_payment_amount)) + conditions.append(pe.unallocated_amount.gte(flt(self.minimum_payment_amount))) + if self.maximum_payment_amount: - condition += " and unallocated_amount <= {0}".format(flt(self.maximum_payment_amount)) - return condition + conditions.append(pe.unallocated_amount.lte(flt(self.maximum_payment_amount))) + + # pass dynamic dimension filter values to payment query + for x in self.dimensions: + dimension = x.fieldname + if self.get(dimension): + conditions.append(pe[dimension] == self.get(dimension)) + + return conditions def get_journal_filter_conditions(self): conditions = [] diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index cd7b7b69529..bb87a776a1d 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -7,6 +7,8 @@ import json import frappe from frappe import _, bold, qb, throw from frappe.model.workflow import get_workflow_name, is_transition_condition_satisfied +from frappe.query_builder import Criterion +from frappe.query_builder.custom import ConstantColumn from frappe.query_builder.functions import Abs, Sum from frappe.utils import ( add_days, @@ -2541,6 +2543,9 @@ def get_advance_payment_entries( condition=None, payment_name=None, ): + pe = qb.DocType("Payment Entry") + per = qb.DocType("Payment Entry Reference") + party_account_field = "paid_from" if party_type == "Customer" else "paid_to" currency_field = ( "paid_from_account_currency" if party_type == "Customer" else "paid_to_account_currency" @@ -2551,76 +2556,74 @@ def get_advance_payment_entries( ) payment_entries_against_order, unallocated_payment_entries = [], [] - limit_cond = "limit %s" % limit if limit else "" + + if payment_name: + condition.append(pe.name.like(f"%%{payment_name}%%")) if order_list or against_all_orders: if order_list: - reference_condition = " and t2.reference_name in ({0})".format( - ", ".join(["%s"] * len(order_list)) + condition.append(per.refernce_name.isin(order_list)) + payment_entries_query = ( + qb.from_(pe) + .inner_join(per) + .on(pe.name == per.parent) + .select( + ConstantColumn("Payment Entry").as_("reference_type"), + pe.name, + pe.remarks, + per.allocated_amount.as_("amount"), + per.name.as_("reference_row"), + per.reference_name.as_("against_order"), + pe.posting_date, + pe[currency_field].as_("currency"), + pe[exchange_rate_field].as_("exchange_rate"), ) - else: - reference_condition = "" - order_list = [] - - payment_name_filter = "" - if payment_name: - payment_name_filter = " and t1.name like '%%{0}%%'".format(payment_name) - - if not condition: - condition = "" - - payment_entries_against_order = frappe.db.sql( - """ - select - 'Payment Entry' as reference_type, t1.name as reference_name, - t1.remarks, t2.allocated_amount as amount, t2.name as reference_row, - t2.reference_name as against_order, t1.posting_date, - t1.{0} as currency, t1.{5} as exchange_rate - from `tabPayment Entry` t1, `tabPayment Entry Reference` t2 - where - t1.name = t2.parent and t1.{1} = %s and t1.payment_type = %s - and t1.party_type = %s and t1.party = %s and t1.docstatus = 1 - and t2.reference_doctype = %s {2} {3} {6} - order by t1.posting_date {4} - """.format( - currency_field, - party_account_field, - reference_condition, - condition, - limit_cond, - exchange_rate_field, - payment_name_filter, - ), - [party_account, payment_type, party_type, party, order_doctype] + order_list, - as_dict=1, + .where( + (pe[party_account_field] == party_account) + & (pe.payment_type == payment_type) + & (pe.party_type == party_type) + & (pe.party == party) + & (pe.docstatus == 1) + & (per.reference_doctype == order_doctype) + ) + .where(Criterion.all(condition)) + .orderby(pe.posting_date) ) + if limit: + payment_entries_query = payment_entries_query.limit(limit) + + payment_entries_against_order = payment_entries_query.run(as_dict=1) + if include_unallocated: - payment_name_filter = "" - if payment_name: - payment_name_filter = " and name like '%%{0}%%'".format(payment_name) - - unallocated_payment_entries = frappe.db.sql( - """ - select 'Payment Entry' as reference_type, name as reference_name, posting_date, - remarks, unallocated_amount as amount, {2} as exchange_rate, {3} as currency - from `tabPayment Entry` - where - {0} = %s and party_type = %s and party = %s and payment_type = %s - and docstatus = 1 and unallocated_amount > 0 {condition} {4} - order by posting_date {1} - """.format( - party_account_field, - limit_cond, - exchange_rate_field, - currency_field, - payment_name_filter, - condition=condition or "", - ), - (party_account, party_type, party, payment_type), - as_dict=1, + unallocated_payment_query = ( + qb.from_(pe) + .select( + ConstantColumn("Payment Entry").as_("reference_type"), + pe.name.as_("reference_name"), + pe.posting_date, + pe.remarks, + pe.unallocated_amount.as_("amount"), + pe[exchange_rate_field].as_("exchange_rate"), + pe[currency_field].as_("currency"), + ) + .where( + (pe[party_account_field] == party_account) + & (pe.party_type == party_type) + & (pe.party == party) + & (pe.payment_type == payment_type) + & (pe.docstatus == 1) + & (pe.unallocated_amount.gt(0)) + ) + .where(Criterion.all(condition)) + .orderby(pe.posting_date) ) + if limit: + unallocated_payment_query = unallocated_payment_query.limit(limit) + + unallocated_payment_entries = unallocated_payment_query.run(as_dict=1) + return list(payment_entries_against_order) + list(unallocated_payment_entries) From c054316127e60d233d0b54fcbdbb260c1e99da48 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 29 Jan 2024 17:41:56 +0530 Subject: [PATCH 216/675] chore: fix typo and initialize a list --- erpnext/controllers/accounts_controller.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index bb87a776a1d..e4a079c5882 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -2557,12 +2557,15 @@ def get_advance_payment_entries( payment_entries_against_order, unallocated_payment_entries = [], [] + if not condition: + condition = [] + if payment_name: condition.append(pe.name.like(f"%%{payment_name}%%")) if order_list or against_all_orders: if order_list: - condition.append(per.refernce_name.isin(order_list)) + condition.append(per.reference_name.isin(order_list)) payment_entries_query = ( qb.from_(pe) .inner_join(per) From d794502681d29f628cc51789f32565ab749313e3 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 29 Jan 2024 18:57:40 +0530 Subject: [PATCH 217/675] fix amount not updated when change rate in material request (backport #39606) (backport #39614) (#39621) fix amount not updated when change rate in material request (backport #39606) (#39614) fix amount not updated when change rate in material request (#39606) * fix amount not updated when change rate in material request * make code consistent (cherry picked from commit efade9b9aee13c4702c69841cdb6766f305d5b30) Co-authored-by: Jeffry Suryadharma <41689493+jeffrysurya@users.noreply.github.com> (cherry picked from commit 2389b41f51d61bd3b79b6b2014be20865737e130) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- erpnext/stock/doctype/material_request/material_request.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index e501fbba0e4..675a3e978c0 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -429,6 +429,9 @@ frappe.ui.form.on("Material Request Item", { rate: function(frm, doctype, name) { const item = locals[doctype][name]; + item.amount = flt(item.qty) * flt(item.rate); + frappe.model.set_value(doctype, name, "amount", item.amount); + refresh_field("amount", item.name, item.parentfield); frm.events.get_item_data(frm, item, false); }, From 1966ea15ba830699804aff2721aa62c260676aa0 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 29 Jan 2024 21:05:21 +0530 Subject: [PATCH 218/675] refactor: pass orders name in a separate criterion --- 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 e4a079c5882..ac197259679 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -2564,15 +2564,16 @@ def get_advance_payment_entries( condition.append(pe.name.like(f"%%{payment_name}%%")) if order_list or against_all_orders: + orders_condition = [] if order_list: - condition.append(per.reference_name.isin(order_list)) + orders_condition.append(per.reference_name.isin(order_list)) payment_entries_query = ( qb.from_(pe) .inner_join(per) .on(pe.name == per.parent) .select( ConstantColumn("Payment Entry").as_("reference_type"), - pe.name, + pe.name.as_("reference_name"), pe.remarks, per.allocated_amount.as_("amount"), per.name.as_("reference_row"), @@ -2590,6 +2591,7 @@ def get_advance_payment_entries( & (per.reference_doctype == order_doctype) ) .where(Criterion.all(condition)) + .where(Criterion.all(orders_condition)) .orderby(pe.posting_date) ) From d0c810accdece6507a290a12b2fe0ac1e4fa042f Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 30 Jan 2024 05:43:17 +0530 Subject: [PATCH 219/675] refactor(test): disable dimensions post test --- .../tests/test_accounts_controller.py | 61 +++++++++++++++---- 1 file changed, 50 insertions(+), 11 deletions(-) diff --git a/erpnext/controllers/tests/test_accounts_controller.py b/erpnext/controllers/tests/test_accounts_controller.py index fad216d5a43..5e3077ea8c8 100644 --- a/erpnext/controllers/tests/test_accounts_controller.py +++ b/erpnext/controllers/tests/test_accounts_controller.py @@ -1258,18 +1258,53 @@ class TestAccountsController(FrappeTestCase): frappe.db.set_value("Company", self.company, "cost_center", cc) def setup_dimensions(self): - # create dimension - from erpnext.accounts.doctype.accounting_dimension.test_accounting_dimension import ( - create_dimension, - ) + if not frappe.db.exists("Accounting Dimension", {"document_type": "Department"}): + frappe.get_doc( + { + "doctype": "Accounting Dimension", + "document_type": "Department", + } + ).insert() + else: + dimension = frappe.get_doc("Accounting Dimension", "Department") + dimension.disabled = 0 + dimension.save() - create_dimension() - # make it non-mandatory - loc = frappe.get_doc("Accounting Dimension", "Location") - for x in loc.dimension_defaults: - x.mandatory_for_bs = False - x.mandatory_for_pl = False - loc.save() + if not frappe.db.exists("Accounting Dimension", {"document_type": "Location"}): + dimension1 = frappe.get_doc( + { + "doctype": "Accounting Dimension", + "document_type": "Location", + } + ) + dimension1.append( + "dimension_defaults", + { + "company": "_Test Company", + "reference_document": "Location", + "default_dimension": "Block 1", + "mandatory_for_bs": 0, + "mandatory_for_pl": 0, + }, + ) + + dimension1.insert() + dimension1.save() + else: + dimension1 = frappe.get_doc("Accounting Dimension", "Location") + dimension1.disabled = 0 + dimension1.save() + + def disable_dimensions(self): + if frappe.db.exists("Accounting Dimension", {"document_type": "Department"}): + dimension = frappe.get_doc("Accounting Dimension", "Department") + dimension.disabled = 1 + dimension.save() + + if frappe.db.exists("Accounting Dimension", {"document_type": "Location"}): + dimension1 = frappe.get_doc("Accounting Dimension", "Location") + dimension1.disabled = 1 + dimension1.save() def test_50_dimensions_filter(self): """ @@ -1341,6 +1376,7 @@ class TestAccountsController(FrappeTestCase): pr.get_unreconciled_entries() self.assertEqual(len(pr.invoices), 0) self.assertEqual(len(pr.payments), 1) + self.disable_dimensions() def test_51_cr_note_should_inherit_dimension(self): self.setup_dimensions() @@ -1383,6 +1419,7 @@ class TestAccountsController(FrappeTestCase): [cr_note.department, cr_note.department], frappe.db.get_all("Journal Entry Account", filters={"parent": x.parent}, pluck="department"), ) + self.disable_dimensions() def test_52_dimension_inhertiance_exc_gain_loss(self): # Sales Invoice in Foreign Currency @@ -1421,6 +1458,7 @@ class TestAccountsController(FrappeTestCase): pluck="department", ), ) + self.disable_dimensions() def test_53_dimension_inheritance_on_advance(self): self.setup_dimensions() @@ -1467,3 +1505,4 @@ class TestAccountsController(FrappeTestCase): pluck="department", ), ) + self.disable_dimensions() From c5ce4db315f955f7c0c95c51667f98c27139463f Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 25 Jan 2024 14:05:42 +0530 Subject: [PATCH 220/675] refactor: prevent '{debit/credit}_to' account mismatch (cherry picked from commit 6f2fae1b6133286ab882e421e69f6a87ecb97d4a) # Conflicts: # erpnext/controllers/accounts_controller.py --- erpnext/controllers/accounts_controller.py | 27 ++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index ac197259679..f1385c721d8 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -187,6 +187,7 @@ class AccountsController(TransactionBase): self.validate_party() self.validate_currency() self.validate_party_account_currency() + self.validate_return_against_account() if self.doctype in ["Purchase Invoice", "Sales Invoice"]: if invalid_advances := [ @@ -320,6 +321,32 @@ class AccountsController(TransactionBase): (self.doctype, self.name), ) +<<<<<<< HEAD +======= + def remove_serial_and_batch_bundle(self): + bundles = frappe.get_all( + "Serial and Batch Bundle", + filters={"voucher_type": self.doctype, "voucher_no": self.name, "docstatus": ("!=", 1)}, + ) + + for bundle in bundles: + frappe.delete_doc("Serial and Batch Bundle", bundle.name) + + 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 = self.get(cr_dr_account_field) + if frappe.get_value(self.doctype, self.return_against, cr_dr_account_field) != 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) + ) + ) + +>>>>>>> 6f2fae1b61 (refactor: prevent '{debit/credit}_to' account mismatch) def validate_deferred_income_expense_account(self): field_map = { "Sales Invoice": "deferred_revenue_account", From faeca79c689987a0a666d5f2bf5e56a2a6a5661a Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 25 Jan 2024 14:21:18 +0530 Subject: [PATCH 221/675] test: account mismatch validation (cherry picked from commit 8bdc76073367d4a912524f16bf098d94340f50c5) --- .../doctype/sales_invoice/test_sales_invoice.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 45a01ab04aa..abf0fc5c0f3 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1531,6 +1531,19 @@ class TestSalesInvoice(FrappeTestCase): self.assertEqual(frappe.db.get_value("Sales Invoice", si1.name, "outstanding_amount"), -1000) self.assertEqual(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"), 2500) + def test_return_invoice_with_account_mismatch(self): + debtors2 = create_account( + parent_account="Accounts Receivable - _TC", + account_name="Debtors 2", + company="_Test Company", + account_type="Receivable", + ) + si = create_sales_invoice(qty=1, rate=1000) + cr_note = create_sales_invoice( + qty=-1, rate=1000, is_return=1, return_against=si.name, debit_to=debtors2, do_not_save=True + ) + self.assertRaises(frappe.ValidationError, cr_note.save) + def test_gle_made_when_asset_is_returned(self): create_asset_data() asset = create_asset(item_code="Macbook Pro") From 9212a74913d653c857f6d9b692fe83ea96a148ce Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 25 Jan 2024 14:29:07 +0530 Subject: [PATCH 222/675] test: debit note account mismatch (cherry picked from commit bdca718103a98eba64e69715eaf628dfd9377d5e) # Conflicts: # erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py --- .../purchase_invoice/test_purchase_invoice.py | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index dc2b37291e8..f17c60bcd09 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1893,6 +1893,55 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin): self.assertEqual(pi.items[0].cost_center, "_Test Cost Center Buying - _TC") +<<<<<<< HEAD +======= + def test_debit_note_with_account_mismatch(self): + new_creditors = create_account( + parent_account="Accounts Payable - _TC", + account_name="Creditors 2", + company="_Test Company", + account_type="Payable", + ) + pi = make_purchase_invoice(qty=1, rate=1000) + dr_note = make_purchase_invoice( + qty=-1, rate=1000, is_return=1, return_against=pi.name, do_not_save=True + ) + dr_note.credit_to = new_creditors + + self.assertRaises(frappe.ValidationError, dr_note.save) + + def test_debit_note_without_item(self): + pi = make_purchase_invoice(item_name="_Test Item", qty=10, do_not_submit=True) + pi.items[0].item_code = "" + pi.save() + + self.assertFalse(pi.items[0].item_code) + pi.submit() + + return_pi = make_purchase_invoice( + item_name="_Test Item", + is_return=1, + return_against=pi.name, + qty=-10, + do_not_save=True, + ) + return_pi.items[0].item_code = "" + return_pi.save() + return_pi.submit() + self.assertEqual(return_pi.docstatus, 1) + + +def set_advance_flag(company, flag, default_account): + frappe.db.set_value( + "Company", + company, + { + "book_advance_payments_in_separate_party_account": flag, + "default_advance_paid_account": default_account, + }, + ) + +>>>>>>> bdca718103 (test: debit note account mismatch) def check_gl_entries( doc, From 0884c5ed83048a71db3947fd5d3df119b7a7d408 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 30 Jan 2024 11:03:25 +0530 Subject: [PATCH 223/675] chore: resolve conflicts --- .../purchase_invoice/test_purchase_invoice.py | 34 ------------------- erpnext/controllers/accounts_controller.py | 12 ------- 2 files changed, 46 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index f17c60bcd09..6942e28726f 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1893,8 +1893,6 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin): self.assertEqual(pi.items[0].cost_center, "_Test Cost Center Buying - _TC") -<<<<<<< HEAD -======= def test_debit_note_with_account_mismatch(self): new_creditors = create_account( parent_account="Accounts Payable - _TC", @@ -1910,38 +1908,6 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin): self.assertRaises(frappe.ValidationError, dr_note.save) - def test_debit_note_without_item(self): - pi = make_purchase_invoice(item_name="_Test Item", qty=10, do_not_submit=True) - pi.items[0].item_code = "" - pi.save() - - self.assertFalse(pi.items[0].item_code) - pi.submit() - - return_pi = make_purchase_invoice( - item_name="_Test Item", - is_return=1, - return_against=pi.name, - qty=-10, - do_not_save=True, - ) - return_pi.items[0].item_code = "" - return_pi.save() - return_pi.submit() - self.assertEqual(return_pi.docstatus, 1) - - -def set_advance_flag(company, flag, default_account): - frappe.db.set_value( - "Company", - company, - { - "book_advance_payments_in_separate_party_account": flag, - "default_advance_paid_account": default_account, - }, - ) - ->>>>>>> bdca718103 (test: debit note account mismatch) def check_gl_entries( doc, diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index f1385c721d8..a4597da8e27 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -321,17 +321,6 @@ class AccountsController(TransactionBase): (self.doctype, self.name), ) -<<<<<<< HEAD -======= - def remove_serial_and_batch_bundle(self): - bundles = frappe.get_all( - "Serial and Batch Bundle", - filters={"voucher_type": self.doctype, "voucher_no": self.name, "docstatus": ("!=", 1)}, - ) - - for bundle in bundles: - frappe.delete_doc("Serial and Batch Bundle", bundle.name) - def validate_return_against_account(self): if ( self.doctype in ["Sales Invoice", "Purchase Invoice"] and self.is_return and self.return_against @@ -346,7 +335,6 @@ class AccountsController(TransactionBase): ) ) ->>>>>>> 6f2fae1b61 (refactor: prevent '{debit/credit}_to' account mismatch) def validate_deferred_income_expense_account(self): field_map = { "Sales Invoice": "deferred_revenue_account", From f6725e43425043eaba7dcdd3cf3768a857a39ee6 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 30 Jan 2024 11:25:20 +0530 Subject: [PATCH 224/675] fix: do not consider rejected warehouses in pick list (#39539) * fix: do not picked rejected materials * test: test case for pick list without rejected materials --- .../doctype/sales_order/test_sales_order.py | 78 ++++++++++++++++ .../stock/doctype/pick_list/pick_list.json | 10 ++- erpnext/stock/doctype/pick_list/pick_list.py | 88 +++++++++++++++++-- .../stock/doctype/warehouse/warehouse.json | 10 ++- 4 files changed, 175 insertions(+), 11 deletions(-) diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py index 3d4c035fdca..2de0168812d 100644 --- a/erpnext/selling/doctype/sales_order/test_sales_order.py +++ b/erpnext/selling/doctype/sales_order/test_sales_order.py @@ -20,6 +20,7 @@ from erpnext.manufacturing.doctype.blanket_order.test_blanket_order import make_ from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle from erpnext.selling.doctype.sales_order.sales_order import ( WarehouseRequired, + create_pick_list, make_delivery_note, make_material_request, make_raw_material_request, @@ -2082,6 +2083,83 @@ class TestSalesOrder(FrappeTestCase): self.assertEqual(so.items[0].rate, scenario.get("expected_rate")) self.assertEqual(so.packed_items[0].rate, scenario.get("expected_rate")) + def test_pick_list_without_rejected_materials(self): + serial_and_batch_item = make_item( + "_Test Serial and Batch Item for Rejected Materials", + properties={ + "has_serial_no": 1, + "has_batch_no": 1, + "create_new_batch": 1, + "batch_number_series": "BAT-TSBIFRM-.#####", + "serial_no_series": "SN-TSBIFRM-.#####", + }, + ).name + + serial_item = make_item( + "_Test Serial Item for Rejected Materials", + properties={ + "has_serial_no": 1, + "serial_no_series": "SN-TSIFRM-.#####", + }, + ).name + + batch_item = make_item( + "_Test Batch Item for Rejected Materials", + properties={ + "has_batch_no": 1, + "create_new_batch": 1, + "batch_number_series": "BAT-TBIFRM-.#####", + }, + ).name + + normal_item = make_item("_Test Normal Item for Rejected Materials").name + + warehouse = "_Test Warehouse - _TC" + rejected_warehouse = "_Test Dummy Rejected Warehouse - _TC" + + if not frappe.db.exists("Warehouse", rejected_warehouse): + frappe.get_doc( + { + "doctype": "Warehouse", + "warehouse_name": rejected_warehouse, + "company": "_Test Company", + "warehouse_group": "_Test Warehouse Group", + "is_rejected_warehouse": 1, + } + ).insert() + + se = make_stock_entry(item_code=normal_item, qty=1, to_warehouse=warehouse, do_not_submit=True) + for item in [serial_and_batch_item, serial_item, batch_item]: + se.append("items", {"item_code": item, "qty": 1, "t_warehouse": warehouse}) + + se.save() + se.submit() + + se = make_stock_entry( + item_code=normal_item, qty=1, to_warehouse=rejected_warehouse, do_not_submit=True + ) + for item in [serial_and_batch_item, serial_item, batch_item]: + se.append("items", {"item_code": item, "qty": 1, "t_warehouse": rejected_warehouse}) + + se.save() + se.submit() + + so = make_sales_order(item_code=normal_item, qty=2, do_not_submit=True) + + for item in [serial_and_batch_item, serial_item, batch_item]: + so.append("items", {"item_code": item, "qty": 2, "warehouse": warehouse}) + + so.save() + so.submit() + + pick_list = create_pick_list(so.name) + + pick_list.save() + for row in pick_list.locations: + self.assertEqual(row.qty, 1.0) + self.assertFalse(row.warehouse == rejected_warehouse) + self.assertTrue(row.warehouse == warehouse) + def automatically_fetch_payment_terms(enable=1): accounts_settings = frappe.get_doc("Accounts Settings") diff --git a/erpnext/stock/doctype/pick_list/pick_list.json b/erpnext/stock/doctype/pick_list/pick_list.json index 7259dc00a81..948011cce69 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.json +++ b/erpnext/stock/doctype/pick_list/pick_list.json @@ -16,6 +16,7 @@ "for_qty", "column_break_4", "parent_warehouse", + "consider_rejected_warehouses", "get_item_locations", "section_break_6", "scan_barcode", @@ -184,11 +185,18 @@ "report_hide": 1, "reqd": 1, "search_index": 1 + }, + { + "default": "0", + "description": "Enable it if users want to consider rejected materials to dispatch.", + "fieldname": "consider_rejected_warehouses", + "fieldtype": "Check", + "label": "Consider Rejected Warehouses" } ], "is_submittable": 1, "links": [], - "modified": "2023-01-24 10:33:43.244476", + "modified": "2024-01-24 17:05:20.317180", "modified_by": "Administrator", "module": "Stock", "name": "Pick List", diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 3c5384d4a88..36c877ebc88 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -205,6 +205,7 @@ class PickList(Document): self.item_count_map.get(item_code), self.company, picked_item_details=picked_items_details.get(item_code), + consider_rejected_warehouses=self.consider_rejected_warehouses, ), ) @@ -524,6 +525,7 @@ def get_available_item_locations( company, ignore_validation=False, picked_item_details=None, + consider_rejected_warehouses=False, ): locations = [] total_picked_qty = ( @@ -534,19 +536,39 @@ def get_available_item_locations( if has_batch_no and has_serial_no: locations = get_available_item_locations_for_serial_and_batched_item( - item_code, from_warehouses, required_qty, company, total_picked_qty + item_code, + from_warehouses, + required_qty, + company, + total_picked_qty, + consider_rejected_warehouses=consider_rejected_warehouses, ) elif has_serial_no: locations = get_available_item_locations_for_serialized_item( - item_code, from_warehouses, required_qty, company, total_picked_qty + item_code, + from_warehouses, + required_qty, + company, + total_picked_qty, + consider_rejected_warehouses=consider_rejected_warehouses, ) elif has_batch_no: locations = get_available_item_locations_for_batched_item( - item_code, from_warehouses, required_qty, company, total_picked_qty + item_code, + from_warehouses, + required_qty, + company, + total_picked_qty, + consider_rejected_warehouses=consider_rejected_warehouses, ) else: locations = get_available_item_locations_for_other_item( - item_code, from_warehouses, required_qty, company, total_picked_qty + item_code, + from_warehouses, + required_qty, + company, + total_picked_qty, + consider_rejected_warehouses=consider_rejected_warehouses, ) total_qty_available = sum(location.get("qty") for location in locations) @@ -597,7 +619,12 @@ def get_available_item_locations( def get_available_item_locations_for_serialized_item( - item_code, from_warehouses, required_qty, company, total_picked_qty=0 + item_code, + from_warehouses, + required_qty, + company, + total_picked_qty=0, + consider_rejected_warehouses=False, ): sn = frappe.qb.DocType("Serial No") query = ( @@ -613,6 +640,10 @@ def get_available_item_locations_for_serialized_item( else: query = query.where(Coalesce(sn.warehouse, "") != "") + if not consider_rejected_warehouses: + if rejected_warehouses := get_rejected_warehouses(): + query = query.where(sn.warehouse.notin(rejected_warehouses)) + serial_nos = query.run(as_list=True) warehouse_serial_nos_map = frappe._dict() @@ -627,7 +658,12 @@ def get_available_item_locations_for_serialized_item( def get_available_item_locations_for_batched_item( - item_code, from_warehouses, required_qty, company, total_picked_qty=0 + item_code, + from_warehouses, + required_qty, + company, + total_picked_qty=0, + consider_rejected_warehouses=False, ): sle = frappe.qb.DocType("Stock Ledger Entry") batch = frappe.qb.DocType("Batch") @@ -653,15 +689,28 @@ def get_available_item_locations_for_batched_item( if from_warehouses: query = query.where(sle.warehouse.isin(from_warehouses)) + if not consider_rejected_warehouses: + if rejected_warehouses := get_rejected_warehouses(): + query = query.where(sle.warehouse.notin(rejected_warehouses)) + return query.run(as_dict=True) def get_available_item_locations_for_serial_and_batched_item( - item_code, from_warehouses, required_qty, company, total_picked_qty=0 + item_code, + from_warehouses, + required_qty, + company, + total_picked_qty=0, + consider_rejected_warehouses=False, ): # Get batch nos by FIFO locations = get_available_item_locations_for_batched_item( - item_code, from_warehouses, required_qty, company + item_code, + from_warehouses, + required_qty, + company, + consider_rejected_warehouses=consider_rejected_warehouses, ) if locations: @@ -691,7 +740,12 @@ def get_available_item_locations_for_serial_and_batched_item( def get_available_item_locations_for_other_item( - item_code, from_warehouses, required_qty, company, total_picked_qty=0 + item_code, + from_warehouses, + required_qty, + company, + total_picked_qty=0, + consider_rejected_warehouses=False, ): bin = frappe.qb.DocType("Bin") query = ( @@ -708,6 +762,10 @@ def get_available_item_locations_for_other_item( wh = frappe.qb.DocType("Warehouse") query = query.from_(wh).where((bin.warehouse == wh.name) & (wh.company == company)) + if not consider_rejected_warehouses: + if rejected_warehouses := get_rejected_warehouses(): + query = query.where(bin.warehouse.notin(rejected_warehouses)) + item_locations = query.run(as_dict=True) return item_locations @@ -1028,3 +1086,15 @@ def update_common_item_properties(item, location): item.serial_no = location.serial_no item.batch_no = location.batch_no item.material_request_item = location.material_request_item + + +def get_rejected_warehouses(): + if not hasattr(frappe.local, "rejected_warehouses"): + frappe.local.rejected_warehouses = [] + + if not frappe.local.rejected_warehouses: + frappe.local.rejected_warehouses = frappe.get_all( + "Warehouse", filters={"is_rejected_warehouse": 1}, pluck="name" + ) + + return frappe.local.rejected_warehouses diff --git a/erpnext/stock/doctype/warehouse/warehouse.json b/erpnext/stock/doctype/warehouse/warehouse.json index 43b2ad2a69b..7b0cade3ca4 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.json +++ b/erpnext/stock/doctype/warehouse/warehouse.json @@ -13,6 +13,7 @@ "column_break_3", "is_group", "parent_warehouse", + "is_rejected_warehouse", "column_break_4", "account", "company", @@ -249,13 +250,20 @@ { "fieldname": "column_break_qajx", "fieldtype": "Column Break" + }, + { + "default": "0", + "description": "If yes, then this warehouse will be used to store rejected materials", + "fieldname": "is_rejected_warehouse", + "fieldtype": "Check", + "label": "Is Rejected Warehouse" } ], "icon": "fa fa-building", "idx": 1, "is_tree": 1, "links": [], - "modified": "2023-05-29 13:10:43.333160", + "modified": "2024-01-24 16:27:28.299520", "modified_by": "Administrator", "module": "Stock", "name": "Warehouse", From 3ee05551150fc5dd28f02e548ad2e505bfc2a29f Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 30 Jan 2024 15:44:03 +0530 Subject: [PATCH 225/675] fix: perf issue while submitting stock entry (backport #39634) (#39641) fix: perf issue while submitting stock entry (#39634) (cherry picked from commit b14886b227730813c83b746f440616508f678392) Co-authored-by: rohitwaghchaure --- .../stock/doctype/purchase_receipt/test_purchase_receipt.py | 6 +++++- erpnext/stock/doctype/stock_entry/stock_entry.py | 1 - 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 0fc04c5f83e..d8141f93e68 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -1594,9 +1594,10 @@ class TestPurchaseReceipt(FrappeTestCase): make_stock_entry( purpose="Material Receipt", item_code=item.name, - qty=15, + qty=20, company=company, to_warehouse=from_warehouse, + posting_date=add_days(today(), -3), ) # Step 3: Create Delivery Note with Internal Customer @@ -1619,6 +1620,8 @@ class TestPurchaseReceipt(FrappeTestCase): from erpnext.stock.doctype.delivery_note.delivery_note import make_inter_company_purchase_receipt pr = make_inter_company_purchase_receipt(dn.name) + pr.set_posting_time = 1 + pr.posting_date = today() pr.items[0].qty = 15 pr.items[0].from_warehouse = target_warehouse pr.items[0].warehouse = to_warehouse @@ -1637,6 +1640,7 @@ class TestPurchaseReceipt(FrappeTestCase): company=company, from_warehouse=from_warehouse, to_warehouse=target_warehouse, + posting_date=add_days(pr.posting_date, -1), ) pr.reload() diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 61f99ee715a..ca31a9a3d31 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -159,7 +159,6 @@ class StockEntry(StockController): set_batch_nos(self, "s_warehouse") self.validate_serialized_batch() - self.set_actual_qty() self.calculate_rate_and_amount() self.validate_putaway_capacity() From b7d8bfc58c4e7fd7ab7e32814730493fcaa91781 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 30 Jan 2024 14:14:00 +0000 Subject: [PATCH 226/675] chore(release): Bumped to Version 14.61.3 ## [14.61.3](https://github.com/frappe/erpnext/compare/v14.61.2...v14.61.3) (2024-01-30) ### Bug Fixes * Asset Depreciation WDV as per Income Tax Act ([b840eb9](https://github.com/frappe/erpnext/commit/b840eb90ebf558d91471c8bd1c629bc15214b94c)) * asset module test cases ([f604798](https://github.com/frappe/erpnext/commit/f604798a45fce39cd5e41c6a0aca8d7240848cdb)) * **Batch:** reload doc after splitting ([c759406](https://github.com/frappe/erpnext/commit/c759406ebbe225fc4f1bb7d7b4050e0386055cb8)) * default enable closing stock balance (backport [#39551](https://github.com/frappe/erpnext/issues/39551)) ([#39553](https://github.com/frappe/erpnext/issues/39553)) ([1e32c62](https://github.com/frappe/erpnext/commit/1e32c6207e2d344b2ba43f8be42f952340eacba5)) * do not auto-populate item delivery date ([1e341f0](https://github.com/frappe/erpnext/commit/1e341f0ff62bfcef778d4f792da6c611ac041372)) * do not consider rejected warehouses in pick list ([#39539](https://github.com/frappe/erpnext/issues/39539)) ([f6725e4](https://github.com/frappe/erpnext/commit/f6725e43425043eaba7dcdd3cf3768a857a39ee6)) * do not delete batches implicitly ([9a5995a](https://github.com/frappe/erpnext/commit/9a5995a3e5f1ff806855f8b88a4dc0b0a3ce73a8)) * **ecom:** do not create a new contact if a contact already exists ([#39290](https://github.com/frappe/erpnext/issues/39290)) ([47c591c](https://github.com/frappe/erpnext/commit/47c591ccf16565ad20ac88d7ba7a45bd6c990690)) * email list for auto reorder material request ([780c069](https://github.com/frappe/erpnext/commit/780c069268084e6cb1b4bacde8cad98485e925d1)) * enqueue JV submission when more than 100 accounts ([66be3c5](https://github.com/frappe/erpnext/commit/66be3c551f4666651b32d78a4fb15cdeed75b189)) * fetch correct quantity and amount for grouped asset ([1dacb79](https://github.com/frappe/erpnext/commit/1dacb794415cdf138be3707dd95cabe5bff3e9f2)) * incorrect amount in the material request item (backport [#39567](https://github.com/frappe/erpnext/issues/39567)) ([#39568](https://github.com/frappe/erpnext/issues/39568)) ([c26f7bb](https://github.com/frappe/erpnext/commit/c26f7bbed0160a533c782d44410001c5fcab9913)) * linter issue ([941f882](https://github.com/frappe/erpnext/commit/941f8824e5f31f42b0f54182d790e8178c9511ab)) * not able to edit / change address from portal ([e3fdb6f](https://github.com/frappe/erpnext/commit/e3fdb6f55cabd34ee97b47b7f50c4aabac59ac06)) * not able to edit address through portal ([b310a55](https://github.com/frappe/erpnext/commit/b310a55727e16ce511173eef1acd6db49f720227)) * Payment Terms Status for Sales Order report should show all payment terms from order not only this comming from template ([2953959](https://github.com/frappe/erpnext/commit/295395918c3515f1ce6757a9ababab42a87d4800)) * perf issue while submitting stock entry (backport [#39634](https://github.com/frappe/erpnext/issues/39634)) ([#39641](https://github.com/frappe/erpnext/issues/39641)) ([3ee0555](https://github.com/frappe/erpnext/commit/3ee05551150fc5dd28f02e548ad2e505bfc2a29f)) * prevent extra transfer against inter transfer transaction (backport [#39213](https://github.com/frappe/erpnext/issues/39213)) ([#39595](https://github.com/frappe/erpnext/issues/39595)) ([bf61030](https://github.com/frappe/erpnext/commit/bf61030dab65e8fff4d6752e320257dacc555f75)) * qtn tests using delivery date ([c50988b](https://github.com/frappe/erpnext/commit/c50988b1bc8b6dbfbec80763e4ecb8a5bb45c38d)) * return doc obj after submit ([0472879](https://github.com/frappe/erpnext/commit/04728792f588cf84cdf53082b92cf0eff4a19f37)) * RM valuation rate in SCR ([#39541](https://github.com/frappe/erpnext/issues/39541)) ([9fd1692](https://github.com/frappe/erpnext/commit/9fd1692db2c1cf45b34273bdd984c37bab13d403)) * typo's and parameter changes ([41c074d](https://github.com/frappe/erpnext/commit/41c074d0bbca82b21fcd27b66adc84fa8a03c6b0)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 15f872b033d..a8d71f538f7 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.61.2" +__version__ = "14.61.3" def get_default_company(user=None): From 4dc5d9a6cad5f353867a96ccd3b4d6eaee34dcf9 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 31 Jan 2024 12:49:22 +0530 Subject: [PATCH 227/675] fix: Exchange rate on MR to PO creation for muticurrency POs (#39646) fix: Exchange rate on MR to PO creation for muticurrency POs (#39646) (cherry picked from commit cfd1666181ffdff8ae79bbbb7863e1b12b3c6090) Co-authored-by: Deepesh Garg --- erpnext/stock/doctype/material_request/material_request.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index b84ccf770b2..b0a0158abf4 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -417,6 +417,7 @@ def make_purchase_order(source_name, target_doc=None, args=None): postprocess, ) + doclist.set_onload("load_after_mapping", False) return doclist From 350b2cdde39c2c318f286bc01c54ebdb681e8a2d Mon Sep 17 00:00:00 2001 From: Gursheen Kaur Anand <40693548+GursheenK@users.noreply.github.com> Date: Thu, 1 Feb 2024 10:13:29 +0530 Subject: [PATCH 228/675] fix: correctly calculate diff amount for included taxes (#39655) (cherry picked from commit 772f540bef28117c008512ead6558db801d395cd) --- .../doctype/payment_entry/payment_entry.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 66c82be596c..b89854b172a 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -874,19 +874,19 @@ class PaymentEntry(AccountsController): ) base_party_amount = flt(self.base_total_allocated_amount) + flt(base_unallocated_amount) - - if self.payment_type == "Receive": - self.difference_amount = base_party_amount - self.base_received_amount - elif self.payment_type == "Pay": - self.difference_amount = self.base_paid_amount - base_party_amount - else: - self.difference_amount = self.base_paid_amount - flt(self.base_received_amount) - - total_deductions = sum(flt(d.amount) for d in self.get("deductions")) included_taxes = self.get_included_taxes() + if self.payment_type == "Receive": + self.difference_amount = base_party_amount - self.base_received_amount + included_taxes + elif self.payment_type == "Pay": + self.difference_amount = self.base_paid_amount - base_party_amount - included_taxes + else: + self.difference_amount = self.base_paid_amount - flt(self.base_received_amount) - included_taxes + + total_deductions = sum(flt(d.amount) for d in self.get("deductions")) + self.difference_amount = flt( - self.difference_amount - total_deductions - included_taxes, self.precision("difference_amount") + self.difference_amount - total_deductions, self.precision("difference_amount") ) def get_included_taxes(self): From 2fbd11d646a9a3b1f18ff2ea0b250957f3aa216f Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 1 Feb 2024 14:53:01 +0530 Subject: [PATCH 229/675] refactor: move ignore ERR filters from SOA to General Ledger (cherry picked from commit c077eda64e8b57011c42fbf7b4fe41af9ab2bc2f) # Conflicts: # erpnext/accounts/report/general_ledger/general_ledger.py --- .../process_statement_of_accounts.py | 16 ++-------------- .../report/general_ledger/general_ledger.js | 6 ++++++ .../report/general_ledger/general_ledger.py | 19 +++++++++++++++++++ 3 files changed, 27 insertions(+), 14 deletions(-) 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 d4b4b37b4ee..222c9628018 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 @@ -64,18 +64,6 @@ def get_statement_dict(doc, get_statement_dict=False): statement_dict = {} ageing = "" - err_journals = None - if doc.report == "General Ledger" and doc.ignore_exchange_rate_revaluation_journals: - err_journals = frappe.db.get_all( - "Journal Entry", - filters={ - "company": doc.company, - "docstatus": 1, - "voucher_type": ("in", ["Exchange Rate Revaluation", "Exchange Gain Or Loss"]), - }, - as_list=True, - ) - for entry in doc.customers: if doc.include_ageing: ageing = set_ageing(doc, entry) @@ -88,8 +76,8 @@ def get_statement_dict(doc, get_statement_dict=False): ) filters = get_common_filters(doc) - if err_journals: - filters.update({"voucher_no_not_in": [x[0] for x in err_journals]}) + if doc.ignore_exchange_rate_revaluation_journals: + filters.update({"ignore_err": True}) if doc.report == "General Ledger": filters.update(get_gl_filters(doc, entry, tax_id, presentation_currency)) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js index f0ac3d0ffdb..2b5abcb7240 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.js +++ b/erpnext/accounts/report/general_ledger/general_ledger.js @@ -193,8 +193,14 @@ frappe.query_reports["General Ledger"] = { "fieldname": "show_remarks", "label": __("Show Remarks"), "fieldtype": "Check" + }, + { + "fieldname": "ignore_err", + "label": __("Ignore Exchange Rate Revaluation Journals"), + "fieldtype": "Check" } + ] } diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 95397452b01..ae772377f30 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -231,6 +231,25 @@ def get_conditions(filters): if filters.get("voucher_no"): conditions.append("voucher_no=%(voucher_no)s") +<<<<<<< HEAD +======= + if filters.get("against_voucher_no"): + conditions.append("against_voucher=%(against_voucher_no)s") + + if filters.get("ignore_err"): + err_journals = frappe.db.get_all( + "Journal Entry", + filters={ + "company": filters.get("company"), + "docstatus": 1, + "voucher_type": ("in", ["Exchange Rate Revaluation", "Exchange Gain Or Loss"]), + }, + as_list=True, + ) + if err_journals: + filters.update({"voucher_no_not_in": [x[0] for x in err_journals]}) + +>>>>>>> c077eda64e (refactor: move ignore ERR filters from SOA to General Ledger) if filters.get("voucher_no_not_in"): conditions.append("voucher_no not in %(voucher_no_not_in)s") From 686da470fa37674633b80c533b4c9f42f42929b7 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 1 Feb 2024 15:03:02 +0530 Subject: [PATCH 230/675] test: ignore_err filter out in General Ledger (cherry picked from commit affca3a519c626a2c3273a82f00ee1579853b0e1) --- .../general_ledger/test_general_ledger.py | 104 +++++++++++++++++- 1 file changed, 103 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/general_ledger/test_general_ledger.py b/erpnext/accounts/report/general_ledger/test_general_ledger.py index a8c362e78c1..c3ed7f2a23f 100644 --- a/erpnext/accounts/report/general_ledger/test_general_ledger.py +++ b/erpnext/accounts/report/general_ledger/test_general_ledger.py @@ -3,7 +3,7 @@ import frappe from frappe.tests.utils import FrappeTestCase -from frappe.utils import today +from frappe.utils import flt, today from erpnext.accounts.report.general_ledger.general_ledger import execute @@ -148,3 +148,105 @@ class TestGeneralLedger(FrappeTestCase): self.assertEqual(data[2]["credit"], 900) self.assertEqual(data[3]["debit"], 100) self.assertEqual(data[3]["credit"], 100) + + def test_ignore_exchange_rate_journals_filter(self): + # create a new account with USD currency + account_name = "Test Debtors USD" + company = "_Test Company" + account = frappe.get_doc( + { + "account_name": account_name, + "is_group": 0, + "company": company, + "root_type": "Asset", + "report_type": "Balance Sheet", + "account_currency": "USD", + "parent_account": "Accounts Receivable - _TC", + "account_type": "Receivable", + "doctype": "Account", + } + ) + account.insert(ignore_if_duplicate=True) + # create a JV to debit 1000 USD at 75 exchange rate + jv = frappe.new_doc("Journal Entry") + jv.posting_date = today() + jv.company = company + jv.multi_currency = 1 + jv.cost_center = "_Test Cost Center - _TC" + jv.set( + "accounts", + [ + { + "account": account.name, + "party_type": "Customer", + "party": "_Test Customer", + "debit_in_account_currency": 1000, + "credit_in_account_currency": 0, + "exchange_rate": 75, + "cost_center": "_Test Cost Center - _TC", + }, + { + "account": "Cash - _TC", + "debit_in_account_currency": 0, + "credit_in_account_currency": 75000, + "cost_center": "_Test Cost Center - _TC", + }, + ], + ) + jv.save() + jv.submit() + + revaluation = frappe.new_doc("Exchange Rate Revaluation") + revaluation.posting_date = today() + revaluation.company = company + accounts = revaluation.get_accounts_data() + revaluation.extend("accounts", accounts) + row = revaluation.accounts[0] + row.new_exchange_rate = 83 + row.new_balance_in_base_currency = flt( + row.new_exchange_rate * flt(row.balance_in_account_currency) + ) + row.gain_loss = row.new_balance_in_base_currency - flt(row.balance_in_base_currency) + revaluation.set_total_gain_loss() + revaluation = revaluation.save().submit() + + # post journal entry for Revaluation doc + frappe.db.set_value( + "Company", company, "unrealized_exchange_gain_loss_account", "_Test Exchange Gain/Loss - _TC" + ) + revaluation_jv = revaluation.make_jv_for_revaluation() + revaluation_jv.cost_center = "_Test Cost Center - _TC" + for acc in revaluation_jv.get("accounts"): + acc.cost_center = "_Test Cost Center - _TC" + revaluation_jv.save() + revaluation_jv.submit() + + # With ignore_err enabled + columns, data = execute( + frappe._dict( + { + "company": company, + "from_date": today(), + "to_date": today(), + "account": [account.name], + "group_by": "Group by Voucher (Consolidated)", + "ignore_err": True, + } + ) + ) + self.assertNotIn(revaluation_jv.name, set([x.voucher_no for x in data])) + + # Without ignore_err enabled + columns, data = execute( + frappe._dict( + { + "company": company, + "from_date": today(), + "to_date": today(), + "account": [account.name], + "group_by": "Group by Voucher (Consolidated)", + "ignore_err": False, + } + ) + ) + self.assertIn(revaluation_jv.name, set([x.voucher_no for x in data])) From 6284553f23fab6c3d2152680e6a530ffb1a03722 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 1 Feb 2024 15:46:57 +0530 Subject: [PATCH 231/675] refactor(test): use party with USD billing currency (cherry picked from commit beff566c8267104b97f7e4b80c36715e4eb91832) --- erpnext/accounts/report/general_ledger/test_general_ledger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/general_ledger/test_general_ledger.py b/erpnext/accounts/report/general_ledger/test_general_ledger.py index c3ed7f2a23f..75f94309bcc 100644 --- a/erpnext/accounts/report/general_ledger/test_general_ledger.py +++ b/erpnext/accounts/report/general_ledger/test_general_ledger.py @@ -179,7 +179,7 @@ class TestGeneralLedger(FrappeTestCase): { "account": account.name, "party_type": "Customer", - "party": "_Test Customer", + "party": "_Test Customer USD", "debit_in_account_currency": 1000, "credit_in_account_currency": 0, "exchange_rate": 75, From 68c5a6e86f38522681837ff45a816555d6c923bf Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 1 Feb 2024 16:23:26 +0530 Subject: [PATCH 232/675] chore: resolve conflict --- erpnext/accounts/report/general_ledger/general_ledger.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index ae772377f30..269d25b99db 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -231,11 +231,6 @@ def get_conditions(filters): if filters.get("voucher_no"): conditions.append("voucher_no=%(voucher_no)s") -<<<<<<< HEAD -======= - if filters.get("against_voucher_no"): - conditions.append("against_voucher=%(against_voucher_no)s") - if filters.get("ignore_err"): err_journals = frappe.db.get_all( "Journal Entry", @@ -249,7 +244,6 @@ def get_conditions(filters): if err_journals: filters.update({"voucher_no_not_in": [x[0] for x in err_journals]}) ->>>>>>> c077eda64e (refactor: move ignore ERR filters from SOA to General Ledger) if filters.get("voucher_no_not_in"): conditions.append("voucher_no not in %(voucher_no_not_in)s") From 8035f5b951db8c9eee392340afd8a21e380df7c6 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 1 Feb 2024 16:05:27 +0530 Subject: [PATCH 233/675] refactor: use pop up to inform of possible data issue and leave a comment in communcation trail as well (cherry picked from commit 78483e2ee6fdef9e36b63408f6796f0a1e78dac1) --- erpnext/accounts/doctype/account/account.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py index 22ddc2ffae3..283e9d2f4ef 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -58,6 +58,7 @@ class Account(NestedSet): self.validate_balance_must_be_debit_or_credit() self.validate_account_currency() self.validate_root_company_and_sync_account_to_children() + self.validate_receivable_payable_account_type() def validate_parent(self): """Fetch Parent Details and validate parent account""" @@ -114,6 +115,24 @@ class Account(NestedSet): "Balance Sheet" if self.root_type in ("Asset", "Liability", "Equity") else "Profit and Loss" ) + def validate_receivable_payable_account_type(self): + doc_before_save = self.get_doc_before_save() + receivable_payable_types = ["Receivable", "Payable"] + if ( + doc_before_save + and doc_before_save.account_type in receivable_payable_types + and doc_before_save.account_type != self.account_type + ): + # check for ledger entries + if frappe.db.get_all("GL Entry", filters={"account": self.name, "is_cancelled": 0}, limit=1): + 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.msgprint(msg) + self.add_comment("Comment", msg) + def validate_root_details(self): # does not exists parent if frappe.db.exists("Account", self.name): From f8ca5c5c8bf0783ffd525571d157d4e2d597c8e4 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 2 Feb 2024 13:23:54 +0530 Subject: [PATCH 234/675] chore: flag for barcode scanner --- erpnext/public/js/utils/barcode_scanner.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/public/js/utils/barcode_scanner.js b/erpnext/public/js/utils/barcode_scanner.js index f1b53cb0721..aadbb24cc04 100644 --- a/erpnext/public/js/utils/barcode_scanner.js +++ b/erpnext/public/js/utils/barcode_scanner.js @@ -1,6 +1,7 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { constructor(opts) { this.frm = opts.frm; + // frappe.flags.trigger_from_barcode_scanner is used for custom scripts // field from which to capture input of scanned data this.scan_field_name = opts.scan_field_name || "scan_barcode"; @@ -84,6 +85,7 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { update_table(data) { return new Promise(resolve => { let cur_grid = this.frm.fields_dict[this.items_table_name].grid; + frappe.flags.trigger_from_barcode_scanner = true; const {item_code, barcode, batch_no, serial_no, uom} = data; @@ -143,12 +145,14 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { revert_selector_flag() { frappe.flags.hide_serial_batch_dialog = false; + frappe.flags.trigger_from_barcode_scanner = false; } set_item(row, item_code, barcode, batch_no, serial_no) { return new Promise(resolve => { const increment = async (value = 1) => { const item_data = {item_code: item_code}; + frappe.flags.trigger_from_barcode_scanner = true; item_data[this.qty_field] = Number((row[this.qty_field] || 0)) + Number(value); await frappe.model.set_value(row.doctype, row.name, item_data); return value; From 57cfce08ef2b24c4ef3f6629f0584e6e08c99991 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 2 Feb 2024 13:23:54 +0530 Subject: [PATCH 235/675] chore: flag for barcode scanner (cherry picked from commit f8ca5c5c8bf0783ffd525571d157d4e2d597c8e4) --- erpnext/public/js/utils/barcode_scanner.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/public/js/utils/barcode_scanner.js b/erpnext/public/js/utils/barcode_scanner.js index f1b53cb0721..aadbb24cc04 100644 --- a/erpnext/public/js/utils/barcode_scanner.js +++ b/erpnext/public/js/utils/barcode_scanner.js @@ -1,6 +1,7 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { constructor(opts) { this.frm = opts.frm; + // frappe.flags.trigger_from_barcode_scanner is used for custom scripts // field from which to capture input of scanned data this.scan_field_name = opts.scan_field_name || "scan_barcode"; @@ -84,6 +85,7 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { update_table(data) { return new Promise(resolve => { let cur_grid = this.frm.fields_dict[this.items_table_name].grid; + frappe.flags.trigger_from_barcode_scanner = true; const {item_code, barcode, batch_no, serial_no, uom} = data; @@ -143,12 +145,14 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { revert_selector_flag() { frappe.flags.hide_serial_batch_dialog = false; + frappe.flags.trigger_from_barcode_scanner = false; } set_item(row, item_code, barcode, batch_no, serial_no) { return new Promise(resolve => { const increment = async (value = 1) => { const item_data = {item_code: item_code}; + frappe.flags.trigger_from_barcode_scanner = true; item_data[this.qty_field] = Number((row[this.qty_field] || 0)) + Number(value); await frappe.model.set_value(row.doctype, row.name, item_data); return value; From 58ea7f41058ebad832e37e131cad84d4e71a57d0 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 2 Feb 2024 17:50:19 +0530 Subject: [PATCH 236/675] refactor: add 'disabled' field to Bank Account --- erpnext/accounts/doctype/bank_account/bank_account.json | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/bank_account/bank_account.json b/erpnext/accounts/doctype/bank_account/bank_account.json index 41d79479ca5..b1d53dc1e70 100644 --- a/erpnext/accounts/doctype/bank_account/bank_account.json +++ b/erpnext/accounts/doctype/bank_account/bank_account.json @@ -13,6 +13,7 @@ "account_type", "account_subtype", "column_break_7", + "disabled", "is_default", "is_company_account", "company", @@ -199,10 +200,16 @@ "fieldtype": "Data", "in_global_search": 1, "label": "Branch Code" + }, + { + "default": "0", + "fieldname": "disabled", + "fieldtype": "Check", + "label": "Disabled" } ], "links": [], - "modified": "2022-05-04 15:49:42.620630", + "modified": "2024-02-02 17:50:09.768835", "modified_by": "Administrator", "module": "Accounts", "name": "Bank Account", From 3808ddbf860d9b5e88f544bb6a2108b22aaaca5c Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sat, 3 Feb 2024 09:10:28 +0530 Subject: [PATCH 237/675] feat: New financial views - Growth and margin views for P&L and balance sheet (backport #39588) (#39601) feat: New financial views - Growth and margin views for P&L and balance sheet (cherry picked from commit 92649de5c647b1a03689fd77e6a01c29175c65a9) # Conflicts: # erpnext/accounts/report/balance_sheet/balance_sheet.js # erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js Co-authored-by: nitmit --- .../report/balance_sheet/balance_sheet.js | 29 +++++++++++ .../profit_and_loss_statement.js | 23 +++++++++ erpnext/public/js/financial_statements.js | 51 +++++++++++++++++++ 3 files changed, 103 insertions(+) diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.js b/erpnext/accounts/report/balance_sheet/balance_sheet.js index f1f8e5f6e7c..1d5d870cbfc 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.js +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.js @@ -6,6 +6,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { erpnext.utils.add_dimensions('Balance Sheet', 10); +<<<<<<< HEAD frappe.query_reports["Balance Sheet"]["filters"].push({ "fieldname": "accumulated_values", "label": __("Accumulated Values"), @@ -19,4 +20,32 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "fieldtype": "Check", "default": 1 }); +======= +frappe.query_reports["Balance Sheet"]["filters"].push( + { + "fieldname": "selected_view", + "label": __("Select View"), + "fieldtype": "Select", + "options": [ + { "value": "Report", "label": __("Report View") }, + { "value": "Growth", "label": __("Growth View") } + ], + "default": "Report", + "reqd": 1 + }, +); + +frappe.query_reports["Balance Sheet"]["filters"].push({ + fieldname: "accumulated_values", + label: __("Accumulated Values"), + fieldtype: "Check", + default: 1, +}); + +frappe.query_reports["Balance Sheet"]["filters"].push({ + fieldname: "include_default_book_entries", + label: __("Include Default FB Entries"), + fieldtype: "Check", + default: 1, +>>>>>>> 92649de5c6 (Adding growth and margin views for P&L and balance sheet financial reports in collaboration with Sapcon Instruments Pvt Ltd) }); diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js index e794f270c2b..d2a2c4b46f5 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js @@ -6,6 +6,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { frappe.query_reports["Profit and Loss Statement"] = $.extend({}, erpnext.financial_statements); +<<<<<<< HEAD erpnext.utils.add_dimensions('Profit and Loss Statement', 10); frappe.query_reports["Profit and Loss Statement"]["filters"].push( @@ -16,4 +17,26 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "default": 1 } ); +======= +frappe.query_reports["Profit and Loss Statement"]["filters"].push( + { + "fieldname": "selected_view", + "label": __("Select View"), + "fieldtype": "Select", + "options": [ + { "value": "Report", "label": __("Report View") }, + { "value": "Growth", "label": __("Growth View") }, + { "value": "Margin", "label": __("Margin View") }, + ], + "default": "Report", + "reqd": 1 + }, +); + +frappe.query_reports["Profit and Loss Statement"]["filters"].push({ + fieldname: "accumulated_values", + label: __("Accumulated Values"), + fieldtype: "Check", + default: 1, +>>>>>>> 92649de5c6 (Adding growth and margin views for P&L and balance sheet financial reports in collaboration with Sapcon Instruments Pvt Ltd) }); diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index 5e1974299ee..14b99aa6308 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -2,7 +2,58 @@ frappe.provide("erpnext.financial_statements"); erpnext.financial_statements = { "filters": get_filters(), + "baseData": null, "formatter": function(value, row, column, data, default_formatter, filter) { + if(frappe.query_report.get_filter_value("selected_view") == "Growth" && 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 = (Math.round(annualGrowth*10000)/100); //calculating the rounded off percentage + + 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; + + value = $(`${marginPercent+'%'}`); + if(marginPercent < 0) + value = $(value).addClass("text-danger"); + else + value = $(value).addClass("text-success"); + value = $(value).wrap("

    ").parent().html(); + return value; + } + + } + if (data && column.fieldname=="account") { value = data.account_name || value; From f01308b97288ac630bf9221df897f13071183b24 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 3 Feb 2024 09:11:12 +0530 Subject: [PATCH 238/675] Revert "feat: New financial views - Growth and margin views for P&L and balance sheet (#39588)" MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revert "feat: New financial views - Growth and margin views for P&L and balan…" This reverts commit 3808ddbf860d9b5e88f544bb6a2108b22aaaca5c. --- .../report/balance_sheet/balance_sheet.js | 29 ----------- .../profit_and_loss_statement.js | 23 --------- erpnext/public/js/financial_statements.js | 51 ------------------- 3 files changed, 103 deletions(-) diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.js b/erpnext/accounts/report/balance_sheet/balance_sheet.js index 1d5d870cbfc..f1f8e5f6e7c 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.js +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.js @@ -6,7 +6,6 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { erpnext.utils.add_dimensions('Balance Sheet', 10); -<<<<<<< HEAD frappe.query_reports["Balance Sheet"]["filters"].push({ "fieldname": "accumulated_values", "label": __("Accumulated Values"), @@ -20,32 +19,4 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "fieldtype": "Check", "default": 1 }); -======= -frappe.query_reports["Balance Sheet"]["filters"].push( - { - "fieldname": "selected_view", - "label": __("Select View"), - "fieldtype": "Select", - "options": [ - { "value": "Report", "label": __("Report View") }, - { "value": "Growth", "label": __("Growth View") } - ], - "default": "Report", - "reqd": 1 - }, -); - -frappe.query_reports["Balance Sheet"]["filters"].push({ - fieldname: "accumulated_values", - label: __("Accumulated Values"), - fieldtype: "Check", - default: 1, -}); - -frappe.query_reports["Balance Sheet"]["filters"].push({ - fieldname: "include_default_book_entries", - label: __("Include Default FB Entries"), - fieldtype: "Check", - default: 1, ->>>>>>> 92649de5c6 (Adding growth and margin views for P&L and balance sheet financial reports in collaboration with Sapcon Instruments Pvt Ltd) }); diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js index d2a2c4b46f5..e794f270c2b 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js @@ -6,7 +6,6 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { frappe.query_reports["Profit and Loss Statement"] = $.extend({}, erpnext.financial_statements); -<<<<<<< HEAD erpnext.utils.add_dimensions('Profit and Loss Statement', 10); frappe.query_reports["Profit and Loss Statement"]["filters"].push( @@ -17,26 +16,4 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "default": 1 } ); -======= -frappe.query_reports["Profit and Loss Statement"]["filters"].push( - { - "fieldname": "selected_view", - "label": __("Select View"), - "fieldtype": "Select", - "options": [ - { "value": "Report", "label": __("Report View") }, - { "value": "Growth", "label": __("Growth View") }, - { "value": "Margin", "label": __("Margin View") }, - ], - "default": "Report", - "reqd": 1 - }, -); - -frappe.query_reports["Profit and Loss Statement"]["filters"].push({ - fieldname: "accumulated_values", - label: __("Accumulated Values"), - fieldtype: "Check", - default: 1, ->>>>>>> 92649de5c6 (Adding growth and margin views for P&L and balance sheet financial reports in collaboration with Sapcon Instruments Pvt Ltd) }); diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index 14b99aa6308..5e1974299ee 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -2,58 +2,7 @@ frappe.provide("erpnext.financial_statements"); erpnext.financial_statements = { "filters": get_filters(), - "baseData": null, "formatter": function(value, row, column, data, default_formatter, filter) { - if(frappe.query_report.get_filter_value("selected_view") == "Growth" && 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 = (Math.round(annualGrowth*10000)/100); //calculating the rounded off percentage - - 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; - - value = $(`${marginPercent+'%'}`); - if(marginPercent < 0) - value = $(value).addClass("text-danger"); - else - value = $(value).addClass("text-success"); - value = $(value).wrap("

    ").parent().html(); - return value; - } - - } - if (data && column.fieldname=="account") { value = data.account_name || value; From e49f8d5f55b0cafda012475e94d48e11577a86e4 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sat, 3 Feb 2024 14:05:47 +0530 Subject: [PATCH 239/675] fix: don't overwrite existing terms in transaction (#39519) * fix: don't overwrite existing terms in transaction (cherry picked from commit 77b044f1a6e7273b21ce5a884429875b10d3bb2e) * refactor: keep the diff small --------- Co-authored-by: barredterra <14891507+barredterra@users.noreply.github.com> --- 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 48bde9dbc98..d5bc7647647 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -746,14 +746,14 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe } let selling_doctypes_for_tc = ["Sales Invoice", "Quotation", "Sales Order", "Delivery Note"]; if (company_doc.default_selling_terms && frappe.meta.has_field(me.frm.doc.doctype, "tc_name") && - selling_doctypes_for_tc.indexOf(me.frm.doc.doctype) != -1) { + selling_doctypes_for_tc.includes(me.frm.doc.doctype) && !me.frm.doc.tc_name) { me.frm.set_value("tc_name", company_doc.default_selling_terms); } let buying_doctypes_for_tc = ["Request for Quotation", "Supplier Quotation", "Purchase Order", "Material Request", "Purchase Receipt"]; // Purchase Invoice is excluded as per issue #3345 if (company_doc.default_buying_terms && frappe.meta.has_field(me.frm.doc.doctype, "tc_name") && - buying_doctypes_for_tc.indexOf(me.frm.doc.doctype) != -1) { + buying_doctypes_for_tc.includes(me.frm.doc.doctype) && !me.frm.doc.tc_name) { me.frm.set_value("tc_name", company_doc.default_buying_terms); } From e9c2f6d6f1051d7c3b74a0d82fe7a12dbcac80a9 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Mon, 5 Feb 2024 11:05:45 +0530 Subject: [PATCH 240/675] fix: message for subscription when no current inv --- erpnext/accounts/doctype/subscription/subscription.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py index 9dab4e91fba..836043d1cd4 100644 --- a/erpnext/accounts/doctype/subscription/subscription.py +++ b/erpnext/accounts/doctype/subscription/subscription.py @@ -594,7 +594,7 @@ class Subscription(Document): """ current_invoice = self.get_current_invoice() if not current_invoice: - frappe.throw(_("Current invoice {0} is missing").format(current_invoice.invoice)) + frappe.throw(_("Subscription {0}: Current invoice is missing.").format(frappe.bold(self.name))) else: if not self.has_outstanding_invoice(): self.status = "Active" From 4a609d8fa8b8f0688e883382181c80063a7cdc1f Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 1 Feb 2024 19:29:06 +0530 Subject: [PATCH 241/675] fix: incorrect landed cost voucher amount (cherry picked from commit d78a1e78148a3702990ae347a2b1c31b580c308e) --- .../doctype/landed_cost_voucher/landed_cost_voucher.py | 7 +++++++ .../stock/doctype/purchase_receipt/purchase_receipt.py | 10 +++++----- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py index 3511cec2fd7..c73dc65f8a8 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py @@ -122,6 +122,13 @@ class LandedCostVoucher(Document): self.get("items")[item_count - 1].applicable_charges += diff def validate_applicable_charges_for_item(self): + if self.distribute_charges_based_on == "Distribute Manually" and len(self.taxes) > 1: + frappe.throw( + _( + "Please keep one Applicable Charges, when 'Distribute Charges Based On' is 'Distribute Manually'. For more charges, please create another Landed Cost Voucher." + ) + ) + based_on = self.distribute_charges_based_on.lower() if based_on != "distribute manually": diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index d44bb4d26d8..274073b638e 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -1200,16 +1200,16 @@ def get_item_account_wise_additional_cost(purchase_document): for lcv in landed_cost_vouchers: landed_cost_voucher_doc = frappe.get_doc("Landed Cost Voucher", lcv.parent) + based_on_field = None # Use amount field for total item cost for manually cost distributed LCVs - if landed_cost_voucher_doc.distribute_charges_based_on == "Distribute Manually": - based_on_field = "amount" - else: + if landed_cost_voucher_doc.distribute_charges_based_on != "Distribute Manually": based_on_field = frappe.scrub(landed_cost_voucher_doc.distribute_charges_based_on) total_item_cost = 0 - for item in landed_cost_voucher_doc.items: - total_item_cost += item.get(based_on_field) + if based_on_field: + for item in landed_cost_voucher_doc.items: + total_item_cost += item.get(based_on_field) for item in landed_cost_voucher_doc.items: if item.receipt_document == purchase_document: From 85e6b39e23ae468e88eb49af9f5981ac854a5ac3 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 31 Jan 2024 11:32:17 +0530 Subject: [PATCH 242/675] perf: timeout for auto material request through reorder level (cherry picked from commit 951023f434a36ef03f874b3dcbd4f995168b7b5a) # Conflicts: # erpnext/stock/doctype/stock_entry/test_stock_entry.py --- erpnext/controllers/selling_controller.py | 2 +- .../doctype/stock_entry/test_stock_entry.py | 84 +++++++++ erpnext/stock/get_item_details.py | 7 +- erpnext/stock/reorder_item.py | 163 +++++++++++++----- 4 files changed, 214 insertions(+), 42 deletions(-) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index d55aeeacc08..c9b12f4f723 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -586,7 +586,7 @@ class SellingController(StockController): if self.doctype in ["Sales Order", "Quotation"]: for item in self.items: item.gross_profit = flt( - ((item.base_rate - item.valuation_rate) * item.stock_qty), self.precision("amount", item) + ((item.base_rate - flt(item.valuation_rate)) * item.stock_qty), self.precision("amount", item) ) def set_customer_address(self): diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index 8afe23a1d7a..92430861c05 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -1734,6 +1734,90 @@ class TestStockEntry(FrappeTestCase): self.assertFalse(doc.is_enqueue_action()) frappe.flags.in_test = True +<<<<<<< HEAD +======= + def test_negative_batch(self): + item_code = "Test Negative Batch Item - 001" + make_item( + item_code, + {"has_batch_no": 1, "create_new_batch": 1, "batch_naming_series": "Test-BCH-NNS.#####"}, + ) + + se1 = make_stock_entry( + item_code=item_code, + purpose="Material Receipt", + qty=100, + target="_Test Warehouse - _TC", + ) + + se1.reload() + + batch_no = get_batch_from_bundle(se1.items[0].serial_and_batch_bundle) + + se2 = make_stock_entry( + item_code=item_code, + purpose="Material Issue", + batch_no=batch_no, + qty=10, + source="_Test Warehouse - _TC", + ) + + se2.reload() + + se3 = make_stock_entry( + item_code=item_code, + purpose="Material Receipt", + qty=100, + target="_Test Warehouse - _TC", + ) + + se3.reload() + + self.assertRaises(frappe.ValidationError, se1.cancel) + + def test_auto_reorder_level(self): + from erpnext.stock.reorder_item import reorder_item + + item_doc = make_item( + "Test Auto Reorder Item - 001", + properties={"stock_uom": "Kg", "purchase_uom": "Nos", "is_stock_item": 1}, + 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=["qty", "stock_uom", "stock_qty"], + filters={"item_code": item_doc.name, "uom": "Nos"}, + ) + + for mri in mrs: + self.assertEqual(mri.stock_uom, "Kg") + self.assertEqual(mri.stock_qty, 10) + self.assertEqual(mri.qty, 2) + + for mr in mr_list: + mr.cancel() + mr.delete() + +>>>>>>> 951023f434 (perf: timeout for auto material request through reorder level) def make_serialized_item(**args): args = frappe._dict(args) diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 10d3ef4b7a9..37a6a0d6892 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -87,7 +87,8 @@ def get_item_details(args, doc=None, for_validate=False, overwrite_warehouse=Tru get_party_item_code(args, item, out) - set_valuation_rate(out, args) + if args.get("doctype") in ["Sales Order", "Quotation"]: + set_valuation_rate(out, args) update_party_blanket_order(args, out) @@ -303,7 +304,9 @@ def get_basic_details(args, item, overwrite_warehouse=True): if not item: item = frappe.get_doc("Item", args.get("item_code")) - if item.variant_of and not item.taxes: + if ( + item.variant_of and not item.taxes and frappe.db.exists("Item Tax", {"parent": item.variant_of}) + ): item.update_template_tables() item_defaults = get_item_defaults(item.name, args.company) diff --git a/erpnext/stock/reorder_item.py b/erpnext/stock/reorder_item.py index e172cecb236..de4242893c6 100644 --- a/erpnext/stock/reorder_item.py +++ b/erpnext/stock/reorder_item.py @@ -34,73 +34,157 @@ def _reorder_item(): erpnext.get_default_company() or frappe.db.sql("""select name from tabCompany limit 1""")[0][0] ) - items_to_consider = frappe.db.sql_list( - """select name from `tabItem` item - where is_stock_item=1 and has_variants=0 - and disabled=0 - and (end_of_life is null or end_of_life='0000-00-00' or end_of_life > %(today)s) - and (exists (select name from `tabItem Reorder` ir where ir.parent=item.name) - or (variant_of is not null and variant_of != '' - and exists (select name from `tabItem Reorder` ir where ir.parent=item.variant_of)) - )""", - {"today": nowdate()}, - ) + items_to_consider = get_items_for_reorder() if not items_to_consider: return item_warehouse_projected_qty = get_item_warehouse_projected_qty(items_to_consider) - def add_to_material_request( - item_code, warehouse, reorder_level, reorder_qty, material_request_type, warehouse_group=None - ): - if warehouse not in warehouse_company: + def add_to_material_request(**kwargs): + if isinstance(kwargs, dict): + kwargs = frappe._dict(kwargs) + + if kwargs.warehouse not in warehouse_company: # a disabled warehouse return - reorder_level = flt(reorder_level) - reorder_qty = flt(reorder_qty) + reorder_level = flt(kwargs.reorder_level) + reorder_qty = flt(kwargs.reorder_qty) # projected_qty will be 0 if Bin does not exist - if warehouse_group: - projected_qty = flt(item_warehouse_projected_qty.get(item_code, {}).get(warehouse_group)) + if kwargs.warehouse_group: + projected_qty = flt( + item_warehouse_projected_qty.get(kwargs.item_code, {}).get(kwargs.warehouse_group) + ) else: - projected_qty = flt(item_warehouse_projected_qty.get(item_code, {}).get(warehouse)) + projected_qty = flt( + item_warehouse_projected_qty.get(kwargs.item_code, {}).get(kwargs.warehouse) + ) if (reorder_level or reorder_qty) and projected_qty < reorder_level: deficiency = reorder_level - projected_qty if deficiency > reorder_qty: reorder_qty = deficiency - company = warehouse_company.get(warehouse) or default_company + company = warehouse_company.get(kwargs.warehouse) or default_company - material_requests[material_request_type].setdefault(company, []).append( - {"item_code": item_code, "warehouse": warehouse, "reorder_qty": reorder_qty} + material_requests[kwargs.material_request_type].setdefault(company, []).append( + { + "item_code": kwargs.item_code, + "warehouse": kwargs.warehouse, + "reorder_qty": reorder_qty, + "item_details": kwargs.item_details, + } ) - for item_code in items_to_consider: - item = frappe.get_doc("Item", item_code) + for item_code, reorder_levels in items_to_consider.items(): + for d in reorder_levels: + if d.has_variants: + continue - if item.variant_of and not item.get("reorder_levels"): - item.update_template_tables() - - if item.get("reorder_levels"): - for d in item.get("reorder_levels"): - add_to_material_request( - item_code, - d.warehouse, - d.warehouse_reorder_level, - d.warehouse_reorder_qty, - d.material_request_type, - warehouse_group=d.warehouse_group, - ) + add_to_material_request( + item_code=item_code, + warehouse=d.warehouse, + reorder_level=d.warehouse_reorder_level, + reorder_qty=d.warehouse_reorder_qty, + material_request_type=d.material_request_type, + warehouse_group=d.warehouse_group, + item_details=frappe._dict( + { + "item_code": item_code, + "name": item_code, + "item_name": d.item_name, + "item_group": d.item_group, + "brand": d.brand, + "description": d.description, + "stock_uom": d.stock_uom, + "purchase_uom": d.purchase_uom, + } + ), + ) if material_requests: return create_material_request(material_requests) +def get_items_for_reorder() -> dict[str, list]: + reorder_table = frappe.qb.DocType("Item Reorder") + item_table = frappe.qb.DocType("Item") + + query = ( + frappe.qb.from_(reorder_table) + .inner_join(item_table) + .on(reorder_table.parent == item_table.name) + .select( + reorder_table.warehouse, + reorder_table.warehouse_group, + reorder_table.material_request_type, + reorder_table.warehouse_reorder_level, + reorder_table.warehouse_reorder_qty, + item_table.name, + item_table.stock_uom, + item_table.purchase_uom, + item_table.description, + item_table.item_name, + item_table.item_group, + item_table.brand, + item_table.variant_of, + item_table.has_variants, + ) + .where( + (item_table.disabled == 0) + & (item_table.is_stock_item == 1) + & ( + (item_table.end_of_life.isnull()) + | (item_table.end_of_life > nowdate()) + | (item_table.end_of_life == "0000-00-00") + ) + ) + ) + + data = query.run(as_dict=True) + itemwise_reorder = frappe._dict({}) + for d in data: + itemwise_reorder.setdefault(d.name, []).append(d) + + itemwise_reorder = get_reorder_levels_for_variants(itemwise_reorder) + + return itemwise_reorder + + +def get_reorder_levels_for_variants(itemwise_reorder): + item_table = frappe.qb.DocType("Item") + + query = ( + frappe.qb.from_(item_table) + .select( + item_table.name, + item_table.variant_of, + ) + .where( + (item_table.disabled == 0) + & (item_table.is_stock_item == 1) + & ( + (item_table.end_of_life.isnull()) + | (item_table.end_of_life > nowdate()) + | (item_table.end_of_life == "0000-00-00") + ) + & (item_table.variant_of.notnull()) + ) + ) + + variants_item = query.run(as_dict=True) + for row in variants_item: + if not itemwise_reorder.get(row.name) and itemwise_reorder.get(row.variant_of): + itemwise_reorder.setdefault(row.name, []).extend(itemwise_reorder.get(row.variant_of, [])) + + return itemwise_reorder + + def get_item_warehouse_projected_qty(items_to_consider): item_warehouse_projected_qty = {} + items_to_consider = list(items_to_consider.keys()) for item_code, warehouse, projected_qty in frappe.db.sql( """select item_code, warehouse, projected_qty @@ -164,7 +248,7 @@ def create_material_request(material_requests): for d in items: d = frappe._dict(d) - item = frappe.get_doc("Item", d.item_code) + item = d.get("item_details") uom = item.stock_uom conversion_factor = 1.0 @@ -190,6 +274,7 @@ def create_material_request(material_requests): "item_code": d.item_code, "schedule_date": add_days(nowdate(), cint(item.lead_time_days)), "qty": qty, + "conversion_factor": conversion_factor, "uom": uom, "stock_uom": item.stock_uom, "warehouse": d.warehouse, From b32848d69dfb6614b013eca0df328e7923523a21 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 5 Feb 2024 11:46:39 +0530 Subject: [PATCH 243/675] perf: memory consumption for the stock balance report (#39626) (cherry picked from commit b70f3de16be169a723842a3a99c046a3809d0768) # Conflicts: # erpnext/stock/report/stock_balance/stock_balance.py --- .../report/stock_balance/stock_balance.py | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py index 7a5a8615d0c..4e6f9865aca 100644 --- a/erpnext/stock/report/stock_balance/stock_balance.py +++ b/erpnext/stock/report/stock_balance/stock_balance.py @@ -90,8 +90,7 @@ class StockBalanceReport(object): self.opening_data.setdefault(group_by_key, entry) def prepare_new_data(self): - if not self.sle_entries: - return + self.item_warehouse_map = self.get_item_warehouse_map() if self.filters.get("show_stock_ageing_data"): self.filters["show_warehouse_wise_stock"] = True @@ -99,7 +98,13 @@ class StockBalanceReport(object): _func = itemgetter(1) +<<<<<<< HEAD self.item_warehouse_map = self.get_item_warehouse_map() +======= + del self.sle_entries + + sre_details = self.get_sre_reserved_qty_details() +>>>>>>> b70f3de16b (perf: memory consumption for the stock balance report (#39626)) variant_values = {} if self.filters.get("show_variant_attributes"): @@ -139,15 +144,22 @@ class StockBalanceReport(object): item_warehouse_map = {} self.opening_vouchers = self.get_opening_vouchers() - for entry in self.sle_entries: - group_by_key = self.get_group_by_key(entry) - if group_by_key not in item_warehouse_map: - self.initialize_data(item_warehouse_map, group_by_key, entry) + if self.filters.get("show_stock_ageing_data"): + self.sle_entries = self.sle_query.run(as_dict=True) - self.prepare_item_warehouse_map(item_warehouse_map, entry, group_by_key) + with frappe.db.unbuffered_cursor(): + if not self.filters.get("show_stock_ageing_data"): + self.sle_entries = self.sle_query.run(as_dict=True, as_iterator=True) - if self.opening_data.get(group_by_key): - del self.opening_data[group_by_key] + for entry in self.sle_entries: + group_by_key = self.get_group_by_key(entry) + if group_by_key not in item_warehouse_map: + self.initialize_data(item_warehouse_map, group_by_key, entry) + + self.prepare_item_warehouse_map(item_warehouse_map, entry, group_by_key) + + if self.opening_data.get(group_by_key): + del self.opening_data[group_by_key] for group_by_key, entry in self.opening_data.items(): if group_by_key not in item_warehouse_map: @@ -236,7 +248,8 @@ class StockBalanceReport(object): .where( (table.docstatus == 1) & (table.company == self.filters.company) - & ((table.to_date <= self.from_date)) + & (table.to_date <= self.from_date) + & (table.status == "Completed") ) .orderby(table.to_date, order=Order.desc) .limit(1) @@ -289,7 +302,7 @@ class StockBalanceReport(object): if self.filters.get("company"): query = query.where(sle.company == self.filters.get("company")) - self.sle_entries = query.run(as_dict=True) + self.sle_query = query def apply_inventory_dimensions_filters(self, query, sle) -> str: inventory_dimension_fields = self.get_inventory_dimension_fields() From 26dfbb7a641a4a3342030daad6e1c21ff3175103 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 5 Feb 2024 12:13:14 +0530 Subject: [PATCH 244/675] chore: fix conflicts --- erpnext/stock/report/stock_balance/stock_balance.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py index 4e6f9865aca..32a2b302d7b 100644 --- a/erpnext/stock/report/stock_balance/stock_balance.py +++ b/erpnext/stock/report/stock_balance/stock_balance.py @@ -98,14 +98,8 @@ class StockBalanceReport(object): _func = itemgetter(1) -<<<<<<< HEAD - self.item_warehouse_map = self.get_item_warehouse_map() -======= del self.sle_entries - sre_details = self.get_sre_reserved_qty_details() ->>>>>>> b70f3de16b (perf: memory consumption for the stock balance report (#39626)) - variant_values = {} if self.filters.get("show_variant_attributes"): variant_values = self.get_variant_values_for() From e38bb836a5c4b58f8973e5389b63348006a49f86 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 5 Feb 2024 13:20:03 +0530 Subject: [PATCH 245/675] chore: fix conflicts --- .../doctype/stock_entry/test_stock_entry.py | 42 ------------------- 1 file changed, 42 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index 92430861c05..1d7e4da26d5 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -1734,47 +1734,6 @@ class TestStockEntry(FrappeTestCase): self.assertFalse(doc.is_enqueue_action()) frappe.flags.in_test = True -<<<<<<< HEAD -======= - def test_negative_batch(self): - item_code = "Test Negative Batch Item - 001" - make_item( - item_code, - {"has_batch_no": 1, "create_new_batch": 1, "batch_naming_series": "Test-BCH-NNS.#####"}, - ) - - se1 = make_stock_entry( - item_code=item_code, - purpose="Material Receipt", - qty=100, - target="_Test Warehouse - _TC", - ) - - se1.reload() - - batch_no = get_batch_from_bundle(se1.items[0].serial_and_batch_bundle) - - se2 = make_stock_entry( - item_code=item_code, - purpose="Material Issue", - batch_no=batch_no, - qty=10, - source="_Test Warehouse - _TC", - ) - - se2.reload() - - se3 = make_stock_entry( - item_code=item_code, - purpose="Material Receipt", - qty=100, - target="_Test Warehouse - _TC", - ) - - se3.reload() - - self.assertRaises(frappe.ValidationError, se1.cancel) - def test_auto_reorder_level(self): from erpnext.stock.reorder_item import reorder_item @@ -1817,7 +1776,6 @@ class TestStockEntry(FrappeTestCase): mr.cancel() mr.delete() ->>>>>>> 951023f434 (perf: timeout for auto material request through reorder level) def make_serialized_item(**args): args = frappe._dict(args) From 7952bf43187c88b1c5ce5fd4a84ec45f526d72a2 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 14:05:24 +0530 Subject: [PATCH 246/675] feat: copy emails from lead to customer (#38647) feat: copy emails from lead to customer (cherry picked from commit 906ac093e37694c4616cffb961b131ce9002315b) Co-authored-by: barredterra <14891507+barredterra@users.noreply.github.com> --- erpnext/selling/doctype/customer/customer.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index b7fcadb1eca..74f68ea6922 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -171,6 +171,7 @@ class Customer(TransactionBase): if self.flags.is_new_doc: self.link_lead_address_and_contact() + self.copy_communication() self.update_customer_groups() @@ -224,6 +225,17 @@ class Customer(TransactionBase): linked_doc.append("links", dict(link_doctype="Customer", link_name=self.name)) linked_doc.save(ignore_permissions=self.flags.ignore_permissions) + def copy_communication(self): + if not self.lead_name or not frappe.db.get_single_value( + "CRM Settings", "carry_forward_communication_and_comments" + ): + return + + from erpnext.crm.utils import copy_comments, link_communications + + copy_comments("Lead", self.lead_name, self) + link_communications("Lead", self.lead_name, self) + def validate_name_with_customer_group(self): if frappe.db.exists("Customer Group", self.name): frappe.throw( From 7691256f4df404ca94f826feb2bc05f280b2857a Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 14:34:16 +0530 Subject: [PATCH 247/675] perf: Move dimension validation out of GL Entry doctype (#39730) perf: Move dimension validation out of GL Entry doctype (#39730) (cherry picked from commit b834ed10d6b6faa1f76e1343ca8b347ca393b7dd) Co-authored-by: Deepesh Garg --- erpnext/accounts/doctype/gl_entry/gl_entry.py | 46 +------------------ erpnext/accounts/general_ledger.py | 42 +++++++++++++++++ 2 files changed, 43 insertions(+), 45 deletions(-) diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index 3a564825b55..6e07b0ec430 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -13,16 +13,9 @@ import erpnext from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( get_checks_for_pl_and_bs_accounts, ) -from erpnext.accounts.doctype.accounting_dimension_filter.accounting_dimension_filter import ( - get_dimension_filter_map, -) from erpnext.accounts.party import validate_party_frozen_disabled, validate_party_gle_currency from erpnext.accounts.utils import get_account_currency, get_fiscal_year -from erpnext.exceptions import ( - InvalidAccountCurrency, - InvalidAccountDimensionError, - MandatoryAccountDimensionError, -) +from erpnext.exceptions import InvalidAccountCurrency exclude_from_linked_with = True @@ -54,7 +47,6 @@ class GLEntry(Document): if not self.flags.from_repost and self.voucher_type != "Period Closing Voucher": self.validate_account_details(adv_adj) self.validate_dimensions_for_pl_and_bs() - self.validate_allowed_dimensions() validate_balance_type(self.account, adv_adj) validate_frozen_account(self.account, adv_adj) @@ -164,42 +156,6 @@ class GLEntry(Document): ) ) - def validate_allowed_dimensions(self): - dimension_filter_map = get_dimension_filter_map() - for key, value in dimension_filter_map.items(): - dimension = key[0] - account = key[1] - - if self.account == account: - if value["is_mandatory"] and not self.get(dimension): - frappe.throw( - _("{0} is mandatory for account {1}").format( - frappe.bold(frappe.unscrub(dimension)), frappe.bold(self.account) - ), - MandatoryAccountDimensionError, - ) - - if value["allow_or_restrict"] == "Allow": - if self.get(dimension) and self.get(dimension) not in value["allowed_dimensions"]: - frappe.throw( - _("Invalid value {0} for {1} against account {2}").format( - frappe.bold(self.get(dimension)), - frappe.bold(frappe.unscrub(dimension)), - frappe.bold(self.account), - ), - InvalidAccountDimensionError, - ) - else: - if self.get(dimension) and self.get(dimension) in value["allowed_dimensions"]: - frappe.throw( - _("Invalid value {0} for {1} against account {2}").format( - frappe.bold(self.get(dimension)), - frappe.bold(frappe.unscrub(dimension)), - frappe.bold(self.account), - ), - InvalidAccountDimensionError, - ) - def check_pl_account(self): if ( self.is_opening == "Yes" diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index 134ddddf9e0..38fcd976ad4 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -13,9 +13,13 @@ import erpnext from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( get_accounting_dimensions, ) +from erpnext.accounts.doctype.accounting_dimension_filter.accounting_dimension_filter import ( + get_dimension_filter_map, +) from erpnext.accounts.doctype.accounting_period.accounting_period import ClosedAccountingPeriod from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget from erpnext.accounts.utils import create_payment_ledger_entry +from erpnext.exceptions import InvalidAccountDimensionError, MandatoryAccountDimensionError def make_gl_entries( @@ -354,6 +358,7 @@ def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False): process_debit_credit_difference(gl_map) + dimension_filter_map = get_dimension_filter_map() if gl_map: check_freezing_date(gl_map[0]["posting_date"], adv_adj) is_opening = any(d.get("is_opening") == "Yes" for d in gl_map) @@ -361,6 +366,7 @@ def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False): validate_against_pcv(is_opening, gl_map[0]["posting_date"], gl_map[0]["company"]) for entry in gl_map: + validate_allowed_dimensions(entry, dimension_filter_map) make_entry(entry, adv_adj, update_outstanding, from_repost) @@ -672,3 +678,39 @@ def set_as_cancel(voucher_type, voucher_no): where voucher_type=%s and voucher_no=%s and is_cancelled = 0""", (now(), frappe.session.user, voucher_type, voucher_no), ) + + +def validate_allowed_dimensions(gl_entry, dimension_filter_map): + for key, value in dimension_filter_map.items(): + dimension = key[0] + account = key[1] + + if gl_entry.account == account: + if value["is_mandatory"] and not gl_entry.get(dimension): + frappe.throw( + _("{0} is mandatory for account {1}").format( + frappe.bold(frappe.unscrub(dimension)), frappe.bold(gl_entry.account) + ), + MandatoryAccountDimensionError, + ) + + if value["allow_or_restrict"] == "Allow": + if gl_entry.get(dimension) and gl_entry.get(dimension) not in value["allowed_dimensions"]: + frappe.throw( + _("Invalid value {0} for {1} against account {2}").format( + frappe.bold(gl_entry.get(dimension)), + frappe.bold(frappe.unscrub(dimension)), + frappe.bold(gl_entry.account), + ), + InvalidAccountDimensionError, + ) + else: + if gl_entry.get(dimension) and gl_entry.get(dimension) in value["allowed_dimensions"]: + frappe.throw( + _("Invalid value {0} for {1} against account {2}").format( + frappe.bold(gl_entry.get(dimension)), + frappe.bold(frappe.unscrub(dimension)), + frappe.bold(gl_entry.account), + ), + InvalidAccountDimensionError, + ) From 46ac4f471423ff652510e582f7a3e59141db2f02 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 16:09:09 +0530 Subject: [PATCH 248/675] fix: remove applied pricing rule on qty change (backport #39688) (#39736) fix: remove pricing rule (cherry picked from commit 7c6a5a0f23b948953815870b726c30b0fd076338) Co-authored-by: s-aga-r --- erpnext/controllers/accounts_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index a4597da8e27..a3f48141aac 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -671,7 +671,7 @@ class AccountsController(TransactionBase): if self.get("is_subcontracted"): args["is_subcontracted"] = self.is_subcontracted - ret = get_item_details(args, self, for_validate=True, overwrite_warehouse=False) + ret = get_item_details(args, self, for_validate=for_validate, overwrite_warehouse=False) for fieldname, value in ret.items(): if item.meta.get_field(fieldname) and value is not None: From 44c09de7298005651c09e62ba578b7a8126e80ef Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 16:52:01 +0530 Subject: [PATCH 249/675] fix: Blanket Order Ordered Quantity (backport #39725) (#39738) * fix: disable no-copy for blanket order in PO (cherry picked from commit 5ce5c352e4ab2293b3f5b5dac9bc1a2a1912e620) # Conflicts: # erpnext/buying/doctype/purchase_order_item/purchase_order_item.json * fix: update BO Ordered Quantity on PO Close/Open (cherry picked from commit 61ded697a7384d8ef133a42424d8a14763bb6061) # Conflicts: # erpnext/buying/doctype/purchase_order/purchase_order.py * test: BO on PO Close/Open (cherry picked from commit 27d6c8b6d52ada292eef5c42506e95bcf933eec8) * chore: `conflicts` --------- Co-authored-by: s-aga-r --- .../doctype/purchase_order/purchase_order.py | 1 + .../purchase_order/test_purchase_order.py | 25 +++++++++++++++++++ .../purchase_order_item.json | 4 +-- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 8131104b825..5d1dfafc3c9 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -317,6 +317,7 @@ class PurchaseOrder(BuyingController): self.update_requested_qty() self.update_ordered_qty() self.update_reserved_qty_for_subcontract() + self.update_blanket_order() self.notify_update() clear_doctype_notifications(self) diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index b0bbc5d0c71..739a989c79e 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -814,6 +814,30 @@ class TestPurchaseOrder(FrappeTestCase): # To test if the PO does NOT have a Blanket Order self.assertEqual(po_doc.items[0].blanket_order, None) + def test_blanket_order_on_po_close_and_open(self): + # Step - 1: Create Blanket Order + bo = make_blanket_order(blanket_order_type="Purchasing", quantity=10, rate=10) + + # Step - 2: Create Purchase Order + po = create_purchase_order( + item_code="_Test Item", qty=5, against_blanket_order=1, against_blanket=bo.name + ) + + bo.load_from_db() + self.assertEqual(bo.items[0].ordered_qty, 5) + + # Step - 3: Close Purchase Order + po.update_status("Closed") + + bo.load_from_db() + self.assertEqual(bo.items[0].ordered_qty, 0) + + # Step - 4: Re-Open Purchase Order + po.update_status("Re-open") + + bo.load_from_db() + self.assertEqual(bo.items[0].ordered_qty, 5) + def test_payment_terms_are_fetched_when_creating_purchase_invoice(self): from erpnext.accounts.doctype.payment_entry.test_payment_entry import ( create_payment_terms_template, @@ -1016,6 +1040,7 @@ def create_purchase_order(**args): "schedule_date": add_days(nowdate(), 1), "include_exploded_items": args.get("include_exploded_items", 1), "against_blanket_order": args.against_blanket_order, + "against_blanket": args.against_blanket, "material_request": args.material_request, "material_request_item": args.material_request_item, }, diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json index 1a9035c3327..feb1a9b8828 100644 --- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json +++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json @@ -546,7 +546,6 @@ "fieldname": "blanket_order", "fieldtype": "Link", "label": "Blanket Order", - "no_copy": 1, "options": "Blanket Order" }, { @@ -554,7 +553,6 @@ "fieldname": "blanket_order_rate", "fieldtype": "Currency", "label": "Blanket Order Rate", - "no_copy": 1, "print_hide": 1, "read_only": 1 }, @@ -918,7 +916,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-11-24 19:07:34.921094", + "modified": "2024-02-05 11:23:24.859435", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order Item", From 33ae0fa2f4a7fefff85198f3a1faf62be6e0e833 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 5 Feb 2024 17:38:00 +0530 Subject: [PATCH 250/675] fix: correctly calculate diff amount for included taxes (#39655) fix: correctly calculate diff amount for included taxes (#39655) (cherry picked from commit 772f540bef28117c008512ead6558db801d395cd) (cherry picked from commit 350b2cdde39c2c318f286bc01c54ebdb681e8a2d) Co-authored-by: Gursheen Kaur Anand <40693548+GursheenK@users.noreply.github.com> --- .../doctype/payment_entry/payment_entry.py | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 66c82be596c..b89854b172a 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -874,19 +874,19 @@ class PaymentEntry(AccountsController): ) base_party_amount = flt(self.base_total_allocated_amount) + flt(base_unallocated_amount) - - if self.payment_type == "Receive": - self.difference_amount = base_party_amount - self.base_received_amount - elif self.payment_type == "Pay": - self.difference_amount = self.base_paid_amount - base_party_amount - else: - self.difference_amount = self.base_paid_amount - flt(self.base_received_amount) - - total_deductions = sum(flt(d.amount) for d in self.get("deductions")) included_taxes = self.get_included_taxes() + if self.payment_type == "Receive": + self.difference_amount = base_party_amount - self.base_received_amount + included_taxes + elif self.payment_type == "Pay": + self.difference_amount = self.base_paid_amount - base_party_amount - included_taxes + else: + self.difference_amount = self.base_paid_amount - flt(self.base_received_amount) - included_taxes + + total_deductions = sum(flt(d.amount) for d in self.get("deductions")) + self.difference_amount = flt( - self.difference_amount - total_deductions - included_taxes, self.precision("difference_amount") + self.difference_amount - total_deductions, self.precision("difference_amount") ) def get_included_taxes(self): From 99929e94342a6ee2337abed6572b7853f10c716d Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Mon, 5 Feb 2024 12:09:13 +0000 Subject: [PATCH 251/675] chore(release): Bumped to Version 14.61.4 ## [14.61.4](https://github.com/frappe/erpnext/compare/v14.61.3...v14.61.4) (2024-02-05) ### Bug Fixes * correctly calculate diff amount for included taxes ([#39655](https://github.com/frappe/erpnext/issues/39655)) ([33ae0fa](https://github.com/frappe/erpnext/commit/33ae0fa2f4a7fefff85198f3a1faf62be6e0e833)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index a8d71f538f7..01144b8e109 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.61.3" +__version__ = "14.61.4" def get_default_company(user=None): From a1a70bbae0723f3543f44daeaf675a0989061d76 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 5 Feb 2024 19:26:31 +0530 Subject: [PATCH 252/675] perf: timeout while submitting the purchase receipt entry (cherry picked from commit 1fa62333773a59feb5a0a39d274ebcae362ed286) --- erpnext/buying/doctype/purchase_order/purchase_order.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 5d1dfafc3c9..9d4846056ea 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -457,6 +457,7 @@ class PurchaseOrder(BuyingController): ) +@frappe.request_cache def item_last_purchase_rate(name, conversion_rate, item_code, conversion_factor=1.0): """get last purchase rate for an item""" From 0163e13aa30aaebd65a598ad38322226bbd4bdfa Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 2 Feb 2024 17:45:50 +0530 Subject: [PATCH 253/675] refactor: ensure unique accounts for each Bank Account's (cherry picked from commit 2caa2d677c09793f7097c3e76ebb4180a3f2a336) --- .../accounts/doctype/bank_account/bank_account.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/erpnext/accounts/doctype/bank_account/bank_account.py b/erpnext/accounts/doctype/bank_account/bank_account.py index addcf62e5b6..ece27e77ee9 100644 --- a/erpnext/accounts/doctype/bank_account/bank_account.py +++ b/erpnext/accounts/doctype/bank_account/bank_account.py @@ -9,6 +9,7 @@ from frappe.contacts.address_and_contact import ( load_address_and_contact, ) from frappe.model.document import Document +from frappe.utils import comma_and, get_link_to_form class BankAccount(Document): @@ -25,6 +26,17 @@ class BankAccount(Document): def validate(self): self.validate_company() self.validate_iban() + self.validate_account() + + def validate_account(self): + if self.account: + if accounts := frappe.db.get_all("Bank Account", filters={"account": self.account}, as_list=1): + frappe.throw( + _("'{0}' account is already used by {1}. Use another account.").format( + frappe.bold(self.account), + frappe.bold(comma_and([get_link_to_form(self.doctype, x[0]) for x in accounts])), + ) + ) def validate_company(self): if self.is_company_account and not self.company: From f64f0437aecbc23d319cafb2a282e56b6a5efc08 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 3 Feb 2024 10:58:31 +0530 Subject: [PATCH 254/675] refactor(test): generate uniq GL acc and Bank acc for each test case (cherry picked from commit a9a2ec81de73cde0995c837e12cd5dc79a584841) --- .../bank_transaction/test_bank_transaction.py | 72 ++++++++++++------- 1 file changed, 46 insertions(+), 26 deletions(-) diff --git a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py index eb0dc74825d..b35ed01cd9a 100644 --- a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py +++ b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py @@ -32,8 +32,16 @@ class TestBankTransaction(FrappeTestCase): frappe.db.delete(dt) make_pos_profile() - add_transactions() - add_vouchers() + + # generate and use a uniq hash identifier for 'Bank Account' and it's linked GL 'Account' to avoid validation error + uniq_identifier = frappe.generate_hash(length=10) + gl_account = create_gl_account("_Test Bank " + uniq_identifier) + bank_account = create_bank_account( + gl_account=gl_account, bank_account_name="Checking Account " + uniq_identifier + ) + + add_transactions(bank_account=bank_account) + add_vouchers(gl_account=gl_account) # This test checks if ERPNext is able to provide a linked payment for a bank transaction based on the amount of the bank transaction. def test_linked_payments(self): @@ -213,7 +221,9 @@ class TestBankTransaction(FrappeTestCase): self.assertEqual(linked_payments[0][2], repayment_entry.name) -def create_bank_account(bank_name="Citi Bank", account_name="_Test Bank - _TC"): +def create_bank_account( + bank_name="Citi Bank", gl_account="_Test Bank - _TC", bank_account_name="Checking Account" +): try: frappe.get_doc( { @@ -225,21 +235,35 @@ def create_bank_account(bank_name="Citi Bank", account_name="_Test Bank - _TC"): pass try: - frappe.get_doc( + bank_account = frappe.get_doc( { "doctype": "Bank Account", - "account_name": "Checking Account", + "account_name": bank_account_name, "bank": bank_name, - "account": account_name, + "account": gl_account, } ).insert(ignore_if_duplicate=True) except frappe.DuplicateEntryError: pass + return bank_account.name -def add_transactions(): - create_bank_account() +def create_gl_account(gl_account_name="_Test Bank - _TC"): + gl_account = frappe.get_doc( + { + "doctype": "Account", + "company": "_Test Company", + "parent_account": "Current Assets - _TC", + "account_type": "Bank", + "is_group": 0, + "account_name": gl_account_name, + } + ).insert() + return gl_account.name + + +def add_transactions(bank_account="_Test Bank - _TC"): doc = frappe.get_doc( { "doctype": "Bank Transaction", @@ -247,7 +271,7 @@ def add_transactions(): "date": "2018-10-23", "deposit": 1200, "currency": "INR", - "bank_account": "Checking Account - Citi Bank", + "bank_account": bank_account, } ).insert() doc.submit() @@ -259,7 +283,7 @@ def add_transactions(): "date": "2018-10-23", "deposit": 1700, "currency": "INR", - "bank_account": "Checking Account - Citi Bank", + "bank_account": bank_account, } ).insert() doc.submit() @@ -271,7 +295,7 @@ def add_transactions(): "date": "2018-10-26", "withdrawal": 690, "currency": "INR", - "bank_account": "Checking Account - Citi Bank", + "bank_account": bank_account, } ).insert() doc.submit() @@ -283,7 +307,7 @@ def add_transactions(): "date": "2018-10-27", "deposit": 3900, "currency": "INR", - "bank_account": "Checking Account - Citi Bank", + "bank_account": bank_account, } ).insert() doc.submit() @@ -295,13 +319,13 @@ def add_transactions(): "date": "2018-10-27", "withdrawal": 109080, "currency": "INR", - "bank_account": "Checking Account - Citi Bank", + "bank_account": bank_account, } ).insert() doc.submit() -def add_vouchers(): +def add_vouchers(gl_account="_Test Bank - _TC"): try: frappe.get_doc( { @@ -317,7 +341,7 @@ def add_vouchers(): pi = make_purchase_invoice(supplier="Conrad Electronic", qty=1, rate=690) - pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC") + pe = get_payment_entry("Purchase Invoice", pi.name, bank_account=gl_account) pe.reference_no = "Conrad Oct 18" pe.reference_date = "2018-10-24" pe.insert() @@ -336,14 +360,14 @@ def add_vouchers(): pass pi = make_purchase_invoice(supplier="Mr G", qty=1, rate=1200) - pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC") + pe = get_payment_entry("Purchase Invoice", pi.name, bank_account=gl_account) pe.reference_no = "Herr G Oct 18" pe.reference_date = "2018-10-24" pe.insert() pe.submit() pi = make_purchase_invoice(supplier="Mr G", qty=1, rate=1700) - pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC") + pe = get_payment_entry("Purchase Invoice", pi.name, bank_account=gl_account) pe.reference_no = "Herr G Nov 18" pe.reference_date = "2018-11-01" pe.insert() @@ -374,10 +398,10 @@ def add_vouchers(): pass pi = make_purchase_invoice(supplier="Poore Simon's", qty=1, rate=3900, is_paid=1, do_not_save=1) - pi.cash_bank_account = "_Test Bank - _TC" + pi.cash_bank_account = gl_account pi.insert() pi.submit() - pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC") + pe = get_payment_entry("Purchase Invoice", pi.name, bank_account=gl_account) pe.reference_no = "Poore Simon's Oct 18" pe.reference_date = "2018-10-28" pe.paid_amount = 690 @@ -386,7 +410,7 @@ def add_vouchers(): pe.submit() si = create_sales_invoice(customer="Poore Simon's", qty=1, rate=3900) - pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC") + pe = get_payment_entry("Sales Invoice", si.name, bank_account=gl_account) pe.reference_no = "Poore Simon's Oct 18" pe.reference_date = "2018-10-28" pe.insert() @@ -409,16 +433,12 @@ def add_vouchers(): 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 - _TC"} - ) + mode_of_payment.append("accounts", {"company": "_Test Company", "default_account": gl_account}) mode_of_payment.save() si = create_sales_invoice(customer="Fayva", qty=1, rate=109080, do_not_save=1) si.is_pos = 1 - si.append( - "payments", {"mode_of_payment": "Cash", "account": "_Test Bank - _TC", "amount": 109080} - ) + si.append("payments", {"mode_of_payment": "Cash", "account": gl_account, "amount": 109080}) si.insert() si.submit() From 8267fee9b8666cecda4884a1c8743549ec406245 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 3 Feb 2024 12:08:01 +0530 Subject: [PATCH 255/675] refactor(test): make use of test fixtures in Payment Order (cherry picked from commit 322cdbaccf0b8697000aae4e56efa659a34fa8e5) --- .../payment_order/test_payment_order.py | 32 ++++++++++++------- 1 file changed, 20 insertions(+), 12 deletions(-) diff --git a/erpnext/accounts/doctype/payment_order/test_payment_order.py b/erpnext/accounts/doctype/payment_order/test_payment_order.py index 0dcb1794b9a..60f288e1f07 100644 --- a/erpnext/accounts/doctype/payment_order/test_payment_order.py +++ b/erpnext/accounts/doctype/payment_order/test_payment_order.py @@ -4,9 +4,13 @@ import unittest import frappe +from frappe.tests.utils import FrappeTestCase from frappe.utils import getdate -from erpnext.accounts.doctype.bank_transaction.test_bank_transaction import create_bank_account +from erpnext.accounts.doctype.bank_transaction.test_bank_transaction import ( + create_bank_account, + create_gl_account, +) from erpnext.accounts.doctype.payment_entry.payment_entry import ( get_payment_entry, make_payment_order, @@ -14,28 +18,32 @@ from erpnext.accounts.doctype.payment_entry.payment_entry import ( from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice -class TestPaymentOrder(unittest.TestCase): +class TestPaymentOrder(FrappeTestCase): def setUp(self): - create_bank_account() + # generate and use a uniq hash identifier for 'Bank Account' and it's linked GL 'Account' to avoid validation error + uniq_identifier = frappe.generate_hash(length=10) + self.gl_account = create_gl_account("_Test Bank " + uniq_identifier) + self.bank_account = create_bank_account( + gl_account=self.gl_account, bank_account_name="Checking Account " + uniq_identifier + ) def tearDown(self): - for bt in frappe.get_all("Payment Order"): - doc = frappe.get_doc("Payment Order", bt.name) - doc.cancel() - doc.delete() + frappe.db.rollback() def test_payment_order_creation_against_payment_entry(self): purchase_invoice = make_purchase_invoice() payment_entry = get_payment_entry( - "Purchase Invoice", purchase_invoice.name, bank_account="_Test Bank - _TC" + "Purchase Invoice", purchase_invoice.name, bank_account=self.gl_account ) payment_entry.reference_no = "_Test_Payment_Order" payment_entry.reference_date = getdate() - payment_entry.party_bank_account = "Checking Account - Citi Bank" + payment_entry.party_bank_account = self.bank_account payment_entry.insert() payment_entry.submit() - doc = create_payment_order_against_payment_entry(payment_entry, "Payment Entry") + doc = create_payment_order_against_payment_entry( + payment_entry, "Payment Entry", self.bank_account + ) reference_doc = doc.get("references")[0] self.assertEqual(reference_doc.reference_name, payment_entry.name) self.assertEqual(reference_doc.reference_doctype, "Payment Entry") @@ -43,13 +51,13 @@ class TestPaymentOrder(unittest.TestCase): self.assertEqual(reference_doc.amount, 250) -def create_payment_order_against_payment_entry(ref_doc, order_type): +def create_payment_order_against_payment_entry(ref_doc, order_type, bank_account): payment_order = frappe.get_doc( dict( doctype="Payment Order", company="_Test Company", payment_order_type=order_type, - company_bank_account="Checking Account - Citi Bank", + company_bank_account=bank_account, ) ) doc = make_payment_order(ref_doc.name, payment_order) From a6067c623957123753dd64b5a1b6581280587a99 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 6 Feb 2024 12:45:43 +0530 Subject: [PATCH 256/675] fix: show warehouse title field in sales docs (backport #39746) (#39754) fix: show warehouse title field in sales docs (cherry picked from commit ee14faaa39bcb0fb8d813aee7e00eedc8d3a38c1) Co-authored-by: s-aga-r --- erpnext/controllers/queries.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 06ea8336bd6..303548cca42 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -679,17 +679,24 @@ def warehouse_query(doctype, txt, searchfield, start, page_len, filters): conditions, bin_conditions = [], [] filter_dict = get_doctype_wise_filters(filters) - query = """select `tabWarehouse`.name, + warehouse_field = "name" + meta = frappe.get_meta("Warehouse") + if meta.get("show_title_field_in_link") and meta.get("title_field"): + searchfield = meta.get("title_field") + warehouse_field = meta.get("title_field") + + query = """select `tabWarehouse`.`{warehouse_field}`, CONCAT_WS(' : ', 'Actual Qty', ifnull(round(`tabBin`.actual_qty, 2), 0 )) actual_qty from `tabWarehouse` left join `tabBin` on `tabBin`.warehouse = `tabWarehouse`.name {bin_conditions} where `tabWarehouse`.`{key}` like {txt} {fcond} {mcond} - order by ifnull(`tabBin`.actual_qty, 0) desc + order by ifnull(`tabBin`.actual_qty, 0) desc, `tabWarehouse`.`{warehouse_field}` asc limit {page_len} offset {start} """.format( + warehouse_field=warehouse_field, bin_conditions=get_filters_cond( doctype, filter_dict.get("Bin"), bin_conditions, ignore_permissions=True ), From 9a8a9568b1944ed473e54a8dff753ae062a758bb Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Tue, 6 Feb 2024 18:44:23 +0000 Subject: [PATCH 257/675] chore(release): Bumped to Version 14.62.0 # [14.62.0](https://github.com/frappe/erpnext/compare/v14.61.4...v14.62.0) (2024-02-06) ### Bug Fixes * Blanket Order Ordered Quantity (backport [#39725](https://github.com/frappe/erpnext/issues/39725)) ([#39738](https://github.com/frappe/erpnext/issues/39738)) ([44c09de](https://github.com/frappe/erpnext/commit/44c09de7298005651c09e62ba578b7a8126e80ef)) * correctly calculate diff amount for included taxes ([#39655](https://github.com/frappe/erpnext/issues/39655)) ([350b2cd](https://github.com/frappe/erpnext/commit/350b2cdde39c2c318f286bc01c54ebdb681e8a2d)) * don't overwrite existing terms in transaction ([#39519](https://github.com/frappe/erpnext/issues/39519)) ([e49f8d5](https://github.com/frappe/erpnext/commit/e49f8d5f55b0cafda012475e94d48e11577a86e4)) * Exchange rate on MR to PO creation for muticurrency POs ([#39646](https://github.com/frappe/erpnext/issues/39646)) ([4dc5d9a](https://github.com/frappe/erpnext/commit/4dc5d9a6cad5f353867a96ccd3b4d6eaee34dcf9)) * incorrect landed cost voucher amount ([4a609d8](https://github.com/frappe/erpnext/commit/4a609d8fa8b8f0688e883382181c80063a7cdc1f)) * remove applied pricing rule on qty change (backport [#39688](https://github.com/frappe/erpnext/issues/39688)) ([#39736](https://github.com/frappe/erpnext/issues/39736)) ([46ac4f4](https://github.com/frappe/erpnext/commit/46ac4f471423ff652510e582f7a3e59141db2f02)) * show warehouse title field in sales docs (backport [#39746](https://github.com/frappe/erpnext/issues/39746)) ([#39754](https://github.com/frappe/erpnext/issues/39754)) ([a6067c6](https://github.com/frappe/erpnext/commit/a6067c623957123753dd64b5a1b6581280587a99)) ### Features * copy emails from lead to customer ([#38647](https://github.com/frappe/erpnext/issues/38647)) ([7952bf4](https://github.com/frappe/erpnext/commit/7952bf43187c88b1c5ce5fd4a84ec45f526d72a2)) * New financial views - Growth and margin views for P&L and balance sheet (backport [#39588](https://github.com/frappe/erpnext/issues/39588)) ([#39601](https://github.com/frappe/erpnext/issues/39601)) ([3808ddb](https://github.com/frappe/erpnext/commit/3808ddbf860d9b5e88f544bb6a2108b22aaaca5c)) ### Performance Improvements * memory consumption for the stock balance report ([#39626](https://github.com/frappe/erpnext/issues/39626)) ([b32848d](https://github.com/frappe/erpnext/commit/b32848d69dfb6614b013eca0df328e7923523a21)) * Move dimension validation out of GL Entry doctype ([#39730](https://github.com/frappe/erpnext/issues/39730)) ([7691256](https://github.com/frappe/erpnext/commit/7691256f4df404ca94f826feb2bc05f280b2857a)) * timeout for auto material request through reorder level ([85e6b39](https://github.com/frappe/erpnext/commit/85e6b39e23ae468e88eb49af9f5981ac854a5ac3)) * timeout while submitting the purchase receipt entry ([a1a70bb](https://github.com/frappe/erpnext/commit/a1a70bbae0723f3543f44daeaf675a0989061d76)) ### Reverts * Revert "feat: New financial views - Growth and margin views for P&L and balance sheet (#39588)" ([f01308b](https://github.com/frappe/erpnext/commit/f01308b97288ac630bf9221df897f13071183b24)), closes [#39588](https://github.com/frappe/erpnext/issues/39588) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 01144b8e109..181e366135b 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.61.4" +__version__ = "14.62.0" def get_default_company(user=None): From e3591270c689fa262f757feb9413654fa8fb54b6 Mon Sep 17 00:00:00 2001 From: Babuuu <59009890+aynugek@users.noreply.github.com> Date: Wed, 7 Feb 2024 13:12:14 +0000 Subject: [PATCH 258/675] refactor: Clean up code used to fetch website item stock details --- erpnext/utilities/product.py | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/erpnext/utilities/product.py b/erpnext/utilities/product.py index 49066cd0c8a..963cd2bf8f0 100644 --- a/erpnext/utilities/product.py +++ b/erpnext/utilities/product.py @@ -11,7 +11,6 @@ from erpnext.stock.doctype.warehouse.warehouse import get_child_warehouses def get_web_item_qty_in_stock(item_code, item_warehouse_field, warehouse=None): - in_stock, stock_qty = 0, "" template_item_code, is_stock_item = frappe.db.get_value( "Item", item_code, ["variant_of", "is_stock_item"] ) @@ -52,10 +51,11 @@ def get_web_item_qty_in_stock(item_code, item_warehouse_field, warehouse=None): .where((BIN.item_code == item_code) & (BIN.warehouse == warehouse)) ).run() + stock_qty = stock_qty[0][0] if stock_qty: total_stock += adjust_qty_for_expired_items(item_code, stock_qty, warehouse) - in_stock = total_stock > 0 and 1 or 0 + in_stock = int(total_stock > 0) return frappe._dict( {"in_stock": in_stock, "stock_qty": total_stock, "is_stock_item": is_stock_item} @@ -63,20 +63,16 @@ def get_web_item_qty_in_stock(item_code, item_warehouse_field, warehouse=None): def adjust_qty_for_expired_items(item_code, stock_qty, warehouse): - batches = frappe.get_all("Batch", filters=[{"item": item_code}], fields=["expiry_date", "name"]) + batches = frappe.get_all("Batch", filters={"item": item_code}, fields=["expiry_date", "name"]) expired_batches = get_expired_batches(batches) - stock_qty = [list(item) for item in stock_qty] for batch in expired_batches: if warehouse: - stock_qty[0][0] = max(0, stock_qty[0][0] - get_batch_qty(batch, warehouse)) + stock_qty = max(0, stock_qty - get_batch_qty(batch, warehouse)) else: - stock_qty[0][0] = max(0, stock_qty[0][0] - qty_from_all_warehouses(get_batch_qty(batch))) + stock_qty = max(0, stock_qty - qty_from_all_warehouses(get_batch_qty(batch))) - if not stock_qty[0][0]: - break - - return stock_qty[0][0] if stock_qty else 0 + return stock_qty def get_expired_batches(batches): From c56f3a58ab31aaecee2b4007aac53d10f6e73473 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Wed, 7 Feb 2024 18:46:07 +0530 Subject: [PATCH 259/675] fix: remove duplicates from tax category map --- .../tds_payable_monthly.py | 10 +-- .../test_tds_payable_monthly.py | 89 +++++++++++++++---- 2 files changed, 74 insertions(+), 25 deletions(-) diff --git a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py index b96bfbeb817..ba5cdbe6567 100644 --- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py +++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py @@ -63,16 +63,14 @@ def get_result( tax_amount += entry.credit - entry.debit # infer tax withholding category from the account if it's the single account for this category tax_withholding_category = tds_accounts.get(entry.account) - rate = tax_rate_map.get(tax_withholding_category) # or else the consolidated value from the voucher document if not tax_withholding_category: - # or else from the party default tax_withholding_category = tax_category_map.get(name) - rate = tax_rate_map.get(tax_withholding_category) + # or else from the party default if not tax_withholding_category: tax_withholding_category = party_map.get(party, {}).get("tax_withholding_category") - rate = tax_rate_map.get(tax_withholding_category) + rate = tax_rate_map.get(tax_withholding_category) if net_total_map.get(name): if voucher_type == "Journal Entry" and tax_amount and rate: # back calcalute total amount from rate and tax_amount @@ -295,7 +293,7 @@ def get_tds_docs(filters): tds_accounts = {} for tds_acc in _tds_accounts: # if it turns out not to be the only tax withholding category, then don't include in the map - if tds_accounts.get(tds_acc["account"]): + if tds_acc["account"] in tds_accounts: tds_accounts[tds_acc["account"]] = None else: tds_accounts[tds_acc["account"]] = tds_acc["parent"] @@ -408,7 +406,7 @@ def get_doc_info(vouchers, doctype, tax_category_map, net_total_map=None): "paid_amount_after_tax", "base_paid_amount", ], - "Journal Entry": ["tax_withholding_category", "total_amount"], + "Journal Entry": ["total_amount"], } entries = frappe.get_all( diff --git a/erpnext/accounts/report/tds_payable_monthly/test_tds_payable_monthly.py b/erpnext/accounts/report/tds_payable_monthly/test_tds_payable_monthly.py index 89ecef1904c..f36b7ef1906 100644 --- a/erpnext/accounts/report/tds_payable_monthly/test_tds_payable_monthly.py +++ b/erpnext/accounts/report/tds_payable_monthly/test_tds_payable_monthly.py @@ -5,7 +5,6 @@ import frappe from frappe.tests.utils import FrappeTestCase from frappe.utils import today -from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_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 @@ -22,18 +21,42 @@ class TestTdsPayableMonthly(AccountsTestMixin, FrappeTestCase): self.create_company() self.clear_old_entries() create_tax_accounts() - create_tcs_category() def test_tax_withholding_for_customers(self): + create_tax_category(cumulative_threshold=300) + frappe.db.set_value("Customer", "_Test Customer", "tax_withholding_category", "TCS") si = create_sales_invoice(rate=1000) pe = create_tcs_payment_entry() + jv = create_tcs_journal_entry() + filters = frappe._dict( company="_Test Company", party_type="Customer", from_date=today(), to_date=today() ) result = execute(filters)[1] expected_values = [ + # Check for JV totals using back calculation logic + [jv.name, "TCS", 0.075, -10000.0, -7.5, -10000.0], [pe.name, "TCS", 0.075, 2550, 0.53, 2550.53], - [si.name, "TCS", 0.075, 1000, 0.53, 1000.53], + [si.name, "TCS", 0.075, 1000, 0.52, 1000.52], + ] + self.check_expected_values(result, expected_values) + + def test_single_account_for_multiple_categories(self): + create_tax_category("TDS - 1", rate=10, account="TDS - _TC") + inv_1 = make_purchase_invoice(rate=1000, do_not_submit=True) + inv_1.tax_withholding_category = "TDS - 1" + inv_1.submit() + + create_tax_category("TDS - 2", rate=20, account="TDS - _TC") + inv_2 = make_purchase_invoice(rate=1000, do_not_submit=True) + inv_2.tax_withholding_category = "TDS - 2" + inv_2.submit() + result = execute( + frappe._dict(company="_Test Company", party_type="Supplier", from_date=today(), to_date=today()) + )[1] + expected_values = [ + [inv_1.name, "TDS - 1", 10, 5000, 500, 5500], + [inv_2.name, "TDS - 2", 20, 5000, 1000, 6000], ] self.check_expected_values(result, expected_values) @@ -41,12 +64,15 @@ class TestTdsPayableMonthly(AccountsTestMixin, FrappeTestCase): for i in range(len(result)): voucher = frappe._dict(result[i]) voucher_expected_values = expected_values[i] - self.assertEqual(voucher.ref_no, voucher_expected_values[0]) - self.assertEqual(voucher.section_code, voucher_expected_values[1]) - self.assertEqual(voucher.rate, voucher_expected_values[2]) - self.assertEqual(voucher.base_total, voucher_expected_values[3]) - self.assertEqual(voucher.tax_amount, voucher_expected_values[4]) - self.assertEqual(voucher.grand_total, voucher_expected_values[5]) + voucher_actual_values = ( + voucher.ref_no, + voucher.section_code, + voucher.rate, + voucher.base_total, + voucher.tax_amount, + voucher.grand_total, + ) + self.assertSequenceEqual(voucher_actual_values, voucher_expected_values) def tearDown(self): self.clear_old_entries() @@ -67,24 +93,20 @@ def create_tax_accounts(): ).insert(ignore_if_duplicate=True) -def create_tcs_category(): +def create_tax_category(category="TCS", rate=0.075, account="TCS - _TC", cumulative_threshold=0): fiscal_year = get_fiscal_year(today(), company="_Test Company") from_date = fiscal_year[1] to_date = fiscal_year[2] - tax_category = create_tax_withholding_category( - category_name="TCS", - rate=0.075, + create_tax_withholding_category( + category_name=category, + rate=rate, from_date=from_date, to_date=to_date, - account="TCS - _TC", - cumulative_threshold=300, + account=account, + cumulative_threshold=cumulative_threshold, ) - customer = frappe.get_doc("Customer", "_Test Customer") - customer.tax_withholding_category = "TCS" - customer.save() - def create_tcs_payment_entry(): payment_entry = create_payment_entry( @@ -109,3 +131,32 @@ def create_tcs_payment_entry(): ) payment_entry.submit() return payment_entry + + +def create_tcs_journal_entry(): + jv = frappe.new_doc("Journal Entry") + jv.posting_date = today() + jv.company = "_Test Company" + jv.set( + "accounts", + [ + { + "account": "Debtors - _TC", + "party_type": "Customer", + "party": "_Test Customer", + "credit_in_account_currency": 10000, + }, + { + "account": "Debtors - _TC", + "party_type": "Customer", + "party": "_Test Customer", + "debit_in_account_currency": 9992.5, + }, + { + "account": "TCS - _TC", + "debit_in_account_currency": 7.5, + }, + ], + ) + jv.insert() + return jv.submit() From de47e67dfacd7dabb4c935728a9d95df736343c9 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 7 Feb 2024 19:03:38 +0530 Subject: [PATCH 260/675] fix: set rate for PO created against BO (backport #39765) (#39766) * fix: set rate for PO created against BO (cherry picked from commit 0e5b4e5f07d15fe04855f1c836c5412d3644035a) # Conflicts: # erpnext/manufacturing/doctype/blanket_order/blanket_order.py * chore: `conflicts` --------- Co-authored-by: s-aga-r --- erpnext/manufacturing/doctype/blanket_order/blanket_order.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py index 0135a4f9712..029b4b720a7 100644 --- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py +++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py @@ -65,6 +65,7 @@ def make_order(source_name): def update_item(source, target, source_parent): target_qty = source.get("qty") - source.get("ordered_qty") target.qty = target_qty if not flt(target_qty) < 0 else 0 + target.rate = source.get("rate") item = get_item_defaults(target.item_code, source_parent.company) if item: target.item_name = item.get("item_name") @@ -86,6 +87,10 @@ def make_order(source_name): }, }, ) + + if target_doc.doctype == "Purchase Order": + target_doc.set_missing_values() + return target_doc From d6a758d1f4c751f7be83197e677638bb0db80596 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Wed, 7 Feb 2024 19:12:32 +0530 Subject: [PATCH 261/675] fix: accommodate for default rounding method in v14 --- .../report/tds_payable_monthly/test_tds_payable_monthly.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/tds_payable_monthly/test_tds_payable_monthly.py b/erpnext/accounts/report/tds_payable_monthly/test_tds_payable_monthly.py index f36b7ef1906..742d2f37c45 100644 --- a/erpnext/accounts/report/tds_payable_monthly/test_tds_payable_monthly.py +++ b/erpnext/accounts/report/tds_payable_monthly/test_tds_payable_monthly.py @@ -37,7 +37,7 @@ class TestTdsPayableMonthly(AccountsTestMixin, FrappeTestCase): # Check for JV totals using back calculation logic [jv.name, "TCS", 0.075, -10000.0, -7.5, -10000.0], [pe.name, "TCS", 0.075, 2550, 0.53, 2550.53], - [si.name, "TCS", 0.075, 1000, 0.52, 1000.52], + [si.name, "TCS", 0.075, 1000.0, 0.53, 1000.53], ] self.check_expected_values(result, expected_values) From df9d52d3ce7155fb7f917383c7d795769aed1c15 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 7 Feb 2024 19:26:14 +0530 Subject: [PATCH 262/675] fix: incorrect planned qty in PP (backport #39785) (#39792) fix: incorrect planned qty in PP (cherry picked from commit a8ebc94a366e8f15e9bbeab3da064cf5f22dd1b9) Co-authored-by: s-aga-r --- .../doctype/production_plan/production_plan.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index c98d639663a..6ccd11bf9ff 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -236,9 +236,10 @@ class ProductionPlan(Document): so_item.parent, so_item.item_code, so_item.warehouse, - ( - (so_item.qty - so_item.work_order_qty - so_item.delivered_qty) * so_item.conversion_factor - ).as_("pending_qty"), + so_item.qty, + so_item.work_order_qty, + so_item.delivered_qty, + so_item.conversion_factor, so_item.description, so_item.name, so_item.bom_no, @@ -261,6 +262,11 @@ class ProductionPlan(Document): items = items_query.run(as_dict=True) + for item in items: + item.pending_qty = ( + flt(item.qty) - max(item.work_order_qty, item.delivered_qty, 0) * item.conversion_factor + ) + pi = frappe.qb.DocType("Packed Item") packed_items_query = ( From a4e6c388cb7838ad1f3c497c315a0fefa4e10f06 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Wed, 7 Feb 2024 18:46:07 +0530 Subject: [PATCH 263/675] fix: remove duplicates from tax category map (cherry picked from commit c56f3a58ab31aaecee2b4007aac53d10f6e73473) --- .../tds_payable_monthly.py | 10 +-- .../test_tds_payable_monthly.py | 89 +++++++++++++++---- 2 files changed, 74 insertions(+), 25 deletions(-) diff --git a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py index b96bfbeb817..ba5cdbe6567 100644 --- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py +++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py @@ -63,16 +63,14 @@ def get_result( tax_amount += entry.credit - entry.debit # infer tax withholding category from the account if it's the single account for this category tax_withholding_category = tds_accounts.get(entry.account) - rate = tax_rate_map.get(tax_withholding_category) # or else the consolidated value from the voucher document if not tax_withholding_category: - # or else from the party default tax_withholding_category = tax_category_map.get(name) - rate = tax_rate_map.get(tax_withholding_category) + # or else from the party default if not tax_withholding_category: tax_withholding_category = party_map.get(party, {}).get("tax_withholding_category") - rate = tax_rate_map.get(tax_withholding_category) + rate = tax_rate_map.get(tax_withholding_category) if net_total_map.get(name): if voucher_type == "Journal Entry" and tax_amount and rate: # back calcalute total amount from rate and tax_amount @@ -295,7 +293,7 @@ def get_tds_docs(filters): tds_accounts = {} for tds_acc in _tds_accounts: # if it turns out not to be the only tax withholding category, then don't include in the map - if tds_accounts.get(tds_acc["account"]): + if tds_acc["account"] in tds_accounts: tds_accounts[tds_acc["account"]] = None else: tds_accounts[tds_acc["account"]] = tds_acc["parent"] @@ -408,7 +406,7 @@ def get_doc_info(vouchers, doctype, tax_category_map, net_total_map=None): "paid_amount_after_tax", "base_paid_amount", ], - "Journal Entry": ["tax_withholding_category", "total_amount"], + "Journal Entry": ["total_amount"], } entries = frappe.get_all( diff --git a/erpnext/accounts/report/tds_payable_monthly/test_tds_payable_monthly.py b/erpnext/accounts/report/tds_payable_monthly/test_tds_payable_monthly.py index 89ecef1904c..f36b7ef1906 100644 --- a/erpnext/accounts/report/tds_payable_monthly/test_tds_payable_monthly.py +++ b/erpnext/accounts/report/tds_payable_monthly/test_tds_payable_monthly.py @@ -5,7 +5,6 @@ import frappe from frappe.tests.utils import FrappeTestCase from frappe.utils import today -from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_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 @@ -22,18 +21,42 @@ class TestTdsPayableMonthly(AccountsTestMixin, FrappeTestCase): self.create_company() self.clear_old_entries() create_tax_accounts() - create_tcs_category() def test_tax_withholding_for_customers(self): + create_tax_category(cumulative_threshold=300) + frappe.db.set_value("Customer", "_Test Customer", "tax_withholding_category", "TCS") si = create_sales_invoice(rate=1000) pe = create_tcs_payment_entry() + jv = create_tcs_journal_entry() + filters = frappe._dict( company="_Test Company", party_type="Customer", from_date=today(), to_date=today() ) result = execute(filters)[1] expected_values = [ + # Check for JV totals using back calculation logic + [jv.name, "TCS", 0.075, -10000.0, -7.5, -10000.0], [pe.name, "TCS", 0.075, 2550, 0.53, 2550.53], - [si.name, "TCS", 0.075, 1000, 0.53, 1000.53], + [si.name, "TCS", 0.075, 1000, 0.52, 1000.52], + ] + self.check_expected_values(result, expected_values) + + def test_single_account_for_multiple_categories(self): + create_tax_category("TDS - 1", rate=10, account="TDS - _TC") + inv_1 = make_purchase_invoice(rate=1000, do_not_submit=True) + inv_1.tax_withholding_category = "TDS - 1" + inv_1.submit() + + create_tax_category("TDS - 2", rate=20, account="TDS - _TC") + inv_2 = make_purchase_invoice(rate=1000, do_not_submit=True) + inv_2.tax_withholding_category = "TDS - 2" + inv_2.submit() + result = execute( + frappe._dict(company="_Test Company", party_type="Supplier", from_date=today(), to_date=today()) + )[1] + expected_values = [ + [inv_1.name, "TDS - 1", 10, 5000, 500, 5500], + [inv_2.name, "TDS - 2", 20, 5000, 1000, 6000], ] self.check_expected_values(result, expected_values) @@ -41,12 +64,15 @@ class TestTdsPayableMonthly(AccountsTestMixin, FrappeTestCase): for i in range(len(result)): voucher = frappe._dict(result[i]) voucher_expected_values = expected_values[i] - self.assertEqual(voucher.ref_no, voucher_expected_values[0]) - self.assertEqual(voucher.section_code, voucher_expected_values[1]) - self.assertEqual(voucher.rate, voucher_expected_values[2]) - self.assertEqual(voucher.base_total, voucher_expected_values[3]) - self.assertEqual(voucher.tax_amount, voucher_expected_values[4]) - self.assertEqual(voucher.grand_total, voucher_expected_values[5]) + voucher_actual_values = ( + voucher.ref_no, + voucher.section_code, + voucher.rate, + voucher.base_total, + voucher.tax_amount, + voucher.grand_total, + ) + self.assertSequenceEqual(voucher_actual_values, voucher_expected_values) def tearDown(self): self.clear_old_entries() @@ -67,24 +93,20 @@ def create_tax_accounts(): ).insert(ignore_if_duplicate=True) -def create_tcs_category(): +def create_tax_category(category="TCS", rate=0.075, account="TCS - _TC", cumulative_threshold=0): fiscal_year = get_fiscal_year(today(), company="_Test Company") from_date = fiscal_year[1] to_date = fiscal_year[2] - tax_category = create_tax_withholding_category( - category_name="TCS", - rate=0.075, + create_tax_withholding_category( + category_name=category, + rate=rate, from_date=from_date, to_date=to_date, - account="TCS - _TC", - cumulative_threshold=300, + account=account, + cumulative_threshold=cumulative_threshold, ) - customer = frappe.get_doc("Customer", "_Test Customer") - customer.tax_withholding_category = "TCS" - customer.save() - def create_tcs_payment_entry(): payment_entry = create_payment_entry( @@ -109,3 +131,32 @@ def create_tcs_payment_entry(): ) payment_entry.submit() return payment_entry + + +def create_tcs_journal_entry(): + jv = frappe.new_doc("Journal Entry") + jv.posting_date = today() + jv.company = "_Test Company" + jv.set( + "accounts", + [ + { + "account": "Debtors - _TC", + "party_type": "Customer", + "party": "_Test Customer", + "credit_in_account_currency": 10000, + }, + { + "account": "Debtors - _TC", + "party_type": "Customer", + "party": "_Test Customer", + "debit_in_account_currency": 9992.5, + }, + { + "account": "TCS - _TC", + "debit_in_account_currency": 7.5, + }, + ], + ) + jv.insert() + return jv.submit() From 79479b633fd9255aacddd60de456b7ee55439c01 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Wed, 7 Feb 2024 19:12:32 +0530 Subject: [PATCH 264/675] fix: accommodate for default rounding method in v14 (cherry picked from commit d6a758d1f4c751f7be83197e677638bb0db80596) --- .../report/tds_payable_monthly/test_tds_payable_monthly.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/tds_payable_monthly/test_tds_payable_monthly.py b/erpnext/accounts/report/tds_payable_monthly/test_tds_payable_monthly.py index f36b7ef1906..742d2f37c45 100644 --- a/erpnext/accounts/report/tds_payable_monthly/test_tds_payable_monthly.py +++ b/erpnext/accounts/report/tds_payable_monthly/test_tds_payable_monthly.py @@ -37,7 +37,7 @@ class TestTdsPayableMonthly(AccountsTestMixin, FrappeTestCase): # Check for JV totals using back calculation logic [jv.name, "TCS", 0.075, -10000.0, -7.5, -10000.0], [pe.name, "TCS", 0.075, 2550, 0.53, 2550.53], - [si.name, "TCS", 0.075, 1000, 0.52, 1000.52], + [si.name, "TCS", 0.075, 1000.0, 0.53, 1000.53], ] self.check_expected_values(result, expected_values) From 33777a4d4cab5a207d984a040ddf0c3f0b237245 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 7 Feb 2024 16:32:23 +0530 Subject: [PATCH 265/675] refactor: cancel Cr/Dr JE's on Sales/Purchase return cancel (cherry picked from commit 0549535603cae6a7afbd3375c8dd62b517af3c6e) --- erpnext/controllers/accounts_controller.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index a3f48141aac..2420d987c23 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1374,6 +1374,24 @@ class AccountsController(TransactionBase): x.update({dim.fieldname: self.get(dim.fieldname)}) reconcile_against_document(lst, active_dimensions=active_dimensions) + def cancel_system_generated_credit_debit_notes(self): + # Cancel 'Credit/Debit' Note Journal Entries, if found. + if self.doctype in ["Sales Invoice", "Purchase Invoice"]: + voucher_type = "Credit Note" if self.doctype == "Sales Invoice" else "Debit Note" + journals = frappe.db.get_all( + "Journal Entry", + filters={ + "is_system_generated": 1, + "reference_type": self.doctype, + "reference_name": self.name, + "voucher_type": voucher_type, + "docstatus": 1, + }, + pluck="name", + ) + for x in journals: + frappe.get_doc("Journal Entry", x).cancel() + def on_cancel(self): from erpnext.accounts.doctype.bank_transaction.bank_transaction import ( remove_from_bank_transaction, @@ -1386,6 +1404,8 @@ class AccountsController(TransactionBase): remove_from_bank_transaction(self.doctype, self.name) if self.doctype in ["Sales Invoice", "Purchase Invoice", "Payment Entry", "Journal Entry"]: + self.cancel_system_generated_credit_debit_notes() + # Cancel Exchange Gain/Loss Journal before unlinking cancel_exchange_gain_loss_journal(self) From e97f30d8e227fe3455d63873f7c84c1a5e9c000e Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 7 Feb 2024 17:21:29 +0530 Subject: [PATCH 266/675] test: Invoice status on Cr/Dr note cancellation (cherry picked from commit 31a8c3bdc45f0e32a51e43260db8484e5f17aa75) --- .../test_payment_reconciliation.py | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py index d7a73f0ce71..89240ac0d8a 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py @@ -591,6 +591,66 @@ class TestPaymentReconciliation(FrappeTestCase): self.assertEqual(si.status, "Paid") self.assertEqual(si.outstanding_amount, 0) + def test_invoice_status_after_cr_note_cancellation(self): + # This test case is made after the 'always standalone Credit/Debit notes' feature is introduced + transaction_date = nowdate() + amount = 100 + + si = self.create_sales_invoice(qty=1, rate=amount, posting_date=transaction_date) + + cr_note = self.create_sales_invoice( + qty=-1, rate=amount, posting_date=transaction_date, do_not_save=True, do_not_submit=True + ) + cr_note.is_return = 1 + cr_note.return_against = si.name + cr_note = cr_note.save().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})) + pr.reconcile() + + pr.get_unreconciled_entries() + self.assertEqual(pr.get("invoices"), []) + self.assertEqual(pr.get("payments"), []) + + journals = frappe.db.get_all( + "Journal Entry", + filters={ + "is_system_generated": 1, + "docstatus": 1, + "voucher_type": "Credit Note", + "reference_type": si.doctype, + "reference_name": si.name, + }, + pluck="name", + ) + self.assertEqual(len(journals), 1) + + # assert status outstanding + si.reload() + self.assertEqual(si.status, "Credit Note Issued") + self.assertEqual(si.outstanding_amount, 0) + + cr_note.reload() + cr_note.cancel() + # 'Credit Note' Journal should be auto cancelled + journals = frappe.db.get_all( + "Journal Entry", + filters={ + "is_system_generated": 1, + "docstatus": 1, + "voucher_type": "Credit Note", + "reference_type": si.doctype, + "reference_name": si.name, + }, + pluck="name", + ) + self.assertEqual(len(journals), 0) + def test_cr_note_partial_against_invoice(self): transaction_date = nowdate() amount = 100 From dd7472856837d5eba22c26a3c9e11619cfd8b7be Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 7 Feb 2024 17:33:55 +0530 Subject: [PATCH 267/675] refactor(test): assert Invoice status as well (cherry picked from commit 33efe0d12d85484e551dc9ebebf8840d427ecc67) --- .../payment_reconciliation/test_payment_reconciliation.py | 6 +++++- 1 file changed, 5 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 89240ac0d8a..fb75a0f7caf 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py @@ -630,7 +630,7 @@ class TestPaymentReconciliation(FrappeTestCase): ) self.assertEqual(len(journals), 1) - # assert status outstanding + # assert status and outstanding si.reload() self.assertEqual(si.status, "Credit Note Issued") self.assertEqual(si.outstanding_amount, 0) @@ -650,6 +650,10 @@ class TestPaymentReconciliation(FrappeTestCase): pluck="name", ) self.assertEqual(len(journals), 0) + # assert status and outstanding + si.reload() + self.assertEqual(si.status, "Unpaid") + self.assertEqual(si.outstanding_amount, 100) def test_cr_note_partial_against_invoice(self): transaction_date = nowdate() From 8d9b5764dd827ae92affbdf5d089a525c21f8fcb Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 7 Feb 2024 19:59:33 +0530 Subject: [PATCH 268/675] refactor(test): Forex Credit Note cancellation against Invoice (cherry picked from commit 2f676ced5c712823c5737f40230ec8b1994cd2dd) --- .../controllers/tests/test_accounts_controller.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/erpnext/controllers/tests/test_accounts_controller.py b/erpnext/controllers/tests/test_accounts_controller.py index 5e3077ea8c8..0c9d34d82a2 100644 --- a/erpnext/controllers/tests/test_accounts_controller.py +++ b/erpnext/controllers/tests/test_accounts_controller.py @@ -1108,18 +1108,18 @@ class TestAccountsController(FrappeTestCase): cr_note.reload() cr_note.cancel() - # Exchange Gain/Loss Journal should've been created. + # with the introduction of 'cancel_system_generated_credit_debit_notes' in accounts controller + # JE(Credit Note) will be cancelled once the parent is cancelled exc_je_for_si = self.get_journals_for(si.doctype, si.name) exc_je_for_cr = self.get_journals_for(cr_note.doctype, cr_note.name) - self.assertNotEqual(exc_je_for_si, []) - self.assertEqual(len(exc_je_for_si), 1) + self.assertEqual(exc_je_for_si, []) + self.assertEqual(len(exc_je_for_si), 0) self.assertEqual(len(exc_je_for_cr), 0) - # The Credit Note JE is still active and is referencing the sales invoice - # So, outstanding stays the same + # No references, full outstanding si.reload() - self.assertEqual(si.outstanding_amount, 1) - self.assert_ledger_outstanding(si.doctype, si.name, 80.0, 1.0) + self.assertEqual(si.outstanding_amount, 2) + self.assert_ledger_outstanding(si.doctype, si.name, 160.0, 2.0) def test_40_cost_center_from_payment_entry(self): """ From 596c9fd5076163b82aa35a03caab56c510fd17ca Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 7 Feb 2024 21:00:18 +0530 Subject: [PATCH 269/675] fix: incorrect planned qty in PP (backport #39785) (backport #39792) (#39795) fix: incorrect planned qty in PP (backport #39785) (#39792) fix: incorrect planned qty in PP (cherry picked from commit a8ebc94a366e8f15e9bbeab3da064cf5f22dd1b9) Co-authored-by: s-aga-r (cherry picked from commit df9d52d3ce7155fb7f917383c7d795769aed1c15) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- .../doctype/production_plan/production_plan.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index c98d639663a..6ccd11bf9ff 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -236,9 +236,10 @@ class ProductionPlan(Document): so_item.parent, so_item.item_code, so_item.warehouse, - ( - (so_item.qty - so_item.work_order_qty - so_item.delivered_qty) * so_item.conversion_factor - ).as_("pending_qty"), + so_item.qty, + so_item.work_order_qty, + so_item.delivered_qty, + so_item.conversion_factor, so_item.description, so_item.name, so_item.bom_no, @@ -261,6 +262,11 @@ class ProductionPlan(Document): items = items_query.run(as_dict=True) + for item in items: + item.pending_qty = ( + flt(item.qty) - max(item.work_order_qty, item.delivered_qty, 0) * item.conversion_factor + ) + pi = frappe.qb.DocType("Packed Item") packed_items_query = ( From 7413c8b0a9e3ee0c692bcf70502af1899e031129 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 7 Feb 2024 15:31:34 +0000 Subject: [PATCH 270/675] chore(release): Bumped to Version 14.62.1 ## [14.62.1](https://github.com/frappe/erpnext/compare/v14.62.0...v14.62.1) (2024-02-07) ### Bug Fixes * incorrect planned qty in PP (backport [#39785](https://github.com/frappe/erpnext/issues/39785)) (backport [#39792](https://github.com/frappe/erpnext/issues/39792)) ([#39795](https://github.com/frappe/erpnext/issues/39795)) ([596c9fd](https://github.com/frappe/erpnext/commit/596c9fd5076163b82aa35a03caab56c510fd17ca)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 181e366135b..1b177e1e0cc 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.62.0" +__version__ = "14.62.1" def get_default_company(user=None): From 5c59ab5975229ce36cecd6f655f61a5f246bae34 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 7 Feb 2024 16:32:23 +0530 Subject: [PATCH 271/675] refactor: cancel Cr/Dr JE's on Sales/Purchase return cancel (cherry picked from commit 0549535603cae6a7afbd3375c8dd62b517af3c6e) --- erpnext/controllers/accounts_controller.py | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index a3f48141aac..2420d987c23 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1374,6 +1374,24 @@ class AccountsController(TransactionBase): x.update({dim.fieldname: self.get(dim.fieldname)}) reconcile_against_document(lst, active_dimensions=active_dimensions) + def cancel_system_generated_credit_debit_notes(self): + # Cancel 'Credit/Debit' Note Journal Entries, if found. + if self.doctype in ["Sales Invoice", "Purchase Invoice"]: + voucher_type = "Credit Note" if self.doctype == "Sales Invoice" else "Debit Note" + journals = frappe.db.get_all( + "Journal Entry", + filters={ + "is_system_generated": 1, + "reference_type": self.doctype, + "reference_name": self.name, + "voucher_type": voucher_type, + "docstatus": 1, + }, + pluck="name", + ) + for x in journals: + frappe.get_doc("Journal Entry", x).cancel() + def on_cancel(self): from erpnext.accounts.doctype.bank_transaction.bank_transaction import ( remove_from_bank_transaction, @@ -1386,6 +1404,8 @@ class AccountsController(TransactionBase): remove_from_bank_transaction(self.doctype, self.name) if self.doctype in ["Sales Invoice", "Purchase Invoice", "Payment Entry", "Journal Entry"]: + self.cancel_system_generated_credit_debit_notes() + # Cancel Exchange Gain/Loss Journal before unlinking cancel_exchange_gain_loss_journal(self) From c419c1de06114ef8fa442d49dadb3cbc7dfd8b04 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 7 Feb 2024 17:21:29 +0530 Subject: [PATCH 272/675] test: Invoice status on Cr/Dr note cancellation (cherry picked from commit 31a8c3bdc45f0e32a51e43260db8484e5f17aa75) --- .../test_payment_reconciliation.py | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py index d7a73f0ce71..89240ac0d8a 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py @@ -591,6 +591,66 @@ class TestPaymentReconciliation(FrappeTestCase): self.assertEqual(si.status, "Paid") self.assertEqual(si.outstanding_amount, 0) + def test_invoice_status_after_cr_note_cancellation(self): + # This test case is made after the 'always standalone Credit/Debit notes' feature is introduced + transaction_date = nowdate() + amount = 100 + + si = self.create_sales_invoice(qty=1, rate=amount, posting_date=transaction_date) + + cr_note = self.create_sales_invoice( + qty=-1, rate=amount, posting_date=transaction_date, do_not_save=True, do_not_submit=True + ) + cr_note.is_return = 1 + cr_note.return_against = si.name + cr_note = cr_note.save().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})) + pr.reconcile() + + pr.get_unreconciled_entries() + self.assertEqual(pr.get("invoices"), []) + self.assertEqual(pr.get("payments"), []) + + journals = frappe.db.get_all( + "Journal Entry", + filters={ + "is_system_generated": 1, + "docstatus": 1, + "voucher_type": "Credit Note", + "reference_type": si.doctype, + "reference_name": si.name, + }, + pluck="name", + ) + self.assertEqual(len(journals), 1) + + # assert status outstanding + si.reload() + self.assertEqual(si.status, "Credit Note Issued") + self.assertEqual(si.outstanding_amount, 0) + + cr_note.reload() + cr_note.cancel() + # 'Credit Note' Journal should be auto cancelled + journals = frappe.db.get_all( + "Journal Entry", + filters={ + "is_system_generated": 1, + "docstatus": 1, + "voucher_type": "Credit Note", + "reference_type": si.doctype, + "reference_name": si.name, + }, + pluck="name", + ) + self.assertEqual(len(journals), 0) + def test_cr_note_partial_against_invoice(self): transaction_date = nowdate() amount = 100 From 129ab38fba27f915c47ccce1eecbcbe29c7d0e12 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 7 Feb 2024 17:33:55 +0530 Subject: [PATCH 273/675] refactor(test): assert Invoice status as well (cherry picked from commit 33efe0d12d85484e551dc9ebebf8840d427ecc67) --- .../payment_reconciliation/test_payment_reconciliation.py | 6 +++++- 1 file changed, 5 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 89240ac0d8a..fb75a0f7caf 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py @@ -630,7 +630,7 @@ class TestPaymentReconciliation(FrappeTestCase): ) self.assertEqual(len(journals), 1) - # assert status outstanding + # assert status and outstanding si.reload() self.assertEqual(si.status, "Credit Note Issued") self.assertEqual(si.outstanding_amount, 0) @@ -650,6 +650,10 @@ class TestPaymentReconciliation(FrappeTestCase): pluck="name", ) self.assertEqual(len(journals), 0) + # assert status and outstanding + si.reload() + self.assertEqual(si.status, "Unpaid") + self.assertEqual(si.outstanding_amount, 100) def test_cr_note_partial_against_invoice(self): transaction_date = nowdate() From 8888ce196df86dc850fe9287cc84611a2aedcced Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 7 Feb 2024 19:59:33 +0530 Subject: [PATCH 274/675] refactor(test): Forex Credit Note cancellation against Invoice (cherry picked from commit 2f676ced5c712823c5737f40230ec8b1994cd2dd) --- .../controllers/tests/test_accounts_controller.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/erpnext/controllers/tests/test_accounts_controller.py b/erpnext/controllers/tests/test_accounts_controller.py index 5e3077ea8c8..0c9d34d82a2 100644 --- a/erpnext/controllers/tests/test_accounts_controller.py +++ b/erpnext/controllers/tests/test_accounts_controller.py @@ -1108,18 +1108,18 @@ class TestAccountsController(FrappeTestCase): cr_note.reload() cr_note.cancel() - # Exchange Gain/Loss Journal should've been created. + # with the introduction of 'cancel_system_generated_credit_debit_notes' in accounts controller + # JE(Credit Note) will be cancelled once the parent is cancelled exc_je_for_si = self.get_journals_for(si.doctype, si.name) exc_je_for_cr = self.get_journals_for(cr_note.doctype, cr_note.name) - self.assertNotEqual(exc_je_for_si, []) - self.assertEqual(len(exc_je_for_si), 1) + self.assertEqual(exc_je_for_si, []) + self.assertEqual(len(exc_je_for_si), 0) self.assertEqual(len(exc_je_for_cr), 0) - # The Credit Note JE is still active and is referencing the sales invoice - # So, outstanding stays the same + # No references, full outstanding si.reload() - self.assertEqual(si.outstanding_amount, 1) - self.assert_ledger_outstanding(si.doctype, si.name, 80.0, 1.0) + self.assertEqual(si.outstanding_amount, 2) + self.assert_ledger_outstanding(si.doctype, si.name, 160.0, 2.0) def test_40_cost_center_from_payment_entry(self): """ From 0822a2b40af8f2052c037e552e5d8ce8140df75d Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 8 Feb 2024 07:28:09 +0000 Subject: [PATCH 275/675] chore(release): Bumped to Version 14.62.2 ## [14.62.2](https://github.com/frappe/erpnext/compare/v14.62.1...v14.62.2) (2024-02-08) ### Bug Fixes * accommodate for default rounding method in v14 ([79479b6](https://github.com/frappe/erpnext/commit/79479b633fd9255aacddd60de456b7ee55439c01)) * remove duplicates from tax category map ([a4e6c38](https://github.com/frappe/erpnext/commit/a4e6c388cb7838ad1f3c497c315a0fefa4e10f06)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 1b177e1e0cc..5b40c6d60fd 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.62.1" +__version__ = "14.62.2" def get_default_company(user=None): From f79e0d1e37d167ac817c2ef48c35343978c2c3b7 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 8 Feb 2024 13:22:55 +0530 Subject: [PATCH 276/675] fix: broken route option in Profitability report --- .../report/profitability_analysis/profitability_analysis.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/profitability_analysis/profitability_analysis.js b/erpnext/accounts/report/profitability_analysis/profitability_analysis.js index 4dbc687366d..1ac52fe5206 100644 --- a/erpnext/accounts/report/profitability_analysis/profitability_analysis.js +++ b/erpnext/accounts/report/profitability_analysis/profitability_analysis.js @@ -105,7 +105,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "to_fiscal_year": data.fiscal_year }; - if(data.based_on == 'cost_center'){ + if(data.based_on == 'Cost Center'){ frappe.route_options["cost_center"] = data.account } else { frappe.route_options["project"] = data.account From 2885b8fa44b70270be92706b45b02f36405868ea Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 8 Feb 2024 14:18:50 +0530 Subject: [PATCH 277/675] fix: do not throw validation for canceled SLE (backport #39769) (#39810) fix: do not throw validation for cancelled sle (cherry picked from commit 32ccf3524ad973b1bc8c5c7eefa4c79f5d8b4444) Co-authored-by: Rohit Waghchaure --- erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py | 3 +++ 1 file changed, 3 insertions(+) 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 1d52b17149b..9580e83ed95 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py @@ -53,6 +53,9 @@ class StockLedgerEntry(Document): self.validate_inventory_dimension_negative_stock() def validate_inventory_dimension_negative_stock(self): + if self.is_cancelled: + return + extra_cond = "" kwargs = {} From d6054dbdbda69ac830e6123a3bc5ca220c108d0e Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 8 Feb 2024 14:18:50 +0530 Subject: [PATCH 278/675] fix: do not throw validation for canceled SLE (backport #39769) (#39810) fix: do not throw validation for cancelled sle (cherry picked from commit 32ccf3524ad973b1bc8c5c7eefa4c79f5d8b4444) Co-authored-by: Rohit Waghchaure (cherry picked from commit 2885b8fa44b70270be92706b45b02f36405868ea) --- erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py | 3 +++ 1 file changed, 3 insertions(+) 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 1d52b17149b..9580e83ed95 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py @@ -53,6 +53,9 @@ class StockLedgerEntry(Document): self.validate_inventory_dimension_negative_stock() def validate_inventory_dimension_negative_stock(self): + if self.is_cancelled: + return + extra_cond = "" kwargs = {} From 8caf6555294f2799fc8fa89ed14e5c9309b8a0dc Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 8 Feb 2024 09:12:55 +0000 Subject: [PATCH 279/675] chore(release): Bumped to Version 14.62.3 ## [14.62.3](https://github.com/frappe/erpnext/compare/v14.62.2...v14.62.3) (2024-02-08) ### Bug Fixes * do not throw validation for canceled SLE (backport [#39769](https://github.com/frappe/erpnext/issues/39769)) ([#39810](https://github.com/frappe/erpnext/issues/39810)) ([d6054db](https://github.com/frappe/erpnext/commit/d6054dbdbda69ac830e6123a3bc5ca220c108d0e)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 5b40c6d60fd..3847833443a 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.62.2" +__version__ = "14.62.3" def get_default_company(user=None): From f2d094d1ab8df6af4bd07410f9a1e933e0cce98a Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Thu, 8 Feb 2024 15:47:14 +0530 Subject: [PATCH 280/675] fix: Handling circular linking while cancelling asset capitalization --- erpnext/assets/doctype/asset/asset.js | 2 +- erpnext/assets/doctype/asset/depreciation.py | 2 +- .../doctype/asset_capitalization/asset_capitalization.js | 1 + .../doctype/asset_capitalization/asset_capitalization.py | 6 +++++- 4 files changed, 8 insertions(+), 3 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index 120ca44cd23..9356484e7a4 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -322,7 +322,7 @@ frappe.ui.form.on('Asset', { }, make_schedules_editable: function(frm) { - if (frm.doc.finance_books.length) { + if (frm.doc.finance_books && frm.doc.finance_books.length) { var is_manual_hence_editable = frm.doc.finance_books.filter(d => d.depreciation_method == "Manual").length > 0 ? true : false; var is_shift_hence_editable = frm.doc.finance_books.filter(d => d.shift_based).length > 0 diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py index 7eb600025e0..6523713ece0 100644 --- a/erpnext/assets/doctype/asset/depreciation.py +++ b/erpnext/assets/doctype/asset/depreciation.py @@ -508,7 +508,7 @@ def modify_depreciation_schedule_for_asset_repairs(asset): def reverse_depreciation_entry_made_after_disposal(asset, date): - if not asset.calculate_depreciation: + if not asset.calculate_depreciation or not asset.get("schedules"): return row = -1 diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js index 67f8421fbde..e3e57ec87b2 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js @@ -7,6 +7,7 @@ frappe.provide("erpnext.assets"); erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.stock.StockController { setup() { this.setup_posting_date_time_check(); + this.frm.ignore_doctypes_on_cancel_all = ["Asset Movement"]; } onload() { diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index d1bf15eb459..9cfa4294fb0 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -77,17 +77,21 @@ class AssetCapitalization(StockController): "Stock Ledger Entry", "Repost Item Valuation", "Asset", + "Asset Movement" ) self.cancel_target_asset() self.update_stock_ledger() self.make_gl_entries() self.restore_consumed_asset_items() - + def cancel_target_asset(self): if self.entry_type == "Capitalization" and self.target_asset: asset_doc = frappe.get_doc("Asset", self.target_asset) + asset_doc.db_set("capitalized_in", None) if asset_doc.docstatus == 1: asset_doc.cancel() + elif asset_doc.docstatus == 0: + asset_doc.delete() def set_title(self): self.title = self.target_asset_name or self.target_item_name or self.target_item_code From dcf19c3ed918f18ade8e47b7a435650d0abd4280 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 9 Feb 2024 16:50:14 +0530 Subject: [PATCH 281/675] fix: reconciliation issue due to notation difference --- erpnext/accounts/utils.py | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index b3e9996b515..280cf338f70 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -10,7 +10,7 @@ import frappe.defaults from frappe import _, qb, throw from frappe.model.meta import get_field_precision from frappe.query_builder import AliasedQuery, Criterion, Table -from frappe.query_builder.functions import Sum +from frappe.query_builder.functions import Round, Sum from frappe.query_builder.utils import DocType from frappe.utils import ( cint, @@ -549,16 +549,19 @@ def check_if_advance_entry_modified(args): args, ) else: - ret = frappe.db.sql( - """select name from `tabPayment Entry` - where - name = %(voucher_no)s and docstatus = 1 - and party_type = %(party_type)s and party = %(party)s and {0} = %(account)s - and round(unallocated_amount, {1}) = round(%(unreconciled_amount)s, {1}) - """.format( - party_account_field, precision - ), - args, + pe = qb.DocType("Payment Entry") + ret = ( + qb.from_(pe) + .select(pe.name) + .where( + (pe.name == args.voucher_no) + & (pe.docstatus == 1) + & (pe.party_type == args.party_type) + & (pe.party == args.party) + & (pe[party_account_field] == args.account) + & (Round(pe.unallocated_amount, precision) == Round(args.unreconciled_amount, precision)) + ) + .run() ) if not ret: From 79a16bad156803bbf0298b6794b7e1301aad49bc Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 11 Feb 2024 17:19:59 +0530 Subject: [PATCH 282/675] fix: set route options to cost center (#37235) fix: set route to cost center cost center mapping is not correct (cherry picked from commit 5e4b73918db809afe0c3880a63537d8e4743f3bf) Co-authored-by: NIYAZ RAZAK <76736615+niyazrazak@users.noreply.github.com> From de6e8c74c5873393385441deb1bffac8006c1f45 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sun, 11 Feb 2024 17:38:37 +0530 Subject: [PATCH 283/675] fix(ux): set rate as price list rate on uom change in MR (backport #39816) (#39817) * fix: add price list rate field in MR Item (cherry picked from commit 61a29eb5fbd0535a4d91f2fd4c4e7fbb0edf6ace) # Conflicts: # erpnext/stock/doctype/material_request_item/material_request_item.py * fix: set rate as price list rate on uom change (cherry picked from commit 5cf0759b0c6c41740b19197aa4e13722a806af97) * chore: linter (cherry picked from commit 1745371cd67939bf2d8bac9e0aac6478332c17de) * chore: `conflicts` --------- Co-authored-by: s-aga-r --- .../material_request/material_request.js | 20 +++++++++++++++---- .../material_request_item.json | 12 ++++++++++- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index 675a3e978c0..4dc8e6b9cbd 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -228,9 +228,17 @@ frappe.ui.form.on('Material Request', { const qty_fields = ['actual_qty', 'projected_qty', 'min_order_qty']; if(!r.exc) { - $.each(r.message, function(k, v) { - if(!d[k] || in_list(qty_fields, k)) d[k] = v; + $.each(r.message, function(key, value) { + if(!d[key] || qty_fields.includes(key)) { + d[key] = value; + } }); + + if (d.price_list_rate != r.message.price_list_rate) { + d.price_list_rate = r.message.price_list_rate; + + frappe.model.set_value(d.doctype, d.name, "rate", d.price_list_rate); + } } } }); @@ -432,7 +440,6 @@ frappe.ui.form.on("Material Request Item", { item.amount = flt(item.qty) * flt(item.rate); frappe.model.set_value(doctype, name, "amount", item.amount); refresh_field("amount", item.name, item.parentfield); - frm.events.get_item_data(frm, item, false); }, item_code: function(frm, doctype, name) { @@ -452,7 +459,12 @@ frappe.ui.form.on("Material Request Item", { set_schedule_date(frm); } } - } + }, + + conversion_factor: function(frm, doctype, name) { + const item = locals[doctype][name]; + frm.events.get_item_data(frm, item, false); + }, }); erpnext.buying.MaterialRequestController = class MaterialRequestController extends erpnext.buying.BuyingController { diff --git a/erpnext/stock/doctype/material_request_item/material_request_item.json b/erpnext/stock/doctype/material_request_item/material_request_item.json index ed4a7e7cf64..d03a356c28a 100644 --- a/erpnext/stock/doctype/material_request_item/material_request_item.json +++ b/erpnext/stock/doctype/material_request_item/material_request_item.json @@ -35,6 +35,7 @@ "received_qty", "rate_and_amount_section_break", "rate", + "price_list_rate", "col_break3", "amount", "accounting_details_section", @@ -474,13 +475,22 @@ "fieldtype": "Link", "label": "WIP Composite Asset", "options": "Asset" + }, + { + "fieldname": "price_list_rate", + "fieldtype": "Currency", + "hidden": 1, + "label": "Price List Rate", + "options": "currency", + "print_hide": 1, + "read_only": 1 } ], "idx": 1, "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-11-14 18:37:59.599115", + "modified": "2024-02-08 16:30:56.137858", "modified_by": "Administrator", "module": "Stock", "name": "Material Request Item", From 24386006d695123bbb02f3a976790f7fcffe7635 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 12 Feb 2024 11:44:41 +0530 Subject: [PATCH 284/675] fix: cancel asset capitalization --- .../assets/doctype/asset_capitalization/asset_capitalization.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index 9cfa4294fb0..a347ece5f5b 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -90,8 +90,6 @@ class AssetCapitalization(StockController): asset_doc.db_set("capitalized_in", None) if asset_doc.docstatus == 1: asset_doc.cancel() - elif asset_doc.docstatus == 0: - asset_doc.delete() def set_title(self): self.title = self.target_asset_name or self.target_item_name or self.target_item_code From 08e02710cda1057846bead48021548a537779fc4 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 12 Feb 2024 15:54:03 +0530 Subject: [PATCH 285/675] perf: cached get_last_purchase_details to fix performance issue (backport #39854) (#39855) perf: cached get_last_purchase_details to fix performance issue (#39854) (cherry picked from commit b966c06a4f7ec1d64e475a626ee934695c77a2a4) Co-authored-by: rohitwaghchaure --- erpnext/controllers/stock_controller.py | 3 +++ erpnext/stock/doctype/item/item.py | 1 + 2 files changed, 4 insertions(+) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index f6ccb824aea..bc3ec7f93c2 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -831,6 +831,9 @@ class StockController(AccountsController): "Stock Reconciliation", ) + if not frappe.get_all("Putaway Rule", limit=1): + return + if self.doctype == "Purchase Invoice" and self.get("update_stock") == 0: valid_doctype = False diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 96cec1dd0a2..ac1b3f27848 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -1047,6 +1047,7 @@ def validate_cancelled_item(item_code, docstatus=None): frappe.throw(_("Item {0} is cancelled").format(item_code)) +@frappe.request_cache 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 From a61cffd7c20f7b3d0daf068c013bab09b0266e7a Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 12 Feb 2024 18:17:49 +0530 Subject: [PATCH 286/675] perf: production plan submission (backport #39846) (#39859) perf: production plan submission (cherry picked from commit aa1c69dd7aa7731b699853bafcfc40d19d5ab70a) Co-authored-by: s-aga-r --- .../material_request_plan_item.json | 11 +++++++---- .../doctype/production_plan/production_plan.json | 5 +++-- .../doctype/production_plan/production_plan.py | 8 ++++---- .../manufacturing/doctype/work_order/work_order.json | 5 +++-- .../doctype/work_order_item/work_order_item.json | 5 +++-- 5 files changed, 20 insertions(+), 14 deletions(-) diff --git a/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json b/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json index d07bf0fa66b..06c1b497551 100644 --- a/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json +++ b/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.json @@ -38,7 +38,8 @@ "in_list_view": 1, "label": "Item Code", "options": "Item", - "reqd": 1 + "reqd": 1, + "search_index": 1 }, { "fieldname": "item_name", @@ -53,7 +54,8 @@ "in_standard_filter": 1, "label": "For Warehouse", "options": "Warehouse", - "reqd": 1 + "reqd": 1, + "search_index": 1 }, { "columns": 1, @@ -141,7 +143,8 @@ "fieldname": "from_warehouse", "fieldtype": "Link", "label": "From Warehouse", - "options": "Warehouse" + "options": "Warehouse", + "search_index": 1 }, { "fetch_from": "item_code.safety_stock", @@ -199,7 +202,7 @@ ], "istable": 1, "links": [], - "modified": "2023-09-12 12:09:08.358326", + "modified": "2024-02-11 16:21:11.977018", "modified_by": "Administrator", "module": "Manufacturing", "name": "Material Request Plan Item", diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json index 257b60c4869..54c3893928b 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.json +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json @@ -298,7 +298,8 @@ "no_copy": 1, "options": "\nDraft\nSubmitted\nNot Started\nIn Process\nCompleted\nClosed\nCancelled\nMaterial Requested", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "search_index": 1 }, { "fieldname": "amended_from", @@ -436,7 +437,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2023-12-26 16:31:13.740777", + "modified": "2024-02-11 15:42:47.642481", "modified_by": "Administrator", "module": "Manufacturing", "name": "Production Plan", diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 6ccd11bf9ff..f8c18e8d8ba 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -1691,23 +1691,23 @@ def get_reserved_qty_for_production_plan(item_code, warehouse): return reserved_qty_for_production_plan - reserved_qty_for_production +@frappe.request_cache def get_non_completed_production_plans(): table = frappe.qb.DocType("Production Plan") child = frappe.qb.DocType("Production Plan Item") - query = ( + return ( frappe.qb.from_(table) .inner_join(child) .on(table.name == child.parent) .select(table.name) + .distinct() .where( (table.docstatus == 1) & (table.status.notin(["Completed", "Closed"])) & (child.planned_qty > child.ordered_qty) ) - ).run(as_dict=True) - - return list(set([d.name for d in query])) + ).run(pluck="name") def get_raw_materials_of_sub_assembly_items( diff --git a/erpnext/manufacturing/doctype/work_order/work_order.json b/erpnext/manufacturing/doctype/work_order/work_order.json index fb44dfdffbb..1c9a1492798 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.json +++ b/erpnext/manufacturing/doctype/work_order/work_order.json @@ -448,7 +448,8 @@ "no_copy": 1, "options": "Production Plan", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "search_index": 1 }, { "fieldname": "production_plan_item", @@ -600,7 +601,7 @@ "image_field": "image", "is_submittable": 1, "links": [], - "modified": "2023-08-11 18:35:49.852069", + "modified": "2024-02-11 15:47:13.454422", "modified_by": "Administrator", "module": "Manufacturing", "name": "Work Order", 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 f354d45381c..0f4d693544e 100644 --- a/erpnext/manufacturing/doctype/work_order_item/work_order_item.json +++ b/erpnext/manufacturing/doctype/work_order_item/work_order_item.json @@ -36,7 +36,8 @@ "fieldtype": "Link", "in_list_view": 1, "label": "Item Code", - "options": "Item" + "options": "Item", + "search_index": 1 }, { "fieldname": "source_warehouse", @@ -141,7 +142,7 @@ ], "istable": 1, "links": [], - "modified": "2022-09-28 10:50:43.512562", + "modified": "2024-02-11 15:45:32.318374", "modified_by": "Administrator", "module": "Manufacturing", "name": "Work Order Item", From 8f58b613e4585623087d59585016057b05a71579 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Mon, 12 Feb 2024 17:05:03 +0100 Subject: [PATCH 287/675] fix: calculate `stock_value_diff` `d.item_tax_amount` is already in base currency. (cherry picked from commit 5df585179812c9f6f4b8f9593c24ef29529c8258) --- erpnext/stock/doctype/purchase_receipt/purchase_receipt.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 274073b638e..bf5f9f4d1d4 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -572,9 +572,7 @@ class PurchaseReceipt(BuyingController): ) stock_value_diff = ( - flt(d.base_net_amount) - + flt(d.item_tax_amount / self.conversion_rate) - + flt(d.landed_cost_voucher_amount) + flt(d.base_net_amount) + flt(d.item_tax_amount) + flt(d.landed_cost_voucher_amount) ) elif warehouse_account.get(d.warehouse): stock_value_diff = get_stock_value_difference(self.name, d.name, d.warehouse) From d0b9c568d3645ffa53dfe3e6589c5f2e3b6eff30 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 14 Feb 2024 15:57:48 +0530 Subject: [PATCH 288/675] fix: landed cost voucher not submitting because of incorrect reference (backport #39898) (#39899) fix: landed cost voucher not submitting because of incorrect reference (#39898) (cherry picked from commit 6239fd704b7d7a60c54b8042a8cc83b5c9e75eab) Co-authored-by: rohitwaghchaure --- erpnext/controllers/buying_controller.py | 4 ++-- .../landed_cost_voucher.py | 21 +++++++++++++++++++ 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index a2b7a65fce0..4a627696032 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -190,8 +190,8 @@ class BuyingController(SubcontractingController): lc_voucher_data = frappe.db.sql( """select sum(applicable_charges), cost_center from `tabLanded Cost Item` - where docstatus = 1 and purchase_receipt_item = %s""", - d.name, + where docstatus = 1 and purchase_receipt_item = %s and receipt_document = %s""", + (d.name, self.name), ) d.landed_cost_voucher_amount = lc_voucher_data[0][0] if lc_voucher_data else 0.0 if not d.cost_center and lc_voucher_data and lc_voucher_data[0][1]: diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py index c73dc65f8a8..11a001ccc70 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py @@ -38,6 +38,7 @@ class LandedCostVoucher(Document): def validate(self): self.check_mandatory() self.validate_receipt_documents() + self.validate_line_items() init_landed_taxes_and_totals(self) self.set_total_taxes_and_charges() if not self.get("items"): @@ -45,6 +46,26 @@ class LandedCostVoucher(Document): self.set_applicable_charges_on_item() + def validate_line_items(self): + for d in self.get("items"): + if ( + d.docstatus == 0 + and d.purchase_receipt_item + and not frappe.db.exists( + d.receipt_document_type + " Item", + {"name": d.purchase_receipt_item, "parent": d.receipt_document}, + ) + ): + frappe.throw( + _("Row {0}: {2} Item {1} does not exist in {2} {3}").format( + d.idx, + frappe.bold(d.purchase_receipt_item), + d.receipt_document_type, + frappe.bold(d.receipt_document), + ), + title=_("Incorrect Reference Document (Purchase Receipt Item)"), + ) + def check_mandatory(self): if not self.get("purchase_receipts"): frappe.throw(_("Please enter Receipt Document")) From ab7e323648169e5115b05b28b66d5b6859ff3925 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Wed, 14 Feb 2024 17:28:50 +0530 Subject: [PATCH 289/675] fix: production plan issue with sales order (backport #39901) (#39903) fix: production plan issue with sales order (#39901) (cherry picked from commit d0df5df4a609a04d98ee97c9e56931672143587d) Co-authored-by: rohitwaghchaure --- .../manufacturing/doctype/production_plan/production_plan.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index f8c18e8d8ba..aea6c987177 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -576,7 +576,10 @@ class ProductionPlan(Document): "project": self.project, } - key = (d.item_code, d.sales_order, d.warehouse) + key = (d.item_code, d.sales_order, d.sales_order_item, d.warehouse) + if self.combine_items: + key = (d.item_code, d.sales_order, d.warehouse) + if not d.sales_order: key = (d.name, d.item_code, d.warehouse) From 22ace5cb5a5b2350df685587f20b3a7b948c5548 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 14 Feb 2024 12:06:19 +0000 Subject: [PATCH 290/675] chore(release): Bumped to Version 14.62.4 ## [14.62.4](https://github.com/frappe/erpnext/compare/v14.62.3...v14.62.4) (2024-02-14) ### Bug Fixes * production plan issue with sales order (backport [#39901](https://github.com/frappe/erpnext/issues/39901)) ([#39903](https://github.com/frappe/erpnext/issues/39903)) ([ab7e323](https://github.com/frappe/erpnext/commit/ab7e323648169e5115b05b28b66d5b6859ff3925)) * accommodate for default rounding method in v14 ([d6a758d](https://github.com/frappe/erpnext/commit/d6a758d1f4c751f7be83197e677638bb0db80596)) * broken route option in Profitability report ([f79e0d1](https://github.com/frappe/erpnext/commit/f79e0d1e37d167ac817c2ef48c35343978c2c3b7)) * calculate `stock_value_diff` ([8f58b61](https://github.com/frappe/erpnext/commit/8f58b613e4585623087d59585016057b05a71579)) * cancel asset capitalization ([2438600](https://github.com/frappe/erpnext/commit/24386006d695123bbb02f3a976790f7fcffe7635)) * do not throw validation for canceled SLE (backport [#39769](https://github.com/frappe/erpnext/issues/39769)) ([#39810](https://github.com/frappe/erpnext/issues/39810)) ([2885b8f](https://github.com/frappe/erpnext/commit/2885b8fa44b70270be92706b45b02f36405868ea)) * Handling circular linking while cancelling asset capitalization ([f2d094d](https://github.com/frappe/erpnext/commit/f2d094d1ab8df6af4bd07410f9a1e933e0cce98a)) * incorrect planned qty in PP (backport [#39785](https://github.com/frappe/erpnext/issues/39785)) ([#39792](https://github.com/frappe/erpnext/issues/39792)) ([df9d52d](https://github.com/frappe/erpnext/commit/df9d52d3ce7155fb7f917383c7d795769aed1c15)) * landed cost voucher not submitting because of incorrect reference (backport [#39898](https://github.com/frappe/erpnext/issues/39898)) ([#39899](https://github.com/frappe/erpnext/issues/39899)) ([d0b9c56](https://github.com/frappe/erpnext/commit/d0b9c568d3645ffa53dfe3e6589c5f2e3b6eff30)) * remove duplicates from tax category map ([c56f3a5](https://github.com/frappe/erpnext/commit/c56f3a58ab31aaecee2b4007aac53d10f6e73473)) * set rate for PO created against BO (backport [#39765](https://github.com/frappe/erpnext/issues/39765)) ([#39766](https://github.com/frappe/erpnext/issues/39766)) ([de47e67](https://github.com/frappe/erpnext/commit/de47e67dfacd7dabb4c935728a9d95df736343c9)) * set route options to cost center ([#37235](https://github.com/frappe/erpnext/issues/37235)) ([79a16ba](https://github.com/frappe/erpnext/commit/79a16bad156803bbf0298b6794b7e1301aad49bc)) * **ux:** set rate as price list rate on uom change in MR (backport [#39816](https://github.com/frappe/erpnext/issues/39816)) ([#39817](https://github.com/frappe/erpnext/issues/39817)) ([de6e8c7](https://github.com/frappe/erpnext/commit/de6e8c74c5873393385441deb1bffac8006c1f45)) ### Performance Improvements * cached get_last_purchase_details to fix performance issue (backport [#39854](https://github.com/frappe/erpnext/issues/39854)) ([#39855](https://github.com/frappe/erpnext/issues/39855)) ([08e0271](https://github.com/frappe/erpnext/commit/08e02710cda1057846bead48021548a537779fc4)) * production plan submission (backport [#39846](https://github.com/frappe/erpnext/issues/39846)) ([#39859](https://github.com/frappe/erpnext/issues/39859)) ([a61cffd](https://github.com/frappe/erpnext/commit/a61cffd7c20f7b3d0daf068c013bab09b0266e7a)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 3847833443a..a36b9c32636 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.62.3" +__version__ = "14.62.4" def get_default_company(user=None): From 83bf28616e33a34174ade3504dbfae8571f8e58c Mon Sep 17 00:00:00 2001 From: kunhi Date: Mon, 12 Feb 2024 12:18:37 +0400 Subject: [PATCH 291/675] fix: fetch company terms (cherry picked from commit d97b6d38ef732205f8de17dcb01d90ea1f5120fa) --- erpnext/accounts/doctype/sales_invoice/sales_invoice.js | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 2ac7370f4ef..9bdbff25577 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -11,6 +11,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e super.setup(doc); } company() { + super.company(); erpnext.accounts.dimensions.update_dimension(this.frm, this.frm.doctype); let me = this; From 45a04943189c221a73f752257565cdd77c2f19ab Mon Sep 17 00:00:00 2001 From: kunhi Date: Tue, 13 Feb 2024 22:32:10 +0400 Subject: [PATCH 292/675] fix: no need call for company method in sales invoice js (cherry picked from commit e3bd8d10b0a99fca326c7252063ef11b3665d3cf) --- .../doctype/sales_invoice/sales_invoice.js | 20 ------------------- 1 file changed, 20 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 9bdbff25577..5ed26c7937d 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -10,26 +10,6 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e this.setup_posting_date_time_check(); super.setup(doc); } - company() { - super.company(); - erpnext.accounts.dimensions.update_dimension(this.frm, this.frm.doctype); - - let me = this; - if (this.frm.doc.company) { - frappe.call({ - method: - "erpnext.accounts.party.get_party_account", - args: { - party_type: 'Customer', - party: this.frm.doc.customer, - company: this.frm.doc.company - }, - callback: (response) => { - if (response) me.frm.set_value("debit_to", response.message); - }, - }); - } - } onload() { var me = this; super.onload(); From 5eaa11b9e871048c286c278ad7601ddf4ef171bc Mon Sep 17 00:00:00 2001 From: kunhi Date: Tue, 13 Feb 2024 22:49:39 +0400 Subject: [PATCH 293/675] fix: update_dimension is required and not need party account method (cherry picked from commit e6949d71f628a15ea87ee143d09dce47b66f0e02) --- erpnext/accounts/doctype/sales_invoice/sales_invoice.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 5ed26c7937d..b2672424af9 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -10,6 +10,10 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e this.setup_posting_date_time_check(); super.setup(doc); } + company() { + super.company(); + erpnext.accounts.dimensions.update_dimension(this.frm, this.frm.doctype); + } onload() { var me = this; super.onload(); From ee9a51f93fef19b89b1d01a8f7de98237ddbefa2 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 14 Feb 2024 18:45:11 +0530 Subject: [PATCH 294/675] fix: party item code in Blanket Order (cherry picked from commit 1a8f7f94036e1703ab51f8f591d88688ad2c9f52) # Conflicts: # erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.py --- .../doctype/blanket_order/blanket_order.py | 39 +++++++++++++++++++ .../blanket_order/test_blanket_order.py | 25 ++++++++++++ .../blanket_order_item.json | 12 +++++- .../blanket_order_item/blanket_order_item.py | 23 +++++++++++ 4 files changed, 98 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py index 029b4b720a7..fe5f836a935 100644 --- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py +++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py @@ -16,11 +16,50 @@ class BlanketOrder(Document): def validate(self): self.validate_dates() self.validate_duplicate_items() + self.set_party_item_code() def validate_dates(self): if getdate(self.from_date) > getdate(self.to_date): frappe.throw(_("From date cannot be greater than To date")) + def set_party_item_code(self): + item_ref = {} + if self.blanket_order_type == "Selling": + item_ref = self.get_customer_items_ref() + print(item_ref) + else: + item_ref = self.get_supplier_items_ref() + + if not item_ref: + return + + for row in self.items: + row.party_item_code = item_ref.get(row.item_code) + + def get_customer_items_ref(self): + items = [d.item_code for d in self.items] + + return frappe._dict( + frappe.get_all( + "Item Customer Detail", + filters={"parent": ("in", items), "customer_name": self.customer}, + fields=["parent", "ref_code"], + as_list=True, + ) + ) + + def get_supplier_items_ref(self): + items = [d.item_code for d in self.items] + + return frappe._dict( + frappe.get_all( + "Item Supplier", + filters={"parent": ("in", items), "supplier": self.supplier}, + fields=["parent", "supplier_part_no"], + as_list=True, + ) + ) + def validate_duplicate_items(self): item_list = [] for item in self.items: diff --git a/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py b/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py index e9fc25b5bcb..3f3b6f092c0 100644 --- a/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py +++ b/erpnext/manufacturing/doctype/blanket_order/test_blanket_order.py @@ -5,6 +5,7 @@ from frappe.tests.utils import FrappeTestCase from frappe.utils import add_months, today from erpnext import get_company_currency +from erpnext.stock.doctype.item.test_item import make_item from .blanket_order import make_order @@ -90,6 +91,30 @@ class TestBlanketOrder(FrappeTestCase): frappe.db.set_single_value("Buying Settings", "blanket_order_allowance", 10) po.submit() + def test_party_item_code(self): + item_doc = make_item("_Test Item 1 for Blanket Order") + item_code = item_doc.name + + customer = "_Test Customer" + supplier = "_Test Supplier" + + if not frappe.db.exists( + "Item Customer Detail", {"customer_name": customer, "parent": item_code} + ): + item_doc.append("customer_items", {"customer_name": customer, "ref_code": "CUST-REF-1"}) + item_doc.save() + + if not frappe.db.exists("Item Supplier", {"supplier": supplier, "parent": item_code}): + item_doc.append("supplier_items", {"supplier": supplier, "supplier_part_no": "SUPP-PART-1"}) + item_doc.save() + + # Blanket Order for Selling + bo = make_blanket_order(blanket_order_type="Selling", customer=customer, item_code=item_code) + self.assertEqual(bo.items[0].party_item_code, "CUST-REF-1") + + bo = make_blanket_order(blanket_order_type="Purchasing", supplier=supplier, item_code=item_code) + self.assertEqual(bo.items[0].party_item_code, "SUPP-PART-1") + def make_blanket_order(**args): args = frappe._dict(args) diff --git a/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.json b/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.json index 977ad547f55..aa7831fd6b8 100644 --- a/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.json +++ b/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.json @@ -1,4 +1,5 @@ { + "actions": [], "creation": "2018-05-24 07:20:04.255236", "doctype": "DocType", "editable_grid": 1, @@ -6,6 +7,7 @@ "field_order": [ "item_code", "item_name", + "party_item_code", "column_break_3", "qty", "rate", @@ -62,10 +64,17 @@ "fieldname": "terms_and_conditions", "fieldtype": "Text", "label": "Terms and Conditions" + }, + { + "fieldname": "party_item_code", + "fieldtype": "Data", + "label": "Party Item Code", + "read_only": 1 } ], "istable": 1, - "modified": "2019-11-18 19:37:46.245878", + "links": [], + "modified": "2024-02-14 18:25:26.479672", "modified_by": "Administrator", "module": "Manufacturing", "name": "Blanket Order Item", @@ -74,5 +83,6 @@ "quick_entry": 1, "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.py b/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.py index ebce209fbc2..7260884d60c 100644 --- a/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.py +++ b/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.py @@ -6,4 +6,27 @@ from frappe.model.document import Document class BlanketOrderItem(Document): +<<<<<<< HEAD +======= + # 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 + + item_code: DF.Link + item_name: DF.Data | None + ordered_qty: DF.Float + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + party_item_code: DF.Data | None + qty: DF.Float + rate: DF.Currency + terms_and_conditions: DF.Text | None + # end: auto-generated types + +>>>>>>> 1a8f7f9403 (fix: party item code in Blanket Order) pass From 1cca51afc66d79757f7605c5d1b047811e877af4 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 15 Feb 2024 14:28:06 +0530 Subject: [PATCH 295/675] chore: fix linter issue (cherry picked from commit 230a7d8d537500c0f401f21c742f2a2ad653062a) --- erpnext/manufacturing/doctype/blanket_order/blanket_order.py | 1 - 1 file changed, 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py index fe5f836a935..83629092fa4 100644 --- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.py +++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.py @@ -26,7 +26,6 @@ class BlanketOrder(Document): item_ref = {} if self.blanket_order_type == "Selling": item_ref = self.get_customer_items_ref() - print(item_ref) else: item_ref = self.get_supplier_items_ref() From ca8fb17ee849d5d6564ca15f03d32353d246c646 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 15 Feb 2024 17:32:34 +0530 Subject: [PATCH 296/675] chore: fix conflicts --- .../blanket_order_item/blanket_order_item.py | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.py b/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.py index 7260884d60c..ebce209fbc2 100644 --- a/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.py +++ b/erpnext/manufacturing/doctype/blanket_order_item/blanket_order_item.py @@ -6,27 +6,4 @@ from frappe.model.document import Document class BlanketOrderItem(Document): -<<<<<<< HEAD -======= - # 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 - - item_code: DF.Link - item_name: DF.Data | None - ordered_qty: DF.Float - parent: DF.Data - parentfield: DF.Data - parenttype: DF.Data - party_item_code: DF.Data | None - qty: DF.Float - rate: DF.Currency - terms_and_conditions: DF.Text | None - # end: auto-generated types - ->>>>>>> 1a8f7f9403 (fix: party item code in Blanket Order) pass From cea0e1fb91f8ae8a166af6615ed7878b885dd257 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Thu, 15 Feb 2024 19:58:16 +0100 Subject: [PATCH 297/675] fix(Bank Transaction): precision for `(un)allocated_amount` Manual Backport of #39926 --- .../accounts/doctype/bank_transaction/bank_transaction.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py index 86c3a8a9336..c0a65ed0d4c 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py @@ -41,9 +41,10 @@ class BankTransaction(StatusUpdater): else: allocated_amount = 0.0 - amount = abs(flt(self.withdrawal) - flt(self.deposit)) - self.db_set("allocated_amount", flt(allocated_amount)) - self.db_set("unallocated_amount", amount - flt(allocated_amount)) + unallocated_amount = abs(flt(self.withdrawal) - flt(self.deposit)) - allocated_amount + + self.db_set("allocated_amount", flt(allocated_amount, self.precision("allocated_amount"))) + self.db_set("unallocated_amount", flt(unallocated_amount, self.precision("unallocated_amount"))) self.reload() self.set_status(update=True) From 8deaba8defd483bf9027bd03c58154f01f20fb24 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Fri, 16 Feb 2024 19:58:58 +0100 Subject: [PATCH 298/675] fix(Issue): create communication Ignore permisions and mandatory. Required, for example, when Issue is created by Customer via portal. (cherry picked from commit 3f1d008741e1255b2b9c0d31aa489d9b427ab5c7) --- erpnext/support/doctype/issue/issue.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/support/doctype/issue/issue.py b/erpnext/support/doctype/issue/issue.py index 7f3e0cf4c21..25041b1973b 100644 --- a/erpnext/support/doctype/issue/issue.py +++ b/erpnext/support/doctype/issue/issue.py @@ -72,8 +72,8 @@ class Issue(Document): "reference_name": self.name, } ) - communication.ignore_permissions = True - communication.ignore_mandatory = True + communication.flags.ignore_permissions = True + communication.flags.ignore_mandatory = True communication.save() @frappe.whitelist() From 5894bb3bbe2a7c6b52b52aaaa19412904ac49cca Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 17 Feb 2024 07:19:32 +0530 Subject: [PATCH 299/675] refactor: use popup to inform on additional reconciliation step (cherry picked from commit 0d260faa0070f767220ba724dd42b0f2d2cc922d) --- erpnext/controllers/accounts_controller.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 2420d987c23..f642cde73c3 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -201,6 +201,18 @@ class AccountsController(TransactionBase): ) ) + if self.get("is_return") and self.get("return_against"): + document_type = "Credit Note" if self.doctype == "Sales Invoice" else "Debit Note" + frappe.msgprint( + _( + "{0} will be treated as a standalone {0}. Post creation use {1} tool to reconcile against {2}." + ).format( + document_type, + get_link_to_form("Payment Reconciliation", "Payment Reconciliation"), + get_link_to_form(self.doctype, self.get("return_against")), + ) + ) + pos_check_field = "is_pos" if self.doctype == "Sales Invoice" else "is_paid" if cint(self.allocate_advances_automatically) and not cint(self.get(pos_check_field)): self.set_advances() From b1a4249041b1834965cc719ac733423a198fe615 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 19 Feb 2024 07:44:02 +0530 Subject: [PATCH 300/675] fix: typeerror 'Item Group' filter on Purchase Register --- .../item_wise_purchase_register/item_wise_purchase_register.py | 3 ++- 1 file changed, 2 insertions(+), 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 5d3d4d74978..b5dd66f75fe 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 @@ -319,7 +319,8 @@ def get_items(filters, additional_query_columns): `tabPurchase Invoice`.supplier, `tabPurchase Invoice`.remarks, `tabPurchase Invoice`.base_net_total, `tabPurchase Invoice`.unrealized_profit_loss_account, `tabPurchase Invoice Item`.`item_code`, `tabPurchase Invoice Item`.description, - `tabPurchase Invoice Item`.`item_name` as pi_item_name, `tabPurchase Invoice Item`.`item_group` as pi_item_group, + `tabPurchase Invoice Item`.`item_name` as pi_item_name, `tabPurchase Invoice Item`.`item_group` + ,`tabPurchase Invoice Item`.`item_group` as pi_item_group, `tabItem`.`item_name` as i_item_name, `tabItem`.`item_group` as i_item_group, `tabPurchase Invoice Item`.`project`, `tabPurchase Invoice Item`.`purchase_order`, `tabPurchase Invoice Item`.`purchase_receipt`, `tabPurchase Invoice Item`.`po_detail`, From 4921b038bdcfef61191bb31c97e6583a564f3589 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 17 Feb 2024 06:41:00 +0530 Subject: [PATCH 301/675] fix: group node in warehouse filter in Item-wise Sales Register (cherry picked from commit 44538bd02aa150844135d21934bf403ff5244e68) --- .../item_wise_sales_register/item_wise_sales_register.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py index ce22d7566c1..94457d59981 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -350,7 +350,13 @@ def get_conditions(filters, additional_conditions=None): and ifnull(`tabSales Invoice Payment`.mode_of_payment, '') = %(mode_of_payment)s)""" if filters.get("warehouse"): - conditions += """and ifnull(`tabSales Invoice Item`.warehouse, '') = %(warehouse)s""" + if frappe.db.get_value("Warehouse", filters.get("warehouse"), "is_group"): + lft, rgt = frappe.db.get_all( + "Warehouse", filters={"name": filters.get("warehouse")}, fields=["lft", "rgt"], as_list=True + )[0] + conditions += f"and ifnull(`tabSales Invoice Item`.warehouse, '') in (select name from `tabWarehouse` where lft > {lft} and rgt < {rgt}) " + else: + conditions += """and ifnull(`tabSales Invoice Item`.warehouse, '') = %(warehouse)s""" if filters.get("brand"): conditions += """and ifnull(`tabSales Invoice Item`.brand, '') = %(brand)s""" From 241507a87ff108eabcc8fb7422a1dd80a9836e26 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 15 Feb 2024 12:35:14 +0530 Subject: [PATCH 302/675] refactor: add total row if only one party is being filtered (cherry picked from commit b1dfa2537bee220e8bb915b8152e4aa3014befa9) --- .../report/accounts_receivable/accounts_receivable.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 9219cc5e383..88e26863cf1 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -85,7 +85,10 @@ class ReceivablePayableReport(object): self.skip_total_row = 1 if self.filters.get("in_party_currency"): - self.skip_total_row = 1 + if self.filters.get("party") and len(self.filters.get("party")) == 1: + self.skip_total_row = 0 + else: + self.skip_total_row = 1 def get_data(self): self.get_ple_entries() From d913ec52db9d90a8eae519e2dd2eaa5b657bb0c7 Mon Sep 17 00:00:00 2001 From: Nitali Mittal <51705792+nitmit@users.noreply.github.com> Date: Mon, 19 Feb 2024 16:36:23 +0530 Subject: [PATCH 303/675] feat: New financial views - Growth and margin views for P&L and balance sheet feat: New financial views - Growth and margin views for P&L and balance sheet --- .../report/balance_sheet/balance_sheet.js | 13 +++++ .../profit_and_loss_statement.js | 15 ++++++ erpnext/public/js/financial_statements.js | 50 +++++++++++++++++++ 3 files changed, 78 insertions(+) diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.js b/erpnext/accounts/report/balance_sheet/balance_sheet.js index f1f8e5f6e7c..57de73e72d0 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.js +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.js @@ -6,6 +6,19 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { erpnext.utils.add_dimensions('Balance Sheet', 10); + frappe.query_reports["Balance Sheet"]["filters"].push( + { + "fieldname": "selected_view", + "label": __("Select View"), + "fieldtype": "Select", + "options": [ + { "value": "Report", "label": __("Report View") }, + { "value": "Growth", "label": __("Growth View") } + ], + "default": "Report", + "reqd": 1 + }, + ); frappe.query_reports["Balance Sheet"]["filters"].push({ "fieldname": "accumulated_values", "label": __("Accumulated Values"), diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js index e794f270c2b..4e5cd312734 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js @@ -8,6 +8,21 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { erpnext.utils.add_dimensions('Profit and Loss Statement', 10); + frappe.query_reports["Profit and Loss Statement"]["filters"].push( + { + "fieldname": "selected_view", + "label": __("Select View"), + "fieldtype": "Select", + "options": [ + { "value": "Report", "label": __("Report View") }, + { "value": "Growth", "label": __("Growth View") }, + { "value": "Margin", "label": __("Margin View") }, + ], + "default": "Report", + "reqd": 1 + }, + ); + frappe.query_reports["Profit and Loss Statement"]["filters"].push( { "fieldname": "include_default_book_entries", diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index 5e1974299ee..666505e2e38 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -2,7 +2,57 @@ frappe.provide("erpnext.financial_statements"); erpnext.financial_statements = { "filters": get_filters(), + "baseData": null, "formatter": function(value, row, column, data, default_formatter, filter) { + if(frappe.query_report.get_filter_value("selected_view") == "Growth" && 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 = (Math.round(annualGrowth*10000)/100); //calculating the rounded off percentage + + 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; + + value = $(`${marginPercent+'%'}`); + if(marginPercent < 0) + value = $(value).addClass("text-danger"); + else + value = $(value).addClass("text-success"); + value = $(value).wrap("

    ").parent().html(); + return value; + } + + } if (data && column.fieldname=="account") { value = data.account_name || value; From abceb1b6110953bca1609e739a0a835542b6b946 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 20 Feb 2024 12:21:12 +0530 Subject: [PATCH 304/675] fix: linter issue --- .../assets/doctype/asset_capitalization/asset_capitalization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index a347ece5f5b..8db401bcc59 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -77,7 +77,7 @@ class AssetCapitalization(StockController): "Stock Ledger Entry", "Repost Item Valuation", "Asset", - "Asset Movement" + "Asset Movement", ) self.cancel_target_asset() self.update_stock_ledger() From 11bddc14bb3169fed080ea7451b6573f1b92a980 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 20 Feb 2024 11:58:47 +0530 Subject: [PATCH 305/675] fix: Delete linked asset movement record on cancellation of purchase receipt/invoice --- erpnext/controllers/buying_controller.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 4a627696032..1abf95f5953 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -811,7 +811,8 @@ class BuyingController(SubcontractingController): if self.doctype == "Purchase Invoice" and not self.get("update_stock"): return - frappe.db.sql("delete from `tabAsset Movement` where reference_name=%s", self.name) + asset_movement = frappe.db.get_value("Asset Movement", {"reference_name": self.name}, "name") + frappe.delete_doc("Asset Movement", asset_movement, force=1) def validate_schedule_date(self): if not self.get("items"): From 963ddac528daa6ac9694757af704282f9bd22262 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 13 Feb 2024 09:40:57 +0530 Subject: [PATCH 306/675] fix: Adjust amount in last row due to rounding --- erpnext/assets/doctype/asset/asset.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 3fcb9720dd1..4560d098cd7 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -513,14 +513,14 @@ class Asset(AccountsController): ) # Adjust depreciation amount in the last period based on the expected value after useful life - if finance_book.expected_value_after_useful_life and ( + if ( ( n == cint(final_number_of_depreciations) - 1 - and value_after_depreciation != finance_book.expected_value_after_useful_life + and flt(value_after_depreciation) != flt(finance_book.expected_value_after_useful_life) ) - or value_after_depreciation < finance_book.expected_value_after_useful_life + or flt(value_after_depreciation) < flt(finance_book.expected_value_after_useful_life) ): - depreciation_amount += value_after_depreciation - finance_book.expected_value_after_useful_life + depreciation_amount += flt(value_after_depreciation) - flt(finance_book.expected_value_after_useful_life) skip_row = True if flt(depreciation_amount, self.precision("gross_purchase_amount")) > 0: From e99485bfa7d78f99094b08d5147f02cc3921cb12 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 20 Feb 2024 11:57:06 +0530 Subject: [PATCH 307/675] fix: linter issues --- erpnext/assets/doctype/asset/asset.py | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 4560d098cd7..7f5e4debf29 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -514,13 +514,14 @@ class Asset(AccountsController): # Adjust depreciation amount in the last period based on the expected value after useful life if ( - ( - n == cint(final_number_of_depreciations) - 1 - and flt(value_after_depreciation) != flt(finance_book.expected_value_after_useful_life) - ) - or flt(value_after_depreciation) < flt(finance_book.expected_value_after_useful_life) + n == cint(final_number_of_depreciations) - 1 + and flt(value_after_depreciation) != flt(finance_book.expected_value_after_useful_life) + ) or flt(value_after_depreciation) < flt( + finance_book.expected_value_after_useful_life ): - depreciation_amount += flt(value_after_depreciation) - flt(finance_book.expected_value_after_useful_life) + depreciation_amount += flt(value_after_depreciation) - flt( + finance_book.expected_value_after_useful_life + ) skip_row = True if flt(depreciation_amount, self.precision("gross_purchase_amount")) > 0: From a4fbea3722ef61e7d79e8158629e564f6f32487a Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 20 Feb 2024 12:31:34 +0530 Subject: [PATCH 308/675] fix: linter issue --- .../assets/doctype/asset_capitalization/asset_capitalization.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index 8db401bcc59..2b5d8d0ffdd 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -83,7 +83,7 @@ class AssetCapitalization(StockController): self.update_stock_ledger() self.make_gl_entries() self.restore_consumed_asset_items() - + def cancel_target_asset(self): if self.entry_type == "Capitalization" and self.target_asset: asset_doc = frappe.get_doc("Asset", self.target_asset) From 385b08dc50ba27f00fd1e71f8e3156a813ca7219 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 20 Feb 2024 13:13:35 +0530 Subject: [PATCH 309/675] fix: show active bom in the dropdown while making stock entry and MR (backport #39974) (#39975) fix: show active bom in the dropdown while making stock entry and MR (#39974) (cherry picked from commit 133f8bd92a33fb15d1ababf8b5f1d4f061426538) Co-authored-by: rohitwaghchaure --- erpnext/stock/doctype/material_request/material_request.js | 2 +- erpnext/stock/doctype/stock_entry/stock_entry.js | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index 4dc8e6b9cbd..c5bc601e391 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -250,7 +250,7 @@ frappe.ui.form.on('Material Request', { fields: [ {"fieldname":"bom", "fieldtype":"Link", "label":__("BOM"), options:"BOM", reqd: 1, get_query: function() { - return {filters: { docstatus:1 }}; + return {filters: { docstatus:1, "is_active": 1 }}; }}, {"fieldname":"warehouse", "fieldtype":"Link", "label":__("For Warehouse"), options:"Warehouse", reqd: 1}, diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index 0786ce6be2a..b6459ba1e28 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -548,7 +548,9 @@ frappe.ui.form.on('Stock Entry', { let fields = [ {"fieldname":"bom", "fieldtype":"Link", "label":__("BOM"), - options:"BOM", reqd: 1, get_query: filters()}, + options:"BOM", reqd: 1, get_query: () => { + return {filters: { docstatus:1, "is_active": 1 }}; + }}, {"fieldname":"source_warehouse", "fieldtype":"Link", "label":__("Source Warehouse"), options:"Warehouse"}, {"fieldname":"target_warehouse", "fieldtype":"Link", "label":__("Target Warehouse"), From 8e71665e4f19cddedc534c7e7996d76a6d83d0cc Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 20 Feb 2024 15:53:20 +0530 Subject: [PATCH 310/675] fix: 'NoneType' object is not iterable (backport #39977) (#39980) fix: 'NoneType' object is not iterable (#39977) (cherry picked from commit 8e7d47b3a7453ce7ded83c4205c556716f2afa19) Co-authored-by: rohitwaghchaure --- erpnext/stock/doctype/pick_list/pick_list.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py index 36c877ebc88..86462476a5d 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.py +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -32,6 +32,10 @@ class PickList(Document): self.update_status() self.set_item_locations() + if self.get("locations"): + self.validate_sales_order_percentage() + + def validate_sales_order_percentage(self): # set percentage picked in SO for location in self.get("locations"): if ( From 5b9905f27ab80dcc1b74b22d7dfa5eb52b710c2a Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 20 Feb 2024 17:54:58 +0530 Subject: [PATCH 311/675] fix: On cancelation of capitalization, reverse depreciation entry only if journal entry exists --- erpnext/assets/doctype/asset/depreciation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py index 6523713ece0..22bd6b1d6ff 100644 --- a/erpnext/assets/doctype/asset/depreciation.py +++ b/erpnext/assets/doctype/asset/depreciation.py @@ -520,7 +520,7 @@ def reverse_depreciation_entry_made_after_disposal(asset, date): else: row += 1 - if schedule.schedule_date == date: + if schedule.schedule_date == date and schedule.journal_entry: if not disposal_was_made_on_original_schedule_date( asset, schedule, row, date ) or disposal_happens_in_the_future(date): From 46cd929d00a0a71ef067ef66490d999921884110 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 20 Feb 2024 12:11:12 +0530 Subject: [PATCH 312/675] fix: Delete orphaned asset movement item records --- erpnext/patches.txt | 1 + .../v14_0/delete_orphaned_asset_movement_item_records.py | 7 +++++++ 2 files changed, 8 insertions(+) create mode 100644 erpnext/patches/v14_0/delete_orphaned_asset_movement_item_records.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 6b7b13ff46e..125158a5add 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -358,3 +358,4 @@ erpnext.patches.v14_0.migrate_gl_to_payment_ledger erpnext.stock.doctype.delivery_note.patches.drop_unused_return_against_index # 2023-12-20 erpnext.patches.v14_0.set_maintain_stock_for_bom_item execute:frappe.db.set_single_value('E Commerce Settings', 'show_actual_qty', 1) +erpnext.patches.v14_0.delete_orphaned_asset_movement_item_records diff --git a/erpnext/patches/v14_0/delete_orphaned_asset_movement_item_records.py b/erpnext/patches/v14_0/delete_orphaned_asset_movement_item_records.py new file mode 100644 index 00000000000..dff04c09458 --- /dev/null +++ b/erpnext/patches/v14_0/delete_orphaned_asset_movement_item_records.py @@ -0,0 +1,7 @@ +import frappe + +def execute(): + frappe.db.sql(""" + DELETE FROM `tabAsset Movement Item` + WHERE parent NOT IN (SELECT name FROM `tabAsset Movement`) + """) \ No newline at end of file From ea1a0b3a28cdb4cc2f6e41c748e9577a49470ac8 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 20 Feb 2024 15:51:29 +0530 Subject: [PATCH 313/675] fix: linter issue --- .../delete_orphaned_asset_movement_item_records.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/erpnext/patches/v14_0/delete_orphaned_asset_movement_item_records.py b/erpnext/patches/v14_0/delete_orphaned_asset_movement_item_records.py index dff04c09458..a1d7dc9b3d4 100644 --- a/erpnext/patches/v14_0/delete_orphaned_asset_movement_item_records.py +++ b/erpnext/patches/v14_0/delete_orphaned_asset_movement_item_records.py @@ -1,7 +1,11 @@ import frappe + def execute(): - frappe.db.sql(""" - DELETE FROM `tabAsset Movement Item` - WHERE parent NOT IN (SELECT name FROM `tabAsset Movement`) - """) \ No newline at end of file + # nosemgrep + frappe.db.sql( + """ + DELETE FROM `tabAsset Movement Item` + WHERE parent NOT IN (SELECT name FROM `tabAsset Movement`) + """ + ) From 64099b0bf75ff6574a65e49be0f39feddeeedecb Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Wed, 21 Feb 2024 09:26:58 +0530 Subject: [PATCH 314/675] fix: webpages are not showing (#39988) --- erpnext/utilities/product.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/utilities/product.py b/erpnext/utilities/product.py index 963cd2bf8f0..69559c1ed9b 100644 --- a/erpnext/utilities/product.py +++ b/erpnext/utilities/product.py @@ -51,8 +51,8 @@ def get_web_item_qty_in_stock(item_code, item_warehouse_field, warehouse=None): .where((BIN.item_code == item_code) & (BIN.warehouse == warehouse)) ).run() - stock_qty = stock_qty[0][0] if stock_qty: + stock_qty = flt(stock_qty[0][0]) total_stock += adjust_qty_for_expired_items(item_code, stock_qty, warehouse) in_stock = int(total_stock > 0) From 8ab75560d5067fbb759d66b1b04ffdfcd38945bb Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 21 Feb 2024 05:30:04 +0000 Subject: [PATCH 315/675] chore(release): Bumped to Version 14.63.0 # [14.63.0](https://github.com/frappe/erpnext/compare/v14.62.4...v14.63.0) (2024-02-21) ### Bug Fixes * 'NoneType' object is not iterable (backport [#39977](https://github.com/frappe/erpnext/issues/39977)) ([#39980](https://github.com/frappe/erpnext/issues/39980)) ([8e71665](https://github.com/frappe/erpnext/commit/8e71665e4f19cddedc534c7e7996d76a6d83d0cc)) * Adjust amount in last row due to rounding ([963ddac](https://github.com/frappe/erpnext/commit/963ddac528daa6ac9694757af704282f9bd22262)) * **Bank Transaction:** precision for `(un)allocated_amount` ([cea0e1f](https://github.com/frappe/erpnext/commit/cea0e1fb91f8ae8a166af6615ed7878b885dd257)), closes [#39926](https://github.com/frappe/erpnext/issues/39926) * fetch company terms ([83bf286](https://github.com/frappe/erpnext/commit/83bf28616e33a34174ade3504dbfae8571f8e58c)) * group node in warehouse filter in Item-wise Sales Register ([4921b03](https://github.com/frappe/erpnext/commit/4921b038bdcfef61191bb31c97e6583a564f3589)) * **Issue:** create communication ([8deaba8](https://github.com/frappe/erpnext/commit/8deaba8defd483bf9027bd03c58154f01f20fb24)) * linter issue ([a4fbea3](https://github.com/frappe/erpnext/commit/a4fbea3722ef61e7d79e8158629e564f6f32487a)) * linter issue ([abceb1b](https://github.com/frappe/erpnext/commit/abceb1b6110953bca1609e739a0a835542b6b946)) * linter issues ([e99485b](https://github.com/frappe/erpnext/commit/e99485bfa7d78f99094b08d5147f02cc3921cb12)) * no need call for company method in sales invoice js ([45a0494](https://github.com/frappe/erpnext/commit/45a04943189c221a73f752257565cdd77c2f19ab)) * On cancelation of capitalization, reverse depreciation entry only if journal entry exists ([5b9905f](https://github.com/frappe/erpnext/commit/5b9905f27ab80dcc1b74b22d7dfa5eb52b710c2a)) * party item code in Blanket Order ([ee9a51f](https://github.com/frappe/erpnext/commit/ee9a51f93fef19b89b1d01a8f7de98237ddbefa2)) * reconciliation issue due to notation difference ([dcf19c3](https://github.com/frappe/erpnext/commit/dcf19c3ed918f18ade8e47b7a435650d0abd4280)) * show active bom in the dropdown while making stock entry and MR (backport [#39974](https://github.com/frappe/erpnext/issues/39974)) ([#39975](https://github.com/frappe/erpnext/issues/39975)) ([385b08d](https://github.com/frappe/erpnext/commit/385b08dc50ba27f00fd1e71f8e3156a813ca7219)) * typeerror 'Item Group' filter on Purchase Register ([b1a4249](https://github.com/frappe/erpnext/commit/b1a4249041b1834965cc719ac733423a198fe615)) * update_dimension is required and not need party account method ([5eaa11b](https://github.com/frappe/erpnext/commit/5eaa11b9e871048c286c278ad7601ddf4ef171bc)) * webpages are not showing ([#39988](https://github.com/frappe/erpnext/issues/39988)) ([64099b0](https://github.com/frappe/erpnext/commit/64099b0bf75ff6574a65e49be0f39feddeeedecb)) ### Features * New financial views - Growth and margin views for P&L and balance sheet ([d913ec5](https://github.com/frappe/erpnext/commit/d913ec52db9d90a8eae519e2dd2eaa5b657bb0c7)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index a36b9c32636..b6ba58052e5 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.62.4" +__version__ = "14.63.0" def get_default_company(user=None): From c8e1409b803e42f6ecedb4c2bd56dffb147fdbf9 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Wed, 21 Feb 2024 14:36:14 +0530 Subject: [PATCH 316/675] fix: Completed Work Orders report not working (cherry picked from commit 11f4cb914a665bbfba61ec76059c8eba48a467be) --- .../completed_work_orders.json | 39 ++++++++++--------- 1 file changed, 21 insertions(+), 18 deletions(-) diff --git a/erpnext/manufacturing/report/completed_work_orders/completed_work_orders.json b/erpnext/manufacturing/report/completed_work_orders/completed_work_orders.json index be50e93f1ba..7925b8a8ab8 100644 --- a/erpnext/manufacturing/report/completed_work_orders/completed_work_orders.json +++ b/erpnext/manufacturing/report/completed_work_orders/completed_work_orders.json @@ -1,25 +1,28 @@ { - "add_total_row": 0, - "apply_user_permissions": 1, - "creation": "2013-08-12 12:44:27", - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 3, - "is_standard": "Yes", - "modified": "2018-02-13 04:58:51.549413", - "modified_by": "Administrator", - "module": "Manufacturing", - "name": "Completed Work Orders", - "owner": "Administrator", - "query": "SELECT\n `tabWork Order`.name as \"Work Order:Link/Work Order:200\",\n `tabWork Order`.creation as \"Date:Date:120\",\n `tabWork Order`.production_item as \"Item:Link/Item:150\",\n `tabWork Order`.qty as \"To Produce:Int:100\",\n `tabWork Order`.produced_qty as \"Produced:Int:100\",\n `tabWork Order`.company as \"Company:Link/Company:\"\nFROM\n `tabWork Order`\nWHERE\n `tabWork Order`.docstatus=1\n AND ifnull(`tabWork Order`.produced_qty,0) = `tabWork Order`.qty", - "ref_doctype": "Work Order", - "report_name": "Completed Work Orders", - "report_type": "Query Report", + "add_total_row": 0, + "columns": [], + "creation": "2013-08-12 12:44:27", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "idx": 3, + "is_standard": "Yes", + "letterhead": null, + "modified": "2024-02-21 14:35:14.301848", + "modified_by": "Administrator", + "module": "Manufacturing", + "name": "Completed Work Orders", + "owner": "Administrator", + "prepared_report": 0, + "query": "SELECT\n `tabWork Order`.name as \"Work Order:Link/Work Order:200\",\n `tabWork Order`.creation as \"Date:Date:120\",\n `tabWork Order`.production_item as \"Item:Link/Item:150\",\n `tabWork Order`.qty as \"To Produce:Int:100\",\n `tabWork Order`.produced_qty as \"Produced:Int:100\",\n `tabWork Order`.company as \"Company:Link/Company:\"\nFROM\n `tabWork Order`\nWHERE\n `tabWork Order`.docstatus=1\n AND ifnull(`tabWork Order`.produced_qty,0) >= `tabWork Order`.qty", + "ref_doctype": "Work Order", + "report_name": "Completed Work Orders", + "report_type": "Query Report", "roles": [ { "role": "Manufacturing User" - }, + }, { "role": "Stock User" } From 02cd8c4b9898f053016b902d59fd47d08131958b Mon Sep 17 00:00:00 2001 From: Govind S Menokee Date: Wed, 21 Feb 2024 19:46:44 +0530 Subject: [PATCH 317/675] fix: UAE tax computation fix --- erpnext/regional/united_arab_emirates/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/united_arab_emirates/utils.py b/erpnext/regional/united_arab_emirates/utils.py index efeaeed324c..634a152147d 100644 --- a/erpnext/regional/united_arab_emirates/utils.py +++ b/erpnext/regional/united_arab_emirates/utils.py @@ -25,7 +25,7 @@ def update_itemised_tax_data(doc): # dont even bother checking in item tax template as it contains both input and output accounts - double the tax rate item_code = row.item_code or row.item_name if itemised_tax.get(item_code): - for tax in itemised_tax.get(row.item_code).values(): + for tax in itemised_tax.get(item_code).values(): _tax_rate = flt(tax.get("tax_rate", 0), row.precision("tax_rate")) tax_amount += flt((row.net_amount * _tax_rate) / 100, row.precision("tax_amount")) tax_rate += _tax_rate From 079ba7670c83b8530b196ae948fb0c402c93f8a3 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 22 Feb 2024 09:50:33 +0530 Subject: [PATCH 318/675] fix: incorrect item name in MR (backport #40018) (#40023) fix: incorrect item name in MR (#40018) (cherry picked from commit 864d7ae04c3a2992f94eafb47060468f0cfb3c9c) Co-authored-by: rohitwaghchaure --- .../doctype/material_request/material_request.js | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index c5bc601e391..20bfbef8f49 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -199,6 +199,7 @@ frappe.ui.form.on('Material Request', { get_item_data: function(frm, item, overwrite_warehouse=false) { if (item && !item.item_code) { return; } + frappe.call({ method: "erpnext.stock.get_item_details.get_item_details", args: { @@ -225,20 +226,22 @@ frappe.ui.form.on('Material Request', { }, callback: function(r) { const d = item; - const qty_fields = ['actual_qty', 'projected_qty', 'min_order_qty']; + const allow_to_change_fields = ['actual_qty', 'projected_qty', 'min_order_qty', 'item_name', 'description', 'stock_uom', 'uom', 'conversion_factor', 'stock_qty']; if(!r.exc) { $.each(r.message, function(key, value) { - if(!d[key] || qty_fields.includes(key)) { + if(!d[key] || allow_to_change_fields.includes(key)) { d[key] = value; } }); if (d.price_list_rate != r.message.price_list_rate) { + d.rate = 0.0; d.price_list_rate = r.message.price_list_rate; - frappe.model.set_value(d.doctype, d.name, "rate", d.price_list_rate); } + + refresh_field("items"); } } }); @@ -435,7 +438,7 @@ frappe.ui.form.on("Material Request Item", { frm.events.get_item_data(frm, item, false); }, - rate: function(frm, doctype, name) { + rate(frm, doctype, name) { const item = locals[doctype][name]; item.amount = flt(item.qty) * flt(item.rate); frappe.model.set_value(doctype, name, "amount", item.amount); From 865cba406f7826501e33853052d15665d6b72448 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 22 Feb 2024 11:39:48 +0530 Subject: [PATCH 319/675] fix: negative stock error while making stock reconciliation (backport #40016) (#40025) * fix: negative stock error while making stock reconciliation (#40016) fix: negative stock error while making stock reco (cherry picked from commit da184d709b9f61bc9bd62cd6beddf5e19c661042) # Conflicts: # erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py * chore: fix conflicts * chore: fix linter issue --------- Co-authored-by: rohitwaghchaure --- .../doctype/stock_reconciliation/stock_reconciliation.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 9a46ae71ad2..44c2d85b4cc 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -699,8 +699,13 @@ class StockReconciliation(StockController): def has_negative_stock_allowed(self): allow_negative_stock = cint(frappe.db.get_single_value("Stock Settings", "allow_negative_stock")) + if allow_negative_stock: + return True - if all(d.batch_no and flt(d.qty) == flt(d.current_qty) for d in self.items): + if any( + (d.batch_no and flt(d.qty) == flt(d.current_qty)) + for d in self.items + ): allow_negative_stock = True return allow_negative_stock From 2a2f314821d38ede4c39ef750ed34503748d623a Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 22 Feb 2024 12:29:56 +0530 Subject: [PATCH 320/675] fix: timesheet per billed state edge case (backport #40010) (#40028) fix: timesheet per billed state edge case (#40010) If value is 100.0000x then it won't set status correctly but will set it the next time it's loaded from db. (cherry picked from commit 38e88db2c9245a3fec392941d2937cace7bf8e5f) Co-authored-by: Ankush Menat --- erpnext/projects/doctype/timesheet/timesheet.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py index 9e52befd22e..4db647eb3e2 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -77,7 +77,7 @@ class Timesheet(Document): def set_status(self): self.status = {"0": "Draft", "1": "Submitted", "2": "Cancelled"}[str(self.docstatus or 0)] - if self.per_billed == 100: + if flt(self.per_billed, self.precision("per_billed")) >= 100.0: self.status = "Billed" if self.sales_invoice: From 7355fce75e9c5084e4583c6510e66702cce8e94e Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 22 Feb 2024 11:39:48 +0530 Subject: [PATCH 321/675] fix: negative stock error while making stock reconciliation (backport #40016) (#40025) * fix: negative stock error while making stock reconciliation (#40016) fix: negative stock error while making stock reco (cherry picked from commit da184d709b9f61bc9bd62cd6beddf5e19c661042) # Conflicts: # erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py * chore: fix conflicts * chore: fix linter issue --------- Co-authored-by: rohitwaghchaure (cherry picked from commit 865cba406f7826501e33853052d15665d6b72448) --- .../doctype/stock_reconciliation/stock_reconciliation.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 9a46ae71ad2..44c2d85b4cc 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -699,8 +699,13 @@ class StockReconciliation(StockController): def has_negative_stock_allowed(self): allow_negative_stock = cint(frappe.db.get_single_value("Stock Settings", "allow_negative_stock")) + if allow_negative_stock: + return True - if all(d.batch_no and flt(d.qty) == flt(d.current_qty) for d in self.items): + if any( + (d.batch_no and flt(d.qty) == flt(d.current_qty)) + for d in self.items + ): allow_negative_stock = True return allow_negative_stock From 315d4dec90eb907cd2598724b5d7a4109bab5975 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 22 Feb 2024 08:18:35 +0000 Subject: [PATCH 322/675] chore(release): Bumped to Version 14.63.1 ## [14.63.1](https://github.com/frappe/erpnext/compare/v14.63.0...v14.63.1) (2024-02-22) ### Bug Fixes * negative stock error while making stock reconciliation (backport [#40016](https://github.com/frappe/erpnext/issues/40016)) ([#40025](https://github.com/frappe/erpnext/issues/40025)) ([7355fce](https://github.com/frappe/erpnext/commit/7355fce75e9c5084e4583c6510e66702cce8e94e)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index b6ba58052e5..aa35e62c842 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.63.0" +__version__ = "14.63.1" def get_default_company(user=None): From 8b3c809cb68ed9badb4d9fa4defc083b38bd4184 Mon Sep 17 00:00:00 2001 From: SherinKR Date: Thu, 22 Feb 2024 15:56:58 +0530 Subject: [PATCH 323/675] fix: account validation error on bank account after editing existing bank account --- erpnext/accounts/doctype/bank_account/bank_account.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/bank_account/bank_account.py b/erpnext/accounts/doctype/bank_account/bank_account.py index ece27e77ee9..ec3fa831200 100644 --- a/erpnext/accounts/doctype/bank_account/bank_account.py +++ b/erpnext/accounts/doctype/bank_account/bank_account.py @@ -30,7 +30,7 @@ class BankAccount(Document): def validate_account(self): if self.account: - if accounts := frappe.db.get_all("Bank Account", filters={"account": self.account}, as_list=1): + if accounts := frappe.db.get_all("Bank Account", filters={"account": self.account, 'name':['!=', self.name]}, as_list=1): frappe.throw( _("'{0}' account is already used by {1}. Use another account.").format( frappe.bold(self.account), From b364fe704723cfc0e508c8ff9f39645a8625b703 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 22 Feb 2024 16:20:15 +0530 Subject: [PATCH 324/675] fix: communication_date in party dashboards (backport #40005) (#40020) fix: accommodate for changed orderby statement (cherry picked from commit 87df7ff71772474bb858dac6ce9132280ce2ab82) Co-authored-by: Gursheen Anand --- erpnext/accounts/party.py | 41 +++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index c83969b34bc..8f8b9a81f8a 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -9,7 +9,7 @@ from frappe import _, msgprint, qb, scrub from frappe.contacts.doctype.address.address import get_company_address, get_default_address from frappe.core.doctype.user_permission.user_permission import get_permitted_documents from frappe.model.utils import get_fetch_values -from frappe.query_builder.functions import Abs, Date, Sum +from frappe.query_builder.functions import Abs, Count, Date, Sum from frappe.utils import ( add_days, add_months, @@ -761,34 +761,37 @@ def get_timeline_data(doctype, name): from frappe.desk.form.load import get_communication_data out = {} - fields = "creation, count(*)" after = add_years(None, -1).strftime("%Y-%m-%d") - group_by = "group by Date(creation)" data = get_communication_data( doctype, name, after=after, - group_by="group by creation", - fields="C.creation as creation, count(C.name)", + group_by="group by communication_date", + fields="C.communication_date as communication_date, count(C.name)", as_dict=False, ) # fetch and append data from Activity Log - data += frappe.db.sql( - """select {fields} - from `tabActivity Log` - where (reference_doctype=%(doctype)s and reference_name=%(name)s) - or (timeline_doctype in (%(doctype)s) and timeline_name=%(name)s) - or (reference_doctype in ("Quotation", "Opportunity") and timeline_name=%(name)s) - and status!='Success' and creation > {after} - {group_by} order by creation desc - """.format( - fields=fields, group_by=group_by, after=after - ), - {"doctype": doctype, "name": name}, - as_dict=False, - ) + activity_log = frappe.qb.DocType("Activity Log") + data += ( + frappe.qb.from_(activity_log) + .select(activity_log.communication_date, Count(activity_log.name)) + .where( + ( + ((activity_log.reference_doctype == doctype) & (activity_log.reference_name == name)) + | ((activity_log.timeline_doctype == doctype) & (activity_log.timeline_name == name)) + | ( + (activity_log.reference_doctype.isin(["Quotation", "Opportunity"])) + & (activity_log.timeline_name == name) + ) + ) + & (activity_log.status != "Success") + & (activity_log.creation > after) + ) + .groupby(activity_log.communication_date) + .orderby(activity_log.communication_date, order=frappe.qb.desc) + ).run() timeline_items = dict(data) From d6fad08d20c6d29e799a9dc43c75db4c0c6926fa Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 22 Feb 2024 16:24:30 +0530 Subject: [PATCH 325/675] fix: Webpages not working without login --- erpnext/setup/doctype/item_group/item_group.json | 4 +++- erpnext/setup/doctype/item_group/templates/item_group.html | 7 +++++++ .../setup/doctype/item_group/templates/item_group_row.html | 4 ++++ 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 erpnext/setup/doctype/item_group/templates/item_group.html create mode 100644 erpnext/setup/doctype/item_group/templates/item_group_row.html diff --git a/erpnext/setup/doctype/item_group/item_group.json b/erpnext/setup/doctype/item_group/item_group.json index 2986087277c..71dfdb4f91a 100644 --- a/erpnext/setup/doctype/item_group/item_group.json +++ b/erpnext/setup/doctype/item_group/item_group.json @@ -1,5 +1,6 @@ { "actions": [], + "allow_guest_to_view": 1, "allow_import": 1, "allow_rename": 1, "autoname": "field:item_group_name", @@ -227,13 +228,14 @@ "label": "Include Descendants" } ], + "has_web_view": 1, "icon": "fa fa-sitemap", "idx": 1, "image_field": "image", "is_tree": 1, "links": [], "max_attachments": 3, - "modified": "2023-01-05 12:21:30.458628", + "modified": "2024-02-22 16:23:46.936496", "modified_by": "Administrator", "module": "Setup", "name": "Item Group", diff --git a/erpnext/setup/doctype/item_group/templates/item_group.html b/erpnext/setup/doctype/item_group/templates/item_group.html new file mode 100644 index 00000000000..db123090aae --- /dev/null +++ b/erpnext/setup/doctype/item_group/templates/item_group.html @@ -0,0 +1,7 @@ +{% extends "templates/web.html" %} + +{% block page_content %} +

    {{ title }}

    +{% endblock %} + + \ No newline at end of file diff --git a/erpnext/setup/doctype/item_group/templates/item_group_row.html b/erpnext/setup/doctype/item_group/templates/item_group_row.html new file mode 100644 index 00000000000..d7014b453ab --- /dev/null +++ b/erpnext/setup/doctype/item_group/templates/item_group_row.html @@ -0,0 +1,4 @@ + + From a40e41051986a8c1d8e38b9f2436deb15a3e54ef Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 22 Feb 2024 16:24:30 +0530 Subject: [PATCH 326/675] fix: Webpages not working without login (cherry picked from commit d6fad08d20c6d29e799a9dc43c75db4c0c6926fa) --- erpnext/setup/doctype/item_group/item_group.json | 4 +++- erpnext/setup/doctype/item_group/templates/item_group.html | 7 +++++++ .../setup/doctype/item_group/templates/item_group_row.html | 4 ++++ 3 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 erpnext/setup/doctype/item_group/templates/item_group.html create mode 100644 erpnext/setup/doctype/item_group/templates/item_group_row.html diff --git a/erpnext/setup/doctype/item_group/item_group.json b/erpnext/setup/doctype/item_group/item_group.json index 2986087277c..71dfdb4f91a 100644 --- a/erpnext/setup/doctype/item_group/item_group.json +++ b/erpnext/setup/doctype/item_group/item_group.json @@ -1,5 +1,6 @@ { "actions": [], + "allow_guest_to_view": 1, "allow_import": 1, "allow_rename": 1, "autoname": "field:item_group_name", @@ -227,13 +228,14 @@ "label": "Include Descendants" } ], + "has_web_view": 1, "icon": "fa fa-sitemap", "idx": 1, "image_field": "image", "is_tree": 1, "links": [], "max_attachments": 3, - "modified": "2023-01-05 12:21:30.458628", + "modified": "2024-02-22 16:23:46.936496", "modified_by": "Administrator", "module": "Setup", "name": "Item Group", diff --git a/erpnext/setup/doctype/item_group/templates/item_group.html b/erpnext/setup/doctype/item_group/templates/item_group.html new file mode 100644 index 00000000000..db123090aae --- /dev/null +++ b/erpnext/setup/doctype/item_group/templates/item_group.html @@ -0,0 +1,7 @@ +{% extends "templates/web.html" %} + +{% block page_content %} +

    {{ title }}

    +{% endblock %} + + \ No newline at end of file diff --git a/erpnext/setup/doctype/item_group/templates/item_group_row.html b/erpnext/setup/doctype/item_group/templates/item_group_row.html new file mode 100644 index 00000000000..d7014b453ab --- /dev/null +++ b/erpnext/setup/doctype/item_group/templates/item_group_row.html @@ -0,0 +1,4 @@ + + From 0653f6854bad89490a7665a9def0ae592e60abe1 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 22 Feb 2024 11:58:43 +0000 Subject: [PATCH 327/675] chore(release): Bumped to Version 14.63.2 ## [14.63.2](https://github.com/frappe/erpnext/compare/v14.63.1...v14.63.2) (2024-02-22) ### Bug Fixes * Webpages not working without login ([a40e410](https://github.com/frappe/erpnext/commit/a40e41051986a8c1d8e38b9f2436deb15a3e54ef)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index aa35e62c842..3e2d97eacc8 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.63.1" +__version__ = "14.63.2" def get_default_company(user=None): From 23e32b9b33e77ea3916e292c092cdfbedb9b309f Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 22 Feb 2024 18:29:56 +0530 Subject: [PATCH 328/675] fix: communication_date in party dashboards (backport #40005) (backport #40020) (#40044) fix: communication_date in party dashboards (backport #40005) (#40020) fix: accommodate for changed orderby statement (cherry picked from commit 87df7ff71772474bb858dac6ce9132280ce2ab82) Co-authored-by: Gursheen Anand (cherry picked from commit b364fe704723cfc0e508c8ff9f39645a8625b703) Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> --- erpnext/accounts/party.py | 41 +++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index c83969b34bc..8f8b9a81f8a 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -9,7 +9,7 @@ from frappe import _, msgprint, qb, scrub from frappe.contacts.doctype.address.address import get_company_address, get_default_address from frappe.core.doctype.user_permission.user_permission import get_permitted_documents from frappe.model.utils import get_fetch_values -from frappe.query_builder.functions import Abs, Date, Sum +from frappe.query_builder.functions import Abs, Count, Date, Sum from frappe.utils import ( add_days, add_months, @@ -761,34 +761,37 @@ def get_timeline_data(doctype, name): from frappe.desk.form.load import get_communication_data out = {} - fields = "creation, count(*)" after = add_years(None, -1).strftime("%Y-%m-%d") - group_by = "group by Date(creation)" data = get_communication_data( doctype, name, after=after, - group_by="group by creation", - fields="C.creation as creation, count(C.name)", + group_by="group by communication_date", + fields="C.communication_date as communication_date, count(C.name)", as_dict=False, ) # fetch and append data from Activity Log - data += frappe.db.sql( - """select {fields} - from `tabActivity Log` - where (reference_doctype=%(doctype)s and reference_name=%(name)s) - or (timeline_doctype in (%(doctype)s) and timeline_name=%(name)s) - or (reference_doctype in ("Quotation", "Opportunity") and timeline_name=%(name)s) - and status!='Success' and creation > {after} - {group_by} order by creation desc - """.format( - fields=fields, group_by=group_by, after=after - ), - {"doctype": doctype, "name": name}, - as_dict=False, - ) + activity_log = frappe.qb.DocType("Activity Log") + data += ( + frappe.qb.from_(activity_log) + .select(activity_log.communication_date, Count(activity_log.name)) + .where( + ( + ((activity_log.reference_doctype == doctype) & (activity_log.reference_name == name)) + | ((activity_log.timeline_doctype == doctype) & (activity_log.timeline_name == name)) + | ( + (activity_log.reference_doctype.isin(["Quotation", "Opportunity"])) + & (activity_log.timeline_name == name) + ) + ) + & (activity_log.status != "Success") + & (activity_log.creation > after) + ) + .groupby(activity_log.communication_date) + .orderby(activity_log.communication_date, order=frappe.qb.desc) + ).run() timeline_items = dict(data) From 95197c05329aa7df6d2314f49de1b3d4a6ccef68 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Thu, 22 Feb 2024 13:01:09 +0000 Subject: [PATCH 329/675] chore(release): Bumped to Version 14.63.3 ## [14.63.3](https://github.com/frappe/erpnext/compare/v14.63.2...v14.63.3) (2024-02-22) ### Bug Fixes * communication_date in party dashboards (backport [#40005](https://github.com/frappe/erpnext/issues/40005)) (backport [#40020](https://github.com/frappe/erpnext/issues/40020)) ([#40044](https://github.com/frappe/erpnext/issues/40044)) ([23e32b9](https://github.com/frappe/erpnext/commit/23e32b9b33e77ea3916e292c092cdfbedb9b309f)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 3e2d97eacc8..96f8ec27ec1 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.63.2" +__version__ = "14.63.3" def get_default_company(user=None): From 3c170b980575f76a3caf3624f0cbee8845a0044f Mon Sep 17 00:00:00 2001 From: "Nihantra C. Patel" <141945075+Nihantra-Patel@users.noreply.github.com> Date: Fri, 9 Feb 2024 12:14:55 +0530 Subject: [PATCH 330/675] fix: translatable columns in Sales Pipeline Analytics report (cherry picked from commit c5050c935bb75c5fdf10be4dfea16557da90b487) --- .../sales_pipeline_analytics/sales_pipeline_analytics.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.py b/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.py index dea3f2dd36d..4f7436ff9e4 100644 --- a/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.py +++ b/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.py @@ -41,7 +41,9 @@ class SalesPipelineAnalytics(object): month_list = self.get_month_list() for month in month_list: - self.columns.append({"fieldname": month, "fieldtype": based_on, "label": month, "width": 200}) + self.columns.append( + {"fieldname": month, "fieldtype": based_on, "label": _(month), "width": 200} + ) elif self.filters.get("range") == "Quarterly": for quarter in range(1, 5): @@ -156,7 +158,7 @@ class SalesPipelineAnalytics(object): for column in self.columns: if column["fieldname"] != "opportunity_owner" and column["fieldname"] != "sales_stage": - labels.append(column["fieldname"]) + labels.append(_(column["fieldname"])) self.chart = {"data": {"labels": labels, "datasets": datasets}, "type": "line"} From 2fc490a5d77a3c25d1e17124bdd626442257da4d Mon Sep 17 00:00:00 2001 From: "Nihantra C. Patel" <141945075+Nihantra-Patel@users.noreply.github.com> Date: Fri, 9 Feb 2024 16:13:44 +0530 Subject: [PATCH 331/675] fix: remove cancelled payment entry from Payment Period Based On Invoice Date (cherry picked from commit a2a8a8f2e0c7ca3f62c31ac8834e5a6053aeb87d) --- .../payment_period_based_on_invoice_date.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py index 3f178f4715c..0f6ff74e26e 100644 --- a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py +++ b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py @@ -163,7 +163,7 @@ def get_entries(filters): """select voucher_type, voucher_no, party_type, party, posting_date, debit, credit, remarks, against_voucher from `tabGL Entry` - where company=%(company)s and voucher_type in ('Journal Entry', 'Payment Entry') {0} + where company=%(company)s and voucher_type in ('Journal Entry', 'Payment Entry') {0} and is_cancelled = 0 """.format( get_conditions(filters) ), From a1c0c2359f5a3b78006c77f2b8e3eac9c488fa21 Mon Sep 17 00:00:00 2001 From: "Nihantra C. Patel" <141945075+Nihantra-Patel@users.noreply.github.com> Date: Fri, 9 Feb 2024 16:27:52 +0530 Subject: [PATCH 332/675] fix: remove cancelled payment entry from PPBOID report (cherry picked from commit 186cc3d7488b126ce5d2d6d2f9095f138fd44de3) --- .../payment_period_based_on_invoice_date.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py index 0f6ff74e26e..eaeaa62d9a2 100644 --- a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py +++ b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py @@ -163,7 +163,7 @@ def get_entries(filters): """select voucher_type, voucher_no, party_type, party, posting_date, debit, credit, remarks, against_voucher from `tabGL Entry` - where company=%(company)s and voucher_type in ('Journal Entry', 'Payment Entry') {0} and is_cancelled = 0 + where company=%(company)s and voucher_type in ('Journal Entry', 'Payment Entry') and is_cancelled = 0 {0} """.format( get_conditions(filters) ), From e5098c521ba5eb0371a902b671b0f3e0f5213d86 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 20 Feb 2024 21:49:28 +0530 Subject: [PATCH 333/675] fix: Cr/Dr notes with POS Payments (cherry picked from commit 68a23730f3fde5d8f306d77c3db5e373b1c6e937) --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 19f52ad1e77..288acd9f792 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1293,9 +1293,7 @@ class SalesInvoice(SellingController): "credit_in_account_currency": payment_mode.base_amount if self.party_account_currency == self.company_currency else payment_mode.amount, - "against_voucher": self.return_against - if cint(self.is_return) and self.return_against - else self.name, + "against_voucher": self.name, "against_voucher_type": self.doctype, "cost_center": self.cost_center, }, From a9791c85c7fa047f1c4ddbe45d942bd383904a1c Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 22 Feb 2024 20:46:07 +0530 Subject: [PATCH 334/675] test: ledger entries of Cr Note of POS Invoice (cherry picked from commit 4288713abe2e614e2454589fd5e344ba87f38fe1) --- .../sales_invoice/test_sales_invoice.py | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index abf0fc5c0f3..ce5456da5d3 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -1081,6 +1081,44 @@ class TestSalesInvoice(FrappeTestCase): self.assertEqual(pos.grand_total, 100.0) self.assertEqual(pos.write_off_amount, 10) + def test_ledger_entries_of_return_pos_invoice(self): + make_pos_profile() + + pos = create_sales_invoice(do_not_save=True) + pos.is_pos = 1 + pos.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 100}) + pos.save().submit() + self.assertEqual(pos.outstanding_amount, 0.0) + self.assertEqual(pos.status, "Paid") + + from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_sales_return + + pos_return = make_sales_return(pos.name) + pos_return.save().submit() + pos_return.reload() + pos.reload() + self.assertEqual(pos_return.is_return, 1) + self.assertEqual(pos_return.return_against, pos.name) + self.assertEqual(pos_return.outstanding_amount, 0.0) + self.assertEqual(pos_return.status, "Return") + self.assertEqual(pos.outstanding_amount, 0.0) + self.assertEqual(pos.status, "Credit Note Issued") + + expected = ( + ("Cash - _TC", 0.0, 100.0, pos_return.name, None), + ("Debtors - _TC", 0.0, 100.0, pos_return.name, pos_return.name), + ("Debtors - _TC", 100.0, 0.0, pos_return.name, pos_return.name), + ("Sales - _TC", 100.0, 0.0, pos_return.name, None), + ) + res = frappe.db.get_all( + "GL Entry", + filters={"voucher_no": pos_return.name, "is_cancelled": 0}, + fields=["account", "debit", "credit", "voucher_no", "against_voucher"], + order_by="account, debit, credit", + as_list=1, + ) + self.assertEqual(expected, res) + def test_pos_with_no_gl_entry_for_change_amount(self): frappe.db.set_value("Accounts Settings", None, "post_change_gl_entries", 0) From 9e9486d5abba143b69e745b3aa929b623d8727e5 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 23 Feb 2024 05:45:04 +0530 Subject: [PATCH 335/675] refactor: skip popup for POS invoices (cherry picked from commit 3634c4c284061ecd2fe5576467238532f748b07a) --- erpnext/controllers/accounts_controller.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index f642cde73c3..f096917aa8e 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -201,7 +201,8 @@ class AccountsController(TransactionBase): ) ) - if self.get("is_return") and self.get("return_against"): + if self.get("is_return") and self.get("return_against") and not self.get("is_pos"): + # if self.get("is_return") and self.get("return_against"): document_type = "Credit Note" if self.doctype == "Sales Invoice" else "Debit Note" frappe.msgprint( _( From 08459cef4ab4399ec6cce8590c0e4aae7a3d72c7 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Thu, 22 Feb 2024 15:12:02 +0530 Subject: [PATCH 336/675] fix: delete PLE containing invoice in against (cherry picked from commit c1e1fd882950352c61e5881076c34fe3436142c6) --- erpnext/controllers/accounts_controller.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index f096917aa8e..efca0e2c0c8 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -325,6 +325,7 @@ class AccountsController(TransactionBase): ple = frappe.qb.DocType("Payment Ledger Entry") frappe.qb.from_(ple).delete().where( (ple.voucher_type == self.doctype) & (ple.voucher_no == self.name) + | ((ple.against_voucher_type == self.doctype) & (ple.against_voucher_no == self.name)) ).run() frappe.db.sql( "delete from `tabGL Entry` where voucher_type=%s and voucher_no=%s", (self.doctype, self.name) From 3d243a7fa5918572eff91c6279c459c8b4c725d6 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Thu, 22 Feb 2024 16:00:52 +0530 Subject: [PATCH 337/675] fix: only check for delinked PLEs (cherry picked from commit 146c5b3e16b0521f0590aa6dfa957979fc6f3d9a) --- 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 efca0e2c0c8..560e7715e68 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -325,7 +325,12 @@ class AccountsController(TransactionBase): ple = frappe.qb.DocType("Payment Ledger Entry") frappe.qb.from_(ple).delete().where( (ple.voucher_type == self.doctype) & (ple.voucher_no == self.name) - | ((ple.against_voucher_type == self.doctype) & (ple.against_voucher_no == self.name)) + | ( + (ple.against_voucher_type == self.doctype) + & (ple.against_voucher_no == self.name) + & ple.delinked + == 1 + ) ).run() frappe.db.sql( "delete from `tabGL Entry` where voucher_type=%s and voucher_no=%s", (self.doctype, self.name) From 96703fb34d0c5017126ccb54591c1c57543fa3d0 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Fri, 23 Feb 2024 12:42:10 +0530 Subject: [PATCH 338/675] build: specify frappe dependency (#40060) --- pyproject.toml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index 7d1e7af50e4..7f28903f120 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -43,3 +43,6 @@ force_grid_wrap = 0 use_parentheses = true ensure_newline_before_comments = true indent = "\t" + +[tool.bench.frappe-dependencies] +frappe = ">=14.0.0,<15.0.0" From 8c7d0d4b85daa7749e5dee1dbe1fc0fcc6f4c158 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Thu, 22 Feb 2024 16:08:11 +0530 Subject: [PATCH 339/675] fix: skip max discount validation for rate adjustment (cherry picked from commit 5a3b133d6520661fe28e4c2d73c17ef4eb48be1e) --- erpnext/controllers/selling_controller.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index c9b12f4f723..34abe9f764f 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -31,7 +31,8 @@ class SellingController(StockController): def validate(self): super(SellingController, self).validate() self.validate_items() - self.validate_max_discount() + if not self.get("is_debit_note"): + self.validate_max_discount() self.validate_selling_price() self.set_qty_as_per_stock_uom() self.set_po_nos(for_validate=True) From 882cf98288103ad6ec731a4cfc58357471337287 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Thu, 22 Feb 2024 16:24:42 +0530 Subject: [PATCH 340/675] fix: skip SO & DN validation for debit note (cherry picked from commit e2d16955dd6de197ef621e5f8fb3159f5a9bfaaa) --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 288acd9f792..730c47569fa 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -90,7 +90,7 @@ class SalesInvoice(SellingController): super(SalesInvoice, self).validate() self.validate_auto_set_posting_time() - if not self.is_pos: + if not (self.is_pos or self.is_debit_note): self.so_dn_required() self.set_tax_withholding() From 3112fca087153d95699ff9ecaa1fc1215d65fa91 Mon Sep 17 00:00:00 2001 From: "Nihantra C. Patel" <141945075+Nihantra-Patel@users.noreply.github.com> Date: Fri, 9 Feb 2024 15:18:45 +0530 Subject: [PATCH 341/675] fix: check_credit_limit on_update_after_submit of Sales Order (cherry picked from commit 17452b76933e7edb087741671f876782d0a70859) --- erpnext/selling/doctype/sales_order/sales_order.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index bec24fe5e99..82680368181 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -362,6 +362,9 @@ class SalesOrder(SellingController): def on_update(self): pass + def on_update_after_submit(self): + self.check_credit_limit() + def before_update_after_submit(self): self.validate_po() self.validate_drop_ship() From db7c360fa1d7283cb5c145795e35e50803f6b7d3 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 23 Feb 2024 14:03:09 +0530 Subject: [PATCH 342/675] test: credit limit on update after submit (cherry picked from commit 467c0898e9a2e4fb84049a3a3a20d3aa63c7f5a0) --- .../selling/doctype/customer/test_customer.py | 34 ++++++++++++++++--- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/erpnext/selling/doctype/customer/test_customer.py b/erpnext/selling/doctype/customer/test_customer.py index 7a601a78876..7e91e6e6599 100644 --- a/erpnext/selling/doctype/customer/test_customer.py +++ b/erpnext/selling/doctype/customer/test_customer.py @@ -297,11 +297,35 @@ class TestCustomer(FrappeTestCase): if credit_limit > outstanding_amt: set_credit_limit("_Test Customer", "_Test Company", credit_limit) - # Makes Sales invoice from Sales Order - so.save(ignore_permissions=True) - si = make_sales_invoice(so.name) - si.save(ignore_permissions=True) - self.assertRaises(frappe.ValidationError, make_sales_order) + def test_customer_credit_limit_after_submit(self): + from erpnext.controllers.accounts_controller import update_child_qty_rate + from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order + + outstanding_amt = self.get_customer_outstanding_amount() + credit_limit = get_credit_limit("_Test Customer", "_Test Company") + + if outstanding_amt <= 0.0: + item_qty = int((abs(outstanding_amt) + 200) / 100) + make_sales_order(qty=item_qty) + + if credit_limit <= 0.0: + set_credit_limit("_Test Customer", "_Test Company", outstanding_amt + 100) + + so = make_sales_order(rate=100, qty=1) + # Update qty in submitted Sales Order to trigger Credit Limit validation + fields = ["name", "item_code", "delivery_date", "conversion_factor", "qty", "rate", "uom", "idx"] + modified_item = frappe._dict() + for x in fields: + modified_item[x] = so.items[0].get(x) + modified_item["docname"] = so.items[0].name + modified_item["qty"] = 2 + self.assertRaises( + frappe.ValidationError, + update_child_qty_rate, + so.doctype, + frappe.json.dumps([modified_item]), + so.name, + ) def test_customer_credit_limit_on_change(self): outstanding_amt = self.get_customer_outstanding_amount() From 579b27d0102c86c42c32b61c2adbd16280d45532 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sat, 24 Feb 2024 17:22:37 +0530 Subject: [PATCH 343/675] fix: Cannot read properties of undefined (backport #40081) (#40082) * fix: Cannot read properties of undefined (cherry picked from commit 44ed52c5cfa6a2432769bbb6f3c352aa81025de2) * chore: fix linter issue --------- Co-authored-by: Rohit Waghchaure --- erpnext/public/js/utils.js | 5 ++++- .../doctype/stock_reconciliation/stock_reconciliation.py | 5 +---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 4c76e2a869e..27c7444daf4 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -40,7 +40,10 @@ $.extend(erpnext, { is_perpetual_inventory_enabled: function(company) { if(company) { - return frappe.get_doc(":Company", company).enable_perpetual_inventory + let company_local = locals[":Company"] && locals[":Company"][company]; + if(company_local) { + return cint(company_local.enable_perpetual_inventory); + } } }, diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 44c2d85b4cc..878af0e22ca 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -702,10 +702,7 @@ class StockReconciliation(StockController): if allow_negative_stock: return True - if any( - (d.batch_no and flt(d.qty) == flt(d.current_qty)) - for d in self.items - ): + if any((d.batch_no and flt(d.qty) == flt(d.current_qty)) for d in self.items): allow_negative_stock = True return allow_negative_stock From e4611853171bc1fdcd68a3de5f373e82cae4d108 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sat, 24 Feb 2024 21:02:45 +0530 Subject: [PATCH 344/675] fix: do not make MR against raw materials of available sub assemblies (backport #40085) (#40086) fix: do not make MR against raw materials of available sub assemblies (#40085) (cherry picked from commit 4c9048fb3960668edd63a6e716b26d2e444bc1f9) Co-authored-by: rohitwaghchaure --- .../production_plan/production_plan.py | 24 +++++++-------- .../production_plan/test_production_plan.py | 29 +++++++++++++++++++ 2 files changed, 40 insertions(+), 13 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index aea6c987177..5495c49803d 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -1430,19 +1430,17 @@ def get_items_for_material_requests(doc, warehouses=None, get_parent_warehouse_d frappe.throw(_("For row {0}: Enter Planned Qty").format(data.get("idx"))) if bom_no: - if ( - data.get("include_exploded_items") - and doc.get("sub_assembly_items") - and doc.get("skip_available_sub_assembly_item") - ): - item_details = get_raw_materials_of_sub_assembly_items( - item_details, - company, - bom_no, - include_non_stock_items, - sub_assembly_items, - planned_qty=planned_qty, - ) + if data.get("include_exploded_items") and doc.get("skip_available_sub_assembly_item"): + item_details = {} + if doc.get("sub_assembly_items"): + item_details = get_raw_materials_of_sub_assembly_items( + item_details, + company, + bom_no, + include_non_stock_items, + sub_assembly_items, + planned_qty=planned_qty, + ) elif data.get("include_exploded_items") and include_subcontracted_items: # fetch exploded items from BOM diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py index 2b9751926a2..3d878c140f8 100644 --- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py @@ -1221,6 +1221,35 @@ class TestProductionPlan(FrappeTestCase): if row.item_code == "SubAssembly2 For SUB Test": self.assertEqual(row.quantity, 10) + def test_sub_assembly_and_their_raw_materials_exists(self): + from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom + + bom_tree = { + "FG1 For SUB Test": { + "SAB1 For SUB Test": {"CP1 For SUB Test": {}}, + "SAB2 For SUB Test": {}, + } + } + + parent_bom = create_nested_bom(bom_tree, prefix="") + for item in ["SAB1 For SUB Test", "SAB2 For SUB Test"]: + make_stock_entry(item_code=item, qty=10, rate=100, target="_Test Warehouse - _TC") + + plan = create_production_plan( + item_code=parent_bom.item, + planned_qty=10, + ignore_existing_ordered_qty=1, + do_not_submit=1, + skip_available_sub_assembly_item=1, + warehouse="_Test Warehouse - _TC", + ) + + items = get_items_for_material_requests( + plan.as_dict(), warehouses=[{"warehouse": "_Test Warehouse - _TC"}] + ) + + self.assertFalse(items) + def test_transfer_and_purchase_mrp_for_purchase_uom(self): from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse From 2450a73c6f41c7ec880acf8f5825f7306a2940ba Mon Sep 17 00:00:00 2001 From: Samuel Danieli <23150094+scdanieli@users.noreply.github.com> Date: Thu, 25 Aug 2022 07:53:38 +0200 Subject: [PATCH 345/675] chore: german translations (#31463) (cherry picked from commit 915102a40040e38239024acb2647f54fe350809b) # Conflicts: # erpnext/translations/de.csv --- erpnext/translations/de.csv | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index 3a8b7fe1a4a..e0589c833d9 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -189,6 +189,10 @@ All other ITC,Alle anderen ITC, All the mandatory Task for employee creation hasn't been done yet.,Alle obligatorischen Aufgaben zur Mitarbeitererstellung wurden noch nicht erledigt., Allocate Payment Amount,Zahlungsbetrag zuweisen, Allocated Amount,Zugewiesene Menge, +<<<<<<< HEAD +======= +Allocated Leaves,Zugewiesene Urlaubstage, +>>>>>>> 915102a400 (chore: german translations (#31463)) Allocating leaves...,Blätter zuordnen..., Already record exists for the item {0},Es existiert bereits ein Datensatz für den Artikel {0}, "Already set default in pos profile {0} for user {1}, kindly disabled default","Im Standardprofil {0} für den Benutzer {1} ist der Standard bereits festgelegt, standardmäßig deaktiviert", @@ -7873,8 +7877,8 @@ Material Request Warehouse,Materialanforderungslager, Select warehouse for material requests,Wählen Sie Lager für Materialanfragen, Transfer Materials For Warehouse {0},Material für Lager übertragen {0}, Production Plan Material Request Warehouse,Produktionsplan Materialanforderungslager, -Sets 'Source Warehouse' in each row of the items table.,Legt 'Source Warehouse' in jeder Zeile der Artikeltabelle fest., -Sets 'Target Warehouse' in each row of the items table.,"Füllt das Feld ""Ziel Lager"" in allen Positionen der folgenden Tabelle.", +Sets 'Source Warehouse' in each row of the items table.,Legt in jeder Zeile der Artikeltabelle das „Ausgangslager“ fest., +Sets 'Target Warehouse' in each row of the items table.,Legt in jeder Zeile der Artikeltabelle das „Eingangslager“ fest., Show Cancelled Entries,Abgebrochene Einträge anzeigen, Backdated Stock Entry,Backdated Stock Entry, Row #{}: Currency of {} - {} doesn't matches company currency.,Zeile # {}: Die Währung von {} - {} stimmt nicht mit der Firmenwährung überein., @@ -9060,6 +9064,14 @@ Leave Type Allocation,Zuordnung Abwesenheitsarten, From Lead,Aus Lead, From Opportunity,Aus Chance, Publish in Website,Auf Webseite veröffentlichen, +<<<<<<< HEAD +======= +Total Allocated Leave(s),Gesamte zugewiesene Urlaubstage, +Expired Leave(s),Verfallene Urlaubstage, +Used Leave(s),Verbrauchte Urlaubstage, +Leave(s) Pending Approval,Urlaubstage zur Genehmigung ausstehend, +Available Leave(s),Verfügbare Urlaubstage, +>>>>>>> 915102a400 (chore: german translations (#31463)) Party Specific Item,Parteispezifischer Artikel, Active Customers,Aktive Kunden, Annual Sales,Jährlicher Umsatz, @@ -9074,6 +9086,10 @@ Accounts Payable Ageing,Fälligkeit Verbindlichkeiten, Budget Variance,Budgetabweichung, Based On Value,Basierend auf Wert, Restrict Items Based On,Artikel einschränken auf Basis von, +<<<<<<< HEAD +======= +Earnings & Deductions,Erträge & Abzüge, +>>>>>>> 915102a400 (chore: german translations (#31463)) Is Process Loss,Ist Prozessverlust, Is Finished Item,Ist fertiger Artikel, Is Scrap Item,Ist Schrott, @@ -9082,6 +9098,7 @@ Show Remarks,Bemerkungen anzeigen, Website Item,Webseiten-Artikel, Update Property,Eigenschaft aktualisieren, Recurring Sales Invoice,Wiederkehrende Ausgangsrechnung, +<<<<<<< HEAD Total Asset,Aktiva, Total Liability,Verbindlichkeiten, Total Equity,Eigenkapital, @@ -9108,3 +9125,5 @@ Lost Quotations,Verlorene Angebote, Lost Quotations %,Verlorene Angebote %, Lost Value,Verlorener Wert, Lost Value %,Verlorener Wert %, +======= +>>>>>>> 915102a400 (chore: german translations (#31463)) From 2899d2bac1e0dd63d13b12c18564fc3382b19329 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Sat, 24 Feb 2024 18:28:51 +0100 Subject: [PATCH 346/675] chore: resolve conflicts --- erpnext/translations/de.csv | 19 ------------------- 1 file changed, 19 deletions(-) diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index e0589c833d9..13aaa67c489 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -189,10 +189,6 @@ All other ITC,Alle anderen ITC, All the mandatory Task for employee creation hasn't been done yet.,Alle obligatorischen Aufgaben zur Mitarbeitererstellung wurden noch nicht erledigt., Allocate Payment Amount,Zahlungsbetrag zuweisen, Allocated Amount,Zugewiesene Menge, -<<<<<<< HEAD -======= -Allocated Leaves,Zugewiesene Urlaubstage, ->>>>>>> 915102a400 (chore: german translations (#31463)) Allocating leaves...,Blätter zuordnen..., Already record exists for the item {0},Es existiert bereits ein Datensatz für den Artikel {0}, "Already set default in pos profile {0} for user {1}, kindly disabled default","Im Standardprofil {0} für den Benutzer {1} ist der Standard bereits festgelegt, standardmäßig deaktiviert", @@ -9064,14 +9060,6 @@ Leave Type Allocation,Zuordnung Abwesenheitsarten, From Lead,Aus Lead, From Opportunity,Aus Chance, Publish in Website,Auf Webseite veröffentlichen, -<<<<<<< HEAD -======= -Total Allocated Leave(s),Gesamte zugewiesene Urlaubstage, -Expired Leave(s),Verfallene Urlaubstage, -Used Leave(s),Verbrauchte Urlaubstage, -Leave(s) Pending Approval,Urlaubstage zur Genehmigung ausstehend, -Available Leave(s),Verfügbare Urlaubstage, ->>>>>>> 915102a400 (chore: german translations (#31463)) Party Specific Item,Parteispezifischer Artikel, Active Customers,Aktive Kunden, Annual Sales,Jährlicher Umsatz, @@ -9086,10 +9074,6 @@ Accounts Payable Ageing,Fälligkeit Verbindlichkeiten, Budget Variance,Budgetabweichung, Based On Value,Basierend auf Wert, Restrict Items Based On,Artikel einschränken auf Basis von, -<<<<<<< HEAD -======= -Earnings & Deductions,Erträge & Abzüge, ->>>>>>> 915102a400 (chore: german translations (#31463)) Is Process Loss,Ist Prozessverlust, Is Finished Item,Ist fertiger Artikel, Is Scrap Item,Ist Schrott, @@ -9098,7 +9082,6 @@ Show Remarks,Bemerkungen anzeigen, Website Item,Webseiten-Artikel, Update Property,Eigenschaft aktualisieren, Recurring Sales Invoice,Wiederkehrende Ausgangsrechnung, -<<<<<<< HEAD Total Asset,Aktiva, Total Liability,Verbindlichkeiten, Total Equity,Eigenkapital, @@ -9125,5 +9108,3 @@ Lost Quotations,Verlorene Angebote, Lost Quotations %,Verlorene Angebote %, Lost Value,Verlorener Wert, Lost Value %,Verlorener Wert %, -======= ->>>>>>> 915102a400 (chore: german translations (#31463)) From fe986989a9a9bc249bb07564a47b4c44df4db920 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Sat, 24 Feb 2024 18:29:46 +0100 Subject: [PATCH 347/675] chore: remove duplicate german translations --- erpnext/translations/de.csv | 2 -- 1 file changed, 2 deletions(-) diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index 13aaa67c489..2f714326ad9 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -9057,7 +9057,6 @@ Apply Putaway Rule,Einlagerungsregel anwenden, Default Discount Account,Standard-Rabattkonto, Default Provisional Account,Standard Provisorisches Konto, Leave Type Allocation,Zuordnung Abwesenheitsarten, -From Lead,Aus Lead, From Opportunity,Aus Chance, Publish in Website,Auf Webseite veröffentlichen, Party Specific Item,Parteispezifischer Artikel, @@ -9103,7 +9102,6 @@ Select Alternative Items for Sales Order,Alternativpositionen für Auftragsbest Select an item from each set to be used in the Sales Order.,"Wählen Sie aus den Alternativen jeweils einen Artikel aus, der in die Auftragsbestätigung übernommen werden soll.", Is Alternative,Ist Alternative, Alternative Items,Alternativpositionen, -Component Type,Komponententyp, Lost Quotations,Verlorene Angebote, Lost Quotations %,Verlorene Angebote %, Lost Value,Verlorener Wert, From 4bc6c551983121b4db86b5d63df628b859288761 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 23 Feb 2024 17:22:01 +0530 Subject: [PATCH 348/675] refactor: update payments section on item removal (cherry picked from commit 406793a6ff2f4e9aef9fc30d20fa2c3b81ca2f50) --- erpnext/public/js/controllers/transaction.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index d5bc7647647..b4b85c4a2bb 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -140,7 +140,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe } if(this.frm.fields_dict["items"]) { - this["items_remove"] = this.calculate_net_weight; + this["items_remove"] = this.process_item_removal; } if(this.frm.fields_dict["recurring_print_format"]) { @@ -1192,6 +1192,11 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe } } + process_item_removal() { + this.frm.trigger("calculate_taxes_and_totals"); + this.frm.trigger("calculate_net_weight"); + } + calculate_net_weight(){ /* Calculate Total Net Weight then further applied shipping rule to calculate shipping charges.*/ var me = this; From bb3a7cdb3dfa6db1a219fc80d6c93f6e9f28001d Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Sun, 25 Feb 2024 21:32:24 +0530 Subject: [PATCH 349/675] fix: amount label according to party type (cherry picked from commit 9c8d103d8af85e52095071d43d8b8a03bdbf3255) --- .../accounts/report/tds_payable_monthly/tds_payable_monthly.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py index ba5cdbe6567..c0963150b1f 100644 --- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py +++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.py @@ -242,7 +242,7 @@ def get_columns(filters): "width": 120, }, { - "label": _("Tax Amount"), + "label": _("TDS Amount") if filters.get("party_type") == "Supplier" else _("TCS Amount"), "fieldname": "tax_amount", "fieldtype": "Float", "width": 120, From 0484857402b987212084a083f15cb759103dca82 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Fri, 23 Feb 2024 12:14:54 +0530 Subject: [PATCH 350/675] fix: only consider contributed qty towards achieved targets (cherry picked from commit 339698d172c2c070fb843ac2bbf1e8c6d6c7b8dc) --- .../item_group_wise_sales_target_variance.py | 56 +++++++++---------- 1 file changed, 25 insertions(+), 31 deletions(-) diff --git a/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/item_group_wise_sales_target_variance.py b/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/item_group_wise_sales_target_variance.py index 7d28f2b90d2..f2f1e4cfbaa 100644 --- a/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/item_group_wise_sales_target_variance.py +++ b/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/item_group_wise_sales_target_variance.py @@ -206,42 +206,36 @@ def prepare_data( def get_actual_data(filters, sales_users_or_territory_data, date_field, sales_field): fiscal_year = get_fiscal_year(fiscal_year=filters.get("fiscal_year"), as_dict=1) - dates = [fiscal_year.year_start_date, fiscal_year.year_end_date] - select_field = "`tab{0}`.{1}".format(filters.get("doctype"), sales_field) - child_table = "`tab{0}`".format(filters.get("doctype") + " Item") + parent_doc = frappe.qb.DocType(filters.get("doctype")) + child_doc = frappe.qb.DocType(filters.get("doctype") + " Item") + sales_team = frappe.qb.DocType("Sales Team") + + query = ( + frappe.qb.from_(parent_doc) + .inner_join(child_doc) + .on(child_doc.parent == parent_doc.name) + .inner_join(sales_team) + .on(sales_team.parent == parent_doc.name) + .select( + child_doc.item_group, + (child_doc.stock_qty * sales_team.allocated_percentage / 100).as_("stock_qty"), + (child_doc.base_net_amount * sales_team.allocated_percentage / 100).as_("base_net_amount"), + sales_team.sales_person, + parent_doc[date_field], + ) + .where( + (parent_doc.docstatus == 1) + & (parent_doc[date_field].between(fiscal_year.year_start_date, fiscal_year.year_end_date)) + ) + ) if sales_field == "sales_person": - select_field = "`tabSales Team`.sales_person" - child_table = "`tab{0}`, `tabSales Team`".format(filters.get("doctype") + " Item") - cond = """`tabSales Team`.parent = `tab{0}`.name and - `tabSales Team`.sales_person in ({1}) """.format( - filters.get("doctype"), ",".join(["%s"] * len(sales_users_or_territory_data)) - ) + query = query.where(sales_team.sales_person.isin(sales_users_or_territory_data)) else: - cond = "`tab{0}`.{1} in ({2})".format( - filters.get("doctype"), sales_field, ",".join(["%s"] * len(sales_users_or_territory_data)) - ) + query = query.where(parent_doc[sales_field].isin(sales_users_or_territory_data)) - return frappe.db.sql( - """ SELECT `tab{child_doc}`.item_group, - `tab{child_doc}`.stock_qty, `tab{child_doc}`.base_net_amount, - {select_field}, `tab{parent_doc}`.{date_field} - FROM `tab{parent_doc}`, {child_table} - WHERE - `tab{child_doc}`.parent = `tab{parent_doc}`.name - and `tab{parent_doc}`.docstatus = 1 and {cond} - and `tab{parent_doc}`.{date_field} between %s and %s""".format( - cond=cond, - date_field=date_field, - select_field=select_field, - child_table=child_table, - parent_doc=filters.get("doctype"), - child_doc=filters.get("doctype") + " Item", - ), - tuple(sales_users_or_territory_data + dates), - as_dict=1, - ) + return query.run(as_dict=True) def get_parents_data(filters, partner_doctype): From 89d19422d1f0236c10ae5165fc682c431c3d076c Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Fri, 23 Feb 2024 12:17:54 +0530 Subject: [PATCH 351/675] feat: show contributed qty in transaction summary (cherry picked from commit a823f16dff52ed8a92086aebbfa1b0a326eb2425) --- .../sales_person_wise_transaction_summary.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py b/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py index 9f3ba0da8bd..847488f6fbf 100644 --- a/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py +++ b/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.py @@ -36,6 +36,7 @@ def execute(filters=None): d.base_net_amount, d.sales_person, d.allocated_percentage, + (d.stock_qty * d.allocated_percentage / 100), d.contribution_amt, company_currency, ] @@ -103,7 +104,7 @@ def get_columns(filters): "fieldtype": "Link", "width": 140, }, - {"label": _("Qty"), "fieldname": "qty", "fieldtype": "Float", "width": 140}, + {"label": _("SO Total Qty"), "fieldname": "qty", "fieldtype": "Float", "width": 140}, { "label": _("Amount"), "options": "currency", @@ -119,6 +120,12 @@ def get_columns(filters): "width": 140, }, {"label": _("Contribution %"), "fieldname": "contribution", "fieldtype": "Float", "width": 140}, + { + "label": _("Contribution Qty"), + "fieldname": "contribution_qty", + "fieldtype": "Float", + "width": 140, + }, { "label": _("Contribution Amount"), "options": "currency", From 57f4eb121c46ad66138bbdc2f381d9b9f47d8a3c Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Fri, 23 Feb 2024 15:05:36 +0530 Subject: [PATCH 352/675] test: sales person target variance (cherry picked from commit 7566c1ee783bc0ea79ed44f4ecd9d9ea8eef8dd0) --- ...son_target_variance_based_on_item_group.py | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 erpnext/selling/report/sales_person_target_variance_based_on_item_group/test_sales_person_target_variance_based_on_item_group.py diff --git a/erpnext/selling/report/sales_person_target_variance_based_on_item_group/test_sales_person_target_variance_based_on_item_group.py b/erpnext/selling/report/sales_person_target_variance_based_on_item_group/test_sales_person_target_variance_based_on_item_group.py new file mode 100644 index 00000000000..4ae5d2bee88 --- /dev/null +++ b/erpnext/selling/report/sales_person_target_variance_based_on_item_group/test_sales_person_target_variance_based_on_item_group.py @@ -0,0 +1,84 @@ +import frappe +from frappe.tests.utils import FrappeTestCase +from frappe.utils import flt, nowdate + +from erpnext.accounts.utils import get_fiscal_year +from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order +from erpnext.selling.report.sales_person_target_variance_based_on_item_group.sales_person_target_variance_based_on_item_group import ( + execute, +) + + +class TestSalesPersonTargetVarianceBasedOnItemGroup(FrappeTestCase): + def setUp(self): + self.fiscal_year = get_fiscal_year(nowdate())[0] + + def tearDown(self): + frappe.db.rollback() + + def test_achieved_target_and_variance(self): + # Create a Target Distribution + distribution = frappe.new_doc("Monthly Distribution") + distribution.distribution_id = "Target Report Distribution" + distribution.fiscal_year = self.fiscal_year + distribution.get_months() + distribution.insert() + + # Create sales people with targets + person_1 = create_sales_person_with_target("Sales Person 1", self.fiscal_year, distribution.name) + person_2 = create_sales_person_with_target("Sales Person 2", self.fiscal_year, distribution.name) + + # Create a Sales Order with 50-50 contribution + so = make_sales_order( + rate=1000, + qty=20, + do_not_submit=True, + ) + so.set( + "sales_team", + [ + { + "sales_person": person_1.name, + "allocated_percentage": 50, + "allocated_amount": 10000, + }, + { + "sales_person": person_2.name, + "allocated_percentage": 50, + "allocated_amount": 10000, + }, + ], + ) + so.submit() + + # Check Achieved Target and Variance + result = execute( + frappe._dict( + { + "fiscal_year": self.fiscal_year, + "doctype": "Sales Order", + "period": "Yearly", + "target_on": "Quantity", + } + ) + )[1] + row = frappe._dict(result[0]) + self.assertSequenceEqual( + [flt(value, 2) for value in (row.total_target, row.total_achieved, row.total_variance)], + [50, 10, -40], + ) + + +def create_sales_person_with_target(sales_person_name, fiscal_year, distribution_id): + sales_person = frappe.new_doc("Sales Person") + sales_person.sales_person_name = sales_person_name + sales_person.append( + "targets", + { + "fiscal_year": fiscal_year, + "target_qty": 50, + "target_amount": 30000, + "distribution_id": distribution_id, + }, + ) + return sales_person.insert() From 23e256aedf65c8ad9fb39a507772798a374a209a Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 26 Feb 2024 12:53:06 +0530 Subject: [PATCH 353/675] fix: Patch to remove cancelled asset capitalization from asset --- .../asset_capitalization/asset_capitalization.py | 4 ++++ erpnext/patches.txt | 1 + ...remove_cancelled_asset_capitalization_from_asset.py | 10 ++++++++++ 3 files changed, 15 insertions(+) create mode 100644 erpnext/patches/v14_0/remove_cancelled_asset_capitalization_from_asset.py diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py index 2b5d8d0ffdd..f3464e2aba1 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py @@ -83,6 +83,10 @@ class AssetCapitalization(StockController): self.update_stock_ledger() self.make_gl_entries() self.restore_consumed_asset_items() + + def on_trash(self): + frappe.db.set_value("Asset", self.target_asset, "capitalized_in", None) + super(AssetCapitalization, self).on_trash() def cancel_target_asset(self): if self.entry_type == "Capitalization" and self.target_asset: diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 125158a5add..e0b8534f9fe 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -359,3 +359,4 @@ erpnext.stock.doctype.delivery_note.patches.drop_unused_return_against_index # 2 erpnext.patches.v14_0.set_maintain_stock_for_bom_item execute:frappe.db.set_single_value('E Commerce Settings', 'show_actual_qty', 1) erpnext.patches.v14_0.delete_orphaned_asset_movement_item_records +erpnext.patches.v14_0.remove_cancelled_asset_capitalization_from_asset diff --git a/erpnext/patches/v14_0/remove_cancelled_asset_capitalization_from_asset.py b/erpnext/patches/v14_0/remove_cancelled_asset_capitalization_from_asset.py new file mode 100644 index 00000000000..82f3c205582 --- /dev/null +++ b/erpnext/patches/v14_0/remove_cancelled_asset_capitalization_from_asset.py @@ -0,0 +1,10 @@ +import frappe + +def execute(): + cancelled_asset_capitalizations = frappe.get_all( + "Asset Capitalization", + filters={"docstatus": 2}, + fields=["name", "target_asset"], + ) + for asset_capitalization in cancelled_asset_capitalizations: + frappe.db.set_value("Asset", asset_capitalization.target_asset, "capitalized_in", None) From 6828a2d46d4009db4fa9c386292fcf44e32d899d Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 26 Feb 2024 11:31:27 +0530 Subject: [PATCH 354/675] refactor: patch to setup dimensions in Reconciliation tool (cherry picked from commit 461fb183fc5269ca4e77da1115e173b536ca3fd9) --- erpnext/patches.txt | 1 + ...create_accounting_dimensions_in_reconciliation_tool.py | 8 ++++++++ 2 files changed, 9 insertions(+) create mode 100644 erpnext/patches/v14_0/create_accounting_dimensions_in_reconciliation_tool.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 125158a5add..90650a640ad 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -353,6 +353,7 @@ erpnext.patches.v14_0.update_zero_asset_quantity_field execute:frappe.db.set_single_value("Buying Settings", "project_update_frequency", "Each Transaction") erpnext.patches.v14_0.clear_reconciliation_values_from_singles erpnext.patches.v14_0.update_total_asset_cost_field +erpnext.patches.v14_0.create_accounting_dimensions_in_reconciliation_tool # 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 diff --git a/erpnext/patches/v14_0/create_accounting_dimensions_in_reconciliation_tool.py b/erpnext/patches/v14_0/create_accounting_dimensions_in_reconciliation_tool.py new file mode 100644 index 00000000000..4466eaace8d --- /dev/null +++ b/erpnext/patches/v14_0/create_accounting_dimensions_in_reconciliation_tool.py @@ -0,0 +1,8 @@ +from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( + create_accounting_dimensions_for_doctype, +) + + +def execute(): + create_accounting_dimensions_for_doctype(doctype="Payment Reconciliation") + create_accounting_dimensions_for_doctype(doctype="Payment Reconciliation Allocation") From c7ed854850739d39442828019d6a813c5aa064fc Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 26 Feb 2024 14:49:32 +0530 Subject: [PATCH 355/675] fix: Reload document on cancel to avoid document modified error --- erpnext/assets/doctype/asset/asset.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 7f5e4debf29..55545610100 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -57,6 +57,7 @@ class Asset(AccountsController): self.validate_in_use_date() self.set_status() self.make_asset_movement() + self.reload() if not self.booked_fixed_asset and self.validate_make_gl_entry(): self.make_gl_entries() @@ -64,6 +65,7 @@ class Asset(AccountsController): self.validate_cancellation() self.cancel_movement_entries() self.cancel_capitalization() + self.reload() self.delete_depreciation_entries() self.set_status() self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry") From fe88a110b1c6c7eebc015f4ad90319fcdf1246b1 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 26 Feb 2024 17:08:56 +0530 Subject: [PATCH 356/675] fix: on unreconciliation, update advance paid (cherry picked from commit c9e2f03a3a30a382c0fdb2c065e77f43fed2ced3) --- .../doctype/unreconcile_payment/unreconcile_payment.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.py b/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.py index 77906a78332..ae9c19187ba 100644 --- a/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.py +++ b/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.py @@ -63,6 +63,11 @@ class UnreconcilePayment(Document): update_voucher_outstanding( alloc.reference_doctype, alloc.reference_name, alloc.account, alloc.party_type, alloc.party ) + if doc.doctype in frappe.get_hooks("advance_payment_payable_doctypes") + frappe.get_hooks( + "advance_payment_receivable_doctypes" + ): + doc.set_total_advance_paid() + frappe.db.set_value("Unreconcile Payment Entries", alloc.name, "unlinked", True) From 58c869c5628b8ef6d26ce076b5e775ae684a4812 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 26 Feb 2024 17:32:25 +0530 Subject: [PATCH 357/675] test: advance paid update on sales/purchase order unreconciliation (cherry picked from commit 1f01ff3487f55a86723106b9f3c6d42cdaa837ba) --- .../test_unreconcile_payment.py | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py b/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py index f404d9981a3..57f66dd21db 100644 --- a/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py +++ b/erpnext/accounts/doctype/unreconcile_payment/test_unreconcile_payment.py @@ -8,6 +8,7 @@ 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.test.accounts_mixin import AccountsTestMixin +from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase): @@ -49,6 +50,16 @@ class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase): ) return pe + def create_sales_order(self): + so = make_sales_order( + company=self.company, + customer=self.customer, + item=self.item, + rate=100, + transaction_date=today(), + ) + return so + def test_01_unreconcile_invoice(self): si1 = self.create_sales_invoice() si2 = self.create_sales_invoice() @@ -314,3 +325,41 @@ class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase): ), 1, ) + + def test_05_unreconcile_order(self): + so = self.create_sales_order() + + pe = self.create_payment_entry() + # Allocation payment against Sales Order + pe.paid_amount = 100 + pe.append( + "references", + {"reference_doctype": so.doctype, "reference_name": so.name, "allocated_amount": 100}, + ) + pe.save().submit() + + # Assert 'Advance Paid' + so.reload() + self.assertEqual(so.advance_paid, 100) + + 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), 1) + allocations = [x.reference_name for x in unreconcile.allocations] + self.assertEquals([so.name], allocations) + # unreconcile so + unreconcile.save().submit() + + # Assert 'Advance Paid' + so.reload() + pe.reload() + self.assertEqual(so.advance_paid, 0) + self.assertEqual(len(pe.references), 0) + self.assertEqual(pe.unallocated_amount, 100) From 8a573a1aff13dd878e5c990fd0b2d8f492ed8ad2 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 26 Feb 2024 18:52:05 +0530 Subject: [PATCH 358/675] fix: capacity planning issue in the job card (backport #40092) (#40100) * fix: capacity planning issue in the job card (#40092) * fix: capacity planning issue in the job card * test: test case to test capacity planning for workstation (cherry picked from commit 75f8464724382ac703a16ee3fd73de2c9bb56d13) # Conflicts: # erpnext/manufacturing/doctype/job_card/job_card.py # erpnext/manufacturing/doctype/work_order/test_work_order.py * chore: fix conflicts * chore: fix conflicts * chore: fix test cases --------- Co-authored-by: rohitwaghchaure --- .../doctype/job_card/job_card.py | 25 +++- .../doctype/work_order/test_work_order.py | 107 ++++++++++++++++++ .../doctype/work_order/work_order.py | 19 +++- 3 files changed, 139 insertions(+), 12 deletions(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index f47858ba9ff..1823efbbaaa 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -163,7 +163,7 @@ class JobCard(Document): for row in self.sub_operations: self.total_completed_qty += row.completed_qty - def get_overlap_for(self, args, check_next_available_slot=False): + def get_overlap_for(self, args): production_capacity = 1 jc = frappe.qb.DocType("Job Card") @@ -175,9 +175,6 @@ class JobCard(Document): ((jctl.from_time >= args.from_time) & (jctl.to_time <= args.to_time)), ] - if check_next_available_slot: - time_conditions.append(((jctl.from_time >= args.from_time) & (jctl.to_time >= args.to_time))) - query = ( frappe.qb.from_(jctl) .from_(jc) @@ -279,13 +276,29 @@ class JobCard(Document): self.check_workstation_time(row) def validate_overlap_for_workstation(self, args, row): + if args.get("to_time") and get_datetime(args.to_time) < get_datetime(args.from_time): + args.to_time = add_to_date(row.planned_start_time, minutes=row.remaining_time_in_mins) + # get the last record based on the to time from the job card - data = self.get_overlap_for(args, check_next_available_slot=True) + data = self.get_overlap_for(args) + + if not data: + row.planned_start_time = args.from_time + return + if data: if not self.workstation: self.workstation = data.workstation - row.planned_start_time = get_datetime(data.to_time + get_mins_between_operations()) + if data.get("planned_start_time"): + args.planned_start_time = get_datetime(data.planned_start_time) + else: + args.planned_start_time = get_datetime(data.to_time + get_mins_between_operations()) + + args.from_time = args.planned_start_time + args.to_time = add_to_date(args.planned_start_time, minutes=row.remaining_time_in_mins) + + self.validate_overlap_for_workstation(args, row) def check_workstation_time(self, row): workstation_doc = frappe.get_cached_doc("Workstation", self.workstation) diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index 74c2ece6e7e..8e24d4cd59a 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -1778,6 +1778,113 @@ class TestWorkOrder(FrappeTestCase): "Manufacturing Settings", "set_op_cost_and_scrape_from_sub_assemblies", 0 ) + def test_capcity_planning_for_workstation(self): + frappe.db.set_single_value( + "Manufacturing Settings", + { + "disable_capacity_planning": 0, + "capacity_planning_for_days": 1, + "mins_between_operations": 10, + }, + ) + + properties = {"is_stock_item": 1, "valuation_rate": 100} + fg_item = make_item("Test FG Item For Capacity Planning", properties).name + + rm_item = make_item("Test RM Item For Capacity Planning", properties).name + + workstation = "Test Workstation For Capacity Planning" + if not frappe.db.exists("Workstation", workstation): + make_workstation(workstation=workstation, production_capacity=1) + + operation = "Test Operation For Capacity Planning" + if not frappe.db.exists("Operation", operation): + make_operation(operation=operation, workstation=workstation) + + bom_doc = make_bom( + item=fg_item, + source_warehouse="Stores - _TC", + raw_materials=[rm_item], + with_operations=1, + do_not_submit=True, + ) + + bom_doc.append( + "operations", + {"operation": operation, "time_in_mins": 1420, "hour_rate": 100, "workstation": workstation}, + ) + bom_doc.submit() + + # 1st Work Order, + # Capacity to run parallel the operation 'Test Operation For Capacity Planning' is 2 + wo_doc = make_wo_order_test_record( + production_item=fg_item, qty=1, planned_start_date="2024-02-25 00:00:00", do_not_submit=1 + ) + + wo_doc.submit() + job_cards = frappe.get_all( + "Job Card", + filters={"work_order": wo_doc.name}, + ) + + self.assertEqual(len(job_cards), 1) + + # 2nd Work Order, + wo_doc = make_wo_order_test_record( + production_item=fg_item, qty=1, planned_start_date="2024-02-25 00:00:00", do_not_submit=1 + ) + + wo_doc.submit() + job_cards = frappe.get_all( + "Job Card", + filters={"work_order": wo_doc.name}, + ) + + self.assertEqual(len(job_cards), 1) + + # 3rd Work Order, capacity is full + wo_doc = make_wo_order_test_record( + production_item=fg_item, qty=1, planned_start_date="2024-02-25 00:00:00", do_not_submit=1 + ) + + self.assertRaises(CapacityError, wo_doc.submit) + + frappe.db.set_single_value( + "Manufacturing Settings", {"disable_capacity_planning": 1, "mins_between_operations": 0} + ) + + +def make_operation(**kwargs): + kwargs = frappe._dict(kwargs) + + operation_doc = frappe.get_doc( + { + "doctype": "Operation", + "name": kwargs.operation, + "workstation": kwargs.workstation, + } + ) + operation_doc.insert() + + return operation_doc + + +def make_workstation(**kwargs): + kwargs = frappe._dict(kwargs) + + workstation_doc = frappe.get_doc( + { + "doctype": "Workstation", + "workstation_name": kwargs.workstation, + "workstation_type": kwargs.workstation_type, + "production_capacity": kwargs.production_capacity or 0, + "hour_rate": kwargs.hour_rate or 100, + } + ) + workstation_doc.insert() + + return workstation_doc + def prepare_boms_for_sub_assembly_test(): if not frappe.db.exists("BOM", {"item": "Test Final SF Item 1"}): diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index f95b4f66a33..77a0c54c734 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -172,8 +172,12 @@ class WorkOrder(Document): def calculate_operating_cost(self): self.planned_operating_cost, self.actual_operating_cost = 0.0, 0.0 for d in self.get("operations"): - d.planned_operating_cost = flt(d.hour_rate) * (flt(d.time_in_mins) / 60.0) - d.actual_operating_cost = flt(d.hour_rate) * (flt(d.actual_operation_time) / 60.0) + d.planned_operating_cost = flt( + flt(d.hour_rate) * (flt(d.time_in_mins) / 60.0), d.precision("planned_operating_cost") + ) + d.actual_operating_cost = flt( + flt(d.hour_rate) * (flt(d.actual_operation_time) / 60.0), d.precision("actual_operating_cost") + ) self.planned_operating_cost += flt(d.planned_operating_cost) self.actual_operating_cost += flt(d.actual_operating_cost) @@ -489,7 +493,6 @@ class WorkOrder(Document): def prepare_data_for_job_card(self, row, index, plan_days, enable_capacity_planning): self.set_operation_start_end_time(index, row) - original_start_time = row.planned_start_time job_card_doc = create_job_card( self, row, auto_create=True, enable_capacity_planning=enable_capacity_planning ) @@ -498,11 +501,15 @@ class WorkOrder(Document): row.planned_start_time = job_card_doc.time_logs[-1].from_time row.planned_end_time = job_card_doc.time_logs[-1].to_time - if date_diff(row.planned_start_time, original_start_time) > plan_days: + if date_diff(row.planned_end_time, self.planned_start_date) > plan_days: frappe.message_log.pop() frappe.throw( - _("Unable to find the time slot in the next {0} days for the operation {1}.").format( - plan_days, row.operation + _( + "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}." + ).format( + plan_days, + row.operation, + get_link_to_form("Manufacturing Settings", "Manufacturing Settings"), ), CapacityError, ) From 812eebf1ab7c71ca8daea273684dec5d0f4a539b Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 26 Feb 2024 20:32:48 +0530 Subject: [PATCH 359/675] fix: use correct variable name for hotfix branches --- .../doctype/unreconcile_payment/unreconcile_payment.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.py b/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.py index ae9c19187ba..dd714573b1b 100644 --- a/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.py +++ b/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.py @@ -63,9 +63,7 @@ class UnreconcilePayment(Document): update_voucher_outstanding( alloc.reference_doctype, alloc.reference_name, alloc.account, alloc.party_type, alloc.party ) - if doc.doctype in frappe.get_hooks("advance_payment_payable_doctypes") + frappe.get_hooks( - "advance_payment_receivable_doctypes" - ): + if doc.doctype in frappe.get_hooks("advance_payment_doctypes"): doc.set_total_advance_paid() frappe.db.set_value("Unreconcile Payment Entries", alloc.name, "unlinked", True) From 193cd0db96d45c9bdd377a29e7ae530ed18ec0aa Mon Sep 17 00:00:00 2001 From: "Nihantra C. Patel" <141945075+Nihantra-Patel@users.noreply.github.com> Date: Mon, 26 Feb 2024 23:01:41 +0530 Subject: [PATCH 360/675] fix: sales funnel text color in light/dark theme (#40132) * fix: sales funnel text color in light/dark theme * fix: sales funnel text color in light/dark theme --- erpnext/selling/page/sales_funnel/sales_funnel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/page/sales_funnel/sales_funnel.js b/erpnext/selling/page/sales_funnel/sales_funnel.js index e3d0a55c3a0..c37c7266362 100644 --- a/erpnext/selling/page/sales_funnel/sales_funnel.js +++ b/erpnext/selling/page/sales_funnel/sales_funnel.js @@ -229,7 +229,7 @@ erpnext.SalesFunnel = class SalesFunnel { context.fill(); // draw text - context.fillStyle = "black"; + context.fillStyle = ""; context.textBaseline = "middle"; context.font = "1.1em sans-serif"; context.fillText(__(title), width + 20, y_mid); From e289aef1b4a6a0ab4827214422486f16efd7d4cd Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Tue, 27 Feb 2024 09:59:08 +0530 Subject: [PATCH 361/675] fix: linting issue --- .../doctype/stock_reconciliation/stock_reconciliation.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 44c2d85b4cc..878af0e22ca 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -702,10 +702,7 @@ class StockReconciliation(StockController): if allow_negative_stock: return True - if any( - (d.batch_no and flt(d.qty) == flt(d.current_qty)) - for d in self.items - ): + if any((d.batch_no and flt(d.qty) == flt(d.current_qty)) for d in self.items): allow_negative_stock = True return allow_negative_stock From 65a2adfc551546744ad97194b5d72fc0b8f42237 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Tue, 27 Feb 2024 12:16:08 +0530 Subject: [PATCH 362/675] fix: linting issue --- .../doctype/stock_reconciliation/stock_reconciliation.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 44c2d85b4cc..878af0e22ca 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -702,10 +702,7 @@ class StockReconciliation(StockController): if allow_negative_stock: return True - if any( - (d.batch_no and flt(d.qty) == flt(d.current_qty)) - for d in self.items - ): + if any((d.batch_no and flt(d.qty) == flt(d.current_qty)) for d in self.items): allow_negative_stock = True return allow_negative_stock From 8638d14babaf4c846a7f6222c7018fc8f59545b2 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Sun, 25 Feb 2024 13:17:41 +0530 Subject: [PATCH 363/675] fix: unique gl account for plaid bank accounts (cherry picked from commit bf6e32a960e761fa869b44db15cf61fabb5c7aef) --- .../doctype/plaid_settings/plaid_settings.py | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py index 5354d0d6c13..af1052a3700 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/plaid_settings.py @@ -10,7 +10,6 @@ from frappe.model.document import Document from frappe.utils import add_months, formatdate, getdate, sbool, today from plaid.errors import ItemError -from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account from erpnext.erpnext_integrations.doctype.plaid_settings.plaid_connector import PlaidConnector @@ -74,9 +73,15 @@ def add_bank_accounts(response, bank, company): bank = json.loads(bank) result = [] - default_gl_account = get_default_bank_cash_account(company, "Bank") - if not default_gl_account: - frappe.throw(_("Please setup a default bank account for company {0}").format(company)) + parent_gl_account = frappe.db.get_all( + "Account", {"company": company, "account_type": "Bank", "is_group": 1, "disabled": 0} + ) + if not parent_gl_account: + frappe.throw( + _( + "Please setup and enable a group account with the Account Type - {0} for the company {1}" + ).format(frappe.bold("Bank"), company) + ) for account in response["accounts"]: acc_type = frappe.db.get_value("Bank Account Type", account["type"]) @@ -92,11 +97,22 @@ def add_bank_accounts(response, bank, company): if not existing_bank_account: try: + gl_account = frappe.get_doc( + { + "doctype": "Account", + "account_name": account["name"] + " - " + response["institution"]["name"], + "parent_account": parent_gl_account[0].name, + "account_type": "Bank", + "company": company, + } + ) + gl_account.insert(ignore_if_duplicate=True) + new_account = frappe.get_doc( { "doctype": "Bank Account", "bank": bank["bank_name"], - "account": default_gl_account.account, + "account": gl_account.name, "account_name": account["name"], "account_type": account.get("type", ""), "account_subtype": account.get("subtype", ""), From ad6067795f87140c3252c9053d01e22f735dce49 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Sun, 25 Feb 2024 13:18:30 +0530 Subject: [PATCH 364/675] fix: remove config for default bank account in test (cherry picked from commit c42444ab3bf1e35ad5bb87f5f94ea78b9052c12e) --- .../doctype/plaid_settings/test_plaid_settings.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py index 6d34a204cd2..302bdc436c6 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py @@ -7,7 +7,6 @@ import unittest import frappe from frappe.utils.response import json_handler -from erpnext.accounts.doctype.journal_entry.journal_entry import get_default_bank_cash_account from erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings import ( add_account_subtype, add_account_type, @@ -106,14 +105,6 @@ class TestPlaidSettings(unittest.TestCase): bank = json.dumps(frappe.get_doc("Bank", "Citi").as_dict(), default=json_handler) company = frappe.db.get_single_value("Global Defaults", "default_company") - if frappe.db.get_value("Company", company, "default_bank_account") is None: - frappe.db.set_value( - "Company", - company, - "default_bank_account", - get_default_bank_cash_account(company, "Cash").get("account"), - ) - add_bank_accounts(bank_accounts, bank, company) transactions = { From 019d8f92fed10d479870ecea94b6abc2f7c508dd Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Sun, 25 Feb 2024 11:59:44 +0530 Subject: [PATCH 365/675] feat: update billed amount in PO and PR (cherry picked from commit 9f6535472d50e651fa99ef7b9001ce913acb6902) # Conflicts: # erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json # erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py --- .../purchase_invoice/purchase_invoice.json | 20 ++ .../purchase_invoice/purchase_invoice.py | 186 ++++++++++++++++++ 2 files changed, 206 insertions(+) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 643047e982d..de81742ac00 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -22,6 +22,8 @@ "is_paid", "is_return", "return_against", + "update_billed_amount_in_purchase_order", + "update_billed_amount_in_purchase_receipt", "apply_tds", "tax_withholding_category", "amended_from", @@ -410,6 +412,20 @@ "read_only": 1, "search_index": 1 }, + { + "default": "0", + "depends_on": "eval: doc.is_return", + "fieldname": "update_billed_amount_in_purchase_order", + "fieldtype": "Check", + "label": "Update Billed Amount in Purchase Order" + }, + { + "default": "1", + "depends_on": "eval: doc.is_return", + "fieldname": "update_billed_amount_in_purchase_receipt", + "fieldtype": "Check", + "label": "Update Billed Amount in Purchase Receipt" + }, { "fieldname": "section_addresses", "fieldtype": "Section Break", @@ -1594,7 +1610,11 @@ "idx": 204, "is_submittable": 1, "links": [], +<<<<<<< HEAD "modified": "2023-11-03 15:47:30.319200", +======= + "modified": "2024-02-25 11:20:28.366808", +>>>>>>> 9f6535472d (feat: update billed amount in PO and PR) "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 802ac8080a9..c04f8d40364 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -54,6 +54,180 @@ form_grid_templates = {"items": "templates/form_grid/item_grid.html"} class PurchaseInvoice(BuyingController): +<<<<<<< HEAD +======= + # 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.accounts.doctype.advance_tax.advance_tax import AdvanceTax + from erpnext.accounts.doctype.payment_schedule.payment_schedule import PaymentSchedule + from erpnext.accounts.doctype.pricing_rule_detail.pricing_rule_detail import PricingRuleDetail + from erpnext.accounts.doctype.purchase_invoice_advance.purchase_invoice_advance import ( + PurchaseInvoiceAdvance, + ) + from erpnext.accounts.doctype.purchase_invoice_item.purchase_invoice_item import ( + PurchaseInvoiceItem, + ) + from erpnext.accounts.doctype.purchase_taxes_and_charges.purchase_taxes_and_charges import ( + PurchaseTaxesandCharges, + ) + from erpnext.accounts.doctype.tax_withheld_vouchers.tax_withheld_vouchers import ( + TaxWithheldVouchers, + ) + from erpnext.buying.doctype.purchase_receipt_item_supplied.purchase_receipt_item_supplied import ( + PurchaseReceiptItemSupplied, + ) + + additional_discount_percentage: DF.Float + address_display: DF.SmallText | None + advance_tax: DF.Table[AdvanceTax] + advances: DF.Table[PurchaseInvoiceAdvance] + against_expense_account: DF.SmallText | None + allocate_advances_automatically: DF.Check + amended_from: DF.Link | None + apply_discount_on: DF.Literal["", "Grand Total", "Net Total"] + apply_tds: DF.Check + auto_repeat: DF.Link | None + base_discount_amount: DF.Currency + base_grand_total: DF.Currency + base_in_words: DF.Data | None + base_net_total: DF.Currency + base_paid_amount: DF.Currency + base_rounded_total: DF.Currency + base_rounding_adjustment: DF.Currency + base_tax_withholding_net_total: DF.Currency + base_taxes_and_charges_added: DF.Currency + base_taxes_and_charges_deducted: DF.Currency + base_total: DF.Currency + base_total_taxes_and_charges: DF.Currency + base_write_off_amount: DF.Currency + bill_date: DF.Date | None + bill_no: DF.Data | None + billing_address: DF.Link | None + billing_address_display: DF.SmallText | None + buying_price_list: DF.Link | None + cash_bank_account: DF.Link | None + clearance_date: DF.Date | None + company: DF.Link | None + contact_display: DF.SmallText | None + contact_email: DF.SmallText | None + contact_mobile: DF.SmallText | None + contact_person: DF.Link | None + conversion_rate: DF.Float + cost_center: DF.Link | None + credit_to: DF.Link + currency: DF.Link | None + disable_rounded_total: DF.Check + discount_amount: DF.Currency + due_date: DF.Date | None + from_date: DF.Date | None + grand_total: DF.Currency + group_same_items: DF.Check + hold_comment: DF.SmallText | None + ignore_default_payment_terms_template: DF.Check + ignore_pricing_rule: DF.Check + in_words: DF.Data | None + incoterm: DF.Link | None + inter_company_invoice_reference: DF.Link | None + is_internal_supplier: DF.Check + is_old_subcontracting_flow: DF.Check + is_opening: DF.Literal["No", "Yes"] + is_paid: DF.Check + is_return: DF.Check + is_subcontracted: DF.Check + items: DF.Table[PurchaseInvoiceItem] + language: DF.Data | None + letter_head: DF.Link | None + mode_of_payment: DF.Link | None + named_place: DF.Data | None + naming_series: DF.Literal["ACC-PINV-.YYYY.-", "ACC-PINV-RET-.YYYY.-"] + net_total: DF.Currency + on_hold: DF.Check + only_include_allocated_payments: DF.Check + other_charges_calculation: DF.LongText | None + outstanding_amount: DF.Currency + paid_amount: DF.Currency + party_account_currency: DF.Link | None + payment_schedule: DF.Table[PaymentSchedule] + payment_terms_template: DF.Link | None + per_received: DF.Percent + plc_conversion_rate: DF.Float + posting_date: DF.Date + posting_time: DF.Time | None + price_list_currency: DF.Link | None + pricing_rules: DF.Table[PricingRuleDetail] + project: DF.Link | None + rejected_warehouse: DF.Link | None + release_date: DF.Date | None + remarks: DF.SmallText | None + repost_required: DF.Check + represents_company: DF.Link | None + return_against: DF.Link | None + rounded_total: DF.Currency + rounding_adjustment: DF.Currency + scan_barcode: DF.Data | None + select_print_heading: DF.Link | None + set_from_warehouse: DF.Link | None + set_posting_time: DF.Check + set_warehouse: DF.Link | None + shipping_address: DF.Link | None + shipping_address_display: DF.SmallText | None + shipping_rule: DF.Link | None + status: DF.Literal[ + "", + "Draft", + "Return", + "Debit Note Issued", + "Submitted", + "Paid", + "Partly Paid", + "Unpaid", + "Overdue", + "Cancelled", + "Internal Transfer", + ] + subscription: DF.Link | None + supplied_items: DF.Table[PurchaseReceiptItemSupplied] + supplier: DF.Link + supplier_address: DF.Link | None + supplier_group: DF.Link | None + supplier_name: DF.Data | None + supplier_warehouse: DF.Link | None + tax_category: DF.Link | None + tax_id: DF.ReadOnly | None + tax_withheld_vouchers: DF.Table[TaxWithheldVouchers] + tax_withholding_category: DF.Link | None + tax_withholding_net_total: DF.Currency + taxes: DF.Table[PurchaseTaxesandCharges] + taxes_and_charges: DF.Link | None + taxes_and_charges_added: DF.Currency + taxes_and_charges_deducted: DF.Currency + tc_name: DF.Link | None + terms: DF.TextEditor | None + title: DF.Data | None + to_date: DF.Date | None + total: DF.Currency + total_advance: DF.Currency + total_net_weight: DF.Float + total_qty: DF.Float + total_taxes_and_charges: DF.Currency + unrealized_profit_loss_account: DF.Link | None + update_billed_amount_in_purchase_order: DF.Check + update_billed_amount_in_purchase_receipt: DF.Check + update_stock: DF.Check + use_company_roundoff_cost_center: DF.Check + use_transaction_date_exchange_rate: DF.Check + write_off_account: DF.Link | None + write_off_amount: DF.Currency + write_off_cost_center: DF.Link | None + # end: auto-generated types + +>>>>>>> 9f6535472d (feat: update billed amount in PO and PR) def __init__(self, *args, **kwargs): super(PurchaseInvoice, self).__init__(*args, **kwargs) self.status_updater = [ @@ -514,6 +688,11 @@ class PurchaseInvoice(BuyingController): super(PurchaseInvoice, self).on_submit() self.check_prev_docstatus() + + if self.is_return and not self.update_billed_amount_in_purchase_order: + # NOTE status updating bypassed for is_return + self.status_updater = [] + self.update_status_updater_args() self.update_prevdoc_status() @@ -1264,6 +1443,10 @@ class PurchaseInvoice(BuyingController): self.check_on_hold_or_closed_status() + if self.is_return and not self.update_billed_amount_in_purchase_order: + # NOTE status updating bypassed for is_return + self.status_updater = [] + self.update_status_updater_args() self.update_prevdoc_status() @@ -1357,6 +1540,9 @@ class PurchaseInvoice(BuyingController): frappe.throw(_("Supplier Invoice No exists in Purchase Invoice {0}").format(pi)) def update_billing_status_in_pr(self, update_modified=True): + if self.is_return and not self.update_billed_amount_in_purchase_receipt: + return + updated_pr = [] po_details = [] From 4f87e73f559e36a30bbfc342510b9079e843d712 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Sun, 25 Feb 2024 19:40:28 +0530 Subject: [PATCH 366/675] test: po billed amount against debit note (cherry picked from commit 81dbfe189e66f48cee4a37c54544aeaa4f0b3c2e) # Conflicts: # erpnext/buying/doctype/purchase_order/test_purchase_order.py --- .../purchase_order/test_purchase_order.py | 159 ++++++++++++++++++ 1 file changed, 159 insertions(+) diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index 739a989c79e..f2c12ae6733 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -940,6 +940,165 @@ class TestPurchaseOrder(FrappeTestCase): self.assertRaises(frappe.ValidationError, po.save) +<<<<<<< HEAD +======= + def test_update_items_for_subcontracting_purchase_order(self): + from erpnext.controllers.tests.test_subcontracting_controller import ( + get_subcontracting_order, + make_bom_for_subcontracted_items, + make_raw_materials, + make_service_items, + make_subcontracted_items, + ) + + def update_items(po, qty): + trans_items = [po.items[0].as_dict()] + trans_items[0]["qty"] = qty + trans_items[0]["fg_item_qty"] = qty + trans_items = json.dumps(trans_items, default=str) + + return update_child_qty_rate( + po.doctype, + trans_items, + po.name, + ) + + make_subcontracted_items() + make_raw_materials() + make_service_items() + make_bom_for_subcontracted_items() + + service_items = [ + { + "warehouse": "_Test Warehouse - _TC", + "item_code": "Subcontracted Service Item 7", + "qty": 10, + "rate": 100, + "fg_item": "Subcontracted Item SA7", + "fg_item_qty": 10, + }, + ] + po = create_purchase_order( + rm_items=service_items, + is_subcontracted=1, + supplier_warehouse="_Test Warehouse 1 - _TC", + ) + + update_items(po, qty=20) + po.reload() + + # Test - 1: Items should be updated as there is no Subcontracting Order against PO + self.assertEqual(po.items[0].qty, 20) + self.assertEqual(po.items[0].fg_item_qty, 20) + + sco = get_subcontracting_order(po_name=po.name, warehouse="_Test Warehouse - _TC") + + # Test - 2: ValidationError should be raised as there is Subcontracting Order against PO + self.assertRaises(frappe.ValidationError, update_items, po=po, qty=30) + + sco.reload() + sco.cancel() + po.reload() + + update_items(po, qty=30) + po.reload() + + # Test - 3: Items should be updated as the Subcontracting Order is cancelled + self.assertEqual(po.items[0].qty, 30) + self.assertEqual(po.items[0].fg_item_qty, 30) + + @change_settings("Buying Settings", {"auto_create_subcontracting_order": 1}) + def test_auto_create_subcontracting_order(self): + from erpnext.controllers.tests.test_subcontracting_controller import ( + make_bom_for_subcontracted_items, + make_raw_materials, + make_service_items, + make_subcontracted_items, + ) + + make_subcontracted_items() + make_raw_materials() + make_service_items() + make_bom_for_subcontracted_items() + + service_items = [ + { + "warehouse": "_Test Warehouse - _TC", + "item_code": "Subcontracted Service Item 7", + "qty": 10, + "rate": 100, + "fg_item": "Subcontracted Item SA7", + "fg_item_qty": 10, + }, + ] + po = create_purchase_order( + rm_items=service_items, + is_subcontracted=1, + supplier_warehouse="_Test Warehouse 1 - _TC", + ) + + self.assertTrue(frappe.db.get_value("Subcontracting Order", {"purchase_order": po.name})) + + def test_purchase_order_advance_payment_status(self): + from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry + from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request + + po = create_purchase_order() + self.assertEqual( + frappe.db.get_value(po.doctype, po.name, "advance_payment_status"), "Not Initiated" + ) + + pr = make_payment_request(dt=po.doctype, dn=po.name, submit_doc=True, return_doc=True) + self.assertEqual(frappe.db.get_value(po.doctype, po.name, "advance_payment_status"), "Initiated") + + pe = get_payment_entry(po.doctype, po.name).save().submit() + self.assertEqual( + frappe.db.get_value(po.doctype, po.name, "advance_payment_status"), "Fully Paid" + ) + + pe.reload() + pe.cancel() + self.assertEqual(frappe.db.get_value(po.doctype, po.name, "advance_payment_status"), "Initiated") + + pr.reload() + pr.cancel() + self.assertEqual( + frappe.db.get_value(po.doctype, po.name, "advance_payment_status"), "Not Initiated" + ) + + def test_po_billed_amount_against_return_entry(self): + from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import make_debit_note + + # Create a Purchase Order and Fully Bill it + po = create_purchase_order() + pi = make_pi_from_po(po.name) + pi.insert() + pi.submit() + + # Debit Note - 50% Qty & enable updating PO billed amount + pi_return = make_debit_note(pi.name) + pi_return.items[0].qty = -5 + pi_return.update_billed_amount_in_purchase_order = 1 + pi_return.submit() + + # Check if the billed amount reduced + po.reload() + self.assertEqual(po.per_billed, 50) + + pi_return.reload() + pi_return.cancel() + + # Debit Note - 50% Qty & disable updating PO billed amount + pi_return = make_debit_note(pi.name) + pi_return.items[0].qty = -5 + pi_return.update_billed_amount_in_purchase_order = 0 + pi_return.submit() + + # Check if the billed amount stayed the same + po.reload() + self.assertEqual(po.per_billed, 100) + +>>>>>>> 81dbfe189e (test: po billed amount against debit note) def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier From a84fc0fe408523d11266e2937be98fc053c76542 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Sun, 25 Feb 2024 20:03:33 +0530 Subject: [PATCH 367/675] test: pr billed amount against debit note (cherry picked from commit 6d408448943c47f0a3632d5772a0786b14c75a59) --- .../purchase_receipt/test_purchase_receipt.py | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index d8141f93e68..7defbc5bcdf 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -2189,6 +2189,41 @@ class TestPurchaseReceipt(FrappeTestCase): pr.cancel() self.assertTrue(frappe.db.exists("Batch", batch_no)) + def test_pr_billed_amount_against_return_entry(self): + from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import make_debit_note + from erpnext.stock.doctype.purchase_receipt.purchase_receipt import ( + make_purchase_invoice as make_pi_from_pr, + ) + + # Create a Purchase Receipt and Fully Bill it + pr = make_purchase_receipt(qty=10) + pi = make_pi_from_pr(pr.name) + pi.insert() + pi.submit() + + # Debit Note - 50% Qty & enable updating PR billed amount + pi_return = make_debit_note(pi.name) + pi_return.items[0].qty = -5 + pi_return.update_billed_amount_in_purchase_receipt = 1 + pi_return.submit() + + # Check if the billed amount reduced + pr.reload() + self.assertEqual(pr.per_billed, 50) + + pi_return.reload() + pi_return.cancel() + + # Debit Note - 50% Qty & disable updating PR billed amount + pi_return = make_debit_note(pi.name) + pi_return.items[0].qty = -5 + pi_return.update_billed_amount_in_purchase_receipt = 0 + pi_return.submit() + + # Check if the billed amount stayed the same + pr.reload() + self.assertEqual(pr.per_billed, 100) + def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier From 42b7be60ef3cb68040fa7985eaaf20c330486d25 Mon Sep 17 00:00:00 2001 From: Gursheen Kaur Anand <40693548+GursheenK@users.noreply.github.com> Date: Tue, 27 Feb 2024 15:57:43 +0530 Subject: [PATCH 368/675] chore: resolve conflicts --- .../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 de81742ac00..813c70806f4 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -1610,11 +1610,7 @@ "idx": 204, "is_submittable": 1, "links": [], -<<<<<<< HEAD - "modified": "2023-11-03 15:47:30.319200", -======= "modified": "2024-02-25 11:20:28.366808", ->>>>>>> 9f6535472d (feat: update billed amount in PO and PR) "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", From 8f983e61093a077e373b6f8218dfa15b97ba5692 Mon Sep 17 00:00:00 2001 From: Gursheen Kaur Anand <40693548+GursheenK@users.noreply.github.com> Date: Tue, 27 Feb 2024 16:01:33 +0530 Subject: [PATCH 369/675] chore: resolve conflicts --- .../purchase_order/test_purchase_order.py | 127 ------------------ 1 file changed, 127 deletions(-) diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py index f2c12ae6733..3a498ee271b 100644 --- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py @@ -940,132 +940,6 @@ class TestPurchaseOrder(FrappeTestCase): self.assertRaises(frappe.ValidationError, po.save) -<<<<<<< HEAD -======= - def test_update_items_for_subcontracting_purchase_order(self): - from erpnext.controllers.tests.test_subcontracting_controller import ( - get_subcontracting_order, - make_bom_for_subcontracted_items, - make_raw_materials, - make_service_items, - make_subcontracted_items, - ) - - def update_items(po, qty): - trans_items = [po.items[0].as_dict()] - trans_items[0]["qty"] = qty - trans_items[0]["fg_item_qty"] = qty - trans_items = json.dumps(trans_items, default=str) - - return update_child_qty_rate( - po.doctype, - trans_items, - po.name, - ) - - make_subcontracted_items() - make_raw_materials() - make_service_items() - make_bom_for_subcontracted_items() - - service_items = [ - { - "warehouse": "_Test Warehouse - _TC", - "item_code": "Subcontracted Service Item 7", - "qty": 10, - "rate": 100, - "fg_item": "Subcontracted Item SA7", - "fg_item_qty": 10, - }, - ] - po = create_purchase_order( - rm_items=service_items, - is_subcontracted=1, - supplier_warehouse="_Test Warehouse 1 - _TC", - ) - - update_items(po, qty=20) - po.reload() - - # Test - 1: Items should be updated as there is no Subcontracting Order against PO - self.assertEqual(po.items[0].qty, 20) - self.assertEqual(po.items[0].fg_item_qty, 20) - - sco = get_subcontracting_order(po_name=po.name, warehouse="_Test Warehouse - _TC") - - # Test - 2: ValidationError should be raised as there is Subcontracting Order against PO - self.assertRaises(frappe.ValidationError, update_items, po=po, qty=30) - - sco.reload() - sco.cancel() - po.reload() - - update_items(po, qty=30) - po.reload() - - # Test - 3: Items should be updated as the Subcontracting Order is cancelled - self.assertEqual(po.items[0].qty, 30) - self.assertEqual(po.items[0].fg_item_qty, 30) - - @change_settings("Buying Settings", {"auto_create_subcontracting_order": 1}) - def test_auto_create_subcontracting_order(self): - from erpnext.controllers.tests.test_subcontracting_controller import ( - make_bom_for_subcontracted_items, - make_raw_materials, - make_service_items, - make_subcontracted_items, - ) - - make_subcontracted_items() - make_raw_materials() - make_service_items() - make_bom_for_subcontracted_items() - - service_items = [ - { - "warehouse": "_Test Warehouse - _TC", - "item_code": "Subcontracted Service Item 7", - "qty": 10, - "rate": 100, - "fg_item": "Subcontracted Item SA7", - "fg_item_qty": 10, - }, - ] - po = create_purchase_order( - rm_items=service_items, - is_subcontracted=1, - supplier_warehouse="_Test Warehouse 1 - _TC", - ) - - self.assertTrue(frappe.db.get_value("Subcontracting Order", {"purchase_order": po.name})) - - def test_purchase_order_advance_payment_status(self): - from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry - from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request - - po = create_purchase_order() - self.assertEqual( - frappe.db.get_value(po.doctype, po.name, "advance_payment_status"), "Not Initiated" - ) - - pr = make_payment_request(dt=po.doctype, dn=po.name, submit_doc=True, return_doc=True) - self.assertEqual(frappe.db.get_value(po.doctype, po.name, "advance_payment_status"), "Initiated") - - pe = get_payment_entry(po.doctype, po.name).save().submit() - self.assertEqual( - frappe.db.get_value(po.doctype, po.name, "advance_payment_status"), "Fully Paid" - ) - - pe.reload() - pe.cancel() - self.assertEqual(frappe.db.get_value(po.doctype, po.name, "advance_payment_status"), "Initiated") - - pr.reload() - pr.cancel() - self.assertEqual( - frappe.db.get_value(po.doctype, po.name, "advance_payment_status"), "Not Initiated" - ) - def test_po_billed_amount_against_return_entry(self): from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import make_debit_note @@ -1098,7 +972,6 @@ class TestPurchaseOrder(FrappeTestCase): po.reload() self.assertEqual(po.per_billed, 100) ->>>>>>> 81dbfe189e (test: po billed amount against debit note) def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier From 614119b3e18b5852e09a28b8479e68b637dc882f Mon Sep 17 00:00:00 2001 From: Gursheen Kaur Anand <40693548+GursheenK@users.noreply.github.com> Date: Tue, 27 Feb 2024 16:04:34 +0530 Subject: [PATCH 370/675] chore: resolve conflicts --- .../purchase_invoice/purchase_invoice.py | 174 ------------------ 1 file changed, 174 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index c04f8d40364..f6fcd7e70e0 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -54,180 +54,6 @@ form_grid_templates = {"items": "templates/form_grid/item_grid.html"} class PurchaseInvoice(BuyingController): -<<<<<<< HEAD -======= - # 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.accounts.doctype.advance_tax.advance_tax import AdvanceTax - from erpnext.accounts.doctype.payment_schedule.payment_schedule import PaymentSchedule - from erpnext.accounts.doctype.pricing_rule_detail.pricing_rule_detail import PricingRuleDetail - from erpnext.accounts.doctype.purchase_invoice_advance.purchase_invoice_advance import ( - PurchaseInvoiceAdvance, - ) - from erpnext.accounts.doctype.purchase_invoice_item.purchase_invoice_item import ( - PurchaseInvoiceItem, - ) - from erpnext.accounts.doctype.purchase_taxes_and_charges.purchase_taxes_and_charges import ( - PurchaseTaxesandCharges, - ) - from erpnext.accounts.doctype.tax_withheld_vouchers.tax_withheld_vouchers import ( - TaxWithheldVouchers, - ) - from erpnext.buying.doctype.purchase_receipt_item_supplied.purchase_receipt_item_supplied import ( - PurchaseReceiptItemSupplied, - ) - - additional_discount_percentage: DF.Float - address_display: DF.SmallText | None - advance_tax: DF.Table[AdvanceTax] - advances: DF.Table[PurchaseInvoiceAdvance] - against_expense_account: DF.SmallText | None - allocate_advances_automatically: DF.Check - amended_from: DF.Link | None - apply_discount_on: DF.Literal["", "Grand Total", "Net Total"] - apply_tds: DF.Check - auto_repeat: DF.Link | None - base_discount_amount: DF.Currency - base_grand_total: DF.Currency - base_in_words: DF.Data | None - base_net_total: DF.Currency - base_paid_amount: DF.Currency - base_rounded_total: DF.Currency - base_rounding_adjustment: DF.Currency - base_tax_withholding_net_total: DF.Currency - base_taxes_and_charges_added: DF.Currency - base_taxes_and_charges_deducted: DF.Currency - base_total: DF.Currency - base_total_taxes_and_charges: DF.Currency - base_write_off_amount: DF.Currency - bill_date: DF.Date | None - bill_no: DF.Data | None - billing_address: DF.Link | None - billing_address_display: DF.SmallText | None - buying_price_list: DF.Link | None - cash_bank_account: DF.Link | None - clearance_date: DF.Date | None - company: DF.Link | None - contact_display: DF.SmallText | None - contact_email: DF.SmallText | None - contact_mobile: DF.SmallText | None - contact_person: DF.Link | None - conversion_rate: DF.Float - cost_center: DF.Link | None - credit_to: DF.Link - currency: DF.Link | None - disable_rounded_total: DF.Check - discount_amount: DF.Currency - due_date: DF.Date | None - from_date: DF.Date | None - grand_total: DF.Currency - group_same_items: DF.Check - hold_comment: DF.SmallText | None - ignore_default_payment_terms_template: DF.Check - ignore_pricing_rule: DF.Check - in_words: DF.Data | None - incoterm: DF.Link | None - inter_company_invoice_reference: DF.Link | None - is_internal_supplier: DF.Check - is_old_subcontracting_flow: DF.Check - is_opening: DF.Literal["No", "Yes"] - is_paid: DF.Check - is_return: DF.Check - is_subcontracted: DF.Check - items: DF.Table[PurchaseInvoiceItem] - language: DF.Data | None - letter_head: DF.Link | None - mode_of_payment: DF.Link | None - named_place: DF.Data | None - naming_series: DF.Literal["ACC-PINV-.YYYY.-", "ACC-PINV-RET-.YYYY.-"] - net_total: DF.Currency - on_hold: DF.Check - only_include_allocated_payments: DF.Check - other_charges_calculation: DF.LongText | None - outstanding_amount: DF.Currency - paid_amount: DF.Currency - party_account_currency: DF.Link | None - payment_schedule: DF.Table[PaymentSchedule] - payment_terms_template: DF.Link | None - per_received: DF.Percent - plc_conversion_rate: DF.Float - posting_date: DF.Date - posting_time: DF.Time | None - price_list_currency: DF.Link | None - pricing_rules: DF.Table[PricingRuleDetail] - project: DF.Link | None - rejected_warehouse: DF.Link | None - release_date: DF.Date | None - remarks: DF.SmallText | None - repost_required: DF.Check - represents_company: DF.Link | None - return_against: DF.Link | None - rounded_total: DF.Currency - rounding_adjustment: DF.Currency - scan_barcode: DF.Data | None - select_print_heading: DF.Link | None - set_from_warehouse: DF.Link | None - set_posting_time: DF.Check - set_warehouse: DF.Link | None - shipping_address: DF.Link | None - shipping_address_display: DF.SmallText | None - shipping_rule: DF.Link | None - status: DF.Literal[ - "", - "Draft", - "Return", - "Debit Note Issued", - "Submitted", - "Paid", - "Partly Paid", - "Unpaid", - "Overdue", - "Cancelled", - "Internal Transfer", - ] - subscription: DF.Link | None - supplied_items: DF.Table[PurchaseReceiptItemSupplied] - supplier: DF.Link - supplier_address: DF.Link | None - supplier_group: DF.Link | None - supplier_name: DF.Data | None - supplier_warehouse: DF.Link | None - tax_category: DF.Link | None - tax_id: DF.ReadOnly | None - tax_withheld_vouchers: DF.Table[TaxWithheldVouchers] - tax_withholding_category: DF.Link | None - tax_withholding_net_total: DF.Currency - taxes: DF.Table[PurchaseTaxesandCharges] - taxes_and_charges: DF.Link | None - taxes_and_charges_added: DF.Currency - taxes_and_charges_deducted: DF.Currency - tc_name: DF.Link | None - terms: DF.TextEditor | None - title: DF.Data | None - to_date: DF.Date | None - total: DF.Currency - total_advance: DF.Currency - total_net_weight: DF.Float - total_qty: DF.Float - total_taxes_and_charges: DF.Currency - unrealized_profit_loss_account: DF.Link | None - update_billed_amount_in_purchase_order: DF.Check - update_billed_amount_in_purchase_receipt: DF.Check - update_stock: DF.Check - use_company_roundoff_cost_center: DF.Check - use_transaction_date_exchange_rate: DF.Check - write_off_account: DF.Link | None - write_off_amount: DF.Currency - write_off_cost_center: DF.Link | None - # end: auto-generated types - ->>>>>>> 9f6535472d (feat: update billed amount in PO and PR) def __init__(self, *args, **kwargs): super(PurchaseInvoice, self).__init__(*args, **kwargs) self.status_updater = [ From 22db6f6b72ae80126b31c84eaa48b7e80e6e68a1 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Tue, 27 Feb 2024 16:51:48 +0530 Subject: [PATCH 371/675] fix: test for plaid bank account validation --- .../doctype/plaid_settings/test_plaid_settings.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py b/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py index 302bdc436c6..7312d8a5ad4 100644 --- a/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py +++ b/erpnext/erpnext_integrations/doctype/plaid_settings/test_plaid_settings.py @@ -42,7 +42,7 @@ class TestPlaidSettings(unittest.TestCase): add_account_subtype("loan") self.assertEqual(frappe.get_doc("Bank Account Subtype", "loan").name, "loan") - def test_default_bank_account(self): + def test_parent_bank_account_validation(self): if not frappe.db.exists("Bank", "Citi"): frappe.get_doc({"doctype": "Bank", "bank_name": "Citi"}).insert() @@ -70,12 +70,19 @@ class TestPlaidSettings(unittest.TestCase): bank = json.dumps(frappe.get_doc("Bank", "Citi").as_dict(), default=json_handler) company = frappe.db.get_single_value("Global Defaults", "default_company") - frappe.db.set_value("Company", company, "default_bank_account", None) + group_bank_account = frappe.db.get_all( + "Account", {"company": company, "account_type": "Bank", "is_group": 1}, pluck="name" + ) + if group_bank_account: + frappe.db.set_value("Account", group_bank_account[0], "disabled", 1) self.assertRaises( frappe.ValidationError, add_bank_accounts, response=bank_accounts, bank=bank, company=company ) + if group_bank_account: + frappe.db.set_value("Account", group_bank_account[0], "disabled", 0) + def test_new_transaction(self): if not frappe.db.exists("Bank", "Citi"): frappe.get_doc({"doctype": "Bank", "bank_name": "Citi"}).insert() From 9a85ab45e7233aa7a66884f04044ffb404d4e0f1 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 27 Feb 2024 17:18:24 +0530 Subject: [PATCH 372/675] fix: parent warehouse checks in the production plan for sub-assemblies (backport #40150) (#40156) fix: parent warehouse checks in the production plan for sub-assemblies (cherry picked from commit 6f5815e44f0532667509e64bc0266769d794c9be) Co-authored-by: rohitwaghchaure --- .../production_plan/production_plan.js | 6 ++ .../production_plan/production_plan.json | 4 +- .../production_plan/production_plan.py | 82 +++++++++++-------- .../production_plan/test_production_plan.py | 44 ++++++++++ .../production_plan_item.json | 8 +- .../production_plan_sub_assembly_item.json | 6 +- 6 files changed, 112 insertions(+), 38 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js index c9c474db7f0..667ece2077e 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.js +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js @@ -518,6 +518,12 @@ frappe.ui.form.on("Production Plan Sales Order", { } }); +frappe.ui.form.on("Production Plan Sub Assembly Item", { + fg_warehouse(frm, cdt, cdn) { + erpnext.utils.copy_value_in_all_rows(frm.doc, cdt, cdn, "sub_assembly_items", "fg_warehouse"); + }, +}) + frappe.tour['Production Plan'] = [ { fieldname: "get_items_from", diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json index 54c3893928b..84bbad58c38 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.json +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json @@ -421,9 +421,11 @@ "fieldtype": "Column Break" }, { + "description": "When a parent warehouse is chosen, the system conducts stock checks against the associated child warehouses", "fieldname": "sub_assembly_warehouse", "fieldtype": "Link", "label": "Sub Assembly Warehouse", + "mandatory_depends_on": "eval:doc.skip_available_sub_assembly_item === 1", "options": "Warehouse" }, { @@ -437,7 +439,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2024-02-11 15:42:47.642481", + "modified": "2024-02-27 13:34:20.692211", "modified_by": "Administrator", "module": "Manufacturing", "name": "Production Plan", diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 5495c49803d..7efc4f75e5b 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -817,8 +817,8 @@ class ProductionPlan(Document): sub_assembly_items_store = [] # temporary store to process all subassembly items for row in self.po_items: - if self.skip_available_sub_assembly_item and not row.warehouse: - frappe.throw(_("Row #{0}: Please select the FG Warehouse in Assembly Items").format(row.idx)) + if self.skip_available_sub_assembly_item and not self.sub_assembly_warehouse: + frappe.throw(_("Row #{0}: Please select the Sub Assembly Warehouse").format(row.idx)) if not row.item_code: frappe.throw(_("Row #{0}: Please select Item Code in Assembly Items").format(row.idx)) @@ -828,15 +828,24 @@ class ProductionPlan(Document): bom_data = [] - warehouse = ( - (self.sub_assembly_warehouse or row.warehouse) - if self.skip_available_sub_assembly_item - else None - ) + warehouse = (self.sub_assembly_warehouse) if self.skip_available_sub_assembly_item else None get_sub_assembly_items(row.bom_no, bom_data, row.planned_qty, self.company, warehouse=warehouse) self.set_sub_assembly_items_based_on_level(row, bom_data, manufacturing_type) sub_assembly_items_store.extend(bom_data) + if not sub_assembly_items_store and self.skip_available_sub_assembly_item: + message = ( + _( + "As there are sufficient Sub Assembly Items, Work Order is not required for Warehouse {0}." + ).format(self.sub_assembly_warehouse) + + "

    " + ) + message += _( + "If you still want to proceed, please disable 'Skip Available Sub Assembly Items' checkbox." + ) + + frappe.msgprint(message, title=_("Note")) + if self.combine_sub_items: # Combine subassembly items sub_assembly_items_store = self.combine_subassembly_items(sub_assembly_items_store) @@ -849,15 +858,19 @@ class ProductionPlan(Document): def set_sub_assembly_items_based_on_level(self, row, bom_data, manufacturing_type=None): "Modify bom_data, set additional details." + is_group_warehouse = frappe.db.get_value("Warehouse", self.sub_assembly_warehouse, "is_group") + for data in bom_data: data.qty = data.stock_qty data.production_plan_item = row.name - data.fg_warehouse = self.sub_assembly_warehouse or row.warehouse data.schedule_date = row.planned_start_date data.type_of_manufacturing = manufacturing_type or ( "Subcontract" if data.is_sub_contracted_item else "In House" ) + if not is_group_warehouse: + data.fg_warehouse = self.sub_assembly_warehouse + def set_default_supplier_for_subcontracting_order(self): items = [ d.production_item for d in self.sub_assembly_items if d.type_of_manufacturing == "Subcontract" @@ -1401,7 +1414,7 @@ def get_items_for_material_requests(doc, warehouses=None, get_parent_warehouse_d so_item_details = frappe._dict() sub_assembly_items = {} - if doc.get("skip_available_sub_assembly_item"): + if doc.get("skip_available_sub_assembly_item") and doc.get("sub_assembly_items"): for d in doc.get("sub_assembly_items"): sub_assembly_items.setdefault((d.get("production_item"), d.get("bom_no")), d.get("qty")) @@ -1613,34 +1626,37 @@ def get_sub_assembly_items(bom_no, bom_data, to_produce_qty, company, warehouse= stock_qty = (d.stock_qty / d.parent_bom_qty) * flt(to_produce_qty) if warehouse: - bin_dict = get_bin_details(d, company, for_warehouse=warehouse) + bin_details = get_bin_details(d, company, for_warehouse=warehouse) - if bin_dict and bin_dict[0].projected_qty > 0: - if bin_dict[0].projected_qty > stock_qty: - continue - else: - stock_qty = stock_qty - bin_dict[0].projected_qty + for _bin_dict in bin_details: + if _bin_dict.projected_qty > 0: + if _bin_dict.projected_qty > stock_qty: + stock_qty = 0 + continue + else: + stock_qty = stock_qty - _bin_dict.projected_qty - bom_data.append( - frappe._dict( - { - "parent_item_code": parent_item_code, - "description": d.description, - "production_item": d.item_code, - "item_name": d.item_name, - "stock_uom": d.stock_uom, - "uom": d.stock_uom, - "bom_no": d.value, - "is_sub_contracted_item": d.is_sub_contracted_item, - "bom_level": indent, - "indent": indent, - "stock_qty": stock_qty, - } + if stock_qty > 0: + bom_data.append( + frappe._dict( + { + "parent_item_code": parent_item_code, + "description": d.description, + "production_item": d.item_code, + "item_name": d.item_name, + "stock_uom": d.stock_uom, + "uom": d.stock_uom, + "bom_no": d.value, + "is_sub_contracted_item": d.is_sub_contracted_item, + "bom_level": indent, + "indent": indent, + "stock_qty": stock_qty, + } + ) ) - ) - if d.value: - get_sub_assembly_items(d.value, bom_data, stock_qty, company, warehouse, indent=indent + 1) + if d.value: + get_sub_assembly_items(d.value, bom_data, stock_qty, company, warehouse, indent=indent + 1) def set_default_warehouses(row, default_warehouses): diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py index 3d878c140f8..7e33eb80f7d 100644 --- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py @@ -1194,6 +1194,7 @@ class TestProductionPlan(FrappeTestCase): ignore_existing_ordered_qty=1, do_not_submit=1, skip_available_sub_assembly_item=1, + sub_assembly_warehouse="_Test Warehouse - _TC", warehouse="_Test Warehouse - _TC", ) @@ -1327,6 +1328,7 @@ class TestProductionPlan(FrappeTestCase): ignore_existing_ordered_qty=1, do_not_submit=1, skip_available_sub_assembly_item=1, + sub_assembly_warehouse="_Test Warehouse - _TC", warehouse="_Test Warehouse - _TC", ) @@ -1579,6 +1581,48 @@ class TestProductionPlan(FrappeTestCase): for row in work_orders: self.assertEqual(row.qty, wo_qty[row.name]) + def test_parent_warehouse_for_sub_assembly_items(self): + from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom + from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse + + parent_warehouse = "_Test Warehouse Group - _TC" + sub_warehouse = create_warehouse("Sub Warehouse", company="_Test Company") + + fg_item = make_item(properties={"is_stock_item": 1}).name + sf_item = make_item(properties={"is_stock_item": 1}).name + rm_item = make_item(properties={"is_stock_item": 1}).name + + bom_tree = {fg_item: {sf_item: {rm_item: {}}}} + create_nested_bom(bom_tree, prefix="") + + pln = create_production_plan( + item_code=fg_item, + planned_qty=10, + warehouse="_Test Warehouse - _TC", + sub_assembly_warehouse=parent_warehouse, + skip_available_sub_assembly_item=1, + do_not_submit=1, + skip_getting_mr_items=1, + ) + + pln.get_sub_assembly_items() + + for row in pln.sub_assembly_items: + self.assertFalse(row.fg_warehouse) + self.assertEqual(row.production_item, sf_item) + self.assertEqual(row.qty, 10.0) + + make_stock_entry(item_code=sf_item, qty=5, target=sub_warehouse, rate=100) + + pln.sub_assembly_items = [] + pln.get_sub_assembly_items() + + self.assertEqual(pln.sub_assembly_warehouse, parent_warehouse) + for row in pln.sub_assembly_items: + self.assertFalse(row.fg_warehouse) + self.assertEqual(row.production_item, sf_item) + self.assertEqual(row.qty, 5.0) + def create_production_plan(**args): """ diff --git a/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.json b/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.json index 0688278e091..78a389760a7 100644 --- a/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.json +++ b/erpnext/manufacturing/doctype/production_plan_item/production_plan_item.json @@ -11,6 +11,7 @@ "bom_no", "column_break_6", "planned_qty", + "stock_uom", "warehouse", "planned_start_date", "section_break_9", @@ -18,7 +19,6 @@ "ordered_qty", "column_break_17", "description", - "stock_uom", "produced_qty", "reference_section", "sales_order", @@ -65,6 +65,7 @@ "width": "100px" }, { + "columns": 1, "fieldname": "planned_qty", "fieldtype": "Float", "in_list_view": 1, @@ -80,6 +81,7 @@ "fieldtype": "Column Break" }, { + "columns": 2, "fieldname": "warehouse", "fieldtype": "Link", "in_list_view": 1, @@ -141,8 +143,10 @@ "width": "200px" }, { + "columns": 1, "fieldname": "stock_uom", "fieldtype": "Link", + "in_list_view": 1, "label": "UOM", "oldfieldname": "stock_uom", "oldfieldtype": "Data", @@ -216,7 +220,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2022-11-25 14:15:40.061514", + "modified": "2024-02-27 13:24:43.571844", "modified_by": "Administrator", "module": "Manufacturing", "name": "Production Plan Item", diff --git a/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json b/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json index aff740b732e..7965965d2b6 100644 --- a/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json +++ b/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json @@ -101,7 +101,6 @@ "columns": 1, "fieldname": "bom_level", "fieldtype": "Int", - "in_list_view": 1, "label": "Level (BOM)", "read_only": 1 }, @@ -149,8 +148,10 @@ "label": "Indent" }, { + "columns": 2, "fieldname": "fg_warehouse", "fieldtype": "Link", + "in_list_view": 1, "label": "Target Warehouse", "options": "Warehouse" }, @@ -170,6 +171,7 @@ "options": "Supplier" }, { + "columns": 1, "fieldname": "schedule_date", "fieldtype": "Datetime", "in_list_view": 1, @@ -207,7 +209,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-11-03 13:33:42.959387", + "modified": "2024-02-27 13:45:17.422435", "modified_by": "Administrator", "module": "Manufacturing", "name": "Production Plan Sub Assembly Item", From 88af431eeaf2fbe90f762e3d1fea4ef61ff8668e Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Tue, 27 Feb 2024 17:20:01 +0530 Subject: [PATCH 373/675] chore: semantic commits bump node version to 20 --- .github/workflows/semantic-commits.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/semantic-commits.yml b/.github/workflows/semantic-commits.yml index 1744bc33a9e..da3d564e66c 100644 --- a/.github/workflows/semantic-commits.yml +++ b/.github/workflows/semantic-commits.yml @@ -21,7 +21,7 @@ jobs: - uses: actions/setup-node@v3 with: - node-version: 14 + node-version: 20 check-latest: true - name: Check commit titles From 8c772cfb9f2c01f2e1dbf514fb819b3d0f3836f7 Mon Sep 17 00:00:00 2001 From: Smit Vora Date: Tue, 27 Feb 2024 20:33:02 +0530 Subject: [PATCH 374/675] fix: add flags for repost to ensure correct accounting from India Compliance App --- .../repost_accounting_ledger/repost_accounting_ledger.py | 2 ++ .../doctype/repost_item_valuation/repost_item_valuation.py | 1 + 2 files changed, 3 insertions(+) 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 9211b286c7d..28355964cfa 100644 --- a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py +++ b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py @@ -70,6 +70,7 @@ class RepostAccountingLedger(Document): ).append(gle.update({"old": True})) def generate_preview_data(self): + frappe.flags.through_repost_accounting_ledger = True self.gl_entries = [] self.get_existing_ledger_entries() for x in self.vouchers: @@ -123,6 +124,7 @@ class RepostAccountingLedger(Document): @frappe.whitelist() def start_repost(account_repost_doc=str) -> None: + frappe.flags.through_repost_accounting_ledger = True if account_repost_doc: repost_doc = frappe.get_doc("Repost Accounting Ledger", account_repost_doc) 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 6ffb34dc135..5d087a46242 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py @@ -226,6 +226,7 @@ def on_doctype_update(): def repost(doc): try: + frappe.flags.through_repost_item_valuation = True if not frappe.db.exists("Repost Item Valuation", doc.name): return From 30315aba37bb86eb2ecf2805c77b9ef2316d26ba Mon Sep 17 00:00:00 2001 From: ljain112 Date: Mon, 26 Feb 2024 18:41:33 +0530 Subject: [PATCH 375/675] fix: default taxable value for item not found in item list (cherry picked from commit 5885978fc2f829b064568e8e2b6a28143f69218c) --- erpnext/controllers/taxes_and_totals.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index e24618af103..2881c15a14d 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -1005,7 +1005,7 @@ def get_itemised_tax_breakup_data(doc): for item_code, taxes in itemised_tax.items(): itemised_tax_data.append( frappe._dict( - {"item": item_code, "taxable_amount": itemised_taxable_amount.get(item_code), **taxes} + {"item": item_code, "taxable_amount": itemised_taxable_amount.get(item_code, 0), **taxes} ) ) From de6957262d6129d9a084edcbdb92b8ce17f5f20b Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 28 Feb 2024 05:13:29 +0000 Subject: [PATCH 376/675] chore(release): Bumped to Version 14.64.0 # [14.64.0](https://github.com/frappe/erpnext/compare/v14.63.3...v14.64.0) (2024-02-28) ### Bug Fixes * add flags for repost to ensure correct accounting from India Compliance App ([8c772cf](https://github.com/frappe/erpnext/commit/8c772cfb9f2c01f2e1dbf514fb819b3d0f3836f7)) * amount label according to party type ([bb3a7cd](https://github.com/frappe/erpnext/commit/bb3a7cdb3dfa6db1a219fc80d6c93f6e9f28001d)) * Cannot read properties of undefined (backport [#40081](https://github.com/frappe/erpnext/issues/40081)) ([#40082](https://github.com/frappe/erpnext/issues/40082)) ([579b27d](https://github.com/frappe/erpnext/commit/579b27d0102c86c42c32b61c2adbd16280d45532)) * capacity planning issue in the job card (backport [#40092](https://github.com/frappe/erpnext/issues/40092)) ([#40100](https://github.com/frappe/erpnext/issues/40100)) ([8a573a1](https://github.com/frappe/erpnext/commit/8a573a1aff13dd878e5c990fd0b2d8f492ed8ad2)) * check_credit_limit on_update_after_submit of Sales Order ([3112fca](https://github.com/frappe/erpnext/commit/3112fca087153d95699ff9ecaa1fc1215d65fa91)) * communication_date in party dashboards (backport [#40005](https://github.com/frappe/erpnext/issues/40005)) ([#40020](https://github.com/frappe/erpnext/issues/40020)) ([b364fe7](https://github.com/frappe/erpnext/commit/b364fe704723cfc0e508c8ff9f39645a8625b703)) * Completed Work Orders report not working ([c8e1409](https://github.com/frappe/erpnext/commit/c8e1409b803e42f6ecedb4c2bd56dffb147fdbf9)) * Cr/Dr notes with POS Payments ([e5098c5](https://github.com/frappe/erpnext/commit/e5098c521ba5eb0371a902b671b0f3e0f5213d86)) * default taxable value for item not found in item list ([30315ab](https://github.com/frappe/erpnext/commit/30315aba37bb86eb2ecf2805c77b9ef2316d26ba)) * Delete linked asset movement record on cancellation of purchase receipt/invoice ([11bddc1](https://github.com/frappe/erpnext/commit/11bddc14bb3169fed080ea7451b6573f1b92a980)) * Delete orphaned asset movement item records ([46cd929](https://github.com/frappe/erpnext/commit/46cd929d00a0a71ef067ef66490d999921884110)) * delete PLE containing invoice in against ([08459ce](https://github.com/frappe/erpnext/commit/08459cef4ab4399ec6cce8590c0e4aae7a3d72c7)) * do not make MR against raw materials of available sub assemblies (backport [#40085](https://github.com/frappe/erpnext/issues/40085)) ([#40086](https://github.com/frappe/erpnext/issues/40086)) ([e461185](https://github.com/frappe/erpnext/commit/e4611853171bc1fdcd68a3de5f373e82cae4d108)) * incorrect item name in MR (backport [#40018](https://github.com/frappe/erpnext/issues/40018)) ([#40023](https://github.com/frappe/erpnext/issues/40023)) ([079ba76](https://github.com/frappe/erpnext/commit/079ba7670c83b8530b196ae948fb0c402c93f8a3)) * linter issue ([ea1a0b3](https://github.com/frappe/erpnext/commit/ea1a0b3a28cdb4cc2f6e41c748e9577a49470ac8)) * linting issue ([65a2adf](https://github.com/frappe/erpnext/commit/65a2adfc551546744ad97194b5d72fc0b8f42237)) * linting issue ([e289aef](https://github.com/frappe/erpnext/commit/e289aef1b4a6a0ab4827214422486f16efd7d4cd)) * negative stock error while making stock reconciliation (backport [#40016](https://github.com/frappe/erpnext/issues/40016)) ([#40025](https://github.com/frappe/erpnext/issues/40025)) ([865cba4](https://github.com/frappe/erpnext/commit/865cba406f7826501e33853052d15665d6b72448)) * on unreconciliation, update advance paid ([fe88a11](https://github.com/frappe/erpnext/commit/fe88a110b1c6c7eebc015f4ad90319fcdf1246b1)) * only check for delinked PLEs ([3d243a7](https://github.com/frappe/erpnext/commit/3d243a7fa5918572eff91c6279c459c8b4c725d6)) * only consider contributed qty towards achieved targets ([0484857](https://github.com/frappe/erpnext/commit/0484857402b987212084a083f15cb759103dca82)) * parent warehouse checks in the production plan for sub-assemblies (backport [#40150](https://github.com/frappe/erpnext/issues/40150)) ([#40156](https://github.com/frappe/erpnext/issues/40156)) ([9a85ab4](https://github.com/frappe/erpnext/commit/9a85ab45e7233aa7a66884f04044ffb404d4e0f1)) * remove cancelled payment entry from Payment Period Based On Invoice Date ([2fc490a](https://github.com/frappe/erpnext/commit/2fc490a5d77a3c25d1e17124bdd626442257da4d)) * remove cancelled payment entry from PPBOID report ([a1c0c23](https://github.com/frappe/erpnext/commit/a1c0c2359f5a3b78006c77f2b8e3eac9c488fa21)) * remove config for default bank account in test ([ad60677](https://github.com/frappe/erpnext/commit/ad6067795f87140c3252c9053d01e22f735dce49)) * sales funnel text color in light/dark theme ([#40132](https://github.com/frappe/erpnext/issues/40132)) ([193cd0d](https://github.com/frappe/erpnext/commit/193cd0db96d45c9bdd377a29e7ae530ed18ec0aa)) * skip max discount validation for rate adjustment ([8c7d0d4](https://github.com/frappe/erpnext/commit/8c7d0d4b85daa7749e5dee1dbe1fc0fcc6f4c158)) * skip SO & DN validation for debit note ([882cf98](https://github.com/frappe/erpnext/commit/882cf98288103ad6ec731a4cfc58357471337287)) * test for plaid bank account validation ([22db6f6](https://github.com/frappe/erpnext/commit/22db6f6b72ae80126b31c84eaa48b7e80e6e68a1)) * timesheet per billed state edge case (backport [#40010](https://github.com/frappe/erpnext/issues/40010)) ([#40028](https://github.com/frappe/erpnext/issues/40028)) ([2a2f314](https://github.com/frappe/erpnext/commit/2a2f314821d38ede4c39ef750ed34503748d623a)) * translatable columns in Sales Pipeline Analytics report ([3c170b9](https://github.com/frappe/erpnext/commit/3c170b980575f76a3caf3624f0cbee8845a0044f)) * unique gl account for plaid bank accounts ([8638d14](https://github.com/frappe/erpnext/commit/8638d14babaf4c846a7f6222c7018fc8f59545b2)) * use correct variable name for hotfix branches ([812eebf](https://github.com/frappe/erpnext/commit/812eebf1ab7c71ca8daea273684dec5d0f4a539b)) * Webpages not working without login ([d6fad08](https://github.com/frappe/erpnext/commit/d6fad08d20c6d29e799a9dc43c75db4c0c6926fa)) ### Features * show contributed qty in transaction summary ([89d1942](https://github.com/frappe/erpnext/commit/89d19422d1f0236c10ae5165fc682c431c3d076c)) * update billed amount in PO and PR ([019d8f9](https://github.com/frappe/erpnext/commit/019d8f92fed10d479870ecea94b6abc2f7c508dd)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 96f8ec27ec1..17db2feece6 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.63.3" +__version__ = "14.64.0" def get_default_company(user=None): From c8b817c87a9e510a865c48ad9e2890c892246e8e Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Wed, 28 Feb 2024 18:01:27 +0530 Subject: [PATCH 377/675] fix: make warning for previously existing SO an alert (cherry picked from commit 24dcd64c16eaea2f7ab0585235dfaf64cfe47bf3) --- erpnext/selling/doctype/sales_order/sales_order.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 82680368181..3821a237161 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -102,7 +102,8 @@ class SalesOrder(SellingController): frappe.msgprint( _("Warning: Sales Order {0} already exists against Customer's Purchase Order {1}").format( frappe.bold(so[0][0]), frappe.bold(self.po_no) - ) + ), + alert=True, ) else: frappe.throw( From 035c90c3b84934711f82ee61242b737a7d1a1290 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Thu, 29 Feb 2024 11:34:23 +0530 Subject: [PATCH 378/675] fix(setup): avoid duplicate entry for Analytics role (backport #40183) (#40184) fix(setup): avoid duplicate entry for Analytics role (#40183) (cherry picked from commit 29f91a7919210640f4d460167ee215817689ab52) Co-authored-by: Rucha Mahabal --- erpnext/setup/install.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py index 71b1ca7c058..d217bfa5219 100644 --- a/erpnext/setup/install.py +++ b/erpnext/setup/install.py @@ -19,7 +19,9 @@ default_mail_footer = """
    Date: Fri, 26 Jan 2024 21:46:19 +0530 Subject: [PATCH 379/675] fix: provisional reverse entry amount (cherry picked from commit 3e59c668064c037548c7c29c4d944efbcb37ff7b) --- .../doctype/purchase_invoice/purchase_invoice.py | 6 +++--- .../stock/doctype/purchase_receipt/purchase_receipt.py | 9 ++++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index f6fcd7e70e0..7ad9417173e 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -869,7 +869,7 @@ class PurchaseInvoice(BuyingController): purchase_receipt_doc_map[item.purchase_receipt] = purchase_receipt_doc # Post reverse entry for Stock-Received-But-Not-Billed if it is booked in Purchase Receipt - expense_booked_in_pr = frappe.db.get_value( + expense_booked_in_pr, credit = frappe.db.get_value( "GL Entry", { "is_cancelled": 0, @@ -878,13 +878,13 @@ class PurchaseInvoice(BuyingController): "voucher_detail_no": item.pr_detail, "account": provisional_account, }, - ["name"], + ["name", "credit"], ) if expense_booked_in_pr: # Intentionally passing purchase invoice item to handle partial billing purchase_receipt_doc.add_provisional_gl_entry( - item, gl_entries, self.posting_date, provisional_account, reverse=1 + item, gl_entries, self.posting_date, provisional_account, reverse=1, item_amount=credit ) if not self.is_internal_transfer(): diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index bf5f9f4d1d4..79e6ab84d95 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -615,16 +615,19 @@ class PurchaseReceipt(BuyingController): ) def add_provisional_gl_entry( - self, item, gl_entries, posting_date, provisional_account, reverse=0 + self, item, gl_entries, posting_date, provisional_account, reverse=0, item_amount=None ): credit_currency = get_account_currency(provisional_account) expense_account = item.expense_account debit_currency = get_account_currency(item.expense_account) remarks = self.get("remarks") or _("Accounting Entry for Service") multiplication_factor = 1 + amount = item.base_amount if reverse: multiplication_factor = -1 + # Post reverse entry for previously posted amount + amount = item_amount expense_account = frappe.db.get_value( "Purchase Receipt Item", {"name": item.get("pr_detail")}, ["expense_account"] ) @@ -634,7 +637,7 @@ class PurchaseReceipt(BuyingController): account=provisional_account, cost_center=item.cost_center, debit=0.0, - credit=multiplication_factor * item.base_amount, + credit=multiplication_factor * amount, remarks=remarks, against_account=expense_account, account_currency=credit_currency, @@ -648,7 +651,7 @@ class PurchaseReceipt(BuyingController): gl_entries=gl_entries, account=expense_account, cost_center=item.cost_center, - debit=multiplication_factor * item.base_amount, + debit=multiplication_factor * amount, credit=0.0, remarks=remarks, against_account=provisional_account, From 5f23614960aa0f41b11d8b5f651d85cbfbf2753c Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Mon, 29 Jan 2024 11:34:06 +0530 Subject: [PATCH 380/675] fix: handle partial invoice against provisional entry (cherry picked from commit 2a46799188a0267d76bdd292062f5a5f82429493) --- .../purchase_invoice/purchase_invoice.py | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 7ad9417173e..bede2641f15 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -859,9 +859,12 @@ class PurchaseInvoice(BuyingController): if provisional_accounting_for_non_stock_items: if item.purchase_receipt: - provisional_account = frappe.db.get_value( - "Purchase Receipt Item", item.pr_detail, "provisional_expense_account" - ) or self.get_company_default("default_provisional_account") + provisional_account, pr_base_rate = frappe.get_cached_value( + "Purchase Receipt Item", item.pr_detail, ["provisional_expense_account", "base_rate"] + ) + provisional_account = provisional_account or self.get_company_default( + "default_provisional_account" + ) purchase_receipt_doc = purchase_receipt_doc_map.get(item.purchase_receipt) if not purchase_receipt_doc: @@ -869,7 +872,7 @@ class PurchaseInvoice(BuyingController): purchase_receipt_doc_map[item.purchase_receipt] = purchase_receipt_doc # Post reverse entry for Stock-Received-But-Not-Billed if it is booked in Purchase Receipt - expense_booked_in_pr, credit = frappe.db.get_value( + expense_booked_in_pr = frappe.db.get_value( "GL Entry", { "is_cancelled": 0, @@ -878,13 +881,18 @@ class PurchaseInvoice(BuyingController): "voucher_detail_no": item.pr_detail, "account": provisional_account, }, - ["name", "credit"], + "name", ) if expense_booked_in_pr: # Intentionally passing purchase invoice item to handle partial billing purchase_receipt_doc.add_provisional_gl_entry( - item, gl_entries, self.posting_date, provisional_account, reverse=1, item_amount=credit + item, + gl_entries, + self.posting_date, + provisional_account, + reverse=1, + item_amount=(item.qty * pr_base_rate), ) if not self.is_internal_transfer(): From 14d9d29913e78a585230763b3f25a2e903c263a7 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Mon, 5 Feb 2024 15:52:46 +0530 Subject: [PATCH 381/675] fix: over billing qty along with rate (cherry picked from commit cc96d2b50cbce3c137eebd754956344718cd1771) --- .../accounts/doctype/purchase_invoice/purchase_invoice.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index bede2641f15..cfaaf767786 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -859,8 +859,10 @@ class PurchaseInvoice(BuyingController): if provisional_accounting_for_non_stock_items: if item.purchase_receipt: - provisional_account, pr_base_rate = frappe.get_cached_value( - "Purchase Receipt Item", item.pr_detail, ["provisional_expense_account", "base_rate"] + provisional_account, pr_qty, pr_base_rate = frappe.get_cached_value( + "Purchase Receipt Item", + item.pr_detail, + ["provisional_expense_account", "qty", "base_rate"], ) provisional_account = provisional_account or self.get_company_default( "default_provisional_account" @@ -892,7 +894,7 @@ class PurchaseInvoice(BuyingController): self.posting_date, provisional_account, reverse=1, - item_amount=(item.qty * pr_base_rate), + item_amount=(min(item.qty, pr_qty) * pr_base_rate), ) if not self.is_internal_transfer(): From aa52cd67bdeddc54303151180529250519f882c8 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Mon, 5 Feb 2024 15:55:17 +0530 Subject: [PATCH 382/675] test: overbilling for provisional accounting (cherry picked from commit ff3ca50a4b1787cd557dffefda4997a7f20cd643) --- .../purchase_invoice/test_purchase_invoice.py | 89 ++++++++++++++++--- 1 file changed, 75 insertions(+), 14 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 6942e28726f..1ce5a81eae5 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1520,18 +1520,7 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin): self.assertEqual(payment_entry.taxes[0].allocated_amount, 0) def test_provisional_accounting_entry(self): - create_item("_Test Non Stock Item", is_stock_item=0) - - provisional_account = create_account( - account_name="Provision Account", - parent_account="Current Liabilities - _TC", - company="_Test Company", - ) - - company = frappe.get_doc("Company", "_Test Company") - company.enable_provisional_accounting_for_non_stock_items = 1 - company.default_provisional_account = provisional_account - company.save() + setup_provisional_accounting() pr = make_purchase_receipt( item_code="_Test Non Stock Item", posting_date=add_days(nowdate(), -2) @@ -1575,8 +1564,58 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin): self, pr.name, expected_gle_for_purchase_receipt_post_pi_cancel, pr.posting_date ) - company.enable_provisional_accounting_for_non_stock_items = 0 - company.save() + toggle_provisional_accounting_setting() + + def test_provisional_accounting_entry_for_over_billing(self): + setup_provisional_accounting() + + # Configure Buying Settings to allow rate change + frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 0) + + # Create PR: rate = 1000, qty = 5 + pr = make_purchase_receipt( + item_code="_Test Non Stock Item", rate=1000, posting_date=add_days(nowdate(), -2) + ) + + # Overbill PR: rate = 2000, qty = 10 + pi = create_purchase_invoice_from_receipt(pr.name) + pi.set_posting_time = 1 + pi.posting_date = add_days(pr.posting_date, -1) + pi.items[0].qty = 10 + pi.items[0].rate = 2000 + pi.items[0].expense_account = "Cost of Goods Sold - _TC" + pi.save() + pi.submit() + + expected_gle = [ + ["Cost of Goods Sold - _TC", 20000, 0, add_days(pr.posting_date, -1)], + ["Creditors - _TC", 0, 20000, add_days(pr.posting_date, -1)], + ] + + check_gl_entries(self, pi.name, expected_gle, pi.posting_date) + + expected_gle_for_purchase_receipt = [ + ["Provision Account - _TC", 5000, 0, pr.posting_date], + ["_Test Account Cost for Goods Sold - _TC", 0, 5000, pr.posting_date], + ["Provision Account - _TC", 0, 5000, pi.posting_date], + ["_Test Account Cost for Goods Sold - _TC", 5000, 0, pi.posting_date], + ] + + check_gl_entries(self, pr.name, expected_gle_for_purchase_receipt, pr.posting_date) + + # Cancel purchase invoice to check reverse provisional entry cancellation + pi.cancel() + + expected_gle_for_purchase_receipt_post_pi_cancel = [ + ["Provision Account - _TC", 0, 5000, pi.posting_date], + ["_Test Account Cost for Goods Sold - _TC", 5000, 0, pi.posting_date], + ] + + check_gl_entries( + self, pr.name, expected_gle_for_purchase_receipt_post_pi_cancel, pr.posting_date + ) + + toggle_provisional_accounting_setting() def test_adjust_incoming_rate(self): frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 0) @@ -2091,4 +2130,26 @@ def make_purchase_invoice_against_cost_center(**args): return pi +def setup_provisional_accounting(**args): + args = frappe._dict(args) + create_item("_Test Non Stock Item", is_stock_item=0) + company = args.company or "_Test Company" + provisional_account = create_account( + account_name=args.account_name or "Provision Account", + parent_account=args.parent_account or "Current Liabilities - _TC", + company=company, + ) + toggle_provisional_accounting_setting( + enable=1, company=company, provisional_account=provisional_account + ) + + +def toggle_provisional_accounting_setting(**args): + args = frappe._dict(args) + company = frappe.get_doc("Company", args.company or "_Test Company") + company.enable_provisional_accounting_for_non_stock_items = args.enable or 0 + company.default_provisional_account = args.provisional_account + company.save() + + test_records = frappe.get_test_records("Purchase Invoice") From c8b31833f5fff9b77455cf71eac85e5f599947b0 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Mon, 5 Feb 2024 15:58:06 +0530 Subject: [PATCH 383/675] test: partial billing for provisional accounting (cherry picked from commit c5770f2eccc62c27f18a108ad60ae4f42946a9d6) --- .../purchase_invoice/test_purchase_invoice.py | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 1ce5a81eae5..ba97fc685a3 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1617,6 +1617,45 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin): toggle_provisional_accounting_setting() + def test_provisional_accounting_entry_for_partial_billing(self): + setup_provisional_accounting() + + # Configure Buying Settings to allow rate change + frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 0) + + # Create PR: rate = 1000, qty = 5 + pr = make_purchase_receipt( + item_code="_Test Non Stock Item", rate=1000, posting_date=add_days(nowdate(), -2) + ) + + # Partially bill PR: rate = 500, qty = 2 + pi = create_purchase_invoice_from_receipt(pr.name) + pi.set_posting_time = 1 + pi.posting_date = add_days(pr.posting_date, -1) + pi.items[0].qty = 2 + pi.items[0].rate = 500 + pi.items[0].expense_account = "Cost of Goods Sold - _TC" + pi.save() + pi.submit() + + expected_gle = [ + ["Cost of Goods Sold - _TC", 1000, 0, add_days(pr.posting_date, -1)], + ["Creditors - _TC", 0, 1000, add_days(pr.posting_date, -1)], + ] + + check_gl_entries(self, pi.name, expected_gle, pi.posting_date) + + expected_gle_for_purchase_receipt = [ + ["Provision Account - _TC", 5000, 0, pr.posting_date], + ["_Test Account Cost for Goods Sold - _TC", 0, 5000, pr.posting_date], + ["Provision Account - _TC", 0, 1000, pi.posting_date], + ["_Test Account Cost for Goods Sold - _TC", 1000, 0, pi.posting_date], + ] + + check_gl_entries(self, pr.name, expected_gle_for_purchase_receipt, pr.posting_date) + + toggle_provisional_accounting_setting() + def test_adjust_incoming_rate(self): frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 0) From 646b55eeea46f6d9b2597e2beb3a1cdad461df8a Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Thu, 29 Feb 2024 16:44:46 +0100 Subject: [PATCH 384/675] fix(Project): filter department by company (cherry picked from commit 5e736f0d061b05db8cdc0fd324fa3530079e7a07) --- erpnext/projects/doctype/project/project.js | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js index 8f7437fef8a..0cdd9c10e7f 100644 --- a/erpnext/projects/doctype/project/project.js +++ b/erpnext/projects/doctype/project/project.js @@ -35,6 +35,14 @@ frappe.ui.form.on("Project", { }; }); + frm.set_query("department", function (doc) { + return { + filters: { + "company": doc.company, + } + }; + }); + // sales order frm.set_query('sales_order', function () { var filters = { From 1b5a23709a751f925d78c476bd5e51b743135d3f Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 27 Feb 2024 15:34:34 +0530 Subject: [PATCH 385/675] fix: incorrect exchange rate if JE has multi parties (cherry picked from commit 694c17487d76ddc38764d58311ff2153a15d1ee5) --- .../payment_reconciliation/payment_reconciliation.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 5605d068187..33f736fe915 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -566,7 +566,12 @@ class PaymentReconciliation(Document): journals_map = frappe._dict( frappe.db.get_all( "Journal Entry Account", - filters={"parent": ("in", journals), "account": ("in", [self.receivable_payable_account])}, + filters={ + "parent": ("in", journals), + "account": ("in", [self.receivable_payable_account]), + "party_type": self.party_type, + "party": self.party, + }, fields=[ "parent as `name`", "exchange_rate", From e25ec4156e894c855d6b7bef8b9b5868d29420cc Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 28 Feb 2024 17:07:37 +0530 Subject: [PATCH 386/675] fix: don't override reference exchange rate (cherry picked from commit eaac02655b174cca9daa2d16ba85c335e70879c3) --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 7 ++++++- erpnext/accounts/utils.py | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index b89854b172a..d04f0ac7f3c 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -335,7 +335,10 @@ class PaymentEntry(AccountsController): ) def set_missing_ref_details( - self, force: bool = False, update_ref_details_only_for: list | None = None + self, + force: bool = False, + update_ref_details_only_for: list | None = None, + ref_exchange_rate: float | None = None, ) -> None: for d in self.get("references"): if d.allocated_amount: @@ -347,6 +350,8 @@ class PaymentEntry(AccountsController): ref_details = get_reference_details( d.reference_doctype, d.reference_name, self.party_account_currency ) + if ref_exchange_rate: + ref_details.update({"exchange_rate": ref_exchange_rate}) for field, value in ref_details.items(): if d.exchange_gain_loss: diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 280cf338f70..4f1f967f202 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -682,7 +682,7 @@ def update_reference_in_payment_entry( payment_entry.setup_party_account_field() payment_entry.set_missing_values() if not skip_ref_details_update_for_pe: - payment_entry.set_missing_ref_details() + payment_entry.set_missing_ref_details(ref_exchange_rate=d.exchange_rate or None) payment_entry.set_amounts() payment_entry.make_exchange_gain_loss_journal( From 572def058f9a995ff31fe3daac19ea40efdf2e76 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 28 Feb 2024 17:07:57 +0530 Subject: [PATCH 387/675] test: exchange rate fetch on JE with multiple forex parties (cherry picked from commit ed95d41a51687208e603ffc355fa717018d14185) --- .../test_payment_reconciliation.py | 100 ++++++++++++++++++ 1 file changed, 100 insertions(+) diff --git a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py index fb75a0f7caf..eb4396a5c6f 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/test_payment_reconciliation.py @@ -56,6 +56,7 @@ class TestPaymentReconciliation(FrappeTestCase): self.expense_account = "Cost of Goods Sold - _PR" self.debit_to = "Debtors - _PR" self.creditors = "Creditors - _PR" + self.cash = "Cash - _PR" # create bank account if frappe.db.exists("Account", "HDFC - _PR"): @@ -486,6 +487,91 @@ class TestPaymentReconciliation(FrappeTestCase): self.assertEqual(len(pr.get("invoices")), 0) self.assertEqual(len(pr.get("payments")), 0) + def test_payment_against_foreign_currency_journal(self): + transaction_date = nowdate() + + self.supplier = "_Test Supplier USD" + self.supplier2 = make_supplier("_Test Supplier2 USD", "USD") + amount = 100 + exc_rate1 = 80 + exc_rate2 = 83 + + je = frappe.new_doc("Journal Entry") + je.posting_date = transaction_date + je.company = self.company + je.user_remark = "test" + je.multi_currency = 1 + je.set( + "accounts", + [ + { + "account": self.creditors_usd, + "party_type": "Supplier", + "party": self.supplier, + "exchange_rate": exc_rate1, + "cost_center": self.cost_center, + "credit": amount * exc_rate1, + "credit_in_account_currency": amount, + }, + { + "account": self.creditors_usd, + "party_type": "Supplier", + "party": self.supplier2, + "exchange_rate": exc_rate2, + "cost_center": self.cost_center, + "credit": amount * exc_rate2, + "credit_in_account_currency": amount, + }, + { + "account": self.expense_account, + "cost_center": self.cost_center, + "debit": (amount * exc_rate1) + (amount * exc_rate2), + "debit_in_account_currency": (amount * exc_rate1) + (amount * exc_rate2), + }, + ], + ) + je.save().submit() + + pe = self.create_payment_entry(amount=amount, posting_date=transaction_date) + pe.payment_type = "Pay" + pe.party_type = "Supplier" + pe.party = self.supplier + pe.paid_to = self.creditors_usd + pe.paid_from = self.cash + pe.paid_amount = 8000 + pe.received_amount = 100 + pe.target_exchange_rate = exc_rate1 + pe.paid_to_account_currency = "USD" + pe.save().submit() + + pr = self.create_payment_reconciliation(party_is_customer=False) + pr.receivable_payable_account = self.creditors_usd + pr.minimum_invoice_amount = pr.maximum_invoice_amount = amount + pr.from_invoice_date = pr.to_invoice_date = transaction_date + pr.from_payment_date = pr.to_payment_date = transaction_date + + 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})) + + # There should no difference_amount as the Journal and Payment have same exchange rate - 'exc_rate1' + for row in pr.allocation: + self.assertEqual(flt(row.get("difference_amount")), 0.0) + + pr.reconcile() + + # check PR tool output + self.assertEqual(len(pr.get("invoices")), 0) + self.assertEqual(len(pr.get("payments")), 0) + + journals = frappe.db.get_all( + "Journal Entry Account", + filters={"reference_type": je.doctype, "reference_name": je.name, "docstatus": 1}, + fields=["parent"], + ) + self.assertEqual([], journals) + def test_journal_against_invoice(self): transaction_date = nowdate() amount = 100 @@ -1248,3 +1334,17 @@ def make_customer(customer_name, currency=None): return customer.name else: return customer_name + + +def make_supplier(supplier_name, currency=None): + if not frappe.db.exists("Supplier", supplier_name): + supplier = frappe.new_doc("Supplier") + supplier.supplier_name = supplier_name + supplier.type = "Individual" + supplier.supplier_group = "Local" + if currency: + supplier.default_currency = currency + supplier.save() + return supplier.name + else: + return supplier_name From 64bf52899f0cf69922a9640699271cd060eb2088 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 2 Mar 2024 14:57:48 +0530 Subject: [PATCH 388/675] chore: resolve linter --- erpnext/accounts/doctype/bank_account/bank_account.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/bank_account/bank_account.py b/erpnext/accounts/doctype/bank_account/bank_account.py index ec3fa831200..28a4a41e28e 100644 --- a/erpnext/accounts/doctype/bank_account/bank_account.py +++ b/erpnext/accounts/doctype/bank_account/bank_account.py @@ -30,7 +30,9 @@ class BankAccount(Document): def validate_account(self): if self.account: - if accounts := frappe.db.get_all("Bank Account", filters={"account": self.account, 'name':['!=', self.name]}, as_list=1): + if accounts := frappe.db.get_all( + "Bank Account", filters={"account": self.account, "name": ["!=", self.name]}, as_list=1 + ): frappe.throw( _("'{0}' account is already used by {1}. Use another account.").format( frappe.bold(self.account), From c1a0ac655ea5fd3b9d8d00f36c5ea15ca5af3038 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 1 Mar 2024 17:36:56 +0530 Subject: [PATCH 389/675] fix: make use of 'flt' to prevent really low precision exc gain/loss (cherry picked from commit 0aa72f841dbfb4a19fc6f7802172dcc5667369d7) --- erpnext/controllers/accounts_controller.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 560e7715e68..0795ab0f747 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -45,6 +45,7 @@ from erpnext.accounts.party import ( from erpnext.accounts.utils import ( create_gain_loss_journal, get_account_currency, + get_currency_precision, get_fiscal_years, validate_fiscal_year, ) @@ -1169,10 +1170,12 @@ class AccountsController(TransactionBase): # These are generated by Sales/Purchase Invoice during reconciliation and advance allocation. # and below logic is only for such scenarios if args: + precision = get_currency_precision() for arg in args: # Advance section uses `exchange_gain_loss` and reconciliation uses `difference_amount` if ( - arg.get("difference_amount", 0) != 0 or arg.get("exchange_gain_loss", 0) != 0 + flt(arg.get("difference_amount", 0), precision) != 0 + or flt(arg.get("exchange_gain_loss", 0), precision) != 0 ) and arg.get("difference_account"): party_account = arg.get("account") From 52e1c2f48b6750767fe1e047ff31499b727dcef9 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 1 Mar 2024 17:46:42 +0530 Subject: [PATCH 390/675] fix: allow gain/loss for Journals against Journals (cherry picked from commit 5b67631d40ddebcc6111aecb9c2f8882903d88f9) # Conflicts: # erpnext/accounts/doctype/journal_entry/journal_entry.py --- .../doctype/journal_entry/journal_entry.py | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 6e3019bb8f0..3f427332276 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -475,17 +475,33 @@ class JournalEntry(AccountsController): elif d.party_type == "Supplier" and flt(d.credit) > 0: frappe.throw(_("Row {0}: Advance against Supplier must be debit").format(d.idx)) + def system_generated_gain_loss(self): + return ( + self.voucher_type == "Exchange Gain Or Loss" + and self.multi_currency + and self.is_system_generated + ) + def validate_against_jv(self): for d in self.get("accounts"): if d.reference_type == "Journal Entry": +<<<<<<< HEAD account_root_type = frappe.db.get_value("Account", d.account, "root_type") if account_root_type == "Asset" and flt(d.debit) > 0: +======= + account_root_type = frappe.get_cached_value("Account", d.account, "root_type") + if account_root_type == "Asset" and flt(d.debit) > 0 and not self.system_generated_gain_loss(): +>>>>>>> 5b67631d40 (fix: allow gain/loss for Journals against Journals) frappe.throw( _( "Row #{0}: For {1}, you can select reference document only if account gets credited" ).format(d.idx, d.account) ) - elif account_root_type == "Liability" and flt(d.credit) > 0: + elif ( + account_root_type == "Liability" + and flt(d.credit) > 0 + and not self.system_generated_gain_loss() + ): frappe.throw( _( "Row #{0}: For {1}, you can select reference document only if account gets debited" @@ -517,7 +533,7 @@ class JournalEntry(AccountsController): for jvd in against_entries: if flt(jvd[dr_or_cr]) > 0: valid = True - if not valid: + if not valid and not self.system_generated_gain_loss(): frappe.throw( _("Against Journal Entry {0} does not have any unmatched {1} entry").format( d.reference_name, dr_or_cr From 46063518d75601d8cea175c1739de28b9d885169 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 1 Mar 2024 17:53:51 +0530 Subject: [PATCH 391/675] test: gain/loss on Journals against Journals (cherry picked from commit 8a5078b826e9390bc63069fefff3cf3de8bae449) # Conflicts: # erpnext/controllers/tests/test_accounts_controller.py --- .../tests/test_accounts_controller.py | 81 +++++++++++++++++-- 1 file changed, 76 insertions(+), 5 deletions(-) diff --git a/erpnext/controllers/tests/test_accounts_controller.py b/erpnext/controllers/tests/test_accounts_controller.py index 0c9d34d82a2..daea1d8b399 100644 --- a/erpnext/controllers/tests/test_accounts_controller.py +++ b/erpnext/controllers/tests/test_accounts_controller.py @@ -56,7 +56,8 @@ class TestAccountsController(FrappeTestCase): 20 series - Sales Invoice against Journals 30 series - Sales Invoice against Credit Notes 40 series - Company default Cost center is unset - 50 series - Dimension inheritence + 50 series = Journals against Journals + 90 series - Dimension inheritence """ def setUp(self): @@ -1306,7 +1307,7 @@ class TestAccountsController(FrappeTestCase): dimension1.disabled = 1 dimension1.save() - def test_50_dimensions_filter(self): + def test_90_dimensions_filter(self): """ Test workings of dimension filters """ @@ -1378,7 +1379,7 @@ class TestAccountsController(FrappeTestCase): self.assertEqual(len(pr.payments), 1) self.disable_dimensions() - def test_51_cr_note_should_inherit_dimension(self): + def test_91_cr_note_should_inherit_dimension(self): self.setup_dimensions() rate_in_account_currency = 1 @@ -1421,7 +1422,7 @@ class TestAccountsController(FrappeTestCase): ) self.disable_dimensions() - def test_52_dimension_inhertiance_exc_gain_loss(self): + def test_92_dimension_inhertiance_exc_gain_loss(self): # Sales Invoice in Foreign Currency self.setup_dimensions() rate = 80 @@ -1460,7 +1461,7 @@ class TestAccountsController(FrappeTestCase): ) self.disable_dimensions() - def test_53_dimension_inheritance_on_advance(self): + def test_93_dimension_inheritance_on_advance(self): self.setup_dimensions() dpt = "Research & Development" @@ -1505,4 +1506,74 @@ class TestAccountsController(FrappeTestCase): pluck="department", ), ) +<<<<<<< HEAD self.disable_dimensions() +======= + + def test_50_journal_against_journal(self): + # Invoice in Foreign Currency + journal_as_invoice = self.create_journal_entry( + acc1=self.debit_usd, + acc1_exc_rate=83, + acc2=self.cash, + acc1_amount=1, + acc2_amount=83, + acc2_exc_rate=1, + ) + journal_as_invoice.accounts[0].party_type = "Customer" + journal_as_invoice.accounts[0].party = self.customer + journal_as_invoice = journal_as_invoice.save().submit() + + # Payment + journal_as_payment = self.create_journal_entry( + acc1=self.debit_usd, + acc1_exc_rate=75, + acc2=self.cash, + acc1_amount=-1, + acc2_amount=-75, + acc2_exc_rate=1, + ) + journal_as_payment.accounts[0].party_type = "Customer" + journal_as_payment.accounts[0].party = self.customer + journal_as_payment = journal_as_payment.save().submit() + + # Reconcile the remaining amount + pr = self.create_payment_reconciliation() + # pr.receivable_payable_account = self.debit_usd + pr.get_unreconciled_entries() + self.assertEqual(len(pr.invoices), 1) + self.assertEqual(len(pr.payments), 1) + invoices = [x.as_dict() for x in pr.invoices] + payments = [x.as_dict() for x in pr.payments] + pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments})) + pr.reconcile() + self.assertEqual(len(pr.invoices), 0) + self.assertEqual(len(pr.payments), 0) + + # There should be no outstanding in both currencies + journal_as_invoice.reload() + self.assert_ledger_outstanding(journal_as_invoice.doctype, journal_as_invoice.name, 0.0, 0.0) + + # Exchange Gain/Loss Journal should've been created. + exc_je_for_si = self.get_journals_for(journal_as_invoice.doctype, journal_as_invoice.name) + exc_je_for_je = self.get_journals_for(journal_as_payment.doctype, journal_as_payment.name) + self.assertNotEqual(exc_je_for_si, []) + self.assertEqual( + len(exc_je_for_si), 2 + ) # payment also has reference. so, there are 2 journals referencing invoice + self.assertEqual(len(exc_je_for_je), 1) + self.assertIn(exc_je_for_je[0], exc_je_for_si) + + # Cancel Payment + journal_as_payment.reload() + journal_as_payment.cancel() + + journal_as_invoice.reload() + self.assert_ledger_outstanding(journal_as_invoice.doctype, journal_as_invoice.name, 83.0, 1.0) + + # Exchange Gain/Loss Journal should've been cancelled + exc_je_for_si = self.get_journals_for(journal_as_invoice.doctype, journal_as_invoice.name) + exc_je_for_je = self.get_journals_for(journal_as_payment.doctype, journal_as_payment.name) + self.assertEqual(exc_je_for_si, []) + self.assertEqual(exc_je_for_je, []) +>>>>>>> 8a5078b826 (test: gain/loss on Journals against Journals) From 753223da7851fffc43bb0697d9ff9b1755c6d1c0 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sat, 2 Mar 2024 15:59:48 +0530 Subject: [PATCH 392/675] chore: resolve merge conflict --- erpnext/accounts/doctype/journal_entry/journal_entry.py | 5 ----- erpnext/controllers/tests/test_accounts_controller.py | 3 --- 2 files changed, 8 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 3f427332276..7ea21c9f134 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -485,13 +485,8 @@ class JournalEntry(AccountsController): def validate_against_jv(self): for d in self.get("accounts"): if d.reference_type == "Journal Entry": -<<<<<<< HEAD - account_root_type = frappe.db.get_value("Account", d.account, "root_type") - if account_root_type == "Asset" and flt(d.debit) > 0: -======= account_root_type = frappe.get_cached_value("Account", d.account, "root_type") if account_root_type == "Asset" and flt(d.debit) > 0 and not self.system_generated_gain_loss(): ->>>>>>> 5b67631d40 (fix: allow gain/loss for Journals against Journals) frappe.throw( _( "Row #{0}: For {1}, you can select reference document only if account gets credited" diff --git a/erpnext/controllers/tests/test_accounts_controller.py b/erpnext/controllers/tests/test_accounts_controller.py index daea1d8b399..e39d03dccb9 100644 --- a/erpnext/controllers/tests/test_accounts_controller.py +++ b/erpnext/controllers/tests/test_accounts_controller.py @@ -1506,9 +1506,7 @@ class TestAccountsController(FrappeTestCase): pluck="department", ), ) -<<<<<<< HEAD self.disable_dimensions() -======= def test_50_journal_against_journal(self): # Invoice in Foreign Currency @@ -1576,4 +1574,3 @@ class TestAccountsController(FrappeTestCase): exc_je_for_je = self.get_journals_for(journal_as_payment.doctype, journal_as_payment.name) self.assertEqual(exc_je_for_si, []) self.assertEqual(exc_je_for_je, []) ->>>>>>> 8a5078b826 (test: gain/loss on Journals against Journals) From b937c4be4f25d079a5340402f8702515005b698e Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Sat, 2 Mar 2024 16:49:40 +0530 Subject: [PATCH 393/675] fix: rate change on changing of the qty (backport #40241) (#40242) fix: rate change on changing of the qty (#40241) (cherry picked from commit e7d707797aeab2f5d209690c959e88851c9a6999) Co-authored-by: rohitwaghchaure --- erpnext/public/js/controllers/taxes_and_totals.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index c179928b511..186c342a75e 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -22,7 +22,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { item_rate = flt(item.rate_with_margin , precision("rate", item)); - if (item.discount_percentage) { + if (item.discount_percentage && !item.discount_amount) { item.discount_amount = flt(item.rate_with_margin) * flt(item.discount_percentage) / 100; } From 89e7ad790fd2c135299b465d1483443d01a3ada0 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Sun, 3 Mar 2024 18:00:10 +0530 Subject: [PATCH 394/675] chore: linting issue --- erpnext/accounts/doctype/bank_account/bank_account.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/bank_account/bank_account.py b/erpnext/accounts/doctype/bank_account/bank_account.py index ec3fa831200..28a4a41e28e 100644 --- a/erpnext/accounts/doctype/bank_account/bank_account.py +++ b/erpnext/accounts/doctype/bank_account/bank_account.py @@ -30,7 +30,9 @@ class BankAccount(Document): def validate_account(self): if self.account: - if accounts := frappe.db.get_all("Bank Account", filters={"account": self.account, 'name':['!=', self.name]}, as_list=1): + if accounts := frappe.db.get_all( + "Bank Account", filters={"account": self.account, "name": ["!=", self.name]}, as_list=1 + ): frappe.throw( _("'{0}' account is already used by {1}. Use another account.").format( frappe.bold(self.account), From 38baf8d4061969f08aecbedba4b456e6551b1678 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Sun, 3 Mar 2024 18:08:23 +0530 Subject: [PATCH 395/675] feat: add company filter to child table field (cherry picked from commit 51909077bd90fe86ab3e1c916412a18e183ab823) --- .../cost_center_allocation.js | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/doctype/cost_center_allocation/cost_center_allocation.js b/erpnext/accounts/doctype/cost_center_allocation/cost_center_allocation.js index ab0baab24a0..0da90161f51 100644 --- a/erpnext/accounts/doctype/cost_center_allocation/cost_center_allocation.js +++ b/erpnext/accounts/doctype/cost_center_allocation/cost_center_allocation.js @@ -3,16 +3,21 @@ frappe.ui.form.on('Cost Center Allocation', { setup: function(frm) { - let filters = {"is_group": 0}; - if (frm.doc.company) { - $.extend(filters, { - "company": frm.doc.company - }); - } - frm.set_query('main_cost_center', function() { return { - filters: filters + filters: { + company: frm.doc.company, + is_group: 0 + } + }; + }); + + frm.set_query('cost_center', 'allocation_percentages', function() { + return { + filters: { + company: frm.doc.company, + is_group: 0 + } }; }); } From 85a7ce6d30e7481409469d2e07cb221904ea64dc Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Thu, 12 Oct 2023 11:54:12 +0530 Subject: [PATCH 396/675] refactor: remove balance fields from jv account --- .../journal_entry_account.json | 21 ------------------- 1 file changed, 21 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json index 3132fe9b12b..94a050a9004 100644 --- a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json +++ b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json @@ -9,12 +9,10 @@ "field_order": [ "account", "account_type", - "balance", "col_break1", "bank_account", "party_type", "party", - "party_balance", "accounting_dimensions_section", "cost_center", "dimension_col_break", @@ -63,17 +61,6 @@ "label": "Account Type", "print_hide": 1 }, - { - "fieldname": "balance", - "fieldtype": "Currency", - "label": "Account Balance", - "no_copy": 1, - "oldfieldname": "balance", - "oldfieldtype": "Data", - "options": "account_currency", - "print_hide": 1, - "read_only": 1 - }, { "default": ":Company", "description": "If Income or Expense", @@ -107,14 +94,6 @@ "label": "Party", "options": "party_type" }, - { - "fieldname": "party_balance", - "fieldtype": "Currency", - "label": "Party Balance", - "options": "account_currency", - "print_hide": 1, - "read_only": 1 - }, { "fieldname": "currency_section", "fieldtype": "Section Break", From d715c29edc3246e5b16c554fa64aa04c1700c018 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Thu, 12 Oct 2023 12:04:44 +0530 Subject: [PATCH 397/675] refactor: remove balance formatter --- .../doctype/journal_entry/journal_entry.js | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index f86320d917a..948e2d47d95 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -184,7 +184,6 @@ var update_jv_details = function(doc, r) { $.each(r, function(i, d) { var row = frappe.model.add_child(doc, "Journal Entry Account", "accounts"); frappe.model.set_value(row.doctype, row.name, "account", d.account) - frappe.model.set_value(row.doctype, row.name, "balance", d.balance) }); refresh_field("accounts"); } @@ -193,7 +192,6 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro onload() { this.load_defaults(); this.setup_queries(); - this.setup_balance_formatter(); erpnext.accounts.dimensions.setup_dimension_filters(this.frm, this.frm.doctype); } @@ -292,19 +290,6 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro } - setup_balance_formatter() { - const formatter = function(value, df, options, doc) { - var currency = frappe.meta.get_field_currency(df, doc); - var dr_or_cr = value ? ('') : ""; - return "
    " - + ((value==null || value==="") ? "" : format_currency(Math.abs(value), currency)) - + " " + dr_or_cr - + "
    "; - }; - this.frm.fields_dict.accounts.grid.update_docfield_property('balance', 'formatter', formatter); - this.frm.fields_dict.accounts.grid.update_docfield_property('party_balance', 'formatter', formatter); - } - reference_name(doc, cdt, cdn) { var d = frappe.get_doc(cdt, cdn); From a1c22811a246a41082817f64ea980425eae8a7b2 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Thu, 12 Oct 2023 12:07:36 +0530 Subject: [PATCH 398/675] refactor: exclude balance while setting acc details --- erpnext/accounts/doctype/journal_entry/journal_entry.js | 9 ++++----- erpnext/accounts/doctype/journal_entry/journal_entry.py | 5 ++--- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index 948e2d47d95..8196589829d 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -397,11 +397,11 @@ frappe.ui.form.on("Journal Entry Account", { } }, cost_center: function(frm, dt, dn) { - erpnext.journal_entry.set_account_balance(frm, dt, dn); + erpnext.journal_entry.set_account_details(frm, dt, dn); }, account: function(frm, dt, dn) { - erpnext.journal_entry.set_account_balance(frm, dt, dn); + erpnext.journal_entry.set_account_details(frm, dt, dn); }, debit_in_account_currency: function(frm, cdt, cdn) { @@ -585,14 +585,14 @@ $.extend(erpnext.journal_entry, { }); $.extend(erpnext.journal_entry, { - set_account_balance: function(frm, dt, dn) { + set_account_details: function(frm, dt, dn) { var d = locals[dt][dn]; if(d.account) { if(!frm.doc.company) frappe.throw(__("Please select Company first")); if(!frm.doc.posting_date) frappe.throw(__("Please select Posting Date first")); return frappe.call({ - method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_account_balance_and_party_type", + method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_account_details_and_party_type", args: { account: d.account, date: frm.doc.posting_date, @@ -600,7 +600,6 @@ $.extend(erpnext.journal_entry, { debit: flt(d.debit_in_account_currency), credit: flt(d.credit_in_account_currency), exchange_rate: d.exchange_rate, - cost_center: d.cost_center }, callback: function(r) { if(r.message) { diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 7ea21c9f134..0fbeb302aeb 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -1425,8 +1425,8 @@ def get_party_account_and_balance(company, party_type, party, cost_center=None): @frappe.whitelist() -def get_account_balance_and_party_type( - account, date, company, debit=None, credit=None, exchange_rate=None, cost_center=None +def get_account_details_and_party_type( + account, date, company, debit=None, credit=None, exchange_rate=None ): """Returns dict of account balance and party type to be set in Journal Entry on selection of account.""" if not frappe.has_permission("Account"): @@ -1448,7 +1448,6 @@ def get_account_balance_and_party_type( party_type = "" grid_values = { - "balance": get_balance_on(account, date, cost_center=cost_center), "party_type": party_type, "account_type": account_details.account_type, "account_currency": account_details.account_currency or company_currency, From e3d6f70eeecc212559b877e01a1acf40f651bac3 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Thu, 12 Oct 2023 12:09:58 +0530 Subject: [PATCH 399/675] refactor: exclude balances while setting currency --- .../accounts/doctype/journal_entry/journal_entry.js | 3 +-- .../accounts/doctype/journal_entry/journal_entry.py | 11 ++--------- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index 8196589829d..5f59f036ef9 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -385,13 +385,12 @@ frappe.ui.form.on("Journal Entry Account", { if(!d.account && d.party_type && d.party) { if(!frm.doc.company) frappe.throw(__("Please select Company")); return frm.call({ - method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_party_account_and_balance", + method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_party_account_and_currency", child: d, args: { company: frm.doc.company, party_type: d.party_type, party: d.party, - cost_center: d.cost_center } }); } diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 0fbeb302aeb..fa20ecfd64e 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -1405,22 +1405,15 @@ def get_outstanding(args): @frappe.whitelist() -def get_party_account_and_balance(company, party_type, party, cost_center=None): +def get_party_account_and_currency(company, party_type, party): if not frappe.has_permission("Account"): frappe.msgprint(_("No Permission"), raise_exception=1) account = get_party_account(party_type, party, company) - account_balance = get_balance_on(account=account, cost_center=cost_center) - party_balance = get_balance_on( - party_type=party_type, party=party, company=company, cost_center=cost_center - ) - return { "account": account, - "balance": account_balance, - "party_balance": party_balance, - "account_currency": frappe.db.get_value("Account", account, "account_currency"), + "account_currency": frappe.get_cached_value("Account", account, "account_currency"), } From 2f8fe17b60848bf5568d9677dc02f32a6c28001e Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Thu, 12 Oct 2023 12:13:21 +0530 Subject: [PATCH 400/675] refactor: remove controller logic for setting balances --- .../doctype/journal_entry/journal_entry.py | 20 +------------------ 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index fa20ecfd64e..88388128653 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -68,7 +68,6 @@ class JournalEntry(AccountsController): self.set_print_format_fields() self.validate_credit_debit_note() self.validate_empty_accounts_table() - self.set_account_and_party_balance() self.validate_inter_company_accounts() self.validate_depr_entry_voucher_type() @@ -1076,21 +1075,6 @@ class JournalEntry(AccountsController): if not self.get("accounts"): frappe.throw(_("Accounts table cannot be blank.")) - def set_account_and_party_balance(self): - account_balance = {} - party_balance = {} - for d in self.get("accounts"): - if d.account not in account_balance: - account_balance[d.account] = get_balance_on(account=d.account, date=self.posting_date) - - if (d.party_type, d.party) not in party_balance: - party_balance[(d.party_type, d.party)] = get_balance_on( - party_type=d.party_type, party=d.party, date=self.posting_date, company=self.company - ) - - d.account_balance = account_balance[d.account] - d.party_balance = party_balance[(d.party_type, d.party)] - @frappe.whitelist() def get_default_bank_cash_account(company, account_type=None, mode_of_payment=None, account=None): @@ -1256,8 +1240,6 @@ def get_payment_entry(ref_doc, args): "account_type": frappe.db.get_value("Account", args.get("party_account"), "account_type"), "account_currency": args.get("party_account_currency") or get_account_currency(args.get("party_account")), - "balance": get_balance_on(args.get("party_account")), - "party_balance": get_balance_on(party=args.get("party"), party_type=args.get("party_type")), "exchange_rate": exchange_rate, args.get("amount_field_party"): args.get("amount"), "is_advance": args.get("is_advance"), @@ -1421,7 +1403,7 @@ def get_party_account_and_currency(company, party_type, party): def get_account_details_and_party_type( account, date, company, debit=None, credit=None, exchange_rate=None ): - """Returns dict of account balance and party type to be set in Journal Entry on selection of account.""" + """Returns dict of account details and party type to be set in Journal Entry on selection of account.""" if not frappe.has_permission("Account"): frappe.msgprint(_("No Permission"), raise_exception=1) From 091d6f330a8ecc48e1797955dd36a70587ddb698 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Mon, 4 Mar 2024 13:14:32 +0530 Subject: [PATCH 401/675] fix: do not allow to cancel incomplete reposting (backport #40224) (#40229) * fix: do not allow to cancel incomplete reposting (#40224) (cherry picked from commit 72ac56b6c4366910f7fc4f561811240ad0694965) # Conflicts: # erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py * chore: fix conflicts --------- Co-authored-by: rohitwaghchaure --- .../repost_item_valuation/repost_item_valuation.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) 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 5d087a46242..4d3e9f0aaf6 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py @@ -173,14 +173,9 @@ class RepostItemValuation(Document): if self.status not in ("Queued", "In Progress"): return - if not (self.voucher_no and self.voucher_no): - return - - transaction_status = frappe.db.get_value(self.voucher_type, self.voucher_no, "docstatus") - if transaction_status == 2: - msg = _("Cannot cancel as processing of cancelled documents is pending.") - msg += "
    " + _("Please try again in an hour.") - frappe.throw(msg, title=_("Pending processing")) + msg = _("Cannot cancel as processing of cancelled documents is pending.") + msg += "
    " + _("Please try again in an hour.") + frappe.throw(msg, title=_("Pending processing")) @frappe.whitelist() def restart_reposting(self): From d7b738ff61e0ebab62ff2c15247f90b79401e9a4 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Tue, 27 Feb 2024 14:40:15 +0530 Subject: [PATCH 402/675] perf: Optimization for providional gl entries --- .../purchase_invoice/purchase_invoice.py | 55 ++++++++++--------- 1 file changed, 29 insertions(+), 26 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index cfaaf767786..35652b71c4d 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -720,7 +720,7 @@ class PurchaseInvoice(BuyingController): "Company", self.company, "enable_provisional_accounting_for_non_stock_items" ) ) - + provisional_enpenses_booked_in_pr = False purchase_receipt_doc_map = {} for item in self.get("items"): @@ -859,34 +859,37 @@ class PurchaseInvoice(BuyingController): if provisional_accounting_for_non_stock_items: if item.purchase_receipt: - provisional_account, pr_qty, pr_base_rate = frappe.get_cached_value( - "Purchase Receipt Item", - item.pr_detail, - ["provisional_expense_account", "qty", "base_rate"], - ) - provisional_account = provisional_account or self.get_company_default( - "default_provisional_account" - ) - purchase_receipt_doc = purchase_receipt_doc_map.get(item.purchase_receipt) + if not provisional_enpenses_booked_in_pr: + provisional_account, pr_qty, pr_base_rate = frappe.get_cached_value( + "Purchase Receipt Item", + item.pr_detail, + ["provisional_expense_account", "qty", "base_rate"], + ) + provisional_account = provisional_account or self.get_company_default( + "default_provisional_account" + ) + # Post reverse entry for Stock-Received-But-Not-Billed if it is booked in Purchase Receipt + provision_gle_against_pr = frappe.db.get_value( + "GL Entry", + { + "is_cancelled": 0, + "voucher_type": "Purchase Receipt", + "voucher_no": item.purchase_receipt, + "voucher_detail_no": item.pr_detail, + "account": provisional_account, + }, + ["name"], + ) + if provision_gle_against_pr: + provisional_enpenses_booked_in_pr = True - if not purchase_receipt_doc: - purchase_receipt_doc = frappe.get_doc("Purchase Receipt", item.purchase_receipt) - purchase_receipt_doc_map[item.purchase_receipt] = purchase_receipt_doc + if provisional_enpenses_booked_in_pr: + purchase_receipt_doc = purchase_receipt_doc_map.get(item.purchase_receipt) - # Post reverse entry for Stock-Received-But-Not-Billed if it is booked in Purchase Receipt - expense_booked_in_pr = frappe.db.get_value( - "GL Entry", - { - "is_cancelled": 0, - "voucher_type": "Purchase Receipt", - "voucher_no": item.purchase_receipt, - "voucher_detail_no": item.pr_detail, - "account": provisional_account, - }, - "name", - ) + if not purchase_receipt_doc: + purchase_receipt_doc = frappe.get_doc("Purchase Receipt", item.purchase_receipt) + purchase_receipt_doc_map[item.purchase_receipt] = purchase_receipt_doc - if expense_booked_in_pr: # Intentionally passing purchase invoice item to handle partial billing purchase_receipt_doc.add_provisional_gl_entry( item, From f204d810bba5a6fdb3137b780489907c30b650fc Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 4 Mar 2024 15:46:15 +0530 Subject: [PATCH 403/675] perf: Performance optimization for validating budget --- erpnext/accounts/doctype/budget/budget.py | 6 +- .../budget_account/budget_account.json | 124 +++++------------- 2 files changed, 40 insertions(+), 90 deletions(-) diff --git a/erpnext/accounts/doctype/budget/budget.py b/erpnext/accounts/doctype/budget/budget.py index 6cfd15d3ec8..2f3b9a4784e 100644 --- a/erpnext/accounts/doctype/budget/budget.py +++ b/erpnext/accounts/doctype/budget/budget.py @@ -109,6 +109,8 @@ class Budget(Document): def validate_expense_against_budget(args, expense_amount=0): args = frappe._dict(args) + if not frappe.get_all("Budget", limit=1): + return if args.get("company") and not args.fiscal_year: args.fiscal_year = get_fiscal_year(args.get("posting_date"), company=args.get("company"))[0] @@ -142,13 +144,13 @@ def validate_expense_against_budget(args, expense_amount=0): if ( args.get(budget_against) and args.account - and frappe.db.get_value("Account", {"name": args.account, "root_type": "Expense"}) + and (frappe.get_cached_value("Account", args.account, "root_type") == "Expense") ): doctype = dimension.get("document_type") if frappe.get_cached_value("DocType", doctype, "is_tree"): - lft, rgt = frappe.db.get_value(doctype, args.get(budget_against), ["lft", "rgt"]) + lft, rgt = frappe.get_cached_value(doctype, args.get(budget_against), ["lft", "rgt"]) condition = """and exists(select name from `tab%s` where lft<=%s and rgt>=%s and name=b.%s)""" % ( doctype, diff --git a/erpnext/accounts/doctype/budget_account/budget_account.json b/erpnext/accounts/doctype/budget_account/budget_account.json index ead07614a7f..c7d872647f1 100644 --- a/erpnext/accounts/doctype/budget_account/budget_account.json +++ b/erpnext/accounts/doctype/budget_account/budget_account.json @@ -1,94 +1,42 @@ { - "allow_copy": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2016-05-16 11:54:09.286135", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "creation": "2016-05-16 11:54:09.286135", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "account", + "budget_amount" + ], "fields": [ { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "account", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Account", - "length": 0, - "no_copy": 0, - "options": "Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "account", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Account", + "options": "Account", + "reqd": 1, + "search_index": 1 + }, { - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "budget_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 1, - "label": "Budget Amount", - "length": 0, - "no_copy": 0, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "fieldname": "budget_amount", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Budget Amount", + "options": "Company:company:default_currency", + "reqd": 1 } - ], - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2017-01-02 17:02:53.339420", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Budget Account", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_seen": 0 + ], + "istable": 1, + "links": [], + "modified": "2024-03-04 15:43:27.016947", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Budget Account", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [] } \ No newline at end of file From 8cd8b8f885419fac8297b171e90be7657357a0e3 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 4 Mar 2024 15:47:09 +0530 Subject: [PATCH 404/675] perf: Cached accounting dimensions details --- .../accounting_dimension/accounting_dimension.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py index 8afd313322e..52dda51708f 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py @@ -236,14 +236,15 @@ def get_accounting_dimensions(as_list=True, filters=None): def get_checks_for_pl_and_bs_accounts(): - dimensions = frappe.db.sql( - """SELECT p.label, p.disabled, p.fieldname, c.default_dimension, c.company, c.mandatory_for_pl, c.mandatory_for_bs - FROM `tabAccounting Dimension`p ,`tabAccounting Dimension Detail` c - WHERE p.name = c.parent""", - as_dict=1, - ) + if frappe.flags.accounting_dimensions_details is None: + frappe.flags.accounting_dimensions_details = frappe.db.sql( + """SELECT p.label, p.disabled, p.fieldname, c.default_dimension, c.company, c.mandatory_for_pl, c.mandatory_for_bs + FROM `tabAccounting Dimension`p ,`tabAccounting Dimension Detail` c + WHERE p.name = c.parent""", + as_dict=1, + ) - return dimensions + return frappe.flags.accounting_dimensions_details def get_dimension_with_children(doctype, dimensions): From aa75a6014264853668acd15e996e8fd0af8e7ebe Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Mon, 4 Mar 2024 15:47:56 +0530 Subject: [PATCH 405/675] perf: Optimzed code for merging similar gl entries --- erpnext/accounts/doctype/gl_entry/gl_entry.py | 4 +- erpnext/accounts/general_ledger.py | 43 +++++++++---------- 2 files changed, 23 insertions(+), 24 deletions(-) diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py index 6e07b0ec430..c0f6502910e 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.py +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py @@ -127,7 +127,7 @@ class GLEntry(Document): frappe.throw(msg, title=_("Missing Cost Center")) def validate_dimensions_for_pl_and_bs(self): - account_type = frappe.db.get_value("Account", self.account, "report_type") + account_type = frappe.get_cached_value("Account", self.account, "report_type") for dimension in get_checks_for_pl_and_bs_accounts(): if ( @@ -252,7 +252,7 @@ class GLEntry(Document): def validate_balance_type(account, adv_adj=False): if not adv_adj and account: - balance_must_be = frappe.db.get_value("Account", account, "balance_must_be") + balance_must_be = frappe.get_cached_value("Account", account, "balance_must_be") if balance_must_be: balance = frappe.db.sql( """select sum(debit) - sum(credit) diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index 38fcd976ad4..b1223b271f5 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -234,11 +234,13 @@ def get_cost_center_allocation_data(company, posting_date): def merge_similar_entries(gl_map, precision=None): merged_gl_map = [] accounting_dimensions = get_accounting_dimensions() + merge_properties = get_merge_properties(accounting_dimensions) for entry in gl_map: + 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 - same_head = check_if_in_list(entry, merged_gl_map, accounting_dimensions) + same_head = check_if_in_list(entry, merged_gl_map) if same_head: same_head.debit = flt(same_head.debit) + flt(entry.debit) same_head.debit_in_account_currency = flt(same_head.debit_in_account_currency) + flt( @@ -272,37 +274,34 @@ def merge_similar_entries(gl_map, precision=None): return merged_gl_map - -def check_if_in_list(gle, gl_map, dimensions=None): - account_head_fieldnames = [ - "voucher_detail_no", - "party", - "against_voucher", +def get_merge_properties(dimensions=None): + merge_properties = [ + "account", "cost_center", - "against_voucher_type", + "party", "party_type", + "voucher_detail_no", + "against_voucher", + "against_voucher_type", "project", "finance_book", ] - if dimensions: - account_head_fieldnames = account_head_fieldnames + dimensions + merge_properties.extend(dimensions) + return merge_properties +def get_merge_key(entry, merge_properties): + merge_key = [] + for fieldname in merge_properties: + merge_key.append(entry.get(fieldname, '')) + + return tuple(merge_key) + +def check_if_in_list(gle, gl_map): for e in gl_map: - same_head = True - if e.account != gle.account: - same_head = False - continue - - for fieldname in account_head_fieldnames: - if cstr(e.get(fieldname)) != cstr(gle.get(fieldname)): - same_head = False - break - - if same_head: + if e.merge_key == gle.merge_key: return e - def toggle_debit_credit_if_negative(gl_map): for entry in gl_map: # toggle debit, credit if negative entry From af568464dbb05d5dc3922daffbf8b57a956f85c6 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 5 Mar 2024 14:46:24 +0530 Subject: [PATCH 406/675] fix: incorrect TCS on customer and suppliers with same name (cherry picked from commit 9904a9868c346028fa3b1809ac575ea1e1c6632b) --- .../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 943c0057f99..1d1955e3d51 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -522,6 +522,7 @@ def get_tcs_amount(parties, inv, tax_details, vouchers, adv_vouchers): "GL Entry", { "is_cancelled": 0, + "party_type": "Customer", "party": ["in", parties], "company": inv.company, "voucher_no": ["in", vouchers], @@ -536,6 +537,7 @@ def get_tcs_amount(parties, inv, tax_details, vouchers, adv_vouchers): conditions = [] conditions.append(ple.amount.lt(0)) conditions.append(ple.delinked == 0) + conditions.append(ple.party_type == "Customer") conditions.append(ple.party.isin(parties)) conditions.append(ple.voucher_no == ple.against_voucher_no) conditions.append(ple.company == inv.company) @@ -555,6 +557,7 @@ def get_tcs_amount(parties, inv, tax_details, vouchers, adv_vouchers): { "is_cancelled": 0, "credit": [">", 0], + "party_type": "Customer", "party": ["in", parties], "posting_date": ["between", (tax_details.from_date, tax_details.to_date)], "company": inv.company, From b424a184b91d3c69222496116524b85fc69aff34 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 6 Mar 2024 03:31:16 +0000 Subject: [PATCH 407/675] chore(release): Bumped to Version 14.65.0 # [14.65.0](https://github.com/frappe/erpnext/compare/v14.64.0...v14.65.0) (2024-03-06) ### Bug Fixes * account validation error on bank account after editing existing bank account ([8b3c809](https://github.com/frappe/erpnext/commit/8b3c809cb68ed9badb4d9fa4defc083b38bd4184)) * allow gain/loss for Journals against Journals ([52e1c2f](https://github.com/frappe/erpnext/commit/52e1c2f48b6750767fe1e047ff31499b727dcef9)) * do not allow to cancel incomplete reposting (backport [#40224](https://github.com/frappe/erpnext/issues/40224)) ([#40229](https://github.com/frappe/erpnext/issues/40229)) ([091d6f3](https://github.com/frappe/erpnext/commit/091d6f330a8ecc48e1797955dd36a70587ddb698)) * don't override reference exchange rate ([e25ec41](https://github.com/frappe/erpnext/commit/e25ec4156e894c855d6b7bef8b9b5868d29420cc)) * handle partial invoice against provisional entry ([5f23614](https://github.com/frappe/erpnext/commit/5f23614960aa0f41b11d8b5f651d85cbfbf2753c)) * incorrect exchange rate if JE has multi parties ([1b5a237](https://github.com/frappe/erpnext/commit/1b5a23709a751f925d78c476bd5e51b743135d3f)) * incorrect TCS on customer and suppliers with same name ([af56846](https://github.com/frappe/erpnext/commit/af568464dbb05d5dc3922daffbf8b57a956f85c6)) * make use of 'flt' to prevent really low precision exc gain/loss ([c1a0ac6](https://github.com/frappe/erpnext/commit/c1a0ac655ea5fd3b9d8d00f36c5ea15ca5af3038)) * make warning for previously existing SO an alert ([c8b817c](https://github.com/frappe/erpnext/commit/c8b817c87a9e510a865c48ad9e2890c892246e8e)) * over billing qty along with rate ([14d9d29](https://github.com/frappe/erpnext/commit/14d9d29913e78a585230763b3f25a2e903c263a7)) * **Project:** filter department by company ([646b55e](https://github.com/frappe/erpnext/commit/646b55eeea46f6d9b2597e2beb3a1cdad461df8a)) * provisional reverse entry amount ([e62c49d](https://github.com/frappe/erpnext/commit/e62c49d9a69f7549a2ca425b000cea648a802daf)) * rate change on changing of the qty (backport [#40241](https://github.com/frappe/erpnext/issues/40241)) ([#40242](https://github.com/frappe/erpnext/issues/40242)) ([b937c4b](https://github.com/frappe/erpnext/commit/b937c4be4f25d079a5340402f8702515005b698e)) * **setup:** avoid duplicate entry for Analytics role (backport [#40183](https://github.com/frappe/erpnext/issues/40183)) ([#40184](https://github.com/frappe/erpnext/issues/40184)) ([035c90c](https://github.com/frappe/erpnext/commit/035c90c3b84934711f82ee61242b737a7d1a1290)) ### Features * add company filter to child table field ([38baf8d](https://github.com/frappe/erpnext/commit/38baf8d4061969f08aecbedba4b456e6551b1678)) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 17db2feece6..1ed6e0263cd 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -3,7 +3,7 @@ import inspect import frappe -__version__ = "14.64.0" +__version__ = "14.65.0" def get_default_company(user=None): From acc0b2faf82c2b352cc2caf14ef26e6e55f34230 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 6 Mar 2024 17:43:27 +0530 Subject: [PATCH 408/675] fix: linter issues --- erpnext/accounts/general_ledger.py | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index b1223b271f5..432abc250b1 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -7,7 +7,7 @@ import copy import frappe from frappe import _ from frappe.model.meta import get_field_precision -from frappe.utils import cint, cstr, flt, formatdate, getdate, now +from frappe.utils import cint, flt, formatdate, getdate, now import erpnext from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( @@ -274,6 +274,7 @@ def merge_similar_entries(gl_map, precision=None): return merged_gl_map + def get_merge_properties(dimensions=None): merge_properties = [ "account", @@ -290,18 +291,21 @@ def get_merge_properties(dimensions=None): merge_properties.extend(dimensions) return merge_properties + def get_merge_key(entry, merge_properties): merge_key = [] for fieldname in merge_properties: - merge_key.append(entry.get(fieldname, '')) - + merge_key.append(entry.get(fieldname, "")) + return tuple(merge_key) + def check_if_in_list(gle, gl_map): for e in gl_map: if e.merge_key == gle.merge_key: return e + def toggle_debit_credit_if_negative(gl_map): for entry in gl_map: # toggle debit, credit if negative entry From e4bd1738752896e9a50231561c8ca5f3a2ee15c5 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 6 Mar 2024 20:32:47 +0530 Subject: [PATCH 409/675] perf: Cache accounting dimension filter map --- .../accounting_dimension_filter.py | 61 ++++++++++--------- 1 file changed, 32 insertions(+), 29 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 80f736fa5bb..56f2eb71393 100644 --- a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py +++ b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py @@ -38,37 +38,40 @@ class AccountingDimensionFilter(Document): def get_dimension_filter_map(): - filters = frappe.db.sql( - """ - SELECT - 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, - `tabAccounting Dimension Filter` p - WHERE - p.name = a.parent - AND p.disabled = 0 - AND p.name = d.parent - """, - as_dict=1, - ) - - dimension_filter_map = {} - - for f in filters: - f.fieldname = scrub(f.accounting_dimension) - - build_map( - dimension_filter_map, - f.fieldname, - f.applicable_on_account, - f.dimension_value, - f.allow_or_restrict, - f.is_mandatory, + if not frappe.flags.get("dimension_filter_map"): + filters = frappe.db.sql( + """ + SELECT + 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, + `tabAccounting Dimension Filter` p + WHERE + p.name = a.parent + AND p.disabled = 0 + AND p.name = d.parent + """, + as_dict=1, ) - return dimension_filter_map + dimension_filter_map = {} + + for f in filters: + f.fieldname = scrub(f.accounting_dimension) + + build_map( + dimension_filter_map, + f.fieldname, + f.applicable_on_account, + f.dimension_value, + f.allow_or_restrict, + f.is_mandatory, + ) + + frappe.flags.dimension_filter_map = dimension_filter_map + + return frappe.flags.dimension_filter_map def build_map(map_object, dimension, account, filter_value, allow_or_restrict, is_mandatory): From 5cd9bf3bda1c63fc04b6b4db8ddfdb2f9c7b456e Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 6 Mar 2024 21:04:07 +0530 Subject: [PATCH 410/675] fix: minor fixes --- .../doctype/accounting_dimension/accounting_dimension.py | 1 + .../doctype/accounting_dimension/test_accounting_dimension.py | 1 + .../accounting_dimension_filter/accounting_dimension_filter.py | 1 + 3 files changed, 3 insertions(+) diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py index 52dda51708f..c6c5f207a6a 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py @@ -237,6 +237,7 @@ def get_accounting_dimensions(as_list=True, filters=None): def get_checks_for_pl_and_bs_accounts(): if frappe.flags.accounting_dimensions_details is None: + # nosemgrep frappe.flags.accounting_dimensions_details = frappe.db.sql( """SELECT p.label, p.disabled, p.fieldname, c.default_dimension, c.company, c.mandatory_for_pl, c.mandatory_for_bs FROM `tabAccounting Dimension`p ,`tabAccounting Dimension Detail` c diff --git a/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py index 25ef2ea5c2c..6de3215cc59 100644 --- a/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py +++ b/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py @@ -78,6 +78,7 @@ class TestAccountingDimension(unittest.TestCase): def tearDown(self): disable_dimension() + frappe.flags.accounting_dimensions_details = None def create_dimension(): 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 56f2eb71393..65262764294 100644 --- a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py +++ b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.py @@ -39,6 +39,7 @@ class AccountingDimensionFilter(Document): def get_dimension_filter_map(): if not frappe.flags.get("dimension_filter_map"): + # nosemgrep filters = frappe.db.sql( """ SELECT From 05385e4acb0f3af7e9cd19c752df8d7287f73401 Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 6 Mar 2024 22:33:36 +0530 Subject: [PATCH 411/675] perf: skip unnecessary validation while transaction cancellation --- .../accounting_dimension/test_accounting_dimension.py | 1 + .../test_accounting_dimension_filter.py | 2 ++ .../doctype/payment_ledger_entry/payment_ledger_entry.py | 9 +++++---- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py index 6de3215cc59..2ceaffc4e25 100644 --- a/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py +++ b/erpnext/accounts/doctype/accounting_dimension/test_accounting_dimension.py @@ -79,6 +79,7 @@ class TestAccountingDimension(unittest.TestCase): def tearDown(self): disable_dimension() frappe.flags.accounting_dimensions_details = None + frappe.flags.dimension_filter_map = None def create_dimension(): diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py b/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py index f13f2f9f279..3dc87bb4e38 100644 --- a/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py +++ b/erpnext/accounts/doctype/accounting_dimension_filter/test_accounting_dimension_filter.py @@ -47,6 +47,8 @@ class TestAccountingDimensionFilter(unittest.TestCase): def tearDown(self): disable_dimension_filter() disable_dimension() + frappe.flags.accounting_dimensions_details = None + frappe.flags.dimension_filter_map = None for si in self.invoice_list: si.load_from_db() diff --git a/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.py b/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.py index bcbcb670faf..bd438ee7606 100644 --- a/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.py +++ b/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.py @@ -132,11 +132,12 @@ class PaymentLedgerEntry(Document): def on_update(self): adv_adj = self.flags.adv_adj if not self.flags.from_repost: - self.validate_account_details() - self.validate_dimensions_for_pl_and_bs() - self.validate_allowed_dimensions() - validate_balance_type(self.account, adv_adj) validate_frozen_account(self.account, adv_adj) + if not self.delinked: + self.validate_account_details() + self.validate_dimensions_for_pl_and_bs() + self.validate_allowed_dimensions() + validate_balance_type(self.account, adv_adj) # update outstanding amount if ( From 911a582941ec2a63aeb642b43d2af91c7e543b6a Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Wed, 6 Mar 2024 16:52:19 +0100 Subject: [PATCH 412/675] fix(Shipment Parcel): make length, width and height non-mandatory It's tedious to enter all these values and some shipping providers only need to know the weigth and quantity. Companies can alwas cutomize the values to be mandatory, but the reverse is not possible. I'll make sure that the erpnext-shipping app does not break because of this (or fix any problems). (cherry picked from commit 898c6e30eb44ece8869b5bb354239631407ef578) --- .../doctype/shipment_parcel/shipment_parcel.json | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/erpnext/stock/doctype/shipment_parcel/shipment_parcel.json b/erpnext/stock/doctype/shipment_parcel/shipment_parcel.json index 6943edcdc91..0dc4fd113fc 100644 --- a/erpnext/stock/doctype/shipment_parcel/shipment_parcel.json +++ b/erpnext/stock/doctype/shipment_parcel/shipment_parcel.json @@ -16,22 +16,19 @@ "fieldname": "length", "fieldtype": "Int", "in_list_view": 1, - "label": "Length (cm)", - "reqd": 1 + "label": "Length (cm)" }, { "fieldname": "width", "fieldtype": "Int", "in_list_view": 1, - "label": "Width (cm)", - "reqd": 1 + "label": "Width (cm)" }, { "fieldname": "height", "fieldtype": "Int", "in_list_view": 1, - "label": "Height (cm)", - "reqd": 1 + "label": "Height (cm)" }, { "fieldname": "weight", @@ -52,7 +49,7 @@ ], "istable": 1, "links": [], - "modified": "2020-07-09 12:54:14.847170", + "modified": "2024-03-06 16:48:57.355757", "modified_by": "Administrator", "module": "Stock", "name": "Shipment Parcel", @@ -61,5 +58,6 @@ "quick_entry": 1, "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file From 411157c258a94a1c162d081b3a4fb2bfb3afe12e Mon Sep 17 00:00:00 2001 From: "Nihantra C. Patel" <141945075+Nihantra-Patel@users.noreply.github.com> Date: Thu, 7 Mar 2024 17:43:00 +0530 Subject: [PATCH 413/675] fix: load ignored doctype and include Mode of Payment Account (#40334) (cherry picked from commit 68baa3612abfe70465adb869279a706a3e60c355) --- .../transaction_deletion_record/transaction_deletion_record.js | 2 +- .../transaction_deletion_record/transaction_deletion_record.py | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js index 6a50ef8bbd9..a7b0709b3da 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js @@ -29,7 +29,7 @@ frappe.ui.form.on('Transaction Deletion Record', { }); function populate_doctypes_to_be_ignored(doctypes_to_be_ignored_array, frm) { - if (!(frm.doc.doctypes_to_be_ignored)) { + if (frm.doc.doctypes_to_be_ignored.length === 0) { var i; for (i = 0; i < doctypes_to_be_ignored_array.length; i++) { frm.add_child('doctypes_to_be_ignored', { 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 d266285b29a..649b43b5e91 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.py @@ -267,6 +267,7 @@ def get_doctypes_to_be_ignored(): "Bank Account", "Item Tax Template", "Mode of Payment", + "Mode of Payment Account", "Item Default", "Customer", "Supplier", From 8d63d19d451de4b9ff50d1c34e8d3cd911af94c3 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 7 Mar 2024 09:14:56 +0530 Subject: [PATCH 414/675] fix: Filter for projects in Sales Cycle (cherry picked from commit d0e0b66b2f0586e93a3e3cc640036c8e311d2070) --- erpnext/controllers/queries.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 303548cca42..59060925025 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -338,7 +338,9 @@ def get_project_name(doctype, txt, searchfield, start, page_len, filters): ifelse = CustomFunction("IF", ["condition", "then", "else"]) if filters and filters.get("customer"): - qb_filter_and_conditions.append(proj.customer == filters.get("customer")) + qb_filter_and_conditions.append( + (proj.customer == filters.get("customer")) | proj.customer.isnull() | proj.customer == "" + ) qb_filter_and_conditions.append(proj.status.notin(["Completed", "Cancelled"])) From ea605e76a33c4014ec25e791ee8c9504ec515683 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 7 Mar 2024 10:08:51 +0530 Subject: [PATCH 415/675] fix: Sender email in process statements of accounts (cherry picked from commit 65a2f3d12c0e813ca6af3d2ebe1e1734c8d8acc7) --- .../process_statement_of_accounts.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) 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 222c9628018..9c0bedd243f 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 @@ -397,11 +397,16 @@ def send_emails(document_name, from_scheduler=False, posting_date=None): subject = frappe.render_template(doc.subject, context) message = frappe.render_template(doc.body, context) + if doc.sender: + sender_email = frappe.db.get_value("Email Account", doc.sender, "email_id") + else: + sender_email = frappe.session.user + frappe.enqueue( queue="short", method=frappe.sendmail, recipients=recipients, - sender=doc.sender or frappe.session.user, + sender=sender_email, cc=cc, subject=subject, message=message, From e0d1b4b15f0064bc7488848e21ea44991564f553 Mon Sep 17 00:00:00 2001 From: Devin Slauenwhite Date: Fri, 8 Mar 2024 18:09:11 -0500 Subject: [PATCH 416/675] fix: get_user_default_as_list args order (cherry picked from commit ac1961b687f76987cb8095d76d4b3e1cb67d766c) --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 96f8ec27ec1..497416b12ab 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -13,7 +13,7 @@ def get_default_company(user=None): if not user: user = frappe.session.user - companies = get_user_default_as_list(user, "company") + companies = get_user_default_as_list("company", user) if companies: default_company = companies[0] else: From c46be27e9e0b1bfee837ddb53215e3b8403b557f Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Thu, 29 Feb 2024 17:08:25 +0100 Subject: [PATCH 417/675] ci: add prettier to pre-commit (cherry picked from commit 2c16036ef3f2ebf6a707b5ae8fa486fa102bc1dc) --- .editorconfig | 9 ++++++++- .eslintrc | 3 ++- .pre-commit-config.yaml | 45 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 2 deletions(-) diff --git a/.editorconfig b/.editorconfig index 24f122a8d43..e7d5cfeddcb 100644 --- a/.editorconfig +++ b/.editorconfig @@ -9,6 +9,13 @@ trim_trailing_whitespace = true charset = utf-8 # python, js indentation settings -[{*.py,*.js}] +[{*.py,*.js,*.vue,*.css,*.scss,*.html}] indent_style = tab indent_size = 4 +max_line_length = 110 + +# JSON files - mostly doctype schema files +[{*.json}] +insert_final_newline = false +indent_style = space +indent_size = 2 diff --git a/.eslintrc b/.eslintrc index 276d6ff3725..4a5f87171e8 100644 --- a/.eslintrc +++ b/.eslintrc @@ -156,6 +156,7 @@ "onScan": true, "html2canvas": true, "extend_cscript": true, - "localforage": true + "localforage": true, + "Plaid": true } } diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index fba2f74a5b9..b95b32d7ddb 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,6 +20,51 @@ repos: - id: check-yaml - id: debug-statements + - repo: https://github.com/pre-commit/mirrors-prettier + rev: v2.7.1 + hooks: + - id: prettier + types_or: [javascript, vue, scss] + # Ignore any files that might contain jinja / bundles + exclude: | + (?x)^( + erpnext/public/dist/.*| + cypress/.*| + .*node_modules.*| + .*boilerplate.*| + erpnext/public/js/controllers/.*| + erpnext/templates/pages/order.js| + erpnext/templates/includes/.*| + .*/supplier_quotation.js| + .*/sales_taxes_and_charges_template.js| + .*/purchase_taxes_and_charges_template.js| + .*/subcontracting_order.js| + .*/landed_cost_voucher.js| + .*/payment_entry.js| + .*/loan_interest_accrual.js| + .*/loan_disbursement.js| + .*/loan_application.js| + .*/italy.js| + .*/sales_invoice.js| + .*/subcontracting_receipt.js| + .*/request_for_quotation.js| + .*/pos_profile.js| + .*/opportunity.js| + .*/quotation.js| + .*/sales_common.js| + .*/sales_order.js| + .*/pos_invoice.js| + .*/purchase_invoice.js| + .*/loan_repayment.js| + .*/material_request.js| + .*/purchase_receipt.js| + .*/delivery_note.js| + .*/loan.js| + .*/stock_entry.js| + .*/purchase_order.js| + .*/loan_write_off.js + )$ + - repo: https://github.com/PyCQA/flake8 rev: 5.0.4 hooks: From 4c629d31c6e3f88af449c2ec98680922f88d4b5b Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Mon, 11 Mar 2024 10:58:48 +0530 Subject: [PATCH 418/675] style: format js files --- commitlint.config.js | 26 +- .../account_balance_timeline.js | 8 +- erpnext/accounts/doctype/account/account.js | 214 +- .../accounts/doctype/account/account_tree.js | 304 +- .../accounting_dimension.js | 72 +- .../accounting_dimension_filter.js | 56 +- .../accounting_period/accounting_period.js | 23 +- .../accounts_settings/accounts_settings.js | 6 +- .../regional/united_states.js | 13 +- erpnext/accounts/doctype/bank/bank.js | 75 +- .../doctype/bank_account/bank_account.js | 44 +- .../bank_account_subtype.js | 6 +- .../bank_account_type/bank_account_type.js | 3 +- .../doctype/bank_clearance/bank_clearance.js | 56 +- .../doctype/bank_guarantee/bank_guarantee.js | 61 +- .../bank_reconciliation_tool.js | 130 +- .../bank_statement_import.js | 146 +- .../bank_statement_import_list.js | 34 +- .../bank_transaction/bank_transaction.js | 25 +- .../bank_transaction/bank_transaction_list.js | 12 +- erpnext/accounts/doctype/budget/budget.js | 42 +- .../cash_flow_mapper/cash_flow_mapper.js | 4 +- .../cash_flow_mapping/cash_flow_mapping.js | 34 +- .../cash_flow_mapping_template.js | 4 +- .../cash_flow_mapping_template_details.js | 4 +- .../cashier_closing/cashier_closing.js | 7 +- .../chart_of_accounts_importer.js | 169 +- .../cheque_print_template.js | 48 +- .../doctype/cost_center/cost_center.js | 115 +- .../doctype/cost_center/cost_center_tree.js | 108 +- .../cost_center_allocation.js | 18 +- .../doctype/coupon_code/coupon_code.js | 43 +- .../currency_exchange_settings.js | 25 +- erpnext/accounts/doctype/dunning/dunning.js | 63 +- .../doctype/dunning_type/dunning_type.js | 3 +- .../exchange_rate_revaluation.js | 98 +- .../doctype/finance_book/finance_book.js | 6 +- .../doctype/fiscal_year/fiscal_year.js | 20 +- erpnext/accounts/doctype/gl_entry/gl_entry.js | 8 +- .../invoice_discounting.js | 148 +- .../invoice_discounting_list.js | 21 +- .../item_tax_template/item_tax_template.js | 52 +- .../doctype/journal_entry/journal_entry.js | 506 +- .../journal_entry/journal_entry_list.js | 16 +- .../journal_entry_template.js | 52 +- .../doctype/ledger_merge/ledger_merge.js | 98 +- .../loyalty_point_entry.js | 6 +- .../loyalty_program/loyalty_program.js | 47 +- .../mode_of_payment/mode_of_payment.js | 14 +- .../monthly_distribution.js | 12 +- .../opening_invoice_creation_tool.js | 112 +- .../accounts/doctype/party_link/party_link.js | 29 +- .../payment_entry/payment_entry_list.js | 15 +- .../payment_gateway_account.js | 8 +- .../payment_ledger_entry.js | 3 +- .../doctype/payment_order/payment_order.js | 121 +- .../payment_reconciliation.js | 341 +- .../payment_request/payment_request.js | 84 +- .../payment_request/payment_request_list.js | 24 +- .../doctype/payment_term/payment_term.js | 16 +- .../payment_terms_template.js | 18 +- .../period_closing_voucher.js | 55 +- .../pos_closing_entry/pos_closing_entry.js | 131 +- .../pos_closing_entry_list.js | 19 +- .../doctype/pos_invoice/pos_invoice_list.js | 43 +- .../pos_invoice_merge_log.js | 24 +- .../pos_opening_entry/pos_opening_entry.js | 49 +- .../pos_opening_entry_list.js | 17 +- .../pos_profile_user/pos_profile_user.js | 4 +- .../doctype/pos_settings/pos_settings.js | 88 +- .../doctype/pricing_rule/pricing_rule.js | 94 +- .../process_deferred_accounting.js | 47 +- .../process_payment_reconciliation.js | 115 +- .../process_payment_reconciliation_list.js | 18 +- .../process_payment_reconciliation_log.js | 9 +- ...process_payment_reconciliation_log_list.js | 18 +- .../process_statement_of_accounts.js | 181 +- .../promotional_scheme/promotional_scheme.js | 39 +- .../purchase_invoice/purchase_invoice_list.js | 35 +- .../repost_accounting_ledger.js | 38 +- .../repost_payment_ledger.js | 52 +- .../repost_payment_ledger_list.js | 10 +- .../sales_invoice/sales_invoice_list.js | 38 +- .../doctype/share_transfer/share_transfer.js | 85 +- .../accounts/doctype/share_type/share_type.js | 6 +- .../doctype/shareholder/shareholder.js | 39 +- .../doctype/shipping_rule/shipping_rule.js | 34 +- .../doctype/subscription/subscription.js | 107 +- .../doctype/subscription/subscription_list.js | 20 +- .../subscription_plan/subscription_plan.js | 8 +- .../doctype/tax_category/tax_category.js | 6 +- erpnext/accounts/doctype/tax_rule/tax_rule.js | 36 +- .../tax_withholding_category.js | 14 +- .../unreconcile_payment.js | 35 +- .../report/account_balance/account_balance.js | 72 +- .../accounts_payable/accounts_payable.js | 243 +- .../accounts_payable_summary.js | 179 +- .../accounts_receivable.js | 274 +- .../accounts_receivable_summary.js | 215 +- .../asset_depreciation_ledger.js | 78 +- .../asset_depreciations_and_balances.js | 48 +- .../report/balance_sheet/balance_sheet.js | 44 +- .../bank_clearance_summary.js | 55 +- .../bank_reconciliation_statement.js | 71 +- .../billed_items_to_be_received.js | 38 +- .../budget_variance_report.js | 57 +- .../accounts/report/cash_flow/cash_flow.js | 21 +- .../consolidated_financial_statement.js | 212 +- .../customer_ledger_summary.js | 126 +- .../deferred_revenue_and_expense.js | 152 +- .../delivered_items_to_be_billed.js | 6 +- .../dimension_wise_accounts_balance_report.js | 102 +- .../general_and_payment_ledger_comparison.js | 60 +- .../report/general_ledger/general_ledger.js | 238 +- .../gross_and_net_profit_report.js | 23 +- .../report/gross_profit/gross_profit.js | 81 +- .../inactive_sales_items.js | 12 +- .../item_wise_purchase_register.js | 73 +- .../item_wise_sales_register.js | 89 +- .../report/payment_ledger/payment_ledger.js | 113 +- .../payment_period_based_on_invoice_date.js | 50 +- .../report/pos_register/pos_register.js | 91 +- .../profit_and_loss_statement.js | 48 +- .../profitability_analysis.js | 123 +- .../purchase_invoice_trends.js | 6 +- .../purchase_register/purchase_register.js | 86 +- .../received_items_to_be_billed.js | 6 +- .../sales_invoice_trends.js | 6 +- .../sales_payment_summary.js | 58 +- .../report/sales_register/sales_register.js | 102 +- .../report/share_balance/share_balance.js | 26 +- .../report/share_ledger/share_ledger.js | 24 +- .../supplier_ledger_summary.js | 102 +- .../accounts/report/tax_detail/tax_detail.js | 432 +- .../tds_computation_summary.js | 82 +- .../tds_payable_monthly.js | 82 +- .../report/trial_balance/trial_balance.js | 152 +- .../trial_balance_for_party.js | 114 +- .../voucher_wise_balance.js | 44 +- erpnext/assets/doctype/asset/asset.js | 701 +- erpnext/assets/doctype/asset/asset_list.js | 26 +- .../asset_capitalization.js | 159 +- .../doctype/asset_category/asset_category.js | 71 +- .../asset_maintenance/asset_maintenance.js | 73 +- .../asset_maintenance_log.js | 10 +- .../asset_maintenance_log_calendar.js | 24 +- .../asset_maintenance_log_list.js | 14 +- .../asset_maintenance_team.js | 6 +- .../doctype/asset_movement/asset_movement.js | 91 +- .../doctype/asset_repair/asset_repair.js | 76 +- .../doctype/asset_repair/asset_repair_list.js | 12 +- .../asset_shift_allocation.js | 10 +- .../asset_shift_factor/asset_shift_factor.js | 3 +- .../asset_value_adjustment.js | 42 +- erpnext/assets/doctype/location/location.js | 15 +- .../assets/doctype/location/location_tree.js | 14 +- .../maintenance_team_member.js | 6 +- .../fixed_asset_register.js | 104 +- .../bulk_transaction_log.js | 30 +- .../buying_settings/buying_settings.js | 23 +- .../purchase_order/purchase_order_list.js | 50 +- erpnext/buying/doctype/supplier/supplier.js | 191 +- .../buying/doctype/supplier/supplier_list.js | 8 +- .../supplier_quotation_list.js | 18 +- .../supplier_scorecard/supplier_scorecard.js | 62 +- .../supplier_scorecard_list.js | 4 +- .../supplier_scorecard_criteria.js | 2 +- .../supplier_scorecard_period.js | 5 +- .../supplier_scorecard_standing.js | 4 +- .../supplier_scorecard_variable.js | 4 +- .../procurement_tracker.js | 8 +- .../purchase_analytics/purchase_analytics.js | 85 +- .../purchase_order_analysis.js | 110 +- .../purchase_order_trends.js | 6 +- .../requested_items_to_order_and_receive.js | 96 +- .../subcontract_order_summary.js | 18 +- .../subcontracted_item_to_be_received.js | 16 +- ...tracted_raw_materials_to_be_transferred.js | 18 +- .../supplier_quotation_comparison.js | 128 +- .../crm/doctype/appointment/appointment.js | 24 +- .../appointment_booking_settings.js | 12 +- erpnext/crm/doctype/campaign/campaign.js | 24 +- erpnext/crm/doctype/competitor/competitor.js | 3 +- erpnext/crm/doctype/contract/contract.js | 12 +- erpnext/crm/doctype/contract/contract_list.js | 2 +- .../contract_fulfilment_checklist.js | 6 +- .../contract_template/contract_template.js | 6 +- .../crm/doctype/crm_settings/crm_settings.js | 3 +- .../doctype/email_campaign/email_campaign.js | 8 +- .../email_campaign/email_campaign_list.js | 12 +- erpnext/crm/doctype/lead/lead.js | 199 +- erpnext/crm/doctype/lead/lead_list.js | 54 +- .../crm/doctype/lead_source/lead_source.js | 3 +- .../linkedin_settings/linkedin_settings.js | 65 +- .../lost_reason_detail/lost_reason_detail.js | 6 +- .../doctype/market_segment/market_segment.js | 6 +- .../doctype/opportunity/opportunity_list.js | 28 +- .../opportunity_lost_reason.js | 6 +- .../opportunity_type/opportunity_type.js | 6 +- erpnext/crm/doctype/prospect/prospect.js | 43 +- .../crm/doctype/sales_stage/sales_stage.js | 6 +- .../social_media_post/social_media_post.js | 93 +- .../social_media_post_list.js | 23 +- .../twitter_settings/twitter_settings.js | 61 +- .../campaign_efficiency.js | 22 +- .../first_response_time_for_opportunity.js | 46 +- .../lead_conversion_time.js | 24 +- .../crm/report/lead_details/lead_details.js | 70 +- .../lead_owner_efficiency.js | 31 +- .../lost_opportunity/lost_opportunity.js | 80 +- .../opportunity_summary_by_sales_stage.js | 27 +- .../prospects_engaged_but_not_converted.js | 30 +- .../sales_pipeline_analytics.js | 24 +- .../e_commerce_settings.js | 51 +- .../doctype/item_review/item_review.js | 3 +- .../doctype/website_item/website_item.js | 42 +- .../doctype/website_item/website_item_list.js | 12 +- .../e_commerce/doctype/wishlist/wishlist.js | 3 +- erpnext/e_commerce/product_ui/grid.js | 59 +- erpnext/e_commerce/product_ui/list.js | 69 +- erpnext/e_commerce/product_ui/search.js | 63 +- erpnext/e_commerce/product_ui/views.js | 206 +- .../gocardless_mandate/gocardless_mandate.js | 3 +- .../gocardless_settings.js | 6 +- .../doctype/mpesa_settings/mpesa_settings.js | 21 +- .../doctype/plaid_settings/plaid_settings.js | 103 +- .../quickbooks_migrator.js | 70 +- .../tally_migration/tally_migration.js | 161 +- .../taxjar_settings/taxjar_settings.js | 20 +- .../woocommerce_settings.js | 44 +- .../top_10_pledged_loan_securities.js | 8 +- .../loan_management/doctype/loan/loan_list.js | 14 +- .../loan_balance_adjustment.js | 3 +- .../doctype/loan_refund/loan_refund.js | 3 +- .../doctype/loan_security/loan_security.js | 3 +- .../loan_security_pledge.js | 33 +- .../loan_security_pledge_list.js | 14 +- .../loan_security_price.js | 3 +- .../loan_security_shortfall.js | 18 +- .../loan_security_type/loan_security_type.js | 3 +- .../loan_security_unpledge.js | 11 +- .../loan_security_unpledge_list.js | 12 +- .../doctype/loan_type/loan_type.js | 26 +- .../loan_management/doctype/pledge/pledge.js | 3 +- .../process_loan_interest_accrual.js | 3 +- .../process_loan_security_shortfall.js | 8 +- .../sanctioned_loan_amount.js | 3 +- erpnext/loan_management/loan_common.js | 47 +- .../applicant_wise_loan_security_exposure.js | 18 +- .../loan_interest_report.js | 62 +- .../loan_repayment_and_closure.js | 51 +- .../loan_security_exposure.js | 18 +- .../loan_security_status.js | 58 +- .../maintenance_schedule.js | 182 +- .../maintenance_visit/maintenance_visit.js | 109 +- .../maintenance_visit_list.js | 20 +- .../doctype/blanket_order/blanket_order.js | 106 +- erpnext/manufacturing/doctype/bom/bom.js | 477 +- erpnext/manufacturing/doctype/bom/bom_list.js | 14 +- erpnext/manufacturing/doctype/bom/bom_tree.js | 47 +- .../doctype/bom_update_log/bom_update_log.js | 3 +- .../bom_update_log/bom_update_log_list.js | 18 +- .../bom_update_tool/bom_update_tool.js | 40 +- .../doctype/downtime_entry/downtime_entry.js | 3 +- .../doctype/job_card/job_card.js | 335 +- .../doctype/job_card/job_card_calendar.js | 42 +- .../doctype/job_card/job_card_list.js | 12 +- .../manufacturing_settings.js | 23 +- .../material_request_plan_item.js | 6 +- .../doctype/operation/operation.js | 24 +- .../production_plan/production_plan.js | 358 +- .../production_plan/production_plan_list.js | 24 +- ...duction_plan_material_request_warehouse.js | 3 +- .../manufacturing/doctype/routing/routing.js | 57 +- .../doctype/sub_operation/sub_operation.js | 3 +- .../doctype/work_order/work_order.js | 791 +- .../doctype/work_order/work_order_calendar.js | 56 +- .../doctype/work_order/work_order_list.js | 40 +- .../doctype/workstation/_test_workstation.js | 16 +- .../doctype/workstation/workstation.js | 41 +- .../doctype/workstation/workstation_list.js | 2 +- .../workstation_type/workstation_type.js | 3 +- .../bom_comparison_tool.js | 181 +- .../report/bom_explorer/bom_explorer.js | 6 +- .../bom_operations_time.js | 52 +- .../bom_stock_calculated.js | 44 +- .../bom_stock_report/bom_stock_report.js | 59 +- .../bom_variance_report.js | 36 +- .../cost_of_poor_quality_report.js | 64 +- .../downtime_analysis/downtime_analysis.js | 18 +- .../exponential_smoothing_forecasting.js | 130 +- .../job_card_summary/job_card_summary.js | 40 +- .../process_loss_report.js | 66 +- .../production_analytics.js | 26 +- .../production_plan_summary.js | 18 +- .../production_planning_report.js | 116 +- .../quality_inspection_summary.js | 18 +- .../work_order_consumed_materials.js | 34 +- .../work_order_stock_report.js | 16 +- .../work_order_summary/work_order_summary.js | 38 +- erpnext/portal/doctype/homepage/homepage.js | 32 +- .../homepage_section/homepage_section.js | 4 +- .../doctype/activity_cost/activity_cost.js | 2 +- .../doctype/activity_type/activity_type.js | 15 +- erpnext/projects/doctype/project/project.js | 186 +- .../projects/doctype/project/project_list.js | 10 +- .../doctype/project/project_timesheet.js | 142 +- .../project_template/project_template.js | 12 +- .../doctype/project_type/project_type.js | 4 +- .../doctype/project_update/project_update.js | 8 +- .../projects_settings/projects_settings.js | 6 +- erpnext/projects/doctype/task/task.js | 39 +- .../projects/doctype/task/task_calendar.js | 26 +- erpnext/projects/doctype/task/task_list.js | 40 +- erpnext/projects/doctype/task/task_tree.js | 70 +- .../projects/doctype/task_type/task_type.js | 3 +- .../projects/doctype/timesheet/timesheet.js | 276 +- .../doctype/timesheet/timesheet_calendar.js | 42 +- .../doctype/timesheet/timesheet_list.js | 18 +- .../daily_timesheet_summary.js | 22 +- .../delayed_tasks_summary.js | 36 +- .../employee_billing_summary.js | 18 +- .../project_billing_summary.js | 20 +- .../report/project_summary/project_summary.js | 54 +- .../project_wise_stock_tracking.js | 6 +- erpnext/projects/web_form/tasks/tasks.js | 4 +- erpnext/public/js/account_tree_grid.js | 193 +- erpnext/public/js/address.js | 27 +- erpnext/public/js/agriculture/ternary_plot.js | 219 +- .../data_table_manager.js | 63 +- .../dialog_manager.js | 103 +- .../bank_reconciliation_tool/number_card.js | 19 +- .../public/js/bulk_transaction_processing.js | 27 +- erpnext/public/js/call_popup/call_popup.js | 232 +- erpnext/public/js/communication.js | 136 +- erpnext/public/js/conf.js | 20 +- erpnext/public/js/contact.js | 8 +- erpnext/public/js/customer_reviews.js | 44 +- erpnext/public/js/erpnext-web.bundle.js | 2 +- erpnext/public/js/event.js | 62 +- erpnext/public/js/financial_statements.js | 267 +- erpnext/public/js/help_links.js | 140 +- .../hierarchy_chart_desktop.js | 328 +- .../hierarchy_chart/hierarchy_chart_mobile.js | 267 +- erpnext/public/js/leaflet/leaflet.draw.js | 1863 ++++- erpnext/public/js/leaflet/leaflet.js | 6379 +++++++++++++++-- erpnext/public/js/newsletter.js | 4 +- erpnext/public/js/payment/payments.js | 222 +- erpnext/public/js/projects/timer.js | 77 +- erpnext/public/js/purchase_trends_filters.js | 98 +- erpnext/public/js/queries.js | 156 +- erpnext/public/js/sales_trends_filters.js | 86 +- erpnext/public/js/setup_wizard.js | 176 +- erpnext/public/js/shopping_cart.js | 127 +- erpnext/public/js/sms_manager.js | 130 +- erpnext/public/js/stock_analytics.js | 162 +- erpnext/public/js/stock_grid_report.js | 53 +- erpnext/public/js/telephony.js | 31 +- erpnext/public/js/utils.js | 988 +-- erpnext/public/js/utils/barcode_scanner.js | 110 +- .../js/utils/contact_address_quick_entry.js | 142 +- erpnext/public/js/utils/crm_activities.js | 153 +- .../public/js/utils/customer_quick_entry.js | 2 +- .../public/js/utils/dimension_tree_filter.js | 62 +- erpnext/public/js/utils/item_quick_entry.js | 286 +- erpnext/public/js/utils/item_selector.js | 63 +- erpnext/public/js/utils/party.js | 201 +- .../js/utils/serial_no_batch_selector.js | 486 +- .../public/js/utils/supplier_quick_entry.js | 2 +- erpnext/public/js/utils/unreconcile.js | 137 +- erpnext/public/js/website_theme.js | 13 +- erpnext/public/js/website_utils.js | 14 +- erpnext/public/js/wishlist.js | 79 +- erpnext/public/scss/erpnext.scss | 72 +- erpnext/public/scss/hierarchy_chart.scss | 19 +- erpnext/public/scss/order-page.scss | 150 +- erpnext/public/scss/point-of-sale.scss | 13 +- erpnext/public/scss/shopping_cart.scss | 110 +- erpnext/public/scss/website.scss | 10 +- .../non_conformance/non_conformance.js | 3 +- .../doctype/quality_action/quality_action.js | 4 +- .../quality_feedback/quality_feedback.js | 8 +- .../quality_feedback_template.js | 3 +- .../doctype/quality_goal/quality_goal.js | 2 +- .../quality_meeting/quality_meeting.js | 4 +- .../quality_meeting/quality_meeting_list.js | 11 +- .../quality_meeting_agenda.js | 3 +- .../quality_procedure/quality_procedure.js | 18 +- .../quality_procedure_tree.js | 20 +- .../doctype/quality_review/quality_review.js | 12 +- .../quality_review/quality_review_list.js | 12 +- .../import_supplier_invoice.js | 29 +- .../ksa_vat_sales_account.js | 3 +- .../ksa_vat_setting/ksa_vat_setting.js | 6 +- .../ksa_vat_setting/ksa_vat_setting_list.js | 10 +- .../lower_deduction_certificate.js | 3 +- .../product_tax_category.js | 3 +- .../south_africa_vat_settings.js | 16 +- .../uae_vat_settings/uae_vat_settings.js | 12 +- .../electronic_invoice_register.js | 58 +- .../fichier_des_ecritures_comptables_[fec].js | 62 +- erpnext/regional/report/irs_1099/irs_1099.js | 53 +- erpnext/regional/report/ksa_vat/ksa_vat.js | 62 +- .../report/uae_vat_201/uae_vat_201.js | 45 +- .../vat_audit_report/vat_audit_report.js | 40 +- erpnext/selling/doctype/customer/customer.js | 232 +- .../selling/doctype/customer/customer_list.js | 2 +- .../doctype/industry_type/industry_type.js | 10 +- .../installation_note/installation_note.js | 49 +- .../party_specific_item.js | 3 +- .../doctype/quotation/quotation_list.js | 33 +- .../sales_order/sales_order_calendar.js | 55 +- .../doctype/sales_order/sales_order_list.js | 65 +- .../sales_partner_type/sales_partner_type.js | 6 +- .../selling_settings/selling_settings.js | 6 +- .../selling/doctype/sms_center/sms_center.js | 4 +- .../page/point_of_sale/point_of_sale.js | 12 +- .../page/point_of_sale/pos_controller.js | 361 +- .../page/point_of_sale/pos_item_cart.js | 635 +- .../page/point_of_sale/pos_item_details.js | 183 +- .../page/point_of_sale/pos_item_selector.js | 196 +- .../page/point_of_sale/pos_number_pad.js | 32 +- .../page/point_of_sale/pos_past_order_list.js | 56 +- .../point_of_sale/pos_past_order_summary.js | 205 +- .../selling/page/point_of_sale/pos_payment.js | 298 +- .../selling/page/sales_funnel/sales_funnel.js | 134 +- .../address_and_contacts.js | 44 +- .../available_stock_for_packing_items.js | 6 +- .../customer_acquisition_and_loyalty.js | 54 +- .../customer_credit_balance.js | 28 +- .../customer_wise_item_price.js | 34 +- .../inactive_customers/inactive_customers.js | 26 +- .../item_wise_sales_history.js | 32 +- .../payment_terms_status_for_sales_order.js | 189 +- .../pending_so_items_for_purchase_request.js | 3 +- .../quotation_trends/quotation_trends.js | 6 +- .../report/sales_analytics/sales_analytics.js | 70 +- .../sales_order_analysis.js | 102 +- .../sales_order_trends/sales_order_trends.js | 6 +- .../sales_partner_commission_summary.js | 24 +- ...ner_target_variance_based_on_item_group.js | 34 +- .../sales_partner_transaction_summary.js | 28 +- .../sales_person_commission_summary.js | 24 +- ...son_target_variance_based_on_item_group.js | 34 +- .../sales_person_wise_transaction_summary.js | 28 +- ...ory_target_variance_based_on_item_group.js | 34 +- .../territory_wise_sales.js | 16 +- .../authorization_rule/authorization_rule.js | 96 +- erpnext/setup/doctype/branch/branch.js | 6 +- erpnext/setup/doctype/brand/brand.js | 122 +- erpnext/setup/doctype/company/company.js | 358 +- erpnext/setup/doctype/company/company_list.js | 4 +- erpnext/setup/doctype/company/company_tree.js | 24 +- .../currency_exchange/currency_exchange.js | 21 +- .../doctype/customer_group/customer_group.js | 39 +- .../customer_group/customer_group_tree.js | 4 +- .../setup/doctype/department/department.js | 16 +- .../doctype/department/department_tree.js | 18 +- .../setup/doctype/designation/designation.js | 6 +- erpnext/setup/doctype/driver/driver.js | 24 +- .../doctype/email_digest/email_digest.js | 24 +- erpnext/setup/doctype/employee/employee.js | 85 +- .../setup/doctype/employee/employee_list.js | 12 +- .../setup/doctype/employee/employee_tree.js | 26 +- .../doctype/employee_group/employee_group.js | 4 +- .../global_defaults/global_defaults.js | 24 +- .../doctype/holiday_list/holiday_list.js | 18 +- .../holiday_list/holiday_list_calendar.js | 24 +- .../setup/doctype/item_group/item_group.js | 123 +- .../doctype/item_group/item_group_tree.js | 4 +- .../setup/doctype/party_type/party_type.js | 18 +- .../doctype/print_heading/print_heading.js | 10 +- .../quotation_lost_reason.js | 10 +- .../doctype/sales_partner/sales_partner.js | 33 +- .../doctype/sales_person/sales_person.js | 72 +- .../doctype/sales_person/sales_person_tree.js | 24 +- .../doctype/supplier_group/supplier_group.js | 26 +- .../supplier_group/supplier_group_tree.js | 2 +- erpnext/setup/doctype/territory/territory.js | 38 +- .../setup/doctype/territory/territory_tree.js | 4 +- .../transaction_deletion_record.js | 33 +- .../transaction_deletion_record_list.js | 6 +- erpnext/setup/doctype/uom/uom.js | 10 +- .../uom_conversion_factor.js | 6 +- erpnext/setup/doctype/vehicle/vehicle.js | 6 +- .../welcome_to_erpnext/welcome_to_erpnext.js | 21 +- erpnext/stock/dashboard/item_dashboard.js | 232 +- .../warehouse_wise_stock_value.js | 8 +- erpnext/stock/doctype/batch/batch.js | 176 +- erpnext/stock/doctype/batch/batch_list.js | 17 +- erpnext/stock/doctype/bin/bin.js | 6 +- .../closing_stock_balance.js | 14 +- .../customs_tariff_number.js | 6 +- .../delivery_note/delivery_note_list.js | 77 +- .../delivery_settings/delivery_settings.js | 6 +- .../doctype/delivery_trip/delivery_trip.js | 166 +- .../delivery_trip/delivery_trip_list.js | 4 +- .../inventory_dimension.js | 77 +- erpnext/stock/doctype/item/item.js | 871 ++- erpnext/stock/doctype/item/item_list.js | 30 +- .../item_alternative/item_alternative.js | 10 +- .../doctype/item_attribute/item_attribute.js | 4 +- .../item_manufacturer/item_manufacturer.js | 3 +- .../stock/doctype/item_price/item_price.js | 21 +- .../doctype/item_price/item_price_list.js | 2 +- .../item_variant_settings.js | 43 +- .../doctype/manufacturer/manufacturer.js | 15 +- .../material_request/material_request_list.js | 20 +- .../doctype/packing_slip/packing_slip.js | 52 +- erpnext/stock/doctype/pick_list/pick_list.js | 228 +- .../stock/doctype/pick_list/pick_list_list.js | 12 +- .../stock/doctype/price_list/price_list.js | 20 +- .../purchase_receipt/purchase_receipt_list.js | 27 +- .../doctype/putaway_rule/putaway_rule.js | 32 +- .../doctype/putaway_rule/putaway_rule_list.js | 12 +- .../quality_inspection/quality_inspection.js | 57 +- .../quality_inspection_parameter.js | 3 +- .../quality_inspection_parameter_group.js | 3 +- .../quality_inspection_template.js | 6 +- .../quick_stock_balance.js | 63 +- .../repost_item_valuation.js | 94 +- erpnext/stock/doctype/serial_no/serial_no.js | 20 +- .../stock/doctype/serial_no/serial_no_list.js | 15 +- erpnext/stock/doctype/shipment/shipment.js | 451 +- .../stock/doctype/shipment/shipment_list.js | 8 +- .../shipment_parcel_template.js | 3 +- .../doctype/stock_entry/stock_entry_list.js | 57 +- .../stock_entry_type/stock_entry_type.js | 3 +- .../stock_ledger_entry/stock_ledger_entry.js | 8 +- .../stock_reconciliation.js | 218 +- .../stock_reposting_settings.js | 22 +- .../doctype/stock_settings/stock_settings.js | 24 +- .../doctype/uom_category/uom_category.js | 6 +- .../doctype/variant_field/variant_field.js | 6 +- erpnext/stock/doctype/warehouse/warehouse.js | 30 +- .../stock/doctype/warehouse/warehouse_tree.js | 35 +- .../doctype/warehouse_type/warehouse_type.js | 3 +- .../stock/landed_taxes_and_charges_common.js | 53 +- .../stock/page/stock_balance/stock_balance.js | 98 +- .../warehouse_capacity_summary.js | 110 +- .../batch_item_expiry_status.js | 50 +- .../batch_wise_balance_history.js | 105 +- erpnext/stock/report/bom_search/bom_search.js | 16 +- .../cogs_by_item_group/cogs_by_item_group.js | 47 +- .../delayed_item_report.js | 28 +- .../delayed_order_report.js | 28 +- .../delivery_note_trends.js | 6 +- ...eue_vs_qty_after_transaction_comparison.js | 55 +- ...incorrect_balance_qty_after_transaction.js | 22 +- .../incorrect_serial_no_valuation.js | 40 +- .../incorrect_stock_value_report.js | 46 +- .../item_price_stock/item_price_stock.js | 16 +- .../stock/report/item_prices/item_prices.js | 22 +- .../item_shortage_report.js | 34 +- .../item_variant_details.js | 14 +- .../itemwise_recommended_reorder_level.js | 42 +- .../product_bundle_balance.js | 64 +- .../purchase_receipt_trends.js | 6 +- .../serial_no_ledger/serial_no_ledger.js | 62 +- .../stock/report/stock_ageing/stock_ageing.js | 98 +- .../report/stock_analytics/stock_analytics.js | 85 +- .../stock_and_account_value_comparison.js | 55 +- .../report/stock_balance/stock_balance.js | 141 +- .../stock/report/stock_ledger/stock_ledger.js | 133 +- .../stock_ledger_invariant_check.js | 70 +- .../stock_ledger_variance.js | 79 +- .../stock_projected_qty.js | 76 +- .../stock_qty_vs_serial_no_count.js | 39 +- .../supplier_wise_sales_analytics.js | 36 +- .../total_stock_summary.js | 36 +- ...rehouse_wise_item_balance_age_and_value.js | 92 +- .../warehouse_wise_stock_balance.js | 33 +- .../subcontracting_order_list.js | 14 +- .../subcontracting_receipt_list.js | 10 +- erpnext/support/doctype/issue/issue.js | 108 +- erpnext/support/doctype/issue/issue_list.js | 34 +- .../doctype/issue_priority/issue_priority.js | 3 +- .../support/doctype/issue_type/issue_type.js | 6 +- .../service_level_agreement.js | 140 +- .../support_settings/support_settings.js | 6 +- .../doctype/warranty_claim/warranty_claim.js | 5 +- .../warranty_claim/warranty_claim_list.js | 4 +- .../first_response_time_for_issues.js | 50 +- .../report/issue_analytics/issue_analytics.js | 85 +- .../report/issue_summary/issue_summary.js | 38 +- .../support_hour_distribution.js | 28 +- erpnext/support/web_form/issues/issues.js | 4 +- .../telephony/doctype/call_log/call_log.js | 16 +- .../incoming_call_settings.js | 28 +- .../telephony_call_type.js | 3 +- .../voice_call_settings.js | 3 +- .../generators/item/item_configure.js | 234 +- .../templates/generators/item/item_inquiry.js | 88 +- erpnext/templates/pages/cart.js | 135 +- erpnext/templates/pages/projects.js | 123 +- .../doctype/rename_tool/rename_tool.js | 21 +- erpnext/utilities/doctype/sms_log/sms_log.js | 6 +- erpnext/utilities/doctype/video/video.js | 6 +- erpnext/utilities/doctype/video/video_list.js | 8 +- .../doctype/video_settings/video_settings.js | 3 +- .../youtube_interactions.js | 8 +- .../utilities/web_form/addresses/addresses.js | 4 +- erpnext/www/all-products/index.js | 4 +- erpnext/www/book_appointment/index.js | 393 +- erpnext/www/shop-by-category/index.js | 6 +- 605 files changed, 27454 insertions(+), 18952 deletions(-) diff --git a/commitlint.config.js b/commitlint.config.js index 8847564e53c..56702092214 100644 --- a/commitlint.config.js +++ b/commitlint.config.js @@ -1,25 +1,13 @@ module.exports = { - parserPreset: 'conventional-changelog-conventionalcommits', + parserPreset: "conventional-changelog-conventionalcommits", rules: { - 'subject-empty': [2, 'never'], - 'type-case': [2, 'always', 'lower-case'], - 'type-empty': [2, 'never'], - 'type-enum': [ + "subject-empty": [2, "never"], + "type-case": [2, "always", "lower-case"], + "type-empty": [2, "never"], + "type-enum": [ 2, - 'always', - [ - 'build', - 'chore', - 'ci', - 'docs', - 'feat', - 'fix', - 'perf', - 'refactor', - 'revert', - 'style', - 'test', - ], + "always", + ["build", "chore", "ci", "docs", "feat", "fix", "perf", "refactor", "revert", "style", "test"], ], }, }; diff --git a/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.js b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.js index d8a83e53dc0..09846114ea2 100644 --- a/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.js +++ b/erpnext/accounts/dashboard_chart_source/account_balance_timeline/account_balance_timeline.js @@ -1,4 +1,4 @@ -frappe.provide('frappe.dashboards.chart_sources'); +frappe.provide("frappe.dashboards.chart_sources"); frappe.dashboards.chart_sources["Account Balance Timeline"] = { method: "erpnext.accounts.dashboard_chart_source.account_balance_timeline.account_balance_timeline.get", @@ -9,14 +9,14 @@ frappe.dashboards.chart_sources["Account Balance Timeline"] = { fieldtype: "Link", options: "Company", default: frappe.defaults.get_user_default("Company"), - reqd: 1 + reqd: 1, }, { fieldname: "account", label: __("Account"), fieldtype: "Link", options: "Account", - reqd: 1 + reqd: 1, }, - ] + ], }; diff --git a/erpnext/accounts/doctype/account/account.js b/erpnext/accounts/doctype/account/account.js index 7d63b257faf..f26d1f82797 100644 --- a/erpnext/accounts/doctype/account/account.js +++ b/erpnext/accounts/doctype/account/account.js @@ -1,33 +1,32 @@ // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.ui.form.on('Account', { - setup: function(frm) { - frm.add_fetch('parent_account', 'report_type', 'report_type'); - frm.add_fetch('parent_account', 'root_type', 'root_type'); +frappe.ui.form.on("Account", { + setup: function (frm) { + frm.add_fetch("parent_account", "report_type", "report_type"); + frm.add_fetch("parent_account", "root_type", "root_type"); }, - onload: function(frm) { - frm.set_query('parent_account', function(doc) { + onload: function (frm) { + frm.set_query("parent_account", function (doc) { return { filters: { - "is_group": 1, - "company": doc.company - } + is_group: 1, + company: doc.company, + }, }; }); }, - refresh: function(frm) { - frm.toggle_display('account_name', frm.is_new()); + refresh: function (frm) { + frm.toggle_display("account_name", frm.is_new()); // hide fields if group - frm.toggle_display(['account_type', 'tax_rate'], cint(frm.doc.is_group) == 0); + frm.toggle_display(["account_type", "tax_rate"], cint(frm.doc.is_group) == 0); // disable fields - frm.toggle_enable(['is_group', 'company'], false); + frm.toggle_enable(["is_group", "company"], false); if (cint(frm.doc.is_group) == 0) { - frm.toggle_display('freeze_account', frm.doc.__onload - && frm.doc.__onload.can_freeze_account); + frm.toggle_display("freeze_account", frm.doc.__onload && frm.doc.__onload.can_freeze_account); } // read-only for root accounts @@ -38,79 +37,101 @@ frappe.ui.form.on('Account', { } else { // credit days and type if customer or supplier frm.set_intro(null); - frm.trigger('account_type'); + frm.trigger("account_type"); // show / hide convert buttons - frm.trigger('add_toolbar_buttons'); + frm.trigger("add_toolbar_buttons"); } - if (frm.has_perm('write')) { - frm.add_custom_button(__('Merge Account'), function () { - frm.trigger("merge_account"); - }, __('Actions')); - frm.add_custom_button(__('Update Account Name / Number'), function () { - frm.trigger("update_account_number"); - }, __('Actions')); + if (frm.has_perm("write")) { + frm.add_custom_button( + __("Merge Account"), + function () { + frm.trigger("merge_account"); + }, + __("Actions") + ); + frm.add_custom_button( + __("Update Account Name / Number"), + function () { + frm.trigger("update_account_number"); + }, + __("Actions") + ); } } }, account_type: function (frm) { if (frm.doc.is_group == 0) { - frm.toggle_display(['tax_rate'], frm.doc.account_type == 'Tax'); - frm.toggle_display('warehouse', frm.doc.account_type == 'Stock'); + frm.toggle_display(["tax_rate"], frm.doc.account_type == "Tax"); + frm.toggle_display("warehouse", frm.doc.account_type == "Stock"); } }, - add_toolbar_buttons: function(frm) { - frm.add_custom_button(__('Chart of Accounts'), () => { - frappe.set_route("Tree", "Account"); - }, __('View')); + add_toolbar_buttons: function (frm) { + frm.add_custom_button( + __("Chart of Accounts"), + () => { + frappe.set_route("Tree", "Account"); + }, + __("View") + ); if (frm.doc.is_group == 1) { - frm.add_custom_button(__('Convert to Non-Group'), function () { - return frappe.call({ - doc: frm.doc, - method: 'convert_group_to_ledger', - callback: function() { - frm.refresh(); - } - }); - }, __('Actions')); + frm.add_custom_button( + __("Convert to Non-Group"), + function () { + return frappe.call({ + doc: frm.doc, + method: "convert_group_to_ledger", + callback: function () { + frm.refresh(); + }, + }); + }, + __("Actions") + ); + } else if (cint(frm.doc.is_group) == 0 && frappe.boot.user.can_read.indexOf("GL Entry") !== -1) { + frm.add_custom_button( + __("General Ledger"), + function () { + frappe.route_options = { + account: frm.doc.name, + from_date: frappe.sys_defaults.year_start_date, + to_date: frappe.sys_defaults.year_end_date, + company: frm.doc.company, + }; + frappe.set_route("query-report", "General Ledger"); + }, + __("View") + ); - } else if (cint(frm.doc.is_group) == 0 - && frappe.boot.user.can_read.indexOf("GL Entry") !== -1) { - frm.add_custom_button(__('General Ledger'), function () { - frappe.route_options = { - "account": frm.doc.name, - "from_date": frappe.sys_defaults.year_start_date, - "to_date": frappe.sys_defaults.year_end_date, - "company": frm.doc.company - }; - frappe.set_route("query-report", "General Ledger"); - }, __('View')); - - frm.add_custom_button(__('Convert to Group'), function () { - return frappe.call({ - doc: frm.doc, - method: 'convert_ledger_to_group', - callback: function() { - frm.refresh(); - } - }); - }, __('Actions')); + frm.add_custom_button( + __("Convert to Group"), + function () { + return frappe.call({ + doc: frm.doc, + method: "convert_ledger_to_group", + callback: function () { + frm.refresh(); + }, + }); + }, + __("Actions") + ); } }, - merge_account: function(frm) { + merge_account: function (frm) { var d = new frappe.ui.Dialog({ - title: __('Merge with Existing Account'), + title: __("Merge with Existing Account"), fields: [ { - "label" : "Name", - "fieldname": "name", - "fieldtype": "Data", - "reqd": 1, - "default": frm.doc.name - } + label: "Name", + fieldname: "name", + fieldtype: "Data", + reqd: 1, + default: frm.doc.name, + }, ], - primary_action: function() { + primary_action: function () { var data = d.get_values(); frappe.call({ method: "erpnext.accounts.doctype.account.account.merge_account", @@ -118,42 +139,45 @@ frappe.ui.form.on('Account', { old: frm.doc.name, new: data.name, }, - callback: function(r) { - if(!r.exc) { - if(r.message) { + callback: function (r) { + if (!r.exc) { + if (r.message) { frappe.set_route("Form", "Account", r.message); } d.hide(); } - } + }, }); }, - primary_action_label: __('Merge') + primary_action_label: __("Merge"), }); d.show(); }, - update_account_number: function(frm) { + update_account_number: function (frm) { var d = new frappe.ui.Dialog({ - title: __('Update Account Number / Name'), + title: __("Update Account Number / Name"), fields: [ { - "label": "Account Name", - "fieldname": "account_name", - "fieldtype": "Data", - "reqd": 1, - "default": frm.doc.account_name + label: "Account Name", + fieldname: "account_name", + fieldtype: "Data", + reqd: 1, + default: frm.doc.account_name, }, { - "label": "Account Number", - "fieldname": "account_number", - "fieldtype": "Data", - "default": frm.doc.account_number - } + label: "Account Number", + fieldname: "account_number", + fieldtype: "Data", + default: frm.doc.account_number, + }, ], - primary_action: function() { + primary_action: function () { var data = d.get_values(); - if(data.account_number === frm.doc.account_number && data.account_name === frm.doc.account_name) { + if ( + data.account_number === frm.doc.account_number && + data.account_name === frm.doc.account_name + ) { d.hide(); return; } @@ -163,11 +187,11 @@ frappe.ui.form.on('Account', { args: { account_number: data.account_number, account_name: data.account_name, - name: frm.doc.name + name: frm.doc.name, }, - callback: function(r) { - if(!r.exc) { - if(r.message) { + callback: function (r) { + if (!r.exc) { + if (r.message) { frappe.set_route("Form", "Account", r.message); } else { frm.set_value("account_number", data.account_number); @@ -175,11 +199,11 @@ frappe.ui.form.on('Account', { } d.hide(); } - } + }, }); }, - primary_action_label: __('Update') + primary_action_label: __("Update"), }); d.show(); - } + }, }); diff --git a/erpnext/accounts/doctype/account/account_tree.js b/erpnext/accounts/doctype/account/account_tree.js index d537adfcbfd..e0bfa6e902a 100644 --- a/erpnext/accounts/doctype/account/account_tree.js +++ b/erpnext/accounts/doctype/account/account_tree.js @@ -1,4 +1,4 @@ -frappe.provide("frappe.treeview_settings") +frappe.provide("frappe.treeview_settings"); frappe.treeview_settings["Account"] = { breadcrumb: "Accounts", @@ -7,12 +7,12 @@ frappe.treeview_settings["Account"] = { filters: [ { fieldname: "company", - fieldtype:"Select", + fieldtype: "Select", options: erpnext.utils.get_tree_options("company"), label: __("Company"), default: erpnext.utils.get_tree_default("company"), - on_change: function() { - var me = frappe.treeview_settings['Account'].treeview; + on_change: function () { + var me = frappe.treeview_settings["Account"].treeview; var company = me.page.fields_dict.company.get_value(); if (!company) { frappe.throw(__("Please set a Company")); @@ -22,30 +22,36 @@ frappe.treeview_settings["Account"] = { args: { company: company, }, - callback: function(r) { - if(r.message) { + callback: function (r) { + if (r.message) { let root_company = r.message.length ? r.message[0] : ""; me.page.fields_dict.root_company.set_value(root_company); - frappe.db.get_value("Company", {"name": company}, "allow_account_creation_against_child_company", (r) => { - frappe.flags.ignore_root_company_validation = r.allow_account_creation_against_child_company; - }); + frappe.db.get_value( + "Company", + { name: company }, + "allow_account_creation_against_child_company", + (r) => { + frappe.flags.ignore_root_company_validation = + r.allow_account_creation_against_child_company; + } + ); } - } + }, }); - } + }, }, { fieldname: "root_company", - fieldtype:"Data", + fieldtype: "Data", label: __("Root Company"), hidden: true, - disable_onchange: true - } + disable_onchange: true, + }, ], root_label: "Accounts", - get_tree_nodes: 'erpnext.accounts.utils.get_children', - on_get_node: function(nodes, deep=false) { + get_tree_nodes: "erpnext.accounts.utils.get_children", + on_get_node: function (nodes, deep = false) { if (frappe.boot.user.can_read.indexOf("GL Entry") == -1) return; let accounts = []; @@ -57,151 +63,231 @@ frappe.treeview_settings["Account"] = { } frappe.db.get_single_value("Accounts Settings", "show_balance_in_coa").then((value) => { - if(value) { - + if (value) { const get_balances = frappe.call({ - method: 'erpnext.accounts.utils.get_account_balances', + method: "erpnext.accounts.utils.get_account_balances", args: { accounts: accounts, - company: cur_tree.args.company + company: cur_tree.args.company, }, }); - get_balances.then(r => { + get_balances.then((r) => { if (!r.message || r.message.length == 0) return; for (let account of r.message) { - const node = cur_tree.nodes && cur_tree.nodes[account.value]; if (!node || node.is_root) continue; // show Dr if positive since balance is calculated as debit - credit else show Cr const balance = account.balance_in_account_currency || account.balance; - const dr_or_cr = balance > 0 ? "Dr": "Cr"; + const dr_or_cr = balance > 0 ? "Dr" : "Cr"; const format = (value, currency) => format_currency(Math.abs(value), currency); - if (account.balance!==undefined) { - node.parent && node.parent.find('.balance-area').remove(); - $('' - + (account.balance_in_account_currency ? - (format(account.balance_in_account_currency, account.account_currency) + " / ") : "") - + format(account.balance, account.company_currency) - + " " + dr_or_cr - + '').insertBefore(node.$ul); + if (account.balance !== undefined) { + node.parent && node.parent.find(".balance-area").remove(); + $( + '' + + (account.balance_in_account_currency + ? format( + account.balance_in_account_currency, + account.account_currency + ) + " / " + : "") + + format(account.balance, account.company_currency) + + " " + + dr_or_cr + + "" + ).insertBefore(node.$ul); } } }); } }); }, - add_tree_node: 'erpnext.accounts.utils.add_ac', - menu_items:[ + add_tree_node: "erpnext.accounts.utils.add_ac", + menu_items: [ { - label: __('New Company'), - action: function() { frappe.new_doc("Company", true) }, - condition: 'frappe.boot.user.can_create.indexOf("Company") !== -1' - } + label: __("New Company"), + action: function () { + frappe.new_doc("Company", true); + }, + condition: 'frappe.boot.user.can_create.indexOf("Company") !== -1', + }, ], fields: [ - {fieldtype:'Data', fieldname:'account_name', label:__('New Account Name'), reqd:true, - description: __("Name of new Account. Note: Please don't create accounts for Customers and Suppliers")}, - {fieldtype:'Data', fieldname:'account_number', label:__('Account Number'), - description: __("Number of new Account, it will be included in the account name as a prefix")}, - {fieldtype:'Check', fieldname:'is_group', label:__('Is Group'), - description: __('Further accounts can be made under Groups, but entries can be made against non-Groups')}, - {fieldtype:'Select', fieldname:'root_type', label:__('Root Type'), - options: ['Asset', 'Liability', 'Equity', 'Income', 'Expense'].join('\n'), - depends_on: 'eval:doc.is_group && !doc.parent_account'}, - {fieldtype:'Select', fieldname:'account_type', label:__('Account Type'), - options: frappe.get_meta("Account").fields.filter(d => d.fieldname=='account_type')[0].options, - description: __("Optional. This setting will be used to filter in various transactions.") + { + fieldtype: "Data", + fieldname: "account_name", + label: __("New Account Name"), + reqd: true, + description: __( + "Name of new Account. Note: Please don't create accounts for Customers and Suppliers" + ), + }, + { + fieldtype: "Data", + fieldname: "account_number", + label: __("Account Number"), + description: __("Number of new Account, it will be included in the account name as a prefix"), + }, + { + fieldtype: "Check", + fieldname: "is_group", + label: __("Is Group"), + description: __( + "Further accounts can be made under Groups, but entries can be made against non-Groups" + ), + }, + { + fieldtype: "Select", + fieldname: "root_type", + label: __("Root Type"), + options: ["Asset", "Liability", "Equity", "Income", "Expense"].join("\n"), + depends_on: "eval:doc.is_group && !doc.parent_account", + }, + { + fieldtype: "Select", + fieldname: "account_type", + label: __("Account Type"), + options: frappe.get_meta("Account").fields.filter((d) => d.fieldname == "account_type")[0] + .options, + description: __("Optional. This setting will be used to filter in various transactions."), + }, + { + fieldtype: "Float", + fieldname: "tax_rate", + label: __("Tax Rate"), + depends_on: 'eval:doc.is_group==0&&doc.account_type=="Tax"', + }, + { + fieldtype: "Link", + fieldname: "account_currency", + label: __("Currency"), + options: "Currency", + description: __("Optional. Sets company's default currency, if not specified."), }, - {fieldtype:'Float', fieldname:'tax_rate', label:__('Tax Rate'), - depends_on: 'eval:doc.is_group==0&&doc.account_type=="Tax"'}, - {fieldtype:'Link', fieldname:'account_currency', label:__('Currency'), options:"Currency", - description: __("Optional. Sets company's default currency, if not specified.")} ], - ignore_fields:["parent_account"], - onload: function(treeview) { - frappe.treeview_settings['Account'].treeview = {}; - $.extend(frappe.treeview_settings['Account'].treeview, treeview); + ignore_fields: ["parent_account"], + onload: function (treeview) { + frappe.treeview_settings["Account"].treeview = {}; + $.extend(frappe.treeview_settings["Account"].treeview, treeview); function get_company() { return treeview.page.fields_dict.company.get_value(); } // tools - treeview.page.add_inner_button(__("Chart of Cost Centers"), function() { - frappe.set_route('Tree', 'Cost Center', {company: get_company()}); - }, __('View')); + treeview.page.add_inner_button( + __("Chart of Cost Centers"), + function () { + frappe.set_route("Tree", "Cost Center", { company: get_company() }); + }, + __("View") + ); - treeview.page.add_inner_button(__("Opening Invoice Creation Tool"), function() { - frappe.set_route('Form', 'Opening Invoice Creation Tool', {company: get_company()}); - }, __('View')); + treeview.page.add_inner_button( + __("Opening Invoice Creation Tool"), + function () { + frappe.set_route("Form", "Opening Invoice Creation Tool", { company: get_company() }); + }, + __("View") + ); - treeview.page.add_inner_button(__("Period Closing Voucher"), function() { - frappe.set_route('List', 'Period Closing Voucher', {company: get_company()}); - }, __('View')); + treeview.page.add_inner_button( + __("Period Closing Voucher"), + function () { + frappe.set_route("List", "Period Closing Voucher", { company: get_company() }); + }, + __("View") + ); - - treeview.page.add_inner_button(__("Journal Entry"), function() { - frappe.new_doc('Journal Entry', {company: get_company()}); - }, __('Create')); - treeview.page.add_inner_button(__("Company"), function() { - frappe.new_doc('Company'); - }, __('Create')); + treeview.page.add_inner_button( + __("Journal Entry"), + function () { + frappe.new_doc("Journal Entry", { company: get_company() }); + }, + __("Create") + ); + treeview.page.add_inner_button( + __("Company"), + function () { + frappe.new_doc("Company"); + }, + __("Create") + ); // financial statements - for (let report of ['Trial Balance', 'General Ledger', 'Balance Sheet', - 'Profit and Loss Statement', 'Cash Flow Statement', 'Accounts Payable', 'Accounts Receivable']) { - treeview.page.add_inner_button(__(report), function() { - frappe.set_route('query-report', report, {company: get_company()}); - }, __('Financial Statements')); + for (let report of [ + "Trial Balance", + "General Ledger", + "Balance Sheet", + "Profit and Loss Statement", + "Cash Flow Statement", + "Accounts Payable", + "Accounts Receivable", + ]) { + treeview.page.add_inner_button( + __(report), + function () { + frappe.set_route("query-report", report, { company: get_company() }); + }, + __("Financial Statements") + ); } - }, - post_render: function(treeview) { - frappe.treeview_settings['Account'].treeview["tree"] = treeview.tree; - treeview.page.set_primary_action(__("New"), function() { - let root_company = treeview.page.fields_dict.root_company.get_value(); + post_render: function (treeview) { + frappe.treeview_settings["Account"].treeview["tree"] = treeview.tree; + treeview.page.set_primary_action( + __("New"), + function () { + let root_company = treeview.page.fields_dict.root_company.get_value(); - if(root_company) { - frappe.throw(__("Please add the account to root level Company - {0}"), [root_company]); - } else { - treeview.new_node(); - } - }, "add"); + if (root_company) { + frappe.throw(__("Please add the account to root level Company - {0}"), [root_company]); + } else { + treeview.new_node(); + } + }, + "add" + ); }, toolbar: [ { - label:__("Add Child"), - condition: function(node) { - return frappe.boot.user.can_create.indexOf("Account") !== -1 - && (!frappe.treeview_settings['Account'].treeview.page.fields_dict.root_company.get_value() - || frappe.flags.ignore_root_company_validation) - && node.expandable && !node.hide_add; + label: __("Add Child"), + condition: function (node) { + return ( + frappe.boot.user.can_create.indexOf("Account") !== -1 && + (!frappe.treeview_settings[ + "Account" + ].treeview.page.fields_dict.root_company.get_value() || + frappe.flags.ignore_root_company_validation) && + node.expandable && + !node.hide_add + ); }, - click: function() { - var me = frappe.views.trees['Account']; + click: function () { + var me = frappe.views.trees["Account"]; me.new_node(); }, - btnClass: "hidden-xs" + btnClass: "hidden-xs", }, { - condition: function(node) { - return !node.root && frappe.boot.user.can_read.indexOf("GL Entry") !== -1 + condition: function (node) { + return !node.root && frappe.boot.user.can_read.indexOf("GL Entry") !== -1; }, label: __("View Ledger"), - click: function(node, btn) { + click: function (node, btn) { frappe.route_options = { - "account": node.label, - "from_date": frappe.sys_defaults.year_start_date, - "to_date": frappe.sys_defaults.year_end_date, - "company": frappe.treeview_settings['Account'].treeview.page.fields_dict.company.get_value() + account: node.label, + from_date: frappe.sys_defaults.year_start_date, + to_date: frappe.sys_defaults.year_end_date, + company: + frappe.treeview_settings["Account"].treeview.page.fields_dict.company.get_value(), }; frappe.set_route("query-report", "General Ledger"); }, - btnClass: "hidden-xs" - } + btnClass: "hidden-xs", + }, ], - extend_toolbar: true -} + extend_toolbar: true, +}; diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js index 2f53f7b640d..cd883e5bf72 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.js @@ -1,74 +1,86 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Accounting Dimension', { - refresh: function(frm) { - frm.set_query('document_type', () => { +frappe.ui.form.on("Accounting Dimension", { + refresh: function (frm) { + frm.set_query("document_type", () => { let invalid_doctypes = frappe.model.core_doctypes_list; - invalid_doctypes.push('Accounting Dimension', 'Project', - 'Cost Center', 'Accounting Dimension Detail', 'Company'); + invalid_doctypes.push( + "Accounting Dimension", + "Project", + "Cost Center", + "Accounting Dimension Detail", + "Company" + ); return { filters: { - name: ['not in', invalid_doctypes] - } + name: ["not in", invalid_doctypes], + }, }; }); - frm.set_query("offsetting_account", "dimension_defaults", function(doc, cdt, cdn) { + frm.set_query("offsetting_account", "dimension_defaults", function (doc, cdt, cdn) { let d = locals[cdt][cdn]; return { filters: { company: d.company, root_type: ["in", ["Asset", "Liability"]], - is_group: 0 - } - } + is_group: 0, + }, + }; }); if (!frm.is_new()) { - frm.add_custom_button(__('Show {0}', [frm.doc.document_type]), function () { + frm.add_custom_button(__("Show {0}", [frm.doc.document_type]), function () { frappe.set_route("List", frm.doc.document_type); }); let button = frm.doc.disabled ? "Enable" : "Disable"; - frm.add_custom_button(__(button), function() { - - frm.set_value('disabled', 1 - frm.doc.disabled); + frm.add_custom_button(__(button), function () { + frm.set_value("disabled", 1 - frm.doc.disabled); frappe.call({ method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.disable_dimension", args: { - doc: frm.doc + doc: frm.doc, }, freeze: true, - callback: function(r) { + callback: function (r) { let message = frm.doc.disabled ? "Dimension Disabled" : "Dimension Enabled"; frm.save(); - frappe.show_alert({message:__(message), indicator:'green'}); - } + frappe.show_alert({ message: __(message), indicator: "green" }); + }, }); }); } }, - document_type: function(frm) { + document_type: function (frm) { + frm.set_value("label", frm.doc.document_type); + frm.set_value("fieldname", frappe.model.scrub(frm.doc.document_type)); - frm.set_value('label', frm.doc.document_type); - frm.set_value('fieldname', frappe.model.scrub(frm.doc.document_type)); - - frappe.db.get_value('Accounting Dimension', {'document_type': frm.doc.document_type}, 'document_type', (r) => { - if (r && r.document_type) { - frm.set_df_property('document_type', 'description', "Document type is already set as dimension"); + frappe.db.get_value( + "Accounting Dimension", + { document_type: frm.doc.document_type }, + "document_type", + (r) => { + if (r && r.document_type) { + frm.set_df_property( + "document_type", + "description", + "Document type is already set as dimension" + ); + } } - }); + ); }, }); -frappe.ui.form.on('Accounting Dimension Detail', { - dimension_defaults_add: function(frm, cdt, cdn) { +frappe.ui.form.on("Accounting Dimension Detail", { + dimension_defaults_add: function (frm, cdt, cdn) { let row = locals[cdt][cdn]; row.reference_document = frm.doc.document_type; - } + }, }); diff --git a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js index 8a6b021b8ad..7f655967b8e 100644 --- a/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js +++ b/erpnext/accounts/doctype/accounting_dimension_filter/accounting_dimension_filter.js @@ -1,10 +1,9 @@ // Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Accounting Dimension Filter', { - refresh: function(frm, cdt, cdn) { - let help_content = - ` +frappe.ui.form.on("Accounting Dimension Filter", { + refresh: function (frm, cdt, cdn) { + let help_content = `

    @@ -13,67 +12,70 @@ frappe.ui.form.on('Accounting Dimension Filter', {

    `; - frm.set_df_property('dimension_filter_help', 'options', help_content); + frm.set_df_property("dimension_filter_help", "options", help_content); }, - onload: function(frm) { - frm.set_query('applicable_on_account', 'accounts', function() { + onload: function (frm) { + frm.set_query("applicable_on_account", "accounts", function () { return { filters: { - 'company': frm.doc.company - } + company: frm.doc.company, + }, }; }); - frappe.db.get_list('Accounting Dimension', - {fields: ['document_type']}).then((res) => { - let options = ['Cost Center', 'Project']; + frappe.db.get_list("Accounting Dimension", { fields: ["document_type"] }).then((res) => { + let options = ["Cost Center", "Project"]; res.forEach((dimension) => { options.push(dimension.document_type); }); - frm.set_df_property('accounting_dimension', 'options', options); + frm.set_df_property("accounting_dimension", "options", options); }); - frm.trigger('setup_filters'); + frm.trigger("setup_filters"); }, - setup_filters: function(frm) { + setup_filters: function (frm) { let filters = {}; if (frm.doc.accounting_dimension) { - frappe.model.with_doctype(frm.doc.accounting_dimension, function() { + frappe.model.with_doctype(frm.doc.accounting_dimension, function () { if (frappe.model.is_tree(frm.doc.accounting_dimension)) { - filters['is_group'] = 0; + filters["is_group"] = 0; } - if (frappe.meta.has_field(frm.doc.accounting_dimension, 'company')) { - filters['company'] = frm.doc.company; + if (frappe.meta.has_field(frm.doc.accounting_dimension, "company")) { + filters["company"] = frm.doc.company; } - frm.set_query('dimension_value', 'dimensions', function() { + frm.set_query("dimension_value", "dimensions", function () { return { - filters: filters + filters: filters, }; }); }); } }, - accounting_dimension: function(frm) { + accounting_dimension: function (frm) { frm.clear_table("dimensions"); let row = frm.add_child("dimensions"); row.accounting_dimension = frm.doc.accounting_dimension; - frm.fields_dict["dimensions"].grid.update_docfield_property("dimension_value", "label", frm.doc.accounting_dimension); + frm.fields_dict["dimensions"].grid.update_docfield_property( + "dimension_value", + "label", + frm.doc.accounting_dimension + ); frm.refresh_field("dimensions"); - frm.trigger('setup_filters'); + frm.trigger("setup_filters"); }, }); -frappe.ui.form.on('Allowed Dimension', { - dimensions_add: function(frm, cdt, cdn) { +frappe.ui.form.on("Allowed Dimension", { + dimensions_add: function (frm, cdt, cdn) { let row = locals[cdt][cdn]; row.accounting_dimension = frm.doc.accounting_dimension; frm.refresh_field("dimensions"); - } + }, }); diff --git a/erpnext/accounts/doctype/accounting_period/accounting_period.js b/erpnext/accounts/doctype/accounting_period/accounting_period.js index f17b6f9c695..441471f8c06 100644 --- a/erpnext/accounts/doctype/accounting_period/accounting_period.js +++ b/erpnext/accounts/doctype/accounting_period/accounting_period.js @@ -1,30 +1,33 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Accounting Period', { - onload: function(frm) { - if(frm.doc.closed_documents.length === 0 || (frm.doc.closed_documents.length === 1 && frm.doc.closed_documents[0].document_type == undefined)) { +frappe.ui.form.on("Accounting Period", { + onload: function (frm) { + if ( + frm.doc.closed_documents.length === 0 || + (frm.doc.closed_documents.length === 1 && frm.doc.closed_documents[0].document_type == undefined) + ) { frappe.call({ method: "get_doctypes_for_closing", - doc:frm.doc, - callback: function(r) { - if(r.message) { + doc: frm.doc, + callback: function (r) { + if (r.message) { cur_frm.clear_table("closed_documents"); - r.message.forEach(function(element) { + r.message.forEach(function (element) { var c = frm.add_child("closed_documents"); c.document_type = element.document_type; c.closed = element.closed; }); refresh_field("closed_documents"); } - } + }, }); } frm.set_query("document_type", "closed_documents", () => { return { query: "erpnext.controllers.queries.get_doctypes_for_closing", - } + }; }); - } + }, }); diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.js b/erpnext/accounts/doctype/accounts_settings/accounts_settings.js index 0627675de79..5b9a52e8f8b 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.js +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.js @@ -1,8 +1,6 @@ // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Accounts Settings', { - refresh: function(frm) { - - } +frappe.ui.form.on("Accounts Settings", { + refresh: function (frm) {}, }); diff --git a/erpnext/accounts/doctype/accounts_settings/regional/united_states.js b/erpnext/accounts/doctype/accounts_settings/regional/united_states.js index 3e38386481c..a522de9da75 100644 --- a/erpnext/accounts/doctype/accounts_settings/regional/united_states.js +++ b/erpnext/accounts/doctype/accounts_settings/regional/united_states.js @@ -1,8 +1,11 @@ - -frappe.ui.form.on('Accounts Settings', { - refresh: function(frm) { +frappe.ui.form.on("Accounts Settings", { + refresh: function (frm) { frm.set_df_property("acc_frozen_upto", "label", "Books Closed Through"); - frm.set_df_property("frozen_accounts_modifier", "label", "Role Allowed to Close Books & Make Changes to Closed Periods"); + frm.set_df_property( + "frozen_accounts_modifier", + "label", + "Role Allowed to Close Books & Make Changes to Closed Periods" + ); frm.set_df_property("credit_controller", "label", "Credit Manager"); - } + }, }); diff --git a/erpnext/accounts/doctype/bank/bank.js b/erpnext/accounts/doctype/bank/bank.js index 35d606ba3ae..0e186af41ba 100644 --- a/erpnext/accounts/doctype/bank/bank.js +++ b/erpnext/accounts/doctype/bank/bank.js @@ -1,41 +1,39 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.provide('erpnext.integrations'); +frappe.provide("erpnext.integrations"); -frappe.ui.form.on('Bank', { - onload: function(frm) { +frappe.ui.form.on("Bank", { + onload: function (frm) { add_fields_to_mapping_table(frm); }, - refresh: function(frm) { + refresh: function (frm) { add_fields_to_mapping_table(frm); - frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Bank' }; + frappe.dynamic_link = { doc: frm.doc, fieldname: "name", doctype: "Bank" }; - frm.toggle_display(['address_html','contact_html'], !frm.doc.__islocal); + frm.toggle_display(["address_html", "contact_html"], !frm.doc.__islocal); if (frm.doc.__islocal) { - frm.set_df_property('address_and_contact', 'hidden', 1); + frm.set_df_property("address_and_contact", "hidden", 1); frappe.contacts.clear_address_and_contact(frm); - } - else { - frm.set_df_property('address_and_contact', 'hidden', 0); + } else { + frm.set_df_property("address_and_contact", "hidden", 0); frappe.contacts.render_address_and_contact(frm); } if (frm.doc.plaid_access_token) { - frm.add_custom_button(__('Refresh Plaid Link'), () => { + frm.add_custom_button(__("Refresh Plaid Link"), () => { new erpnext.integrations.refreshPlaidLink(frm.doc.plaid_access_token); }); } - } + }, }); - let add_fields_to_mapping_table = function (frm) { let options = []; - frappe.model.with_doctype("Bank Transaction", function() { + frappe.model.with_doctype("Bank Transaction", function () { let meta = frappe.get_meta("Bank Transaction"); - meta.fields.forEach(value => { + meta.fields.forEach((value) => { if (!["Section Break", "Column Break"].includes(value.fieldtype)) { options.push(value.fieldname); } @@ -43,30 +41,32 @@ let add_fields_to_mapping_table = function (frm) { }); frm.fields_dict.bank_transaction_mapping.grid.update_docfield_property( - 'bank_transaction_field', 'options', options + "bank_transaction_field", + "options", + options ); }; erpnext.integrations.refreshPlaidLink = class refreshPlaidLink { constructor(access_token) { this.access_token = access_token; - this.plaidUrl = 'https://cdn.plaid.com/link/v2/stable/link-initialize.js'; + this.plaidUrl = "https://cdn.plaid.com/link/v2/stable/link-initialize.js"; this.init_config(); } async init_config() { - this.plaid_env = await frappe.db.get_single_value('Plaid Settings', 'plaid_env'); + this.plaid_env = await frappe.db.get_single_value("Plaid Settings", "plaid_env"); this.token = await this.get_link_token_for_update(); this.init_plaid(); } async get_link_token_for_update() { const token = frappe.xcall( - 'erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.get_link_token_for_update', + "erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.get_link_token_for_update", { access_token: this.access_token } - ) + ); if (!token) { - frappe.throw(__('Cannot retrieve link token for update. Check Error Log for more information')); + frappe.throw(__("Cannot retrieve link token for update. Check Error Log for more information")); } return token; } @@ -93,13 +93,13 @@ erpnext.integrations.refreshPlaidLink = class refreshPlaidLink { resolve(); return; } - const el = document.createElement('script'); - el.type = 'text/javascript'; + const el = document.createElement("script"); + el.type = "text/javascript"; el.async = true; el.src = src; - el.addEventListener('load', resolve); - el.addEventListener('error', reject); - el.addEventListener('abort', reject); + el.addEventListener("load", resolve); + el.addEventListener("error", reject); + el.addEventListener("abort", reject); document.head.appendChild(el); }); } @@ -108,20 +108,29 @@ erpnext.integrations.refreshPlaidLink = class refreshPlaidLink { me.linkHandler = Plaid.create({ env: me.plaid_env, token: me.token, - onSuccess: me.plaid_success + onSuccess: me.plaid_success, }); } onScriptError(error) { - frappe.msgprint(__("There was an issue connecting to Plaid's authentication server. Check browser console for more information")); + frappe.msgprint( + __( + "There was an issue connecting to Plaid's authentication server. Check browser console for more information" + ) + ); console.log(error); } plaid_success(token, response) { - frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.update_bank_account_ids', { - response: response, - }).then(() => { - frappe.show_alert({ message: __('Plaid Link Updated'), indicator: 'green' }); - }); + frappe + .xcall( + "erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.update_bank_account_ids", + { + response: response, + } + ) + .then(() => { + frappe.show_alert({ message: __("Plaid Link Updated"), indicator: "green" }); + }); } }; diff --git a/erpnext/accounts/doctype/bank_account/bank_account.js b/erpnext/accounts/doctype/bank_account/bank_account.js index 0598190b518..202f750fb50 100644 --- a/erpnext/accounts/doctype/bank_account/bank_account.js +++ b/erpnext/accounts/doctype/bank_account/bank_account.js @@ -1,45 +1,49 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Bank Account', { - setup: function(frm) { - frm.set_query("account", function() { +frappe.ui.form.on("Bank Account", { + setup: function (frm) { + frm.set_query("account", function () { return { filters: { - 'account_type': 'Bank', - 'company': frm.doc.company, - 'is_group': 0 - } + account_type: "Bank", + company: frm.doc.company, + is_group: 0, + }, }; }); - frm.set_query("party_type", function() { + frm.set_query("party_type", function () { return { query: "erpnext.setup.doctype.party_type.party_type.get_party_type", }; }); }, - refresh: function(frm) { - frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Bank Account' } + refresh: function (frm) { + frappe.dynamic_link = { doc: frm.doc, fieldname: "name", doctype: "Bank Account" }; - frm.toggle_display(['address_html','contact_html'], !frm.doc.__islocal); + frm.toggle_display(["address_html", "contact_html"], !frm.doc.__islocal); if (frm.doc.__islocal) { frappe.contacts.clear_address_and_contact(frm); - } - else { + } else { frappe.contacts.render_address_and_contact(frm); } if (frm.doc.integration_id) { - frm.add_custom_button(__("Unlink external integrations"), function() { - frappe.confirm(__("This action will unlink this account from any external service integrating ERPNext with your bank accounts. It cannot be undone. Are you certain ?"), function() { - frm.set_value("integration_id", ""); - }); + frm.add_custom_button(__("Unlink external integrations"), function () { + frappe.confirm( + __( + "This action will unlink this account from any external service integrating ERPNext with your bank accounts. It cannot be undone. Are you certain ?" + ), + function () { + frm.set_value("integration_id", ""); + } + ); }); } }, - is_company_account: function(frm) { - frm.set_df_property('account', 'reqd', frm.doc.is_company_account); - } + is_company_account: function (frm) { + frm.set_df_property("account", "reqd", frm.doc.is_company_account); + }, }); diff --git a/erpnext/accounts/doctype/bank_account_subtype/bank_account_subtype.js b/erpnext/accounts/doctype/bank_account_subtype/bank_account_subtype.js index f0456651c8f..e8fe4e0b9bd 100644 --- a/erpnext/accounts/doctype/bank_account_subtype/bank_account_subtype.js +++ b/erpnext/accounts/doctype/bank_account_subtype/bank_account_subtype.js @@ -1,8 +1,6 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Bank Account Subtype', { - refresh: function() { - - } +frappe.ui.form.on("Bank Account Subtype", { + refresh: function () {}, }); diff --git a/erpnext/accounts/doctype/bank_account_type/bank_account_type.js b/erpnext/accounts/doctype/bank_account_type/bank_account_type.js index 4cfabe3d1d1..1285fe4866f 100644 --- a/erpnext/accounts/doctype/bank_account_type/bank_account_type.js +++ b/erpnext/accounts/doctype/bank_account_type/bank_account_type.js @@ -1,8 +1,7 @@ // Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Bank Account Type', { +frappe.ui.form.on("Bank Account Type", { // refresh: function(frm) { - // } }); diff --git a/erpnext/accounts/doctype/bank_clearance/bank_clearance.js b/erpnext/accounts/doctype/bank_clearance/bank_clearance.js index 71f2dcca1b2..ddf7bc5dec6 100644 --- a/erpnext/accounts/doctype/bank_clearance/bank_clearance.js +++ b/erpnext/accounts/doctype/bank_clearance/bank_clearance.js @@ -2,80 +2,76 @@ // License: GNU General Public License v3. See license.txt frappe.ui.form.on("Bank Clearance", { - setup: function(frm) { + setup: function (frm) { frm.add_fetch("account", "account_currency", "account_currency"); - frm.set_query("account", function() { + frm.set_query("account", function () { return { - "filters": { - "account_type": ["in",["Bank","Cash"]], - "is_group": 0, - } + filters: { + account_type: ["in", ["Bank", "Cash"]], + is_group: 0, + }, }; }); frm.set_query("bank_account", function () { return { filters: { - 'is_company_account': 1 + is_company_account: 1, }, }; }); }, - onload: function(frm) { - - let default_bank_account = frappe.defaults.get_user_default("Company")? - locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"]: ""; + onload: function (frm) { + let default_bank_account = frappe.defaults.get_user_default("Company") + ? locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"] + : ""; frm.set_value("account", default_bank_account); - - frm.set_value("from_date", frappe.datetime.month_start()); frm.set_value("to_date", frappe.datetime.month_end()); }, - refresh: function(frm) { + refresh: function (frm) { frm.disable_save(); - frm.add_custom_button(__('Get Payment Entries'), () => - frm.trigger("get_payment_entries") - ); + frm.add_custom_button(__("Get Payment Entries"), () => frm.trigger("get_payment_entries")); - frm.change_custom_button_type('Get Payment Entries', null, 'primary'); + frm.change_custom_button_type("Get Payment Entries", null, "primary"); }, - update_clearance_date: function(frm) { + update_clearance_date: function (frm) { return frappe.call({ method: "update_clearance_date", doc: frm.doc, - callback: function(r, rt) { + 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.change_custom_button_type("Get Payment Entries", null, "primary"); + frm.change_custom_button_type("Update Clearance Date", null, "default"); } - } + }, }); }, - get_payment_entries: function(frm) { + get_payment_entries: function (frm) { return frappe.call({ method: "get_payment_entries", doc: frm.doc, - callback: function(r, rt) { + callback: function (r, rt) { frm.refresh_field("payment_entries"); if (frm.doc.payment_entries.length) { - frm.add_custom_button(__('Update Clearance Date'), () => + 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'); + frm.change_custom_button_type("Get Payment Entries", null, "default"); + frm.change_custom_button_type("Update Clearance Date", null, "primary"); } - } + }, }); - } + }, }); diff --git a/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.js b/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.js index 99cc0a72fb3..060c4b5edaa 100644 --- a/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.js +++ b/erpnext/accounts/doctype/bank_guarantee/bank_guarantee.js @@ -1,39 +1,39 @@ // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -cur_frm.add_fetch('bank_account','account','account'); -cur_frm.add_fetch('bank_account','bank_account_no','bank_account_no'); -cur_frm.add_fetch('bank_account','iban','iban'); -cur_frm.add_fetch('bank_account','branch_code','branch_code'); -cur_frm.add_fetch('bank','swift_number','swift_number'); +cur_frm.add_fetch("bank_account", "account", "account"); +cur_frm.add_fetch("bank_account", "bank_account_no", "bank_account_no"); +cur_frm.add_fetch("bank_account", "iban", "iban"); +cur_frm.add_fetch("bank_account", "branch_code", "branch_code"); +cur_frm.add_fetch("bank", "swift_number", "swift_number"); -frappe.ui.form.on('Bank Guarantee', { - setup: function(frm) { - frm.set_query("bank", function() { - return { - filters: { - company: frm.doc.company - } - }; - }); - frm.set_query("bank_account", function() { +frappe.ui.form.on("Bank Guarantee", { + setup: function (frm) { + frm.set_query("bank", function () { return { filters: { company: frm.doc.company, - bank: frm.doc.bank - } - } + }, + }; }); - frm.set_query("project", function() { + frm.set_query("bank_account", function () { return { filters: { - customer: frm.doc.customer - } + company: frm.doc.company, + bank: frm.doc.bank, + }, + }; + }); + frm.set_query("project", function () { + return { + filters: { + customer: frm.doc.customer, + }, }; }); }, - bg_type: function(frm) { + bg_type: function (frm) { if (frm.doc.bg_type == "Receiving") { frm.set_value("reference_doctype", "Sales Order"); } else if (frm.doc.bg_type == "Providing") { @@ -41,34 +41,33 @@ frappe.ui.form.on('Bank Guarantee', { } }, - reference_docname: function(frm) { + reference_docname: function (frm) { if (frm.doc.reference_docname && frm.doc.reference_doctype) { let party_field = frm.doc.reference_doctype == "Sales Order" ? "customer" : "supplier"; frappe.call({ method: "erpnext.accounts.doctype.bank_guarantee.bank_guarantee.get_voucher_details", args: { - "bank_guarantee_type": frm.doc.bg_type, - "reference_name": frm.doc.reference_docname + bank_guarantee_type: frm.doc.bg_type, + reference_name: frm.doc.reference_docname, }, - callback: function(r) { + callback: function (r) { if (r.message) { if (r.message[party_field]) frm.set_value(party_field, r.message[party_field]); if (r.message.project) frm.set_value("project", r.message.project); if (r.message.grand_total) frm.set_value("amount", r.message.grand_total); } - } + }, }); - } }, - start_date: function(frm) { + start_date: function (frm) { var end_date = frappe.datetime.add_days(cur_frm.doc.start_date, cur_frm.doc.validity - 1); cur_frm.set_value("end_date", end_date); }, - validity: function(frm) { + validity: function (frm) { var end_date = frappe.datetime.add_days(cur_frm.doc.start_date, cur_frm.doc.validity - 1); cur_frm.set_value("end_date", end_date); - } + }, }); diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js index f2380bde125..10c0781e1d3 100644 --- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js +++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js @@ -8,21 +8,22 @@ frappe.ui.form.on("Bank Reconciliation Tool", { return { filters: { company: frm.doc.company, - 'is_company_account': 1 + is_company_account: 1, }, }; }); - let no_bank_transactions_text = - `
    ${__("No Matching Bank Transactions Found")}
    ` + let no_bank_transactions_text = `
    ${__( + "No Matching Bank Transactions Found" + )}
    `; set_field_options("no_bank_transactions", no_bank_transactions_text); }, onload: function (frm) { // Set default filter dates - let today = frappe.datetime.get_today() + let today = frappe.datetime.get_today(); frm.doc.bank_statement_from_date = frappe.datetime.add_months(today, -1); frm.doc.bank_statement_to_date = today; - frm.trigger('bank_account'); + frm.trigger("bank_account"); }, filter_by_reference_date: function (frm) { @@ -37,34 +38,27 @@ frappe.ui.form.on("Bank Reconciliation Tool", { refresh: function (frm) { frm.disable_save(); - frappe.require("bank-reconciliation-tool.bundle.js", () => - frm.trigger("make_reconciliation_tool") - ); + frappe.require("bank-reconciliation-tool.bundle.js", () => frm.trigger("make_reconciliation_tool")); frm.add_custom_button(__("Upload Bank Statement"), () => - frappe.call({ - method: - "erpnext.accounts.doctype.bank_statement_import.bank_statement_import.upload_bank_statement", - args: { - dt: frm.doc.doctype, - dn: frm.doc.name, - company: frm.doc.company, - bank_account: frm.doc.bank_account, - }, - callback: function (r) { - if (!r.exc) { - var doc = frappe.model.sync(r.message); - frappe.set_route( - "Form", - doc[0].doctype, - doc[0].name - ); - } - }, - }) + frappe.call({ + method: "erpnext.accounts.doctype.bank_statement_import.bank_statement_import.upload_bank_statement", + args: { + dt: frm.doc.doctype, + dn: frm.doc.name, + company: frm.doc.company, + bank_account: frm.doc.bank_account, + }, + callback: function (r) { + if (!r.exc) { + var doc = frappe.model.sync(r.message); + frappe.set_route("Form", doc[0].doctype, doc[0].name); + } + }, + }) ); - frm.add_custom_button(__('Auto Reconcile'), function() { + frm.add_custom_button(__("Auto Reconcile"), function () { frappe.call({ method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.auto_reconcile_vouchers", args: { @@ -75,33 +69,22 @@ frappe.ui.form.on("Bank Reconciliation Tool", { from_reference_date: frm.doc.from_reference_date, to_reference_date: frm.doc.to_reference_date, }, - }) + }); }); - frm.add_custom_button(__('Get Unreconciled Entries'), function() { + frm.add_custom_button(__("Get Unreconciled Entries"), function () { frm.trigger("make_reconciliation_tool"); }); - frm.change_custom_button_type('Get Unreconciled Entries', null, 'primary'); - + frm.change_custom_button_type("Get Unreconciled Entries", null, "primary"); }, bank_account: function (frm) { - frappe.db.get_value( - "Bank Account", - frm.doc.bank_account, - "account", - (r) => { - frappe.db.get_value( - "Account", - r.account, - "account_currency", - (r) => { - frm.doc.account_currency = r.account_currency; - frm.trigger("render_chart"); - } - ); - } - ); + frappe.db.get_value("Bank Account", frm.doc.bank_account, "account", (r) => { + frappe.db.get_value("Account", r.account, "account_currency", (r) => { + frm.doc.account_currency = r.account_currency; + frm.trigger("render_chart"); + }); + }); frm.trigger("get_account_opening_balance"); }, @@ -120,11 +103,7 @@ frappe.ui.form.on("Bank Reconciliation Tool", { ) { frm.trigger("render_chart"); frm.trigger("render"); - frappe.utils.scroll_to( - frm.get_field("reconciliation_tool_cards").$wrapper, - true, - 30 - ); + frappe.utils.scroll_to(frm.get_field("reconciliation_tool_cards").$wrapper, true, 30); } }); } @@ -133,11 +112,10 @@ frappe.ui.form.on("Bank Reconciliation Tool", { get_account_opening_balance(frm) { if (frm.doc.bank_account && frm.doc.bank_statement_from_date) { frappe.call({ - method: - "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_account_balance", + method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_account_balance", args: { bank_account: frm.doc.bank_account, - till_date: frappe.datetime.add_days(frm.doc.bank_statement_from_date, -1) + till_date: frappe.datetime.add_days(frm.doc.bank_statement_from_date, -1), }, callback: (response) => { frm.set_value("account_opening_balance", response.message); @@ -149,8 +127,7 @@ frappe.ui.form.on("Bank Reconciliation Tool", { get_cleared_balance(frm) { if (frm.doc.bank_account && frm.doc.bank_statement_to_date) { return frappe.call({ - method: - "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_account_balance", + method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_account_balance", args: { bank_account: frm.doc.bank_account, till_date: frm.doc.bank_statement_to_date, @@ -163,41 +140,30 @@ frappe.ui.form.on("Bank Reconciliation Tool", { }, render_chart(frm) { - frm.cards_manager = new erpnext.accounts.bank_reconciliation.NumberCardManager( - { - $reconciliation_tool_cards: frm.get_field( - "reconciliation_tool_cards" - ).$wrapper, - bank_statement_closing_balance: - frm.doc.bank_statement_closing_balance, - cleared_balance: frm.cleared_balance, - currency: frm.doc.account_currency, - } - ); + frm.cards_manager = new erpnext.accounts.bank_reconciliation.NumberCardManager({ + $reconciliation_tool_cards: frm.get_field("reconciliation_tool_cards").$wrapper, + bank_statement_closing_balance: frm.doc.bank_statement_closing_balance, + cleared_balance: frm.cleared_balance, + currency: frm.doc.account_currency, + }); }, render(frm) { if (frm.doc.bank_account) { - frm.bank_reconciliation_data_table_manager = new erpnext.accounts.bank_reconciliation.DataTableManager( - { + frm.bank_reconciliation_data_table_manager = + new erpnext.accounts.bank_reconciliation.DataTableManager({ company: frm.doc.company, bank_account: frm.doc.bank_account, - $reconciliation_tool_dt: frm.get_field( - "reconciliation_tool_dt" - ).$wrapper, - $no_bank_transactions: frm.get_field( - "no_bank_transactions" - ).$wrapper, + $reconciliation_tool_dt: frm.get_field("reconciliation_tool_dt").$wrapper, + $no_bank_transactions: frm.get_field("no_bank_transactions").$wrapper, bank_statement_from_date: frm.doc.bank_statement_from_date, bank_statement_to_date: frm.doc.bank_statement_to_date, filter_by_reference_date: frm.doc.filter_by_reference_date, from_reference_date: frm.doc.from_reference_date, to_reference_date: frm.doc.to_reference_date, - bank_statement_closing_balance: - frm.doc.bank_statement_closing_balance, + bank_statement_closing_balance: frm.doc.bank_statement_closing_balance, cards_manager: frm.cards_manager, - } - ); + }); } }, }); diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js index db68dfad79e..c4b442070d2 100644 --- a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js +++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import.js @@ -17,11 +17,9 @@ frappe.ui.form.on("Bank Statement Import", { frm.import_in_progress = false; if (data_import !== frm.doc.name) return; frappe.model.clear_doc("Bank Statement Import", frm.doc.name); - frappe.model - .with_doc("Bank Statement Import", frm.doc.name) - .then(() => { - frm.refresh(); - }); + frappe.model.with_doc("Bank Statement Import", frm.doc.name).then(() => { + frm.refresh(); + }); }); frappe.realtime.on("data_import_progress", (data) => { frm.import_in_progress = true; @@ -48,20 +46,9 @@ frappe.ui.form.on("Bank Statement Import", { : __("Updating {0} of {1}, {2}", message_args); } if (data.skipping) { - message = __( - "Skipping {0} of {1}, {2}", - [ - data.current, - data.total, - eta_message, - ] - ); + message = __("Skipping {0} of {1}, {2}", [data.current, data.total, eta_message]); } - frm.dashboard.show_progress( - __("Import Progress"), - percent, - message - ); + frm.dashboard.show_progress(__("Import Progress"), percent, message); frm.page.set_indicator(__("In Progress"), "orange"); // hide progress when complete @@ -103,15 +90,12 @@ frappe.ui.form.on("Bank Statement Import", { frm.trigger("show_report_error_button"); if (frm.doc.status === "Partial Success") { - frm.add_custom_button(__("Export Errored Rows"), () => - frm.trigger("export_errored_rows") - ); + frm.add_custom_button(__("Export Errored Rows"), () => frm.trigger("export_errored_rows")); } if (frm.doc.status.includes("Success")) { - frm.add_custom_button( - __("Go to {0} List", [__(frm.doc.reference_doctype)]), - () => frappe.set_route("List", frm.doc.reference_doctype) + frm.add_custom_button(__("Go to {0} List", [__(frm.doc.reference_doctype)]), () => + frappe.set_route("List", frm.doc.reference_doctype) ); } }, @@ -128,13 +112,8 @@ frappe.ui.form.on("Bank Statement Import", { frm.disable_save(); if (frm.doc.status !== "Success") { if (!frm.is_new() && frm.has_import_file()) { - let label = - frm.doc.status === "Pending" - ? __("Start Import") - : __("Retry"); - frm.page.set_primary_action(label, () => - frm.events.start_import(frm) - ); + let label = frm.doc.status === "Pending" ? __("Start Import") : __("Retry"); + frm.page.set_primary_action(label, () => frm.events.start_import(frm)); } else { frm.page.set_primary_action(__("Save"), () => frm.save()); } @@ -176,24 +155,24 @@ frappe.ui.form.on("Bank Statement Import", { message = successful_records.length > 1 ? __( - "Successfully imported {0} records out of {1}. Click on Export Errored Rows, fix the errors and import again.", - message_args - ) + "Successfully imported {0} records out of {1}. Click on Export Errored Rows, fix the errors and import again.", + message_args + ) : __( - "Successfully imported {0} record out of {1}. Click on Export Errored Rows, fix the errors and import again.", - message_args - ); + "Successfully imported {0} record out of {1}. Click on Export Errored Rows, fix the errors and import again.", + message_args + ); } else { message = successful_records.length > 1 ? __( - "Successfully updated {0} records out of {1}. Click on Export Errored Rows, fix the errors and import again.", - message_args - ) + "Successfully updated {0} records out of {1}. Click on Export Errored Rows, fix the errors and import again.", + message_args + ) : __( - "Successfully updated {0} record out of {1}. Click on Export Errored Rows, fix the errors and import again.", - message_args - ); + "Successfully updated {0} record out of {1}. Click on Export Errored Rows, fix the errors and import again.", + message_args + ); } } frm.dashboard.set_headline(message); @@ -236,8 +215,7 @@ frappe.ui.form.on("Bank Statement Import", { }, download_template() { - let method = - "/api/method/frappe.core.doctype.data_import.data_import.download_template"; + let method = "/api/method/frappe.core.doctype.data_import.data_import.download_template"; open_url_post(method, { doctype: "Bank Transaction", @@ -250,7 +228,7 @@ frappe.ui.form.on("Bank Statement Import", { "description", "reference_number", "bank_account", - "currency" + "currency", ], }, }); @@ -321,10 +299,7 @@ frappe.ui.form.on("Bank Statement Import", { show_import_preview(frm, preview_data) { let import_log = JSON.parse(frm.doc.statement_import_log || "[]"); - if ( - frm.import_preview && - frm.import_preview.doctype === frm.doc.reference_doctype - ) { + if (frm.import_preview && frm.import_preview.doctype === frm.doc.reference_doctype) { frm.import_preview.preview_data = preview_data; frm.import_preview.import_log = import_log; frm.import_preview.refresh(); @@ -340,19 +315,10 @@ frappe.ui.form.on("Bank Statement Import", { frm, events: { remap_column(changed_map) { - let template_options = JSON.parse( - frm.doc.template_options || "{}" - ); - template_options.column_to_field_map = - template_options.column_to_field_map || {}; - Object.assign( - template_options.column_to_field_map, - changed_map - ); - frm.set_value( - "template_options", - JSON.stringify(template_options) - ); + let template_options = JSON.parse(frm.doc.template_options || "{}"); + template_options.column_to_field_map = template_options.column_to_field_map || {}; + Object.assign(template_options.column_to_field_map, changed_map); + frm.set_value("template_options", JSON.stringify(template_options)); frm.save().then(() => frm.trigger("import_file")); }, }, @@ -386,8 +352,7 @@ frappe.ui.form.on("Bank Statement Import", { let other_warnings = []; for (let warning of warnings) { if (warning.row) { - warnings_by_row[warning.row] = - warnings_by_row[warning.row] || []; + warnings_by_row[warning.row] = warnings_by_row[warning.row] || []; warnings_by_row[warning.row].push(warning); } else { other_warnings.push(warning); @@ -402,9 +367,7 @@ frappe.ui.form.on("Bank Statement Import", { if (w.field) { let label = w.field.label + - (w.field.parent !== frm.doc.reference_doctype - ? ` (${w.field.parent})` - : ""); + (w.field.parent !== frm.doc.reference_doctype ? ` (${w.field.parent})` : ""); return `
  • ${label}: ${w.message}
  • `; } return `
  • ${w.message}
  • `; @@ -423,10 +386,9 @@ frappe.ui.form.on("Bank Statement Import", { .map((warning) => { let header = ""; if (warning.col) { - let column_number = `${__( - "Column {0}", - [warning.col] - )}`; + let column_number = `${__("Column {0}", [ + warning.col, + ])}`; let column_header = columns[warning.col].header_title; header = `${column_number} (${column_header})`; } @@ -465,36 +427,28 @@ frappe.ui.form.on("Bank Statement Import", { let html = ""; if (log.success) { if (frm.doc.import_type === "Insert New Records") { - html = __( - "Successfully imported {0}", [ - `${frappe.utils.get_form_link( - frm.doc.reference_doctype, - log.docname, - true - )}`, - ] - ); + html = __("Successfully imported {0}", [ + `${frappe.utils.get_form_link( + frm.doc.reference_doctype, + log.docname, + true + )}`, + ]); } else { - html = __( - "Successfully updated {0}", [ - `${frappe.utils.get_form_link( - frm.doc.reference_doctype, - log.docname, - true - )}`, - ] - ); + html = __("Successfully updated {0}", [ + `${frappe.utils.get_form_link( + frm.doc.reference_doctype, + log.docname, + true + )}`, + ]); } } else { let messages = log.messages .map(JSON.parse) .map((m) => { - let title = m.title - ? `${m.title}` - : ""; - let message = m.message - ? `
    ${m.message}
    ` - : ""; + let title = m.title ? `${m.title}` : ""; + let message = m.message ? `
    ${m.message}
    ` : ""; return title + message; }) .join(""); diff --git a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import_list.js b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import_list.js index 6c754022e68..4ab65ff73aa 100644 --- a/erpnext/accounts/doctype/bank_statement_import/bank_statement_import_list.js +++ b/erpnext/accounts/doctype/bank_statement_import/bank_statement_import_list.js @@ -1,36 +1,34 @@ let imports_in_progress = []; -frappe.listview_settings['Bank Statement Import'] = { +frappe.listview_settings["Bank Statement Import"] = { onload(listview) { - frappe.realtime.on('data_import_progress', data => { + frappe.realtime.on("data_import_progress", (data) => { if (!imports_in_progress.includes(data.data_import)) { imports_in_progress.push(data.data_import); } }); - frappe.realtime.on('data_import_refresh', data => { - imports_in_progress = imports_in_progress.filter( - d => d !== data.data_import - ); + frappe.realtime.on("data_import_refresh", (data) => { + imports_in_progress = imports_in_progress.filter((d) => d !== data.data_import); listview.refresh(); }); }, - get_indicator: function(doc) { + get_indicator: function (doc) { var colors = { - 'Pending': 'orange', - 'Not Started': 'orange', - 'Partial Success': 'orange', - 'Success': 'green', - 'In Progress': 'orange', - 'Error': 'red' + Pending: "orange", + "Not Started": "orange", + "Partial Success": "orange", + Success: "green", + "In Progress": "orange", + Error: "red", }; let status = doc.status; if (imports_in_progress.includes(doc.name)) { - status = 'In Progress'; + status = "In Progress"; } - if (status == 'Pending') { - status = 'Not Started'; + if (status == "Pending") { + status = "Not Started"; } - return [__(status), colors[status], 'status,=,' + doc.status]; + return [__(status), colors[status], "status,=," + doc.status]; }, - hide_name_column: true + hide_name_column: true, }; diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.js b/erpnext/accounts/doctype/bank_transaction/bank_transaction.js index b3cc1cbb1be..d899d429178 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.js +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.js @@ -3,7 +3,7 @@ frappe.ui.form.on("Bank Transaction", { onload(frm) { - frm.set_query("payment_document", "payment_entries", function() { + frm.set_query("payment_document", "payment_entries", function () { const payment_doctypes = frm.events.get_payment_doctypes(frm); return { filters: { @@ -23,7 +23,7 @@ frappe.ui.form.on("Bank Transaction", { set_bank_statement_filter(frm); }, - setup: function(frm) { + setup: function (frm) { frm.set_query("party_type", function () { return { filters: { @@ -33,16 +33,10 @@ frappe.ui.form.on("Bank Transaction", { }); }, - get_payment_doctypes: function() { + get_payment_doctypes: function () { // get payment doctypes from all the apps - return [ - "Payment Entry", - "Journal Entry", - "Sales Invoice", - "Purchase Invoice", - "Bank Transaction", - ]; - } + return ["Payment Entry", "Journal Entry", "Sales Invoice", "Purchase Invoice", "Bank Transaction"]; + }, }); frappe.ui.form.on("Bank Transaction Payments", { @@ -54,10 +48,11 @@ frappe.ui.form.on("Bank Transaction Payments", { const update_clearance_date = (frm, cdt, cdn) => { if (frm.doc.docstatus === 1) { frappe - .xcall( - "erpnext.accounts.doctype.bank_transaction.bank_transaction.unclear_reference_payment", - { doctype: cdt, docname: cdn, bt_name: frm.doc.name } - ) + .xcall("erpnext.accounts.doctype.bank_transaction.bank_transaction.unclear_reference_payment", { + doctype: cdt, + docname: cdn, + bt_name: frm.doc.name, + }) .then((e) => { if (e == "success") { frappe.show_alert({ diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction_list.js b/erpnext/accounts/doctype/bank_transaction/bank_transaction_list.js index 2585ee9c923..9942c09bf47 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction_list.js +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction_list.js @@ -1,15 +1,15 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.listview_settings['Bank Transaction'] = { +frappe.listview_settings["Bank Transaction"] = { add_fields: ["unallocated_amount"], - get_indicator: function(doc) { - if(doc.docstatus == 2) { + get_indicator: function (doc) { + if (doc.docstatus == 2) { return [__("Cancelled"), "red", "docstatus,=,2"]; - } else if(flt(doc.unallocated_amount)<=0) { + } else if (flt(doc.unallocated_amount) <= 0) { return [__("Reconciled"), "green", "unallocated_amount,=,0"]; - } else if(flt(doc.unallocated_amount)>0) { + } else if (flt(doc.unallocated_amount) > 0) { return [__("Unreconciled"), "orange", "unallocated_amount,>,0"]; } - } + }, }; diff --git a/erpnext/accounts/doctype/budget/budget.js b/erpnext/accounts/doctype/budget/budget.js index e162e3222d3..6e874f7c08c 100644 --- a/erpnext/accounts/doctype/budget/budget.js +++ b/erpnext/accounts/doctype/budget/budget.js @@ -2,48 +2,48 @@ // For license information, please see license.txt frappe.provide("erpnext.accounts.dimensions"); -frappe.ui.form.on('Budget', { - onload: function(frm) { - frm.set_query("account", "accounts", function() { +frappe.ui.form.on("Budget", { + onload: function (frm) { + frm.set_query("account", "accounts", function () { return { filters: { company: frm.doc.company, report_type: "Profit and Loss", - is_group: 0 - } + is_group: 0, + }, }; }); - frm.set_query("monthly_distribution", function() { + frm.set_query("monthly_distribution", function () { return { filters: { - fiscal_year: frm.doc.fiscal_year - } + fiscal_year: frm.doc.fiscal_year, + }, }; }); erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); }, - refresh: function(frm) { - frm.trigger("toggle_reqd_fields") + refresh: function (frm) { + frm.trigger("toggle_reqd_fields"); }, - budget_against: function(frm) { - frm.trigger("set_null_value") - frm.trigger("toggle_reqd_fields") + budget_against: function (frm) { + frm.trigger("set_null_value"); + frm.trigger("toggle_reqd_fields"); }, - set_null_value: function(frm) { - if(frm.doc.budget_against == 'Cost Center') { - frm.set_value('project', null) + set_null_value: function (frm) { + if (frm.doc.budget_against == "Cost Center") { + frm.set_value("project", null); } else { - frm.set_value('cost_center', null) + frm.set_value("cost_center", null); } }, - toggle_reqd_fields: function(frm) { - frm.toggle_reqd("cost_center", frm.doc.budget_against=="Cost Center"); - frm.toggle_reqd("project", frm.doc.budget_against=="Project"); - } + toggle_reqd_fields: function (frm) { + frm.toggle_reqd("cost_center", frm.doc.budget_against == "Cost Center"); + frm.toggle_reqd("project", frm.doc.budget_against == "Project"); + }, }); diff --git a/erpnext/accounts/doctype/cash_flow_mapper/cash_flow_mapper.js b/erpnext/accounts/doctype/cash_flow_mapper/cash_flow_mapper.js index 13d223ad407..5a1df905ce6 100644 --- a/erpnext/accounts/doctype/cash_flow_mapper/cash_flow_mapper.js +++ b/erpnext/accounts/doctype/cash_flow_mapper/cash_flow_mapper.js @@ -1,6 +1,4 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Cash Flow Mapper', { - -}); +frappe.ui.form.on("Cash Flow Mapper", {}); diff --git a/erpnext/accounts/doctype/cash_flow_mapping/cash_flow_mapping.js b/erpnext/accounts/doctype/cash_flow_mapping/cash_flow_mapping.js index 00c71657c5c..402dd7e2202 100644 --- a/erpnext/accounts/doctype/cash_flow_mapping/cash_flow_mapping.js +++ b/erpnext/accounts/doctype/cash_flow_mapping/cash_flow_mapping.js @@ -1,43 +1,45 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Cash Flow Mapping', { - refresh: function(frm) { +frappe.ui.form.on("Cash Flow Mapping", { + refresh: function (frm) { frm.events.disable_unchecked_fields(frm); }, - reset_check_fields: function(frm) { - frm.fields.filter(field => field.df.fieldtype === 'Check') - .map(field => frm.set_df_property(field.df.fieldname, 'read_only', 0)); + reset_check_fields: function (frm) { + frm.fields + .filter((field) => field.df.fieldtype === "Check") + .map((field) => frm.set_df_property(field.df.fieldname, "read_only", 0)); }, has_checked_field(frm) { - const val = frm.fields.filter(field => field.value === 1); + const val = frm.fields.filter((field) => field.value === 1); return val.length ? 1 : 0; }, - _disable_unchecked_fields: function(frm) { + _disable_unchecked_fields: function (frm) { // get value of clicked field - frm.fields.filter(field => field.value === 0) - .map(field => frm.set_df_property(field.df.fieldname, 'read_only', 1)); + frm.fields + .filter((field) => field.value === 0) + .map((field) => frm.set_df_property(field.df.fieldname, "read_only", 1)); }, - disable_unchecked_fields: function(frm) { + disable_unchecked_fields: function (frm) { frm.events.reset_check_fields(frm); const checked = frm.events.has_checked_field(frm); if (checked) { frm.events._disable_unchecked_fields(frm); } }, - is_working_capital: function(frm) { + is_working_capital: function (frm) { frm.events.disable_unchecked_fields(frm); }, - is_finance_cost: function(frm) { + is_finance_cost: function (frm) { frm.events.disable_unchecked_fields(frm); }, - is_income_tax_liability: function(frm) { + is_income_tax_liability: function (frm) { frm.events.disable_unchecked_fields(frm); }, - is_income_tax_expense: function(frm) { + is_income_tax_expense: function (frm) { frm.events.disable_unchecked_fields(frm); }, - is_finance_cost_adjustment: function(frm) { + is_finance_cost_adjustment: function (frm) { frm.events.disable_unchecked_fields(frm); - } + }, }); diff --git a/erpnext/accounts/doctype/cash_flow_mapping_template/cash_flow_mapping_template.js b/erpnext/accounts/doctype/cash_flow_mapping_template/cash_flow_mapping_template.js index 8611153cd8b..5b799b70131 100644 --- a/erpnext/accounts/doctype/cash_flow_mapping_template/cash_flow_mapping_template.js +++ b/erpnext/accounts/doctype/cash_flow_mapping_template/cash_flow_mapping_template.js @@ -1,6 +1,4 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Cash Flow Mapping Template', { - -}); +frappe.ui.form.on("Cash Flow Mapping Template", {}); diff --git a/erpnext/accounts/doctype/cash_flow_mapping_template_details/cash_flow_mapping_template_details.js b/erpnext/accounts/doctype/cash_flow_mapping_template_details/cash_flow_mapping_template_details.js index 2e5dce4fb57..b0e869fd608 100644 --- a/erpnext/accounts/doctype/cash_flow_mapping_template_details/cash_flow_mapping_template_details.js +++ b/erpnext/accounts/doctype/cash_flow_mapping_template_details/cash_flow_mapping_template_details.js @@ -1,6 +1,4 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Cash Flow Mapping Template Details', { - -}); +frappe.ui.form.on("Cash Flow Mapping Template Details", {}); diff --git a/erpnext/accounts/doctype/cashier_closing/cashier_closing.js b/erpnext/accounts/doctype/cashier_closing/cashier_closing.js index ce791e43acd..71664b7ada3 100644 --- a/erpnext/accounts/doctype/cashier_closing/cashier_closing.js +++ b/erpnext/accounts/doctype/cashier_closing/cashier_closing.js @@ -1,11 +1,10 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.ui.form.on('Cashier Closing', { - - setup: function(frm){ +frappe.ui.form.on("Cashier Closing", { + setup: function (frm) { if (frm.doc.user == "" || frm.doc.user == null) { frm.doc.user = frappe.session.user; } - } + }, }); diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js index 56fa6ce2f30..1d8bb853083 100644 --- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js +++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js @@ -1,4 +1,4 @@ -frappe.ui.form.on('Chart of Accounts Importer', { +frappe.ui.form.on("Chart of Accounts Importer", { onload: function (frm) { frm.set_value("company", ""); frm.set_value("import_file", ""); @@ -8,31 +8,34 @@ frappe.ui.form.on('Chart of Accounts Importer', { frm.disable_save(); // make company mandatory - frm.set_df_property('company', 'reqd', frm.doc.company ? 0 : 1); - frm.set_df_property('import_file_section', 'hidden', frm.doc.company ? 0 : 1); + frm.set_df_property("company", "reqd", frm.doc.company ? 0 : 1); + frm.set_df_property("import_file_section", "hidden", frm.doc.company ? 0 : 1); if (frm.doc.import_file) { frappe.run_serially([ () => generate_tree_preview(frm), () => create_import_button(frm), - () => frm.set_df_property('chart_preview', 'hidden', 0) + () => frm.set_df_property("chart_preview", "hidden", 0), ]); } - frm.set_df_property('chart_preview', 'hidden', - $(frm.fields_dict['chart_tree'].wrapper).html()!="" ? 0 : 1); + frm.set_df_property( + "chart_preview", + "hidden", + $(frm.fields_dict["chart_tree"].wrapper).html() != "" ? 0 : 1 + ); }, - download_template: function(frm) { + download_template: function (frm) { var d = new frappe.ui.Dialog({ title: __("Download Template"), fields: [ { - label : "File Type", + label: "File Type", fieldname: "file_type", fieldtype: "Select", reqd: 1, - options: ["Excel", "CSV"] + options: ["Excel", "CSV"], }, { label: "Template Type", @@ -41,21 +44,27 @@ frappe.ui.form.on('Chart of Accounts Importer', { reqd: 1, options: ["Sample Template", "Blank Template"], change: () => { - let template_type = d.get_value('template_type'); + let template_type = d.get_value("template_type"); if (template_type === "Sample Template") { - d.set_df_property('template_type', 'description', + d.set_df_property( + "template_type", + "description", `The Sample Template contains all the required accounts pre filled in the template. - You can add more accounts or change existing accounts in the template as per your choice.`); + You can add more accounts or change existing accounts in the template as per your choice.` + ); } else { - d.set_df_property('template_type', 'description', + d.set_df_property( + "template_type", + "description", `The Blank Template contains just the account type and root type required to build the Chart - of Accounts. Please enter the account names and add more rows as per your requirement.`); + of Accounts. Please enter the account names and add more rows as per your requirement.` + ); } - } + }, }, { - label : "Company", + label: "Company", fieldname: "company", fieldtype: "Link", reqd: 1, @@ -63,25 +72,25 @@ frappe.ui.form.on('Chart of Accounts Importer', { default: frm.doc.company, }, ], - primary_action: function() { + primary_action: function () { let data = d.get_values(); if (!data.template_type) { - frappe.throw(__('Please select Template Type to download template')); + frappe.throw(__("Please select Template Type to download template")); } open_url_post( - '/api/method/erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.download_template', + "/api/method/erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.download_template", { file_type: data.file_type, template_type: data.template_type, - company: data.company + company: data.company, } ); d.hide(); }, - primary_action_label: __('Download') + primary_action_label: __("Download"), }); d.show(); }, @@ -89,7 +98,7 @@ frappe.ui.form.on('Chart of Accounts Importer', { import_file: function (frm) { if (!frm.doc.import_file) { frm.page.set_indicator(""); - $(frm.fields_dict['chart_tree'].wrapper).empty(); // empty wrapper on removing file + $(frm.fields_dict["chart_tree"].wrapper).empty(); // empty wrapper on removing file } }, @@ -99,89 +108,97 @@ frappe.ui.form.on('Chart of Accounts Importer', { frappe.call({ method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.validate_company", args: { - company: frm.doc.company + company: frm.doc.company, }, - callback: function(r) { - if(r.message===false) { + callback: function (r) { + if (r.message === false) { frm.set_value("company", ""); - frappe.throw(__("Transactions against the Company already exist! Chart of Accounts can only be imported for a Company with no transactions.")); + frappe.throw( + __( + "Transactions against the Company already exist! Chart of Accounts can only be imported for a Company with no transactions." + ) + ); } else { frm.trigger("refresh"); } - } + }, }); } - } + }, }); -var create_import_button = function(frm) { - frm.page.set_primary_action(__("Import"), function () { +var create_import_button = function (frm) { + frm.page + .set_primary_action(__("Import"), function () { + return frappe.call({ + method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.import_coa", + args: { + file_name: frm.doc.import_file, + company: frm.doc.company, + }, + freeze: true, + freeze_message: __("Creating Accounts..."), + callback: function (r) { + if (!r.exc) { + clearInterval(frm.page["interval"]); + frm.page.set_indicator(__("Import Successful"), "blue"); + create_reset_button(frm); + } + }, + }); + }) + .addClass("btn btn-primary"); +}; + +var create_reset_button = function (frm) { + frm.page + .set_primary_action(__("Reset"), function () { + frm.page.clear_primary_action(); + delete frm.page["show_import_button"]; + frm.reload_doc(); + }) + .addClass("btn btn-primary"); +}; + +var validate_coa = function (frm) { + if (frm.doc.import_file) { + let parent = __("All Accounts"); return frappe.call({ - method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.import_coa", + method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.get_coa", args: { - file_name: frm.doc.import_file, - company: frm.doc.company - }, - freeze: true, - freeze_message: __("Creating Accounts..."), - callback: function(r) { - if (!r.exc) { - clearInterval(frm.page["interval"]); - frm.page.set_indicator(__('Import Successful'), 'blue'); - create_reset_button(frm); - } - } - }); - }).addClass('btn btn-primary'); -}; - -var create_reset_button = function(frm) { - frm.page.set_primary_action(__("Reset"), function () { - frm.page.clear_primary_action(); - delete frm.page["show_import_button"]; - frm.reload_doc(); - }).addClass('btn btn-primary'); -}; - -var validate_coa = function(frm) { - if (frm.doc.import_file) { - let parent = __('All Accounts'); - return frappe.call({ - 'method': 'erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.get_coa', - 'args': { file_name: frm.doc.import_file, parent: parent, - doctype: 'Chart of Accounts Importer', + doctype: "Chart of Accounts Importer", file_type: frm.doc.file_type, - for_validate: 1 + for_validate: 1, }, - callback: function(r) { - if (r.message['show_import_button']) { - frm.page['show_import_button'] = Boolean(r.message['show_import_button']); + callback: function (r) { + if (r.message["show_import_button"]) { + frm.page["show_import_button"] = Boolean(r.message["show_import_button"]); } - } + }, }); } }; -var generate_tree_preview = function(frm) { - let parent = __('All Accounts'); - $(frm.fields_dict['chart_tree'].wrapper).empty(); // empty wrapper to load new data +var generate_tree_preview = function (frm) { + let parent = __("All Accounts"); + $(frm.fields_dict["chart_tree"].wrapper).empty(); // empty wrapper to load new data // generate tree structure based on the csv data return new frappe.ui.Tree({ - parent: $(frm.fields_dict['chart_tree'].wrapper), + parent: $(frm.fields_dict["chart_tree"].wrapper), label: parent, expandable: true, - method: 'erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.get_coa', + method: "erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.get_coa", args: { file_name: frm.doc.import_file, parent: parent, - doctype: 'Chart of Accounts Importer', - file_type: frm.doc.file_type + doctype: "Chart of Accounts Importer", + file_type: frm.doc.file_type, }, - onclick: function(node) { + onclick: function (node) { parent = node.value; - } + }, }); }; diff --git a/erpnext/accounts/doctype/cheque_print_template/cheque_print_template.js b/erpnext/accounts/doctype/cheque_print_template/cheque_print_template.js index d10c61858f1..17cb8d00e48 100644 --- a/erpnext/accounts/doctype/cheque_print_template/cheque_print_template.js +++ b/erpnext/accounts/doctype/cheque_print_template/cheque_print_template.js @@ -3,18 +3,20 @@ frappe.provide("erpnext.cheque_print"); -frappe.ui.form.on('Cheque Print Template', { - refresh: function(frm) { - if(!frm.doc.__islocal) { - frm.add_custom_button(frm.doc.has_print_format?__("Update Print Format"):__("Create Print Format"), - function() { +frappe.ui.form.on("Cheque Print Template", { + refresh: function (frm) { + if (!frm.doc.__islocal) { + frm.add_custom_button( + frm.doc.has_print_format ? __("Update Print Format") : __("Create Print Format"), + function () { erpnext.cheque_print.view_cheque_print(frm); - }).addClass("btn-primary"); + } + ).addClass("btn-primary"); - $(frm.fields_dict.cheque_print_preview.wrapper).empty() + $(frm.fields_dict.cheque_print_preview.wrapper).empty(); - - var template = '
    \ + var template = + '
    \
    frm.events.convert_to_ledger(frm)); + frm.add_custom_button(__("Convert to Non-Group"), () => frm.events.convert_to_ledger(frm)); } else if (doc.is_group == 0) { - frm.add_custom_button(__('Convert to Group'), - () => frm.events.convert_to_group(frm)); + frm.add_custom_button(__("Convert to Group"), () => frm.events.convert_to_group(frm)); } }, convert_to_group(frm) { - frm.call('convert_ledger_to_group').then(r => { - if(r.message === 1) { + frm.call("convert_ledger_to_group").then((r) => { + if (r.message === 1) { frm.refresh(); } }); }, convert_to_ledger(frm) { - frm.call('convert_group_to_ledger').then(r => { - if(r.message === 1) { + frm.call("convert_group_to_ledger").then((r) => { + if (r.message === 1) { frm.refresh(); } }); - } + }, }); diff --git a/erpnext/accounts/doctype/cost_center/cost_center_tree.js b/erpnext/accounts/doctype/cost_center/cost_center_tree.js index 1d482c58f1a..3edeb8efb0b 100644 --- a/erpnext/accounts/doctype/cost_center/cost_center_tree.js +++ b/erpnext/accounts/doctype/cost_center/cost_center_tree.js @@ -1,54 +1,84 @@ frappe.treeview_settings["Cost Center"] = { breadcrumb: "Accounts", get_tree_root: false, - filters: [{ - fieldname: "company", - fieldtype:"Select", - options: erpnext.utils.get_tree_options("company"), - label: __("Company"), - default: erpnext.utils.get_tree_default("company") - }], - root_label: "Cost Centers", - get_tree_nodes: 'erpnext.accounts.utils.get_children', - add_tree_node: 'erpnext.accounts.utils.add_cc', - menu_items:[ + filters: [ { - label: __('New Company'), - action: function() { frappe.new_doc("Company", true) }, - condition: 'frappe.boot.user.can_create.indexOf("Company") !== -1' - } + fieldname: "company", + fieldtype: "Select", + options: erpnext.utils.get_tree_options("company"), + label: __("Company"), + default: erpnext.utils.get_tree_default("company"), + }, ], - fields:[ - {fieldtype:'Data', fieldname:'cost_center_name', label:__('New Cost Center Name'), reqd:true}, - {fieldtype:'Check', fieldname:'is_group', label:__('Is Group'), - description:__('Further cost centers can be made under Groups but entries can be made against non-Groups')}, - {fieldtype:'Data', fieldname:'cost_center_number', label:__('Cost Center Number'), - description: __("Number of new Cost Center, it will be included in the cost center name as a prefix")} + root_label: "Cost Centers", + get_tree_nodes: "erpnext.accounts.utils.get_children", + add_tree_node: "erpnext.accounts.utils.add_cc", + menu_items: [ + { + label: __("New Company"), + action: function () { + frappe.new_doc("Company", true); + }, + condition: 'frappe.boot.user.can_create.indexOf("Company") !== -1', + }, ], - ignore_fields:["parent_cost_center"], - onload: function(treeview) { + fields: [ + { fieldtype: "Data", fieldname: "cost_center_name", label: __("New Cost Center Name"), reqd: true }, + { + fieldtype: "Check", + fieldname: "is_group", + label: __("Is Group"), + description: __( + "Further cost centers can be made under Groups but entries can be made against non-Groups" + ), + }, + { + fieldtype: "Data", + fieldname: "cost_center_number", + label: __("Cost Center Number"), + description: __( + "Number of new Cost Center, it will be included in the cost center name as a prefix" + ), + }, + ], + ignore_fields: ["parent_cost_center"], + onload: function (treeview) { function get_company() { return treeview.page.fields_dict.company.get_value(); } // tools - treeview.page.add_inner_button(__("Chart of Accounts"), function() { - frappe.set_route('Tree', 'Account', {company: get_company()}); - }, __('View')); + treeview.page.add_inner_button( + __("Chart of Accounts"), + function () { + frappe.set_route("Tree", "Account", { company: get_company() }); + }, + __("View") + ); // make - treeview.page.add_inner_button(__("Budget List"), function() { - frappe.set_route('List', 'Budget', {company: get_company()}); - }, __('Budget')); + treeview.page.add_inner_button( + __("Budget List"), + function () { + frappe.set_route("List", "Budget", { company: get_company() }); + }, + __("Budget") + ); - treeview.page.add_inner_button(__("Monthly Distribution"), function() { - frappe.set_route('List', 'Monthly Distribution', {company: get_company()}); - }, __('Budget')); + treeview.page.add_inner_button( + __("Monthly Distribution"), + function () { + frappe.set_route("List", "Monthly Distribution", { company: get_company() }); + }, + __("Budget") + ); - treeview.page.add_inner_button(__("Budget Variance Report"), function() { - frappe.set_route('query-report', 'Budget Variance Report', {company: get_company()}); - }, __('Budget')); - - } - -} + treeview.page.add_inner_button( + __("Budget Variance Report"), + function () { + frappe.set_route("query-report", "Budget Variance Report", { company: get_company() }); + }, + __("Budget") + ); + }, +}; diff --git a/erpnext/accounts/doctype/cost_center_allocation/cost_center_allocation.js b/erpnext/accounts/doctype/cost_center_allocation/cost_center_allocation.js index 0da90161f51..8adc5b21215 100644 --- a/erpnext/accounts/doctype/cost_center_allocation/cost_center_allocation.js +++ b/erpnext/accounts/doctype/cost_center_allocation/cost_center_allocation.js @@ -1,24 +1,24 @@ // Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Cost Center Allocation', { - setup: function(frm) { - frm.set_query('main_cost_center', function() { +frappe.ui.form.on("Cost Center Allocation", { + setup: function (frm) { + frm.set_query("main_cost_center", function () { return { filters: { company: frm.doc.company, - is_group: 0 - } + is_group: 0, + }, }; }); - frm.set_query('cost_center', 'allocation_percentages', function() { + frm.set_query("cost_center", "allocation_percentages", function () { return { filters: { company: frm.doc.company, - is_group: 0 - } + is_group: 0, + }, }; }); - } + }, }); diff --git a/erpnext/accounts/doctype/coupon_code/coupon_code.js b/erpnext/accounts/doctype/coupon_code/coupon_code.js index da3a9f8132f..f8b5bdc35f5 100644 --- a/erpnext/accounts/doctype/coupon_code/coupon_code.js +++ b/erpnext/accounts/doctype/coupon_code/coupon_code.js @@ -1,44 +1,41 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Coupon Code', { - setup: function(frm) { - frm.set_query("pricing_rule", function() { +frappe.ui.form.on("Coupon Code", { + setup: function (frm) { + frm.set_query("pricing_rule", function () { return { - filters: [ - ["Pricing Rule","coupon_code_based", "=", "1"] - ] + filters: [["Pricing Rule", "coupon_code_based", "=", "1"]], }; }); }, - coupon_name:function(frm){ - if (frm.doc.__islocal===1) { + coupon_name: function (frm) { + if (frm.doc.__islocal === 1) { frm.trigger("make_coupon_code"); } }, - coupon_type:function(frm){ - if (frm.doc.__islocal===1) { + coupon_type: function (frm) { + if (frm.doc.__islocal === 1) { frm.trigger("make_coupon_code"); } }, - make_coupon_code: function(frm) { - var coupon_name=frm.doc.coupon_name; + make_coupon_code: function (frm) { + var coupon_name = frm.doc.coupon_name; var coupon_code; - if (frm.doc.coupon_type=='Gift Card') { - coupon_code=Math.random().toString(12).substring(2, 12).toUpperCase(); + if (frm.doc.coupon_type == "Gift Card") { + coupon_code = Math.random().toString(12).substring(2, 12).toUpperCase(); + } else if (frm.doc.coupon_type == "Promotional") { + coupon_name = coupon_name.replace(/\s/g, ""); + coupon_code = coupon_name.toUpperCase().slice(0, 8); } - else if(frm.doc.coupon_type=='Promotional'){ - coupon_name=coupon_name.replace(/\s/g,''); - coupon_code=coupon_name.toUpperCase().slice(0,8); - } - frm.doc.coupon_code=coupon_code; - frm.refresh_field('coupon_code'); + frm.doc.coupon_code = coupon_code; + frm.refresh_field("coupon_code"); }, - refresh: function(frm) { + refresh: function (frm) { if (frm.doc.pricing_rule) { - frm.add_custom_button(__("Add/Edit Coupon Conditions"), function(){ + frm.add_custom_button(__("Add/Edit Coupon Conditions"), function () { frappe.set_route("Form", "Pricing Rule", frm.doc.pricing_rule); }); } - } + }, }); diff --git a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.js b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.js index 6c40f2bec0d..d931f627dbd 100644 --- a/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.js +++ b/erpnext/accounts/doctype/currency_exchange_settings/currency_exchange_settings.js @@ -1,28 +1,27 @@ // Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Currency Exchange Settings', { - service_provider: function(frm) { +frappe.ui.form.on("Currency Exchange Settings", { + service_provider: function (frm) { if (frm.doc.service_provider == "exchangerate.host") { - let result = ['result']; + let result = ["result"]; let params = { - date: '{transaction_date}', - from: '{from_currency}', - to: '{to_currency}' + date: "{transaction_date}", + from: "{from_currency}", + to: "{to_currency}", }; add_param(frm, "https://api.exchangerate.host/convert", params, result); } else if (frm.doc.service_provider == "frankfurter.app") { - let result = ['rates', '{to_currency}']; + let result = ["rates", "{to_currency}"]; let params = { - base: '{from_currency}', - symbols: '{to_currency}' + base: "{from_currency}", + symbols: "{to_currency}", }; add_param(frm, "https://frankfurter.app/{transaction_date}", params, result); } - } + }, }); - function add_param(frm, api, params, result) { var row; frm.clear_table("req_params"); @@ -30,13 +29,13 @@ function add_param(frm, api, params, result) { frm.doc.api_endpoint = api; - $.each(params, function(key, value) { + $.each(params, function (key, value) { row = frm.add_child("req_params"); row.key = key; row.value = value; }); - $.each(result, function(key, value) { + $.each(result, function (key, value) { row = frm.add_child("result_key"); row.key = value; }); diff --git a/erpnext/accounts/doctype/dunning/dunning.js b/erpnext/accounts/doctype/dunning/dunning.js index 9909c6c2ab0..5ddee638dea 100644 --- a/erpnext/accounts/doctype/dunning/dunning.js +++ b/erpnext/accounts/doctype/dunning/dunning.js @@ -9,7 +9,7 @@ frappe.ui.form.on("Dunning", { docstatus: 1, company: frm.doc.company, outstanding_amount: [">", 0], - status: "Overdue" + status: "Overdue", }, }; }); @@ -18,18 +18,14 @@ frappe.ui.form.on("Dunning", { filters: { company: frm.doc.company, root_type: "Income", - is_group: 0 - } + is_group: 0, + }, }; }); }, refresh: function (frm) { frm.set_df_property("company", "read_only", frm.doc.__islocal ? 0 : 1); - frm.set_df_property( - "sales_invoice", - "read_only", - frm.doc.__islocal ? 0 : 1 - ); + frm.set_df_property("sales_invoice", "read_only", frm.doc.__islocal ? 0 : 1); if (frm.doc.docstatus === 1 && frm.doc.status === "Unresolved") { frm.add_custom_button(__("Resolve"), () => { frm.set_value("status", "Resolved"); @@ -40,22 +36,27 @@ frappe.ui.form.on("Dunning", { __("Payment"), function () { frm.events.make_payment_entry(frm); - },__("Create") + }, + __("Create") ); frm.page.set_inner_btn_group_as_primary(__("Create")); } - if(frm.doc.docstatus > 0) { - frm.add_custom_button(__('Ledger'), function() { - frappe.route_options = { - "voucher_no": frm.doc.name, - "from_date": frm.doc.posting_date, - "to_date": frm.doc.posting_date, - "company": frm.doc.company, - "show_cancelled_entries": frm.doc.docstatus === 2 - }; - frappe.set_route("query-report", "General Ledger"); - }, __('View')); + if (frm.doc.docstatus > 0) { + frm.add_custom_button( + __("Ledger"), + function () { + frappe.route_options = { + voucher_no: frm.doc.name, + from_date: frm.doc.posting_date, + to_date: frm.doc.posting_date, + company: frm.doc.company, + show_cancelled_entries: frm.doc.docstatus === 2, + }; + frappe.set_route("query-report", "General Ledger"); + }, + __("View") + ); } }, overdue_days: function (frm) { @@ -86,8 +87,7 @@ frappe.ui.form.on("Dunning", { get_dunning_letter_text: function (frm) { if (frm.doc.dunning_type) { frappe.call({ - method: - "erpnext.accounts.doctype.dunning.dunning.get_dunning_letter_text", + method: "erpnext.accounts.doctype.dunning.dunning.get_dunning_letter_text", args: { dunning_type: frm.doc.dunning_type, language: frm.doc.language, @@ -129,26 +129,25 @@ frappe.ui.form.on("Dunning", { }, calculate_overdue_days: function (frm) { if (frm.doc.posting_date && frm.doc.due_date) { - const overdue_days = moment(frm.doc.posting_date).diff( - frm.doc.due_date, - "days" - ); + const overdue_days = moment(frm.doc.posting_date).diff(frm.doc.due_date, "days"); frm.set_value("overdue_days", overdue_days); } }, calculate_interest_and_amount: function (frm) { - const interest_per_year = frm.doc.outstanding_amount * frm.doc.rate_of_interest / 100; - const interest_amount = flt((interest_per_year * cint(frm.doc.overdue_days)) / 365 || 0, precision('interest_amount')); - const dunning_amount = flt(interest_amount + frm.doc.dunning_fee, precision('dunning_amount')); - const grand_total = flt(frm.doc.outstanding_amount + dunning_amount, precision('grand_total')); + const interest_per_year = (frm.doc.outstanding_amount * frm.doc.rate_of_interest) / 100; + const interest_amount = flt( + (interest_per_year * cint(frm.doc.overdue_days)) / 365 || 0, + precision("interest_amount") + ); + const dunning_amount = flt(interest_amount + frm.doc.dunning_fee, precision("dunning_amount")); + const grand_total = flt(frm.doc.outstanding_amount + dunning_amount, precision("grand_total")); frm.set_value("interest_amount", interest_amount); frm.set_value("dunning_amount", dunning_amount); frm.set_value("grand_total", grand_total); }, make_payment_entry: function (frm) { return frappe.call({ - method: - "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry", + method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry", args: { dt: frm.doc.doctype, dn: frm.doc.name, diff --git a/erpnext/accounts/doctype/dunning_type/dunning_type.js b/erpnext/accounts/doctype/dunning_type/dunning_type.js index 54156b488dd..5fa4fd411db 100644 --- a/erpnext/accounts/doctype/dunning_type/dunning_type.js +++ b/erpnext/accounts/doctype/dunning_type/dunning_type.js @@ -1,8 +1,7 @@ // Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Dunning Type', { +frappe.ui.form.on("Dunning Type", { // refresh: function(frm) { - // } }); diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js index 1ef5c837402..741039acae6 100644 --- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js +++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js @@ -1,75 +1,79 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Exchange Rate Revaluation', { - setup: function(frm) { - frm.set_query("party_type", "accounts", function() { +frappe.ui.form.on("Exchange Rate Revaluation", { + setup: function (frm) { + frm.set_query("party_type", "accounts", function () { return { - "filters": { - "name": ["in", Object.keys(frappe.boot.party_account_types)], - } + filters: { + name: ["in", Object.keys(frappe.boot.party_account_types)], + }, }; }); - frm.set_query("account", "accounts", function(doc) { + frm.set_query("account", "accounts", function (doc) { return { - "filters": { - "company": doc.company - } + filters: { + company: doc.company, + }, }; }); }, - refresh: function(frm) { - if(frm.doc.docstatus==1) { + refresh: function (frm) { + if (frm.doc.docstatus == 1) { frappe.call({ - method: 'check_journal_entry_condition', + method: "check_journal_entry_condition", doc: frm.doc, - callback: function(r) { + callback: function (r) { if (r.message) { - frm.add_custom_button(__('Journal Entries'), function() { - return frm.events.make_jv(frm); - }, __('Create')); + frm.add_custom_button( + __("Journal Entries"), + function () { + return frm.events.make_jv(frm); + }, + __("Create") + ); } - } + }, }); } }, - validate_rounding_loss: function(frm) { + validate_rounding_loss: function (frm) { let allowance = frm.doc.rounding_loss_allowance; if (!(allowance >= 0 && allowance < 1)) { frappe.throw(__("Rounding Loss Allowance should be between 0 and 1")); } }, - rounding_loss_allowance: function(frm) { + rounding_loss_allowance: function (frm) { frm.events.validate_rounding_loss(frm); }, - validate: function(frm) { + validate: function (frm) { frm.events.validate_rounding_loss(frm); }, - get_entries: function(frm, account) { + get_entries: function (frm, account) { frappe.call({ method: "get_accounts_data", doc: cur_frm.doc, account: account, - callback: function(r){ + callback: function (r) { frappe.model.clear_table(frm.doc, "accounts"); - if(r.message) { + if (r.message) { r.message.forEach((d) => { - cur_frm.add_child("accounts",d); + cur_frm.add_child("accounts", d); }); frm.events.get_total_gain_loss(frm); refresh_field("accounts"); } - } + }, }); }, - get_total_gain_loss: function(frm) { - if(!(frm.doc.accounts && frm.doc.accounts.length)) return; + get_total_gain_loss: function (frm) { + if (!(frm.doc.accounts && frm.doc.accounts.length)) return; let total_gain_loss = 0; frm.doc.accounts.forEach((d) => { @@ -80,7 +84,7 @@ frappe.ui.form.on('Exchange Rate Revaluation', { frm.refresh_fields(); }, - make_jv : function(frm) { + make_jv: function (frm) { let revaluation_journal = null; let zero_balance_journal = null; frappe.call({ @@ -88,66 +92,68 @@ frappe.ui.form.on('Exchange Rate Revaluation', { doc: frm.doc, freeze: true, freeze_message: "Making Journal Entries...", - callback: function(r){ + callback: function (r) { if (r.message) { let response = r.message; - if(response['revaluation_jv'] || response['zero_balance_jv']) { + if (response["revaluation_jv"] || response["zero_balance_jv"]) { frappe.msgprint(__("Journals have been created")); } } - } + }, }); - } + }, }); frappe.ui.form.on("Exchange Rate Revaluation Account", { - new_exchange_rate: function(frm, cdt, cdn) { + new_exchange_rate: function (frm, cdt, cdn) { var row = frappe.get_doc(cdt, cdn); - row.new_balance_in_base_currency = flt(row.new_exchange_rate * flt(row.balance_in_account_currency), - precision("new_balance_in_base_currency", row)); + row.new_balance_in_base_currency = flt( + row.new_exchange_rate * flt(row.balance_in_account_currency), + precision("new_balance_in_base_currency", row) + ); row.gain_loss = row.new_balance_in_base_currency - flt(row.balance_in_base_currency); refresh_field("accounts"); frm.events.get_total_gain_loss(frm); }, - account: function(frm, cdt, cdn) { + account: function (frm, cdt, cdn) { var row = locals[cdt][cdn]; if (row.account) { get_account_details(frm, cdt, cdn); } }, - party: function(frm, cdt, cdn) { + party: function (frm, cdt, cdn) { var row = locals[cdt][cdn]; if (row.party && row.account) { get_account_details(frm, cdt, cdn); } }, - accounts_remove: function(frm) { + accounts_remove: function (frm) { frm.events.get_total_gain_loss(frm); - } + }, }); -var get_account_details = function(frm, cdt, cdn) { +var get_account_details = function (frm, cdt, cdn) { var row = frappe.get_doc(cdt, cdn); - if(!frm.doc.company || !frm.doc.posting_date) { + if (!frm.doc.company || !frm.doc.posting_date) { frappe.throw(__("Please select Company and Posting Date to getting entries")); } frappe.call({ method: "erpnext.accounts.doctype.exchange_rate_revaluation.exchange_rate_revaluation.get_account_details", - args:{ + args: { account: row.account, company: frm.doc.company, posting_date: frm.doc.posting_date, party_type: row.party_type, party: row.party, - rounding_loss_allowance: frm.doc.rounding_loss_allowance + rounding_loss_allowance: frm.doc.rounding_loss_allowance, }, - callback: function(r){ + callback: function (r) { $.extend(row, r.message); refresh_field("accounts"); frm.events.get_total_gain_loss(frm); - } + }, }); }; diff --git a/erpnext/accounts/doctype/finance_book/finance_book.js b/erpnext/accounts/doctype/finance_book/finance_book.js index 71191bb0cfa..ebe1d246ef1 100644 --- a/erpnext/accounts/doctype/finance_book/finance_book.js +++ b/erpnext/accounts/doctype/finance_book/finance_book.js @@ -1,8 +1,6 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Finance Book', { - refresh: function(frm) { - - } +frappe.ui.form.on("Finance Book", { + refresh: function (frm) {}, }); diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.js b/erpnext/accounts/doctype/fiscal_year/fiscal_year.js index 508b2eaf2a4..a44b52f08f8 100644 --- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.js +++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.js @@ -1,17 +1,21 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -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)); +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) + ); } }, - year_start_date: function(frm) { + year_start_date: function (frm) { if (!frm.doc.is_short_year) { - let year_end_date = - frappe.datetime.add_days(frappe.datetime.add_months(frm.doc.year_start_date, 12), -1); + let year_end_date = frappe.datetime.add_days( + frappe.datetime.add_months(frm.doc.year_start_date, 12), + -1 + ); frm.set_value("year_end_date", year_end_date); } }, diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.js b/erpnext/accounts/doctype/gl_entry/gl_entry.js index 4d2a5135187..7f81a2a4e02 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.js +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.js @@ -1,8 +1,8 @@ // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('GL Entry', { - refresh: function(frm) { - frm.page.btn_secondary.hide() - } +frappe.ui.form.on("GL Entry", { + refresh: function (frm) { + frm.page.btn_secondary.hide(); + }, }); diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js index db4f7c423f9..974f037a81d 100644 --- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js +++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js @@ -1,47 +1,49 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Invoice Discounting', { +frappe.ui.form.on("Invoice Discounting", { setup: (frm) => { frm.set_query("sales_invoice", "invoices", (doc) => { return { - "filters": { - "docstatus": 1, - "company": doc.company, - "outstanding_amount": [">", 0] + filters: { + docstatus: 1, + company: doc.company, + outstanding_amount: [">", 0], }, }; }); - frm.events.filter_accounts("bank_account", frm, [["account_type", "=", "Bank"]]); frm.events.filter_accounts("bank_charges_account", frm, [["root_type", "=", "Expense"]]); frm.events.filter_accounts("short_term_loan", frm, [["root_type", "=", "Liability"]]); - frm.events.filter_accounts("accounts_receivable_discounted", frm, [["account_type", "=", "Receivable"]]); + frm.events.filter_accounts("accounts_receivable_discounted", frm, [ + ["account_type", "=", "Receivable"], + ]); frm.events.filter_accounts("accounts_receivable_credit", frm, [["account_type", "=", "Receivable"]]); frm.events.filter_accounts("accounts_receivable_unpaid", frm, [["account_type", "=", "Receivable"]]); - }, filter_accounts: (fieldname, frm, addl_filters) => { let filters = [ ["company", "=", frm.doc.company], - ["is_group", "=", 0] + ["is_group", "=", 0], ]; - if(addl_filters){ - filters = $.merge(filters , addl_filters); + if (addl_filters) { + filters = $.merge(filters, addl_filters); } - frm.set_query(fieldname, () => { return { "filters": filters }; }); + frm.set_query(fieldname, () => { + return { filters: filters }; + }); }, - refresh_filters: (frm) =>{ - let invoice_accounts = Object.keys(frm.doc.invoices).map(function(key) { + refresh_filters: (frm) => { + let invoice_accounts = Object.keys(frm.doc.invoices).map(function (key) { return frm.doc.invoices[key].debit_to; }); let filters = [ ["account_type", "=", "Receivable"], - ["name", "not in", invoice_accounts] + ["name", "not in", invoice_accounts], ]; frm.events.filter_accounts("accounts_receivable_credit", frm, filters); frm.events.filter_accounts("accounts_receivable_discounted", frm, filters); @@ -52,19 +54,19 @@ frappe.ui.form.on('Invoice Discounting', { frm.events.show_general_ledger(frm); if (frm.doc.docstatus === 0) { - frm.add_custom_button(__('Get Invoices'), function() { + frm.add_custom_button(__("Get Invoices"), function () { frm.events.get_invoices(frm); }); } if (frm.doc.docstatus === 1 && frm.doc.status !== "Settled") { if (frm.doc.status == "Sanctioned") { - frm.add_custom_button(__('Disburse Loan'), function() { + frm.add_custom_button(__("Disburse Loan"), function () { frm.events.create_disbursement_entry(frm); }).addClass("btn-primary"); } if (frm.doc.status == "Disbursed") { - frm.add_custom_button(__('Close Loan'), function() { + frm.add_custom_button(__("Close Loan"), function () { frm.events.close_loan(frm); }).addClass("btn-primary"); } @@ -92,119 +94,121 @@ frappe.ui.form.on('Invoice Discounting', { calculate_total_amount: (frm) => { let total_amount = 0.0; - for (let row of (frm.doc.invoices || [])) { + for (let row of frm.doc.invoices || []) { total_amount += flt(row.outstanding_amount); } frm.set_value("total_amount", total_amount); }, get_invoices: (frm) => { var d = new frappe.ui.Dialog({ - title: __('Get Invoices based on Filters'), + title: __("Get Invoices based on Filters"), fields: [ { - "label": "Customer", - "fieldname": "customer", - "fieldtype": "Link", - "options": "Customer" + label: "Customer", + fieldname: "customer", + fieldtype: "Link", + options: "Customer", }, { - "label": "From Date", - "fieldname": "from_date", - "fieldtype": "Date" + label: "From Date", + fieldname: "from_date", + fieldtype: "Date", }, { - "label": "To Date", - "fieldname": "to_date", - "fieldtype": "Date" + label: "To Date", + fieldname: "to_date", + fieldtype: "Date", }, { - "fieldname": "col_break", - "fieldtype": "Column Break", + fieldname: "col_break", + fieldtype: "Column Break", }, { - "label": "Min Amount", - "fieldname": "min_amount", - "fieldtype": "Currency" + label: "Min Amount", + fieldname: "min_amount", + fieldtype: "Currency", }, { - "label": "Max Amount", - "fieldname": "max_amount", - "fieldtype": "Currency" - } + label: "Max Amount", + fieldname: "max_amount", + fieldtype: "Currency", + }, ], - primary_action: function() { + primary_action: function () { var data = d.get_values(); frappe.call({ method: "erpnext.accounts.doctype.invoice_discounting.invoice_discounting.get_invoices", args: { - filters: data + filters: data, }, - callback: function(r) { - if(!r.exc) { + callback: function (r) { + if (!r.exc) { d.hide(); - $.each(r.message, function(i, v) { - frm.doc.invoices = frm.doc.invoices.filter(row => row.sales_invoice); + $.each(r.message, function (i, v) { + frm.doc.invoices = frm.doc.invoices.filter((row) => row.sales_invoice); let row = frm.add_child("invoices"); $.extend(row, v); frm.events.refresh_filters(frm); }); refresh_field("invoices"); } - } + }, }); }, - primary_action_label: __('Get Invocies') + primary_action_label: __("Get Invocies"), }); d.show(); }, create_disbursement_entry: (frm) => { frappe.call({ - method:"create_disbursement_entry", + method: "create_disbursement_entry", doc: frm.doc, - callback: function(r) { - if(!r.exc){ + callback: function (r) { + if (!r.exc) { var doclist = frappe.model.sync(r.message); frappe.set_route("Form", doclist[0].doctype, doclist[0].name); } - } + }, }); - }, close_loan: (frm) => { frappe.call({ - method:"close_loan", + method: "close_loan", doc: frm.doc, - callback: function(r) { - if(!r.exc){ + callback: function (r) { + if (!r.exc) { var doclist = frappe.model.sync(r.message); frappe.set_route("Form", doclist[0].doctype, doclist[0].name); } - } + }, }); - }, show_general_ledger: (frm) => { - if(frm.doc.docstatus > 0) { - cur_frm.add_custom_button(__('Accounting Ledger'), function() { - frappe.route_options = { - voucher_no: frm.doc.name, - from_date: frm.doc.posting_date, - to_date: moment(frm.doc.modified).format('YYYY-MM-DD'), - company: frm.doc.company, - group_by: "Group by Voucher (Consolidated)", - show_cancelled_entries: frm.doc.docstatus === 2 - }; - frappe.set_route("query-report", "General Ledger"); - }, __("View")); + if (frm.doc.docstatus > 0) { + cur_frm.add_custom_button( + __("Accounting Ledger"), + function () { + frappe.route_options = { + voucher_no: frm.doc.name, + from_date: frm.doc.posting_date, + to_date: moment(frm.doc.modified).format("YYYY-MM-DD"), + company: frm.doc.company, + group_by: "Group by Voucher (Consolidated)", + show_cancelled_entries: frm.doc.docstatus === 2, + }; + frappe.set_route("query-report", "General Ledger"); + }, + __("View") + ); } - } + }, }); -frappe.ui.form.on('Discounted Invoice', { +frappe.ui.form.on("Discounted Invoice", { sales_invoice: (frm) => { frm.events.calculate_total_amount(frm); frm.events.refresh_filters(frm); @@ -212,5 +216,5 @@ frappe.ui.form.on('Discounted Invoice', { invoices_remove: (frm) => { frm.events.calculate_total_amount(frm); frm.events.refresh_filters(frm); - } + }, }); diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting_list.js b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting_list.js index 4895efcd4cc..0b08e393de2 100644 --- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting_list.js +++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting_list.js @@ -1,21 +1,16 @@ -frappe.listview_settings['Invoice Discounting'] = { +frappe.listview_settings["Invoice Discounting"] = { add_fields: ["status"], - get_indicator: function(doc) - { - if(doc.status == "Draft") { + get_indicator: function (doc) { + if (doc.status == "Draft") { return [__("Draft"), "red", "status,=,Draft"]; - } - else if(doc.status == "Sanctioned") { + } else if (doc.status == "Sanctioned") { return [__("Sanctioned"), "green", "status,=,Sanctioned"]; - } - else if(doc.status == "Disbursed") { + } else if (doc.status == "Disbursed") { return [__("Disbursed"), "blue", "status,=,Disbursed"]; - } - else if(doc.status == "Settled") { + } else if (doc.status == "Settled") { return [__("Settled"), "orange", "status,=,Settled"]; - } - else if(doc.status == "Canceled") { + } else if (doc.status == "Canceled") { return [__("Canceled"), "red", "status,=,Canceled"]; } - } + }, }; diff --git a/erpnext/accounts/doctype/item_tax_template/item_tax_template.js b/erpnext/accounts/doctype/item_tax_template/item_tax_template.js index e921a0d949d..b608ccd3568 100644 --- a/erpnext/accounts/doctype/item_tax_template/item_tax_template.js +++ b/erpnext/accounts/doctype/item_tax_template/item_tax_template.js @@ -1,27 +1,49 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Item Tax Template', { - setup: function(frm) { - frm.set_query("tax_type", "taxes", function(doc) { +frappe.ui.form.on("Item Tax Template", { + setup: function (frm) { + frm.set_query("tax_type", "taxes", function (doc) { return { filters: [ - ['Account', 'company', '=', frm.doc.company], - ['Account', 'is_group', '=', 0], - ['Account', 'account_type', 'in', ['Tax', 'Chargeable', 'Income Account', 'Expense Account', 'Expenses Included In Valuation']] - ] - } + ["Account", "company", "=", frm.doc.company], + ["Account", "is_group", "=", 0], + [ + "Account", + "account_type", + "in", + [ + "Tax", + "Chargeable", + "Income Account", + "Expense Account", + "Expenses Included In Valuation", + ], + ], + ], + }; }); }, company: function (frm) { - frm.set_query("tax_type", "taxes", function(doc) { + frm.set_query("tax_type", "taxes", function (doc) { return { filters: [ - ['Account', 'company', '=', frm.doc.company], - ['Account', 'is_group', '=', 0], - ['Account', 'account_type', 'in', ['Tax', 'Chargeable', 'Income Account', 'Expense Account', 'Expenses Included In Valuation']] - ] - } + ["Account", "company", "=", frm.doc.company], + ["Account", "is_group", "=", 0], + [ + "Account", + "account_type", + "in", + [ + "Tax", + "Chargeable", + "Income Account", + "Expense Account", + "Expenses Included In Valuation", + ], + ], + ], + }; }); - } + }, }); diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index 5f59f036ef9..a2f5455f2f7 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -4,39 +4,57 @@ frappe.provide("erpnext.accounts"); frappe.provide("erpnext.journal_entry"); - frappe.ui.form.on("Journal Entry", { - setup: function(frm) { + setup: function (frm) { frm.add_fetch("bank_account", "account", "account"); - frm.ignore_doctypes_on_cancel_all = ["Sales Invoice", "Purchase Invoice", "Journal Entry", "Repost Payment Ledger", "Asset", "Asset Movement", "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries", "Bank Transaction"]; + frm.ignore_doctypes_on_cancel_all = [ + "Sales Invoice", + "Purchase Invoice", + "Journal Entry", + "Repost Payment Ledger", + "Asset", + "Asset Movement", + "Repost Accounting Ledger", + "Unreconcile Payment", + "Unreconcile Payment Entries", + "Bank Transaction", + ]; }, - refresh: function(frm) { + refresh: function (frm) { erpnext.toggle_naming_series(); - if(frm.doc.docstatus > 0) { - frm.add_custom_button(__('Ledger'), function() { - frappe.route_options = { - "voucher_no": frm.doc.name, - "from_date": frm.doc.posting_date, - "to_date": moment(frm.doc.modified).format('YYYY-MM-DD'), - "company": frm.doc.company, - "finance_book": frm.doc.finance_book, - "group_by": '', - "show_cancelled_entries": frm.doc.docstatus === 2 - }; - frappe.set_route("query-report", "General Ledger"); - }, __('View')); + if (frm.doc.docstatus > 0) { + frm.add_custom_button( + __("Ledger"), + function () { + frappe.route_options = { + voucher_no: frm.doc.name, + from_date: frm.doc.posting_date, + to_date: moment(frm.doc.modified).format("YYYY-MM-DD"), + company: frm.doc.company, + finance_book: frm.doc.finance_book, + group_by: "", + show_cancelled_entries: frm.doc.docstatus === 2, + }; + frappe.set_route("query-report", "General Ledger"); + }, + __("View") + ); } - if(frm.doc.docstatus==1) { - frm.add_custom_button(__('Reverse Journal Entry'), function() { - return erpnext.journal_entry.reverse_journal_entry(frm); - }, __('Actions')); + if (frm.doc.docstatus == 1) { + frm.add_custom_button( + __("Reverse Journal Entry"), + function () { + return erpnext.journal_entry.reverse_journal_entry(frm); + }, + __("Actions") + ); } if (frm.doc.__islocal) { - frm.add_custom_button(__('Quick Entry'), function() { + frm.add_custom_button(__("Quick Entry"), function () { return erpnext.journal_entry.quick_entry(frm); }); } @@ -44,52 +62,63 @@ frappe.ui.form.on("Journal Entry", { // hide /unhide fields based on currency erpnext.journal_entry.toggle_fields_based_on_currency(frm); - if ((frm.doc.voucher_type == "Inter Company Journal Entry") && (frm.doc.docstatus == 1) && (!frm.doc.inter_company_journal_entry_reference)) { - frm.add_custom_button(__("Create Inter Company Journal Entry"), - function() { + if ( + frm.doc.voucher_type == "Inter Company Journal Entry" && + frm.doc.docstatus == 1 && + !frm.doc.inter_company_journal_entry_reference + ) { + frm.add_custom_button( + __("Create Inter Company Journal Entry"), + function () { frm.trigger("make_inter_company_journal_entry"); - }, __('Make')); + }, + __("Make") + ); } erpnext.accounts.unreconcile_payment.add_unreconcile_btn(frm); }, - before_save: function(frm) { - if ((frm.doc.docstatus == 0) && (!frm.doc.is_system_generated)) { - let payment_entry_references = frm.doc.accounts.filter(elem => (elem.reference_type == "Payment Entry")); + before_save: function (frm) { + if (frm.doc.docstatus == 0 && !frm.doc.is_system_generated) { + let payment_entry_references = frm.doc.accounts.filter( + (elem) => elem.reference_type == "Payment Entry" + ); if (payment_entry_references.length > 0) { - let rows = payment_entry_references.map(x => "#"+x.idx); - frappe.throw(__("Rows: {0} have 'Payment Entry' as reference_type. This should not be set manually.", [frappe.utils.comma_and(rows)])); + let rows = payment_entry_references.map((x) => "#" + x.idx); + frappe.throw( + __("Rows: {0} have 'Payment Entry' as reference_type. This should not be set manually.", [ + frappe.utils.comma_and(rows), + ]) + ); } } }, - make_inter_company_journal_entry: function(frm) { + make_inter_company_journal_entry: function (frm) { var d = new frappe.ui.Dialog({ title: __("Select Company"), fields: [ { - 'fieldname': 'company', - 'fieldtype': 'Link', - 'label': __('Company'), - 'options': 'Company', - "get_query": function () { + fieldname: "company", + fieldtype: "Link", + label: __("Company"), + options: "Company", + get_query: function () { return { - filters: [ - ["Company", "name", "!=", frm.doc.company] - ] + filters: [["Company", "name", "!=", frm.doc.company]], }; }, - 'reqd': 1 - } + reqd: 1, + }, ], }); - d.set_primary_action(__('Create'), function() { + d.set_primary_action(__("Create"), function () { d.hide(); var args = d.get_values(); frappe.call({ args: { - "name": frm.doc.name, - "voucher_type": frm.doc.voucher_type, - "company": args.company + name: frm.doc.name, + voucher_type: frm.doc.voucher_type, + company: args.company, }, method: "erpnext.accounts.doctype.journal_entry.journal_entry.make_inter_company_journal_entry", callback: function (r) { @@ -97,96 +126,101 @@ frappe.ui.form.on("Journal Entry", { var doc = frappe.model.sync(r.message)[0]; frappe.set_route("Form", doc.doctype, doc.name); } - } + }, }); }); d.show(); }, - multi_currency: function(frm) { + multi_currency: function (frm) { erpnext.journal_entry.toggle_fields_based_on_currency(frm); }, - posting_date: function(frm) { - if(!frm.doc.multi_currency || !frm.doc.posting_date) return; + posting_date: function (frm) { + if (!frm.doc.multi_currency || !frm.doc.posting_date) return; - $.each(frm.doc.accounts || [], function(i, row) { + $.each(frm.doc.accounts || [], function (i, row) { erpnext.journal_entry.set_exchange_rate(frm, row.doctype, row.name); - }) + }); }, - company: function(frm) { + company: function (frm) { frappe.call({ method: "frappe.client.get_value", args: { doctype: "Company", - filters: {"name": frm.doc.company}, - fieldname: "cost_center" + filters: { name: frm.doc.company }, + fieldname: "cost_center", }, - callback: function(r){ - if(r.message){ - $.each(frm.doc.accounts || [], function(i, jvd) { + callback: function (r) { + if (r.message) { + $.each(frm.doc.accounts || [], function (i, jvd) { frappe.model.set_value(jvd.doctype, jvd.name, "cost_center", r.message.cost_center); }); } - } + }, }); erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); }, - voucher_type: function(frm){ + voucher_type: function (frm) { + if (!frm.doc.company) return null; - if(!frm.doc.company) return null; - - if((!(frm.doc.accounts || []).length) || ((frm.doc.accounts || []).length === 1 && !frm.doc.accounts[0].account)) { - if(in_list(["Bank Entry", "Cash Entry"], frm.doc.voucher_type)) { + if ( + !(frm.doc.accounts || []).length || + ((frm.doc.accounts || []).length === 1 && !frm.doc.accounts[0].account) + ) { + if (in_list(["Bank Entry", "Cash Entry"], frm.doc.voucher_type)) { return frappe.call({ type: "GET", method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_default_bank_cash_account", args: { - "account_type": (frm.doc.voucher_type=="Bank Entry" ? - "Bank" : (frm.doc.voucher_type=="Cash Entry" ? "Cash" : null)), - "company": frm.doc.company + account_type: + frm.doc.voucher_type == "Bank Entry" + ? "Bank" + : frm.doc.voucher_type == "Cash Entry" + ? "Cash" + : null, + company: frm.doc.company, }, - callback: function(r) { - if(r.message) { + callback: function (r) { + if (r.message) { // If default company bank account not set - if(!$.isEmptyObject(r.message)){ + if (!$.isEmptyObject(r.message)) { update_jv_details(frm.doc, [r.message]); } } - } + }, }); } } }, - from_template: function(frm){ - if (frm.doc.from_template){ - frappe.db.get_doc("Journal Entry Template", frm.doc.from_template) - .then((doc) => { - frappe.model.clear_table(frm.doc, "accounts"); - frm.set_value({ - "company": doc.company, - "voucher_type": doc.voucher_type, - "naming_series": doc.naming_series, - "is_opening": doc.is_opening, - "multi_currency": doc.multi_currency - }) - update_jv_details(frm.doc, doc.accounts); + from_template: function (frm) { + if (frm.doc.from_template) { + frappe.db.get_doc("Journal Entry Template", frm.doc.from_template).then((doc) => { + frappe.model.clear_table(frm.doc, "accounts"); + frm.set_value({ + company: doc.company, + voucher_type: doc.voucher_type, + naming_series: doc.naming_series, + is_opening: doc.is_opening, + multi_currency: doc.multi_currency, }); + update_jv_details(frm.doc, doc.accounts); + }); } - } + }, }); -var update_jv_details = function(doc, r) { - $.each(r, function(i, d) { +var update_jv_details = function (doc, r) { + $.each(r, function (i, d) { var row = frappe.model.add_child(doc, "Journal Entry Account", "accounts"); - frappe.model.set_value(row.doctype, row.name, "account", d.account) + frappe.model.set_value(row.doctype, row.name, "account", d.account); }); refresh_field("accounts"); -} +}; erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Controller { onload() { @@ -201,69 +235,67 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro load_defaults() { //this.frm.show_print_first = true; - if(this.frm.doc.__islocal && this.frm.doc.company) { + if (this.frm.doc.__islocal && this.frm.doc.company) { frappe.model.set_default_values(this.frm.doc); - $.each(this.frm.doc.accounts || [], function(i, jvd) { + $.each(this.frm.doc.accounts || [], function (i, jvd) { frappe.model.set_default_values(jvd); }); var posting_date = this.frm.doc.posting_date; - if(!this.frm.doc.amended_from) this.frm.set_value('posting_date', posting_date || frappe.datetime.get_today()); + if (!this.frm.doc.amended_from) + this.frm.set_value("posting_date", posting_date || frappe.datetime.get_today()); } } setup_queries() { var me = this; - me.frm.set_query("account", "accounts", function(doc, cdt, cdn) { + me.frm.set_query("account", "accounts", function (doc, cdt, cdn) { return erpnext.journal_entry.account_query(me.frm); }); - me.frm.set_query("party_type", "accounts", function(doc, cdt, cdn) { + me.frm.set_query("party_type", "accounts", function (doc, cdt, cdn) { const row = locals[cdt][cdn]; return { query: "erpnext.setup.doctype.party_type.party_type.get_party_type", filters: { - 'account': row.account - } - } + account: row.account, + }, + }; }); - me.frm.set_query("reference_name", "accounts", function(doc, cdt, cdn) { + me.frm.set_query("reference_name", "accounts", function (doc, cdt, cdn) { var jvd = frappe.get_doc(cdt, cdn); // journal entry - if(jvd.reference_type==="Journal Entry") { + if (jvd.reference_type === "Journal Entry") { frappe.model.validate_missing(jvd, "account"); return { query: "erpnext.accounts.doctype.journal_entry.journal_entry.get_against_jv", filters: { account: jvd.account, - party: jvd.party - } + party: jvd.party, + }, }; } var out = { - filters: [ - [jvd.reference_type, "docstatus", "=", 1] - ] + filters: [[jvd.reference_type, "docstatus", "=", 1]], }; - if(in_list(["Sales Invoice", "Purchase Invoice"], jvd.reference_type)) { + if (in_list(["Sales Invoice", "Purchase Invoice"], jvd.reference_type)) { out.filters.push([jvd.reference_type, "outstanding_amount", "!=", 0]); // Filter by cost center - if(jvd.cost_center) { + if (jvd.cost_center) { out.filters.push([jvd.reference_type, "cost_center", "in", ["", jvd.cost_center]]); } // account filter frappe.model.validate_missing(jvd, "account"); - var party_account_field = jvd.reference_type==="Sales Invoice" ? "debit_to": "credit_to"; + var party_account_field = jvd.reference_type === "Sales Invoice" ? "debit_to" : "credit_to"; out.filters.push([jvd.reference_type, party_account_field, "=", jvd.account]); - } - if(in_list(["Sales Order", "Purchase Order"], jvd.reference_type)) { + if (in_list(["Sales Order", "Purchase Order"], jvd.reference_type)) { // party_type and party mandatory frappe.model.validate_missing(jvd, "party_type"); frappe.model.validate_missing(jvd, "party"); @@ -271,11 +303,11 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro out.filters.push([jvd.reference_type, "per_billed", "<", 100]); } - if(jvd.party_type && jvd.party) { + if (jvd.party_type && jvd.party) { var party_field = ""; - if(jvd.reference_type.indexOf("Sales")===0) { + if (jvd.reference_type.indexOf("Sales") === 0) { var party_field = "customer"; - } else if (jvd.reference_type.indexOf("Purchase")===0) { + } else if (jvd.reference_type.indexOf("Purchase") === 0) { var party_field = "supplier"; } @@ -286,51 +318,49 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro return out; }); - - } reference_name(doc, cdt, cdn) { var d = frappe.get_doc(cdt, cdn); - if(d.reference_name) { - if (d.reference_type==="Purchase Invoice" && !flt(d.debit)) { - this.get_outstanding('Purchase Invoice', d.reference_name, doc.company, d); - } else if (d.reference_type==="Sales Invoice" && !flt(d.credit)) { - this.get_outstanding('Sales Invoice', d.reference_name, doc.company, d); - } else if (d.reference_type==="Journal Entry" && !flt(d.credit) && !flt(d.debit)) { - this.get_outstanding('Journal Entry', d.reference_name, doc.company, d); + if (d.reference_name) { + if (d.reference_type === "Purchase Invoice" && !flt(d.debit)) { + this.get_outstanding("Purchase Invoice", d.reference_name, doc.company, d); + } else if (d.reference_type === "Sales Invoice" && !flt(d.credit)) { + this.get_outstanding("Sales Invoice", d.reference_name, doc.company, d); + } else if (d.reference_type === "Journal Entry" && !flt(d.credit) && !flt(d.debit)) { + this.get_outstanding("Journal Entry", d.reference_name, doc.company, d); } } } get_outstanding(doctype, docname, company, child) { var args = { - "doctype": doctype, - "docname": docname, - "party": child.party, - "account": child.account, - "account_currency": child.account_currency, - "company": company - } + doctype: doctype, + docname: docname, + party: child.party, + account: child.account, + account_currency: child.account_currency, + company: company, + }; return frappe.call({ method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_outstanding", - args: { args: args}, - callback: function(r) { - if(r.message) { - $.each(r.message, function(field, value) { + args: { args: args }, + callback: function (r) { + if (r.message) { + $.each(r.message, function (field, value) { frappe.model.set_value(child.doctype, child.name, field, value); - }) + }); } - } + }, }); } accounts_add(doc, cdt, cdn) { var row = frappe.get_doc(cdt, cdn); - $.each(doc.accounts, function(i, d) { - if(d.account && d.party && d.party_type) { + $.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; @@ -338,8 +368,8 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro }); // set difference - if(doc.difference) { - if(doc.difference > 0) { + if (doc.difference) { + if (doc.difference > 0) { row.credit_in_account_currency = doc.difference; row.credit = doc.difference; } else { @@ -349,41 +379,43 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro } cur_frm.cscript.update_totals(doc); - erpnext.accounts.dimensions.copy_dimension_from_first_row(this.frm, cdt, cdn, 'accounts'); + erpnext.accounts.dimensions.copy_dimension_from_first_row(this.frm, cdt, cdn, "accounts"); } - }; cur_frm.script_manager.make(erpnext.accounts.JournalEntry); -cur_frm.cscript.update_totals = function(doc) { - var td=0.0; var tc =0.0; +cur_frm.cscript.update_totals = function (doc) { + var td = 0.0; + var tc = 0.0; var accounts = doc.accounts || []; - for(var i in accounts) { + for (var i in accounts) { td += flt(accounts[i].debit, precision("debit", accounts[i])); tc += flt(accounts[i].credit, precision("credit", accounts[i])); } var doc = locals[doc.doctype][doc.name]; doc.total_debit = td; doc.total_credit = tc; - doc.difference = flt((td - tc), precision("difference")); - refresh_many(['total_debit','total_credit','difference']); -} + doc.difference = flt(td - tc, precision("difference")); + refresh_many(["total_debit", "total_credit", "difference"]); +}; -cur_frm.cscript.get_balance = function(doc,dt,dn) { +cur_frm.cscript.get_balance = function (doc, dt, dn) { cur_frm.cscript.update_totals(doc); - cur_frm.call('get_balance', null, () => { cur_frm.refresh(); }); -} + cur_frm.call("get_balance", null, () => { + cur_frm.refresh(); + }); +}; -cur_frm.cscript.validate = function(doc,cdt,cdn) { +cur_frm.cscript.validate = function (doc, cdt, cdn) { cur_frm.cscript.update_totals(doc); -} +}; frappe.ui.form.on("Journal Entry Account", { - party: function(frm, cdt, cdn) { + party: function (frm, cdt, cdn) { var d = frappe.get_doc(cdt, cdn); - if(!d.account && d.party_type && d.party) { - if(!frm.doc.company) frappe.throw(__("Please select Company")); + if (!d.account && d.party_type && d.party) { + if (!frm.doc.company) frappe.throw(__("Please select Company")); return frm.call({ method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_party_account_and_currency", child: d, @@ -391,89 +423,97 @@ frappe.ui.form.on("Journal Entry Account", { company: frm.doc.company, party_type: d.party_type, party: d.party, - } + }, }); } }, - cost_center: function(frm, dt, dn) { + cost_center: function (frm, dt, dn) { erpnext.journal_entry.set_account_details(frm, dt, dn); }, - account: function(frm, dt, dn) { + account: function (frm, dt, dn) { erpnext.journal_entry.set_account_details(frm, dt, dn); }, - debit_in_account_currency: function(frm, cdt, cdn) { + debit_in_account_currency: function (frm, cdt, cdn) { erpnext.journal_entry.set_exchange_rate(frm, cdt, cdn); }, - credit_in_account_currency: function(frm, cdt, cdn) { + credit_in_account_currency: function (frm, cdt, cdn) { erpnext.journal_entry.set_exchange_rate(frm, cdt, cdn); }, - debit: function(frm, dt, dn) { + debit: function (frm, dt, dn) { cur_frm.cscript.update_totals(frm.doc); }, - credit: function(frm, dt, dn) { + credit: function (frm, dt, dn) { cur_frm.cscript.update_totals(frm.doc); }, - exchange_rate: function(frm, cdt, cdn) { + exchange_rate: function (frm, cdt, cdn) { var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency; var row = locals[cdt][cdn]; - if(row.account_currency == company_currency || !frm.doc.multi_currency) { + if (row.account_currency == company_currency || !frm.doc.multi_currency) { frappe.model.set_value(cdt, cdn, "exchange_rate", 1); } erpnext.journal_entry.set_debit_credit_in_company_currency(frm, cdt, cdn); - } -}) + }, +}); -frappe.ui.form.on("Journal Entry Account", "accounts_remove", function(frm) { +frappe.ui.form.on("Journal Entry Account", "accounts_remove", function (frm) { cur_frm.cscript.update_totals(frm.doc); }); $.extend(erpnext.journal_entry, { - toggle_fields_based_on_currency: function(frm) { + toggle_fields_based_on_currency: function (frm) { var fields = ["currency_section", "account_currency", "exchange_rate", "debit", "credit"]; var grid = frm.get_field("accounts").grid; - if(grid) grid.set_column_disp(fields, frm.doc.multi_currency); + if (grid) grid.set_column_disp(fields, frm.doc.multi_currency); // dynamic label var field_label_map = { - "debit_in_account_currency": "Debit", - "credit_in_account_currency": "Credit" + debit_in_account_currency: "Debit", + credit_in_account_currency: "Credit", }; $.each(field_label_map, function (fieldname, label) { frm.fields_dict.accounts.grid.update_docfield_property( fieldname, - 'label', - frm.doc.multi_currency ? (label + " in Account Currency") : label + "label", + frm.doc.multi_currency ? label + " in Account Currency" : label ); - }) + }); }, - set_debit_credit_in_company_currency: function(frm, cdt, cdn) { + set_debit_credit_in_company_currency: function (frm, cdt, cdn) { var row = locals[cdt][cdn]; - frappe.model.set_value(cdt, cdn, "debit", - flt(flt(row.debit_in_account_currency)*row.exchange_rate, precision("debit", row))); + frappe.model.set_value( + cdt, + cdn, + "debit", + flt(flt(row.debit_in_account_currency) * row.exchange_rate, precision("debit", row)) + ); - frappe.model.set_value(cdt, cdn, "credit", - flt(flt(row.credit_in_account_currency)*row.exchange_rate, precision("credit", row))); + frappe.model.set_value( + cdt, + cdn, + "credit", + flt(flt(row.credit_in_account_currency) * row.exchange_rate, precision("credit", row)) + ); cur_frm.cscript.update_totals(frm.doc); }, - set_exchange_rate: function(frm, cdt, cdn) { + set_exchange_rate: function (frm, cdt, cdn) { var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency; var row = locals[cdt][cdn]; - if(row.account_currency == company_currency || !frm.doc.multi_currency) { + if (row.account_currency == company_currency || !frm.doc.multi_currency) { row.exchange_rate = 1; erpnext.journal_entry.set_debit_credit_in_company_currency(frm, cdt, cdn); } else if (!row.exchange_rate || row.exchange_rate == 1 || row.account_type == "Bank") { @@ -488,50 +528,70 @@ $.extend(erpnext.journal_entry, { reference_name: cstr(row.reference_name), debit: flt(row.debit_in_account_currency), credit: flt(row.credit_in_account_currency), - exchange_rate: row.exchange_rate + exchange_rate: row.exchange_rate, }, - callback: function(r) { - if(r.message) { + callback: function (r) { + if (r.message) { row.exchange_rate = r.message; erpnext.journal_entry.set_debit_credit_in_company_currency(frm, cdt, cdn); } - } - }) + }, + }); } else { erpnext.journal_entry.set_debit_credit_in_company_currency(frm, cdt, cdn); } refresh_field("exchange_rate", cdn, "accounts"); }, - quick_entry: function(frm) { + quick_entry: function (frm) { var naming_series_options = frm.fields_dict.naming_series.df.options; - var naming_series_default = frm.fields_dict.naming_series.df.default || naming_series_options.split("\n")[0]; + var naming_series_default = + frm.fields_dict.naming_series.df.default || naming_series_options.split("\n")[0]; var dialog = new frappe.ui.Dialog({ title: __("Quick Journal Entry"), fields: [ - {fieldtype: "Currency", fieldname: "debit", label: __("Amount"), reqd: 1}, - {fieldtype: "Link", fieldname: "debit_account", label: __("Debit Account"), reqd: 1, + { fieldtype: "Currency", fieldname: "debit", label: __("Amount"), reqd: 1 }, + { + fieldtype: "Link", + fieldname: "debit_account", + label: __("Debit Account"), + reqd: 1, options: "Account", - get_query: function() { + get_query: function () { return erpnext.journal_entry.account_query(frm); - } + }, }, - {fieldtype: "Link", fieldname: "credit_account", label: __("Credit Account"), reqd: 1, + { + fieldtype: "Link", + fieldname: "credit_account", + label: __("Credit Account"), + reqd: 1, options: "Account", - get_query: function() { + get_query: function () { return erpnext.journal_entry.account_query(frm); - } + }, }, - {fieldtype: "Date", fieldname: "posting_date", label: __("Date"), reqd: 1, - default: frm.doc.posting_date}, - {fieldtype: "Small Text", fieldname: "user_remark", label: __("User Remark")}, - {fieldtype: "Select", fieldname: "naming_series", label: __("Series"), reqd: 1, - options: naming_series_options, default: naming_series_default}, - ] + { + fieldtype: "Date", + fieldname: "posting_date", + label: __("Date"), + reqd: 1, + default: frm.doc.posting_date, + }, + { fieldtype: "Small Text", fieldname: "user_remark", label: __("User Remark") }, + { + fieldtype: "Select", + fieldname: "naming_series", + label: __("Series"), + reqd: 1, + options: naming_series_options, + default: naming_series_default, + }, + ], }); - dialog.set_primary_action(__("Save"), function() { + dialog.set_primary_action(__("Save"), function () { var btn = this; var values = dialog.get_values(); @@ -548,11 +608,21 @@ $.extend(erpnext.journal_entry, { var debit_row = frm.fields_dict.accounts.grid.add_new_row(); frappe.model.set_value(debit_row.doctype, debit_row.name, "account", values.debit_account); - frappe.model.set_value(debit_row.doctype, debit_row.name, "debit_in_account_currency", values.debit); + frappe.model.set_value( + debit_row.doctype, + debit_row.name, + "debit_in_account_currency", + values.debit + ); var credit_row = frm.fields_dict.accounts.grid.add_new_row(); frappe.model.set_value(credit_row.doctype, credit_row.name, "account", values.credit_account); - frappe.model.set_value(credit_row.doctype, credit_row.name, "credit_in_account_currency", values.debit); + frappe.model.set_value( + credit_row.doctype, + credit_row.name, + "credit_in_account_currency", + values.debit + ); frm.save(); @@ -562,33 +632,33 @@ $.extend(erpnext.journal_entry, { dialog.show(); }, - account_query: function(frm) { + account_query: function (frm) { var filters = { company: frm.doc.company, - is_group: 0 + is_group: 0, }; - if(!frm.doc.multi_currency) { + if (!frm.doc.multi_currency) { $.extend(filters, { - account_currency: frappe.get_doc(":Company", frm.doc.company).default_currency + account_currency: frappe.get_doc(":Company", frm.doc.company).default_currency, }); } return { filters: filters }; }, - reverse_journal_entry: function() { + reverse_journal_entry: function () { frappe.model.open_mapped_doc({ method: "erpnext.accounts.doctype.journal_entry.journal_entry.make_reverse_journal_entry", - frm: cur_frm - }) + frm: cur_frm, + }); }, }); $.extend(erpnext.journal_entry, { - set_account_details: function(frm, dt, dn) { + set_account_details: function (frm, dt, dn) { var d = locals[dt][dn]; - if(d.account) { - if(!frm.doc.company) frappe.throw(__("Please select Company first")); - if(!frm.doc.posting_date) frappe.throw(__("Please select Posting Date first")); + if (d.account) { + if (!frm.doc.company) frappe.throw(__("Please select Company first")); + if (!frm.doc.posting_date) frappe.throw(__("Please select Posting Date first")); return frappe.call({ method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_account_details_and_party_type", @@ -600,13 +670,13 @@ $.extend(erpnext.journal_entry, { credit: flt(d.credit_in_account_currency), exchange_rate: d.exchange_rate, }, - callback: function(r) { - if(r.message) { + callback: function (r) { + if (r.message) { $.extend(d, r.message); erpnext.journal_entry.set_debit_credit_in_company_currency(frm, dt, dn); - refresh_field('accounts'); + refresh_field("accounts"); } - } + }, }); } }, diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry_list.js b/erpnext/accounts/doctype/journal_entry/journal_entry_list.js index 48d6115e3df..9d6e87392e5 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry_list.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry_list.js @@ -1,12 +1,12 @@ -frappe.listview_settings['Journal Entry'] = { +frappe.listview_settings["Journal Entry"] = { add_fields: ["voucher_type", "posting_date", "total_debit", "company", "user_remark"], - get_indicator: function(doc) { - if(doc.docstatus==0) { - return [__("Draft", "red", "docstatus,=,0")] - } else if(doc.docstatus==2) { - return [__("Cancelled", "grey", "docstatus,=,2")] + get_indicator: function (doc) { + if (doc.docstatus == 0) { + return [__("Draft", "red", "docstatus,=,0")]; + } else if (doc.docstatus == 2) { + return [__("Cancelled", "grey", "docstatus,=,2")]; } else { - return [__(doc.voucher_type), "blue", "voucher_type,=," + doc.voucher_type] + return [__(doc.voucher_type), "blue", "voucher_type,=," + doc.voucher_type]; } - } + }, }; diff --git a/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.js b/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.js index 5ebdf61db2c..9a526af4eb4 100644 --- a/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.js +++ b/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.js @@ -2,78 +2,82 @@ // For license information, please see license.txt frappe.ui.form.on("Journal Entry Template", { - onload: function(frm) { - if(frm.is_new()) { + onload: function (frm) { + if (frm.is_new()) { frappe.call({ type: "GET", method: "erpnext.accounts.doctype.journal_entry_template.journal_entry_template.get_naming_series", - callback: function(r){ - if(r.message) { + callback: function (r) { + if (r.message) { frm.set_df_property("naming_series", "options", r.message.split("\n")); frm.set_value("naming_series", r.message.split("\n")[0]); frm.refresh_field("naming_series"); } - } + }, }); } }, - refresh: function(frm) { + refresh: function (frm) { frappe.model.set_default_values(frm.doc); - frm.set_query("account" ,"accounts", function(){ + frm.set_query("account", "accounts", function () { var filters = { company: frm.doc.company, - is_group: 0 + is_group: 0, }; - if(!frm.doc.multi_currency) { + if (!frm.doc.multi_currency) { $.extend(filters, { - account_currency: frappe.get_doc(":Company", frm.doc.company).default_currency + account_currency: frappe.get_doc(":Company", frm.doc.company).default_currency, }); } return { filters: filters }; }); }, - voucher_type: function(frm) { - var add_accounts = function(doc, r) { - $.each(r, function(i, d) { + voucher_type: function (frm) { + var add_accounts = function (doc, r) { + $.each(r, function (i, d) { var row = frappe.model.add_child(doc, "Journal Entry Template Account", "accounts"); row.account = d.account; }); refresh_field("accounts"); }; - if(!frm.doc.company) return; + if (!frm.doc.company) return; frm.trigger("clear_child"); - switch(frm.doc.voucher_type){ + switch (frm.doc.voucher_type) { case "Bank Entry": case "Cash Entry": frappe.call({ type: "GET", method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_default_bank_cash_account", args: { - "account_type": (frm.doc.voucher_type=="Bank Entry" ? - "Bank" : (frm.doc.voucher_type=="Cash Entry" ? "Cash" : null)), - "company": frm.doc.company + account_type: + frm.doc.voucher_type == "Bank Entry" + ? "Bank" + : frm.doc.voucher_type == "Cash Entry" + ? "Cash" + : null, + company: frm.doc.company, }, - callback: function(r) { - if(r.message) { + callback: function (r) { + if (r.message) { // If default company bank account not set - if(!$.isEmptyObject(r.message)){ + if (!$.isEmptyObject(r.message)) { add_accounts(frm.doc, [r.message]); } } - } + }, }); break; default: frm.trigger("clear_child"); } }, - clear_child: function(frm){ + clear_child: function (frm) { frappe.model.clear_table(frm.doc, "accounts"); frm.refresh_field("accounts"); - } + }, }); diff --git a/erpnext/accounts/doctype/ledger_merge/ledger_merge.js b/erpnext/accounts/doctype/ledger_merge/ledger_merge.js index b2db98dbd03..6faae84447b 100644 --- a/erpnext/accounts/doctype/ledger_merge/ledger_merge.js +++ b/erpnext/accounts/doctype/ledger_merge/ledger_merge.js @@ -1,9 +1,9 @@ // Copyright (c) 2021, Wahni Green Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Ledger Merge', { - setup: function(frm) { - frappe.realtime.on('ledger_merge_refresh', ({ ledger_merge }) => { +frappe.ui.form.on("Ledger Merge", { + setup: function (frm) { + frappe.realtime.on("ledger_merge_refresh", ({ ledger_merge }) => { if (ledger_merge !== frm.doc.name) return; frappe.model.clear_doc(frm.doc.doctype, frm.doc.name); frappe.model.with_doc(frm.doc.doctype, frm.doc.name).then(() => { @@ -11,29 +11,29 @@ frappe.ui.form.on('Ledger Merge', { }); }); - frappe.realtime.on('ledger_merge_progress', data => { + frappe.realtime.on("ledger_merge_progress", (data) => { if (data.ledger_merge !== frm.doc.name) return; - let message = __('Merging {0} of {1}', [data.current, data.total]); + let message = __("Merging {0} of {1}", [data.current, data.total]); let percent = Math.floor((data.current * 100) / data.total); - frm.dashboard.show_progress(__('Merge Progress'), percent, message); - frm.page.set_indicator(__('In Progress'), 'orange'); + frm.dashboard.show_progress(__("Merge Progress"), percent, message); + frm.page.set_indicator(__("In Progress"), "orange"); }); - frm.set_query("account", function(doc) { - if (!doc.company) frappe.throw(__('Please set Company')); - if (!doc.root_type) frappe.throw(__('Please set Root Type')); + frm.set_query("account", function (doc) { + if (!doc.company) frappe.throw(__("Please set Company")); + if (!doc.root_type) frappe.throw(__("Please set Root Type")); return { filters: { root_type: doc.root_type, - company: doc.company - } + company: doc.company, + }, }; }); - frm.set_query('account', 'merge_accounts', function(doc) { - if (!doc.company) frappe.throw(__('Please set Company')); - if (!doc.root_type) frappe.throw(__('Please set Root Type')); - if (!doc.account) frappe.throw(__('Please set Account')); + frm.set_query("account", "merge_accounts", function (doc) { + if (!doc.company) frappe.throw(__("Please set Company")); + if (!doc.root_type) frappe.throw(__("Please set Root Type")); + if (!doc.account) frappe.throw(__("Please set Account")); let acc = [doc.account]; frm.doc.merge_accounts.forEach((row) => { acc.push(row.account); @@ -43,86 +43,86 @@ frappe.ui.form.on('Ledger Merge', { is_group: doc.is_group, root_type: doc.root_type, name: ["not in", acc], - company: doc.company - } + company: doc.company, + }, }; }); }, - refresh: function(frm) { + refresh: function (frm) { frm.page.hide_icon_group(); - frm.trigger('set_merge_status'); - frm.trigger('update_primary_action'); + frm.trigger("set_merge_status"); + frm.trigger("update_primary_action"); }, - after_save: function(frm) { + after_save: function (frm) { setTimeout(() => { - frm.trigger('update_primary_action'); + frm.trigger("update_primary_action"); }, 500); }, - update_primary_action: function(frm) { + update_primary_action: function (frm) { if (frm.is_dirty()) { frm.enable_save(); return; } frm.disable_save(); - if (frm.doc.status !== 'Success') { + if (frm.doc.status !== "Success") { if (!frm.is_new()) { - let label = frm.doc.status === 'Pending' ? __('Start Merge') : __('Retry'); + let label = frm.doc.status === "Pending" ? __("Start Merge") : __("Retry"); frm.page.set_primary_action(label, () => frm.events.start_merge(frm)); } else { - frm.page.set_primary_action(__('Save'), () => frm.save()); + frm.page.set_primary_action(__("Save"), () => frm.save()); } } }, - start_merge: function(frm) { + start_merge: function (frm) { frm.call({ - method: 'form_start_merge', + method: "form_start_merge", args: { docname: frm.doc.name }, - btn: frm.page.btn_primary - }).then(r => { + btn: frm.page.btn_primary, + }).then((r) => { if (r.message === true) { frm.disable_save(); } }); }, - set_merge_status: function(frm) { + set_merge_status: function (frm) { if (frm.doc.status == "Pending") return; let successful_records = 0; frm.doc.merge_accounts.forEach((row) => { if (row.merged) successful_records += 1; }); let message_args = [successful_records, frm.doc.merge_accounts.length]; - frm.dashboard.set_headline(__('Successfully merged {0} out of {1}.', message_args)); + frm.dashboard.set_headline(__("Successfully merged {0} out of {1}.", message_args)); }, - root_type: function(frm) { - frm.set_value('account', ''); - frm.set_value('merge_accounts', []); + root_type: function (frm) { + frm.set_value("account", ""); + frm.set_value("merge_accounts", []); }, - company: function(frm) { - frm.set_value('account', ''); - frm.set_value('merge_accounts', []); - } + company: function (frm) { + frm.set_value("account", ""); + frm.set_value("merge_accounts", []); + }, }); -frappe.ui.form.on('Ledger Merge Accounts', { - merge_accounts_add: function(frm) { - frm.trigger('update_primary_action'); +frappe.ui.form.on("Ledger Merge Accounts", { + merge_accounts_add: function (frm) { + frm.trigger("update_primary_action"); }, - merge_accounts_remove: function(frm) { - frm.trigger('update_primary_action'); + merge_accounts_remove: function (frm) { + frm.trigger("update_primary_action"); }, - account: function(frm, cdt, cdn) { + account: function (frm, cdt, cdn) { let row = frappe.get_doc(cdt, cdn); row.account_name = row.account; - frm.refresh_field('merge_accounts'); - frm.trigger('update_primary_action'); - } + frm.refresh_field("merge_accounts"); + frm.trigger("update_primary_action"); + }, }); diff --git a/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.js b/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.js index d7dc7f3f3c7..d522f6d8af1 100644 --- a/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.js +++ b/erpnext/accounts/doctype/loyalty_point_entry/loyalty_point_entry.js @@ -1,8 +1,6 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Loyalty Point Entry', { - refresh: function(frm) { - - } +frappe.ui.form.on("Loyalty Point Entry", { + refresh: function (frm) {}, }); diff --git a/erpnext/accounts/doctype/loyalty_program/loyalty_program.js b/erpnext/accounts/doctype/loyalty_program/loyalty_program.js index 6951b2a2b32..4c29be3b556 100644 --- a/erpnext/accounts/doctype/loyalty_program/loyalty_program.js +++ b/erpnext/accounts/doctype/loyalty_program/loyalty_program.js @@ -3,30 +3,37 @@ frappe.provide("erpnext.accounts.dimensions"); -frappe.ui.form.on('Loyalty Program', { - setup: function(frm) { - var help_content = - ` +frappe.ui.form.on("Loyalty Program", { + setup: function (frm) { + var help_content = `
    + let table_footer = + hidden_logs && hidden_logs.length > 0 + ? ` - `: ""; + ` + : ""; if (field === "fixed_error_log_preview") { rows_head = ` - ` - table_caption = "Resolved Issues" + `; + table_caption = "Resolved Issues"; } else { rows_head = ` - ` - table_caption = "Error Log" + `; + table_caption = "Error Log"; } frm.get_field(field).$wrapper.html(` @@ -144,7 +152,7 @@ frappe.ui.form.on("Tally Migration", { summary[row.doc.doctype] = 1; } } - return summary + return summary; }, {}); console.table(summary); }, @@ -177,7 +185,7 @@ frappe.ui.form.on("Tally Migration", { let hidden_logs = completed_log.slice(20); frm.events.render_html_table(frm, logs, hidden_logs, "fixed_error_log_preview"); - } + }, }); erpnext.tally_migration.getError = (traceback) => { @@ -186,31 +194,33 @@ erpnext.tally_migration.getError = (traceback) => { let message; if (is_multiline) { - let exc_error_idx = traceback.trim().lastIndexOf("\n") + 1 - let error_line = traceback.substr(exc_error_idx) - let split_str_idx = (error_line.indexOf(':') > 0) ? error_line.indexOf(':') + 1 : 0; + let exc_error_idx = traceback.trim().lastIndexOf("\n") + 1; + let error_line = traceback.substr(exc_error_idx); + let split_str_idx = error_line.indexOf(":") > 0 ? error_line.indexOf(":") + 1 : 0; message = error_line.slice(split_str_idx).trim(); } else { message = traceback; } - return message -} + return message; +}; erpnext.tally_migration.cleanDoc = (obj) => { /* Strips all null and empty values of your JSON object */ let temp = obj; - $.each(temp, function(key, value){ - if (value === "" || value === null){ + $.each(temp, function (key, value) { + if (value === "" || value === null) { delete obj[key]; - } else if (Object.prototype.toString.call(value) === '[object Object]') { + } else if (Object.prototype.toString.call(value) === "[object Object]") { erpnext.tally_migration.cleanDoc(value); } else if ($.isArray(value)) { - $.each(value, function (k,v) { erpnext.tally_migration.cleanDoc(v); }); + $.each(value, function (k, v) { + erpnext.tally_migration.cleanDoc(v); + }); } }); return temp; -} +}; erpnext.tally_migration.unresolve = (document) => { /* Mark document migration as unresolved ie. move to failed error log */ @@ -218,9 +228,9 @@ erpnext.tally_migration.unresolve = (document) => { let failed_log = erpnext.tally_migration.failed_import_log; let fixed_log = erpnext.tally_migration.fixed_errors_log; - let modified_fixed_log = fixed_log.filter(row => { + let modified_fixed_log = fixed_log.filter((row) => { if (!frappe.utils.deep_equal(erpnext.tally_migration.cleanDoc(row.doc), document)) { - return row + return row; } }); @@ -231,7 +241,7 @@ erpnext.tally_migration.unresolve = (document) => { frm.dirty(); frm.save(); -} +}; erpnext.tally_migration.resolve = (document) => { /* Mark document migration as resolved ie. move to fixed error log */ @@ -239,9 +249,9 @@ erpnext.tally_migration.resolve = (document) => { let failed_log = erpnext.tally_migration.failed_import_log; let fixed_log = erpnext.tally_migration.fixed_errors_log; - let modified_failed_log = failed_log.filter(row => { + let modified_failed_log = failed_log.filter((row) => { if (!frappe.utils.deep_equal(erpnext.tally_migration.cleanDoc(row.doc), document)) { - return row + return row; } }); fixed_log.push({ doc: document, exc: `Solved on ${Date()}` }); @@ -251,27 +261,27 @@ erpnext.tally_migration.resolve = (document) => { frm.dirty(); frm.save(); -} +}; erpnext.tally_migration.create_new_doc = (document) => { /* Mark as resolved and create new document */ erpnext.tally_migration.resolve(document); return frappe.call({ type: "POST", - method: 'erpnext.erpnext_integrations.doctype.tally_migration.tally_migration.new_doc', + method: "erpnext.erpnext_integrations.doctype.tally_migration.tally_migration.new_doc", args: { - document + document, }, freeze: true, - callback: function(r) { - if(!r.exc) { + callback: function (r) { + if (!r.exc) { frappe.model.sync(r.message); frappe.get_doc(r.message.doctype, r.message.name).__run_link_triggers = true; frappe.set_route("Form", r.message.doctype, r.message.name); } - } + }, }); -} +}; erpnext.tally_migration.get_html_rows = (logs, field) => { let index = 0; @@ -304,14 +314,18 @@ erpnext.tally_migration.get_html_rows = (logs, field) => { `; let create_button = ` - ` + `; let mark_as_unresolved = ` - ` + `; if (field === "fixed_error_log_preview") { return ` @@ -343,7 +357,8 @@ erpnext.tally_migration.get_html_rows = (logs, field) => { `; } - }).join(""); + }) + .join(""); - return rows -} + return rows; +}; diff --git a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.js b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.js index 2925db82e33..eb944d9f279 100644 --- a/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.js +++ b/erpnext/erpnext_integrations/doctype/taxjar_settings/taxjar_settings.js @@ -1,37 +1,35 @@ // Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('TaxJar Settings', { +frappe.ui.form.on("TaxJar Settings", { is_sandbox: (frm) => { frm.toggle_reqd("api_key", !frm.doc.is_sandbox); frm.toggle_reqd("sandbox_api_key", frm.doc.is_sandbox); }, on_load: (frm) => { - frm.set_query('shipping_account_head', function() { + frm.set_query("shipping_account_head", function () { return { filters: { - 'company': frm.doc.company - } + company: frm.doc.company, + }, }; }); - frm.set_query('tax_account_head', function() { + frm.set_query("tax_account_head", function () { return { filters: { - 'company': frm.doc.company - } + company: frm.doc.company, + }, }; }); }, refresh: (frm) => { - frm.add_custom_button(__('Update Nexus List'), function() { + frm.add_custom_button(__("Update Nexus List"), function () { frm.call({ doc: frm.doc, - method: 'update_nexus_list' + method: "update_nexus_list", }); }); }, - - }); diff --git a/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.js b/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.js index d7a3d36a5f1..dc3941b3073 100644 --- a/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.js +++ b/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.js @@ -1,47 +1,47 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Woocommerce Settings', { - refresh (frm) { +frappe.ui.form.on("Woocommerce Settings", { + refresh(frm) { frm.trigger("add_button_generate_secret"); frm.trigger("check_enabled"); - frm.set_query("tax_account", ()=>{ + frm.set_query("tax_account", () => { return { - "filters": { - "company": frappe.defaults.get_default("company"), - "is_group": 0 - } + filters: { + company: frappe.defaults.get_default("company"), + is_group: 0, + }, }; }); }, - enable_sync (frm) { + enable_sync(frm) { frm.trigger("check_enabled"); }, add_button_generate_secret(frm) { - frm.add_custom_button(__('Generate Secret'), () => { - frappe.confirm( - __("Apps using current key won't be able to access, are you sure?"), - () => { - frappe.call({ - type:"POST", - method:"erpnext.erpnext_integrations.doctype.woocommerce_settings.woocommerce_settings.generate_secret", - }).done(() => { + frm.add_custom_button(__("Generate Secret"), () => { + frappe.confirm(__("Apps using current key won't be able to access, are you sure?"), () => { + frappe + .call({ + type: "POST", + method: "erpnext.erpnext_integrations.doctype.woocommerce_settings.woocommerce_settings.generate_secret", + }) + .done(() => { frm.reload_doc(); - }).fail(() => { + }) + .fail(() => { frappe.msgprint(__("Could not generate Secret")); }); - } - ); + }); }); }, - check_enabled (frm) { + check_enabled(frm) { frm.set_df_property("woocommerce_server_url", "reqd", frm.doc.enable_sync); frm.set_df_property("api_consumer_key", "reqd", frm.doc.enable_sync); frm.set_df_property("api_consumer_secret", "reqd", frm.doc.enable_sync); - } + }, }); frappe.ui.form.on("Woocommerce Settings", "onload", function () { @@ -51,6 +51,6 @@ frappe.ui.form.on("Woocommerce Settings", "onload", function () { $.each(r.message, function (key, value) { set_field_options(key, value); }); - } + }, }); }); diff --git a/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.js b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.js index 58179416b1a..1ea33645631 100644 --- a/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.js +++ b/erpnext/loan_management/dashboard_chart_source/top_10_pledged_loan_securities/top_10_pledged_loan_securities.js @@ -1,4 +1,4 @@ -frappe.provide('frappe.dashboards.chart_sources'); +frappe.provide("frappe.dashboards.chart_sources"); frappe.dashboards.chart_sources["Top 10 Pledged Loan Securities"] = { method: "erpnext.loan_management.dashboard_chart_source.top_10_pledged_loan_securities.top_10_pledged_loan_securities.get_data", @@ -8,7 +8,7 @@ frappe.dashboards.chart_sources["Top 10 Pledged Loan Securities"] = { label: __("Company"), fieldtype: "Link", options: "Company", - default: frappe.defaults.get_user_default("Company") - } - ] + default: frappe.defaults.get_user_default("Company"), + }, + ], }; diff --git a/erpnext/loan_management/doctype/loan/loan_list.js b/erpnext/loan_management/doctype/loan/loan_list.js index 6591b729968..ac011627377 100644 --- a/erpnext/loan_management/doctype/loan/loan_list.js +++ b/erpnext/loan_management/doctype/loan/loan_list.js @@ -1,16 +1,16 @@ // Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.listview_settings['Loan'] = { - get_indicator: function(doc) { +frappe.listview_settings["Loan"] = { + get_indicator: function (doc) { var status_color = { - "Draft": "red", - "Sanctioned": "blue", - "Disbursed": "orange", + Draft: "red", + Sanctioned: "blue", + Disbursed: "orange", "Partially Disbursed": "yellow", "Loan Closure Requested": "green", - "Closed": "green" + Closed: "green", }; - return [__(doc.status), status_color[doc.status], "status,=,"+doc.status]; + return [__(doc.status), status_color[doc.status], "status,=," + doc.status]; }, }; diff --git a/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.js b/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.js index 8aec63ad75f..3042c6c7aee 100644 --- a/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.js +++ b/erpnext/loan_management/doctype/loan_balance_adjustment/loan_balance_adjustment.js @@ -1,8 +1,7 @@ // Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Loan Balance Adjustment', { +frappe.ui.form.on("Loan Balance Adjustment", { // refresh: function(frm) { - // } }); diff --git a/erpnext/loan_management/doctype/loan_refund/loan_refund.js b/erpnext/loan_management/doctype/loan_refund/loan_refund.js index f108bf7a281..c507aa5abaa 100644 --- a/erpnext/loan_management/doctype/loan_refund/loan_refund.js +++ b/erpnext/loan_management/doctype/loan_refund/loan_refund.js @@ -1,8 +1,7 @@ // Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Loan Refund', { +frappe.ui.form.on("Loan Refund", { // refresh: function(frm) { - // } }); diff --git a/erpnext/loan_management/doctype/loan_security/loan_security.js b/erpnext/loan_management/doctype/loan_security/loan_security.js index 0e815af76a0..9db3a33bd2d 100644 --- a/erpnext/loan_management/doctype/loan_security/loan_security.js +++ b/erpnext/loan_management/doctype/loan_security/loan_security.js @@ -1,8 +1,7 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Loan Security', { +frappe.ui.form.on("Loan Security", { // refresh: function(frm) { - // } }); diff --git a/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.js b/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.js index 48ca392edf7..be908997487 100644 --- a/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.js +++ b/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge.js @@ -1,43 +1,48 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Loan Security Pledge', { - calculate_amounts: function(frm, cdt, cdn) { +frappe.ui.form.on("Loan Security Pledge", { + calculate_amounts: function (frm, cdt, cdn) { let row = locals[cdt][cdn]; - frappe.model.set_value(cdt, cdn, 'amount', row.qty * row.loan_security_price); - frappe.model.set_value(cdt, cdn, 'post_haircut_amount', cint(row.amount - (row.amount * row.haircut/100))); + frappe.model.set_value(cdt, cdn, "amount", row.qty * row.loan_security_price); + frappe.model.set_value( + cdt, + cdn, + "post_haircut_amount", + cint(row.amount - (row.amount * row.haircut) / 100) + ); let amount = 0; let maximum_amount = 0; - $.each(frm.doc.securities || [], function(i, item){ + $.each(frm.doc.securities || [], function (i, item) { amount += item.amount; maximum_amount += item.post_haircut_amount; }); - frm.set_value('total_security_value', amount); - frm.set_value('maximum_loan_value', maximum_amount); - } + frm.set_value("total_security_value", amount); + frm.set_value("maximum_loan_value", maximum_amount); + }, }); frappe.ui.form.on("Pledge", { - loan_security: function(frm, cdt, cdn) { + loan_security: function (frm, cdt, cdn) { let row = locals[cdt][cdn]; if (row.loan_security) { frappe.call({ method: "erpnext.loan_management.doctype.loan_security_price.loan_security_price.get_loan_security_price", args: { - loan_security: row.loan_security + loan_security: row.loan_security, }, - callback: function(r) { - frappe.model.set_value(cdt, cdn, 'loan_security_price', r.message); + callback: function (r) { + frappe.model.set_value(cdt, cdn, "loan_security_price", r.message); frm.events.calculate_amounts(frm, cdt, cdn); - } + }, }); } }, - qty: function(frm, cdt, cdn) { + qty: function (frm, cdt, cdn) { frm.events.calculate_amounts(frm, cdt, cdn); }, }); diff --git a/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge_list.js b/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge_list.js index 174d1b0d62b..c8f36ec5eee 100644 --- a/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge_list.js +++ b/erpnext/loan_management/doctype/loan_security_pledge/loan_security_pledge_list.js @@ -2,14 +2,14 @@ // License: GNU General Public License v3. See license.txt // render -frappe.listview_settings['Loan Security Pledge'] = { +frappe.listview_settings["Loan Security Pledge"] = { add_fields: ["status"], - get_indicator: function(doc) { + get_indicator: function (doc) { var status_color = { - "Unpledged": "orange", - "Pledged": "green", - "Partially Pledged": "green" + Unpledged: "orange", + Pledged: "green", + "Partially Pledged": "green", }; - return [__(doc.status), status_color[doc.status], "status,=,"+doc.status]; - } + return [__(doc.status), status_color[doc.status], "status,=," + doc.status]; + }, }; diff --git a/erpnext/loan_management/doctype/loan_security_price/loan_security_price.js b/erpnext/loan_management/doctype/loan_security_price/loan_security_price.js index 31b4ec7249e..b79ccb51e37 100644 --- a/erpnext/loan_management/doctype/loan_security_price/loan_security_price.js +++ b/erpnext/loan_management/doctype/loan_security_price/loan_security_price.js @@ -1,8 +1,7 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Loan Security Price', { +frappe.ui.form.on("Loan Security Price", { // refresh: function(frm) { - // } }); diff --git a/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.js b/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.js index f26c138371e..504a5d9748a 100644 --- a/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.js +++ b/erpnext/loan_management/doctype/loan_security_shortfall/loan_security_shortfall.js @@ -1,25 +1,25 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Loan Security Shortfall', { - refresh: function(frm) { - frm.add_custom_button(__("Add Loan Security"), function() { - frm.trigger('shortfall_action'); +frappe.ui.form.on("Loan Security Shortfall", { + refresh: function (frm) { + frm.add_custom_button(__("Add Loan Security"), function () { + frm.trigger("shortfall_action"); }); }, - shortfall_action: function(frm) { + shortfall_action: function (frm) { frappe.call({ method: "erpnext.loan_management.doctype.loan_security_shortfall.loan_security_shortfall.add_security", args: { - 'loan': frm.doc.loan + loan: frm.doc.loan, }, - callback: function(r) { + callback: function (r) { if (r.message) { let doc = frappe.model.sync(r.message)[0]; frappe.set_route("Form", doc.doctype, doc.name); } - } + }, }); - } + }, }); diff --git a/erpnext/loan_management/doctype/loan_security_type/loan_security_type.js b/erpnext/loan_management/doctype/loan_security_type/loan_security_type.js index 3a1e0689c1d..5e277d3d6ba 100644 --- a/erpnext/loan_management/doctype/loan_security_type/loan_security_type.js +++ b/erpnext/loan_management/doctype/loan_security_type/loan_security_type.js @@ -1,8 +1,7 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Loan Security Type', { +frappe.ui.form.on("Loan Security Type", { // refresh: function(frm) { - // }, }); diff --git a/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.js b/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.js index 82232062774..b6803e00de9 100644 --- a/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.js +++ b/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge.js @@ -1,11 +1,10 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Loan Security Unpledge', { - refresh: function(frm) { - - if (frm.doc.docstatus == 1 && frm.doc.status == 'Approved') { - frm.set_df_property('status', 'read_only', 1); +frappe.ui.form.on("Loan Security Unpledge", { + refresh: function (frm) { + if (frm.doc.docstatus == 1 && frm.doc.status == "Approved") { + frm.set_df_property("status", "read_only", 1); } - } + }, }); diff --git a/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge_list.js b/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge_list.js index 196ebbb96ad..52624fa1436 100644 --- a/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge_list.js +++ b/erpnext/loan_management/doctype/loan_security_unpledge/loan_security_unpledge_list.js @@ -2,13 +2,13 @@ // License: GNU General Public License v3. See license.txt // render -frappe.listview_settings['Loan Security Unpledge'] = { +frappe.listview_settings["Loan Security Unpledge"] = { add_fields: ["status"], - get_indicator: function(doc) { + get_indicator: function (doc) { var status_color = { - "Requested": "orange", - "Approved": "green", + Requested: "orange", + Approved: "green", }; - return [__(doc.status), status_color[doc.status], "status,=,"+doc.status]; - } + return [__(doc.status), status_color[doc.status], "status,=," + doc.status]; + }, }; diff --git a/erpnext/loan_management/doctype/loan_type/loan_type.js b/erpnext/loan_management/doctype/loan_type/loan_type.js index 9f9137cfbcd..bb0bd8106f9 100644 --- a/erpnext/loan_management/doctype/loan_type/loan_type.js +++ b/erpnext/loan_management/doctype/loan_type/loan_type.js @@ -1,16 +1,16 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Loan Type', { - onload: function(frm) { +frappe.ui.form.on("Loan Type", { + onload: function (frm) { $.each(["penalty_income_account", "interest_income_account"], function (i, field) { frm.set_query(field, function () { return { - "filters": { - "company": frm.doc.company, - "root_type": "Income", - "is_group": 0 - } + filters: { + company: frm.doc.company, + root_type: "Income", + is_group: 0, + }, }; }); }); @@ -18,13 +18,13 @@ frappe.ui.form.on('Loan Type', { $.each(["payment_account", "loan_account", "disbursement_account"], function (i, field) { frm.set_query(field, function () { return { - "filters": { - "company": frm.doc.company, - "root_type": "Asset", - "is_group": 0 - } + filters: { + company: frm.doc.company, + root_type: "Asset", + is_group: 0, + }, }; }); }); - } + }, }); diff --git a/erpnext/loan_management/doctype/pledge/pledge.js b/erpnext/loan_management/doctype/pledge/pledge.js index fb6ab107782..faee76dbd3a 100644 --- a/erpnext/loan_management/doctype/pledge/pledge.js +++ b/erpnext/loan_management/doctype/pledge/pledge.js @@ -1,8 +1,7 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Pledge', { +frappe.ui.form.on("Pledge", { // refresh: function(frm) { - // } }); diff --git a/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.js b/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.js index c596be2d2a8..45cc07344d8 100644 --- a/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.js +++ b/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.js @@ -1,8 +1,7 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Process Loan Interest Accrual', { +frappe.ui.form.on("Process Loan Interest Accrual", { // refresh: function(frm) { - // } }); diff --git a/erpnext/loan_management/doctype/process_loan_security_shortfall/process_loan_security_shortfall.js b/erpnext/loan_management/doctype/process_loan_security_shortfall/process_loan_security_shortfall.js index 645e3ada9a8..5ac98af15e3 100644 --- a/erpnext/loan_management/doctype/process_loan_security_shortfall/process_loan_security_shortfall.js +++ b/erpnext/loan_management/doctype/process_loan_security_shortfall/process_loan_security_shortfall.js @@ -1,8 +1,8 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Process Loan Security Shortfall', { - onload: function(frm) { - frm.set_value('update_time', frappe.datetime.now_datetime()); - } +frappe.ui.form.on("Process Loan Security Shortfall", { + onload: function (frm) { + frm.set_value("update_time", frappe.datetime.now_datetime()); + }, }); diff --git a/erpnext/loan_management/doctype/sanctioned_loan_amount/sanctioned_loan_amount.js b/erpnext/loan_management/doctype/sanctioned_loan_amount/sanctioned_loan_amount.js index 5361e7ca2a5..af647d974bf 100644 --- a/erpnext/loan_management/doctype/sanctioned_loan_amount/sanctioned_loan_amount.js +++ b/erpnext/loan_management/doctype/sanctioned_loan_amount/sanctioned_loan_amount.js @@ -1,8 +1,7 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Sanctioned Loan Amount', { +frappe.ui.form.on("Sanctioned Loan Amount", { // refresh: function(frm) { - // } }); diff --git a/erpnext/loan_management/loan_common.js b/erpnext/loan_management/loan_common.js index 247e30b8439..72f40c5700f 100644 --- a/erpnext/loan_management/loan_common.js +++ b/erpnext/loan_management/loan_common.js @@ -2,37 +2,42 @@ // For license information, please see license.txt frappe.ui.form.on(cur_frm.doctype, { - refresh: function(frm) { - if (['Loan Disbursement', 'Loan Repayment', 'Loan Interest Accrual', 'Loan Write Off'].includes(frm.doc.doctype) - && frm.doc.docstatus > 0) { + refresh: function (frm) { + if ( + ["Loan Disbursement", "Loan Repayment", "Loan Interest Accrual", "Loan Write Off"].includes( + frm.doc.doctype + ) && + frm.doc.docstatus > 0 + ) { + frm.add_custom_button( + __("Accounting Ledger"), + function () { + frappe.route_options = { + voucher_no: frm.doc.name, + company: frm.doc.company, + from_date: moment(frm.doc.posting_date).format("YYYY-MM-DD"), + to_date: moment(frm.doc.modified).format("YYYY-MM-DD"), + show_cancelled_entries: frm.doc.docstatus === 2, + }; - frm.add_custom_button(__("Accounting Ledger"), function() { - frappe.route_options = { - voucher_no: frm.doc.name, - company: frm.doc.company, - from_date: moment(frm.doc.posting_date).format('YYYY-MM-DD'), - to_date: moment(frm.doc.modified).format('YYYY-MM-DD'), - show_cancelled_entries: frm.doc.docstatus === 2 - }; - - frappe.set_route("query-report", "General Ledger"); - },__("View")); + frappe.set_route("query-report", "General Ledger"); + }, + __("View") + ); } }, - applicant: function(frm) { + applicant: function (frm) { if (!["Loan Application", "Loan"].includes(frm.doc.doctype)) { return; } if (frm.doc.applicant) { - frappe.model.with_doc(frm.doc.applicant_type, frm.doc.applicant, function() { + frappe.model.with_doc(frm.doc.applicant_type, frm.doc.applicant, function () { var applicant = frappe.model.get_doc(frm.doc.applicant_type, frm.doc.applicant); - frm.set_value("applicant_name", - applicant.employee_name || applicant.member_name); + frm.set_value("applicant_name", applicant.employee_name || applicant.member_name); }); - } - else { + } else { frm.set_value("applicant_name", null); } - } + }, }); diff --git a/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.js b/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.js index 73d60c40458..c85fce0f380 100644 --- a/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.js +++ b/erpnext/loan_management/report/applicant_wise_loan_security_exposure/applicant_wise_loan_security_exposure.js @@ -3,14 +3,14 @@ /* eslint-disable */ frappe.query_reports["Applicant-Wise Loan Security Exposure"] = { - "filters": [ + filters: [ { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company"), - "reqd": 1 - } - ] + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, + }, + ], }; diff --git a/erpnext/loan_management/report/loan_interest_report/loan_interest_report.js b/erpnext/loan_management/report/loan_interest_report/loan_interest_report.js index 458c79a1ea8..53ff92fa157 100644 --- a/erpnext/loan_management/report/loan_interest_report/loan_interest_report.js +++ b/erpnext/loan_management/report/loan_interest_report/loan_interest_report.js @@ -3,48 +3,48 @@ /* eslint-disable */ frappe.query_reports["Loan Interest Report"] = { - "filters": [ + filters: [ { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company"), - "reqd": 1 + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, }, { - "fieldname":"applicant_type", - "label": __("Applicant Type"), - "fieldtype": "Select", - "options": ["Customer", "Employee"], - "reqd": 1, - "default": "Customer", - on_change: function() { - frappe.query_report.set_filter_value('applicant', ""); - } + fieldname: "applicant_type", + label: __("Applicant Type"), + fieldtype: "Select", + options: ["Customer", "Employee"], + reqd: 1, + default: "Customer", + on_change: function () { + frappe.query_report.set_filter_value("applicant", ""); + }, }, { - "fieldname": "applicant", - "label": __("Applicant"), - "fieldtype": "Dynamic Link", - "get_options": function() { - var applicant_type = frappe.query_report.get_filter_value('applicant_type'); - var applicant = frappe.query_report.get_filter_value('applicant'); - if(applicant && !applicant_type) { + fieldname: "applicant", + label: __("Applicant"), + fieldtype: "Dynamic Link", + get_options: function () { + var applicant_type = frappe.query_report.get_filter_value("applicant_type"); + var applicant = frappe.query_report.get_filter_value("applicant"); + if (applicant && !applicant_type) { frappe.throw(__("Please select Applicant Type first")); } return applicant_type; - } + }, }, { - "fieldname":"from_date", - "label": __("From Date"), - "fieldtype": "Date", + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", }, { - "fieldname":"to_date", - "label": __("From Date"), - "fieldtype": "Date", + fieldname: "to_date", + label: __("From Date"), + fieldtype: "Date", }, - ] + ], }; diff --git a/erpnext/loan_management/report/loan_repayment_and_closure/loan_repayment_and_closure.js b/erpnext/loan_management/report/loan_repayment_and_closure/loan_repayment_and_closure.js index ed5e937c996..0274715a319 100644 --- a/erpnext/loan_management/report/loan_repayment_and_closure/loan_repayment_and_closure.js +++ b/erpnext/loan_management/report/loan_repayment_and_closure/loan_repayment_and_closure.js @@ -3,39 +3,38 @@ /* eslint-disable */ frappe.query_reports["Loan Repayment and Closure"] = { - "filters": [ + filters: [ { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "reqd": 1, - "default": frappe.defaults.get_user_default("Company") + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + reqd: 1, + default: frappe.defaults.get_user_default("Company"), }, { - "fieldname":"applicant_type", - "label": __("Applicant Type"), - "fieldtype": "Select", - "options": ["Customer", "Employee"], - "reqd": 1, - "default": "Customer", - on_change: function() { - frappe.query_report.set_filter_value('applicant', ""); - } + fieldname: "applicant_type", + label: __("Applicant Type"), + fieldtype: "Select", + options: ["Customer", "Employee"], + reqd: 1, + default: "Customer", + on_change: function () { + frappe.query_report.set_filter_value("applicant", ""); + }, }, { - "fieldname": "applicant", - "label": __("Applicant"), - "fieldtype": "Dynamic Link", - "get_options": function() { - var applicant_type = frappe.query_report.get_filter_value('applicant_type'); - var applicant = frappe.query_report.get_filter_value('applicant'); - if(applicant && !applicant_type) { + fieldname: "applicant", + label: __("Applicant"), + fieldtype: "Dynamic Link", + get_options: function () { + var applicant_type = frappe.query_report.get_filter_value("applicant_type"); + var applicant = frappe.query_report.get_filter_value("applicant"); + if (applicant && !applicant_type) { frappe.throw(__("Please select Applicant Type first")); } return applicant_type; - } - + }, }, - ] + ], }; diff --git a/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.js b/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.js index 777f29624a7..5650d89f495 100644 --- a/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.js +++ b/erpnext/loan_management/report/loan_security_exposure/loan_security_exposure.js @@ -3,14 +3,14 @@ /* eslint-disable */ frappe.query_reports["Loan Security Exposure"] = { - "filters": [ + filters: [ { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company"), - "reqd": 1 - } - ] + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, + }, + ], }; diff --git a/erpnext/loan_management/report/loan_security_status/loan_security_status.js b/erpnext/loan_management/report/loan_security_status/loan_security_status.js index 6e6191c7e44..6e5195b72f3 100644 --- a/erpnext/loan_management/report/loan_security_status/loan_security_status.js +++ b/erpnext/loan_management/report/loan_security_status/loan_security_status.js @@ -3,44 +3,44 @@ /* eslint-disable */ frappe.query_reports["Loan Security Status"] = { - "filters": [ + filters: [ { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "reqd": 1, - "default": frappe.defaults.get_user_default("Company") + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + reqd: 1, + default: frappe.defaults.get_user_default("Company"), }, { - "fieldname":"applicant_type", - "label": __("Applicant Type"), - "fieldtype": "Select", - "options": ["Customer", "Employee"], - "reqd": 1, - "default": "Customer", - on_change: function() { - frappe.query_report.set_filter_value('applicant', ""); - } + fieldname: "applicant_type", + label: __("Applicant Type"), + fieldtype: "Select", + options: ["Customer", "Employee"], + reqd: 1, + default: "Customer", + on_change: function () { + frappe.query_report.set_filter_value("applicant", ""); + }, }, { - "fieldname": "applicant", - "label": __("Applicant"), - "fieldtype": "Dynamic Link", - "get_options": function() { - var applicant_type = frappe.query_report.get_filter_value('applicant_type'); - var applicant = frappe.query_report.get_filter_value('applicant'); - if(applicant && !applicant_type) { + fieldname: "applicant", + label: __("Applicant"), + fieldtype: "Dynamic Link", + get_options: function () { + var applicant_type = frappe.query_report.get_filter_value("applicant_type"); + var applicant = frappe.query_report.get_filter_value("applicant"); + if (applicant && !applicant_type) { frappe.throw(__("Please select Applicant Type first")); } return applicant_type; - } + }, }, { - "fieldname":"pledge_status", - "label": __("Pledge Status"), - "fieldtype": "Select", - "options": ["", "Requested", "Pledged", "Partially Pledged", "Unpledged"], + fieldname: "pledge_status", + label: __("Pledge Status"), + fieldtype: "Select", + options: ["", "Requested", "Pledged", "Partially Pledged", "Unpledged"], }, - ] + ], }; diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js index 5252798ba57..4613a0fc840 100644 --- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js +++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.js @@ -2,15 +2,15 @@ // License: GNU General Public License v3. See license.txt frappe.provide("erpnext.maintenance"); -frappe.ui.form.on('Maintenance Schedule', { +frappe.ui.form.on("Maintenance Schedule", { setup: function (frm) { - frm.set_query('contact_person', erpnext.queries.contact_query); - frm.set_query('customer_address', erpnext.queries.address_query); - frm.set_query('customer', erpnext.queries.customer); + frm.set_query("contact_person", erpnext.queries.contact_query); + frm.set_query("customer_address", erpnext.queries.address_query); + frm.set_query("customer", erpnext.queries.customer); }, onload: function (frm) { if (!frm.doc.status) { - frm.set_value({ status: 'Draft' }); + frm.set_value({ status: "Draft" }); } if (frm.doc.__islocal) { frm.set_value({ transaction_date: frappe.datetime.get_today() }); @@ -18,128 +18,136 @@ frappe.ui.form.on('Maintenance Schedule', { }, refresh: function (frm) { setTimeout(() => { - frm.toggle_display('generate_schedule', !(frm.is_new() || frm.doc.docstatus)); - frm.toggle_display('schedule', !(frm.is_new())); + frm.toggle_display("generate_schedule", !(frm.is_new() || frm.doc.docstatus)); + frm.toggle_display("schedule", !frm.is_new()); }, 10); }, customer: function (frm) { - erpnext.utils.get_party_details(frm) + erpnext.utils.get_party_details(frm); }, customer_address: function (frm) { - erpnext.utils.get_address_display(frm, 'customer_address', 'address_display'); + erpnext.utils.get_address_display(frm, "customer_address", "address_display"); }, contact_person: function (frm) { erpnext.utils.get_contact_details(frm); }, generate_schedule: function (frm) { if (frm.is_new()) { - frappe.msgprint(__('Please save first')); + frappe.msgprint(__("Please save first")); } else { - frm.call('generate_schedule'); + frm.call("generate_schedule"); } - } -}) + }, +}); // TODO commonify this code erpnext.maintenance.MaintenanceSchedule = class MaintenanceSchedule extends frappe.ui.form.Controller { refresh() { - frappe.dynamic_link = {doc: this.frm.doc, fieldname: 'customer', doctype: 'Customer'} + frappe.dynamic_link = { doc: this.frm.doc, fieldname: "customer", doctype: "Customer" }; var me = this; if (this.frm.doc.docstatus === 0) { - this.frm.add_custom_button(__('Sales Order'), + this.frm.add_custom_button( + __("Sales Order"), function () { erpnext.utils.map_current_doc({ method: "erpnext.selling.doctype.sales_order.sales_order.make_maintenance_schedule", source_doctype: "Sales Order", target: me.frm, setters: { - customer: me.frm.doc.customer || undefined + customer: me.frm.doc.customer || undefined, }, get_query_filters: { docstatus: 1, - company: me.frm.doc.company - } + company: me.frm.doc.company, + }, }); - }, __("Get Items From")); + }, + __("Get Items From") + ); } else if (this.frm.doc.docstatus === 1) { let schedules = me.frm.doc.schedules; - let flag = schedules.some(schedule => schedule.completion_status === "Pending"); + let flag = schedules.some((schedule) => schedule.completion_status === "Pending"); if (flag) { - this.frm.add_custom_button(__('Maintenance Visit'), function () { - let options = ""; + this.frm.add_custom_button( + __("Maintenance Visit"), + function () { + let options = ""; - me.frm.call('get_pending_data', {data_type: "items"}).then(r => { - options = r.message; + me.frm.call("get_pending_data", { data_type: "items" }).then((r) => { + options = r.message; - let schedule_id = ""; - let d = new frappe.ui.Dialog({ - title: __("Enter Visit Details"), - fields: [{ - fieldtype: "Select", - fieldname: "item_name", - label: __("Item Name"), - options: options, - reqd: 1, - onchange: function () { - let field = d.get_field("scheduled_date"); - me.frm.call('get_pending_data', - { - item_name: this.value, - data_type: "date" - }).then(r => { - field.df.options = r.message; - field.refresh(); - }); - } - }, - { - label: __('Scheduled Date'), - fieldname: 'scheduled_date', - fieldtype: 'Select', - options: "", - reqd: 1, - onchange: function () { - let field = d.get_field('item_name'); - me.frm.call( - 'get_pending_data', - { - item_name: field.value, - s_date: this.value, - data_type: "id" - }).then(r => { - schedule_id = r.message; - }); - } - }, - ], - primary_action_label: 'Create Visit', - primary_action(values) { - frappe.call({ - method: "erpnext.maintenance.doctype.maintenance_schedule.maintenance_schedule.make_maintenance_visit", - args: { - item_name: values.item_name, - s_id: schedule_id, - source_name: me.frm.doc.name, + let schedule_id = ""; + let d = new frappe.ui.Dialog({ + title: __("Enter Visit Details"), + fields: [ + { + fieldtype: "Select", + fieldname: "item_name", + label: __("Item Name"), + options: options, + reqd: 1, + onchange: function () { + let field = d.get_field("scheduled_date"); + me.frm + .call("get_pending_data", { + item_name: this.value, + data_type: "date", + }) + .then((r) => { + field.df.options = r.message; + field.refresh(); + }); + }, }, - callback: function (r) { - if (!r.exc) { - frappe.model.sync(r.message); - frappe.set_route("Form", r.message.doctype, r.message.name); - } - } - }); - d.hide(); - } + { + label: __("Scheduled Date"), + fieldname: "scheduled_date", + fieldtype: "Select", + options: "", + reqd: 1, + onchange: function () { + let field = d.get_field("item_name"); + me.frm + .call("get_pending_data", { + item_name: field.value, + s_date: this.value, + data_type: "id", + }) + .then((r) => { + schedule_id = r.message; + }); + }, + }, + ], + primary_action_label: "Create Visit", + primary_action(values) { + frappe.call({ + method: "erpnext.maintenance.doctype.maintenance_schedule.maintenance_schedule.make_maintenance_visit", + args: { + item_name: values.item_name, + s_id: schedule_id, + source_name: me.frm.doc.name, + }, + callback: function (r) { + if (!r.exc) { + frappe.model.sync(r.message); + frappe.set_route("Form", r.message.doctype, r.message.name); + } + }, + }); + d.hide(); + }, + }); + d.show(); }); - d.show(); - }); - }, __('Create')); + }, + __("Create") + ); } } } - }; -extend_cscript(cur_frm.cscript, new erpnext.maintenance.MaintenanceSchedule({frm: cur_frm})); +extend_cscript(cur_frm.cscript, new erpnext.maintenance.MaintenanceSchedule({ frm: cur_frm })); diff --git a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js index e2f6cb3a6cc..0a05791b1e9 100644 --- a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js +++ b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.js @@ -2,11 +2,11 @@ // License: GNU General Public License v3. See license.txt frappe.provide("erpnext.maintenance"); -frappe.ui.form.on('Maintenance Visit', { +frappe.ui.form.on("Maintenance Visit", { setup: function (frm) { - frm.set_query('contact_person', erpnext.queries.contact_query); - frm.set_query('customer_address', erpnext.queries.address_query); - frm.set_query('customer', erpnext.queries.customer); + frm.set_query("contact_person", erpnext.queries.contact_query); + frm.set_query("customer_address", erpnext.queries.address_query); + frm.set_query("customer", erpnext.queries.customer); }, onload: function (frm) { // filters for serial no based on item code @@ -15,42 +15,44 @@ frappe.ui.form.on('Maintenance Visit', { if (!item_code) { return; } - frappe.call({ - method: "erpnext.maintenance.doctype.maintenance_schedule.maintenance_schedule.get_serial_nos_from_schedule", - args: { - schedule: frm.doc.maintenance_schedule, - item_code: item_code - } - }).then((r) => { - let serial_nos = r.message; - frm.set_query('serial_no', 'purposes', () => { - if (serial_nos.length > 0) { + frappe + .call({ + method: "erpnext.maintenance.doctype.maintenance_schedule.maintenance_schedule.get_serial_nos_from_schedule", + args: { + schedule: frm.doc.maintenance_schedule, + item_code: item_code, + }, + }) + .then((r) => { + let serial_nos = r.message; + frm.set_query("serial_no", "purposes", () => { + if (serial_nos.length > 0) { + return { + filters: { + item_code: item_code, + name: ["in", serial_nos], + }, + }; + } return { filters: { - 'item_code': item_code, - 'name': ["in", serial_nos] - } + item_code: item_code, + }, }; - } - return { - filters: { - 'item_code': item_code - } - }; + }); }); - }); } else { - frm.set_query('serial_no', 'purposes', (frm, cdt, cdn) => { + frm.set_query("serial_no", "purposes", (frm, cdt, cdn) => { let row = locals[cdt][cdn]; return { filters: { - 'item_code': row.item_code - } + item_code: row.item_code, + }, }; }); } if (!frm.doc.status) { - frm.set_value({ status: 'Draft' }); + frm.set_value({ status: "Draft" }); } if (frm.doc.__islocal) { frm.set_value({ mntc_date: frappe.datetime.get_today() }); @@ -60,25 +62,26 @@ frappe.ui.form.on('Maintenance Visit', { erpnext.utils.get_party_details(frm); }, customer_address: function (frm) { - erpnext.utils.get_address_display(frm, 'customer_address', 'address_display'); + erpnext.utils.get_address_display(frm, "customer_address", "address_display"); }, contact_person: function (frm) { erpnext.utils.get_contact_details(frm); - } -}) + }, +}); // TODO commonify this code erpnext.maintenance.MaintenanceVisit = class MaintenanceVisit extends frappe.ui.form.Controller { refresh() { - frappe.dynamic_link = {doc: this.frm.doc, fieldname: 'customer', doctype: 'Customer'} + frappe.dynamic_link = { doc: this.frm.doc, fieldname: "customer", doctype: "Customer" }; var me = this; if (this.frm.doc.docstatus === 0) { - this.frm.add_custom_button(__('Maintenance Schedule'), + this.frm.add_custom_button( + __("Maintenance Schedule"), function () { if (!me.frm.doc.customer) { - frappe.msgprint(__('Please select Customer first')); + frappe.msgprint(__("Please select Customer first")); return; } erpnext.utils.map_current_doc({ @@ -90,11 +93,14 @@ erpnext.maintenance.MaintenanceVisit = class MaintenanceVisit extends frappe.ui. }, get_query_filters: { docstatus: 1, - company: me.frm.doc.company - } - }) - }, __("Get Items From")); - this.frm.add_custom_button(__('Warranty Claim'), + company: me.frm.doc.company, + }, + }); + }, + __("Get Items From") + ); + this.frm.add_custom_button( + __("Warranty Claim"), function () { erpnext.utils.map_current_doc({ method: "erpnext.support.doctype.warranty_claim.warranty_claim.make_maintenance_visit", @@ -106,14 +112,17 @@ erpnext.maintenance.MaintenanceVisit = class MaintenanceVisit extends frappe.ui. }, get_query_filters: { status: ["in", "Open, Work in Progress"], - company: me.frm.doc.company - } - }) - }, __("Get Items From")); - this.frm.add_custom_button(__('Sales Order'), + company: me.frm.doc.company, + }, + }); + }, + __("Get Items From") + ); + this.frm.add_custom_button( + __("Sales Order"), function () { if (!me.frm.doc.customer) { - frappe.msgprint(__('Please select Customer first')); + frappe.msgprint(__("Please select Customer first")); return; } erpnext.utils.map_current_doc({ @@ -127,11 +136,13 @@ erpnext.maintenance.MaintenanceVisit = class MaintenanceVisit extends frappe.ui. docstatus: 1, company: me.frm.doc.company, order_type: me.frm.doc.order_type, - } - }) - }, __("Get Items From")); + }, + }); + }, + __("Get Items From") + ); } } }; -extend_cscript(cur_frm.cscript, new erpnext.maintenance.MaintenanceVisit({frm: cur_frm})); +extend_cscript(cur_frm.cscript, new erpnext.maintenance.MaintenanceVisit({ frm: cur_frm })); diff --git a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit_list.js b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit_list.js index e98979deb1b..c47706e33d9 100644 --- a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit_list.js +++ b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit_list.js @@ -1,11 +1,15 @@ -frappe.listview_settings['Maintenance Visit'] = { +frappe.listview_settings["Maintenance Visit"] = { add_fields: ["customer", "customer_name", "completion_status", "maintenance_type"], - get_indicator: function(doc) { + get_indicator: function (doc) { var s = doc.completion_status || "Pending"; - return [__(s), { - "Pending": "blue", - "Partially Completed": "orange", - "Fully Completed": "green" - }[s], "completion_status,=," + doc.completion_status]; - } + return [ + __(s), + { + Pending: "blue", + "Partially Completed": "orange", + "Fully Completed": "green", + }[s], + "completion_status,=," + doc.completion_status, + ]; + }, }; diff --git a/erpnext/manufacturing/doctype/blanket_order/blanket_order.js b/erpnext/manufacturing/doctype/blanket_order/blanket_order.js index 7b26a14a57b..ead247cc222 100644 --- a/erpnext/manufacturing/doctype/blanket_order/blanket_order.js +++ b/erpnext/manufacturing/doctype/blanket_order/blanket_order.js @@ -1,60 +1,72 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Blanket Order', { - onload: function(frm) { - frm.trigger('set_tc_name_filter'); +frappe.ui.form.on("Blanket Order", { + onload: function (frm) { + frm.trigger("set_tc_name_filter"); }, - setup: function(frm) { + setup: function (frm) { frm.custom_make_buttons = { - 'Purchase Order': 'Purchase Order', - 'Sales Order': 'Sales Order', - 'Quotation': 'Quotation', + "Purchase Order": "Purchase Order", + "Sales Order": "Sales Order", + Quotation: "Quotation", }; frm.add_fetch("customer", "customer_name", "customer_name"); frm.add_fetch("supplier", "supplier_name", "supplier_name"); }, - refresh: function(frm) { + refresh: function (frm) { erpnext.hide_company(); if (frm.doc.customer && frm.doc.docstatus === 1 && frm.doc.to_date > frappe.datetime.get_today()) { - frm.add_custom_button(__("Sales Order"), function() { - frappe.model.open_mapped_doc({ - method: "erpnext.manufacturing.doctype.blanket_order.blanket_order.make_order", - frm: frm, - args: { - doctype: 'Sales Order' - } - }); - }, __('Create')); + frm.add_custom_button( + __("Sales Order"), + function () { + frappe.model.open_mapped_doc({ + method: "erpnext.manufacturing.doctype.blanket_order.blanket_order.make_order", + frm: frm, + args: { + doctype: "Sales Order", + }, + }); + }, + __("Create") + ); - frm.add_custom_button(__("Quotation"), function() { - frappe.model.open_mapped_doc({ - method: "erpnext.manufacturing.doctype.blanket_order.blanket_order.make_order", - frm: frm, - args: { - doctype: 'Quotation' - } - }); - }, __('Create')); + frm.add_custom_button( + __("Quotation"), + function () { + frappe.model.open_mapped_doc({ + method: "erpnext.manufacturing.doctype.blanket_order.blanket_order.make_order", + frm: frm, + args: { + doctype: "Quotation", + }, + }); + }, + __("Create") + ); } if (frm.doc.supplier && frm.doc.docstatus === 1) { - frm.add_custom_button(__("Purchase Order"), function(){ - frappe.model.open_mapped_doc({ - method: "erpnext.manufacturing.doctype.blanket_order.blanket_order.make_order", - frm: frm, - args: { - doctype: 'Purchase Order' - } - }); - }, __('Create')); + frm.add_custom_button( + __("Purchase Order"), + function () { + frappe.model.open_mapped_doc({ + method: "erpnext.manufacturing.doctype.blanket_order.blanket_order.make_order", + frm: frm, + args: { + doctype: "Purchase Order", + }, + }); + }, + __("Create") + ); } }, - onload_post_render: function(frm) { + onload_post_render: function (frm) { frm.get_field("items").grid.set_multiple_add("item_code", "qty"); }, @@ -66,28 +78,28 @@ frappe.ui.form.on('Blanket Order', { }); }, - set_tc_name_filter: function(frm) { - if (frm.doc.blanket_order_type === 'Selling') { - frm.set_df_property("customer","reqd", 1); - frm.set_df_property("supplier","reqd", 0); + set_tc_name_filter: function (frm) { + if (frm.doc.blanket_order_type === "Selling") { + frm.set_df_property("customer", "reqd", 1); + frm.set_df_property("supplier", "reqd", 0); frm.set_value("supplier", ""); - frm.set_query("tc_name", function() { + frm.set_query("tc_name", function () { return { filters: { selling: 1 } }; }); } - if (frm.doc.blanket_order_type === 'Purchasing') { - frm.set_df_property("supplier","reqd", 1); - frm.set_df_property("customer","reqd", 0); + if (frm.doc.blanket_order_type === "Purchasing") { + frm.set_df_property("supplier", "reqd", 1); + frm.set_df_property("customer", "reqd", 0); frm.set_value("customer", ""); - frm.set_query("tc_name", function() { + frm.set_query("tc_name", function () { return { filters: { buying: 1 } }; }); } }, blanket_order_type: function (frm) { - frm.trigger('set_tc_name_filter'); - } + frm.trigger("set_tc_name_filter"); + }, }); diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index 231c476734c..b7ab6ef3872 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -6,139 +6,150 @@ frappe.provide("erpnext.bom"); frappe.ui.form.on("BOM", { setup(frm) { frm.custom_make_buttons = { - 'Work Order': 'Work Order', - 'Quality Inspection': 'Quality Inspection' + "Work Order": "Work Order", + "Quality Inspection": "Quality Inspection", }; - frm.set_query("bom_no", "items", function() { + frm.set_query("bom_no", "items", function () { return { filters: { - 'currency': frm.doc.currency, - 'company': frm.doc.company - } + currency: frm.doc.currency, + company: frm.doc.company, + }, }; }); - frm.set_query("source_warehouse", "items", function() { + frm.set_query("source_warehouse", "items", function () { return { filters: { - 'company': frm.doc.company - } + company: frm.doc.company, + }, }; }); - frm.set_query("item", function() { + frm.set_query("item", function () { return { query: "erpnext.manufacturing.doctype.bom.bom.item_query", filters: { - "is_stock_item": 1 - } + is_stock_item: 1, + }, }; }); - frm.set_query("project", function() { - return{ - filters:[ - ['Project', 'status', 'not in', 'Completed, Cancelled'] - ] + frm.set_query("project", function () { + return { + filters: [["Project", "status", "not in", "Completed, Cancelled"]], }; }); - frm.set_query("item_code", "items", function(doc) { + frm.set_query("item_code", "items", function (doc) { return { query: "erpnext.manufacturing.doctype.bom.bom.item_query", filters: { - "include_item_in_manufacturing": 1, - "is_fixed_asset": 0 - } + include_item_in_manufacturing: 1, + is_fixed_asset: 0, + }, }; }); - frm.set_query("bom_no", "items", function(doc, cdt, cdn) { + frm.set_query("bom_no", "items", function (doc, cdt, cdn) { var d = locals[cdt][cdn]; return { filters: { - 'item': d.item_code, - 'is_active': 1, - 'docstatus': 1 - } + item: d.item_code, + is_active: 1, + docstatus: 1, + }, }; }); }, - validate: function(frm) { + validate: function (frm) { if (frm.doc.fg_based_operating_cost && frm.doc.with_operations) { - frappe.throw({message: __("Please check either with operations or FG Based Operating Cost."), title: __("Mandatory")}); + frappe.throw({ + message: __("Please check either with operations or FG Based Operating Cost."), + title: __("Mandatory"), + }); } }, - with_operations: function(frm) { + with_operations: function (frm) { frm.set_df_property("fg_based_operating_cost", "hidden", frm.doc.with_operations ? 1 : 0); }, - fg_based_operating_cost: function(frm) { + fg_based_operating_cost: function (frm) { frm.set_df_property("with_operations", "hidden", frm.doc.fg_based_operating_cost ? 1 : 0); }, - onload_post_render: function(frm) { + onload_post_render: function (frm) { frm.get_field("items").grid.set_multiple_add("item_code", "qty"); }, refresh(frm) { frm.toggle_enable("item", frm.doc.__islocal); - frm.set_indicator_formatter('item_code', - function(doc) { - if (doc.original_item){ - return (doc.item_code != doc.original_item) ? "orange" : "" - } - return "" + frm.set_indicator_formatter("item_code", function (doc) { + if (doc.original_item) { + return doc.item_code != doc.original_item ? "orange" : ""; } - ) + return ""; + }); - if (!frm.is_new() && frm.doc.docstatus<2) { - frm.add_custom_button(__("Update Cost"), function() { + if (!frm.is_new() && frm.doc.docstatus < 2) { + frm.add_custom_button(__("Update Cost"), function () { frm.events.update_cost(frm, true); }); - frm.add_custom_button(__("Browse BOM"), function() { + frm.add_custom_button(__("Browse BOM"), function () { frappe.route_options = { - "bom": frm.doc.name + bom: frm.doc.name, }; frappe.set_route("Tree", "BOM"); }); } if (!frm.is_new() && !frm.doc.docstatus == 0) { - frm.add_custom_button(__("New Version"), function() { + frm.add_custom_button(__("New Version"), function () { let new_bom = frappe.model.copy_doc(frm.doc); frappe.set_route("Form", "BOM", new_bom.name); }); } - if(frm.doc.docstatus==1) { - frm.add_custom_button(__("Work Order"), function() { - frm.trigger("make_work_order"); - }, __("Create")); + if (frm.doc.docstatus == 1) { + frm.add_custom_button( + __("Work Order"), + function () { + frm.trigger("make_work_order"); + }, + __("Create") + ); if (frm.doc.has_variants) { - frm.add_custom_button(__("Variant BOM"), function() { - frm.trigger("make_variant_bom"); - }, __("Create")); + frm.add_custom_button( + __("Variant BOM"), + function () { + frm.trigger("make_variant_bom"); + }, + __("Create") + ); } if (frm.doc.inspection_required) { - frm.add_custom_button(__("Quality Inspection"), function() { - frm.trigger("make_quality_inspection"); - }, __("Create")); + frm.add_custom_button( + __("Quality Inspection"), + function () { + frm.trigger("make_quality_inspection"); + }, + __("Create") + ); } - frm.page.set_inner_btn_group_as_primary(__('Create')); + frm.page.set_inner_btn_group_as_primary(__("Create")); } - if(frm.doc.items && frm.doc.allow_alternative_item) { - const has_alternative = frm.doc.items.find(i => i.allow_alternative_item === 1); + if (frm.doc.items && frm.doc.allow_alternative_item) { + const has_alternative = frm.doc.items.find((i) => i.allow_alternative_item === 1); if (frm.doc.docstatus == 0 && has_alternative) { - frm.add_custom_button(__('Alternate Item'), () => { + frm.add_custom_button(__("Alternate Item"), () => { erpnext.utils.select_alternate_items({ frm: frm, child_docname: "items", @@ -146,23 +157,26 @@ frappe.ui.form.on("BOM", { child_doctype: "BOM Item", original_item_field: "original_item", condition: (d) => { - if (d.allow_alternative_item) {return true;} - } - }) + if (d.allow_alternative_item) { + return true; + } + }, + }); }); } } - if (frm.doc.has_variants) { - frm.set_intro(__('This is a Template BOM and will be used to make the work order for {0} of the item {1}', - [ + frm.set_intro( + __("This is a Template BOM and will be used to make the work order for {0} of the item {1}", [ `variants`, `${frm.doc.item}`, - ]), true); + ]), + true + ); frm.$wrapper.find(".variants-intro").on("click", () => { - frappe.set_route("List", "Item", {"variant_of": frm.doc.item}); + frappe.set_route("List", "Item", { variant_of: frm.doc.item }); }); } }, @@ -176,38 +190,43 @@ frappe.ui.form.on("BOM", { item: item, qty: data.qty || 0.0, project: frm.doc.project, - variant_items: variant_items + variant_items: variant_items, }, freeze: true, callback(r) { - if(r.message) { + if (r.message) { let doc = frappe.model.sync(r.message)[0]; frappe.set_route("Form", doc.doctype, doc.name); } - } + }, }); }); }, make_variant_bom(frm) { - frm.events.setup_variant_prompt(frm, "Variant BOM", (frm, item, data, variant_items) => { - frappe.call({ - method: "erpnext.manufacturing.doctype.bom.bom.make_variant_bom", - args: { - source_name: frm.doc.name, - bom_no: frm.doc.name, - item: item, - 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); - } - } - }); - }, true); + frm.events.setup_variant_prompt( + frm, + "Variant BOM", + (frm, item, data, variant_items) => { + frappe.call({ + method: "erpnext.manufacturing.doctype.bom.bom.make_variant_bom", + args: { + source_name: frm.doc.name, + bom_no: frm.doc.name, + item: item, + 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); + } + }, + }); + }, + true + ); }, setup_variant_prompt(frm, title, callback, skip_qty_field) { @@ -215,27 +234,27 @@ frappe.ui.form.on("BOM", { if (frm.doc.has_variants) { fields.push({ - fieldtype: 'Link', - label: __('Variant Item'), - fieldname: 'item', + fieldtype: "Link", + label: __("Variant Item"), + fieldname: "item", options: "Item", reqd: 1, get_query() { return { query: "erpnext.controllers.queries.item_query", filters: { - "variant_of": frm.doc.item - } + variant_of: frm.doc.item, + }, }; - } + }, }); } if (!skip_qty_field) { fields.push({ - fieldtype: 'Float', - label: __('Qty To Manufacture'), - fieldname: 'qty', + fieldtype: "Float", + label: __("Qty To Manufacture"), + fieldname: "qty", reqd: 1, default: 1, onchange: () => { @@ -244,29 +263,23 @@ frappe.ui.form.on("BOM", { acc[item.item_code] = item.qty; return acc; }, {}); - const mf_qty = cur_dialog.fields_list.filter( - (f) => f.df.fieldname === "qty" - )[0]?.value; - const items = cur_dialog.fields.filter( - (f) => f.fieldname === "items" - )[0]?.data; + const mf_qty = cur_dialog.fields_list.filter((f) => f.df.fieldname === "qty")[0]?.value; + const items = cur_dialog.fields.filter((f) => f.fieldname === "items")[0]?.data; if (!items) { return; } items.forEach((item) => { - item.qty = - (variant_items_map[item.item_code] * mf_qty) / - quantity; + item.qty = (variant_items_map[item.item_code] * mf_qty) / quantity; }); cur_dialog.refresh(); - } + }, }); } - var has_template_rm = frm.doc.items.filter(d => d.has_variants === 1) || []; + var has_template_rm = frm.doc.items.filter((d) => d.has_variants === 1) || []; if (has_template_rm && has_template_rm.length > 0) { fields.push({ fieldname: "items", @@ -296,10 +309,10 @@ frappe.ui.form.on("BOM", { return { query: "erpnext.controllers.queries.item_query", filters: { - "variant_of": data.item_code - } + variant_of: data.item_code, + }, }; - } + }, }, { fieldname: "qty", @@ -312,44 +325,48 @@ frappe.ui.form.on("BOM", { fieldname: "source_warehouse", label: __("Source Warehouse"), fieldtype: "Link", - options: "Warehouse" + options: "Warehouse", }, { fieldname: "operation", label: __("Operation"), fieldtype: "Data", hidden: 1, - } + }, ], in_place_edit: true, data: [], - get_data () { + get_data() { return []; }, }); } - let dialog = frappe.prompt(fields, data => { - let item = data.item || frm.doc.item; - let variant_items = data.items || []; + let dialog = frappe.prompt( + fields, + (data) => { + let item = data.item || frm.doc.item; + let variant_items = data.items || []; - variant_items.forEach(d => { - if (!d.variant_item_code) { - frappe.throw(__("Select variant item code for the template item {0}", [d.item_code])); - } - }) + variant_items.forEach((d) => { + if (!d.variant_item_code) { + 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); + }, + __(title), + __("Create") + ); - }, __(title), __("Create")); - - has_template_rm.forEach(d => { + has_template_rm.forEach((d) => { dialog.fields_dict.items.df.data.push({ - "item_code": d.item_code, - "variant_item_code": "", - "qty": d.qty, - "source_warehouse": d.source_warehouse, - "operation": d.operation + item_code: d.item_code, + variant_item_code: "", + qty: d.qty, + source_warehouse: d.source_warehouse, + operation: d.operation, }); }); @@ -361,11 +378,11 @@ frappe.ui.form.on("BOM", { make_quality_inspection(frm) { frappe.model.open_mapped_doc({ method: "erpnext.stock.doctype.quality_inspection.quality_inspection.make_quality_inspection", - frm: frm - }) + frm: frm, + }); }, - update_cost(frm, save_doc=false) { + update_cost(frm, save_doc = false) { return frappe.call({ doc: frm.doc, method: "update_cost", @@ -373,12 +390,12 @@ frappe.ui.form.on("BOM", { args: { update_parent: true, save: save_doc, - from_child_bom: false + from_child_bom: false, }, callback(r) { refresh_field("items"); - if(!r.exc) frm.refresh_fields(); - } + if (!r.exc) frm.refresh_fields(); + }, }); }, @@ -400,39 +417,39 @@ frappe.ui.form.on("BOM", { erpnext.bom.calculate_op_cost(frm.doc); erpnext.bom.calculate_total(frm.doc); } - } + }, }); } }, process_loss_percentage(frm) { - let qty = 0.0 + let qty = 0.0; if (frm.doc.process_loss_percentage) { qty = (frm.doc.quantity * frm.doc.process_loss_percentage) / 100; } frm.set_value("process_loss_qty", qty); - } + }, }); erpnext.bom.BomController = class BomController extends erpnext.TransactionController { conversion_rate(doc) { - if(this.frm.doc.currency === this.get_company_currency()) { + if (this.frm.doc.currency === this.get_company_currency()) { this.frm.set_value("conversion_rate", 1.0); } else { erpnext.bom.update_cost(doc); } } - item_code(doc, cdt, cdn){ + item_code(doc, cdt, cdn) { var scrap_items = false; var child = locals[cdt][cdn]; - if (child.doctype == 'BOM Scrap Item') { + if (child.doctype == "BOM Scrap Item") { scrap_items = true; } if (child.bom_no) { - child.bom_no = ''; + child.bom_no = ""; } get_bom_material_detail(doc, cdt, cdn, scrap_items); @@ -460,26 +477,26 @@ erpnext.bom.BomController = class BomController extends erpnext.TransactionContr } }; -extend_cscript(cur_frm.cscript, new erpnext.bom.BomController({frm: cur_frm})); +extend_cscript(cur_frm.cscript, new erpnext.bom.BomController({ frm: cur_frm })); -cur_frm.cscript.hour_rate = function(doc) { +cur_frm.cscript.hour_rate = function (doc) { erpnext.bom.calculate_op_cost(doc); erpnext.bom.calculate_total(doc); }; cur_frm.cscript.time_in_mins = cur_frm.cscript.hour_rate; -cur_frm.cscript.bom_no = function(doc, cdt, cdn) { +cur_frm.cscript.bom_no = function (doc, cdt, cdn) { get_bom_material_detail(doc, cdt, cdn, false); }; -cur_frm.cscript.is_default = function(doc) { +cur_frm.cscript.is_default = function (doc) { if (doc.is_default) cur_frm.set_value("is_active", 1); }; -var get_bom_material_detail = function(doc, cdt, cdn, scrap_items) { +var get_bom_material_detail = function (doc, cdt, cdn, scrap_items) { if (!doc.company) { - frappe.throw({message: __("Please select a Company first."), title: __("Mandatory")}); + frappe.throw({ message: __("Please select a Company first."), title: __("Mandatory") }); } var d = locals[cdt][cdn]; @@ -488,20 +505,20 @@ var get_bom_material_detail = function(doc, cdt, cdn, scrap_items) { doc: doc, method: "get_bom_material_detail", args: { - "company": doc.company, - "item_code": d.item_code, - "bom_no": d.bom_no != null ? d.bom_no: '', - "scrap_items": scrap_items, - "qty": d.qty, - "stock_qty": d.stock_qty, - "include_item_in_manufacturing": d.include_item_in_manufacturing, - "uom": d.uom, - "stock_uom": d.stock_uom, - "conversion_factor": d.conversion_factor, - "sourced_by_supplier": d.sourced_by_supplier, - "do_not_explode": d.do_not_explode + company: doc.company, + item_code: d.item_code, + bom_no: d.bom_no != null ? d.bom_no : "", + scrap_items: scrap_items, + qty: d.qty, + stock_qty: d.stock_qty, + include_item_in_manufacturing: d.include_item_in_manufacturing, + uom: d.uom, + stock_uom: d.stock_uom, + conversion_factor: d.conversion_factor, + sourced_by_supplier: d.sourced_by_supplier, + do_not_explode: d.do_not_explode, }, - callback: function(r) { + callback: function (r) { d = locals[cdt][cdn]; $.extend(d, r.message); @@ -513,18 +530,18 @@ var get_bom_material_detail = function(doc, cdt, cdn, scrap_items) { erpnext.bom.calculate_scrap_materials_cost(doc); erpnext.bom.calculate_total(doc); }, - freeze: true + freeze: true, }); } }; -cur_frm.cscript.qty = function(doc) { +cur_frm.cscript.qty = function (doc) { erpnext.bom.calculate_rm_cost(doc); erpnext.bom.calculate_scrap_materials_cost(doc); erpnext.bom.calculate_total(doc); }; -cur_frm.cscript.rate = function(doc, cdt, cdn) { +cur_frm.cscript.rate = function (doc, cdt, cdn) { var d = locals[cdt][cdn]; const is_scrap_item = cdt == "BOM Scrap Item"; @@ -538,52 +555,60 @@ cur_frm.cscript.rate = function(doc, cdt, cdn) { } }; -erpnext.bom.update_cost = function(doc) { +erpnext.bom.update_cost = function (doc) { erpnext.bom.calculate_op_cost(doc); erpnext.bom.calculate_rm_cost(doc); erpnext.bom.calculate_scrap_materials_cost(doc); erpnext.bom.calculate_total(doc); }; -erpnext.bom.calculate_op_cost = function(doc) { +erpnext.bom.calculate_op_cost = function (doc) { doc.operating_cost = 0.0; doc.base_operating_cost = 0.0; - if(doc.with_operations) { + if (doc.with_operations) { doc.operations.forEach((item) => { - let operating_cost = flt(flt(item.hour_rate) * flt(item.time_in_mins) / 60, 2); + let operating_cost = flt((flt(item.hour_rate) * flt(item.time_in_mins)) / 60, 2); let base_operating_cost = flt(operating_cost * doc.conversion_rate, 2); - frappe.model.set_value('BOM Operation',item.name, { - "operating_cost": operating_cost, - "base_operating_cost": base_operating_cost + frappe.model.set_value("BOM Operation", item.name, { + operating_cost: operating_cost, + base_operating_cost: base_operating_cost, }); doc.operating_cost += operating_cost; doc.base_operating_cost += base_operating_cost; }); - } else if(doc.fg_based_operating_cost) { + } else if (doc.fg_based_operating_cost) { let total_operating_cost = doc.quantity * flt(doc.operating_cost_per_bom_quantity); doc.operating_cost = total_operating_cost; doc.base_operating_cost = flt(total_operating_cost * doc.conversion_rate, 2); } - refresh_field(['operating_cost', 'base_operating_cost']); + refresh_field(["operating_cost", "base_operating_cost"]); }; // rm : raw material -erpnext.bom.calculate_rm_cost = function(doc) { +erpnext.bom.calculate_rm_cost = function (doc) { var rm = doc.items || []; var total_rm_cost = 0; var base_total_rm_cost = 0; - for(var i=0;i { - d.allow_alternative_item = r.allow_alternative_item - }) + frappe.db.get_value("Item", { name: d.item_code }, "allow_alternative_item", (r) => { + d.allow_alternative_item = r.allow_alternative_item; + }); refresh_field("allow_alternative_item", d.name, d.parentfield); }); -frappe.ui.form.on("BOM Item", "sourced_by_supplier", function(frm, cdt, cdn) { +frappe.ui.form.on("BOM Item", "sourced_by_supplier", function (frm, cdt, cdn) { var d = locals[cdt][cdn]; if (d.sourced_by_supplier) { d.rate = 0; @@ -700,7 +729,7 @@ frappe.ui.form.on("BOM Item", "sourced_by_supplier", function(frm, cdt, cdn) { } }); -frappe.ui.form.on("BOM Item", "rate", function(frm, cdt, cdn) { +frappe.ui.form.on("BOM Item", "rate", function (frm, cdt, cdn) { var d = locals[cdt][cdn]; if (d.sourced_by_supplier) { d.rate = 0; @@ -708,37 +737,41 @@ frappe.ui.form.on("BOM Item", "rate", function(frm, cdt, cdn) { } }); -frappe.ui.form.on("BOM Operation", "operations_remove", function(frm) { +frappe.ui.form.on("BOM Operation", "operations_remove", function (frm) { erpnext.bom.calculate_op_cost(frm.doc); erpnext.bom.calculate_total(frm.doc); }); -frappe.ui.form.on("BOM Item", "items_remove", function(frm) { +frappe.ui.form.on("BOM Item", "items_remove", function (frm) { erpnext.bom.calculate_rm_cost(frm.doc); erpnext.bom.calculate_total(frm.doc); }); -frappe.tour['BOM'] = [ +frappe.tour["BOM"] = [ { fieldname: "item", title: "Item", - description: __("Select the Item to be manufactured. The Item name, UoM, Company, and Currency will be fetched automatically.") + description: __( + "Select the Item to be manufactured. The Item name, UoM, Company, and Currency will be fetched automatically." + ), }, { fieldname: "quantity", title: "Quantity", - description: __("Enter the quantity of the Item that will be manufactured from this Bill of Materials.") + description: __( + "Enter the quantity of the Item that will be manufactured from this Bill of Materials." + ), }, { fieldname: "with_operations", title: "With Operations", - description: __("To add Operations tick the 'With Operations' checkbox.") + description: __("To add Operations tick the 'With Operations' checkbox."), }, { fieldname: "items", title: "Raw Materials", - description: __("Select the raw materials (Items) required to manufacture the Item") - } + description: __("Select the raw materials (Items) required to manufacture the Item"), + }, ]; frappe.ui.form.on("BOM Scrap Item", { diff --git a/erpnext/manufacturing/doctype/bom/bom_list.js b/erpnext/manufacturing/doctype/bom/bom_list.js index 4b5887f180c..a26df545f85 100644 --- a/erpnext/manufacturing/doctype/bom/bom_list.js +++ b/erpnext/manufacturing/doctype/bom/bom_list.js @@ -1,16 +1,16 @@ -frappe.listview_settings['BOM'] = { +frappe.listview_settings["BOM"] = { add_fields: ["is_active", "is_default", "total_cost", "has_variants"], - get_indicator: function(doc) { - if(doc.is_active && doc.has_variants) { + get_indicator: function (doc) { + if (doc.is_active && doc.has_variants) { return [__("Template"), "orange", "has_variants,=,Yes"]; - } else if(doc.is_default) { + } else if (doc.is_default) { return [__("Default"), "green", "is_default,=,Yes"]; - } else if(doc.is_active) { + } else if (doc.is_active) { return [__("Active"), "blue", "is_active,=,Yes"]; - } else if(!doc.is_active) { + } else if (!doc.is_active) { return [__("Not active"), "gray", "is_active,=,No"]; } - } + }, }; frappe.help.youtube_id["BOM"] = "hDV0c1OeWLo"; diff --git a/erpnext/manufacturing/doctype/bom/bom_tree.js b/erpnext/manufacturing/doctype/bom/bom_tree.js index fb99add12cf..534de0e654b 100644 --- a/erpnext/manufacturing/doctype/bom/bom_tree.js +++ b/erpnext/manufacturing/doctype/bom/bom_tree.js @@ -1,12 +1,12 @@ frappe.treeview_settings["BOM"] = { - get_tree_nodes: 'erpnext.manufacturing.doctype.bom.bom.get_children', + get_tree_nodes: "erpnext.manufacturing.doctype.bom.bom.get_children", filters: [ { fieldname: "bom", - fieldtype:"Link", + fieldtype: "Link", options: "BOM", - label: __("BOM") - } + label: __("BOM"), + }, ], title: "BOM", breadcrumb: "Manufacturing", @@ -14,21 +14,21 @@ frappe.treeview_settings["BOM"] = { root_label: "BOM", //fieldname from filters get_tree_root: false, show_expand_all: false, - get_label: function(node) { - if(node.data.qty) { + get_label: function (node) { + if (node.data.qty) { return node.data.qty + " x " + node.data.item_code; } else { return node.data.item_code || node.data.value; } }, - onload: function(me) { + onload: function (me) { var label = frappe.get_route()[0] + "/" + frappe.get_route()[1]; - if(frappe.pages[label]) { + if (frappe.pages[label]) { delete frappe.pages[label]; } var filter = me.opts.filters[0]; - if(frappe.route_options && frappe.route_options[filter.fieldname]) { + if (frappe.route_options && frappe.route_options[filter.fieldname]) { var val = frappe.route_options[filter.fieldname]; delete frappe.route_options[filter.fieldname]; filter.default = ""; @@ -41,28 +41,27 @@ frappe.treeview_settings["BOM"] = { toolbar: [ { toggle_btn: true }, { - label:__("Edit"), - condition: function(node) { + label: __("Edit"), + condition: function (node) { return node.expandable; }, - click: function(node) { - + click: function (node) { frappe.set_route("Form", "BOM", node.data.value); - } - } + }, + }, ], menu_items: [ { label: __("New BOM"), - action: function() { - frappe.new_doc("BOM", true) + action: function () { + frappe.new_doc("BOM", true); }, - condition: 'frappe.boot.user.can_create.indexOf("BOM") !== -1' - } + condition: 'frappe.boot.user.can_create.indexOf("BOM") !== -1', + }, ], - onrender: function(node) { - if(node.is_root && node.data.value!="BOM") { - frappe.model.with_doc("BOM", node.data.value, function() { + onrender: function (node) { + if (node.is_root && node.data.value != "BOM") { + frappe.model.with_doc("BOM", node.data.value, function () { var bom = frappe.model.get_doc("BOM", node.data.value); node.data.image = escape(bom.image) || ""; node.data.description = bom.description || ""; @@ -70,5 +69,5 @@ frappe.treeview_settings["BOM"] = { }); } }, - view_template: 'bom_item_preview' -} + view_template: "bom_item_preview", +}; diff --git a/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.js b/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.js index 6da808e26d1..7664c299b1a 100644 --- a/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.js +++ b/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.js @@ -1,8 +1,7 @@ // Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('BOM Update Log', { +frappe.ui.form.on("BOM Update Log", { // refresh: function(frm) { - // } }); diff --git a/erpnext/manufacturing/doctype/bom_update_log/bom_update_log_list.js b/erpnext/manufacturing/doctype/bom_update_log/bom_update_log_list.js index bc709d8fc43..cdaee70860b 100644 --- a/erpnext/manufacturing/doctype/bom_update_log/bom_update_log_list.js +++ b/erpnext/manufacturing/doctype/bom_update_log/bom_update_log_list.js @@ -1,11 +1,11 @@ -frappe.listview_settings['BOM Update Log'] = { +frappe.listview_settings["BOM Update Log"] = { add_fields: ["status"], get_indicator: (doc) => { let status_map = { - "Queued": "orange", + Queued: "orange", "In Progress": "blue", - "Completed": "green", - "Failed": "red" + Completed: "green", + Failed: "red", }; return [__(doc.status), status_map[doc.status], "status,=," + doc.status]; @@ -15,16 +15,14 @@ frappe.listview_settings['BOM Update Log'] = { return; } - let sidebar_entry = $( - '' - ).appendTo(cur_list.page.sidebar); + let sidebar_entry = $('').appendTo( + cur_list.page.sidebar + ); let message = __("Note: Automatic log deletion only applies to logs of type Update Cost"); $(`
    ${message}
    `).appendTo(sidebar_entry); frappe.require("logtypes.bundle.js", () => { frappe.utils.logtypes.show_log_retention_message(cur_list.doctype); }); - - }, -}; \ No newline at end of file +}; diff --git a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.js b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.js index 7ba6517a4fb..5cdb425bffc 100644 --- a/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.js +++ b/erpnext/manufacturing/doctype/bom_update_tool/bom_update_tool.js @@ -1,24 +1,24 @@ // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('BOM Update Tool', { - setup: function(frm) { - frm.set_query("current_bom", function() { +frappe.ui.form.on("BOM Update Tool", { + setup: function (frm) { + frm.set_query("current_bom", function () { return { query: "erpnext.controllers.queries.bom", - filters: {name: "!" + frm.doc.new_bom} + filters: { name: "!" + frm.doc.new_bom }, }; }); - frm.set_query("new_bom", function() { + frm.set_query("new_bom", function () { return { query: "erpnext.controllers.queries.bom", - filters: {name: "!" + frm.doc.current_bom} + filters: { name: "!" + frm.doc.current_bom }, }; }); }, - refresh: function(frm) { + refresh: function (frm) { frm.disable_save(); frm.events.disable_button(frm, "replace"); @@ -27,7 +27,7 @@ frappe.ui.form.on('BOM Update Tool', { }); }, - disable_button: (frm, field, disable=true) => { + disable_button: (frm, field, disable = true) => { frm.get_field(field).input.disabled = disable; }, @@ -50,15 +50,15 @@ frappe.ui.form.on('BOM Update Tool', { freeze: true, args: { boms: { - "current_bom": frm.doc.current_bom, - "new_bom": frm.doc.new_bom - } + current_bom: frm.doc.current_bom, + new_bom: frm.doc.new_bom, + }, }, - callback: result => { + callback: (result) => { if (result && result.message && !result.exc) { frm.events.confirm_job_start(frm, result.message); } - } + }, }); } }, @@ -67,20 +67,22 @@ frappe.ui.form.on('BOM Update Tool', { frappe.call({ method: "erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.enqueue_update_cost", freeze: true, - callback: result => { + callback: (result) => { if (result && result.message && !result.exc) { frm.events.confirm_job_start(frm, result.message); } - } + }, }); }, confirm_job_start: (frm, log_data) => { let log_link = frappe.utils.get_form_link("BOM Update Log", log_data.name, true); frappe.msgprint({ - "message": __("BOM Updation is queued and may take a few minutes. Check {0} for progress.", [log_link]), - "title": __("BOM Update Initiated"), - "indicator": "blue" + message: __("BOM Updation is queued and may take a few minutes. Check {0} for progress.", [ + log_link, + ]), + title: __("BOM Update Initiated"), + indicator: "blue", }); - } + }, }); diff --git a/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.js b/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.js index 3b7f5ba8d7f..516e33c7a3b 100644 --- a/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.js +++ b/erpnext/manufacturing/doctype/downtime_entry/downtime_entry.js @@ -1,8 +1,7 @@ // Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Downtime Entry', { +frappe.ui.form.on("Downtime Entry", { // refresh: function(frm) { - // } }); diff --git a/erpnext/manufacturing/doctype/job_card/job_card.js b/erpnext/manufacturing/doctype/job_card/job_card.js index 4a46d577445..db9c8849f45 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.js +++ b/erpnext/manufacturing/doctype/job_card/job_card.js @@ -1,29 +1,27 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Job Card', { - setup: function(frm) { - frm.set_query('operation', function() { +frappe.ui.form.on("Job Card", { + setup: function (frm) { + frm.set_query("operation", function () { return { - query: 'erpnext.manufacturing.doctype.job_card.job_card.get_operations', + query: "erpnext.manufacturing.doctype.job_card.job_card.get_operations", filters: { - 'work_order': frm.doc.work_order - } + work_order: frm.doc.work_order, + }, }; }); - frm.set_indicator_formatter('sub_operation', - function(doc) { - if (doc.status == "Pending") { - return "red"; - } else { - return doc.status === "Complete" ? "green" : "orange"; - } + frm.set_indicator_formatter("sub_operation", function (doc) { + if (doc.status == "Pending") { + return "red"; + } else { + return doc.status === "Complete" ? "green" : "orange"; } - ); + }); }, - refresh: function(frm) { + refresh: function (frm) { frappe.flags.pause_job = 0; frappe.flags.resume_job = 0; let has_items = frm.doc.items && frm.doc.items.length; @@ -33,8 +31,7 @@ frappe.ui.form.on('Job Card', { return; } - let has_stock_entry = frm.doc.__onload && - frm.doc.__onload.has_stock_entry ? true : false; + let has_stock_entry = frm.doc.__onload && frm.doc.__onload.has_stock_entry ? true : false; frm.toggle_enable("for_quantity", !has_stock_entry); @@ -60,33 +57,45 @@ frappe.ui.form.on('Job Card', { } if (frm.doc.docstatus == 1 && !frm.doc.is_corrective_job_card) { - frm.trigger('setup_corrective_job_card'); + frm.trigger("setup_corrective_job_card"); } - frm.set_query("quality_inspection", function() { + frm.set_query("quality_inspection", function () { return { query: "erpnext.stock.doctype.quality_inspection.quality_inspection.quality_inspection_query", filters: { - "item_code": frm.doc.production_item, - "reference_name": frm.doc.name - } + item_code: frm.doc.production_item, + reference_name: frm.doc.name, + }, }; }); frm.trigger("toggle_operation_number"); - if (frm.doc.docstatus == 0 && !frm.is_new() && - (frm.doc.for_quantity > frm.doc.total_completed_qty || !frm.doc.for_quantity) - && (frm.doc.items || !frm.doc.items.length || frm.doc.for_quantity == frm.doc.transferred_qty)) { - + if ( + frm.doc.docstatus == 0 && + !frm.is_new() && + (frm.doc.for_quantity > frm.doc.total_completed_qty || !frm.doc.for_quantity) && + (frm.doc.items || !frm.doc.items.length || frm.doc.for_quantity == frm.doc.transferred_qty) + ) { // if Job Card is link to Work Order, the job card must not be able to start if Work Order not "Started" // and if stock mvt for WIP is required if (frm.doc.work_order) { - frappe.db.get_value('Work Order', frm.doc.work_order, ['skip_transfer', 'status'], (result) => { - if (result.skip_transfer === 1 || result.status == 'In Process' || frm.doc.transferred_qty > 0 || !frm.doc.items.length) { - frm.trigger("prepare_timer_buttons"); + frappe.db.get_value( + "Work Order", + frm.doc.work_order, + ["skip_transfer", "status"], + (result) => { + if ( + result.skip_transfer === 1 || + result.status == "In Process" || + frm.doc.transferred_qty > 0 || + !frm.doc.items.length + ) { + frm.trigger("prepare_timer_buttons"); + } } - }); + ); } else { frm.trigger("prepare_timer_buttons"); } @@ -95,89 +104,103 @@ frappe.ui.form.on('Job Card', { frm.trigger("setup_quality_inspection"); if (frm.doc.work_order) { - frappe.db.get_value('Work Order', frm.doc.work_order, - 'transfer_material_against').then((r) => { - if (r.message.transfer_material_against == 'Work Order') { - frm.set_df_property('items', 'hidden', 1); + frappe.db.get_value("Work Order", frm.doc.work_order, "transfer_material_against").then((r) => { + if (r.message.transfer_material_against == "Work Order") { + frm.set_df_property("items", "hidden", 1); } }); } }, - setup_quality_inspection: function(frm) { + setup_quality_inspection: function (frm) { let quality_inspection_field = frm.get_docfield("quality_inspection"); - quality_inspection_field.get_route_options_for_new_doc = function(frm) { - return { - "inspection_type": "In Process", - "reference_type": "Job Card", - "reference_name": frm.doc.name, - "item_code": frm.doc.production_item, - "item_name": frm.doc.item_name, - "item_serial_no": frm.doc.serial_no, - "batch_no": frm.doc.batch_no, - "quality_inspection_template": frm.doc.quality_inspection_template, + quality_inspection_field.get_route_options_for_new_doc = function (frm) { + return { + inspection_type: "In Process", + reference_type: "Job Card", + reference_name: frm.doc.name, + item_code: frm.doc.production_item, + item_name: frm.doc.item_name, + item_serial_no: frm.doc.serial_no, + batch_no: frm.doc.batch_no, + quality_inspection_template: frm.doc.quality_inspection_template, }; }; }, - setup_corrective_job_card: function(frm) { - frm.add_custom_button(__('Corrective Job Card'), () => { - let operations = frm.doc.sub_operations.map(d => d.sub_operation).concat(frm.doc.operation); + setup_corrective_job_card: function (frm) { + frm.add_custom_button( + __("Corrective Job Card"), + () => { + let operations = frm.doc.sub_operations.map((d) => d.sub_operation).concat(frm.doc.operation); - let fields = [ - { - fieldtype: 'Link', label: __('Corrective Operation'), options: 'Operation', - fieldname: 'operation', get_query() { - return { - filters: { - "is_corrective_operation": 1 - } - }; - } - }, { - fieldtype: 'Link', label: __('For Operation'), options: 'Operation', - fieldname: 'for_operation', get_query() { - return { - filters: { - "name": ["in", operations] - } - }; - } - } - ]; + let fields = [ + { + fieldtype: "Link", + label: __("Corrective Operation"), + options: "Operation", + fieldname: "operation", + get_query() { + return { + filters: { + is_corrective_operation: 1, + }, + }; + }, + }, + { + fieldtype: "Link", + label: __("For Operation"), + options: "Operation", + fieldname: "for_operation", + get_query() { + return { + filters: { + name: ["in", operations], + }, + }; + }, + }, + ]; - frappe.prompt(fields, d => { - frm.events.make_corrective_job_card(frm, d.operation, d.for_operation); - }, __("Select Corrective Operation")); - }, __('Make')); + frappe.prompt( + fields, + (d) => { + frm.events.make_corrective_job_card(frm, d.operation, d.for_operation); + }, + __("Select Corrective Operation") + ); + }, + __("Make") + ); }, - make_corrective_job_card: function(frm, operation, for_operation) { + make_corrective_job_card: function (frm, operation, for_operation) { frappe.call({ - method: 'erpnext.manufacturing.doctype.job_card.job_card.make_corrective_job_card', + method: "erpnext.manufacturing.doctype.job_card.job_card.make_corrective_job_card", args: { source_name: frm.doc.name, operation: operation, - for_operation: for_operation + for_operation: for_operation, }, - callback: function(r) { + callback: function (r) { if (r.message) { frappe.model.sync(r.message); frappe.set_route("Form", r.message.doctype, r.message.name); } - } + }, }); }, - operation: function(frm) { + operation: function (frm) { frm.trigger("toggle_operation_number"); if (frm.doc.operation && frm.doc.work_order) { frappe.call({ method: "erpnext.manufacturing.doctype.job_card.job_card.get_operation_details", args: { - "work_order":frm.doc.work_order, - "operation":frm.doc.operation + work_order: frm.doc.work_order, + operation: frm.doc.operation, }, callback: function (r) { if (r.message) { @@ -187,11 +210,13 @@ frappe.ui.form.on('Job Card', { let args = []; r.message.forEach((row) => { - args.push({ "label": row.idx, "value": row.name }); + args.push({ label: row.idx, value: row.name }); }); - let description = __("Operation {0} added multiple times in the work order {1}", - [frm.doc.operation, frm.doc.work_order]); + let description = __("Operation {0} added multiple times in the work order {1}", [ + frm.doc.operation, + frm.doc.work_order, + ]); frm.set_df_property("operation_row_number", "options", args); frm.set_df_property("operation_row_number", "description", description); @@ -199,8 +224,8 @@ frappe.ui.form.on('Job Card', { frm.trigger("toggle_operation_number"); } - } - }) + }, + }); } }, @@ -215,16 +240,24 @@ frappe.ui.form.on('Job Card', { frm.toggle_reqd("operation_row_number", !frm.doc.operation_id && frm.doc.operation); }, - prepare_timer_buttons: function(frm) { + prepare_timer_buttons: function (frm) { frm.trigger("make_dashboard"); if (!frm.doc.started_time && !frm.doc.current_time) { frm.add_custom_button(__("Start Job"), () => { if ((frm.doc.employee && !frm.doc.employee.length) || !frm.doc.employee) { - frappe.prompt({fieldtype: 'Table MultiSelect', label: __('Select Employees'), - options: "Job Card Time Log", fieldname: 'employees'}, d => { - frm.events.start_job(frm, "Work In Progress", d.employees); - }, __("Assign Job to Employee")); + frappe.prompt( + { + fieldtype: "Table MultiSelect", + label: __("Select Employees"), + options: "Job Card Time Log", + fieldname: "employees", + }, + (d) => { + frm.events.start_job(frm, "Work In Progress", d.employees); + }, + __("Assign Job to Employee") + ); } else { frm.events.start_job(frm, "Work In Progress", frm.doc.employee); } @@ -246,16 +279,24 @@ frappe.ui.form.on('Job Card', { set_qty = false; let last_op_row = sub_operations[sub_operations.length - 2]; - if (last_op_row.status == 'Complete') { + if (last_op_row.status == "Complete") { set_qty = true; } } if (set_qty) { - frappe.prompt({fieldtype: 'Float', label: __('Completed Quantity'), - fieldname: 'qty', default: frm.doc.for_quantity}, data => { - frm.events.complete_job(frm, "Complete", data.qty); - }, __("Enter Value")); + frappe.prompt( + { + fieldtype: "Float", + label: __("Completed Quantity"), + fieldname: "qty", + default: frm.doc.for_quantity, + }, + (data) => { + frm.events.complete_job(frm, "Complete", data.qty); + }, + __("Enter Value") + ); } else { frm.events.complete_job(frm, "Complete", 0.0); } @@ -263,64 +304,63 @@ frappe.ui.form.on('Job Card', { } }, - start_job: function(frm, status, employee) { + start_job: function (frm, status, employee) { const args = { job_card_id: frm.doc.name, start_time: frappe.datetime.now_datetime(), employees: employee, - status: status + status: status, }; frm.events.make_time_log(frm, args); }, - complete_job: function(frm, status, completed_qty) { + complete_job: function (frm, status, completed_qty) { const args = { job_card_id: frm.doc.name, complete_time: frappe.datetime.now_datetime(), status: status, - completed_qty: completed_qty + completed_qty: completed_qty, }; frm.events.make_time_log(frm, args); }, - make_time_log: function(frm, args) { + make_time_log: function (frm, args) { frm.events.update_sub_operation(frm, args); frappe.call({ method: "erpnext.manufacturing.doctype.job_card.job_card.make_time_log", args: { - args: args + args: args, }, freeze: true, callback: function () { frm.reload_doc(); frm.trigger("make_dashboard"); - } + }, }); }, - update_sub_operation: function(frm, args) { + update_sub_operation: function (frm, args) { if (frm.doc.sub_operations && frm.doc.sub_operations.length) { - let sub_operations = frm.doc.sub_operations.filter(d => d.status != 'Complete'); + let sub_operations = frm.doc.sub_operations.filter((d) => d.status != "Complete"); if (sub_operations && sub_operations.length) { args["sub_operation"] = sub_operations[0].sub_operation; } } }, - validate: function(frm) { + validate: function (frm) { if ((!frm.doc.time_logs || !frm.doc.time_logs.length) && frm.doc.started_time) { frm.trigger("reset_timer"); } }, - reset_timer: function(frm) { - frm.set_value('started_time' , ''); + reset_timer: function (frm) { + frm.set_value("started_time", ""); }, - make_dashboard: function(frm) { - if(frm.doc.__islocal) - return; + make_dashboard: function (frm) { + if (frm.doc.__islocal) return; frm.dashboard.refresh(); const timer = ` @@ -340,12 +380,15 @@ frappe.ui.form.on('Job Card', { if (frm.doc.status == "On Hold") { updateStopwatch(currentIncrement); } else { - currentIncrement += moment(frappe.datetime.now_datetime()).diff(moment(frm.doc.started_time),"seconds"); + currentIncrement += moment(frappe.datetime.now_datetime()).diff( + moment(frm.doc.started_time), + "seconds" + ); initialiseTimer(); } function initialiseTimer() { - const interval = setInterval(function() { + const interval = setInterval(function () { var current = setCurrentIncrement(); updateStopwatch(current); }, 1000); @@ -353,12 +396,18 @@ frappe.ui.form.on('Job Card', { function updateStopwatch(increment) { var hours = Math.floor(increment / 3600); - var minutes = Math.floor((increment - (hours * 3600)) / 60); - var seconds = increment - (hours * 3600) - (minutes * 60); + var minutes = Math.floor((increment - hours * 3600) / 60); + var seconds = increment - hours * 3600 - minutes * 60; - $(section).find(".hours").text(hours < 10 ? ("0" + hours.toString()) : hours.toString()); - $(section).find(".minutes").text(minutes < 10 ? ("0" + minutes.toString()) : minutes.toString()); - $(section).find(".seconds").text(seconds < 10 ? ("0" + seconds.toString()) : seconds.toString()); + $(section) + .find(".hours") + .text(hours < 10 ? "0" + hours.toString() : hours.toString()); + $(section) + .find(".minutes") + .text(minutes < 10 ? "0" + minutes.toString() : minutes.toString()); + $(section) + .find(".seconds") + .text(seconds < 10 ? "0" + seconds.toString() : seconds.toString()); } function setCurrentIncrement() { @@ -368,69 +417,67 @@ frappe.ui.form.on('Job Card', { } }, - hide_timer: function(frm) { + hide_timer: function (frm) { frm.toolbar.page.inner_toolbar.find(".stopwatch").remove(); }, - for_quantity: function(frm) { + for_quantity: function (frm) { frm.doc.items = []; frm.call({ method: "get_required_items", doc: frm.doc, - callback: function() { + callback: function () { refresh_field("items"); - } - }) + }, + }); }, - make_material_request: function(frm) { + make_material_request: function (frm) { frappe.model.open_mapped_doc({ method: "erpnext.manufacturing.doctype.job_card.job_card.make_material_request", frm: frm, - run_link_triggers: true + run_link_triggers: true, }); }, - make_stock_entry: function(frm) { + make_stock_entry: function (frm) { frappe.model.open_mapped_doc({ method: "erpnext.manufacturing.doctype.job_card.job_card.make_stock_entry", frm: frm, - run_link_triggers: true + run_link_triggers: true, }); }, - timer: function(frm) { - return `` + timer: function (frm) { + return ``; }, - set_total_completed_qty: function(frm) { + set_total_completed_qty: function (frm) { frm.doc.total_completed_qty = 0; - frm.doc.time_logs.forEach(d => { + frm.doc.time_logs.forEach((d) => { if (d.completed_qty) { frm.doc.total_completed_qty += d.completed_qty; } }); if (frm.doc.total_completed_qty && frm.doc.for_quantity > frm.doc.total_completed_qty) { - let flt_precision = precision('for_quantity', frm.doc); - let process_loss_qty = ( - flt(frm.doc.for_quantity, flt_precision) - - flt(frm.doc.total_completed_qty, flt_precision) - ); + let flt_precision = precision("for_quantity", frm.doc); + let process_loss_qty = + flt(frm.doc.for_quantity, flt_precision) - flt(frm.doc.total_completed_qty, flt_precision); - frm.set_value('process_loss_qty', process_loss_qty); + frm.set_value("process_loss_qty", process_loss_qty); } refresh_field("total_completed_qty"); - } + }, }); -frappe.ui.form.on('Job Card Time Log', { - completed_qty: function(frm) { +frappe.ui.form.on("Job Card Time Log", { + completed_qty: function (frm) { frm.events.set_total_completed_qty(frm); }, - to_time: function(frm) { - frm.set_value('started_time', ''); - } -}) + to_time: function (frm) { + frm.set_value("started_time", ""); + }, +}); diff --git a/erpnext/manufacturing/doctype/job_card/job_card_calendar.js b/erpnext/manufacturing/doctype/job_card/job_card_calendar.js index 9e320853514..7b32a6ee289 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card_calendar.js +++ b/erpnext/manufacturing/doctype/job_card/job_card_calendar.js @@ -1,31 +1,31 @@ frappe.views.calendar["Job Card"] = { field_map: { - "start": "from_time", - "end": "to_time", - "id": "name", - "title": "subject", - "color": "color", - "allDay": "allDay", - "progress": "progress" + start: "from_time", + end: "to_time", + id: "name", + title: "subject", + color: "color", + allDay: "allDay", + progress: "progress", }, gantt: { field_map: { - "start": "expected_start_date", - "end": "expected_end_date", - "id": "name", - "title": "subject", - "color": "color", - "allDay": "allDay", - "progress": "progress" - } + start: "expected_start_date", + end: "expected_end_date", + id: "name", + title: "subject", + color: "color", + allDay: "allDay", + progress: "progress", + }, }, filters: [ { - "fieldtype": "Link", - "fieldname": "employee", - "options": "Employee", - "label": __("Employee") - } + fieldtype: "Link", + fieldname: "employee", + options: "Employee", + label: __("Employee"), + }, ], - get_events_method: "erpnext.manufacturing.doctype.job_card.job_card.get_job_details" + get_events_method: "erpnext.manufacturing.doctype.job_card.job_card.get_job_details", }; diff --git a/erpnext/manufacturing/doctype/job_card/job_card_list.js b/erpnext/manufacturing/doctype/job_card/job_card_list.js index 99fca9570f7..e417b7f576d 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card_list.js +++ b/erpnext/manufacturing/doctype/job_card/job_card_list.js @@ -1,17 +1,17 @@ -frappe.listview_settings['Job Card'] = { +frappe.listview_settings["Job Card"] = { has_indicator_for_draft: true, add_fields: ["expected_start_date", "expected_end_date"], - get_indicator: function(doc) { + get_indicator: function (doc) { const status_colors = { "Work In Progress": "orange", - "Completed": "green", - "Cancelled": "red", + Completed: "green", + Cancelled: "red", "Material Transferred": "blue", - "Open": "red", + Open: "red", }; const status = doc.status || "Open"; const color = status_colors[status] || "blue"; return [__(status), color, `status,=,${status}`]; - } + }, }; diff --git a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.js b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.js index a0122a47385..f54478a1c10 100644 --- a/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.js +++ b/erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.js @@ -1,33 +1,40 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Manufacturing Settings', { -}); +frappe.ui.form.on("Manufacturing Settings", {}); frappe.tour["Manufacturing Settings"] = [ { fieldname: "material_consumption", title: __("Allow Multiple Material Consumption"), - description: __("If ticked, multiple materials can be used for a single Work Order. This is useful if one or more time consuming products are being manufactured.") + description: __( + "If ticked, multiple materials can be used for a single Work Order. This is useful if one or more time consuming products are being manufactured." + ), }, { fieldname: "backflush_raw_materials_based_on", title: __("Backflush Raw Materials"), - description: __("The Stock Entry of type 'Manufacture' is known as backflush. Raw materials being consumed to manufacture finished goods is known as backflushing.

    When creating Manufacture Entry, raw-material items are backflushed based on BOM of production item. If you want raw-material items to be backflushed based on Material Transfer entry made against that Work Order instead, then you can set it under this field.") + description: __( + "The Stock Entry of type 'Manufacture' is known as backflush. Raw materials being consumed to manufacture finished goods is known as backflushing.

    When creating Manufacture Entry, raw-material items are backflushed based on BOM of production item. If you want raw-material items to be backflushed based on Material Transfer entry made against that Work Order instead, then you can set it under this field." + ), }, { fieldname: "default_wip_warehouse", title: __("Work In Progress Warehouse"), - description: __("This Warehouse will be auto-updated in the Work In Progress Warehouse field of Work Orders.") + description: __( + "This Warehouse will be auto-updated in the Work In Progress Warehouse field of Work Orders." + ), }, { fieldname: "default_fg_warehouse", title: __("Finished Goods Warehouse"), - description: __("This Warehouse will be auto-updated in the Target Warehouse field of Work Order.") + description: __("This Warehouse will be auto-updated in the Target Warehouse field of Work Order."), }, { fieldname: "update_bom_costs_automatically", title: __("Update BOM Cost Automatically"), - description: __("If ticked, the BOM cost will be automatically updated based on Valuation Rate / Price List Rate / last purchase rate of raw materials.") - } + description: __( + "If ticked, the BOM cost will be automatically updated based on Valuation Rate / Price List Rate / last purchase rate of raw materials." + ), + }, ]; diff --git a/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.js b/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.js index 61c0a997a42..7a9e2b90120 100644 --- a/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.js +++ b/erpnext/manufacturing/doctype/material_request_plan_item/material_request_plan_item.js @@ -1,8 +1,6 @@ // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Material Request Plan Item', { - refresh: function() { - - } +frappe.ui.form.on("Material Request Plan Item", { + refresh: function () {}, }); diff --git a/erpnext/manufacturing/doctype/operation/operation.js b/erpnext/manufacturing/doctype/operation/operation.js index ea73fd6e273..c51ea72c8a4 100644 --- a/erpnext/manufacturing/doctype/operation/operation.js +++ b/erpnext/manufacturing/doctype/operation/operation.js @@ -1,32 +1,34 @@ // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Operation', { - setup: function(frm) { - frm.set_query('operation', 'sub_operations', function() { +frappe.ui.form.on("Operation", { + setup: function (frm) { + frm.set_query("operation", "sub_operations", function () { return { filters: { - 'name': ['not in', [frm.doc.name]] - } + name: ["not in", [frm.doc.name]], + }, }; }); - } + }, }); -frappe.tour['Operation'] = [ +frappe.tour["Operation"] = [ { fieldname: "__newname", title: "Operation Name", - description: __("Enter a name for the Operation, for example, Cutting.") + description: __("Enter a name for the Operation, for example, Cutting."), }, { fieldname: "workstation", title: "Default Workstation", - description: __("Select the Default Workstation where the Operation will be performed. This will be fetched in BOMs and Work Orders.") + description: __( + "Select the Default Workstation where the Operation will be performed. This will be fetched in BOMs and Work Orders." + ), }, { fieldname: "sub_operations", title: "Sub Operations", - description: __("If an operation is divided into sub operations, they can be added here.") - } + description: __("If an operation is divided into sub operations, they can be added here."), + }, ]; diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js index 667ece2077e..54d1414c814 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.js +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js @@ -1,11 +1,10 @@ // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Production Plan', { - +frappe.ui.form.on("Production Plan", { before_save(frm) { // preserve temporary names on production plan item to re-link sub-assembly items - frm.doc.po_items.forEach(item => { + frm.doc.po_items.forEach((item) => { item.temporary_name = item.name; }); }, @@ -14,8 +13,8 @@ frappe.ui.form.on('Production Plan', { frm.trigger("setup_queries"); frm.custom_make_buttons = { - 'Work Order': 'Work Order / Subcontract PO', - 'Material Request': 'Material Request', + "Work Order": "Work Order / Subcontract PO", + "Material Request": "Material Request", }; }, @@ -25,36 +24,36 @@ frappe.ui.form.on('Production Plan', { query: "erpnext.manufacturing.doctype.production_plan.production_plan.sales_order_query", filters: { company: frm.doc.company, - } - } + }, + }; }); - frm.set_query('for_warehouse', function(doc) { + frm.set_query("for_warehouse", function (doc) { return { filters: { company: doc.company, - is_group: 0 - } - } + is_group: 0, + }, + }; }); - frm.set_query('material_request', 'material_requests', function() { + frm.set_query("material_request", "material_requests", function () { return { filters: { material_request_type: "Manufacture", docstatus: 1, status: ["!=", "Stopped"], - } + }, }; }); frm.set_query("item_code", "po_items", (doc, cdt, cdn) => { return { query: "erpnext.controllers.queries.item_query", - filters:{ - 'is_stock_item': 1, - } - } + filters: { + is_stock_item: 1, + }, + }; }); frm.set_query("bom_no", "po_items", (doc, cdt, cdn) => { @@ -62,25 +61,25 @@ frappe.ui.form.on('Production Plan', { if (d.item_code) { return { query: "erpnext.controllers.queries.bom", - filters:{'item': d.item_code, 'docstatus': 1} - } + filters: { item: d.item_code, docstatus: 1 }, + }; } else frappe.msgprint(__("Please enter Item first")); }); frm.set_query("warehouse", "mr_items", (doc) => { return { filters: { - company: doc.company - } - } + company: doc.company, + }, + }; }); frm.set_query("warehouse", "po_items", (doc) => { return { filters: { - company: doc.company - } - } + company: doc.company, + }, + }; }); }, @@ -89,36 +88,62 @@ frappe.ui.form.on('Production Plan', { 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 === "Closed") { - frm.add_custom_button(__("Re-open"), function() { - frm.events.close_open_production_plan(frm, false); - }, __("Status")); + if (frm.doc.status === "Closed") { + frm.add_custom_button( + __("Re-open"), + function () { + frm.events.close_open_production_plan(frm, false); + }, + __("Status") + ); } else { - frm.add_custom_button(__("Close"), function() { - frm.events.close_open_production_plan(frm, true); - }, __("Status")); + frm.add_custom_button( + __("Close"), + function () { + frm.events.close_open_production_plan(frm, true); + }, + __("Status") + ); } if (frm.doc.po_items && frm.doc.status !== "Closed") { - frm.add_custom_button(__("Work Order / Subcontract PO"), ()=> { - frm.trigger("make_work_order"); - }, __('Create')); + frm.add_custom_button( + __("Work Order / Subcontract PO"), + () => { + frm.trigger("make_work_order"); + }, + __("Create") + ); } - if (frm.doc.mr_items && frm.doc.mr_items.length && !in_list(['Material Requested', 'Closed'], frm.doc.status)) { - frm.add_custom_button(__("Material Request"), ()=> { - frm.trigger("make_material_request"); - }, __('Create')); + if ( + frm.doc.mr_items && + frm.doc.mr_items.length && + !in_list(["Material Requested", "Closed"], frm.doc.status) + ) { + frm.add_custom_button( + __("Material Request"), + () => { + frm.trigger("make_material_request"); + }, + __("Create") + ); } } } if (frm.doc.status !== "Closed") { - frm.page.set_inner_btn_group_as_primary(__('Create')); + frm.page.set_inner_btn_group_as_primary(__("Create")); } frm.trigger("material_requirement"); @@ -145,19 +170,19 @@ frappe.ui.form.on('Production Plan', { ${__("Planned Qty: Quantity, for which, Work Order has been raised, but is pending to be manufactured.")}
  • - ${__('Requested Qty: Quantity requested for purchase, but not ordered.')} + ${__("Requested Qty: Quantity requested for purchase, but not ordered.")}
  • - ${__('Ordered Qty: Quantity ordered for purchase, but not received.')} + ${__("Ordered Qty: Quantity ordered for purchase, but not received.")}
  • ${__("Reserved Qty: Quantity ordered for sale, but not delivered.")}
  • - ${__('Reserved Qty for Production: Raw materials quantity to make manufacturing items.')} + ${__("Reserved Qty for Production: Raw materials quantity to make manufacturing items.")}
  • - ${__('Reserved Qty for Subcontract: Raw materials quantity to make subcontracted items.')} + ${__("Reserved Qty for Subcontract: Raw materials quantity to make subcontracted items.")}
  • @@ -168,15 +193,15 @@ frappe.ui.form.on('Production Plan', { set_field_options("projected_qty_formula", projected_qty_formula); }, - close_open_production_plan(frm, close=false) { + close_open_production_plan(frm, close = false) { frappe.call({ method: "set_status", freeze: true, doc: frm.doc, - args: {close : close, update_bin: true}, - callback: function() { + args: { close: close, update_bin: true }, + callback: function () { frm.reload_doc(); - } + }, }); }, @@ -185,19 +210,19 @@ frappe.ui.form.on('Production Plan', { method: "make_work_order", freeze: true, doc: frm.doc, - callback: function() { + callback: function () { frm.reload_doc(); - } + }, }); }, make_material_request(frm) { - - frappe.confirm(__("Do you want to submit the material request"), - function() { + frappe.confirm( + __("Do you want to submit the material request"), + function () { frm.events.create_material_request(frm, 1); }, - function() { + function () { frm.events.create_material_request(frm, 0); } ); @@ -210,9 +235,9 @@ frappe.ui.form.on('Production Plan', { method: "make_material_request", freeze: true, doc: frm.doc, - callback: function(r) { + callback: function (r) { frm.reload_doc(); - } + }, }); }, @@ -220,9 +245,9 @@ frappe.ui.form.on('Production Plan', { frappe.call({ method: "get_open_sales_orders", doc: frm.doc, - callback: function(r) { + callback: function (r) { refresh_field("sales_orders"); - } + }, }); }, @@ -230,22 +255,22 @@ frappe.ui.form.on('Production Plan', { frappe.call({ method: "get_pending_material_requests", doc: frm.doc, - callback: function() { - refresh_field('material_requests'); - } + callback: function () { + refresh_field("material_requests"); + }, }); }, get_items(frm) { - frm.clear_table('prod_plan_references'); + frm.clear_table("prod_plan_references"); frappe.call({ method: "get_items", freeze: true, doc: frm.doc, callback: function () { - refresh_field('po_items'); - } + refresh_field("po_items"); + }, }); }, combine_items(frm) { @@ -255,12 +280,12 @@ frappe.ui.form.on('Production Plan', { method: "get_items", freeze: true, doc: frm.doc, - callback: function() { + callback: function () { frm.refresh_field("po_items"); if (frm.doc.sub_assembly_items.length > 0) { frm.trigger("get_sub_assembly_items"); } - } + }, }); }, @@ -278,9 +303,9 @@ frappe.ui.form.on('Production Plan', { method: "get_sub_assembly_items", freeze: true, doc: frm.doc, - callback: function() { + callback: function () { refresh_field("sub_assembly_items"); - } + }, }); }, @@ -294,9 +319,11 @@ frappe.ui.form.on('Production Plan', { frappe.throw(__("Select the Warehouse")); } - frm.events.get_items_for_material_requests(frm, [{ - warehouse: frm.doc.for_warehouse - }]); + frm.events.get_items_for_material_requests(frm, [ + { + warehouse: frm.doc.for_warehouse, + }, + ]); }, transfer_materials(frm) { @@ -315,26 +342,26 @@ frappe.ui.form.on('Production Plan', { title: title, fields: [ { - 'label': __('Transfer From Warehouses'), - 'fieldtype': 'Table MultiSelect', - 'fieldname': 'warehouses', - 'options': 'Production Plan Material Request Warehouse', + label: __("Transfer From Warehouses"), + fieldtype: "Table MultiSelect", + fieldname: "warehouses", + options: "Production Plan Material Request Warehouse", get_query: function () { return { filters: { - company: frm.doc.company - } + company: frm.doc.company, + }, }; }, }, { - 'label': __('For Warehouse'), - 'fieldtype': 'Link', - 'fieldname': 'target_warehouse', - 'read_only': true, - 'default': frm.doc.for_warehouse - } - ] + label: __("For Warehouse"), + fieldtype: "Link", + fieldname: "target_warehouse", + read_only: true, + default: frm.doc.for_warehouse, + }, + ], }); dialog.show(); @@ -353,82 +380,90 @@ frappe.ui.form.on('Production Plan', { freeze: true, args: { doc: frm.doc, - warehouses: warehouses || [] + warehouses: warehouses || [], }, - callback: function(r) { - if(r.message) { - frm.set_value('mr_items', []); - r.message.forEach(row => { - let d = frm.add_child('mr_items'); + callback: function (r) { + if (r.message) { + frm.set_value("mr_items", []); + r.message.forEach((row) => { + let d = frm.add_child("mr_items"); for (let field in row) { - if (field !== 'name') { + if (field !== "name") { d[field] = row[field]; } } }); } - refresh_field('mr_items'); - } + refresh_field("mr_items"); + }, }); }, download_materials_required(frm) { - const fields = [{ - fieldname: 'warehouses', - fieldtype: 'Table MultiSelect', - label: __('Warehouses'), - default: frm.doc.from_warehouse, - options: "Production Plan Material Request Warehouse", - get_query: function () { - return { - filters: { - company: frm.doc.company - } - }; + const fields = [ + { + fieldname: "warehouses", + fieldtype: "Table MultiSelect", + label: __("Warehouses"), + default: frm.doc.from_warehouse, + options: "Production Plan Material Request Warehouse", + get_query: function () { + return { + filters: { + company: frm.doc.company, + }, + }; + }, }, - }]; + ]; - frappe.prompt(fields, (row) => { - let get_template_url = 'erpnext.manufacturing.doctype.production_plan.production_plan.download_raw_materials'; - open_url_post(frappe.request.url, { - cmd: get_template_url, - doc: frm.doc, - warehouses: row.warehouses - }); - }, __('Select Warehouses to get Stock for Materials Planning'), __('Get Stock')); + frappe.prompt( + fields, + (row) => { + let get_template_url = + "erpnext.manufacturing.doctype.production_plan.production_plan.download_raw_materials"; + open_url_post(frappe.request.url, { + cmd: get_template_url, + doc: frm.doc, + warehouses: row.warehouses, + }); + }, + __("Select Warehouses to get Stock for Materials Planning"), + __("Get Stock") + ); }, show_progress(frm) { var bars = []; - var message = ''; - var title = ''; + var message = ""; + var title = ""; // produced qty let item_wise_qty = {}; frm.doc.po_items.forEach((data) => { - if(!item_wise_qty[data.item_code]) { + if (!item_wise_qty[data.item_code]) { item_wise_qty[data.item_code] = data.produced_qty; } else { item_wise_qty[data.item_code] += data.produced_qty; } - }) + }); if (item_wise_qty) { for (var key in item_wise_qty) { - title += __('Item {0}: {1} qty produced. ', [key, item_wise_qty[key]]); + title += __("Item {0}: {1} qty produced. ", [key, item_wise_qty[key]]); } } bars.push({ - 'title': title, - 'width': (frm.doc.total_produced_qty / frm.doc.total_planned_qty * 100) + '%', - 'progress_class': 'progress-bar-success' + title: title, + width: (frm.doc.total_produced_qty / frm.doc.total_planned_qty) * 100 + "%", + progress_class: "progress-bar-success", }); - if (bars[0].width == '0%') { - bars[0].width = '0.5%'; + if (bars[0].width == "0%") { + bars[0].width = "0.5%"; } message = title; - frm.dashboard.add_progress(__('Status'), bars, message); + frm.dashboard.add_progress(__("Status"), bars, message); }, }); @@ -439,13 +474,13 @@ frappe.ui.form.on("Production Plan Item", { frappe.call({ method: "erpnext.manufacturing.doctype.production_plan.production_plan.get_item_data", args: { - item_code: row.item_code + item_code: row.item_code, }, - callback: function(r) { + callback: function (r) { for (let key in r.message) { frappe.model.set_value(cdt, cdn, key, r.message[key]); } - } + }, }); } }, @@ -460,30 +495,29 @@ frappe.ui.form.on("Material Request Plan Item", { args: { row: row, company: frm.doc.company, - for_warehouse: row.warehouse + for_warehouse: row.warehouse, }, - callback: function(r) { + callback: function (r) { if (r.message) { - let {projected_qty, actual_qty} = r.message[0]; + let { projected_qty, actual_qty } = r.message[0]; frappe.model.set_value(cdt, cdn, { - 'projected_qty': projected_qty, - 'actual_qty': actual_qty + projected_qty: projected_qty, + actual_qty: actual_qty, }); } - } - }) + }, + }); } }, material_request_type(frm, cdt, cdn) { let row = locals[cdt][cdn]; - if (row.from_warehouse && - row.material_request_type !== "Material Transfer") { - frappe.model.set_value(cdt, cdn, 'from_warehouse', ''); + if (row.from_warehouse && row.material_request_type !== "Material Transfer") { + frappe.model.set_value(cdt, cdn, "from_warehouse", ""); } - } + }, }); frappe.ui.form.on("Production Plan Sales Order", { @@ -506,53 +540,61 @@ frappe.ui.form.on("Production Plan Sales Order", { method: "erpnext.manufacturing.doctype.production_plan.production_plan.get_so_details", args: { sales_order }, callback(r) { - const {transaction_date, customer, grand_total} = r.message; - frappe.model.set_value(cdt, cdn, 'sales_order_date', transaction_date); - frappe.model.set_value(cdt, cdn, 'customer', customer); - frappe.model.set_value(cdt, cdn, 'grand_total', grand_total); - } + const { transaction_date, customer, grand_total } = r.message; + frappe.model.set_value(cdt, cdn, "sales_order_date", transaction_date); + frappe.model.set_value(cdt, cdn, "customer", customer); + frappe.model.set_value(cdt, cdn, "grand_total", grand_total); + }, }); - } + }, }); } - } + }, }); frappe.ui.form.on("Production Plan Sub Assembly Item", { fg_warehouse(frm, cdt, cdn) { erpnext.utils.copy_value_in_all_rows(frm.doc, cdt, cdn, "sub_assembly_items", "fg_warehouse"); }, -}) +}); -frappe.tour['Production Plan'] = [ +frappe.tour["Production Plan"] = [ { fieldname: "get_items_from", title: "Get Items From", - description: __("Select whether to get items from a Sales Order or a Material Request. For now select Sales Order.\n A Production Plan can also be created manually where you can select the Items to manufacture.") + description: __( + "Select whether to get items from a Sales Order or a Material Request. For now select Sales Order.\n A Production Plan can also be created manually where you can select the Items to manufacture." + ), }, { fieldname: "get_sales_orders", title: "Get Sales Orders", - description: __("Click on Get Sales Orders to fetch sales orders based on the above filters.") + description: __("Click on Get Sales Orders to fetch sales orders based on the above filters."), }, { fieldname: "get_items", title: "Get Finished Goods for Manufacture", - description: __("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.") + description: __( + "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." + ), }, { fieldname: "po_items", title: "Finished Goods", - description: __("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.") + description: __( + "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." + ), }, { fieldname: "include_non_stock_items", title: "Include Non Stock Items", - description: __("To include non-stock items in the material request planning. i.e. Items for which 'Maintain Stock' checkbox is unticked.") + description: __( + "To include non-stock items in the material request planning. i.e. Items for which 'Maintain Stock' checkbox is unticked." + ), }, { fieldname: "include_subcontracted_items", title: "Include Subcontracted Items", - description: __("To add subcontracted Item's raw materials if include exploded items is disabled.") - } + description: __("To add subcontracted Item's raw materials if include exploded items is disabled."), + }, ]; diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan_list.js b/erpnext/manufacturing/doctype/production_plan/production_plan_list.js index 8f946866247..bfef6e1220f 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan_list.js +++ b/erpnext/manufacturing/doctype/production_plan/production_plan_list.js @@ -1,4 +1,4 @@ -frappe.listview_settings['Production Plan'] = { +frappe.listview_settings["Production Plan"] = { hide_name_column: true, add_fields: ["status"], filters: [["status", "!=", "Closed"]], @@ -6,14 +6,18 @@ frappe.listview_settings['Production Plan'] = { if (doc.status === "Submitted") { return [__("Not Started"), "orange", "status,=,Submitted"]; } else { - return [__(doc.status), { - "Draft": "red", - "In Process": "orange", - "Completed": "green", - "Material Requested": "yellow", - "Cancelled": "gray", - "Closed": "grey" - }[doc.status], "status,=," + doc.status]; + return [ + __(doc.status), + { + Draft: "red", + "In Process": "orange", + Completed: "green", + "Material Requested": "yellow", + Cancelled: "gray", + Closed: "grey", + }[doc.status], + "status,=," + doc.status, + ]; } - } + }, }; diff --git a/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/production_plan_material_request_warehouse.js b/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/production_plan_material_request_warehouse.js index 53f87582d10..d4e74fa3ec7 100644 --- a/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/production_plan_material_request_warehouse.js +++ b/erpnext/manufacturing/doctype/production_plan_material_request_warehouse/production_plan_material_request_warehouse.js @@ -1,8 +1,7 @@ // Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Production Plan Material Request Warehouse', { +frappe.ui.form.on("Production Plan Material Request Warehouse", { // refresh: function(frm) { - // } }); diff --git a/erpnext/manufacturing/doctype/routing/routing.js b/erpnext/manufacturing/doctype/routing/routing.js index b480c70ad56..67e1bc713d0 100644 --- a/erpnext/manufacturing/doctype/routing/routing.js +++ b/erpnext/manufacturing/doctype/routing/routing.js @@ -1,38 +1,39 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Routing', { - refresh: function(frm) { +frappe.ui.form.on("Routing", { + refresh: function (frm) { frm.trigger("display_sequence_id_column"); }, - onload: function(frm) { + onload: function (frm) { frm.trigger("display_sequence_id_column"); }, - display_sequence_id_column: function(frm) { - frm.fields_dict.operations.grid.update_docfield_property( - 'sequence_id', 'in_list_view', 1 + display_sequence_id_column: function (frm) { + frm.fields_dict.operations.grid.update_docfield_property("sequence_id", "in_list_view", 1); + }, + + calculate_operating_cost: function (frm, child) { + const operating_cost = flt( + (flt(child.hour_rate) * flt(child.time_in_mins)) / 60, + precision("operating_cost", child) ); - }, - - calculate_operating_cost: function(frm, child) { - const operating_cost = flt(flt(child.hour_rate) * flt(child.time_in_mins) / 60, precision("operating_cost", child)); frappe.model.set_value(child.doctype, child.name, "operating_cost", operating_cost); - } + }, }); -frappe.ui.form.on('BOM Operation', { - operation: function(frm, cdt, cdn) { +frappe.ui.form.on("BOM Operation", { + operation: function (frm, cdt, cdn) { const d = locals[cdt][cdn]; - if(!d.operation) return; + if (!d.operation) return; frappe.call({ - "method": "frappe.client.get", + method: "frappe.client.get", args: { doctype: "Operation", - name: d.operation + name: d.operation, }, callback: function (data) { if (data.message.description) { @@ -44,41 +45,43 @@ frappe.ui.form.on('BOM Operation', { } frm.events.calculate_operating_cost(frm, d); - } + }, }); }, - workstation: function(frm, cdt, cdn) { + workstation: function (frm, cdt, cdn) { const d = locals[cdt][cdn]; frappe.call({ - "method": "frappe.client.get", + method: "frappe.client.get", args: { doctype: "Workstation", - name: d.workstation + name: d.workstation, }, callback: function (data) { frappe.model.set_value(d.doctype, d.name, "hour_rate", data.message.hour_rate); frm.events.calculate_operating_cost(frm, d); - } + }, }); }, - time_in_mins: function(frm, cdt, cdn) { + time_in_mins: function (frm, cdt, cdn) { const d = locals[cdt][cdn]; frm.events.calculate_operating_cost(frm, d); - } + }, }); -frappe.tour['Routing'] = [ +frappe.tour["Routing"] = [ { fieldname: "routing_name", title: "Routing Name", - description: __("Enter a name for Routing.") + description: __("Enter a name for Routing."), }, { fieldname: "operations", title: "BOM Operations", - description: __("Enter the Operation, the table will fetch the Operation details like Hourly Rate, Workstation automatically.\n\n After that, set the Operation Time in minutes and the table will calculate the Operation Costs based on the Hourly Rate and Operation Time.") - } + description: __( + "Enter the Operation, the table will fetch the Operation details like Hourly Rate, Workstation automatically.\n\n After that, set the Operation Time in minutes and the table will calculate the Operation Costs based on the Hourly Rate and Operation Time." + ), + }, ]; diff --git a/erpnext/manufacturing/doctype/sub_operation/sub_operation.js b/erpnext/manufacturing/doctype/sub_operation/sub_operation.js index be9db6a4089..8d6d3ddb879 100644 --- a/erpnext/manufacturing/doctype/sub_operation/sub_operation.js +++ b/erpnext/manufacturing/doctype/sub_operation/sub_operation.js @@ -1,8 +1,7 @@ // Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Sub Operation', { +frappe.ui.form.on("Sub Operation", { // refresh: function(frm) { - // } }); diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index 88747b8f7a6..5e386035514 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -2,70 +2,70 @@ // For license information, please see license.txt frappe.ui.form.on("Work Order", { - setup: function(frm) { + setup: function (frm) { frm.custom_make_buttons = { - 'Stock Entry': 'Start', - 'Pick List': 'Create Pick List', - 'Job Card': 'Create Job Card' + "Stock Entry": "Start", + "Pick List": "Create Pick List", + "Job Card": "Create Job Card", }; // Set query for warehouses - frm.set_query("wip_warehouse", function() { + frm.set_query("wip_warehouse", function () { return { filters: { - 'company': frm.doc.company, - } + company: frm.doc.company, + }, }; }); - frm.set_query("source_warehouse", function() { + frm.set_query("source_warehouse", function () { return { filters: { - 'company': frm.doc.company, - } + company: frm.doc.company, + }, }; }); - frm.set_query("source_warehouse", "required_items", function() { + frm.set_query("source_warehouse", "required_items", function () { return { filters: { - 'company': frm.doc.company, - } + company: frm.doc.company, + }, }; }); - frm.set_query("sales_order", function() { + frm.set_query("sales_order", function () { return { filters: { - "status": ["not in", ["Closed", "On Hold"]] - } + status: ["not in", ["Closed", "On Hold"]], + }, }; }); - frm.set_query("fg_warehouse", function() { + frm.set_query("fg_warehouse", function () { return { filters: { - 'company': frm.doc.company, - 'is_group': 0 - } + company: frm.doc.company, + is_group: 0, + }, }; }); - frm.set_query("scrap_warehouse", function() { + frm.set_query("scrap_warehouse", function () { return { filters: { - 'company': frm.doc.company, - 'is_group': 0 - } + company: frm.doc.company, + is_group: 0, + }, }; }); // Set query for BOM - frm.set_query("bom_no", function() { + frm.set_query("bom_no", function () { if (frm.doc.production_item) { return { query: "erpnext.controllers.queries.bom", - filters: {item: cstr(frm.doc.production_item)} + filters: { item: cstr(frm.doc.production_item) }, }; } else { frappe.msgprint(__("Please enter Production Item first")); @@ -73,60 +73,62 @@ frappe.ui.form.on("Work Order", { }); // Set query for FG Item - frm.set_query("production_item", function() { + frm.set_query("production_item", function () { return { query: "erpnext.controllers.queries.item_query", filters: { - "is_stock_item": 1, - } + is_stock_item: 1, + }, }; }); // Set query for FG Item - frm.set_query("project", function() { - return{ - filters:[ - ['Project', 'status', 'not in', 'Completed, Cancelled'] - ] + frm.set_query("project", function () { + return { + filters: [["Project", "status", "not in", "Completed, Cancelled"]], }; }); - frm.set_query("operation", "required_items", function() { + frm.set_query("operation", "required_items", function () { return { query: "erpnext.manufacturing.doctype.work_order.work_order.get_bom_operations", filters: { - 'parent': frm.doc.bom_no, - 'parenttype': 'BOM' - } + parent: frm.doc.bom_no, + parenttype: "BOM", + }, }; }); // formatter for work order operation - frm.set_indicator_formatter('operation', - function(doc) { return (frm.doc.qty==doc.completed_qty) ? "green" : "orange"; }); + frm.set_indicator_formatter("operation", function (doc) { + return frm.doc.qty == doc.completed_qty ? "green" : "orange"; + }); }, - onload: function(frm) { - if (!frm.doc.status) - frm.doc.status = 'Draft'; + onload: function (frm) { + if (!frm.doc.status) frm.doc.status = "Draft"; frm.add_fetch("sales_order", "project", "project"); - if(frm.doc.__islocal) { + if (frm.doc.__islocal) { frm.set_value({ - "actual_start_date": "", - "actual_end_date": "" + actual_start_date: "", + actual_end_date: "", }); erpnext.work_order.set_default_warehouse(frm); } }, - source_warehouse: function(frm) { + source_warehouse: function (frm) { let transaction_controller = new erpnext.TransactionController(); - transaction_controller.autofill_warehouse(frm.doc.required_items, "source_warehouse", frm.doc.source_warehouse); + transaction_controller.autofill_warehouse( + frm.doc.required_items, + "source_warehouse", + frm.doc.source_warehouse + ); }, - refresh: function(frm) { + refresh: function (frm) { erpnext.toggle_naming_series(); erpnext.work_order.set_custom_buttons(frm); frm.set_intro(""); @@ -139,27 +141,30 @@ frappe.ui.form.on("Work Order", { } if (frm.doc.status != "Closed") { - if (frm.doc.docstatus === 1 && frm.doc.status !== "Completed" - && frm.doc.operations && frm.doc.operations.length) { - - const not_completed = frm.doc.operations.filter(d => { - if (d.status != 'Completed') { + if ( + frm.doc.docstatus === 1 && + frm.doc.status !== "Completed" && + frm.doc.operations && + frm.doc.operations.length + ) { + const not_completed = frm.doc.operations.filter((d) => { + if (d.status != "Completed") { return true; } }); if (not_completed && not_completed.length) { - frm.add_custom_button(__('Create Job Card'), () => { + frm.add_custom_button(__("Create Job Card"), () => { frm.trigger("make_job_card"); - }).addClass('btn-primary'); + }).addClass("btn-primary"); } } } - if(frm.doc.required_items && frm.doc.allow_alternative_item) { - const has_alternative = frm.doc.required_items.find(i => i.allow_alternative_item === 1); + if (frm.doc.required_items && frm.doc.allow_alternative_item) { + const has_alternative = frm.doc.required_items.find((i) => i.allow_alternative_item === 1); if (frm.doc.docstatus == 0 && has_alternative) { - frm.add_custom_button(__('Alternate Item'), () => { + frm.add_custom_button(__("Alternate Item"), () => { erpnext.utils.select_alternate_items({ frm: frm, child_docname: "required_items", @@ -167,16 +172,20 @@ frappe.ui.form.on("Work Order", { child_doctype: "Work Order Item", original_item_field: "original_item", condition: (d) => { - if (d.allow_alternative_item) {return true;} - } + if (d.allow_alternative_item) { + return true; + } + }, }); }); } } - if (frm.doc.status == "Completed" && - frm.doc.__onload.backflush_raw_materials_based_on == "Material Transferred for Manufacture") { - frm.add_custom_button(__('Create BOM'), () => { + if ( + frm.doc.status == "Completed" && + frm.doc.__onload.backflush_raw_materials_based_on == "Material Transferred for Manufacture" + ) { + frm.add_custom_button(__("Create BOM"), () => { frm.trigger("make_bom"); }); } @@ -184,120 +193,128 @@ frappe.ui.form.on("Work Order", { frm.trigger("add_custom_button_to_return_components"); }, - add_custom_button_to_return_components: function(frm) { + add_custom_button_to_return_components: function (frm) { if (frm.doc.docstatus === 1 && in_list(["Closed", "Completed"], frm.doc.status)) { - let non_consumed_items = frm.doc.required_items.filter(d =>{ - return flt(d.consumed_qty) < flt(d.transferred_qty - d.returned_qty) + let non_consumed_items = frm.doc.required_items.filter((d) => { + return flt(d.consumed_qty) < flt(d.transferred_qty - d.returned_qty); }); if (non_consumed_items && non_consumed_items.length) { - frm.add_custom_button(__("Return Components"), function() { + frm.add_custom_button(__("Return Components"), function () { frm.trigger("create_stock_return_entry"); }).addClass("btn-primary"); } } }, - create_stock_return_entry: function(frm) { + create_stock_return_entry: function (frm) { frappe.call({ method: "erpnext.manufacturing.doctype.work_order.work_order.make_stock_return_entry", args: { - "work_order": frm.doc.name, + work_order: frm.doc.name, }, - callback: function(r) { - if(!r.exc) { + callback: function (r) { + if (!r.exc) { let doc = frappe.model.sync(r.message); frappe.set_route("Form", doc[0].doctype, doc[0].name); } - } + }, }); }, - make_job_card: function(frm) { + make_job_card: function (frm) { let qty = 0; let operations_data = []; - const dialog = frappe.prompt({fieldname: 'operations', fieldtype: 'Table', label: __('Operations'), - fields: [ - { - fieldtype: 'Link', - fieldname: 'operation', - label: __('Operation'), - read_only: 1, - in_list_view: 1 + const dialog = frappe.prompt( + { + fieldname: "operations", + fieldtype: "Table", + label: __("Operations"), + fields: [ + { + fieldtype: "Link", + fieldname: "operation", + label: __("Operation"), + read_only: 1, + in_list_view: 1, + }, + { + fieldtype: "Link", + fieldname: "workstation", + label: __("Workstation"), + read_only: 1, + in_list_view: 1, + }, + { + fieldtype: "Data", + fieldname: "name", + label: __("Operation Id"), + }, + { + fieldtype: "Float", + fieldname: "pending_qty", + label: __("Pending Qty"), + }, + { + fieldtype: "Float", + fieldname: "qty", + label: __("Quantity to Manufacture"), + read_only: 0, + in_list_view: 1, + }, + { + fieldtype: "Float", + fieldname: "batch_size", + label: __("Batch Size"), + read_only: 1, + }, + { + fieldtype: "Int", + fieldname: "sequence_id", + label: __("Sequence Id"), + read_only: 1, + }, + ], + data: operations_data, + in_place_edit: true, + get_data: function () { + return operations_data; }, - { - fieldtype: 'Link', - fieldname: 'workstation', - label: __('Workstation'), - read_only: 1, - in_list_view: 1 - }, - { - fieldtype: 'Data', - fieldname: 'name', - label: __('Operation Id') - }, - { - fieldtype: 'Float', - fieldname: 'pending_qty', - label: __('Pending Qty'), - }, - { - fieldtype: 'Float', - fieldname: 'qty', - label: __('Quantity to Manufacture'), - read_only: 0, - in_list_view: 1, - }, - { - fieldtype: 'Float', - fieldname: 'batch_size', - label: __('Batch Size'), - read_only: 1 - }, - { - fieldtype: 'Int', - fieldname: 'sequence_id', - label: __('Sequence Id'), - read_only: 1 - }, - ], - data: operations_data, - in_place_edit: true, - get_data: function() { - return operations_data; - } - }, function(data) { - frappe.call({ - method: "erpnext.manufacturing.doctype.work_order.work_order.make_job_card", - freeze: true, - args: { - work_order: frm.doc.name, - operations: data.operations, - }, - callback: function() { - frm.reload_doc(); - } - }); - }, __("Job Card"), __("Create")); + }, + function (data) { + frappe.call({ + method: "erpnext.manufacturing.doctype.work_order.work_order.make_job_card", + freeze: true, + args: { + work_order: frm.doc.name, + operations: data.operations, + }, + callback: function () { + frm.reload_doc(); + }, + }); + }, + __("Job Card"), + __("Create") + ); - dialog.fields_dict["operations"].grid.wrapper.find('.grid-add-row').hide(); + dialog.fields_dict["operations"].grid.wrapper.find(".grid-add-row").hide(); var pending_qty = 0; - frm.doc.operations.forEach(data => { - if(data.completed_qty + data.process_loss_qty != frm.doc.qty) { + frm.doc.operations.forEach((data) => { + if (data.completed_qty + data.process_loss_qty != frm.doc.qty) { pending_qty = frm.doc.qty - flt(data.completed_qty) - flt(data.process_loss_qty); if (pending_qty) { dialog.fields_dict.operations.df.data.push({ - 'name': data.name, - 'operation': data.operation, - 'workstation': data.workstation, - 'batch_size': data.batch_size, - 'qty': pending_qty, - 'pending_qty': pending_qty, - 'sequence_id': data.sequence_id + name: data.name, + operation: data.operation, + workstation: data.workstation, + batch_size: data.batch_size, + qty: pending_qty, + pending_qty: pending_qty, + sequence_id: data.sequence_id, }); } } @@ -305,202 +322,212 @@ frappe.ui.form.on("Work Order", { dialog.fields_dict.operations.grid.refresh(); }, - make_bom: function(frm) { + make_bom: function (frm) { frappe.call({ method: "make_bom", doc: frm.doc, - callback: function(r){ + callback: function (r) { if (r.message) { var doc = frappe.model.sync(r.message)[0]; frappe.set_route("Form", doc.doctype, doc.name); } - } + }, }); }, - show_progress_for_items: function(frm) { + show_progress_for_items: function (frm) { var bars = []; - var message = ''; + var message = ""; var added_min = false; // produced qty - var title = __('{0} items produced', [frm.doc.produced_qty]); + var title = __("{0} items produced", [frm.doc.produced_qty]); bars.push({ - 'title': title, - 'width': (frm.doc.produced_qty / frm.doc.qty * 100) + '%', - 'progress_class': 'progress-bar-success' + title: title, + width: (frm.doc.produced_qty / frm.doc.qty) * 100 + "%", + progress_class: "progress-bar-success", }); - if (bars[0].width == '0%') { - bars[0].width = '0.5%'; + if (bars[0].width == "0%") { + bars[0].width = "0.5%"; added_min = 0.5; } message = title; // pending qty - if(!frm.doc.skip_transfer){ + if (!frm.doc.skip_transfer) { var pending_complete = frm.doc.material_transferred_for_manufacturing - frm.doc.produced_qty; - if(pending_complete) { - var width = ((pending_complete / frm.doc.qty * 100) - added_min); - title = __('{0} items in progress', [pending_complete]); + if (pending_complete) { + var width = (pending_complete / frm.doc.qty) * 100 - added_min; + title = __("{0} items in progress", [pending_complete]); bars.push({ - 'title': title, - 'width': (width > 100 ? "99.5" : width) + '%', - 'progress_class': 'progress-bar-warning' + title: title, + width: (width > 100 ? "99.5" : width) + "%", + progress_class: "progress-bar-warning", }); - message = message + '. ' + title; + message = message + ". " + title; } } - frm.dashboard.add_progress(__('Status'), bars, message); + frm.dashboard.add_progress(__("Status"), bars, message); }, - show_progress_for_operations: function(frm) { + show_progress_for_operations: function (frm) { if (frm.doc.operations && frm.doc.operations.length) { - let progress_class = { "Work in Progress": "progress-bar-warning", - "Completed": "progress-bar-success" + Completed: "progress-bar-success", }; let bars = []; - let message = ''; - let title = ''; + let message = ""; + let title = ""; let status_wise_oprtation_data = {}; let total_completed_qty = frm.doc.qty * frm.doc.operations.length; - frm.doc.operations.forEach(d => { + frm.doc.operations.forEach((d) => { if (!status_wise_oprtation_data[d.status]) { status_wise_oprtation_data[d.status] = [d.completed_qty, d.operation]; } else { status_wise_oprtation_data[d.status][0] += d.completed_qty; - status_wise_oprtation_data[d.status][1] += ', ' + d.operation; + status_wise_oprtation_data[d.status][1] += ", " + d.operation; } }); for (let key in status_wise_oprtation_data) { title = __("{0} Operations: {1}", [key, status_wise_oprtation_data[key][1].bold()]); bars.push({ - 'title': title, - 'width': status_wise_oprtation_data[key][0] / total_completed_qty * 100 + '%', - 'progress_class': progress_class[key] + title: title, + width: (status_wise_oprtation_data[key][0] / total_completed_qty) * 100 + "%", + progress_class: progress_class[key], }); - message += title + '. '; + message += title + ". "; } - frm.dashboard.add_progress(__('Status'), bars, message); + frm.dashboard.add_progress(__("Status"), bars, message); } }, - production_item: function(frm) { + production_item: function (frm) { if (frm.doc.production_item) { frappe.call({ method: "erpnext.manufacturing.doctype.work_order.work_order.get_item_details", args: { item: frm.doc.production_item, - project: frm.doc.project + project: frm.doc.project, }, freeze: true, - callback: function(r) { - if(r.message) { - frm.set_value('sales_order', ""); - frm.trigger('set_sales_order'); + callback: function (r) { + if (r.message) { + frm.set_value("sales_order", ""); + frm.trigger("set_sales_order"); erpnext.in_production_item_onchange = true; - $.each(["description", "stock_uom", "project", "bom_no", "allow_alternative_item", - "transfer_material_against", "item_name"], function(i, field) { - frm.set_value(field, r.message[field]); - }); + $.each( + [ + "description", + "stock_uom", + "project", + "bom_no", + "allow_alternative_item", + "transfer_material_against", + "item_name", + ], + function (i, field) { + frm.set_value(field, r.message[field]); + } + ); - if(r.message["set_scrap_wh_mandatory"]){ + if (r.message["set_scrap_wh_mandatory"]) { frm.toggle_reqd("scrap_warehouse", true); } erpnext.in_production_item_onchange = false; } - } + }, }); } }, - project: function(frm) { - if(!erpnext.in_production_item_onchange && !frm.doc.bom_no) { + project: function (frm) { + if (!erpnext.in_production_item_onchange && !frm.doc.bom_no) { frm.trigger("production_item"); } }, - bom_no: function(frm) { + bom_no: function (frm) { return frm.call({ doc: frm.doc, method: "get_items_and_operations_from_bom", freeze: true, - callback: function(r) { - if(r.message["set_scrap_wh_mandatory"]){ + callback: function (r) { + if (r.message["set_scrap_wh_mandatory"]) { frm.toggle_reqd("scrap_warehouse", true); } - } + }, }); }, - use_multi_level_bom: function(frm) { - if(frm.doc.bom_no) { + use_multi_level_bom: function (frm) { + if (frm.doc.bom_no) { frm.trigger("bom_no"); } }, - qty: function(frm) { - frm.trigger('bom_no'); + qty: function (frm) { + frm.trigger("bom_no"); }, - before_submit: function(frm) { + before_submit: function (frm) { frm.fields_dict.required_items.grid.toggle_reqd("source_warehouse", true); - frm.toggle_reqd("transfer_material_against", - frm.doc.operations && frm.doc.operations.length > 0); + frm.toggle_reqd("transfer_material_against", frm.doc.operations && frm.doc.operations.length > 0); }, - set_sales_order: function(frm) { - if(frm.doc.production_item) { + set_sales_order: function (frm) { + if (frm.doc.production_item) { frappe.call({ method: "erpnext.manufacturing.doctype.work_order.work_order.query_sales_order", args: { production_item: frm.doc.production_item }, - callback: function(r) { - frm.set_query("sales_order", function() { + callback: function (r) { + frm.set_query("sales_order", function () { erpnext.in_production_item_onchange = true; return { - filters: [ - ["Sales Order","name", "in", r.message] - ] + filters: [["Sales Order", "name", "in", r.message]], }; }); - } + }, }); } }, - additional_operating_cost: function(frm) { + additional_operating_cost: function (frm) { erpnext.work_order.calculate_cost(frm.doc); erpnext.work_order.calculate_total_cost(frm); }, }); frappe.ui.form.on("Work Order Item", { - source_warehouse: function(frm, cdt, cdn) { + source_warehouse: function (frm, cdt, cdn) { var row = locals[cdt][cdn]; - if(!row.item_code) { + if (!row.item_code) { frappe.throw(__("Please set the Item Code first")); - } else if(row.source_warehouse) { + } else if (row.source_warehouse) { frappe.call({ - "method": "erpnext.stock.utils.get_latest_stock_qty", + method: "erpnext.stock.utils.get_latest_stock_qty", args: { item_code: row.item_code, - warehouse: row.source_warehouse + warehouse: row.source_warehouse, }, callback: function (r) { - frappe.model.set_value(row.doctype, row.name, - "available_qty_at_source_warehouse", r.message); - } + frappe.model.set_value( + row.doctype, + row.name, + "available_qty_at_source_warehouse", + r.message + ); + }, }); } }, - item_code: function(frm, cdt, cdn) { + item_code: function (frm, cdt, cdn) { let row = locals[cdt][cdn]; if (row.item_code) { @@ -508,94 +535,104 @@ frappe.ui.form.on("Work Order Item", { method: "erpnext.stock.doctype.item.item.get_item_details", args: { item_code: row.item_code, - company: frm.doc.company + company: frm.doc.company, }, - callback: function(r) { + callback: function (r) { if (r.message) { frappe.model.set_value(cdt, cdn, { - "required_qty": row.required_qty || 1, - "item_name": r.message.item_name, - "description": r.message.description, - "source_warehouse": r.message.default_warehouse, - "allow_alternative_item": r.message.allow_alternative_item, - "include_item_in_manufacturing": r.message.include_item_in_manufacturing + required_qty: row.required_qty || 1, + item_name: r.message.item_name, + description: r.message.description, + source_warehouse: r.message.default_warehouse, + allow_alternative_item: r.message.allow_alternative_item, + include_item_in_manufacturing: r.message.include_item_in_manufacturing, }); } - } + }, }); } - } + }, }); frappe.ui.form.on("Work Order Operation", { - workstation: function(frm, cdt, cdn) { + workstation: function (frm, cdt, cdn) { var d = locals[cdt][cdn]; if (d.workstation) { frappe.call({ - "method": "frappe.client.get", + method: "frappe.client.get", args: { doctype: "Workstation", - name: d.workstation + name: d.workstation, }, callback: function (data) { frappe.model.set_value(d.doctype, d.name, "hour_rate", data.message.hour_rate); erpnext.work_order.calculate_cost(frm.doc); erpnext.work_order.calculate_total_cost(frm); - } + }, }); } }, - time_in_mins: function(frm, cdt, cdn) { + time_in_mins: function (frm, cdt, cdn) { erpnext.work_order.calculate_cost(frm.doc); erpnext.work_order.calculate_total_cost(frm); }, }); erpnext.work_order = { - set_custom_buttons: function(frm) { + set_custom_buttons: function (frm) { var doc = frm.doc; if (doc.status !== "Closed") { - frm.add_custom_button(__('Close'), function() { - frappe.confirm(__("Once the Work Order is Closed. It can't be resumed."), - () => { + frm.add_custom_button( + __("Close"), + function () { + frappe.confirm(__("Once the Work Order is Closed. It can't be resumed."), () => { erpnext.work_order.change_work_order_status(frm, "Closed"); - } - ); - }, __("Status")); + }); + }, + __("Status") + ); } if (doc.docstatus === 1 && !in_list(["Closed", "Completed"], doc.status)) { - if (doc.status != 'Stopped' && doc.status != 'Completed') { - frm.add_custom_button(__('Stop'), function() { - erpnext.work_order.change_work_order_status(frm, "Stopped"); - }, __("Status")); - } else if (doc.status == 'Stopped') { - frm.add_custom_button(__('Re-open'), function() { - erpnext.work_order.change_work_order_status(frm, "Resumed"); - }, __("Status")); + if (doc.status != "Stopped" && doc.status != "Completed") { + frm.add_custom_button( + __("Stop"), + function () { + erpnext.work_order.change_work_order_status(frm, "Stopped"); + }, + __("Status") + ); + } else if (doc.status == "Stopped") { + frm.add_custom_button( + __("Re-open"), + function () { + erpnext.work_order.change_work_order_status(frm, "Resumed"); + }, + __("Status") + ); } - const show_start_btn = (frm.doc.skip_transfer - || frm.doc.transfer_material_against == 'Job Card') ? 0 : 1; + const show_start_btn = + frm.doc.skip_transfer || frm.doc.transfer_material_against == "Job Card" ? 0 : 1; if (show_start_btn) { let pending_to_transfer = frm.doc.required_items.some( - item => flt(item.transferred_qty) < flt(item.required_qty) + (item) => flt(item.transferred_qty) < flt(item.required_qty) ); - if (pending_to_transfer && frm.doc.status != 'Stopped') { + if (pending_to_transfer && frm.doc.status != "Stopped") { frm.has_start_btn = true; - frm.add_custom_button(__('Create Pick List'), function() { + frm.add_custom_button(__("Create Pick List"), function () { erpnext.work_order.create_pick_list(frm); }); - var start_btn = frm.add_custom_button(__('Start'), function() { - erpnext.work_order.make_se(frm, 'Material Transfer for Manufacture'); + var start_btn = frm.add_custom_button(__("Start"), function () { + erpnext.work_order.make_se(frm, "Material Transfer for Manufacture"); }); - start_btn.addClass('btn-primary'); + start_btn.addClass("btn-primary"); } } - if (frm.doc.status != 'Stopped') { + if (frm.doc.status != "Stopped") { // If "Material Consumption is check in Manufacturing Settings, allow Material Consumption if (frm.doc.__onload && frm.doc.__onload.material_consumption == 1) { if (flt(doc.material_transferred_for_manufacturing) > 0 || frm.doc.skip_transfer) { @@ -604,89 +641,102 @@ erpnext.work_order = { var tbl = frm.doc.required_items || []; var tbl_lenght = tbl.length; for (var i = 0, len = tbl_lenght; i < len; i++) { - let wo_item_qty = frm.doc.required_items[i].transferred_qty || frm.doc.required_items[i].required_qty; + let wo_item_qty = + frm.doc.required_items[i].transferred_qty || + frm.doc.required_items[i].required_qty; if (flt(wo_item_qty) > flt(frm.doc.required_items[i].consumed_qty)) { counter += 1; } } if (counter > 0) { - var consumption_btn = frm.add_custom_button(__('Material Consumption'), function() { - const backflush_raw_materials_based_on = frm.doc.__onload.backflush_raw_materials_based_on; - erpnext.work_order.make_consumption_se(frm, backflush_raw_materials_based_on); - }); - consumption_btn.addClass('btn-primary'); + var consumption_btn = frm.add_custom_button( + __("Material Consumption"), + function () { + const backflush_raw_materials_based_on = + frm.doc.__onload.backflush_raw_materials_based_on; + erpnext.work_order.make_consumption_se( + frm, + backflush_raw_materials_based_on + ); + } + ); + consumption_btn.addClass("btn-primary"); } } } - if(!frm.doc.skip_transfer){ + if (!frm.doc.skip_transfer) { if (flt(doc.material_transferred_for_manufacturing) > 0) { - if ((flt(doc.produced_qty) < flt(doc.material_transferred_for_manufacturing))) { + if (flt(doc.produced_qty) < flt(doc.material_transferred_for_manufacturing)) { frm.has_finish_btn = true; - var finish_btn = frm.add_custom_button(__('Finish'), function() { - erpnext.work_order.make_se(frm, 'Manufacture'); + var finish_btn = frm.add_custom_button(__("Finish"), function () { + erpnext.work_order.make_se(frm, "Manufacture"); }); - if(doc.material_transferred_for_manufacturing>=doc.qty) { + if (doc.material_transferred_for_manufacturing >= doc.qty) { // all materials transferred for manufacturing, make this primary - finish_btn.addClass('btn-primary'); + finish_btn.addClass("btn-primary"); } } else if (frm.doc.__onload && frm.doc.__onload.overproduction_percentage) { let allowance_percentage = frm.doc.__onload.overproduction_percentage; if (allowance_percentage > 0) { - let allowed_qty = frm.doc.qty + ((allowance_percentage / 100) * frm.doc.qty); + let allowed_qty = frm.doc.qty + (allowance_percentage / 100) * frm.doc.qty; - if ((flt(doc.produced_qty) < allowed_qty)) { - frm.add_custom_button(__('Finish'), function() { - erpnext.work_order.make_se(frm, 'Manufacture'); + if (flt(doc.produced_qty) < allowed_qty) { + frm.add_custom_button(__("Finish"), function () { + erpnext.work_order.make_se(frm, "Manufacture"); }); } } } } } else { - if ((flt(doc.produced_qty) < flt(doc.qty))) { - var finish_btn = frm.add_custom_button(__('Finish'), function() { - erpnext.work_order.make_se(frm, 'Manufacture'); + if (flt(doc.produced_qty) < flt(doc.qty)) { + var finish_btn = frm.add_custom_button(__("Finish"), function () { + erpnext.work_order.make_se(frm, "Manufacture"); }); - finish_btn.addClass('btn-primary'); + finish_btn.addClass("btn-primary"); } } } } }, - calculate_cost: function(doc) { - if (doc.operations){ + calculate_cost: function (doc) { + if (doc.operations) { var op = doc.operations; doc.planned_operating_cost = 0.0; - for(var i=0;i { - frappe.prompt({ - fieldtype: 'Float', - label: __('Qty for {0}', [__(purpose)]), - fieldname: 'qty', - description: __('Max: {0}', [max]), - default: max - }, data => { - max += (frm.doc.qty * (frm.doc.__onload.overproduction_percentage || 0.0)) / 100; + frappe.prompt( + { + fieldtype: "Float", + label: __("Qty for {0}", [__(purpose)]), + fieldname: "qty", + description: __("Max: {0}", [max]), + default: max, + }, + (data) => { + max += (frm.doc.qty * (frm.doc.__onload.overproduction_percentage || 0.0)) / 100; - if (data.qty > max) { - frappe.msgprint(__('Quantity must not be more than {0}', [max])); - reject(); - } - data.purpose = purpose; - resolve(data); - }, __('Select Quantity'), __('Create')); + if (data.qty > max) { + frappe.msgprint(__("Quantity must not be more than {0}", [max])); + reject(); + } + data.purpose = purpose; + resolve(data); + }, + __("Select Quantity"), + __("Create") + ); }); }, - make_se: function(frm, purpose) { + make_se: function (frm, purpose) { this.show_prompt_for_qty_input(frm, purpose) - .then(data => { - return frappe.xcall('erpnext.manufacturing.doctype.work_order.work_order.make_stock_entry', { - 'work_order_id': frm.doc.name, - 'purpose': purpose, - 'qty': data.qty + .then((data) => { + return frappe.xcall("erpnext.manufacturing.doctype.work_order.work_order.make_stock_entry", { + work_order_id: frm.doc.name, + purpose: purpose, + qty: data.qty, }); - }).then(stock_entry => { + }) + .then((stock_entry) => { frappe.model.sync(stock_entry); - frappe.set_route('Form', stock_entry.doctype, stock_entry.name); + frappe.set_route("Form", stock_entry.doctype, stock_entry.name); }); - }, - create_pick_list: function(frm, purpose='Material Transfer for Manufacture') { + create_pick_list: function (frm, purpose = "Material Transfer for Manufacture") { this.show_prompt_for_qty_input(frm, purpose) - .then(data => { - return frappe.xcall('erpnext.manufacturing.doctype.work_order.work_order.create_pick_list', { - 'source_name': frm.doc.name, - 'for_qty': data.qty + .then((data) => { + return frappe.xcall("erpnext.manufacturing.doctype.work_order.work_order.create_pick_list", { + source_name: frm.doc.name, + for_qty: data.qty, }); - }).then(pick_list => { + }) + .then((pick_list) => { frappe.model.sync(pick_list); - frappe.set_route('Form', pick_list.doctype, pick_list.name); + frappe.set_route("Form", pick_list.doctype, pick_list.name); }); }, - make_consumption_se: function(frm, backflush_raw_materials_based_on) { - if(!frm.doc.skip_transfer){ - var max = (backflush_raw_materials_based_on === "Material Transferred for Manufacture") ? - flt(frm.doc.material_transferred_for_manufacturing) - flt(frm.doc.produced_qty) : - flt(frm.doc.qty) - flt(frm.doc.produced_qty); - // flt(frm.doc.qty) - flt(frm.doc.material_transferred_for_manufacturing); + make_consumption_se: function (frm, backflush_raw_materials_based_on) { + if (!frm.doc.skip_transfer) { + var max = + backflush_raw_materials_based_on === "Material Transferred for Manufacture" + ? flt(frm.doc.material_transferred_for_manufacturing) - flt(frm.doc.produced_qty) + : flt(frm.doc.qty) - flt(frm.doc.produced_qty); + // flt(frm.doc.qty) - flt(frm.doc.material_transferred_for_manufacturing); } else { var max = flt(frm.doc.qty) - flt(frm.doc.produced_qty); } frappe.call({ - method:"erpnext.manufacturing.doctype.work_order.work_order.make_stock_entry", + method: "erpnext.manufacturing.doctype.work_order.work_order.make_stock_entry", args: { - "work_order_id": frm.doc.name, - "purpose": "Material Consumption for Manufacture", - "qty": max + work_order_id: frm.doc.name, + purpose: "Material Consumption for Manufacture", + qty: max, }, - callback: function(r) { + callback: function (r) { var doclist = frappe.model.sync(r.message); frappe.set_route("Form", doclist[0].doctype, doclist[0].name); - } + }, }); }, - change_work_order_status: function(frm, status) { - let method_name = status=="Closed" ? "close_work_order" : "stop_unstop"; + change_work_order_status: function (frm, status) { + let method_name = status == "Closed" ? "close_work_order" : "stop_unstop"; frappe.call({ method: `erpnext.manufacturing.doctype.work_order.work_order.${method_name}`, freeze: true, freeze_message: __("Updating Work Order status"), args: { work_order: frm.doc.name, - status: status + status: status, }, - callback: function(r) { - if(r.message) { + callback: function (r) { + if (r.message) { frm.set_value("status", r.message); frm.reload_doc(); } - } + }, }); - } + }, }; -frappe.tour['Work Order'] = [ +frappe.tour["Work Order"] = [ { fieldname: "production_item", title: "Item to Manufacture", - description: __("Select the Item to be manufactured.") + description: __("Select the Item to be manufactured."), }, { fieldname: "bom_no", title: "BOM No", - description: __("The default BOM for that item will be fetched by the system. You can also change the BOM.") + description: __( + "The default BOM for that item will be fetched by the system. You can also change the BOM." + ), }, { fieldname: "qty", title: "Qty to Manufacture", - description: __("Enter the quantity to manufacture. Raw material Items will be fetched only when this is set.") + description: __( + "Enter the quantity to manufacture. Raw material Items will be fetched only when this is set." + ), }, { fieldname: "use_multi_level_bom", title: "Use Multi-Level BOM", - description: __("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.") + description: __( + "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." + ), }, { fieldname: "source_warehouse", title: "Source Warehouse", - description: __("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.") + description: __( + "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." + ), }, { fieldname: "fg_warehouse", title: "Target Warehouse", - description: __("The warehouse where you store finished Items before they are shipped.") + description: __("The warehouse where you store finished Items before they are shipped."), }, { fieldname: "wip_warehouse", title: "Work-in-Progress Warehouse", - description: __("The warehouse where your Items will be transferred when you begin production. Group Warehouse can also be selected as a Work in Progress warehouse.") + description: __( + "The warehouse where your Items will be transferred when you begin production. Group Warehouse can also be selected as a Work in Progress warehouse." + ), }, { fieldname: "scrap_warehouse", title: "Scrap Warehouse", - description: __("If the BOM results in Scrap material, the Scrap Warehouse needs to be selected.") + description: __("If the BOM results in Scrap material, the Scrap Warehouse needs to be selected."), }, { fieldname: "required_items", title: "Required Items", - description: __("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.") + description: __( + "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." + ), }, { fieldname: "planned_start_date", title: "Planned Start Date", - description: __("Set the Planned Start Date (an Estimated Date at which you want the Production to begin)") + description: __( + "Set the Planned Start Date (an Estimated Date at which you want the Production to begin)" + ), }, { fieldname: "operations", title: "Operations", - description: __("If the selected BOM has Operations mentioned in it, the system will fetch all Operations from BOM, these values can be changed.") + description: __( + "If the selected BOM has Operations mentioned in it, the system will fetch all Operations from BOM, these values can be changed." + ), }, - - ]; diff --git a/erpnext/manufacturing/doctype/work_order/work_order_calendar.js b/erpnext/manufacturing/doctype/work_order/work_order_calendar.js index 7ce05e9b881..90ce74ce232 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order_calendar.js +++ b/erpnext/manufacturing/doctype/work_order/work_order_calendar.js @@ -2,23 +2,23 @@ // For license information, please see license.txt frappe.views.calendar["Work Order"] = { - fields: ["planned_start_date", "planned_end_date", "status", "produced_qty", "qty", "name", "name"], + fields: ["planned_start_date", "planned_end_date", "status", "produced_qty", "qty", "name", "name"], field_map: { - "start": "planned_start_date", - "end": "planned_end_date", - "id": "name", - "title": "name", - "status": "status", - "allDay": "allDay", - "progress": function(data) { - return flt(data.produced_qty) / data.qty * 100; - } + start: "planned_start_date", + end: "planned_end_date", + id: "name", + title: "name", + status: "status", + allDay: "allDay", + progress: function (data) { + return (flt(data.produced_qty) / data.qty) * 100; + }, }, gantt: true, - get_css_class: function(data) { - if(data.status==="Completed") { + get_css_class: function (data) { + if (data.status === "Completed") { return "success"; - } else if(data.status==="In Process") { + } else if (data.status === "In Process") { return "warning"; } else { return "danger"; @@ -26,23 +26,23 @@ frappe.views.calendar["Work Order"] = { }, filters: [ { - "fieldtype": "Link", - "fieldname": "sales_order", - "options": "Sales Order", - "label": __("Sales Order") + fieldtype: "Link", + fieldname: "sales_order", + options: "Sales Order", + label: __("Sales Order"), }, { - "fieldtype": "Link", - "fieldname": "production_item", - "options": "Item", - "label": __("Production Item") + fieldtype: "Link", + fieldname: "production_item", + options: "Item", + label: __("Production Item"), }, { - "fieldtype": "Link", - "fieldname": "wip_warehouse", - "options": "Warehouse", - "label": __("WIP Warehouse") - } + fieldtype: "Link", + fieldname: "wip_warehouse", + options: "Warehouse", + label: __("WIP Warehouse"), + }, ], - get_events_method: "frappe.desk.calendar.get_events" -} + get_events_method: "frappe.desk.calendar.get_events", +}; diff --git a/erpnext/manufacturing/doctype/work_order/work_order_list.js b/erpnext/manufacturing/doctype/work_order/work_order_list.js index 81c23bb7104..1e1e5661fe5 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order_list.js +++ b/erpnext/manufacturing/doctype/work_order/work_order_list.js @@ -1,19 +1,31 @@ -frappe.listview_settings['Work Order'] = { - add_fields: ["bom_no", "status", "sales_order", "qty", - "produced_qty", "expected_delivery_date", "planned_start_date", "planned_end_date"], +frappe.listview_settings["Work Order"] = { + add_fields: [ + "bom_no", + "status", + "sales_order", + "qty", + "produced_qty", + "expected_delivery_date", + "planned_start_date", + "planned_end_date", + ], filters: [["status", "!=", "Stopped"]], - get_indicator: function(doc) { - if(doc.status==="Submitted") { + get_indicator: function (doc) { + if (doc.status === "Submitted") { return [__("Not Started"), "orange", "status,=,Submitted"]; } else { - return [__(doc.status), { - "Draft": "red", - "Stopped": "red", - "Not Started": "red", - "In Process": "orange", - "Completed": "green", - "Cancelled": "gray" - }[doc.status], "status,=," + doc.status]; + return [ + __(doc.status), + { + Draft: "red", + Stopped: "red", + "Not Started": "red", + "In Process": "orange", + Completed: "green", + Cancelled: "gray", + }[doc.status], + "status,=," + doc.status, + ]; } - } + }, }; diff --git a/erpnext/manufacturing/doctype/workstation/_test_workstation.js b/erpnext/manufacturing/doctype/workstation/_test_workstation.js index 0f09bd1c61f..44050d0a85a 100644 --- a/erpnext/manufacturing/doctype/workstation/_test_workstation.js +++ b/erpnext/manufacturing/doctype/workstation/_test_workstation.js @@ -8,16 +8,16 @@ QUnit.test("test: Workstation", function (assert) { // number of asserts assert.expect(1); - frappe.run_serially('Workstation', [ + frappe.run_serially("Workstation", [ // insert a new Workstation - () => frappe.tests.make([ - // values to be set - {key: 'value'} - ]), + () => + frappe.tests.make([ + // values to be set + { key: "value" }, + ]), () => { - assert.equal(cur_frm.doc.key, 'value'); + assert.equal(cur_frm.doc.key, "value"); }, - () => done() + () => done(), ]); - }); diff --git a/erpnext/manufacturing/doctype/workstation/workstation.js b/erpnext/manufacturing/doctype/workstation/workstation.js index f830b170ed0..5381e440270 100644 --- a/erpnext/manufacturing/doctype/workstation/workstation.js +++ b/erpnext/manufacturing/doctype/workstation/workstation.js @@ -3,17 +3,16 @@ frappe.ui.form.on("Workstation", { onload(frm) { - if(frm.is_new()) - { + if (frm.is_new()) { frappe.call({ - type:"GET", - method:"erpnext.manufacturing.doctype.workstation.workstation.get_default_holiday_list", - callback: function(r) { - if(!r.exe && r.message){ + type: "GET", + method: "erpnext.manufacturing.doctype.workstation.workstation.get_default_holiday_list", + callback: function (r) { + if (!r.exe && r.message) { cur_frm.set_value("holiday_list", r.message); } - } - }) + }, + }); } }, @@ -22,35 +21,39 @@ frappe.ui.form.on("Workstation", { frm.call({ method: "set_data_based_on_workstation_type", doc: frm.doc, - callback: function(r) { + callback: function (r) { frm.refresh_fields(); - } - }) + }, + }); } - } + }, }); -frappe.tour['Workstation'] = [ +frappe.tour["Workstation"] = [ { fieldname: "workstation_name", title: "Workstation Name", - description: __("You can set it as a machine name or operation type. For example, stiching machine 12") + description: __( + "You can set it as a machine name or operation type. For example, stiching machine 12" + ), }, { fieldname: "production_capacity", title: "Production Capacity", - description: __("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.") + description: __( + "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." + ), }, { fieldname: "holiday_list", title: "Holiday List", - description: __("A Holiday List can be added to exclude counting these days for the Workstation.") + description: __("A Holiday List can be added to exclude counting these days for the Workstation."), }, { fieldname: "working_hours", title: "Working Hours", - description: __("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.") + description: __( + "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." + ), }, - - ]; diff --git a/erpnext/manufacturing/doctype/workstation/workstation_list.js b/erpnext/manufacturing/doctype/workstation/workstation_list.js index 6a89d21e1ef..77af49acf19 100644 --- a/erpnext/manufacturing/doctype/workstation/workstation_list.js +++ b/erpnext/manufacturing/doctype/workstation/workstation_list.js @@ -1,5 +1,5 @@ /* eslint-disable */ -frappe.listview_settings['Workstation'] = { +frappe.listview_settings["Workstation"] = { // add_fields: ["status"], // filters:[["status","=", "Open"]] }; diff --git a/erpnext/manufacturing/doctype/workstation_type/workstation_type.js b/erpnext/manufacturing/doctype/workstation_type/workstation_type.js index 419fa6c10ad..f0b3ed36a5e 100644 --- a/erpnext/manufacturing/doctype/workstation_type/workstation_type.js +++ b/erpnext/manufacturing/doctype/workstation_type/workstation_type.js @@ -1,8 +1,7 @@ // Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Workstation Type', { +frappe.ui.form.on("Workstation Type", { // refresh: function(frm) { - // } }); diff --git a/erpnext/manufacturing/page/bom_comparison_tool/bom_comparison_tool.js b/erpnext/manufacturing/page/bom_comparison_tool/bom_comparison_tool.js index 1bcb1efdaad..fcb7e884ecc 100644 --- a/erpnext/manufacturing/page/bom_comparison_tool/bom_comparison_tool.js +++ b/erpnext/manufacturing/page/bom_comparison_tool/bom_comparison_tool.js @@ -1,12 +1,12 @@ -frappe.pages['bom-comparison-tool'].on_page_load = function(wrapper) { +frappe.pages["bom-comparison-tool"].on_page_load = function (wrapper) { var page = frappe.ui.make_app_page({ parent: wrapper, - title: __('BOM Comparison Tool'), - single_column: true + title: __("BOM Comparison Tool"), + single_column: true, }); new erpnext.BOMComparisonTool(page); -} +}; erpnext.BOMComparisonTool = class BOMComparisonTool { constructor(page) { @@ -18,45 +18,45 @@ erpnext.BOMComparisonTool = class BOMComparisonTool { this.form = new frappe.ui.FieldGroup({ fields: [ { - label: __('BOM 1'), - fieldname: 'name1', - fieldtype: 'Link', - options: 'BOM', + label: __("BOM 1"), + fieldname: "name1", + fieldtype: "Link", + options: "BOM", change: () => this.fetch_and_render(), get_query: () => { return { filters: { - "name": ["not in", [this.form.get_value("name2") || ""]] - } - } - } + name: ["not in", [this.form.get_value("name2") || ""]], + }, + }; + }, }, { - fieldtype: 'Column Break' + fieldtype: "Column Break", }, { - label: __('BOM 2'), - fieldname: 'name2', - fieldtype: 'Link', - options: 'BOM', + label: __("BOM 2"), + fieldname: "name2", + fieldtype: "Link", + options: "BOM", change: () => this.fetch_and_render(), get_query: () => { return { filters: { - "name": ["not in", [this.form.get_value("name1") || ""]] - } - } - } + name: ["not in", [this.form.get_value("name1") || ""]], + }, + }; + }, }, { - fieldtype: 'Section Break' + fieldtype: "Section Break", }, { - fieldtype: 'HTML', - fieldname: 'preview' - } + fieldtype: "HTML", + fieldname: "preview", + }, ], - body: this.page.body + body: this.page.body, }); this.form.make(); } @@ -64,33 +64,34 @@ erpnext.BOMComparisonTool = class BOMComparisonTool { fetch_and_render() { let { name1, name2 } = this.form.get_values(); if (!(name1 && name2)) { - this.form.get_field('preview').html(''); + this.form.get_field("preview").html(""); return; } // set working state - this.form.get_field('preview').html(` + this.form.get_field("preview").html(`
    ${__("Fetching...")}
    `); - frappe.call('erpnext.manufacturing.doctype.bom.bom.get_bom_diff', { - bom1: name1, - bom2: name2 - }).then(r => { - let diff = r.message; - frappe.model.with_doctype('BOM', () => { - this.render('BOM', name1, name2, diff); + frappe + .call("erpnext.manufacturing.doctype.bom.bom.get_bom_diff", { + bom1: name1, + bom2: name2, + }) + .then((r) => { + let diff = r.message; + frappe.model.with_doctype("BOM", () => { + this.render("BOM", name1, name2, diff); + }); }); - }); } render(doctype, name1, name2, diff) { - let change_html = (title, doctype, changed) => { let values_changed = this.get_changed_values(doctype, changed) - .map(change => { + .map((change) => { let [fieldname, value1, value2] = change; return `
    @@ -100,14 +101,14 @@ erpnext.BOMComparisonTool = class BOMComparisonTool { `; }) - .join(''); + .join(""); return `

    ${title}

    - ${__('Notes')} + ${__("Notes")}

    • - ${__("Loyalty Points will be calculated from the spent done (via the Sales Invoice), based on collection factor mentioned.")} + ${__( + "Loyalty Points will be calculated from the spent done (via the Sales Invoice), based on collection factor mentioned." + )}
    • - ${__("There can be multiple tiered collection factor based on the total spent. But the conversion factor for redemption will always be same for all the tier.")} + ${__( + "There can be multiple tiered collection factor based on the total spent. But the conversion factor for redemption will always be same for all the tier." + )}
    • - ${__("In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent")} + ${__( + "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent" + )}
    • ${__("If unlimited expiry for the Loyalty Points, keep the Expiry Duration empty or 0.")}
    • - ${__("If Auto Opt In is checked, then the customers will be automatically linked with the concerned Loyalty Program (on save)")} + ${__( + "If Auto Opt In is checked, then the customers will be automatically linked with the concerned Loyalty Program (on save)" + )}
    • ${__("One customer can be part of only single Loyalty Program.")} @@ -37,14 +44,14 @@ frappe.ui.form.on('Loyalty Program', { set_field_options("loyalty_program_help", help_content); }, - onload: function(frm) { - frm.set_query("expense_account", function(doc) { + onload: function (frm) { + frm.set_query("expense_account", function (doc) { return { filters: { - "root_type": "Expense", - 'is_group': 0, - 'company': doc.company - } + root_type: "Expense", + is_group: 0, + company: doc.company, + }, }; }); @@ -52,13 +59,15 @@ frappe.ui.form.on('Loyalty Program', { erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); }, - refresh: function(frm) { + refresh: function (frm) { if (frm.doc.loyalty_program_type === "Single Tier Program" && frm.doc.collection_rules.length > 1) { - frappe.throw(__("Please select the Multiple Tier Program type for more than one collection rules.")); + frappe.throw( + __("Please select the Multiple Tier Program type for more than one collection rules.") + ); } }, - company: function(frm) { + company: function (frm) { erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); - } + }, }); diff --git a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.js b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.js index 103fa96d02d..25d437f154f 100644 --- a/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.js +++ b/erpnext/accounts/doctype/mode_of_payment/mode_of_payment.js @@ -1,16 +1,16 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.ui.form.on('Mode of Payment', { - setup: function(frm) { - frm.set_query("default_account", "accounts", function(doc, cdt, cdn) { +frappe.ui.form.on("Mode of Payment", { + setup: function (frm) { + frm.set_query("default_account", "accounts", function (doc, cdt, cdn) { let d = locals[cdt][cdn]; return { filters: [ - ['Account', 'account_type', 'in', 'Bank, Cash, Receivable'], - ['Account', 'is_group', '=', 0], - ['Account', 'company', '=', d.company] - ] + ["Account", "account_type", "in", "Bank, Cash, Receivable"], + ["Account", "is_group", "=", 0], + ["Account", "company", "=", d.company], + ], }; }); }, diff --git a/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.js b/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.js index 569f0084c6a..3383534f59e 100644 --- a/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.js +++ b/erpnext/accounts/doctype/monthly_distribution/monthly_distribution.js @@ -1,16 +1,16 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.ui.form.on('Monthly Distribution', { +frappe.ui.form.on("Monthly Distribution", { onload(frm) { - if(frm.doc.__islocal) { - return frm.call('get_months').then(() => { - frm.refresh_field('percentages'); + if (frm.doc.__islocal) { + return frm.call("get_months").then(() => { + frm.refresh_field("percentages"); }); } }, refresh(frm) { - frm.toggle_display('distribution_id', frm.doc.__islocal); - } + frm.toggle_display("distribution_id", frm.doc.__islocal); + }, }); 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 88867d11bb8..f1efba8a954 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 @@ -1,48 +1,52 @@ // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Opening Invoice Creation Tool', { - setup: function(frm) { - frm.set_query('party_type', 'invoices', function(doc, cdt, cdn) { +frappe.ui.form.on("Opening Invoice Creation Tool", { + setup: function (frm) { + frm.set_query("party_type", "invoices", function (doc, cdt, cdn) { return { filters: { - 'name': ['in', 'Customer, Supplier'] - } + name: ["in", "Customer, Supplier"], + }, }; }); if (frm.doc.company) { - frm.trigger('setup_company_filters'); + frm.trigger("setup_company_filters"); } - frappe.realtime.on('opening_invoice_creation_progress', data => { + frappe.realtime.on("opening_invoice_creation_progress", (data) => { if (!frm.doc.import_in_progress) { frm.dashboard.reset(); frm.doc.import_in_progress = true; } if (data.count == data.total) { - setTimeout(() => { - frm.doc.import_in_progress = false; - frm.clear_table("invoices"); - frm.refresh_fields(); - frm.page.clear_indicator(); - frm.dashboard.hide_progress(); - frappe.msgprint(__("Opening {0} Invoices created", [frm.doc.invoice_type])); - }, 1500, data.title); + setTimeout( + () => { + frm.doc.import_in_progress = false; + frm.clear_table("invoices"); + frm.refresh_fields(); + frm.page.clear_indicator(); + frm.dashboard.hide_progress(); + frappe.msgprint(__("Opening {0} Invoices created", [frm.doc.invoice_type])); + }, + 1500, + data.title + ); return; } frm.dashboard.show_progress(data.title, (data.count / data.total) * 100, data.message); - frm.page.set_indicator(__('In Progress'), 'orange'); + frm.page.set_indicator(__("In Progress"), "orange"); }); erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); }, - refresh: function(frm) { + refresh: function (frm) { frm.disable_save(); !frm.doc.import_in_progress && frm.trigger("make_dashboard"); - frm.page.set_primary_action(__('Create Invoices'), () => { + frm.page.set_primary_action(__("Create Invoices"), () => { let btn_primary = frm.page.btn_primary.get(0); return frm.call({ doc: frm.doc, @@ -58,100 +62,98 @@ frappe.ui.form.on('Opening Invoice Creation Tool', { } }, - setup_company_filters: function(frm) { - frm.set_query('cost_center', 'invoices', function(doc, cdt, cdn) { + setup_company_filters: function (frm) { + frm.set_query("cost_center", "invoices", function (doc, cdt, cdn) { return { filters: { - 'company': doc.company - } + company: doc.company, + }, }; }); - frm.set_query('cost_center', function(doc) { + frm.set_query("cost_center", function (doc) { return { filters: { - 'company': doc.company - } + company: doc.company, + }, }; }); - frm.set_query('temporary_opening_account', 'invoices', function(doc, cdt, cdn) { + frm.set_query("temporary_opening_account", "invoices", function (doc, cdt, cdn) { return { filters: { - 'company': doc.company - } - } + company: doc.company, + }, + }; }); }, - company: function(frm) { + company: function (frm) { if (frm.doc.company) { - - frm.trigger('setup_company_filters'); + frm.trigger("setup_company_filters"); frappe.call({ - method: 'erpnext.accounts.doctype.opening_invoice_creation_tool.opening_invoice_creation_tool.get_temporary_opening_account', + method: "erpnext.accounts.doctype.opening_invoice_creation_tool.opening_invoice_creation_tool.get_temporary_opening_account", args: { - company: frm.doc.company + company: frm.doc.company, }, callback: (r) => { if (r.message) { frm.doc.__onload.temporary_opening_account = r.message; - frm.trigger('update_invoice_table'); + frm.trigger("update_invoice_table"); } - } - }) + }, + }); } erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); }, - invoice_type: function(frm) { + invoice_type: function (frm) { $.each(frm.doc.invoices, (idx, row) => { - row.party_type = frm.doc.invoice_type == "Sales"? "Customer": "Supplier"; + row.party_type = frm.doc.invoice_type == "Sales" ? "Customer" : "Supplier"; row.party = ""; }); frm.refresh_fields(); }, - make_dashboard: function(frm) { + make_dashboard: function (frm) { let max_count = frm.doc.__onload.max_count; let opening_invoices_summary = frm.doc.__onload.opening_invoices_summary; - if(!$.isEmptyObject(opening_invoices_summary)) { + if (!$.isEmptyObject(opening_invoices_summary)) { let section = frm.dashboard.add_section( - frappe.render_template('opening_invoice_creation_tool_dashboard', { + frappe.render_template("opening_invoice_creation_tool_dashboard", { data: opening_invoices_summary, - max_count: max_count + max_count: max_count, }), __("Opening Invoices Summary") ); - section.on('click', '.invoice-link', function() { - let doctype = $(this).attr('data-type'); - let company = $(this).attr('data-company'); - frappe.set_route('List', doctype, - {'is_opening': 'Yes', 'company': company, 'docstatus': 1}); + section.on("click", ".invoice-link", function () { + let doctype = $(this).attr("data-type"); + let company = $(this).attr("data-company"); + frappe.set_route("List", doctype, { is_opening: "Yes", company: company, docstatus: 1 }); }); frm.dashboard.show(); } }, - update_invoice_table: function(frm) { + update_invoice_table: function (frm) { $.each(frm.doc.invoices, (idx, row) => { if (!row.temporary_opening_account) { row.temporary_opening_account = frm.doc.__onload.temporary_opening_account; } - if(!row.cost_center) { + if (!row.cost_center) { row.cost_center = frm.doc.cost_center; } - row.party_type = frm.doc.invoice_type == "Sales"? "Customer": "Supplier"; + row.party_type = frm.doc.invoice_type == "Sales" ? "Customer" : "Supplier"; }); - } + }, }); -frappe.ui.form.on('Opening Invoice Creation Tool Item', { +frappe.ui.form.on("Opening Invoice Creation Tool Item", { invoices_add: (frm) => { - frm.trigger('update_invoice_table'); - } + frm.trigger("update_invoice_table"); + }, }); diff --git a/erpnext/accounts/doctype/party_link/party_link.js b/erpnext/accounts/doctype/party_link/party_link.js index 6da9291d64d..dfa02c4be9a 100644 --- a/erpnext/accounts/doctype/party_link/party_link.js +++ b/erpnext/accounts/doctype/party_link/party_link.js @@ -1,33 +1,34 @@ // Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Party Link', { - refresh: function(frm) { - frm.set_query('primary_role', () => { +frappe.ui.form.on("Party Link", { + refresh: function (frm) { + frm.set_query("primary_role", () => { return { filters: { - name: ['in', ['Customer', 'Supplier']] - } + name: ["in", ["Customer", "Supplier"]], + }, }; }); - frm.set_query('secondary_role', () => { - let party_types = Object.keys(frappe.boot.party_account_types) - .filter(p => p != frm.doc.primary_role); + frm.set_query("secondary_role", () => { + let party_types = Object.keys(frappe.boot.party_account_types).filter( + (p) => p != frm.doc.primary_role + ); return { filters: { - name: ['in', party_types] - } + name: ["in", party_types], + }, }; }); }, primary_role(frm) { - frm.set_value('primary_party', ''); - frm.set_value('secondary_role', ''); + frm.set_value("primary_party", ""); + frm.set_value("secondary_role", ""); }, secondary_role(frm) { - frm.set_value('secondary_party', ''); - } + frm.set_value("secondary_party", ""); + }, }); diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry_list.js b/erpnext/accounts/doctype/payment_entry/payment_entry_list.js index 2d76fe69ef9..6974e58c78c 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry_list.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry_list.js @@ -1,14 +1,13 @@ -frappe.listview_settings['Payment Entry'] = { - - onload: function(listview) { +frappe.listview_settings["Payment Entry"] = { + onload: function (listview) { if (listview.page.fields_dict.party_type) { - listview.page.fields_dict.party_type.get_query = function() { + listview.page.fields_dict.party_type.get_query = function () { return { - "filters": { - "name": ["in", Object.keys(frappe.boot.party_account_types)], - } + filters: { + name: ["in", Object.keys(frappe.boot.party_account_types)], + }, }; }; } - } + }, }; diff --git a/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.js b/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.js index aff067eab89..f15c2bdab21 100644 --- a/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.js +++ b/erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.js @@ -1,11 +1,11 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.ui.form.on('Payment Gateway Account', { +frappe.ui.form.on("Payment Gateway Account", { refresh(frm) { erpnext.utils.check_payments_app(); - if(!frm.doc.__islocal) { - frm.set_df_property('payment_gateway', 'read_only', 1); + if (!frm.doc.__islocal) { + frm.set_df_property("payment_gateway", "read_only", 1); } - } + }, }); diff --git a/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.js b/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.js index 5a7be8e5ab2..07fe83177ba 100644 --- a/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.js +++ b/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.js @@ -1,8 +1,7 @@ // Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Payment Ledger Entry', { +frappe.ui.form.on("Payment Ledger Entry", { // refresh: function(frm) { - // } }); diff --git a/erpnext/accounts/doctype/payment_order/payment_order.js b/erpnext/accounts/doctype/payment_order/payment_order.js index 7d85d89c452..9d4988c19c9 100644 --- a/erpnext/accounts/doctype/payment_order/payment_order.js +++ b/erpnext/accounts/doctype/payment_order/payment_order.js @@ -1,61 +1,69 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Payment Order', { - setup: function(frm) { - frm.set_query("company_bank_account", function() { +frappe.ui.form.on("Payment Order", { + setup: function (frm) { + frm.set_query("company_bank_account", function () { return { filters: { - "is_company_account":1 - } - } + is_company_account: 1, + }, + }; }); - frm.set_df_property('references', 'cannot_add_rows', true); + frm.set_df_property("references", "cannot_add_rows", true); }, - refresh: function(frm) { + refresh: function (frm) { if (frm.doc.docstatus == 0) { - frm.add_custom_button(__('Payment Request'), function() { - frm.trigger("get_from_payment_request"); - }, __("Get Payments from")); + frm.add_custom_button( + __("Payment Request"), + function () { + frm.trigger("get_from_payment_request"); + }, + __("Get Payments from") + ); - frm.add_custom_button(__('Payment Entry'), function() { - frm.trigger("get_from_payment_entry"); - }, __("Get Payments from")); + frm.add_custom_button( + __("Payment Entry"), + function () { + frm.trigger("get_from_payment_entry"); + }, + __("Get Payments from") + ); - frm.trigger('remove_button'); + frm.trigger("remove_button"); } // payment Entry - if (frm.doc.docstatus===1 && frm.doc.payment_order_type==='Payment Request') { - frm.add_custom_button(__('Create Payment Entries'), function() { + if (frm.doc.docstatus === 1 && frm.doc.payment_order_type === "Payment Request") { + frm.add_custom_button(__("Create Payment Entries"), function () { frm.trigger("make_payment_records"); }); } }, - remove_row_if_empty: function(frm) { + remove_row_if_empty: function (frm) { // remove if first row is empty if (frm.doc.references.length > 0 && !frm.doc.references[0].reference_name) { frm.doc.references = []; } }, - remove_button: function(frm) { + remove_button: function (frm) { // remove custom button of order type that is not imported let label = ["Payment Request", "Payment Entry"]; if (frm.doc.references.length > 0 && frm.doc.payment_order_type) { - label = label.reduce(x => { - x!= frm.doc.payment_order_type; + label = label.reduce((x) => { + x != frm.doc.payment_order_type; return x; }); frm.remove_custom_button(label, "Get from"); } }, - get_from_payment_entry: function(frm) { + get_from_payment_entry: function (frm) { frm.trigger("remove_row_if_empty"); erpnext.utils.map_current_doc({ method: "erpnext.accounts.doctype.payment_entry.payment_entry.make_payment_order", @@ -63,7 +71,7 @@ frappe.ui.form.on('Payment Order', { target: frm, date_field: "posting_date", setters: { - party: frm.doc.supplier || "" + party: frm.doc.supplier || "", }, get_query_filters: { bank: frm.doc.bank, @@ -71,70 +79,79 @@ frappe.ui.form.on('Payment Order', { payment_type: ["!=", "Receive"], bank_account: frm.doc.company_bank_account, paid_from: frm.doc.account, - payment_order_status: ["=", "Initiated"] - } + payment_order_status: ["=", "Initiated"], + }, }); }, - get_from_payment_request: function(frm) { + get_from_payment_request: function (frm) { frm.trigger("remove_row_if_empty"); erpnext.utils.map_current_doc({ method: "erpnext.accounts.doctype.payment_request.payment_request.make_payment_order", source_doctype: "Payment Request", target: frm, setters: { - party: frm.doc.supplier || "" + party: frm.doc.supplier || "", }, get_query_filters: { bank: frm.doc.bank, docstatus: 1, status: ["=", "Initiated"], - } + }, }); }, - make_payment_records: function(frm){ + make_payment_records: function (frm) { var dialog = new frappe.ui.Dialog({ title: __("For Supplier"), fields: [ - {"fieldtype": "Link", "label": __("Supplier"), "fieldname": "supplier", "options":"Supplier", - "get_query": function () { + { + fieldtype: "Link", + label: __("Supplier"), + fieldname: "supplier", + options: "Supplier", + get_query: function () { return { - query:"erpnext.accounts.doctype.payment_order.payment_order.get_supplier_query", - filters: {'parent': frm.doc.name} - } - }, "reqd": 1 + query: "erpnext.accounts.doctype.payment_order.payment_order.get_supplier_query", + filters: { parent: frm.doc.name }, + }; + }, + reqd: 1, }, - {"fieldtype": "Link", "label": __("Mode of Payment"), "fieldname": "mode_of_payment", "options":"Mode of Payment", - "get_query": function () { + { + fieldtype: "Link", + label: __("Mode of Payment"), + fieldname: "mode_of_payment", + options: "Mode of Payment", + get_query: function () { return { - query:"erpnext.accounts.doctype.payment_order.payment_order.get_mop_query", - filters: {'parent': frm.doc.name} - } - } - } - ] + query: "erpnext.accounts.doctype.payment_order.payment_order.get_mop_query", + filters: { parent: frm.doc.name }, + }; + }, + }, + ], }); - dialog.set_primary_action(__("Submit"), function() { + dialog.set_primary_action(__("Submit"), function () { var args = dialog.get_values(); - if(!args) return; + if (!args) return; return frappe.call({ method: "erpnext.accounts.doctype.payment_order.payment_order.make_payment_records", args: { - "name": me.frm.doc.name, - "supplier": args.supplier, - "mode_of_payment": args.mode_of_payment + name: me.frm.doc.name, + supplier: args.supplier, + mode_of_payment: args.mode_of_payment, }, freeze: true, - callback: function(r) { + callback: function (r) { dialog.hide(); frm.refresh(); - } - }) - }) + }, + }); + }); dialog.show(); }, diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js index 16d1839679c..1e37f08bc77 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js @@ -2,85 +2,82 @@ // For license information, please see license.txt frappe.provide("erpnext.accounts"); -erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationController extends frappe.ui.form.Controller { +erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationController extends ( + frappe.ui.form.Controller +) { onload() { - const default_company = frappe.defaults.get_default('company'); - this.frm.set_value('company', default_company); + const default_company = frappe.defaults.get_default("company"); + this.frm.set_value("company", default_company); - this.frm.set_value('party_type', ''); - this.frm.set_value('party', ''); - this.frm.set_value('receivable_payable_account', ''); + this.frm.set_value("party_type", ""); + this.frm.set_value("party", ""); + this.frm.set_value("receivable_payable_account", ""); this.frm.set_query("party_type", () => { - return { - "filters": { - "name": ["in", Object.keys(frappe.boot.party_account_types)], - } - } - }); - - this.frm.set_query('receivable_payable_account', () => { return { filters: { - "company": this.frm.doc.company, - "is_group": 0, - "account_type": frappe.boot.party_account_types[this.frm.doc.party_type] - } + name: ["in", Object.keys(frappe.boot.party_account_types)], + }, }; }); - this.frm.set_query('bank_cash_account', () => { + this.frm.set_query("receivable_payable_account", () => { return { - filters:[ - ['Account', 'company', '=', this.frm.doc.company], - ['Account', 'is_group', '=', 0], - ['Account', 'account_type', 'in', ['Bank', 'Cash']] - ] + filters: { + company: this.frm.doc.company, + is_group: 0, + account_type: frappe.boot.party_account_types[this.frm.doc.party_type], + }, + }; + }); + + this.frm.set_query("bank_cash_account", () => { + return { + filters: [ + ["Account", "company", "=", this.frm.doc.company], + ["Account", "is_group", "=", 0], + ["Account", "account_type", "in", ["Bank", "Cash"]], + ], }; }); this.frm.set_query("cost_center", () => { return { - "filters": { - "company": this.frm.doc.company, - "is_group": 0 - } - } + filters: { + company: this.frm.doc.company, + is_group: 0, + }, + }; }); } refresh() { this.frm.disable_save(); - this.frm.set_df_property('invoices', 'cannot_delete_rows', true); - this.frm.set_df_property('payments', 'cannot_delete_rows', true); - this.frm.set_df_property('allocation', 'cannot_delete_rows', true); - - this.frm.set_df_property('invoices', 'cannot_add_rows', true); - this.frm.set_df_property('payments', 'cannot_add_rows', true); - this.frm.set_df_property('allocation', 'cannot_add_rows', true); + this.frm.set_df_property("invoices", "cannot_delete_rows", true); + this.frm.set_df_property("payments", "cannot_delete_rows", true); + this.frm.set_df_property("allocation", "cannot_delete_rows", true); + this.frm.set_df_property("invoices", "cannot_add_rows", true); + this.frm.set_df_property("payments", "cannot_add_rows", true); + this.frm.set_df_property("allocation", "cannot_add_rows", true); if (this.frm.doc.receivable_payable_account) { - this.frm.add_custom_button(__('Get Unreconciled Entries'), () => + this.frm.add_custom_button(__("Get Unreconciled Entries"), () => this.frm.trigger("get_unreconciled_entries") ); - this.frm.change_custom_button_type('Get Unreconciled Entries', null, 'primary'); + this.frm.change_custom_button_type("Get Unreconciled Entries", null, "primary"); } if (this.frm.doc.invoices.length && this.frm.doc.payments.length) { - this.frm.add_custom_button(__('Allocate'), () => - this.frm.trigger("allocate") - ); - this.frm.change_custom_button_type('Allocate', null, 'primary'); - this.frm.change_custom_button_type('Get Unreconciled Entries', null, 'default'); + this.frm.add_custom_button(__("Allocate"), () => this.frm.trigger("allocate")); + this.frm.change_custom_button_type("Allocate", null, "primary"); + this.frm.change_custom_button_type("Get Unreconciled Entries", null, "default"); } if (this.frm.doc.allocation.length) { - this.frm.add_custom_button(__('Reconcile'), () => - this.frm.trigger("reconcile") - ); - this.frm.change_custom_button_type('Reconcile', null, 'primary'); - this.frm.change_custom_button_type('Get Unreconciled Entries', null, 'default'); - this.frm.change_custom_button_type('Allocate', null, 'default'); + this.frm.add_custom_button(__("Reconcile"), () => this.frm.trigger("reconcile")); + this.frm.change_custom_button_type("Reconcile", null, "primary"); + this.frm.change_custom_button_type("Get Unreconciled Entries", null, "default"); + this.frm.change_custom_button_type("Allocate", null, "default"); } this.frm.trigger("set_query_for_dimension_filters"); @@ -89,31 +86,39 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo if (this.frm.doc.receivable_payable_account) { this.frm.call({ doc: this.frm.doc, - method: 'is_auto_process_enabled', + method: "is_auto_process_enabled", callback: (r) => { if (r.message) { - this.frm.call({ - 'method': "erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.is_any_doc_running", - "args": { - for_filter: { - company: this.frm.doc.company, - party_type: this.frm.doc.party_type, - party: this.frm.doc.party, - receivable_payable_account: this.frm.doc.receivable_payable_account + this.frm + .call({ + method: "erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.is_any_doc_running", + args: { + for_filter: { + company: this.frm.doc.company, + party_type: this.frm.doc.party_type, + party: this.frm.doc.party, + receivable_payable_account: this.frm.doc.receivable_payable_account, + }, + }, + }) + .then((r) => { + if (r.message) { + let doc_link = frappe.utils.get_form_link( + "Process Payment Reconciliation", + r.message, + true + ); + let msg = __( + "Payment Reconciliation Job: {0} is running for this party. Can't reconcile now.", + [doc_link] + ); + this.frm.dashboard.add_comment(msg, "yellow"); } - } - }).then(r => { - if (r.message) { - let doc_link = frappe.utils.get_form_link("Process Payment Reconciliation", r.message, true); - let msg = __("Payment Reconciliation Job: {0} is running for this party. Can't reconcile now.", [doc_link]); - this.frm.dashboard.add_comment(msg, "yellow"); - } - }); + }); } - } + }, }); } - } set_query_for_dimension_filters() { frappe.call({ @@ -123,29 +128,29 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo }, callback: (r) => { if (!r.exc && r.message) { - r.message.forEach(x => { + r.message.forEach((x) => { this.frm.set_query(x.fieldname, () => { return { - 'filters': x.filters + filters: x.filters, }; }); }); } - } + }, }); } company() { - this.frm.set_value('party', ''); - this.frm.set_value('receivable_payable_account', ''); + this.frm.set_value("party", ""); + this.frm.set_value("receivable_payable_account", ""); } party_type() { - this.frm.set_value('party', ''); + this.frm.set_value("party", ""); } party() { - this.frm.set_value('receivable_payable_account', ''); + this.frm.set_value("receivable_payable_account", ""); this.frm.trigger("clear_child_tables"); if (!this.frm.doc.receivable_payable_account && this.frm.doc.party_type && this.frm.doc.party) { @@ -154,15 +159,14 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo args: { company: this.frm.doc.company, party_type: this.frm.doc.party_type, - party: this.frm.doc.party + party: this.frm.doc.party, }, callback: (r) => { if (!r.exc && r.message) { this.frm.set_value("receivable_payable_account", r.message); } this.frm.refresh(); - - } + }, }); } } @@ -180,7 +184,6 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo this.frm.trigger("get_unreconciled_entries"); } - clear_child_tables() { this.frm.clear_table("invoices"); this.frm.clear_table("payments"); @@ -192,52 +195,52 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo this.frm.clear_table("allocation"); return this.frm.call({ doc: this.frm.doc, - method: 'get_unreconciled_entries', + method: "get_unreconciled_entries", callback: () => { if (!(this.frm.doc.payments.length || this.frm.doc.invoices.length)) { - frappe.throw({message: __("No Unreconciled Invoices and Payments found for this party and account")}); - } else if (!(this.frm.doc.invoices.length)) { - frappe.throw({message: __("No Outstanding Invoices found for this party")}); - } else if (!(this.frm.doc.payments.length)) { - frappe.throw({message: __("No Unreconciled Payments found for this party")}); + frappe.throw({ + message: __("No Unreconciled Invoices and Payments found for this party and account"), + }); + } else if (!this.frm.doc.invoices.length) { + frappe.throw({ message: __("No Outstanding Invoices found for this party") }); + } else if (!this.frm.doc.payments.length) { + frappe.throw({ message: __("No Unreconciled Payments found for this party") }); } this.frm.refresh(); - } + }, }); - } allocate() { let payments = this.frm.fields_dict.payments.grid.get_selected_children(); - if (!(payments.length)) { + if (!payments.length) { payments = this.frm.doc.payments; } let invoices = this.frm.fields_dict.invoices.grid.get_selected_children(); - if (!(invoices.length)) { + if (!invoices.length) { invoices = this.frm.doc.invoices; } return this.frm.call({ doc: this.frm.doc, - method: 'allocate_entries', + method: "allocate_entries", args: { payments: payments, - invoices: invoices + invoices: invoices, }, callback: () => { this.frm.refresh(); - } + }, }); } reconcile() { - var show_dialog = this.frm.doc.allocation.filter(d => d.difference_amount); + var show_dialog = this.frm.doc.allocation.filter((d) => d.difference_amount); if (show_dialog && show_dialog.length) { - this.data = []; const dialog = new frappe.ui.Dialog({ title: __("Select Difference Account"), - size: 'extra-large', + size: "extra-large", fields: [ { fieldname: "allocation", @@ -249,77 +252,89 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo get_data: () => { return this.data; }, - fields: [{ - fieldtype:'Data', - fieldname:"docname", - in_list_view: 1, - hidden: 1 - }, { - fieldtype:'Data', - fieldname:"reference_name", - label: __("Voucher No"), - in_list_view: 1, - read_only: 1 - }, { - fieldtype:'Date', - fieldname:"gain_loss_posting_date", - label: __("Posting Date"), - in_list_view: 1, - reqd: 1, - }, { - - fieldtype:'Link', - options: 'Account', - in_list_view: 1, - label: __("Difference Account"), - fieldname: 'difference_account', - reqd: 1, - get_query: () => { - return { - filters: { - company: this.frm.doc.company, - is_group: 0 - } - } - } - }, { - fieldtype:'Currency', - in_list_view: 1, - label: __("Difference Amount"), - fieldname: 'difference_amount', - read_only: 1 - }] + fields: [ + { + fieldtype: "Data", + fieldname: "docname", + in_list_view: 1, + hidden: 1, + }, + { + fieldtype: "Data", + fieldname: "reference_name", + label: __("Voucher No"), + in_list_view: 1, + read_only: 1, + }, + { + fieldtype: "Date", + fieldname: "gain_loss_posting_date", + label: __("Posting Date"), + in_list_view: 1, + reqd: 1, + }, + { + fieldtype: "Link", + options: "Account", + in_list_view: 1, + label: __("Difference Account"), + fieldname: "difference_account", + reqd: 1, + get_query: () => { + return { + filters: { + company: this.frm.doc.company, + is_group: 0, + }, + }; + }, + }, + { + fieldtype: "Currency", + in_list_view: 1, + label: __("Difference Amount"), + fieldname: "difference_amount", + read_only: 1, + }, + ], }, { - fieldtype: 'HTML', - options: " New Journal Entry will be posted for the difference amount " - } + fieldtype: "HTML", + options: " New Journal Entry will be posted for the difference amount ", + }, ], primary_action: () => { const args = dialog.get_values()["allocation"]; - args.forEach(d => { - frappe.model.set_value("Payment Reconciliation Allocation", d.docname, - "difference_account", d.difference_account); - frappe.model.set_value("Payment Reconciliation Allocation", d.docname, - "gain_loss_posting_date", d.gain_loss_posting_date); - + args.forEach((d) => { + frappe.model.set_value( + "Payment Reconciliation Allocation", + d.docname, + "difference_account", + d.difference_account + ); + frappe.model.set_value( + "Payment Reconciliation Allocation", + d.docname, + "gain_loss_posting_date", + d.gain_loss_posting_date + ); }); this.reconcile_payment_entries(); dialog.hide(); }, - primary_action_label: __('Reconcile Entries') + primary_action_label: __("Reconcile Entries"), }); - this.frm.doc.allocation.forEach(d => { + this.frm.doc.allocation.forEach((d) => { if (d.difference_amount) { dialog.fields_dict.allocation.df.data.push({ - 'docname': d.name, - 'reference_name': d.reference_name, - 'difference_amount': d.difference_amount, - 'difference_account': d.difference_account, - 'gain_loss_posting_date': d.gain_loss_posting_date + docname: d.name, + reference_name: d.reference_name, + difference_amount: d.difference_amount, + difference_account: d.difference_account, + gain_loss_posting_date: d.gain_loss_posting_date, }); } }); @@ -335,41 +350,39 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo reconcile_payment_entries() { return this.frm.call({ doc: this.frm.doc, - method: 'reconcile', + method: "reconcile", callback: () => { this.frm.clear_table("allocation"); this.frm.refresh(); - } + }, }); } }; -frappe.ui.form.on('Payment Reconciliation Allocation', { - allocated_amount: function(frm, cdt, cdn) { +frappe.ui.form.on("Payment Reconciliation Allocation", { + allocated_amount: function (frm, cdt, cdn) { let row = locals[cdt][cdn]; // filter invoice - let invoice = frm.doc.invoices.filter((x) => (x.invoice_number == row.invoice_number)); + let invoice = frm.doc.invoices.filter((x) => x.invoice_number == row.invoice_number); // filter payment - let payment = frm.doc.payments.filter((x) => (x.reference_name == row.reference_name)); + let payment = frm.doc.payments.filter((x) => x.reference_name == row.reference_name); frm.call({ doc: frm.doc, - method: 'calculate_difference_on_allocation_change', + method: "calculate_difference_on_allocation_change", args: { payment_entry: payment, invoice: invoice, - allocated_amount: row.allocated_amount + allocated_amount: row.allocated_amount, }, callback: (r) => { if (r.message) { row.difference_amount = r.message; frm.refresh(); } - } + }, }); - } + }, }); - - -extend_cscript(cur_frm.cscript, new erpnext.accounts.PaymentReconciliationController({frm: cur_frm})); +extend_cscript(cur_frm.cscript, new erpnext.accounts.PaymentReconciliationController({ frm: cur_frm })); diff --git a/erpnext/accounts/doctype/payment_request/payment_request.js b/erpnext/accounts/doctype/payment_request/payment_request.js index e9139120283..e5a6040c735 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.js +++ b/erpnext/accounts/doctype/payment_request/payment_request.js @@ -1,83 +1,95 @@ -cur_frm.add_fetch("payment_gateway_account", "payment_account", "payment_account") -cur_frm.add_fetch("payment_gateway_account", "payment_gateway", "payment_gateway") -cur_frm.add_fetch("payment_gateway_account", "message", "message") +cur_frm.add_fetch("payment_gateway_account", "payment_account", "payment_account"); +cur_frm.add_fetch("payment_gateway_account", "payment_gateway", "payment_gateway"); +cur_frm.add_fetch("payment_gateway_account", "message", "message"); frappe.ui.form.on("Payment Request", { - setup: function(frm) { - frm.set_query("party_type", function() { + setup: function (frm) { + frm.set_query("party_type", function () { return { query: "erpnext.setup.doctype.party_type.party_type.get_party_type", }; }); - } -}) + }, +}); -frappe.ui.form.on("Payment Request", "onload", function(frm, dt, dn){ +frappe.ui.form.on("Payment Request", "onload", function (frm, dt, dn) { if (frm.doc.reference_doctype) { frappe.call({ - method:"erpnext.accounts.doctype.payment_request.payment_request.get_print_format_list", - args: {"ref_doctype": frm.doc.reference_doctype}, - callback:function(r){ - set_field_options("print_format", r.message["print_format"]) - } - }) + method: "erpnext.accounts.doctype.payment_request.payment_request.get_print_format_list", + args: { ref_doctype: frm.doc.reference_doctype }, + callback: function (r) { + set_field_options("print_format", r.message["print_format"]); + }, + }); } -}) +}); -frappe.ui.form.on("Payment Request", "refresh", function(frm) { - if(frm.doc.payment_request_type == 'Inward' && frm.doc.payment_channel !== "Phone" && - !in_list(["Initiated", "Paid"], frm.doc.status) && !frm.doc.__islocal && frm.doc.docstatus==1){ - frm.add_custom_button(__('Resend Payment Email'), function(){ +frappe.ui.form.on("Payment Request", "refresh", function (frm) { + if ( + frm.doc.payment_request_type == "Inward" && + frm.doc.payment_channel !== "Phone" && + !in_list(["Initiated", "Paid"], frm.doc.status) && + !frm.doc.__islocal && + frm.doc.docstatus == 1 + ) { + frm.add_custom_button(__("Resend Payment Email"), function () { frappe.call({ method: "erpnext.accounts.doctype.payment_request.payment_request.resend_payment_email", - args: {"docname": frm.doc.name}, + args: { docname: frm.doc.name }, freeze: true, freeze_message: __("Sending"), - callback: function(r){ - if(!r.exc) { + callback: function (r) { + if (!r.exc) { frappe.msgprint(__("Message Sent")); } - } + }, }); }); } - if((!frm.doc.payment_gateway_account || frm.doc.payment_request_type == "Outward") && frm.doc.status == "Initiated") { - frm.add_custom_button(__('Create Payment Entry'), function(){ + if ( + (!frm.doc.payment_gateway_account || frm.doc.payment_request_type == "Outward") && + frm.doc.status == "Initiated" + ) { + frm.add_custom_button(__("Create Payment Entry"), function () { frappe.call({ method: "erpnext.accounts.doctype.payment_request.payment_request.make_payment_entry", - args: {"docname": frm.doc.name}, + args: { docname: frm.doc.name }, freeze: true, - callback: function(r){ - if(!r.exc) { + callback: function (r) { + if (!r.exc) { var doc = frappe.model.sync(r.message); frappe.set_route("Form", r.message.doctype, r.message.name); } - } + }, }); }).addClass("btn-primary"); } }); -frappe.ui.form.on("Payment Request", "is_a_subscription", function(frm) { +frappe.ui.form.on("Payment Request", "is_a_subscription", function (frm) { frm.toggle_reqd("payment_gateway_account", frm.doc.is_a_subscription); frm.toggle_reqd("subscription_plans", frm.doc.is_a_subscription); if (frm.doc.is_a_subscription && frm.doc.reference_doctype && frm.doc.reference_name) { frappe.call({ method: "erpnext.accounts.doctype.payment_request.payment_request.get_subscription_details", - args: {"reference_doctype": frm.doc.reference_doctype, "reference_name": frm.doc.reference_name}, + args: { reference_doctype: frm.doc.reference_doctype, reference_name: frm.doc.reference_name }, freeze: true, - callback: function(data){ - if(!data.exc) { - $.each(data.message || [], function(i, v){ - var d = frappe.model.add_child(frm.doc, "Subscription Plan Detail", "subscription_plans"); + callback: function (data) { + if (!data.exc) { + $.each(data.message || [], function (i, v) { + var d = frappe.model.add_child( + frm.doc, + "Subscription Plan Detail", + "subscription_plans" + ); d.qty = v.qty; d.plan = v.plan; }); frm.refresh_field("subscription_plans"); } - } + }, }); } }); diff --git a/erpnext/accounts/doctype/payment_request/payment_request_list.js b/erpnext/accounts/doctype/payment_request/payment_request_list.js index 85d729cd61c..183ca7c4584 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request_list.js +++ b/erpnext/accounts/doctype/payment_request/payment_request_list.js @@ -1,23 +1,19 @@ -frappe.listview_settings['Payment Request'] = { +frappe.listview_settings["Payment Request"] = { add_fields: ["status"], - get_indicator: function(doc) { - if(doc.status == "Draft") { + get_indicator: function (doc) { + if (doc.status == "Draft") { return [__("Draft"), "gray", "status,=,Draft"]; } - if(doc.status == "Requested") { + if (doc.status == "Requested") { return [__("Requested"), "green", "status,=,Requested"]; - } - else if(doc.status == "Initiated") { + } else if (doc.status == "Initiated") { return [__("Initiated"), "green", "status,=,Initiated"]; - } - else if(doc.status == "Partially Paid") { + } else if (doc.status == "Partially Paid") { return [__("Partially Paid"), "orange", "status,=,Partially Paid"]; - } - else if(doc.status == "Paid") { + } else if (doc.status == "Paid") { return [__("Paid"), "blue", "status,=,Paid"]; - } - else if(doc.status == "Cancelled") { + } else if (doc.status == "Cancelled") { return [__("Cancelled"), "red", "status,=,Cancelled"]; } - } -} + }, +}; diff --git a/erpnext/accounts/doctype/payment_term/payment_term.js b/erpnext/accounts/doctype/payment_term/payment_term.js index feecf93484c..c1c9d7c3258 100644 --- a/erpnext/accounts/doctype/payment_term/payment_term.js +++ b/erpnext/accounts/doctype/payment_term/payment_term.js @@ -1,22 +1,24 @@ // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Payment Term', { +frappe.ui.form.on("Payment Term", { onload(frm) { - frm.trigger('set_dynamic_description'); + frm.trigger("set_dynamic_description"); }, discount(frm) { - frm.trigger('set_dynamic_description'); + frm.trigger("set_dynamic_description"); }, discount_type(frm) { - frm.trigger('set_dynamic_description'); + frm.trigger("set_dynamic_description"); }, set_dynamic_description(frm) { if (frm.doc.discount) { - let description = __("{0}% of total invoice value will be given as discount.", [frm.doc.discount]); - if (frm.doc.discount_type == 'Amount') { + let description = __("{0}% of total invoice value will be given as discount.", [ + frm.doc.discount, + ]); + if (frm.doc.discount_type == "Amount") { description = __("{0} will be given as discount.", [fmt_money(frm.doc.discount)]); } frm.set_df_property("discount", "description", description); } - } + }, }); diff --git a/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.js b/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.js index 6046c13e146..b766c3b412b 100644 --- a/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.js +++ b/erpnext/accounts/doctype/payment_terms_template/payment_terms_template.js @@ -1,12 +1,18 @@ // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Payment Terms Template', { - refresh: function(frm) { - frm.fields_dict.terms.grid.toggle_reqd("payment_term", frm.doc.allocate_payment_based_on_payment_terms); +frappe.ui.form.on("Payment Terms Template", { + refresh: function (frm) { + frm.fields_dict.terms.grid.toggle_reqd( + "payment_term", + frm.doc.allocate_payment_based_on_payment_terms + ); }, - allocate_payment_based_on_payment_terms: function(frm) { - frm.fields_dict.terms.grid.toggle_reqd("payment_term", frm.doc.allocate_payment_based_on_payment_terms); - } + allocate_payment_based_on_payment_terms: function (frm) { + frm.fields_dict.terms.grid.toggle_reqd( + "payment_term", + frm.doc.allocate_payment_based_on_payment_terms + ); + }, }); 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 e923d4ed5e6..82d8cb37fe7 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.js +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.js @@ -1,38 +1,41 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.ui.form.on('Period Closing Voucher', { - onload: function(frm) { +frappe.ui.form.on("Period Closing Voucher", { + onload: function (frm) { if (!frm.doc.transaction_date) frm.doc.transaction_date = frappe.datetime.obj_to_str(new Date()); }, - setup: function(frm) { - frm.set_query("closing_account_head", function() { + setup: function (frm) { + frm.set_query("closing_account_head", function () { return { filters: [ - ['Account', 'company', '=', frm.doc.company], - ['Account', 'is_group', '=', '0'], - ['Account', 'freeze_account', '=', 'No'], - ['Account', 'root_type', 'in', 'Liability, Equity'] - ] - } + ["Account", "company", "=", frm.doc.company], + ["Account", "is_group", "=", "0"], + ["Account", "freeze_account", "=", "No"], + ["Account", "root_type", "in", "Liability, Equity"], + ], + }; }); }, - refresh: function(frm) { - if(frm.doc.docstatus > 0) { - frm.add_custom_button(__('Ledger'), function() { - frappe.route_options = { - "voucher_no": frm.doc.name, - "from_date": frm.doc.posting_date, - "to_date": moment(frm.doc.modified).format('YYYY-MM-DD'), - "company": frm.doc.company, - "group_by": "", - "show_cancelled_entries": frm.doc.docstatus === 2 - }; - frappe.set_route("query-report", "General Ledger"); - }, "fa fa-table"); + refresh: function (frm) { + if (frm.doc.docstatus > 0) { + frm.add_custom_button( + __("Ledger"), + function () { + frappe.route_options = { + voucher_no: frm.doc.name, + from_date: frm.doc.posting_date, + to_date: moment(frm.doc.modified).format("YYYY-MM-DD"), + company: frm.doc.company, + group_by: "", + show_cancelled_entries: frm.doc.docstatus === 2, + }; + frappe.set_route("query-report", "General Ledger"); + }, + "fa fa-table" + ); } - } - -}) + }, +}); 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 91e71e90dd8..715800a7c16 100644 --- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js +++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js @@ -1,36 +1,37 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('POS Closing Entry', { - onload: function(frm) { - frm.ignore_doctypes_on_cancel_all = ['POS Invoice Merge Log']; - frm.set_query("pos_profile", function(doc) { +frappe.ui.form.on("POS Closing Entry", { + onload: function (frm) { + frm.ignore_doctypes_on_cancel_all = ["POS Invoice Merge Log"]; + frm.set_query("pos_profile", function (doc) { return { - filters: { 'user': doc.user } + filters: { user: doc.user }, }; }); - frm.set_query("user", function(doc) { + frm.set_query("user", function (doc) { return { query: "erpnext.accounts.doctype.pos_closing_entry.pos_closing_entry.get_cashiers", - filters: { 'parent': doc.pos_profile } + filters: { parent: doc.pos_profile }, }; }); - frm.set_query("pos_opening_entry", function(doc) { - return { filters: { 'status': 'Open', 'docstatus': 1 } }; + frm.set_query("pos_opening_entry", function (doc) { + return { filters: { status: "Open", docstatus: 1 } }; }); - if (frm.doc.docstatus === 0 && !frm.doc.amended_from) frm.set_value("period_end_date", frappe.datetime.now_datetime()); + if (frm.doc.docstatus === 0 && !frm.doc.amended_from) + frm.set_value("period_end_date", frappe.datetime.now_datetime()); - frappe.realtime.on('closing_process_complete', async function(data) { + frappe.realtime.on("closing_process_complete", async function (data) { await frm.reload_doc(); - if (frm.doc.status == 'Failed' && frm.doc.error_message) { + if (frm.doc.status == "Failed" && frm.doc.error_message) { frappe.msgprint({ - title: __('POS Closing Failed'), + title: __("POS Closing Failed"), message: frm.doc.error_message, - indicator: 'orange', - clear: true + indicator: "orange", + clear: true, }); } }); @@ -47,23 +48,23 @@ frappe.ui.form.on('POS Closing Entry', { } }, - refresh: function(frm) { - if (frm.doc.docstatus == 1 && frm.doc.status == 'Failed') { + refresh: function (frm) { + if (frm.doc.docstatus == 1 && frm.doc.status == "Failed") { const issue = 'issue'; frm.dashboard.set_headline( - __('POS Closing failed while running in a background process. You can resolve the {0} and retry the process again.', [issue])); + __( + "POS Closing failed while running in a background process. You can resolve the {0} and retry the process again.", + [issue] + ) + ); - $('#jump_to_error').on('click', (e) => { + $("#jump_to_error").on("click", (e) => { e.preventDefault(); - frappe.utils.scroll_to( - cur_frm.get_field("error_message").$wrapper, - true, - 30 - ); + frappe.utils.scroll_to(cur_frm.get_field("error_message").$wrapper, true, 30); }); - frm.add_custom_button(__('Retry'), function () { - frm.call('retry', {}, () => { + frm.add_custom_button(__("Retry"), function () { + frm.call("retry", {}, () => { frm.reload_doc(); }); }); @@ -71,48 +72,54 @@ frappe.ui.form.on('POS Closing Entry', { }, pos_opening_entry(frm) { - if (frm.doc.pos_opening_entry && frm.doc.period_start_date && frm.doc.period_end_date && frm.doc.user) { + if ( + frm.doc.pos_opening_entry && + frm.doc.period_start_date && + frm.doc.period_end_date && + frm.doc.user + ) { reset_values(frm); frappe.run_serially([ () => frm.trigger("set_opening_amounts"), - () => frm.trigger("get_pos_invoices") + () => frm.trigger("get_pos_invoices"), ]); } }, set_opening_amounts(frm) { - return frappe.db.get_doc("POS Opening Entry", frm.doc.pos_opening_entry) + return frappe.db + .get_doc("POS Opening Entry", frm.doc.pos_opening_entry) .then(({ balance_details }) => { - balance_details.forEach(detail => { + balance_details.forEach((detail) => { frm.add_child("payment_reconciliation", { mode_of_payment: detail.mode_of_payment, opening_amount: detail.opening_amount, - expected_amount: detail.opening_amount + expected_amount: detail.opening_amount, }); - }) + }); }); }, get_pos_invoices(frm) { return frappe.call({ - method: 'erpnext.accounts.doctype.pos_closing_entry.pos_closing_entry.get_pos_invoices', + method: "erpnext.accounts.doctype.pos_closing_entry.pos_closing_entry.get_pos_invoices", args: { start: frappe.datetime.get_datetime_as_string(frm.doc.period_start_date), end: frappe.datetime.get_datetime_as_string(frm.doc.period_end_date), pos_profile: frm.doc.pos_profile, - user: frm.doc.user + user: frm.doc.user, }, callback: (r) => { let pos_docs = r.message; set_form_data(pos_docs, frm); refresh_fields(frm); set_html_data(frm); - } + }, }); }, - before_save: async function(frm) { - frappe.dom.freeze(__('Processing Sales! Please Wait...')); + before_save: async function (frm) { + frappe.dom.freeze(__("Processing Sales! Please Wait...")); frm.set_value("grand_total", 0); frm.set_value("net_total", 0); @@ -125,12 +132,12 @@ frappe.ui.form.on('POS Closing Entry', { await Promise.all([ frappe.call({ - method: 'erpnext.accounts.doctype.pos_closing_entry.pos_closing_entry.get_pos_invoices', + method: "erpnext.accounts.doctype.pos_closing_entry.pos_closing_entry.get_pos_invoices", args: { start: frappe.datetime.get_datetime_as_string(frm.doc.period_start_date), end: frappe.datetime.get_datetime_as_string(frm.doc.period_end_date), pos_profile: frm.doc.pos_profile, - user: frm.doc.user + user: frm.doc.user, }, callback: (r) => { let pos_invoices = r.message; @@ -143,22 +150,22 @@ frappe.ui.form.on('POS Closing Entry', { refresh_fields(frm); set_html_data(frm); } - } - }) - ]) + }, + }), + ]); frappe.dom.unfreeze(); - } + }, }); -frappe.ui.form.on('POS Closing Entry Detail', { +frappe.ui.form.on("POS Closing Entry Detail", { closing_amount: (frm, cdt, cdn) => { const row = locals[cdt][cdn]; frappe.model.set_value(cdt, cdn, "difference", flt(row.closing_amount - row.expected_amount)); - } -}) + }, +}); function set_form_data(data, frm) { - data.forEach(d => { + data.forEach((d) => { add_to_pos_transaction(d, frm); frm.doc.grand_total += flt(d.grand_total); frm.doc.net_total += flt(d.net_total); @@ -173,13 +180,15 @@ function add_to_pos_transaction(d, frm) { pos_invoice: d.name, posting_date: d.posting_date, grand_total: d.grand_total, - customer: d.customer - }) + customer: d.customer, + }); } function refresh_payments(d, frm) { - d.payments.forEach(p => { - const payment = frm.doc.payment_reconciliation.find(pay => pay.mode_of_payment === p.mode_of_payment); + d.payments.forEach((p) => { + const payment = frm.doc.payment_reconciliation.find( + (pay) => pay.mode_of_payment === p.mode_of_payment + ); if (p.account == d.account_for_change_amount) { p.amount -= flt(d.change_amount); } @@ -191,25 +200,25 @@ function refresh_payments(d, frm) { mode_of_payment: p.mode_of_payment, opening_amount: 0, expected_amount: p.amount, - closing_amount: 0 - }) + closing_amount: 0, + }); } - }) + }); } function refresh_taxes(d, frm) { - d.taxes.forEach(t => { - const tax = frm.doc.taxes.find(tx => tx.account_head === t.account_head && tx.rate === t.rate); + d.taxes.forEach((t) => { + const tax = frm.doc.taxes.find((tx) => tx.account_head === t.account_head && tx.rate === t.rate); if (tax) { tax.amount += flt(t.tax_amount); } else { frm.add_child("taxes", { account_head: t.account_head, rate: t.rate, - amount: t.tax_amount - }) + amount: t.tax_amount, + }); } - }) + }); } function reset_values(frm) { @@ -231,13 +240,13 @@ function refresh_fields(frm) { } function set_html_data(frm) { - if (frm.doc.docstatus === 1 && frm.doc.status == 'Submitted') { + if (frm.doc.docstatus === 1 && frm.doc.status == "Submitted") { frappe.call({ method: "get_payment_reconciliation_details", doc: frm.doc, callback: (r) => { frm.get_field("payment_reconciliation_details").$wrapper.html(r.message); - } + }, }); } } diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry_list.js b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry_list.js index cffeb4d5351..29f00fbff7b 100644 --- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry_list.js +++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry_list.js @@ -2,16 +2,15 @@ // License: GNU General Public License v3. See license.txt // render -frappe.listview_settings['POS Closing Entry'] = { - get_indicator: function(doc) { +frappe.listview_settings["POS Closing Entry"] = { + get_indicator: function (doc) { var status_color = { - "Draft": "red", - "Submitted": "blue", - "Queued": "orange", - "Failed": "red", - "Cancelled": "red" - + Draft: "red", + Submitted: "blue", + Queued: "orange", + Failed: "red", + Cancelled: "red", }; - return [__(doc.status), status_color[doc.status], "status,=,"+doc.status]; - } + return [__(doc.status), status_color[doc.status], "status,=," + doc.status]; + }, }; diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice_list.js b/erpnext/accounts/doctype/pos_invoice/pos_invoice_list.js index 2dbf2a4fcd3..0379932bb7a 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice_list.js +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice_list.js @@ -2,40 +2,47 @@ // License: GNU General Public License v3. See license.txt // render -frappe.listview_settings['POS Invoice'] = { - add_fields: ["customer", "customer_name", "base_grand_total", "outstanding_amount", "due_date", "company", - "currency", "is_return"], - get_indicator: function(doc) { +frappe.listview_settings["POS Invoice"] = { + add_fields: [ + "customer", + "customer_name", + "base_grand_total", + "outstanding_amount", + "due_date", + "company", + "currency", + "is_return", + ], + get_indicator: function (doc) { var status_color = { - "Draft": "red", - "Unpaid": "orange", - "Paid": "green", - "Submitted": "blue", - "Consolidated": "green", - "Return": "darkgrey", + Draft: "red", + Unpaid: "orange", + Paid: "green", + Submitted: "blue", + Consolidated: "green", + Return: "darkgrey", "Unpaid and Discounted": "orange", "Overdue and Discounted": "red", - "Overdue": "red" - + Overdue: "red", }; - return [__(doc.status), status_color[doc.status], "status,=,"+doc.status]; + return [__(doc.status), status_color[doc.status], "status,=," + doc.status]; }, right_column: "grand_total", - onload: function(me) { - me.page.add_action_item('Make Merge Log', function() { + onload: function (me) { + me.page.add_action_item("Make Merge Log", function () { const invoices = me.get_checked_items(); frappe.call({ method: "erpnext.accounts.doctype.pos_invoice.pos_invoice.make_merge_log", freeze: true, - args:{ - "invoices": invoices + args: { + invoices: invoices, }, callback: function (r) { if (r.message) { var doc = frappe.model.sync(r.message)[0]; frappe.set_route("Form", doc.doctype, doc.name); } - } + }, }); }); }, diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.js b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.js index 73c6290d7b0..8423987447e 100644 --- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.js +++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.js @@ -1,21 +1,21 @@ // Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('POS Invoice Merge Log', { - setup: function(frm) { - frm.set_query("pos_invoice", "pos_invoices", doc => { +frappe.ui.form.on("POS Invoice Merge Log", { + setup: function (frm) { + frm.set_query("pos_invoice", "pos_invoices", (doc) => { return { filters: { - 'docstatus': 1, - 'customer': doc.customer, - 'consolidated_invoice': '' - } - } + docstatus: 1, + customer: doc.customer, + consolidated_invoice: "", + }, + }; }); }, - merge_invoices_based_on: function(frm) { - frm.set_value('customer', ''); - frm.set_value('customer_group', ''); - } + merge_invoices_based_on: function (frm) { + frm.set_value("customer", ""); + frm.set_value("customer_group", ""); + }, }); diff --git a/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.js b/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.js index d23f348f04e..6a316d55e6a 100644 --- a/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.js +++ b/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.js @@ -1,56 +1,55 @@ // Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('POS Opening Entry', { +frappe.ui.form.on("POS Opening Entry", { setup(frm) { if (frm.doc.docstatus == 0) { - frm.trigger('set_posting_date_read_only'); - frm.set_value('period_start_date', frappe.datetime.now_datetime()); - frm.set_value('user', frappe.session.user); + frm.trigger("set_posting_date_read_only"); + frm.set_value("period_start_date", frappe.datetime.now_datetime()); + frm.set_value("user", frappe.session.user); } - frm.set_query("user", function(doc) { + frm.set_query("user", function (doc) { return { query: "erpnext.accounts.doctype.pos_closing_entry.pos_closing_entry.get_cashiers", - filters: { 'parent': doc.pos_profile } + filters: { parent: doc.pos_profile }, }; }); }, refresh(frm) { // set default posting date / time - if(frm.doc.docstatus == 0) { - if(!frm.doc.posting_date) { - frm.set_value('posting_date', frappe.datetime.nowdate()); + if (frm.doc.docstatus == 0) { + if (!frm.doc.posting_date) { + frm.set_value("posting_date", frappe.datetime.nowdate()); } - frm.trigger('set_posting_date_read_only'); + frm.trigger("set_posting_date_read_only"); } }, set_posting_date_read_only(frm) { - if(frm.doc.docstatus == 0 && frm.doc.set_posting_date) { - frm.set_df_property('posting_date', 'read_only', 0); + if (frm.doc.docstatus == 0 && frm.doc.set_posting_date) { + frm.set_df_property("posting_date", "read_only", 0); } else { - frm.set_df_property('posting_date', 'read_only', 1); + frm.set_df_property("posting_date", "read_only", 1); } }, set_posting_date(frm) { - frm.trigger('set_posting_date_read_only'); + frm.trigger("set_posting_date_read_only"); }, pos_profile: (frm) => { if (frm.doc.pos_profile) { - frappe.db.get_doc("POS Profile", frm.doc.pos_profile) - .then(({ payments }) => { - if (payments.length) { - frm.doc.balance_details = []; - payments.forEach(({ mode_of_payment }) => { - frm.add_child("balance_details", { mode_of_payment }); - }) - frm.refresh_field("balance_details"); - } - }); + frappe.db.get_doc("POS Profile", frm.doc.pos_profile).then(({ payments }) => { + if (payments.length) { + frm.doc.balance_details = []; + payments.forEach(({ mode_of_payment }) => { + frm.add_child("balance_details", { mode_of_payment }); + }); + frm.refresh_field("balance_details"); + } + }); } - } + }, }); diff --git a/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry_list.js b/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry_list.js index 1ad3c919b71..7ff9c4bbbc0 100644 --- a/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry_list.js +++ b/erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry_list.js @@ -2,15 +2,14 @@ // License: GNU General Public License v3. See license.txt // render -frappe.listview_settings['POS Opening Entry'] = { - get_indicator: function(doc) { +frappe.listview_settings["POS Opening Entry"] = { + get_indicator: function (doc) { var status_color = { - "Draft": "red", - "Open": "orange", - "Closed": "green", - "Cancelled": "red" - + Draft: "red", + Open: "orange", + Closed: "green", + Cancelled: "red", }; - return [__(doc.status), status_color[doc.status], "status,=,"+doc.status]; - } + return [__(doc.status), status_color[doc.status], "status,=," + doc.status]; + }, }; diff --git a/erpnext/accounts/doctype/pos_profile_user/pos_profile_user.js b/erpnext/accounts/doctype/pos_profile_user/pos_profile_user.js index f0884ebef5e..63416745394 100644 --- a/erpnext/accounts/doctype/pos_profile_user/pos_profile_user.js +++ b/erpnext/accounts/doctype/pos_profile_user/pos_profile_user.js @@ -1,6 +1,4 @@ // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('POS Profile User', { - -}); +frappe.ui.form.on("POS Profile User", {}); diff --git a/erpnext/accounts/doctype/pos_settings/pos_settings.js b/erpnext/accounts/doctype/pos_settings/pos_settings.js index 7d8f3562c8c..a2e5a57e5f7 100644 --- a/erpnext/accounts/doctype/pos_settings/pos_settings.js +++ b/erpnext/accounts/doctype/pos_settings/pos_settings.js @@ -1,57 +1,91 @@ // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -let search_fields_datatypes = ['Data', 'Link', 'Dynamic Link', 'Long Text', 'Select', 'Small Text', 'Text', 'Text Editor']; -let do_not_include_fields = ["naming_series", "item_code", "item_name", "stock_uom", "asset_naming_series", - "default_material_request_type", "valuation_method", "warranty_period", "weight_uom", "batch_number_series", - "serial_no_series", "purchase_uom", "customs_tariff_number", "sales_uom", "deferred_revenue_account", - "deferred_expense_account", "quality_inspection_template", "route", "slideshow", "website_image_alt", "thumbnail", - "web_long_description"] +let search_fields_datatypes = [ + "Data", + "Link", + "Dynamic Link", + "Long Text", + "Select", + "Small Text", + "Text", + "Text Editor", +]; +let do_not_include_fields = [ + "naming_series", + "item_code", + "item_name", + "stock_uom", + "asset_naming_series", + "default_material_request_type", + "valuation_method", + "warranty_period", + "weight_uom", + "batch_number_series", + "serial_no_series", + "purchase_uom", + "customs_tariff_number", + "sales_uom", + "deferred_revenue_account", + "deferred_expense_account", + "quality_inspection_template", + "route", + "slideshow", + "website_image_alt", + "thumbnail", + "web_long_description", +]; -frappe.ui.form.on('POS Settings', { - onload: function(frm) { +frappe.ui.form.on("POS Settings", { + onload: function (frm) { frm.trigger("get_invoice_fields"); frm.trigger("add_search_options"); }, - get_invoice_fields: function(frm) { + get_invoice_fields: function (frm) { frappe.model.with_doctype("POS Invoice", () => { - var fields = $.map(frappe.get_doc("DocType", "POS Invoice").fields, function(d) { - if (frappe.model.no_value_type.indexOf(d.fieldtype) === -1 || ['Button'].includes(d.fieldtype)) { - return { label: d.label + ' (' + d.fieldtype + ')', value: d.fieldname }; + var fields = $.map(frappe.get_doc("DocType", "POS Invoice").fields, function (d) { + if ( + frappe.model.no_value_type.indexOf(d.fieldtype) === -1 || + ["Button"].includes(d.fieldtype) + ) { + return { label: d.label + " (" + d.fieldtype + ")", value: d.fieldname }; } else { return null; } }); frm.fields_dict.invoice_fields.grid.update_docfield_property( - 'fieldname', 'options', [""].concat(fields) + "fieldname", + "options", + [""].concat(fields) ); }); - }, - add_search_options: function(frm) { + add_search_options: function (frm) { frappe.model.with_doctype("Item", () => { - var fields = $.map(frappe.get_doc("DocType", "Item").fields, function(d) { - if (search_fields_datatypes.includes(d.fieldtype) && !(do_not_include_fields.includes(d.fieldname))) { + var fields = $.map(frappe.get_doc("DocType", "Item").fields, function (d) { + if ( + search_fields_datatypes.includes(d.fieldtype) && + !do_not_include_fields.includes(d.fieldname) + ) { return [d.label]; } else { return null; } }); - fields.unshift(''); - frm.fields_dict.pos_search_fields.grid.update_docfield_property('field', 'options', fields); + fields.unshift(""); + frm.fields_dict.pos_search_fields.grid.update_docfield_property("field", "options", fields); }); - - } + }, }); frappe.ui.form.on("POS Search Fields", { - field: function(frm, doctype, name) { + field: function (frm, doctype, name) { var doc = frappe.get_doc(doctype, name); - var df = $.map(frappe.get_doc("DocType", "Item").fields, function(d) { + var df = $.map(frappe.get_doc("DocType", "Item").fields, function (d) { if (doc.field == d.label && search_fields_datatypes.includes(d.fieldtype)) { return d; } else { @@ -61,13 +95,13 @@ frappe.ui.form.on("POS Search Fields", { doc.fieldname = df.fieldname; frm.refresh_field("fields"); - } + }, }); frappe.ui.form.on("POS Field", { - fieldname: function(frm, doctype, name) { + fieldname: function (frm, doctype, name) { var doc = frappe.get_doc(doctype, name); - var df = $.map(frappe.get_doc("DocType", "POS Invoice").fields, function(d) { + var df = $.map(frappe.get_doc("DocType", "POS Invoice").fields, function (d) { return doc.fieldname == d.fieldname ? d : null; })[0]; @@ -77,5 +111,5 @@ frappe.ui.form.on("POS Field", { doc.fieldtype = df.fieldtype; doc.default_value = df.default; frm.refresh_field("fields"); - } + }, }); diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.js b/erpnext/accounts/doctype/pricing_rule/pricing_rule.js index 826758245a3..cd5b00e6ad0 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.js +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.js @@ -1,44 +1,43 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.ui.form.on('Pricing Rule', { - setup: function(frm) { - frm.fields_dict["for_price_list"].get_query = function(doc){ +frappe.ui.form.on("Pricing Rule", { + setup: function (frm) { + frm.fields_dict["for_price_list"].get_query = function (doc) { return { filters: { - 'selling': doc.selling, - 'buying': doc.buying, - 'currency': doc.currency - } + selling: doc.selling, + buying: doc.buying, + currency: doc.currency, + }, }; }; - ['items', 'item_groups', 'brands'].forEach(d => { - frm.fields_dict[d].grid.get_field('uom').get_query = function(doc, cdt, cdn){ + ["items", "item_groups", "brands"].forEach((d) => { + frm.fields_dict[d].grid.get_field("uom").get_query = function (doc, cdt, cdn) { var row = locals[cdt][cdn]; return { - query:"erpnext.accounts.doctype.pricing_rule.pricing_rule.get_item_uoms", - filters: {'value': row[frappe.scrub(doc.apply_on)], apply_on: doc.apply_on} - } + query: "erpnext.accounts.doctype.pricing_rule.pricing_rule.get_item_uoms", + filters: { value: row[frappe.scrub(doc.apply_on)], apply_on: doc.apply_on }, + }; }; - }) + }); }, - onload: function(frm) { - if(frm.doc.__islocal && !frm.doc.applicable_for && (frm.doc.customer || frm.doc.supplier)) { - if(frm.doc.customer) { + onload: function (frm) { + if (frm.doc.__islocal && !frm.doc.applicable_for && (frm.doc.customer || frm.doc.supplier)) { + if (frm.doc.customer) { frm.doc.applicable_for = "Customer"; - frm.doc.selling = 1 + frm.doc.selling = 1; } else { frm.doc.applicable_for = "Supplier"; - frm.doc.buying = 1 + frm.doc.buying = 1; } } }, - refresh: function(frm) { - var help_content = - ` + refresh: function (frm) { + var help_content = `

      @@ -97,61 +96,70 @@ frappe.ui.form.on('Pricing Rule', {

      `; - frm.set_df_property('pricing_rule_help', 'options', help_content); + frm.set_df_property("pricing_rule_help", "options", help_content); frm.events.set_options_for_applicable_for(frm); frm.trigger("toggle_reqd_apply_on"); }, - apply_on: function(frm) { + apply_on: function (frm) { frm.trigger("toggle_reqd_apply_on"); }, - toggle_reqd_apply_on: function(frm) { + toggle_reqd_apply_on: function (frm) { const fields = { - 'Item Code': 'items', - 'Item Group': 'item_groups', - 'Brand': 'brands' - } + "Item Code": "items", + "Item Group": "item_groups", + Brand: "brands", + }; for (var key in fields) { - frm.toggle_reqd(fields[key], - frm.doc.apply_on === key ? 1 : 0); + frm.toggle_reqd(fields[key], frm.doc.apply_on === key ? 1 : 0); } }, - rate_or_discount: function(frm) { - if(frm.doc.rate_or_discount == 'Rate') { - frm.set_value('for_price_list', ""); + rate_or_discount: function (frm) { + if (frm.doc.rate_or_discount == "Rate") { + frm.set_value("for_price_list", ""); } }, - selling: function(frm) { + selling: function (frm) { frm.events.set_options_for_applicable_for(frm); }, - buying: function(frm) { + buying: function (frm) { frm.events.set_options_for_applicable_for(frm); }, //Dynamically change the description based on type of margin - margin_type: function(frm){ - frm.set_df_property('margin_rate_or_amount', 'description', frm.doc.margin_type=='Percentage'?'In Percentage %':'In Amount'); + margin_type: function (frm) { + frm.set_df_property( + "margin_rate_or_amount", + "description", + frm.doc.margin_type == "Percentage" ? "In Percentage %" : "In Amount" + ); }, - set_options_for_applicable_for: function(frm) { + set_options_for_applicable_for: function (frm) { var options = [""]; var applicable_for = frm.doc.applicable_for; - if(frm.doc.selling) { - options = $.merge(options, ["Customer", "Customer Group", "Territory", "Sales Partner", "Campaign"]); + if (frm.doc.selling) { + options = $.merge(options, [ + "Customer", + "Customer Group", + "Territory", + "Sales Partner", + "Campaign", + ]); } - if(frm.doc.buying) { + if (frm.doc.buying) { $.merge(options, ["Supplier", "Supplier Group"]); } set_field_options("applicable_for", options.join("\n")); - if(!in_list(options, applicable_for)) applicable_for = null; + if (!in_list(options, applicable_for)) applicable_for = null; frm.set_value("applicable_for", applicable_for); - } + }, }); diff --git a/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.js b/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.js index 1ec6805ae0c..92a6c6259f0 100644 --- a/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.js +++ b/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.js @@ -1,53 +1,58 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Process Deferred Accounting', { - setup: function(frm) { - frm.set_query("document_type", function() { +frappe.ui.form.on("Process Deferred Accounting", { + setup: function (frm) { + frm.set_query("document_type", function () { return { filters: { - 'name': ['in', ['Sales Invoice', 'Purchase Invoice']] - } + name: ["in", ["Sales Invoice", "Purchase Invoice"]], + }, }; }); }, - type: function(frm) { + type: function (frm) { if (frm.doc.company && frm.doc.type) { - frm.set_query("account", function() { + frm.set_query("account", function () { return { filters: { - 'company': frm.doc.company, - 'root_type': frm.doc.type === 'Income' ? 'Liability' : 'Asset', - 'is_group': 0 - } + company: frm.doc.company, + root_type: frm.doc.type === "Income" ? "Liability" : "Asset", + is_group: 0, + }, }; }); } }, - validate: function() { + validate: function () { return new Promise((resolve) => { - return frappe.db.get_single_value('Accounts Settings', 'automatically_process_deferred_accounting_entry') - .then(value => { - if(value) { - frappe.throw(__('Manual entry cannot be created! Disable automatic entry for deferred accounting in accounts settings and try again')); + return frappe.db + .get_single_value("Accounts Settings", "automatically_process_deferred_accounting_entry") + .then((value) => { + if (value) { + frappe.throw( + __( + "Manual entry cannot be created! Disable automatic entry for deferred accounting in accounts settings and try again" + ) + ); } resolve(value); }); }); }, - end_date: function(frm) { + end_date: function (frm) { if (frm.doc.end_date && frm.doc.end_date < frm.doc.start_date) { frappe.throw(__("End date cannot be before start date")); } }, - onload: function(frm) { + onload: function (frm) { if (frm.doc.posting_date && frm.doc.docstatus === 0) { - frm.set_value('start_date', frappe.datetime.add_months(frm.doc.posting_date, -1)); - frm.set_value('end_date', frm.doc.posting_date); + frm.set_value("start_date", frappe.datetime.add_months(frm.doc.posting_date, -1)); + frm.set_value("end_date", frm.doc.posting_date); } - } + }, }); 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 dd601bfc451..0f52a4d998e 100644 --- a/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.js +++ b/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation.js @@ -2,129 +2,128 @@ // For license information, please see license.txt frappe.ui.form.on("Process Payment Reconciliation", { - onload: function(frm) { + onload: function (frm) { // set queries - frm.set_query("party_type", function() { - return { - "filters": { - "name": ["in", Object.keys(frappe.boot.party_account_types)], - } - } - }); - frm.set_query('receivable_payable_account', function(doc) { + frm.set_query("party_type", function () { return { filters: { - "company": doc.company, - "is_group": 0, - "account_type": frappe.boot.party_account_types[doc.party_type] - } + name: ["in", Object.keys(frappe.boot.party_account_types)], + }, }; }); - frm.set_query('cost_center', function(doc) { + frm.set_query("receivable_payable_account", function (doc) { return { filters: { - "company": doc.company, - "is_group": 0, - } + company: doc.company, + is_group: 0, + account_type: frappe.boot.party_account_types[doc.party_type], + }, }; }); - frm.set_query('bank_cash_account', function(doc) { + frm.set_query("cost_center", function (doc) { return { - filters:[ - ['Account', 'company', '=', doc.company], - ['Account', 'is_group', '=', 0], - ['Account', 'account_type', 'in', ['Bank', 'Cash']] - ] + filters: { + company: doc.company, + is_group: 0, + }, + }; + }); + frm.set_query("bank_cash_account", function (doc) { + return { + filters: [ + ["Account", "company", "=", doc.company], + ["Account", "is_group", "=", 0], + ["Account", "account_type", "in", ["Bank", "Cash"]], + ], }; }); - }, - refresh: function(frm) { - if (frm.doc.docstatus==1 && ['Queued', 'Paused'].find(x => x == frm.doc.status)) { - let execute_btn = __("Start / Resume") + refresh: function (frm) { + if (frm.doc.docstatus == 1 && ["Queued", "Paused"].find((x) => x == frm.doc.status)) { + let execute_btn = __("Start / Resume"); frm.add_custom_button(execute_btn, () => { frm.call({ - method: 'erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.trigger_job_for_doc', + method: "erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.trigger_job_for_doc", args: { - docname: frm.doc.name - } - }).then(r => { - if(!r.exc) { + docname: frm.doc.name, + }, + }).then((r) => { + if (!r.exc) { frappe.show_alert(__("Job Started")); frm.reload_doc(); } }); }); } - if (frm.doc.docstatus==1 && ['Completed', 'Running', 'Paused', 'Partially Reconciled'].find(x => x == frm.doc.status)) { + if ( + frm.doc.docstatus == 1 && + ["Completed", "Running", "Paused", "Partially Reconciled"].find((x) => x == frm.doc.status) + ) { frm.call({ - 'method': "erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.get_reconciled_count", + method: "erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.get_reconciled_count", args: { - "docname": frm.docname, - } - }).then(r => { + docname: frm.docname, + }, + }).then((r) => { if (r.message) { let progress = 0; let description = ""; if (r.message.processed) { - progress = (r.message.processed/r.message.total) * 100; - description = r.message.processed + "/" + r.message.total + " processed"; + progress = (r.message.processed / r.message.total) * 100; + description = r.message.processed + "/" + r.message.total + " processed"; } else if (r.message.total == 0 && frm.doc.status == "Completed") { progress = 100; } - - frm.dashboard.add_progress('Reconciliation Progress', progress, description); + frm.dashboard.add_progress("Reconciliation Progress", progress, description); } - }) + }); } - if (frm.doc.docstatus==1 && frm.doc.status == 'Running') { - let execute_btn = __("Pause") + if (frm.doc.docstatus == 1 && frm.doc.status == "Running") { + let execute_btn = __("Pause"); frm.add_custom_button(execute_btn, () => { frm.call({ - 'method': "erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.pause_job_for_doc", + method: "erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.pause_job_for_doc", args: { - "docname": frm.docname, - } - }).then(r => { + docname: frm.docname, + }, + }).then((r) => { if (!r.exc) { frappe.show_alert(__("Job Paused")); - frm.reload_doc() + frm.reload_doc(); } }); - }); } }, company(frm) { - frm.set_value('party', ''); - frm.set_value('receivable_payable_account', ''); + frm.set_value("party", ""); + frm.set_value("receivable_payable_account", ""); }, party_type(frm) { - frm.set_value('party', ''); + frm.set_value("party", ""); }, party(frm) { - frm.set_value('receivable_payable_account', ''); + frm.set_value("receivable_payable_account", ""); if (!frm.doc.receivable_payable_account && frm.doc.party_type && frm.doc.party) { return frappe.call({ method: "erpnext.accounts.party.get_party_account", args: { company: frm.doc.company, party_type: frm.doc.party_type, - party: frm.doc.party + party: frm.doc.party, }, callback: (r) => { if (!r.exc && r.message) { frm.set_value("receivable_payable_account", r.message); } frm.refresh(); - - } + }, }); } - } + }, }); diff --git a/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation_list.js b/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation_list.js index 8012d6e0374..ed182adde1b 100644 --- a/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation_list.js +++ b/erpnext/accounts/doctype/process_payment_reconciliation/process_payment_reconciliation_list.js @@ -1,15 +1,15 @@ -frappe.listview_settings['Process Payment Reconciliation'] = { +frappe.listview_settings["Process Payment Reconciliation"] = { add_fields: ["status"], - get_indicator: function(doc) { + get_indicator: function (doc) { let colors = { - 'Queued': 'orange', - 'Paused': 'orange', - 'Completed': 'green', - 'Partially Reconciled': 'orange', - 'Running': 'blue', - 'Failed': 'red', + Queued: "orange", + Paused: "orange", + Completed: "green", + "Partially Reconciled": "orange", + Running: "blue", + Failed: "red", }; let status = doc.status; - return [__(status), colors[status], 'status,=,'+status]; + return [__(status), colors[status], "status,=," + status]; }, }; diff --git a/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log.js b/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log.js index 2468f10bccf..f483d0039e3 100644 --- a/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log.js +++ b/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log.js @@ -3,15 +3,14 @@ frappe.ui.form.on("Process Payment Reconciliation Log", { refresh(frm) { - if (['Completed', 'Running', 'Paused', 'Partially Reconciled'].find(x => x == frm.doc.status)) { + if (["Completed", "Running", "Paused", "Partially Reconciled"].find((x) => x == frm.doc.status)) { let progress = 0; if (frm.doc.reconciled_entries != 0) { - progress = frm.doc.reconciled_entries / frm.doc.total_allocations * 100; - } else if(frm.doc.total_allocations == 0 && frm.doc.status == "Completed"){ + progress = (frm.doc.reconciled_entries / frm.doc.total_allocations) * 100; + } else if (frm.doc.total_allocations == 0 && frm.doc.status == "Completed") { progress = 100; } - frm.dashboard.add_progress(__('Reconciliation Progress'), progress); + frm.dashboard.add_progress(__("Reconciliation Progress"), progress); } - }, }); diff --git a/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log_list.js b/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log_list.js index 5a652048a23..19c73b249dc 100644 --- a/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log_list.js +++ b/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log_list.js @@ -1,15 +1,15 @@ -frappe.listview_settings['Process Payment Reconciliation Log'] = { +frappe.listview_settings["Process Payment Reconciliation Log"] = { add_fields: ["status"], - get_indicator: function(doc) { + get_indicator: function (doc) { var colors = { - 'Partially Reconciled': 'orange', - 'Paused': 'orange', - 'Reconciled': 'green', - 'Failed': 'red', - 'Cancelled': 'red', - 'Running': 'blue', + "Partially Reconciled": "orange", + Paused: "orange", + Reconciled: "green", + Failed: "red", + Cancelled: "red", + Running: "blue", }; let status = doc.status; - return [__(status), colors[status], "status,=,"+status]; + return [__(status), colors[status], "status,=," + status]; }, }; diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.js b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.js index c5908b783ee..be3579e2d64 100644 --- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.js +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.js @@ -1,153 +1,148 @@ // Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Process Statement Of Accounts', { - view_properties: function(frm) { - frappe.route_options = {doc_type: 'Customer'}; +frappe.ui.form.on("Process Statement Of Accounts", { + view_properties: function (frm) { + frappe.route_options = { doc_type: "Customer" }; frappe.set_route("Form", "Customize Form"); }, - refresh: function(frm){ - if(!frm.doc.__islocal) { - frm.add_custom_button(__('Send Emails'), function(){ + refresh: function (frm) { + if (!frm.doc.__islocal) { + frm.add_custom_button(__("Send Emails"), function () { frappe.call({ method: "erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.send_emails", args: { - "document_name": frm.doc.name, + document_name: frm.doc.name, }, - callback: function(r) { - if(r && r.message) { - frappe.show_alert({message: __('Emails Queued'), indicator: 'blue'}); + callback: function (r) { + if (r && r.message) { + frappe.show_alert({ message: __("Emails Queued"), indicator: "blue" }); + } else { + frappe.msgprint(__("No Records for these settings.")); } - else{ - frappe.msgprint(__('No Records for these settings.')) - } - } + }, }); }); - frm.add_custom_button(__('Download'), function(){ + frm.add_custom_button(__("Download"), function () { var url = frappe.urllib.get_full_url( - '/api/method/erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.download_statements?' - + 'document_name='+encodeURIComponent(frm.doc.name)) + "/api/method/erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.download_statements?" + + "document_name=" + + encodeURIComponent(frm.doc.name) + ); $.ajax({ url: url, - type: 'GET', - success: function(result) { - if(jQuery.isEmptyObject(result)){ - frappe.msgprint(__('No Records for these settings.')); - } - else{ + type: "GET", + success: function (result) { + if (jQuery.isEmptyObject(result)) { + frappe.msgprint(__("No Records for these settings.")); + } else { window.location = url; } - } + }, }); }); } }, - onload: function(frm) { - frm.set_query('currency', function(){ + onload: function (frm) { + frm.set_query("currency", function () { return { filters: { - 'enabled': 1 - } - } - }); - frm.set_query("account", function() { - return { - filters: { - 'company': frm.doc.company - } + enabled: 1, + }, }; }); - if(frm.doc.__islocal){ - frm.set_value('from_date', frappe.datetime.add_months(frappe.datetime.get_today(), -1)); - frm.set_value('to_date', frappe.datetime.get_today()); + frm.set_query("account", function () { + return { + filters: { + company: frm.doc.company, + }, + }; + }); + if (frm.doc.__islocal) { + frm.set_value("from_date", frappe.datetime.add_months(frappe.datetime.get_today(), -1)); + frm.set_value("to_date", frappe.datetime.get_today()); } }, - report: function(frm){ + report: function (frm) { let filters = { - 'company': frm.doc.company, + company: frm.doc.company, + }; + if (frm.doc.report == "Accounts Receivable") { + filters["account_type"] = "Receivable"; } - if(frm.doc.report == 'Accounts Receivable'){ - filters['account_type'] = 'Receivable'; - } - frm.set_query("account", function() { + frm.set_query("account", function () { return { - filters: filters + filters: filters, }; }); - }, - customer_collection: function(frm){ - frm.set_value('collection_name', ''); - if(frm.doc.customer_collection){ - frm.get_field('collection_name').set_label(frm.doc.customer_collection); + customer_collection: function (frm) { + frm.set_value("collection_name", ""); + if (frm.doc.customer_collection) { + frm.get_field("collection_name").set_label(frm.doc.customer_collection); } }, - frequency: function(frm){ - if(frm.doc.frequency != ''){ - frm.set_value('start_date', frappe.datetime.get_today()); - } - else{ - frm.set_value('start_date', ''); + frequency: function (frm) { + if (frm.doc.frequency != "") { + frm.set_value("start_date", frappe.datetime.get_today()); + } else { + frm.set_value("start_date", ""); } }, - fetch_customers: function(frm){ - if(frm.doc.collection_name){ + fetch_customers: function (frm) { + if (frm.doc.collection_name) { frappe.call({ method: "erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.fetch_customers", args: { - 'customer_collection': frm.doc.customer_collection, - 'collection_name': frm.doc.collection_name, - 'primary_mandatory': frm.doc.primary_mandatory + customer_collection: frm.doc.customer_collection, + collection_name: frm.doc.collection_name, + primary_mandatory: frm.doc.primary_mandatory, }, - callback: function(r) { - if(!r.exc) { - if(r.message.length){ - frm.clear_table('customers'); - for (const customer of r.message){ - var row = frm.add_child('customers'); + callback: function (r) { + if (!r.exc) { + if (r.message.length) { + frm.clear_table("customers"); + for (const customer of r.message) { + var row = frm.add_child("customers"); row.customer = customer.name; row.primary_email = customer.primary_email; row.billing_email = customer.billing_email; } - frm.refresh_field('customers'); - } - else{ - frappe.throw(__('No Customers found with selected options.')); + frm.refresh_field("customers"); + } else { + frappe.throw(__("No Customers found with selected options.")); } } - } + }, }); + } else { + frappe.throw("Enter " + frm.doc.customer_collection + " name."); } - else { - frappe.throw('Enter ' + frm.doc.customer_collection + ' name.'); - } - } + }, }); -frappe.ui.form.on('Process Statement Of Accounts Customer', { - customer: function(frm, cdt, cdn){ +frappe.ui.form.on("Process Statement Of Accounts Customer", { + customer: function (frm, cdt, cdn) { var row = locals[cdt][cdn]; - if (!row.customer){ + if (!row.customer) { return; } frappe.call({ - method: 'erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.get_customer_emails', + method: "erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.get_customer_emails", args: { - 'customer_name': row.customer, - 'primary_mandatory': frm.doc.primary_mandatory + customer_name: row.customer, + primary_mandatory: frm.doc.primary_mandatory, }, - callback: function(r){ - if(!r.exe){ - if(r.message.length){ - frappe.model.set_value(cdt, cdn, "primary_email", r.message[0]) - frappe.model.set_value(cdt, cdn, "billing_email", r.message[1]) - } - else { - return + callback: function (r) { + if (!r.exe) { + if (r.message.length) { + frappe.model.set_value(cdt, cdn, "primary_email", r.message[0]); + frappe.model.set_value(cdt, cdn, "billing_email", r.message[1]); + } else { + return; } } - } - }) - } + }, + }); + }, }); diff --git a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.js b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.js index e840c79cd75..7a26c07d01f 100644 --- a/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.js +++ b/erpnext/accounts/doctype/promotional_scheme/promotional_scheme.js @@ -1,51 +1,56 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Promotional Scheme', { - refresh: function(frm) { +frappe.ui.form.on("Promotional Scheme", { + refresh: function (frm) { frm.trigger("set_options_for_applicable_for"); frm.trigger("toggle_reqd_apply_on"); }, - selling: function(frm) { + selling: function (frm) { frm.trigger("set_options_for_applicable_for"); }, - buying: function(frm) { + buying: function (frm) { frm.trigger("set_options_for_applicable_for"); }, - set_options_for_applicable_for: function(frm) { + set_options_for_applicable_for: function (frm) { var options = [""]; var applicable_for = frm.doc.applicable_for; - if(frm.doc.selling) { - options = $.merge(options, ["Customer", "Customer Group", "Territory", "Sales Partner", "Campaign"]); + if (frm.doc.selling) { + options = $.merge(options, [ + "Customer", + "Customer Group", + "Territory", + "Sales Partner", + "Campaign", + ]); } - if(frm.doc.buying) { + if (frm.doc.buying) { $.merge(options, ["Supplier", "Supplier Group"]); } set_field_options("applicable_for", options.join("\n")); - if(!in_list(options, applicable_for)) applicable_for = null; + if (!in_list(options, applicable_for)) applicable_for = null; frm.set_value("applicable_for", applicable_for); }, - apply_on: function(frm) { + apply_on: function (frm) { frm.trigger("toggle_reqd_apply_on"); }, - toggle_reqd_apply_on: function(frm) { + toggle_reqd_apply_on: function (frm) { const fields = { - 'Item Code': 'items', - 'Item Group': 'item_groups', - 'Brand': 'brands' + "Item Code": "items", + "Item Group": "item_groups", + Brand: "brands", }; for (var key in fields) { - frm.toggle_reqd(fields[key], - frm.doc.apply_on === key ? 1 : 0); + frm.toggle_reqd(fields[key], frm.doc.apply_on === key ? 1 : 0); } - } + }, }); diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js index e1c37c60013..4350ba11148 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice_list.js @@ -22,48 +22,35 @@ frappe.listview_settings["Purchase Invoice"] = { return [__(doc.status), "darkgrey", "status,=," + doc.status]; } - if ( - flt(doc.outstanding_amount) > 0 && - doc.docstatus == 1 && - cint(doc.on_hold) - ) { + if (flt(doc.outstanding_amount) > 0 && doc.docstatus == 1 && cint(doc.on_hold)) { if (!doc.release_date) { return [__("On Hold"), "darkgrey"]; - } else if ( - frappe.datetime.get_diff( - doc.release_date, - frappe.datetime.nowdate() - ) > 0 - ) { + } else if (frappe.datetime.get_diff(doc.release_date, frappe.datetime.nowdate()) > 0) { return [__("Temporarily on Hold"), "darkgrey"]; } } const status_colors = { - "Unpaid": "orange", - "Paid": "green", - "Return": "gray", - "Overdue": "red", + Unpaid: "orange", + Paid: "green", + Return: "gray", + Overdue: "red", "Partly Paid": "yellow", "Internal Transfer": "darkgrey", }; if (status_colors[doc.status]) { - return [ - __(doc.status), - status_colors[doc.status], - "status,=," + doc.status, - ]; + return [__(doc.status), status_colors[doc.status], "status,=," + doc.status]; } }, - onload: function(listview) { - listview.page.add_action_item(__("Purchase Receipt"), ()=>{ + onload: function (listview) { + listview.page.add_action_item(__("Purchase Receipt"), () => { erpnext.bulk_transaction_processing.create(listview, "Purchase Invoice", "Purchase Receipt"); }); - listview.page.add_action_item(__("Payment"), ()=>{ + listview.page.add_action_item(__("Payment"), () => { erpnext.bulk_transaction_processing.create(listview, "Purchase Invoice", "Payment Entry"); }); - } + }, }; diff --git a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.js b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.js index c7b7a148cfb..c304c7f17eb 100644 --- a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.js +++ b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.js @@ -2,47 +2,47 @@ // For license information, please see license.txt frappe.ui.form.on("Repost Accounting Ledger", { - setup: function(frm) { - frm.fields_dict['vouchers'].grid.get_field('voucher_type').get_query = function(doc) { + setup: function (frm) { + frm.fields_dict["vouchers"].grid.get_field("voucher_type").get_query = function (doc) { return { - query: "erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger.get_repost_allowed_types" - } - } + query: "erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger.get_repost_allowed_types", + }; + }; - frm.fields_dict['vouchers'].grid.get_field('voucher_no').get_query = function(doc) { + frm.fields_dict["vouchers"].grid.get_field("voucher_no").get_query = function (doc) { if (doc.company) { return { filters: { company: doc.company, - docstatus: 1 - } - } + docstatus: 1, + }, + }; } - } + }; }, - refresh: function(frm) { - frm.add_custom_button(__('Show Preview'), () => { + refresh: function (frm) { + frm.add_custom_button(__("Show Preview"), () => { frm.call({ - method: 'generate_preview', + method: "generate_preview", doc: frm.doc, freeze: true, - freeze_message: __('Generating Preview'), - callback: function(r) { + freeze_message: __("Generating Preview"), + callback: function (r) { if (r && r.message) { let content = r.message; let opts = { title: "Preview", subtitle: "preview", content: content, - print_settings: {orientation: "landscape"}, + print_settings: { orientation: "landscape" }, columns: [], data: [], - } + }; frappe.render_grid(opts); } - } + }, }); }); - } + }, }); diff --git a/erpnext/accounts/doctype/repost_payment_ledger/repost_payment_ledger.js b/erpnext/accounts/doctype/repost_payment_ledger/repost_payment_ledger.js index 6801408c7b3..b9621c06eb8 100644 --- a/erpnext/accounts/doctype/repost_payment_ledger/repost_payment_ledger.js +++ b/erpnext/accounts/doctype/repost_payment_ledger/repost_payment_ledger.js @@ -1,53 +1,53 @@ // Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Repost Payment Ledger', { - setup: function(frm) { +frappe.ui.form.on("Repost Payment Ledger", { + setup: function (frm) { frm.set_query("voucher_type", () => { return { filters: { - name: ['in', ['Purchase Invoice', 'Sales Invoice', 'Payment Entry', 'Journal Entry']] - } + name: ["in", ["Purchase Invoice", "Sales Invoice", "Payment Entry", "Journal Entry"]], + }, }; }); - frm.fields_dict['repost_vouchers'].grid.get_field('voucher_type').get_query = function(doc) { + frm.fields_dict["repost_vouchers"].grid.get_field("voucher_type").get_query = function (doc) { return { filters: { - name: ['in', ['Purchase Invoice', 'Sales Invoice', 'Payment Entry', 'Journal Entry']] - } - } - } + name: ["in", ["Purchase Invoice", "Sales Invoice", "Payment Entry", "Journal Entry"]], + }, + }; + }; - frm.fields_dict['repost_vouchers'].grid.get_field('voucher_no').get_query = function(doc) { + frm.fields_dict["repost_vouchers"].grid.get_field("voucher_no").get_query = function (doc) { if (doc.company) { return { filters: { company: doc.company, - docstatus: 1 - } - } + docstatus: 1, + }, + }; } - } - + }; }, - refresh: function(frm) { - - if (frm.doc.docstatus==1 && ['Queued', 'Failed'].find(x => x == frm.doc.repost_status)) { - frm.set_intro(__("Use 'Repost in background' button to trigger background job. Job can only be triggered when document is in Queued or Failed status.")); - var btn_label = __("Repost in background") + refresh: function (frm) { + if (frm.doc.docstatus == 1 && ["Queued", "Failed"].find((x) => x == frm.doc.repost_status)) { + frm.set_intro( + __( + "Use 'Repost in background' button to trigger background job. Job can only be triggered when document is in Queued or Failed status." + ) + ); + var btn_label = __("Repost in background"); frm.add_custom_button(btn_label, () => { frappe.call({ - method: 'erpnext.accounts.doctype.repost_payment_ledger.repost_payment_ledger.execute_repost_payment_ledger', + method: "erpnext.accounts.doctype.repost_payment_ledger.repost_payment_ledger.execute_repost_payment_ledger", args: { docname: frm.doc.name, - } + }, }); - frappe.msgprint(__('Reposting in the background.')); + frappe.msgprint(__("Reposting in the background.")); }); } - - } + }, }); - diff --git a/erpnext/accounts/doctype/repost_payment_ledger/repost_payment_ledger_list.js b/erpnext/accounts/doctype/repost_payment_ledger/repost_payment_ledger_list.js index e0451845ced..76d17fc6860 100644 --- a/erpnext/accounts/doctype/repost_payment_ledger/repost_payment_ledger_list.js +++ b/erpnext/accounts/doctype/repost_payment_ledger/repost_payment_ledger_list.js @@ -1,12 +1,12 @@ frappe.listview_settings["Repost Payment Ledger"] = { add_fields: ["repost_status"], - get_indicator: function(doc) { + get_indicator: function (doc) { var colors = { - 'Queued': 'orange', - 'Completed': 'green', - 'Failed': 'red', + Queued: "orange", + Completed: "green", + Failed: "red", }; let status = doc.repost_status; - return [__(status), colors[status], 'status,=,'+status]; + return [__(status), colors[status], "status,=," + status]; }, }; diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js index 1605b151a14..f971f68a454 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice_list.js @@ -2,34 +2,42 @@ // License: GNU General Public License v3. See license.txt // render -frappe.listview_settings['Sales Invoice'] = { - add_fields: ["customer", "customer_name", "base_grand_total", "outstanding_amount", "due_date", "company", - "currency", "is_return"], - get_indicator: function(doc) { +frappe.listview_settings["Sales Invoice"] = { + add_fields: [ + "customer", + "customer_name", + "base_grand_total", + "outstanding_amount", + "due_date", + "company", + "currency", + "is_return", + ], + get_indicator: function (doc) { const status_colors = { - "Draft": "grey", - "Unpaid": "orange", - "Paid": "green", - "Return": "gray", + Draft: "grey", + Unpaid: "orange", + Paid: "green", + Return: "gray", "Credit Note Issued": "gray", "Unpaid and Discounted": "orange", "Partly Paid and Discounted": "yellow", "Overdue and Discounted": "red", - "Overdue": "red", + Overdue: "red", "Partly Paid": "yellow", - "Internal Transfer": "darkgrey" + "Internal Transfer": "darkgrey", }; - return [__(doc.status), status_colors[doc.status], "status,=,"+doc.status]; + return [__(doc.status), status_colors[doc.status], "status,=," + doc.status]; }, right_column: "grand_total", - onload: function(listview) { - listview.page.add_action_item(__("Delivery Note"), ()=>{ + onload: function (listview) { + listview.page.add_action_item(__("Delivery Note"), () => { erpnext.bulk_transaction_processing.create(listview, "Sales Invoice", "Delivery Note"); }); - listview.page.add_action_item(__("Payment"), ()=>{ + listview.page.add_action_item(__("Payment"), () => { erpnext.bulk_transaction_processing.create(listview, "Sales Invoice", "Payment Entry"); }); - } + }, }; diff --git a/erpnext/accounts/doctype/share_transfer/share_transfer.js b/erpnext/accounts/doctype/share_transfer/share_transfer.js index 6317c9c8c0d..dd9d9628aa8 100644 --- a/erpnext/accounts/doctype/share_transfer/share_transfer.js +++ b/erpnext/accounts/doctype/share_transfer/share_transfer.js @@ -3,21 +3,19 @@ frappe.provide("erpnext.share_transfer"); -frappe.ui.form.on('Share Transfer', { - refresh: function(frm) { +frappe.ui.form.on("Share Transfer", { + refresh: function (frm) { // Don't show Parties which are a Company - let shareholders = ['from_shareholder', 'to_shareholder']; + let shareholders = ["from_shareholder", "to_shareholder"]; shareholders.forEach((shareholder) => { - frm.fields_dict[shareholder].get_query = function() { + frm.fields_dict[shareholder].get_query = function () { return { - filters: [ - ["Shareholder", "is_company", "=", 0] - ] + filters: [["Shareholder", "is_company", "=", 0]], }; }; }); if (frm.doc.docstatus == 1 && frm.doc.equity_or_liability_account && frm.doc.asset_account) { - frm.add_custom_button(__('Create Journal Entry'), function () { + frm.add_custom_button(__("Create Journal Entry"), function () { erpnext.share_transfer.make_jv(frm); }); } @@ -25,54 +23,59 @@ frappe.ui.form.on('Share Transfer', { frm.toggle_reqd("asset_account", frm.doc.transfer_type != "Transfer"); }, no_of_shares: (frm) => { - if (frm.doc.rate != undefined || frm.doc.rate != null){ + if (frm.doc.rate != undefined || frm.doc.rate != null) { erpnext.share_transfer.update_amount(frm); } }, rate: (frm) => { - if (frm.doc.no_of_shares != undefined || frm.doc.no_of_shares != null){ + if (frm.doc.no_of_shares != undefined || frm.doc.no_of_shares != null) { erpnext.share_transfer.update_amount(frm); } }, - company: async function(frm) { + company: async function (frm) { if (frm.doc.company) { - let currency = (await frappe.db.get_value("Company", frm.doc.company, "default_currency")).message.default_currency; - frm.set_query("equity_or_liability_account", function() { + let currency = (await frappe.db.get_value("Company", frm.doc.company, "default_currency")).message + .default_currency; + frm.set_query("equity_or_liability_account", function () { return { filters: { - "is_group":0, - "root_type": ["in",["Equity","Liability"]], - "company": frm.doc.company, - "account_currency": currency - } + is_group: 0, + root_type: ["in", ["Equity", "Liability"]], + company: frm.doc.company, + account_currency: currency, + }, }; }); - frm.set_query("asset_account", function() { + frm.set_query("asset_account", function () { return { filters: { - "is_group":0, - "root_type":"Asset", - "company": frm.doc.company, - "account_currency": currency - } + is_group: 0, + root_type: "Asset", + company: frm.doc.company, + account_currency: currency, + }, }; }); } }, - transfer_type: function(frm) { + transfer_type: function (frm) { frm.toggle_reqd("asset_account", frm.doc.transfer_type != "Transfer"); - } + }, }); -erpnext.share_transfer.update_amount = function(frm) { +erpnext.share_transfer.update_amount = function (frm) { frm.doc.amount = frm.doc.no_of_shares * frm.doc.rate; frm.refresh_field("amount"); }; erpnext.share_transfer.make_jv = function (frm) { - var account, payment_account, credit_applicant_type, credit_applicant, - debit_applicant_type, debit_applicant; + var account, + payment_account, + credit_applicant_type, + credit_applicant, + debit_applicant_type, + debit_applicant; if (frm.doc.transfer_type == "Transfer") { account = frm.doc.equity_or_liability_account; @@ -81,16 +84,14 @@ erpnext.share_transfer.make_jv = function (frm) { credit_applicant = frm.doc.to_shareholder; debit_applicant_type = "Shareholder"; debit_applicant = frm.doc.from_shareholder; - } - else if (frm.doc.transfer_type == "Issue") { + } else if (frm.doc.transfer_type == "Issue") { account = frm.doc.asset_account; payment_account = frm.doc.equity_or_liability_account; credit_applicant_type = "Shareholder"; credit_applicant = frm.doc.to_shareholder; debit_applicant_type = ""; debit_applicant = ""; - } - else { + } else { account = frm.doc.equity_or_liability_account; payment_account = frm.doc.asset_account; credit_applicant_type = ""; @@ -100,19 +101,19 @@ erpnext.share_transfer.make_jv = function (frm) { } frappe.call({ args: { - "company": frm.doc.company, - "account": account, - "amount": frm.doc.amount, - "payment_account": payment_account, - "credit_applicant_type": credit_applicant_type, - "credit_applicant": credit_applicant, - "debit_applicant_type": debit_applicant_type, - "debit_applicant": debit_applicant + company: frm.doc.company, + account: account, + amount: frm.doc.amount, + payment_account: payment_account, + credit_applicant_type: credit_applicant_type, + credit_applicant: credit_applicant, + debit_applicant_type: debit_applicant_type, + debit_applicant: debit_applicant, }, method: "erpnext.accounts.doctype.share_transfer.share_transfer.make_jv_entry", callback: function (r) { var doc = frappe.model.sync(r.message)[0]; frappe.set_route("Form", doc.doctype, doc.name); - } + }, }); }; diff --git a/erpnext/accounts/doctype/share_type/share_type.js b/erpnext/accounts/doctype/share_type/share_type.js index 1ae85e3a57e..5dfb7607fed 100644 --- a/erpnext/accounts/doctype/share_type/share_type.js +++ b/erpnext/accounts/doctype/share_type/share_type.js @@ -1,8 +1,6 @@ // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Share Type', { - refresh: function(frm) { - - } +frappe.ui.form.on("Share Type", { + refresh: function (frm) {}, }); diff --git a/erpnext/accounts/doctype/shareholder/shareholder.js b/erpnext/accounts/doctype/shareholder/shareholder.js index c6f101e7f31..2a31b805ab3 100644 --- a/erpnext/accounts/doctype/shareholder/shareholder.js +++ b/erpnext/accounts/doctype/shareholder/shareholder.js @@ -1,39 +1,38 @@ // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Shareholder', { - refresh: function(frm) { - frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Shareholder' }; +frappe.ui.form.on("Shareholder", { + refresh: function (frm) { + frappe.dynamic_link = { doc: frm.doc, fieldname: "name", doctype: "Shareholder" }; - frm.toggle_display(['contact_html'], !frm.doc.__islocal); + frm.toggle_display(["contact_html"], !frm.doc.__islocal); if (frm.doc.__islocal) { - hide_field(['contact_html']); + hide_field(["contact_html"]); frappe.contacts.clear_address_and_contact(frm); - } - else { - if (frm.doc.is_company){ - hide_field(['company']); + } else { + if (frm.doc.is_company) { + hide_field(["company"]); } else { - unhide_field(['contact_html']); + unhide_field(["contact_html"]); frappe.contacts.render_address_and_contact(frm); } } - if (frm.doc.folio_no != undefined){ - frm.add_custom_button(__("Share Balance"), function(){ + if (frm.doc.folio_no != undefined) { + frm.add_custom_button(__("Share Balance"), function () { frappe.route_options = { - "shareholder": frm.doc.name, + shareholder: frm.doc.name, }; frappe.set_route("query-report", "Share Balance"); }); - frm.add_custom_button(__("Share Ledger"), function(){ + frm.add_custom_button(__("Share Ledger"), function () { frappe.route_options = { - "shareholder": frm.doc.name, + shareholder: frm.doc.name, }; frappe.set_route("query-report", "Share Ledger"); }); - let fields = ['title', 'folio_no', 'company']; + let fields = ["title", "folio_no", "company"]; fields.forEach((fieldname) => { frm.fields_dict[fieldname].df.read_only = 1; frm.refresh_fields(fieldname); @@ -44,11 +43,11 @@ frappe.ui.form.on('Shareholder', { }, validate: (frm) => { let contact_list = { - contacts: [] + contacts: [], }; - $('div[data-fieldname=contact_html] > .address-box').each( (index, ele) => { - contact_list.contacts.push(ele.innerText.replace(' Edit', '')); + $("div[data-fieldname=contact_html] > .address-box").each((index, ele) => { + contact_list.contacts.push(ele.innerText.replace(" Edit", "")); }); frm.doc.contact_list = JSON.stringify(contact_list); - } + }, }); diff --git a/erpnext/accounts/doctype/shipping_rule/shipping_rule.js b/erpnext/accounts/doctype/shipping_rule/shipping_rule.js index 8e4b806f02d..1ece3e6c3dd 100644 --- a/erpnext/accounts/doctype/shipping_rule/shipping_rule.js +++ b/erpnext/accounts/doctype/shipping_rule/shipping_rule.js @@ -1,33 +1,33 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.provide('erpnext.accounts.dimensions'); +frappe.provide("erpnext.accounts.dimensions"); -frappe.ui.form.on('Shipping Rule', { - onload: function(frm) { +frappe.ui.form.on("Shipping Rule", { + onload: function (frm) { erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); }, - company: function(frm) { + company: function (frm) { erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); }, - refresh: function(frm) { - frm.set_query("account", function() { + refresh: function (frm) { + frm.set_query("account", function () { return { filters: { - company: frm.doc.company - } - } - }) + company: frm.doc.company, + }, + }; + }); - frm.trigger('toggle_reqd'); + frm.trigger("toggle_reqd"); }, - calculate_based_on: function(frm) { - frm.trigger('toggle_reqd'); + calculate_based_on: function (frm) { + frm.trigger("toggle_reqd"); + }, + toggle_reqd: function (frm) { + frm.toggle_reqd("shipping_amount", frm.doc.calculate_based_on === "Fixed"); + frm.toggle_reqd("conditions", frm.doc.calculate_based_on !== "Fixed"); }, - toggle_reqd: function(frm) { - frm.toggle_reqd("shipping_amount", frm.doc.calculate_based_on === 'Fixed'); - frm.toggle_reqd("conditions", frm.doc.calculate_based_on !== 'Fixed'); - } }); diff --git a/erpnext/accounts/doctype/subscription/subscription.js b/erpnext/accounts/doctype/subscription/subscription.js index 4b351f9d764..60981f4b1d1 100644 --- a/erpnext/accounts/doctype/subscription/subscription.js +++ b/erpnext/accounts/doctype/subscription/subscription.js @@ -1,104 +1,99 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Subscription', { - setup: function(frm) { - frm.set_query('party_type', function() { - return { - filters : { - name: ['in', ['Customer', 'Supplier']] - } - } - }); - - frm.set_query('cost_center', function() { +frappe.ui.form.on("Subscription", { + setup: function (frm) { + frm.set_query("party_type", function () { return { filters: { - company: frm.doc.company - } + name: ["in", ["Customer", "Supplier"]], + }, }; }); - frm.set_query('sales_tax_template', function () { + frm.set_query("cost_center", function () { return { filters: { - company: frm.doc.company - } + company: frm.doc.company, + }, + }; + }); + + frm.set_query("sales_tax_template", function () { + return { + filters: { + company: frm.doc.company, + }, }; }); }, - refresh: function(frm) { - if(!frm.is_new()){ - if(frm.doc.status !== 'Cancelled'){ - frm.add_custom_button( - __('Cancel Subscription'), - () => frm.events.cancel_this_subscription(frm) + refresh: function (frm) { + if (!frm.is_new()) { + if (frm.doc.status !== "Cancelled") { + frm.add_custom_button(__("Cancel Subscription"), () => + frm.events.cancel_this_subscription(frm) ); - frm.add_custom_button( - __('Fetch Subscription Updates'), - () => frm.events.get_subscription_updates(frm) + frm.add_custom_button(__("Fetch Subscription Updates"), () => + frm.events.get_subscription_updates(frm) ); - } - else if(frm.doc.status === 'Cancelled'){ - frm.add_custom_button( - __('Restart Subscription'), - () => frm.events.renew_this_subscription(frm) + } else if (frm.doc.status === "Cancelled") { + frm.add_custom_button(__("Restart Subscription"), () => + frm.events.renew_this_subscription(frm) ); } } }, - cancel_this_subscription: function(frm) { + cancel_this_subscription: function (frm) { const doc = frm.doc; frappe.confirm( - __('This action will stop future billing. Are you sure you want to cancel this subscription?'), - function() { + __("This action will stop future billing. Are you sure you want to cancel this subscription?"), + function () { frappe.call({ - method: - "erpnext.accounts.doctype.subscription.subscription.cancel_subscription", - args: {name: doc.name}, - callback: function(data){ - if(!data.exc){ + method: "erpnext.accounts.doctype.subscription.subscription.cancel_subscription", + args: { name: doc.name }, + callback: function (data) { + if (!data.exc) { frm.reload_doc(); } - } + }, }); } ); }, - renew_this_subscription: function(frm) { + renew_this_subscription: function (frm) { const doc = frm.doc; frappe.confirm( - __('You will lose records of previously generated invoices. Are you sure you want to restart this subscription?'), - function() { + __( + "You will lose records of previously generated invoices. Are you sure you want to restart this subscription?" + ), + function () { frappe.call({ - method: - "erpnext.accounts.doctype.subscription.subscription.restart_subscription", - args: {name: doc.name}, - callback: function(data){ - if(!data.exc){ + method: "erpnext.accounts.doctype.subscription.subscription.restart_subscription", + args: { name: doc.name }, + callback: function (data) { + if (!data.exc) { frm.reload_doc(); } - } + }, }); } ); }, - get_subscription_updates: function(frm) { + get_subscription_updates: function (frm) { const doc = frm.doc; frappe.call({ - method: - "erpnext.accounts.doctype.subscription.subscription.get_subscription_updates", - args: {name: doc.name}, + method: "erpnext.accounts.doctype.subscription.subscription.get_subscription_updates", + args: { name: doc.name }, freeze: true, - callback: function(data){ - if(!data.exc){ + callback: function (data) { + if (!data.exc) { frm.reload_doc(); } - } + }, }); - } + }, }); diff --git a/erpnext/accounts/doctype/subscription/subscription_list.js b/erpnext/accounts/doctype/subscription/subscription_list.js index 6490ff3776e..55430a15c5d 100644 --- a/erpnext/accounts/doctype/subscription/subscription_list.js +++ b/erpnext/accounts/doctype/subscription/subscription_list.js @@ -1,17 +1,17 @@ -frappe.listview_settings['Subscription'] = { - get_indicator: function(doc) { - if(doc.status === 'Trialling') { +frappe.listview_settings["Subscription"] = { + get_indicator: function (doc) { + if (doc.status === "Trialling") { return [__("Trialling"), "green"]; - } else if(doc.status === 'Active') { + } else if (doc.status === "Active") { return [__("Active"), "green"]; - } else if(doc.status === 'Completed') { - return [__("Completed"), "green"]; - } else if(doc.status === 'Past Due Date') { + } else if (doc.status === "Completed") { + return [__("Completed"), "green"]; + } else if (doc.status === "Past Due Date") { return [__("Past Due Date"), "orange"]; - } else if(doc.status === 'Unpaid') { + } else if (doc.status === "Unpaid") { return [__("Unpaid"), "red"]; - } else if(doc.status === 'Cancelled') { + } else if (doc.status === "Cancelled") { return [__("Cancelled"), "gray"]; } - } + }, }; diff --git a/erpnext/accounts/doctype/subscription_plan/subscription_plan.js b/erpnext/accounts/doctype/subscription_plan/subscription_plan.js index 00727f103f9..125dc7dd9ae 100644 --- a/erpnext/accounts/doctype/subscription_plan/subscription_plan.js +++ b/erpnext/accounts/doctype/subscription_plan/subscription_plan.js @@ -1,10 +1,10 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Subscription Plan', { - price_determination: function(frm) { - frm.toggle_reqd("cost", frm.doc.price_determination === 'Fixed rate'); - frm.toggle_reqd("price_list", frm.doc.price_determination === 'Based on price list'); +frappe.ui.form.on("Subscription Plan", { + price_determination: function (frm) { + frm.toggle_reqd("cost", frm.doc.price_determination === "Fixed rate"); + frm.toggle_reqd("price_list", frm.doc.price_determination === "Based on price list"); }, subscription_plan: function (frm) { diff --git a/erpnext/accounts/doctype/tax_category/tax_category.js b/erpnext/accounts/doctype/tax_category/tax_category.js index 4b63edbde2d..fe60e037b16 100644 --- a/erpnext/accounts/doctype/tax_category/tax_category.js +++ b/erpnext/accounts/doctype/tax_category/tax_category.js @@ -1,8 +1,6 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Tax Category', { - refresh: function(frm) { - - } +frappe.ui.form.on("Tax Category", { + refresh: function (frm) {}, }); diff --git a/erpnext/accounts/doctype/tax_rule/tax_rule.js b/erpnext/accounts/doctype/tax_rule/tax_rule.js index bc497163e8b..b8c68e8d81d 100644 --- a/erpnext/accounts/doctype/tax_rule/tax_rule.js +++ b/erpnext/accounts/doctype/tax_rule/tax_rule.js @@ -1,40 +1,40 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.ui.form.on("Tax Rule", "customer", function(frm) { - if(frm.doc.customer) { +frappe.ui.form.on("Tax Rule", "customer", function (frm) { + if (frm.doc.customer) { frappe.call({ - method:"erpnext.accounts.doctype.tax_rule.tax_rule.get_party_details", + method: "erpnext.accounts.doctype.tax_rule.tax_rule.get_party_details", args: { - "party": frm.doc.customer, - "party_type": "customer" + party: frm.doc.customer, + party_type: "customer", }, - callback: function(r) { - if(!r.exc) { - $.each(r.message, function(k, v) { + callback: function (r) { + if (!r.exc) { + $.each(r.message, function (k, v) { frm.set_value(k, v); }); } - } + }, }); } }); -frappe.ui.form.on("Tax Rule", "supplier", function(frm) { - if(frm.doc.supplier) { +frappe.ui.form.on("Tax Rule", "supplier", function (frm) { + if (frm.doc.supplier) { frappe.call({ - method:"erpnext.accounts.doctype.tax_rule.tax_rule.get_party_details", + method: "erpnext.accounts.doctype.tax_rule.tax_rule.get_party_details", args: { - "party": frm.doc.supplier, - "party_type": "supplier" + party: frm.doc.supplier, + party_type: "supplier", }, - callback: function(r) { - if(!r.exc) { - $.each(r.message, function(k, v) { + callback: function (r) { + if (!r.exc) { + $.each(r.message, function (k, v) { frm.set_value(k, v); }); } - } + }, }); } }); diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.js b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.js index 7b479749465..4f4f32c4747 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.js +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.js @@ -1,18 +1,18 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Tax Withholding Category', { - setup: function(frm) { - frm.set_query("account", "accounts", function(doc, cdt, cdn) { +frappe.ui.form.on("Tax Withholding Category", { + setup: function (frm) { + frm.set_query("account", "accounts", function (doc, cdt, cdn) { var child = locals[cdt][cdn]; if (child.company) { return { filters: { - 'company': child.company, - 'root_type': ['in', ['Asset', 'Liability']] - } + company: child.company, + root_type: ["in", ["Asset", "Liability"]], + }, }; } }); - } + }, }); diff --git a/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.js b/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.js index 70cefb13b57..63ae30d9d20 100644 --- a/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.js +++ b/erpnext/accounts/doctype/unreconcile_payment/unreconcile_payment.js @@ -3,39 +3,36 @@ frappe.ui.form.on("Unreconcile Payment", { refresh(frm) { - frm.set_query("voucher_type", function() { + frm.set_query("voucher_type", function () { return { filters: { - name: ["in", ["Payment Entry", "Journal Entry"]] - } - } + name: ["in", ["Payment Entry", "Journal Entry"]], + }, + }; }); - - frm.set_query("voucher_no", function(doc) { + frm.set_query("voucher_no", function (doc) { return { filters: { company: doc.company, - docstatus: 1 - } - } + docstatus: 1, + }, + }; }); - }, - get_allocations: function(frm) { + get_allocations: function (frm) { frm.clear_table("allocations"); frappe.call({ method: "get_allocations_from_payment", doc: frm.doc, - callback: function(r) { + callback: function (r) { if (r.message) { - r.message.forEach(x => { - frm.add_child("allocations", x) - }) + r.message.forEach((x) => { + frm.add_child("allocations", x); + }); frm.refresh_fields(); } - } - }) - - } + }, + }); + }, }); diff --git a/erpnext/accounts/report/account_balance/account_balance.js b/erpnext/accounts/report/account_balance/account_balance.js index bb66951cdcd..52775739b8f 100644 --- a/erpnext/accounts/report/account_balance/account_balance.js +++ b/erpnext/accounts/report/account_balance/account_balance.js @@ -3,31 +3,31 @@ /* eslint-disable */ frappe.query_reports["Account Balance"] = { - "filters": [ + filters: [ { - fieldname:"company", + fieldname: "company", label: __("Company"), fieldtype: "Link", options: "Company", - default: frappe.defaults.get_user_default("Company") + default: frappe.defaults.get_user_default("Company"), }, { - fieldname:"report_date", + fieldname: "report_date", label: __("Date"), fieldtype: "Date", default: frappe.datetime.get_today(), - reqd: 1 + reqd: 1, }, { fieldname: "root_type", label: __("Root Type"), fieldtype: "Select", options: [ - { "value": "Asset", "label": __("Asset") }, - { "value": "Liability", "label": __("Liability") }, - { "value": "Income", "label": __("Income") }, - { "value": "Expense", "label": __("Expense") }, - { "value": "Equity", "label": __("Equity") } + { value: "Asset", label: __("Asset") }, + { value: "Liability", label: __("Liability") }, + { value: "Income", label: __("Income") }, + { value: "Expense", label: __("Expense") }, + { value: "Equity", label: __("Equity") }, ], }, { @@ -35,30 +35,32 @@ frappe.query_reports["Account Balance"] = { label: __("Account Type"), fieldtype: "Select", options: [ - { "value": "Accumulated Depreciation", "label": __("Accumulated Depreciation") }, - { "value": "Asset Received But Not Billed", "label": __("Asset Received But Not Billed") }, - { "value": "Bank", "label": __("Bank") }, - { "value": "Cash", "label": __("Cash") }, - { "value": "Chargeble", "label": __("Chargeble") }, - { "value": "Capital Work in Progress", "label": __("Capital Work in Progress") }, - { "value": "Cost of Goods Sold", "label": __("Cost of Goods Sold") }, - { "value": "Depreciation", "label": __("Depreciation") }, - { "value": "Equity", "label": __("Equity") }, - { "value": "Expense Account", "label": __("Expense Account") }, - { "value": "Expenses Included In Asset Valuation", "label": __("Expenses Included In Asset Valuation") }, - { "value": "Expenses Included In Valuation", "label": __("Expenses Included In Valuation") }, - { "value": "Fixed Asset", "label": __("Fixed Asset") }, - { "value": "Income Account", "label": __("Income Account") }, - { "value": "Payable", "label": __("Payable") }, - { "value": "Receivable", "label": __("Receivable") }, - { "value": "Round Off", "label": __("Round Off") }, - { "value": "Stock", "label": __("Stock") }, - { "value": "Stock Adjustment", "label": __("Stock Adjustment") }, - { "value": "Stock Received But Not Billed", "label": __("Stock Received But Not Billed") }, - { "value": "Tax", "label": __("Tax") }, - { "value": "Temporary", "label": __("Temporary") }, + { value: "Accumulated Depreciation", label: __("Accumulated Depreciation") }, + { value: "Asset Received But Not Billed", label: __("Asset Received But Not Billed") }, + { value: "Bank", label: __("Bank") }, + { value: "Cash", label: __("Cash") }, + { value: "Chargeble", label: __("Chargeble") }, + { value: "Capital Work in Progress", label: __("Capital Work in Progress") }, + { value: "Cost of Goods Sold", label: __("Cost of Goods Sold") }, + { value: "Depreciation", label: __("Depreciation") }, + { value: "Equity", label: __("Equity") }, + { value: "Expense Account", label: __("Expense Account") }, + { + value: "Expenses Included In Asset Valuation", + label: __("Expenses Included In Asset Valuation"), + }, + { value: "Expenses Included In Valuation", label: __("Expenses Included In Valuation") }, + { value: "Fixed Asset", label: __("Fixed Asset") }, + { value: "Income Account", label: __("Income Account") }, + { value: "Payable", label: __("Payable") }, + { value: "Receivable", label: __("Receivable") }, + { value: "Round Off", label: __("Round Off") }, + { value: "Stock", label: __("Stock") }, + { value: "Stock Adjustment", label: __("Stock Adjustment") }, + { value: "Stock Received But Not Billed", label: __("Stock Received But Not Billed") }, + { value: "Tax", label: __("Tax") }, + { value: "Temporary", label: __("Temporary") }, ], }, - - ] -} + ], +}; diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.js b/erpnext/accounts/report/accounts_payable/accounts_payable.js index dc2e8a8518b..61a3a96d5fe 100644 --- a/erpnext/accounts/report/accounts_payable/accounts_payable.js +++ b/erpnext/accounts/report/accounts_payable/accounts_payable.js @@ -2,193 +2,194 @@ // License: GNU General Public License v3. See license.txt frappe.query_reports["Accounts Payable"] = { - "filters": [ + filters: [ { - "fieldname": "company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "reqd": 1, - "default": frappe.defaults.get_user_default("Company") + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + reqd: 1, + default: frappe.defaults.get_user_default("Company"), }, { - "fieldname": "report_date", - "label": __("Posting Date"), - "fieldtype": "Date", - "default": frappe.datetime.get_today() + fieldname: "report_date", + label: __("Posting Date"), + fieldtype: "Date", + default: frappe.datetime.get_today(), }, { - "fieldname": "finance_book", - "label": __("Finance Book"), - "fieldtype": "Link", - "options": "Finance Book" + fieldname: "finance_book", + label: __("Finance Book"), + fieldtype: "Link", + options: "Finance Book", }, { - "fieldname": "cost_center", - "label": __("Cost Center"), - "fieldtype": "Link", - "options": "Cost Center", + fieldname: "cost_center", + label: __("Cost Center"), + fieldtype: "Link", + options: "Cost Center", get_query: () => { - var company = frappe.query_report.get_filter_value('company'); + var company = frappe.query_report.get_filter_value("company"); return { filters: { - 'company': company - } - } - } - }, - { - "fieldname": "party_account", - "label": __("Payable Account"), - "fieldtype": "Link", - "options": "Account", - get_query: () => { - var company = frappe.query_report.get_filter_value('company'); - return { - filters: { - 'company': company, - 'account_type': 'Payable', - 'is_group': 0 - } + company: company, + }, }; - } + }, }, { - "fieldname": "ageing_based_on", - "label": __("Ageing Based On"), - "fieldtype": "Select", - "options": 'Posting Date\nDue Date\nSupplier Invoice Date', - "default": "Due Date" + fieldname: "party_account", + label: __("Payable Account"), + fieldtype: "Link", + options: "Account", + get_query: () => { + var company = frappe.query_report.get_filter_value("company"); + return { + filters: { + company: company, + account_type: "Payable", + is_group: 0, + }, + }; + }, }, { - "fieldname": "range1", - "label": __("Ageing Range 1"), - "fieldtype": "Int", - "default": "30", - "reqd": 1 + fieldname: "ageing_based_on", + label: __("Ageing Based On"), + fieldtype: "Select", + options: "Posting Date\nDue Date\nSupplier Invoice Date", + default: "Due Date", }, { - "fieldname": "range2", - "label": __("Ageing Range 2"), - "fieldtype": "Int", - "default": "60", - "reqd": 1 + fieldname: "range1", + label: __("Ageing Range 1"), + fieldtype: "Int", + default: "30", + reqd: 1, }, { - "fieldname": "range3", - "label": __("Ageing Range 3"), - "fieldtype": "Int", - "default": "90", - "reqd": 1 + fieldname: "range2", + label: __("Ageing Range 2"), + fieldtype: "Int", + default: "60", + reqd: 1, }, { - "fieldname": "range4", - "label": __("Ageing Range 4"), - "fieldtype": "Int", - "default": "120", - "reqd": 1 + fieldname: "range3", + label: __("Ageing Range 3"), + fieldtype: "Int", + default: "90", + reqd: 1, }, { - "fieldname": "payment_terms_template", - "label": __("Payment Terms Template"), - "fieldtype": "Link", - "options": "Payment Terms Template" + fieldname: "range4", + label: __("Ageing Range 4"), + fieldtype: "Int", + default: "120", + reqd: 1, }, { - "fieldname":"party_type", - "label": __("Party Type"), - "fieldtype": "Autocomplete", + fieldname: "payment_terms_template", + label: __("Payment Terms Template"), + fieldtype: "Link", + options: "Payment Terms Template", + }, + { + fieldname: "party_type", + label: __("Party Type"), + fieldtype: "Autocomplete", options: get_party_type_options(), - on_change: function() { - frappe.query_report.set_filter_value('party', ""); - frappe.query_report.toggle_filter_display('supplier_group', frappe.query_report.get_filter_value('party_type') !== "Supplier"); - } + on_change: function () { + frappe.query_report.set_filter_value("party", ""); + frappe.query_report.toggle_filter_display( + "supplier_group", + frappe.query_report.get_filter_value("party_type") !== "Supplier" + ); + }, }, { - "fieldname":"party", - "label": __("Party"), - "fieldtype": "MultiSelectList", - get_data: function(txt) { + fieldname: "party", + label: __("Party"), + fieldtype: "MultiSelectList", + get_data: function (txt) { if (!frappe.query_report.filters) return; - let party_type = frappe.query_report.get_filter_value('party_type'); + let party_type = frappe.query_report.get_filter_value("party_type"); if (!party_type) return; return frappe.db.get_link_options(party_type, txt); }, }, { - "fieldname": "supplier_group", - "label": __("Supplier Group"), - "fieldtype": "Link", - "options": "Supplier Group", - "hidden": 1 + fieldname: "supplier_group", + label: __("Supplier Group"), + fieldtype: "Link", + options: "Supplier Group", + hidden: 1, }, { - "fieldname": "group_by_party", - "label": __("Group By Supplier"), - "fieldtype": "Check" + fieldname: "group_by_party", + label: __("Group By Supplier"), + fieldtype: "Check", }, { - "fieldname": "based_on_payment_terms", - "label": __("Based On Payment Terms"), - "fieldtype": "Check", + fieldname: "based_on_payment_terms", + label: __("Based On Payment Terms"), + fieldtype: "Check", }, { - "fieldname": "show_remarks", - "label": __("Show Remarks"), - "fieldtype": "Check", + fieldname: "show_remarks", + label: __("Show Remarks"), + fieldtype: "Check", }, { - "fieldname": "show_future_payments", - "label": __("Show Future Payments"), - "fieldtype": "Check", + fieldname: "show_future_payments", + label: __("Show Future Payments"), + fieldtype: "Check", }, { - "fieldname": "for_revaluation_journals", - "label": __("Revaluation Journals"), - "fieldtype": "Check", + fieldname: "for_revaluation_journals", + label: __("Revaluation Journals"), + fieldtype: "Check", }, { - "fieldname": "in_party_currency", - "label": __("In Party Currency"), - "fieldtype": "Check", + fieldname: "in_party_currency", + label: __("In Party Currency"), + fieldtype: "Check", }, { - "fieldname": "ignore_accounts", - "label": __("Group by Voucher"), - "fieldtype": "Check", + fieldname: "ignore_accounts", + label: __("Group by Voucher"), + fieldtype: "Check", }, - ], - "formatter": function(value, row, column, data, default_formatter) { + formatter: function (value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); if (data && data.bold) { value = value.bold(); - } return value; }, - onload: function(report) { - report.page.add_inner_button(__("Accounts Payable Summary"), function() { + onload: function (report) { + report.page.add_inner_button(__("Accounts Payable Summary"), function () { var filters = report.get_values(); - frappe.set_route('query-report', 'Accounts Payable Summary', {company: filters.company}); + frappe.set_route("query-report", "Accounts Payable Summary", { company: filters.company }); }); - } -} + }, +}; -erpnext.utils.add_dimensions('Accounts Payable', 9); +erpnext.utils.add_dimensions("Accounts Payable", 9); function get_party_type_options() { let options = []; - frappe.db.get_list( - "Party Type", {filters:{"account_type": "Payable"}, fields:['name']} - ).then((res) => { - res.forEach((party_type) => { - options.push(party_type.name); + frappe.db + .get_list("Party Type", { filters: { account_type: "Payable" }, fields: ["name"] }) + .then((res) => { + res.forEach((party_type) => { + options.push(party_type.name); + }); }); - }); return options; } 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 0f206b1cf42..92ea9e8f598 100644 --- a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js +++ b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js @@ -2,140 +2,143 @@ // License: GNU General Public License v3. See license.txt frappe.query_reports["Accounts Payable Summary"] = { - "filters": [ + filters: [ { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company") + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), }, { - "fieldname":"report_date", - "label": __("Posting Date"), - "fieldtype": "Date", - "default": frappe.datetime.get_today() + fieldname: "report_date", + label: __("Posting Date"), + fieldtype: "Date", + default: frappe.datetime.get_today(), }, { - "fieldname":"ageing_based_on", - "label": __("Ageing Based On"), - "fieldtype": "Select", - "options": 'Posting Date\nDue Date', - "default": "Due Date" + fieldname: "ageing_based_on", + label: __("Ageing Based On"), + fieldtype: "Select", + options: "Posting Date\nDue Date", + default: "Due Date", }, { - "fieldname":"range1", - "label": __("Ageing Range 1"), - "fieldtype": "Int", - "default": "30", - "reqd": 1 + 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: "range2", + label: __("Ageing Range 2"), + fieldtype: "Int", + default: "60", + reqd: 1, }, { - "fieldname":"range3", - "label": __("Ageing Range 3"), - "fieldtype": "Int", - "default": "90", - "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: "range4", + label: __("Ageing Range 4"), + fieldtype: "Int", + default: "120", + reqd: 1, }, { - "fieldname":"finance_book", - "label": __("Finance Book"), - "fieldtype": "Link", - "options": "Finance Book" + fieldname: "finance_book", + label: __("Finance Book"), + fieldtype: "Link", + options: "Finance Book", }, { - "fieldname":"cost_center", - "label": __("Cost Center"), - "fieldtype": "Link", - "options": "Cost Center", + fieldname: "cost_center", + label: __("Cost Center"), + fieldtype: "Link", + options: "Cost Center", get_query: () => { - var company = frappe.query_report.get_filter_value('company'); + var company = frappe.query_report.get_filter_value("company"); return { filters: { - 'company': company - } - } - } + company: company, + }, + }; + }, }, { - "fieldname":"party_type", - "label": __("Party Type"), - "fieldtype": "Autocomplete", + fieldname: "party_type", + label: __("Party Type"), + fieldtype: "Autocomplete", options: get_party_type_options(), - on_change: function() { - frappe.query_report.set_filter_value('party', ""); - frappe.query_report.toggle_filter_display('supplier_group', frappe.query_report.get_filter_value('party_type') !== "Supplier"); - } + on_change: function () { + frappe.query_report.set_filter_value("party", ""); + frappe.query_report.toggle_filter_display( + "supplier_group", + frappe.query_report.get_filter_value("party_type") !== "Supplier" + ); + }, }, { - "fieldname":"party", - "label": __("Party"), - "fieldtype": "MultiSelectList", - get_data: function(txt) { + fieldname: "party", + label: __("Party"), + fieldtype: "MultiSelectList", + get_data: function (txt) { if (!frappe.query_report.filters) return; - let party_type = frappe.query_report.get_filter_value('party_type'); + let party_type = frappe.query_report.get_filter_value("party_type"); if (!party_type) return; return frappe.db.get_link_options(party_type, txt); }, }, { - "fieldname":"payment_terms_template", - "label": __("Payment Terms Template"), - "fieldtype": "Link", - "options": "Payment Terms Template" + fieldname: "payment_terms_template", + label: __("Payment Terms Template"), + fieldtype: "Link", + options: "Payment Terms Template", }, { - "fieldname":"supplier_group", - "label": __("Supplier Group"), - "fieldtype": "Link", - "options": "Supplier Group" + fieldname: "supplier_group", + label: __("Supplier Group"), + fieldtype: "Link", + options: "Supplier Group", }, { - "fieldname":"based_on_payment_terms", - "label": __("Based On Payment Terms"), - "fieldtype": "Check", + fieldname: "based_on_payment_terms", + label: __("Based On Payment Terms"), + fieldtype: "Check", }, { - "fieldname": "for_revaluation_journals", - "label": __("Revaluation Journals"), - "fieldtype": "Check", - } + fieldname: "for_revaluation_journals", + label: __("Revaluation Journals"), + fieldtype: "Check", + }, ], - onload: function(report) { - report.page.add_inner_button(__("Accounts Payable"), function() { + onload: function (report) { + report.page.add_inner_button(__("Accounts Payable"), function () { var filters = report.get_values(); - frappe.set_route('query-report', 'Accounts Payable', {company: filters.company}); + frappe.set_route("query-report", "Accounts Payable", { company: filters.company }); }); - } -} + }, +}; -erpnext.utils.add_dimensions('Accounts Payable Summary', 9); +erpnext.utils.add_dimensions("Accounts Payable Summary", 9); function get_party_type_options() { let options = []; - frappe.db.get_list( - "Party Type", {filters:{"account_type": "Payable"}, fields:['name']} - ).then((res) => { - res.forEach((party_type) => { - options.push(party_type.name); + frappe.db + .get_list("Party Type", { filters: { account_type: "Payable" }, fields: ["name"] }) + .then((res) => { + res.forEach((party_type) => { + options.push(party_type.name); + }); }); - }); return options; -} \ No newline at end of file +} diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js index 28aa7bf9ecf..7e4563ee85e 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js @@ -4,224 +4,224 @@ frappe.provide("erpnext.utils"); frappe.query_reports["Accounts Receivable"] = { - "filters": [ + filters: [ { - "fieldname": "company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "reqd": 1, - "default": frappe.defaults.get_user_default("Company") + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + reqd: 1, + default: frappe.defaults.get_user_default("Company"), }, { - "fieldname": "report_date", - "label": __("Posting Date"), - "fieldtype": "Date", - "default": frappe.datetime.get_today() + fieldname: "report_date", + label: __("Posting Date"), + fieldtype: "Date", + default: frappe.datetime.get_today(), }, { - "fieldname": "finance_book", - "label": __("Finance Book"), - "fieldtype": "Link", - "options": "Finance Book" + fieldname: "finance_book", + label: __("Finance Book"), + fieldtype: "Link", + options: "Finance Book", }, { - "fieldname": "cost_center", - "label": __("Cost Center"), - "fieldtype": "Link", - "options": "Cost Center", + fieldname: "cost_center", + label: __("Cost Center"), + fieldtype: "Link", + options: "Cost Center", get_query: () => { - var company = frappe.query_report.get_filter_value('company'); + var company = frappe.query_report.get_filter_value("company"); return { filters: { - 'company': company - } + company: company, + }, }; - } + }, }, { - "fieldname":"party_type", - "label": __("Party Type"), - "fieldtype": "Autocomplete", + fieldname: "party_type", + label: __("Party Type"), + fieldtype: "Autocomplete", options: get_party_type_options(), - on_change: function() { - frappe.query_report.set_filter_value('party', ""); - frappe.query_report.toggle_filter_display('customer_group', frappe.query_report.get_filter_value('party_type') !== "Customer"); - } + on_change: function () { + frappe.query_report.set_filter_value("party", ""); + frappe.query_report.toggle_filter_display( + "customer_group", + frappe.query_report.get_filter_value("party_type") !== "Customer" + ); + }, }, { - "fieldname":"party", - "label": __("Party"), - "fieldtype": "MultiSelectList", - get_data: function(txt) { + fieldname: "party", + label: __("Party"), + fieldtype: "MultiSelectList", + get_data: function (txt) { if (!frappe.query_report.filters) return; - let party_type = frappe.query_report.get_filter_value('party_type'); + let party_type = frappe.query_report.get_filter_value("party_type"); if (!party_type) return; return frappe.db.get_link_options(party_type, txt); }, }, { - "fieldname": "party_account", - "label": __("Receivable Account"), - "fieldtype": "Link", - "options": "Account", + fieldname: "party_account", + label: __("Receivable Account"), + fieldtype: "Link", + options: "Account", get_query: () => { - var company = frappe.query_report.get_filter_value('company'); + var company = frappe.query_report.get_filter_value("company"); return { filters: { - 'company': company, - 'account_type': 'Receivable', - 'is_group': 0 - } + company: company, + account_type: "Receivable", + is_group: 0, + }, }; - } + }, }, { - "fieldname": "ageing_based_on", - "label": __("Ageing Based On"), - "fieldtype": "Select", - "options": 'Posting Date\nDue Date', - "default": "Due Date" + fieldname: "ageing_based_on", + label: __("Ageing Based On"), + fieldtype: "Select", + options: "Posting Date\nDue Date", + default: "Due Date", }, { - "fieldname": "range1", - "label": __("Ageing Range 1"), - "fieldtype": "Int", - "default": "30", - "reqd": 1 + 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: "range2", + label: __("Ageing Range 2"), + fieldtype: "Int", + default: "60", + reqd: 1, }, { - "fieldname": "range3", - "label": __("Ageing Range 3"), - "fieldtype": "Int", - "default": "90", - "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: "range4", + label: __("Ageing Range 4"), + fieldtype: "Int", + default: "120", + reqd: 1, }, { - "fieldname":"customer_group", - "label": __("Customer Group"), - "fieldtype": "MultiSelectList", - "options": "Customer Group", - get_data: function(txt) { - return frappe.db.get_link_options('Customer Group', txt); - } + fieldname: "customer_group", + label: __("Customer Group"), + fieldtype: "MultiSelectList", + options: "Customer Group", + get_data: function (txt) { + return frappe.db.get_link_options("Customer Group", txt); + }, }, { - "fieldname": "payment_terms_template", - "label": __("Payment Terms Template"), - "fieldtype": "Link", - "options": "Payment Terms Template" + fieldname: "payment_terms_template", + label: __("Payment Terms Template"), + fieldtype: "Link", + options: "Payment Terms Template", }, { - "fieldname": "sales_partner", - "label": __("Sales Partner"), - "fieldtype": "Link", - "options": "Sales Partner" + fieldname: "sales_partner", + label: __("Sales Partner"), + fieldtype: "Link", + options: "Sales Partner", }, { - "fieldname": "sales_person", - "label": __("Sales Person"), - "fieldtype": "Link", - "options": "Sales Person" + fieldname: "sales_person", + label: __("Sales Person"), + fieldtype: "Link", + options: "Sales Person", }, { - "fieldname": "territory", - "label": __("Territory"), - "fieldtype": "Link", - "options": "Territory" + fieldname: "territory", + label: __("Territory"), + fieldtype: "Link", + options: "Territory", }, { - "fieldname": "group_by_party", - "label": __("Group By Customer"), - "fieldtype": "Check" + fieldname: "group_by_party", + label: __("Group By Customer"), + fieldtype: "Check", }, { - "fieldname": "based_on_payment_terms", - "label": __("Based On Payment Terms"), - "fieldtype": "Check", + fieldname: "based_on_payment_terms", + label: __("Based On Payment Terms"), + fieldtype: "Check", }, { - "fieldname": "show_future_payments", - "label": __("Show Future Payments"), - "fieldtype": "Check", + fieldname: "show_future_payments", + label: __("Show Future Payments"), + fieldtype: "Check", }, { - "fieldname": "show_delivery_notes", - "label": __("Show Linked Delivery Notes"), - "fieldtype": "Check", + fieldname: "show_delivery_notes", + label: __("Show Linked Delivery Notes"), + fieldtype: "Check", }, { - "fieldname": "show_sales_person", - "label": __("Show Sales Person"), - "fieldtype": "Check", + fieldname: "show_sales_person", + label: __("Show Sales Person"), + fieldtype: "Check", }, { - "fieldname": "show_remarks", - "label": __("Show Remarks"), - "fieldtype": "Check", + fieldname: "show_remarks", + label: __("Show Remarks"), + fieldtype: "Check", }, { - "fieldname": "for_revaluation_journals", - "label": __("Revaluation Journals"), - "fieldtype": "Check", + fieldname: "for_revaluation_journals", + label: __("Revaluation Journals"), + fieldtype: "Check", }, { - "fieldname": "in_party_currency", - "label": __("In Party Currency"), - "fieldtype": "Check", + fieldname: "in_party_currency", + label: __("In Party Currency"), + fieldtype: "Check", }, { - "fieldname": "ignore_accounts", - "label": __("Group by Voucher"), - "fieldtype": "Check", - } - + fieldname: "ignore_accounts", + label: __("Group by Voucher"), + fieldtype: "Check", + }, ], - "formatter": function(value, row, column, data, default_formatter) { + formatter: function (value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); if (data && data.bold) { value = value.bold(); - } return value; }, - onload: function(report) { - report.page.add_inner_button(__("Accounts Receivable Summary"), function() { + onload: function (report) { + report.page.add_inner_button(__("Accounts Receivable Summary"), function () { var filters = report.get_values(); - frappe.set_route('query-report', 'Accounts Receivable Summary', {company: filters.company}); + frappe.set_route("query-report", "Accounts Receivable Summary", { company: filters.company }); }); - } -} - -erpnext.utils.add_dimensions('Accounts Receivable', 9); + }, +}; +erpnext.utils.add_dimensions("Accounts Receivable", 9); function get_party_type_options() { let options = []; - frappe.db.get_list( - "Party Type", {filters:{"account_type": "Receivable"}, fields:['name']} - ).then((res) => { - res.forEach((party_type) => { - options.push(party_type.name); + frappe.db + .get_list("Party Type", { filters: { account_type: "Receivable" }, fields: ["name"] }) + .then((res) => { + res.forEach((party_type) => { + options.push(party_type.name); + }); }); - }); return options; } 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 2f6d2582b33..964abc23747 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js +++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js @@ -2,168 +2,171 @@ // License: GNU General Public License v3. See license.txt frappe.query_reports["Accounts Receivable Summary"] = { - "filters": [ + filters: [ { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company") + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), }, { - "fieldname":"report_date", - "label": __("Posting Date"), - "fieldtype": "Date", - "default": frappe.datetime.get_today() + fieldname: "report_date", + label: __("Posting Date"), + fieldtype: "Date", + default: frappe.datetime.get_today(), }, { - "fieldname":"ageing_based_on", - "label": __("Ageing Based On"), - "fieldtype": "Select", - "options": 'Posting Date\nDue Date', - "default": "Due Date" + fieldname: "ageing_based_on", + label: __("Ageing Based On"), + fieldtype: "Select", + options: "Posting Date\nDue Date", + default: "Due Date", }, { - "fieldname":"range1", - "label": __("Ageing Range 1"), - "fieldtype": "Int", - "default": "30", - "reqd": 1 + 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: "range2", + label: __("Ageing Range 2"), + fieldtype: "Int", + default: "60", + reqd: 1, }, { - "fieldname":"range3", - "label": __("Ageing Range 3"), - "fieldtype": "Int", - "default": "90", - "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: "range4", + label: __("Ageing Range 4"), + fieldtype: "Int", + default: "120", + reqd: 1, }, { - "fieldname":"finance_book", - "label": __("Finance Book"), - "fieldtype": "Link", - "options": "Finance Book" + fieldname: "finance_book", + label: __("Finance Book"), + fieldtype: "Link", + options: "Finance Book", }, { - "fieldname":"cost_center", - "label": __("Cost Center"), - "fieldtype": "Link", - "options": "Cost Center", + fieldname: "cost_center", + label: __("Cost Center"), + fieldtype: "Link", + options: "Cost Center", get_query: () => { - var company = frappe.query_report.get_filter_value('company'); + var company = frappe.query_report.get_filter_value("company"); return { filters: { - 'company': company - } - } - } + company: company, + }, + }; + }, }, { - "fieldname":"party_type", - "label": __("Party Type"), - "fieldtype": "Autocomplete", + fieldname: "party_type", + label: __("Party Type"), + fieldtype: "Autocomplete", options: get_party_type_options(), - on_change: function() { - frappe.query_report.set_filter_value('party', ""); - frappe.query_report.toggle_filter_display('customer_group', frappe.query_report.get_filter_value('party_type') !== "Customer"); - } + on_change: function () { + frappe.query_report.set_filter_value("party", ""); + frappe.query_report.toggle_filter_display( + "customer_group", + frappe.query_report.get_filter_value("party_type") !== "Customer" + ); + }, }, { - "fieldname":"party", - "label": __("Party"), - "fieldtype": "MultiSelectList", - get_data: function(txt) { + fieldname: "party", + label: __("Party"), + fieldtype: "MultiSelectList", + get_data: function (txt) { if (!frappe.query_report.filters) return; - let party_type = frappe.query_report.get_filter_value('party_type'); + let party_type = frappe.query_report.get_filter_value("party_type"); if (!party_type) return; return frappe.db.get_link_options(party_type, txt); }, }, { - "fieldname":"customer_group", - "label": __("Customer Group"), - "fieldtype": "Link", - "options": "Customer Group" + fieldname: "customer_group", + label: __("Customer Group"), + fieldtype: "Link", + options: "Customer Group", }, { - "fieldname":"payment_terms_template", - "label": __("Payment Terms Template"), - "fieldtype": "Link", - "options": "Payment Terms Template" + fieldname: "payment_terms_template", + label: __("Payment Terms Template"), + fieldtype: "Link", + options: "Payment Terms Template", }, { - "fieldname":"territory", - "label": __("Territory"), - "fieldtype": "Link", - "options": "Territory" + fieldname: "territory", + label: __("Territory"), + fieldtype: "Link", + options: "Territory", }, { - "fieldname":"sales_partner", - "label": __("Sales Partner"), - "fieldtype": "Link", - "options": "Sales Partner" + fieldname: "sales_partner", + label: __("Sales Partner"), + fieldtype: "Link", + options: "Sales Partner", }, { - "fieldname":"sales_person", - "label": __("Sales Person"), - "fieldtype": "Link", - "options": "Sales Person" + fieldname: "sales_person", + label: __("Sales Person"), + fieldtype: "Link", + options: "Sales Person", }, { - "fieldname":"based_on_payment_terms", - "label": __("Based On Payment Terms"), - "fieldtype": "Check", + fieldname: "based_on_payment_terms", + label: __("Based On Payment Terms"), + fieldtype: "Check", }, { - "fieldname":"show_future_payments", - "label": __("Show Future Payments"), - "fieldtype": "Check", + fieldname: "show_future_payments", + label: __("Show Future Payments"), + fieldtype: "Check", }, { - "fieldname":"show_gl_balance", - "label": __("Show GL Balance"), - "fieldtype": "Check", + fieldname: "show_gl_balance", + label: __("Show GL Balance"), + fieldtype: "Check", }, { - "fieldname": "for_revaluation_journals", - "label": __("Revaluation Journals"), - "fieldtype": "Check", - } + fieldname: "for_revaluation_journals", + label: __("Revaluation Journals"), + fieldtype: "Check", + }, ], - onload: function(report) { - report.page.add_inner_button(__("Accounts Receivable"), function() { + onload: function (report) { + report.page.add_inner_button(__("Accounts Receivable"), function () { var filters = report.get_values(); - frappe.set_route('query-report', 'Accounts Receivable', { company: filters.company }); + frappe.set_route("query-report", "Accounts Receivable", { company: filters.company }); }); - } -} + }, +}; -erpnext.utils.add_dimensions('Accounts Receivable Summary', 9); +erpnext.utils.add_dimensions("Accounts Receivable Summary", 9); function get_party_type_options() { let options = []; - frappe.db.get_list( - "Party Type", {filters:{"account_type": "Receivable"}, fields:['name']} - ).then((res) => { - res.forEach((party_type) => { - options.push(party_type.name); + frappe.db + .get_list("Party Type", { filters: { account_type: "Receivable" }, fields: ["name"] }) + .then((res) => { + res.forEach((party_type) => { + options.push(party_type.name); + }); }); - }); return options; -} \ No newline at end of file +} diff --git a/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.js b/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.js index 12b94347e00..db49ccb79a8 100644 --- a/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.js +++ b/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.js @@ -2,58 +2,58 @@ // For license information, please see license.txt frappe.query_reports["Asset Depreciation Ledger"] = { - "filters": [ + filters: [ { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company"), - "reqd": 1 + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, }, { - "fieldname":"from_date", - "label": __("From Date"), - "fieldtype": "Date", - "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), - "reqd": 1 + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), + reqd: 1, }, { - "fieldname":"to_date", - "label": __("To Date"), - "fieldtype": "Date", - "default": frappe.datetime.get_today(), - "reqd": 1 + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.datetime.get_today(), + reqd: 1, }, { - "fieldname":"asset", - "label": __("Asset"), - "fieldtype": "Link", - "options": "Asset" + fieldname: "asset", + label: __("Asset"), + fieldtype: "Link", + options: "Asset", }, { - "fieldname":"asset_category", - "label": __("Asset Category"), - "fieldtype": "Link", - "options": "Asset Category" + fieldname: "asset_category", + label: __("Asset Category"), + fieldtype: "Link", + options: "Asset Category", }, { - "fieldname":"cost_center", - "label": __("Cost Center"), - "fieldtype": "Link", - "options": "Cost Center" + fieldname: "cost_center", + label: __("Cost Center"), + fieldtype: "Link", + options: "Cost Center", }, { - "fieldname":"finance_book", - "label": __("Finance Book"), - "fieldtype": "Link", - "options": "Finance Book" + fieldname: "finance_book", + label: __("Finance Book"), + fieldtype: "Link", + options: "Finance Book", }, { - "fieldname": "include_default_book_assets", - "label": __("Include Default FB Assets"), - "fieldtype": "Check", - "default": 1 + fieldname: "include_default_book_assets", + label: __("Include Default FB Assets"), + fieldtype: "Check", + default: 1, }, - ] -} + ], +}; 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 1da35cd95bf..215d431e786 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 @@ -2,34 +2,34 @@ // For license information, please see license.txt frappe.query_reports["Asset Depreciations and Balances"] = { - "filters": [ + filters: [ { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company"), - "reqd": 1 + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, }, { - "fieldname":"from_date", - "label": __("From Date"), - "fieldtype": "Date", - "default": frappe.defaults.get_user_default("year_start_date"), - "reqd": 1 + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.defaults.get_user_default("year_start_date"), + reqd: 1, }, { - "fieldname":"to_date", - "label": __("To Date"), - "fieldtype": "Date", - "default": frappe.defaults.get_user_default("year_end_date"), - "reqd": 1 + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.defaults.get_user_default("year_end_date"), + reqd: 1, }, { - "fieldname":"asset_category", - "label": __("Asset Category"), - "fieldtype": "Link", - "options": "Asset Category" - } - ] -} + fieldname: "asset_category", + label: __("Asset Category"), + fieldtype: "Link", + options: "Asset Category", + }, + ], +}; diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.js b/erpnext/accounts/report/balance_sheet/balance_sheet.js index 57de73e72d0..605dff8b608 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.js +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.js @@ -1,35 +1,33 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.require("assets/erpnext/js/financial_statements.js", function() { +frappe.require("assets/erpnext/js/financial_statements.js", function () { frappe.query_reports["Balance Sheet"] = $.extend({}, erpnext.financial_statements); - erpnext.utils.add_dimensions('Balance Sheet', 10); + erpnext.utils.add_dimensions("Balance Sheet", 10); - frappe.query_reports["Balance Sheet"]["filters"].push( - { - "fieldname": "selected_view", - "label": __("Select View"), - "fieldtype": "Select", - "options": [ - { "value": "Report", "label": __("Report View") }, - { "value": "Growth", "label": __("Growth View") } - ], - "default": "Report", - "reqd": 1 - }, - ); frappe.query_reports["Balance Sheet"]["filters"].push({ - "fieldname": "accumulated_values", - "label": __("Accumulated Values"), - "fieldtype": "Check", - "default": 1 + fieldname: "selected_view", + label: __("Select View"), + fieldtype: "Select", + options: [ + { value: "Report", label: __("Report View") }, + { value: "Growth", label: __("Growth View") }, + ], + default: "Report", + reqd: 1, + }); + frappe.query_reports["Balance Sheet"]["filters"].push({ + fieldname: "accumulated_values", + label: __("Accumulated Values"), + fieldtype: "Check", + default: 1, }); frappe.query_reports["Balance Sheet"]["filters"].push({ - "fieldname": "include_default_book_entries", - "label": __("Include Default FB Entries"), - "fieldtype": "Check", - "default": 1 + fieldname: "include_default_book_entries", + label: __("Include Default FB Entries"), + fieldtype: "Check", + default: 1, }); }); diff --git a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.js b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.js index f0b6c6b20ac..c15d43012db 100644 --- a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.js +++ b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.js @@ -2,37 +2,38 @@ // License: GNU General Public License v3. See license.txt frappe.query_reports["Bank Clearance Summary"] = { - "filters": [ + filters: [ { - "fieldname":"from_date", - "label": __("From Date"), - "fieldtype": "Date", - "default": frappe.defaults.get_user_default("year_start_date"), - "width": "80" + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.defaults.get_user_default("year_start_date"), + width: "80", }, { - "fieldname":"to_date", - "label": __("To Date"), - "fieldtype": "Date", - "default": frappe.datetime.get_today() + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.datetime.get_today(), }, { - "fieldname":"account", - "label": __("Bank Account"), - "fieldtype": "Link", - "options": "Account", - "reqd": 1, - "default": frappe.defaults.get_user_default("Company")? - locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"]: "", - "get_query": function() { + fieldname: "account", + label: __("Bank Account"), + fieldtype: "Link", + options: "Account", + reqd: 1, + default: frappe.defaults.get_user_default("Company") + ? locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"] + : "", + get_query: function () { return { - "query": "erpnext.controllers.queries.get_account_list", - "filters": [ - ['Account', 'account_type', 'in', 'Bank, Cash'], - ['Account', 'is_group', '=', 0], - ] - } - } + query: "erpnext.controllers.queries.get_account_list", + filters: [ + ["Account", "account_type", "in", "Bank, Cash"], + ["Account", "is_group", "=", 0], + ], + }; + }, }, - ] -} + ], +}; 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 9bb6a14c677..efcfa7a5ee5 100644 --- a/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js +++ b/erpnext/accounts/report/bank_reconciliation_statement/bank_reconciliation_statement.js @@ -2,47 +2,48 @@ // License: GNU General Public License v3. See license.txt frappe.query_reports["Bank Reconciliation Statement"] = { - "filters": [ + filters: [ { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "reqd": 1, - "default": frappe.defaults.get_user_default("Company") + 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') + 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], - ] - } - } + 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: "report_date", + label: __("Date"), + fieldtype: "Date", + default: frappe.datetime.get_today(), + reqd: 1, }, { - "fieldname":"include_pos_transactions", - "label": __("Include POS Transactions"), - "fieldtype": "Check" + fieldname: "include_pos_transactions", + label: __("Include POS Transactions"), + fieldtype: "Check", }, - ] -} + ], +}; diff --git a/erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.js b/erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.js index e1fccb6e720..65c8e822227 100644 --- a/erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.js +++ b/erpnext/accounts/report/billed_items_to_be_received/billed_items_to_be_received.js @@ -2,28 +2,28 @@ // For license information, please see license.txt /* eslint-disable */ -frappe.query_reports['Billed Items To Be Received'] = { - 'filters': [ +frappe.query_reports["Billed Items To Be Received"] = { + filters: [ { - 'label': __('Company'), - 'fieldname': 'company', - 'fieldtype': 'Link', - 'options': 'Company', - 'reqd': 1, - 'default': frappe.defaults.get_default('Company') + label: __("Company"), + fieldname: "company", + fieldtype: "Link", + options: "Company", + reqd: 1, + default: frappe.defaults.get_default("Company"), }, { - 'label': __('As on Date'), - 'fieldname': 'posting_date', - 'fieldtype': 'Date', - 'reqd': 1, - 'default': get_today() + label: __("As on Date"), + fieldname: "posting_date", + fieldtype: "Date", + reqd: 1, + default: get_today(), }, { - 'label': __('Purchase Invoice'), - 'fieldname': 'purchase_invoice', - 'fieldtype': 'Link', - 'options': 'Purchase Invoice' - } - ] + label: __("Purchase Invoice"), + fieldname: "purchase_invoice", + fieldtype: "Link", + options: "Purchase Invoice", + }, + ], }; diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.js b/erpnext/accounts/report/budget_variance_report/budget_variance_report.js index c9ddde9b5fb..156ae8f2f2a 100644 --- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.js +++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.js @@ -2,37 +2,35 @@ // License: GNU General Public License v3. See license.txt frappe.query_reports["Budget Variance Report"] = { - "filters": get_filters(), - "formatter": function (value, row, column, data, default_formatter) { + filters: get_filters(), + formatter: function (value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); if (column.fieldname.includes(__("variance"))) { - if (data[column.fieldname] < 0) { value = "" + value + ""; - } - else if (data[column.fieldname] > 0) { + } else if (data[column.fieldname] > 0) { value = "" + value + ""; } } return value; - } -} + }, +}; function get_filters() { function get_dimensions() { let result = []; frappe.call({ method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.get_dimensions", args: { - 'with_cost_center_and_project': true + with_cost_center_and_project: true, }, async: false, - callback: function(r) { - if(!r.exc) { - result = r.message[0].map(elem => elem.document_type); + callback: function (r) { + if (!r.exc) { + result = r.message[0].map((elem) => elem.document_type); } - } + }, }); return result; } @@ -46,7 +44,7 @@ function get_filters() { fieldtype: "Link", options: "Fiscal Year", default: frappe.sys_defaults.fiscal_year, - reqd: 1 + reqd: 1, }, { fieldname: "to_fiscal_year", @@ -54,20 +52,20 @@ function get_filters() { fieldtype: "Link", options: "Fiscal Year", default: frappe.sys_defaults.fiscal_year, - reqd: 1 + reqd: 1, }, { fieldname: "period", label: __("Period"), fieldtype: "Select", options: [ - { "value": "Monthly", "label": __("Monthly") }, - { "value": "Quarterly", "label": __("Quarterly") }, - { "value": "Half-Yearly", "label": __("Half-Yearly") }, - { "value": "Yearly", "label": __("Yearly") } + { value: "Monthly", label: __("Monthly") }, + { value: "Quarterly", label: __("Quarterly") }, + { value: "Half-Yearly", label: __("Half-Yearly") }, + { value: "Yearly", label: __("Yearly") }, ], default: "Yearly", - reqd: 1 + reqd: 1, }, { fieldname: "company", @@ -75,7 +73,7 @@ function get_filters() { fieldtype: "Link", options: "Company", default: frappe.defaults.get_user_default("Company"), - reqd: 1 + reqd: 1, }, { fieldname: "budget_against", @@ -84,32 +82,31 @@ function get_filters() { options: budget_against_options, default: "Cost Center", reqd: 1, - on_change: function() { + on_change: function () { frappe.query_report.set_filter_value("budget_against_filter", []); frappe.query_report.refresh(); - } + }, }, { - fieldname:"budget_against_filter", - label: __('Dimension Filter'), + fieldname: "budget_against_filter", + label: __("Dimension Filter"), fieldtype: "MultiSelectList", - get_data: function(txt) { + get_data: function (txt) { if (!frappe.query_report.filters) return; - let budget_against = frappe.query_report.get_filter_value('budget_against'); + let budget_against = frappe.query_report.get_filter_value("budget_against"); if (!budget_against) return; return frappe.db.get_link_options(budget_against, txt); - } + }, }, { - fieldname:"show_cumulative", + fieldname: "show_cumulative", label: __("Show Cumulative Amount"), fieldtype: "Check", default: 0, }, - ] + ]; return filters; } - diff --git a/erpnext/accounts/report/cash_flow/cash_flow.js b/erpnext/accounts/report/cash_flow/cash_flow.js index b9c62b52861..c3560c8d3f5 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.js +++ b/erpnext/accounts/report/cash_flow/cash_flow.js @@ -1,11 +1,10 @@ // Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.require("assets/erpnext/js/financial_statements.js", function() { - frappe.query_reports["Cash Flow"] = $.extend({}, - erpnext.financial_statements); +frappe.require("assets/erpnext/js/financial_statements.js", function () { + frappe.query_reports["Cash Flow"] = $.extend({}, erpnext.financial_statements); - erpnext.utils.add_dimensions('Cash Flow', 10); + erpnext.utils.add_dimensions("Cash Flow", 10); // The last item in the array is the definition for Presentation Currency // filter. It won't be used in cash flow for now so we pop it. Please take @@ -13,12 +12,10 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { frappe.query_reports["Cash Flow"]["filters"].splice(8, 1); - frappe.query_reports["Cash Flow"]["filters"].push( - { - "fieldname": "include_default_book_entries", - "label": __("Include Default FB Entries"), - "fieldtype": "Check", - "default": 1 - } - ); + frappe.query_reports["Cash Flow"]["filters"].push({ + fieldname: "include_default_book_entries", + label: __("Include Default FB Entries"), + fieldtype: "Check", + default: 1, + }); }); diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js index cb1083b06c1..f700cb7759d 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js @@ -2,125 +2,153 @@ // For license information, please see license.txt /* eslint-disable */ -frappe.require("assets/erpnext/js/financial_statements.js", function() { +frappe.require("assets/erpnext/js/financial_statements.js", function () { frappe.query_reports["Consolidated Financial Statement"] = { - "filters": [ + filters: [ { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company"), - "reqd": 1 + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, }, { - "fieldname":"filter_based_on", - "label": __("Filter Based On"), - "fieldtype": "Select", - "options": ["Fiscal Year", "Date Range"], - "default": ["Fiscal Year"], - "reqd": 1, - on_change: function() { - let filter_based_on = frappe.query_report.get_filter_value('filter_based_on'); - frappe.query_report.toggle_filter_display('from_fiscal_year', filter_based_on === 'Date Range'); - frappe.query_report.toggle_filter_display('to_fiscal_year', filter_based_on === 'Date Range'); - frappe.query_report.toggle_filter_display('period_start_date', filter_based_on === 'Fiscal Year'); - frappe.query_report.toggle_filter_display('period_end_date', filter_based_on === 'Fiscal Year'); + fieldname: "filter_based_on", + label: __("Filter Based On"), + fieldtype: "Select", + options: ["Fiscal Year", "Date Range"], + default: ["Fiscal Year"], + reqd: 1, + on_change: function () { + let filter_based_on = frappe.query_report.get_filter_value("filter_based_on"); + frappe.query_report.toggle_filter_display( + "from_fiscal_year", + filter_based_on === "Date Range" + ); + frappe.query_report.toggle_filter_display( + "to_fiscal_year", + filter_based_on === "Date Range" + ); + frappe.query_report.toggle_filter_display( + "period_start_date", + filter_based_on === "Fiscal Year" + ); + frappe.query_report.toggle_filter_display( + "period_end_date", + filter_based_on === "Fiscal Year" + ); frappe.query_report.refresh(); - } + }, }, { - "fieldname":"period_start_date", - "label": __("Start Date"), - "fieldtype": "Date", - "hidden": 1, - "reqd": 1 + fieldname: "period_start_date", + label: __("Start Date"), + fieldtype: "Date", + hidden: 1, + reqd: 1, }, { - "fieldname":"period_end_date", - "label": __("End Date"), - "fieldtype": "Date", - "hidden": 1, - "reqd": 1 + fieldname: "period_end_date", + label: __("End Date"), + fieldtype: "Date", + hidden: 1, + reqd: 1, }, { - "fieldname":"from_fiscal_year", - "label": __("Start Year"), - "fieldtype": "Link", - "options": "Fiscal Year", - "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), - "reqd": 1, + fieldname: "from_fiscal_year", + label: __("Start Year"), + fieldtype: "Link", + options: "Fiscal Year", + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), + reqd: 1, on_change: () => { - frappe.model.with_doc("Fiscal Year", frappe.query_report.get_filter_value('from_fiscal_year'), function(r) { - let year_start_date = frappe.model.get_value("Fiscal Year", frappe.query_report.get_filter_value('from_fiscal_year'), "year_start_date"); - frappe.query_report.set_filter_value({ - period_start_date: year_start_date - }); - }); - } + frappe.model.with_doc( + "Fiscal Year", + frappe.query_report.get_filter_value("from_fiscal_year"), + function (r) { + let year_start_date = frappe.model.get_value( + "Fiscal Year", + frappe.query_report.get_filter_value("from_fiscal_year"), + "year_start_date" + ); + frappe.query_report.set_filter_value({ + period_start_date: year_start_date, + }); + } + ); + }, }, { - "fieldname":"to_fiscal_year", - "label": __("End Year"), - "fieldtype": "Link", - "options": "Fiscal Year", - "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), - "reqd": 1, + fieldname: "to_fiscal_year", + label: __("End Year"), + fieldtype: "Link", + options: "Fiscal Year", + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), + reqd: 1, on_change: () => { - frappe.model.with_doc("Fiscal Year", frappe.query_report.get_filter_value('to_fiscal_year'), function(r) { - let year_end_date = frappe.model.get_value("Fiscal Year", frappe.query_report.get_filter_value('to_fiscal_year'), "year_end_date"); - frappe.query_report.set_filter_value({ - period_end_date: year_end_date - }); - }); - } + frappe.model.with_doc( + "Fiscal Year", + frappe.query_report.get_filter_value("to_fiscal_year"), + function (r) { + let year_end_date = frappe.model.get_value( + "Fiscal Year", + frappe.query_report.get_filter_value("to_fiscal_year"), + "year_end_date" + ); + frappe.query_report.set_filter_value({ + period_end_date: year_end_date, + }); + } + ); + }, }, { - "fieldname":"finance_book", - "label": __("Finance Book"), - "fieldtype": "Link", - "options": "Finance Book" + fieldname: "finance_book", + label: __("Finance Book"), + fieldtype: "Link", + options: "Finance Book", }, { - "fieldname":"report", - "label": __("Report"), - "fieldtype": "Select", - "options": ["Profit and Loss Statement", "Balance Sheet", "Cash Flow"], - "default": "Balance Sheet", - "reqd": 1 + fieldname: "report", + label: __("Report"), + fieldtype: "Select", + options: ["Profit and Loss Statement", "Balance Sheet", "Cash Flow"], + default: "Balance Sheet", + reqd: 1, }, { - "fieldname": "presentation_currency", - "label": __("Currency"), - "fieldtype": "Select", - "options": erpnext.get_presentation_currency_list(), - "default": frappe.defaults.get_user_default("Currency") + fieldname: "presentation_currency", + label: __("Currency"), + fieldtype: "Select", + options: erpnext.get_presentation_currency_list(), + default: frappe.defaults.get_user_default("Currency"), }, { - "fieldname":"accumulated_in_group_company", - "label": __("Accumulated Values in Group Company"), - "fieldtype": "Check", - "default": 0 + fieldname: "accumulated_in_group_company", + label: __("Accumulated Values in Group Company"), + fieldtype: "Check", + default: 0, }, { - "fieldname": "include_default_book_entries", - "label": __("Include Default FB Entries"), - "fieldtype": "Check", - "default": 1 + fieldname: "include_default_book_entries", + label: __("Include Default FB Entries"), + fieldtype: "Check", + default: 1, }, { - "fieldname": "show_zero_values", - "label": __("Show zero values"), - "fieldtype": "Check" - } + fieldname: "show_zero_values", + label: __("Show zero values"), + fieldtype: "Check", + }, ], - "formatter": function(value, row, column, data, default_formatter) { - if (data && column.fieldname=="account") { + formatter: function (value, row, column, data, default_formatter) { + if (data && column.fieldname == "account") { value = data.account_name || value; column.link_onclick = - "erpnext.financial_statements.open_general_ledger(" + JSON.stringify(data) + ")"; + "erpnext.financial_statements.open_general_ledger(" + JSON.stringify(data) + ")"; column.is_tree = true; } @@ -138,16 +166,16 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { } return value; }, - onload: function() { + onload: function () { let fiscal_year = erpnext.utils.get_fiscal_year(frappe.datetime.get_today()); - frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) { + 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 + period_end_date: fy.year_end_date, }); }); - } - } + }, + }; }); diff --git a/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.js b/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.js index a1236316638..dec2ebd2520 100644 --- a/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.js +++ b/erpnext/accounts/report/customer_ledger_summary/customer_ledger_summary.js @@ -3,95 +3,95 @@ /* eslint-disable */ frappe.query_reports["Customer Ledger Summary"] = { - "filters": [ + filters: [ { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company") + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), }, { - "fieldname":"from_date", - "label": __("From Date"), - "fieldtype": "Date", - "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), - "reqd": 1, - "width": "60px" + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), + reqd: 1, + width: "60px", }, { - "fieldname":"to_date", - "label": __("To Date"), - "fieldtype": "Date", - "default": frappe.datetime.get_today(), - "reqd": 1, - "width": "60px" + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.datetime.get_today(), + reqd: 1, + width: "60px", }, { - "fieldname":"finance_book", - "label": __("Finance Book"), - "fieldtype": "Link", - "options": "Finance Book" + fieldname: "finance_book", + label: __("Finance Book"), + fieldtype: "Link", + options: "Finance Book", }, { - "fieldname":"party", - "label": __("Customer"), - "fieldtype": "Link", - "options": "Customer", + fieldname: "party", + label: __("Customer"), + fieldtype: "Link", + options: "Customer", on_change: () => { - var party = frappe.query_report.get_filter_value('party'); + var party = frappe.query_report.get_filter_value("party"); if (party) { - frappe.db.get_value('Customer', party, ["tax_id", "customer_name"], function(value) { - frappe.query_report.set_filter_value('tax_id', value["tax_id"]); - frappe.query_report.set_filter_value('customer_name', value["customer_name"]); + frappe.db.get_value("Customer", party, ["tax_id", "customer_name"], function (value) { + frappe.query_report.set_filter_value("tax_id", value["tax_id"]); + frappe.query_report.set_filter_value("customer_name", value["customer_name"]); }); } else { - frappe.query_report.set_filter_value('tax_id', ""); - frappe.query_report.set_filter_value('customer_name', ""); + frappe.query_report.set_filter_value("tax_id", ""); + frappe.query_report.set_filter_value("customer_name", ""); } - } + }, }, { - "fieldname":"customer_group", - "label": __("Customer Group"), - "fieldtype": "Link", - "options": "Customer Group" + fieldname: "customer_group", + label: __("Customer Group"), + fieldtype: "Link", + options: "Customer Group", }, { - "fieldname":"payment_terms_template", - "label": __("Payment Terms Template"), - "fieldtype": "Link", - "options": "Payment Terms Template" + fieldname: "payment_terms_template", + label: __("Payment Terms Template"), + fieldtype: "Link", + options: "Payment Terms Template", }, { - "fieldname":"territory", - "label": __("Territory"), - "fieldtype": "Link", - "options": "Territory" + fieldname: "territory", + label: __("Territory"), + fieldtype: "Link", + options: "Territory", }, { - "fieldname":"sales_partner", - "label": __("Sales Partner"), - "fieldtype": "Link", - "options": "Sales Partner" + fieldname: "sales_partner", + label: __("Sales Partner"), + fieldtype: "Link", + options: "Sales Partner", }, { - "fieldname":"sales_person", - "label": __("Sales Person"), - "fieldtype": "Link", - "options": "Sales Person" + fieldname: "sales_person", + label: __("Sales Person"), + fieldtype: "Link", + options: "Sales Person", }, { - "fieldname":"tax_id", - "label": __("Tax Id"), - "fieldtype": "Data", - "hidden": 1 + fieldname: "tax_id", + label: __("Tax Id"), + fieldtype: "Data", + hidden: 1, }, { - "fieldname":"customer_name", - "label": __("Customer Name"), - "fieldtype": "Data", - "hidden": 1 - } - ] + fieldname: "customer_name", + label: __("Customer Name"), + fieldtype: "Data", + hidden: 1, + }, + ], }; diff --git a/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.js b/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.js index 96e0c844ca5..63f1062fd33 100644 --- a/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.js +++ b/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.js @@ -5,110 +5,118 @@ function get_filters() { let filters = [ { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company"), - "reqd": 1 + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, }, { - "fieldname":"filter_based_on", - "label": __("Filter Based On"), - "fieldtype": "Select", - "options": ["Fiscal Year", "Date Range"], - "default": ["Fiscal Year"], - "reqd": 1, - on_change: function() { - let filter_based_on = frappe.query_report.get_filter_value('filter_based_on'); - frappe.query_report.toggle_filter_display('from_fiscal_year', filter_based_on === 'Date Range'); - frappe.query_report.toggle_filter_display('to_fiscal_year', filter_based_on === 'Date Range'); - frappe.query_report.toggle_filter_display('period_start_date', filter_based_on === 'Fiscal Year'); - frappe.query_report.toggle_filter_display('period_end_date', filter_based_on === 'Fiscal Year'); + fieldname: "filter_based_on", + label: __("Filter Based On"), + fieldtype: "Select", + options: ["Fiscal Year", "Date Range"], + default: ["Fiscal Year"], + reqd: 1, + on_change: function () { + let filter_based_on = frappe.query_report.get_filter_value("filter_based_on"); + frappe.query_report.toggle_filter_display( + "from_fiscal_year", + filter_based_on === "Date Range" + ); + frappe.query_report.toggle_filter_display("to_fiscal_year", filter_based_on === "Date Range"); + frappe.query_report.toggle_filter_display( + "period_start_date", + filter_based_on === "Fiscal Year" + ); + frappe.query_report.toggle_filter_display( + "period_end_date", + filter_based_on === "Fiscal Year" + ); frappe.query_report.refresh(); - } + }, }, { - "fieldname":"period_start_date", - "label": __("Start Date"), - "fieldtype": "Date", - "hidden": 1, - "reqd": 1 + fieldname: "period_start_date", + label: __("Start Date"), + fieldtype: "Date", + hidden: 1, + reqd: 1, }, { - "fieldname":"period_end_date", - "label": __("End Date"), - "fieldtype": "Date", - "hidden": 1, - "reqd": 1 + fieldname: "period_end_date", + label: __("End Date"), + fieldtype: "Date", + hidden: 1, + reqd: 1, }, { - "fieldname":"from_fiscal_year", - "label": __("Start Year"), - "fieldtype": "Link", - "options": "Fiscal Year", - "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), - "reqd": 1 + fieldname: "from_fiscal_year", + label: __("Start Year"), + fieldtype: "Link", + options: "Fiscal Year", + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), + reqd: 1, }, { - "fieldname":"to_fiscal_year", - "label": __("End Year"), - "fieldtype": "Link", - "options": "Fiscal Year", - "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), - "reqd": 1 + fieldname: "to_fiscal_year", + label: __("End Year"), + fieldtype: "Link", + options: "Fiscal Year", + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), + reqd: 1, }, { - "fieldname": "periodicity", - "label": __("Periodicity"), - "fieldtype": "Select", - "options": [ - { "value": "Monthly", "label": __("Monthly") }, - { "value": "Quarterly", "label": __("Quarterly") }, - { "value": "Half-Yearly", "label": __("Half-Yearly") }, - { "value": "Yearly", "label": __("Yearly") } + fieldname: "periodicity", + label: __("Periodicity"), + fieldtype: "Select", + options: [ + { value: "Monthly", label: __("Monthly") }, + { value: "Quarterly", label: __("Quarterly") }, + { value: "Half-Yearly", label: __("Half-Yearly") }, + { value: "Yearly", label: __("Yearly") }, ], - "default": "Monthly", - "reqd": 1 + default: "Monthly", + reqd: 1, }, { - "fieldname": "type", - "label": __("Invoice Type"), - "fieldtype": "Select", - "options": [ - { "value": "Revenue", "label": __("Revenue") }, - { "value": "Expense", "label": __("Expense") } + fieldname: "type", + label: __("Invoice Type"), + fieldtype: "Select", + options: [ + { value: "Revenue", label: __("Revenue") }, + { value: "Expense", label: __("Expense") }, ], - "default": "Revenue", - "reqd": 1 + default: "Revenue", + reqd: 1, }, { - "fieldname" : "with_upcoming_postings", - "label": __("Show with upcoming revenue/expense"), - "fieldtype": "Check", - "default": 1 - } - ] + fieldname: "with_upcoming_postings", + label: __("Show with upcoming revenue/expense"), + fieldtype: "Check", + default: 1, + }, + ]; return filters; } frappe.query_reports["Deferred Revenue and Expense"] = { - "filters": get_filters(), - "formatter": function(value, row, column, data, default_formatter){ + filters: get_filters(), + formatter: function (value, row, column, data, default_formatter) { return default_formatter(value, row, column, data); }, - onload: function(report){ + onload: function (report) { let fiscal_year = erpnext.utils.get_fiscal_year(frappe.datetime.get_today()); - frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) { + 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 + period_end_date: fy.year_end_date, }); }); - } + }, }; - diff --git a/erpnext/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.js b/erpnext/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.js index 1454b2c73c8..f6051d7e04f 100644 --- a/erpnext/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.js +++ b/erpnext/accounts/report/delivered_items_to_be_billed/delivered_items_to_be_billed.js @@ -2,7 +2,5 @@ // For license information, please see license.txt frappe.query_reports["Delivered Items To Be Billed"] = { - "filters": [ - - ] -} + filters: [], +}; diff --git a/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.js b/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.js index 9d416db4fdd..27c0c660f01 100644 --- a/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.js +++ b/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.js @@ -2,83 +2,81 @@ // For license information, please see license.txt /* eslint-disable */ -frappe.require("assets/erpnext/js/financial_statements.js", function() { +frappe.require("assets/erpnext/js/financial_statements.js", function () { frappe.query_reports["Dimension-wise Accounts Balance Report"] = { - "filters": [ + filters: [ { - "fieldname": "company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company"), - "reqd": 1 + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, }, { - "fieldname": "fiscal_year", - "label": __("Fiscal Year"), - "fieldtype": "Link", - "options": "Fiscal Year", - "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), - "reqd": 1, - "on_change": function(query_report) { + fieldname: "fiscal_year", + label: __("Fiscal Year"), + fieldtype: "Link", + options: "Fiscal Year", + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), + reqd: 1, + on_change: function (query_report) { var fiscal_year = query_report.get_values().fiscal_year; if (!fiscal_year) { return; } - frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) { + 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({ from_date: fy.year_start_date, - to_date: fy.year_end_date + to_date: fy.year_end_date, }); }); - } + }, }, { - "fieldname": "from_date", - "label": __("From Date"), - "fieldtype": "Date", - "default": frappe.defaults.get_user_default("year_start_date"), - "reqd": 1 + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.defaults.get_user_default("year_start_date"), + reqd: 1, }, { - "fieldname": "to_date", - "label": __("To Date"), - "fieldtype": "Date", - "default": frappe.defaults.get_user_default("year_end_date"), - "reqd": 1 + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.defaults.get_user_default("year_end_date"), + reqd: 1, }, { - "fieldname": "finance_book", - "label": __("Finance Book"), - "fieldtype": "Link", - "options": "Finance Book", + fieldname: "finance_book", + label: __("Finance Book"), + fieldtype: "Link", + options: "Finance Book", }, { - "fieldname": "dimension", - "label": __("Select Dimension"), - "fieldtype": "Select", - "default": "Cost Center", - "options": get_accounting_dimension_options(), - "reqd": 1, + fieldname: "dimension", + label: __("Select Dimension"), + fieldtype: "Select", + default: "Cost Center", + options: get_accounting_dimension_options(), + reqd: 1, }, ], - "formatter": erpnext.financial_statements.formatter, - "tree": true, - "name_field": "account", - "parent_field": "parent_account", - "initial_depth": 3 - } - + formatter: erpnext.financial_statements.formatter, + tree: true, + name_field: "account", + parent_field: "parent_account", + initial_depth: 3, + }; }); function get_accounting_dimension_options() { - let options =["Cost Center", "Project"]; - frappe.db.get_list('Accounting Dimension', - {fields:['document_type']}).then((res) => { - res.forEach((dimension) => { - options.push(dimension.document_type); - }); + let options = ["Cost Center", "Project"]; + frappe.db.get_list("Accounting Dimension", { fields: ["document_type"] }).then((res) => { + res.forEach((dimension) => { + options.push(dimension.document_type); }); - return options + }); + return options; } diff --git a/erpnext/accounts/report/general_and_payment_ledger_comparison/general_and_payment_ledger_comparison.js b/erpnext/accounts/report/general_and_payment_ledger_comparison/general_and_payment_ledger_comparison.js index 7e6b0537e87..4eadf342be8 100644 --- a/erpnext/accounts/report/general_and_payment_ledger_comparison/general_and_payment_ledger_comparison.js +++ b/erpnext/accounts/report/general_and_payment_ledger_comparison/general_and_payment_ledger_comparison.js @@ -4,49 +4,49 @@ function get_filters() { let filters = [ { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company"), - "reqd": 1 + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, }, { - "fieldname":"period_start_date", - "label": __("Start Date"), - "fieldtype": "Date", - "reqd": 1, - "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1) + fieldname: "period_start_date", + label: __("Start Date"), + fieldtype: "Date", + reqd: 1, + default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), }, { - "fieldname":"period_end_date", - "label": __("End Date"), - "fieldtype": "Date", - "reqd": 1, - "default": frappe.datetime.get_today() + fieldname: "period_end_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, { + 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"]] + account_type: ["in", ["Receivable", "Payable"]], }); - } + }, }, { - "fieldname":"voucher_no", - "label": __("Voucher No"), - "fieldtype": "Data", - "width": 100, + fieldname: "voucher_no", + label: __("Voucher No"), + fieldtype: "Data", + width: 100, }, - ] + ]; return filters; } frappe.query_reports["General and Payment Ledger Comparison"] = { - "filters": get_filters() + filters: get_filters(), }; diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js index 2b5abcb7240..3d0a2b78e5e 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.js +++ b/erpnext/accounts/report/general_ledger/general_ledger.js @@ -2,114 +2,114 @@ // License: GNU General Public License v3. See license.txt frappe.query_reports["General Ledger"] = { - "filters": [ + filters: [ { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company"), - "reqd": 1 + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, }, { - "fieldname":"finance_book", - "label": __("Finance Book"), - "fieldtype": "Link", - "options": "Finance Book" + fieldname: "finance_book", + label: __("Finance Book"), + fieldtype: "Link", + options: "Finance Book", }, { - "fieldname":"from_date", - "label": __("From Date"), - "fieldtype": "Date", - "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), - "reqd": 1, - "width": "60px" + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), + reqd: 1, + width: "60px", }, { - "fieldname":"to_date", - "label": __("To Date"), - "fieldtype": "Date", - "default": frappe.datetime.get_today(), - "reqd": 1, - "width": "60px" + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.datetime.get_today(), + reqd: 1, + width: "60px", }, { - "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") + 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"), }); - } + }, }, { - "fieldname":"voucher_no", - "label": __("Voucher No"), - "fieldtype": "Data", - on_change: function() { - frappe.query_report.set_filter_value('group_by', "Group by Voucher (Consolidated)"); - } + fieldname: "voucher_no", + label: __("Voucher No"), + fieldtype: "Data", + on_change: function () { + frappe.query_report.set_filter_value("group_by", "Group by Voucher (Consolidated)"); + }, }, { - "fieldtype": "Break", + fieldtype: "Break", }, { - "fieldname":"party_type", - "label": __("Party Type"), - "fieldtype": "Autocomplete", + fieldname: "party_type", + label: __("Party Type"), + fieldtype: "Autocomplete", options: Object.keys(frappe.boot.party_account_types), - on_change: function() { - frappe.query_report.set_filter_value('party', ""); - } + on_change: function () { + frappe.query_report.set_filter_value("party", ""); + }, }, { - "fieldname":"party", - "label": __("Party"), - "fieldtype": "MultiSelectList", - get_data: function(txt) { + fieldname: "party", + label: __("Party"), + fieldtype: "MultiSelectList", + get_data: function (txt) { if (!frappe.query_report.filters) return; - let party_type = frappe.query_report.get_filter_value('party_type'); + let party_type = frappe.query_report.get_filter_value("party_type"); if (!party_type) return; return frappe.db.get_link_options(party_type, txt); }, - on_change: function() { - var party_type = frappe.query_report.get_filter_value('party_type'); - var parties = frappe.query_report.get_filter_value('party'); + on_change: function () { + var party_type = frappe.query_report.get_filter_value("party_type"); + var parties = frappe.query_report.get_filter_value("party"); - if(!party_type || parties.length === 0 || parties.length > 1) { - frappe.query_report.set_filter_value('party_name', ""); - frappe.query_report.set_filter_value('tax_id', ""); + if (!party_type || parties.length === 0 || parties.length > 1) { + frappe.query_report.set_filter_value("party_name", ""); + frappe.query_report.set_filter_value("tax_id", ""); return; } else { var party = parties[0]; var fieldname = erpnext.utils.get_party_name(party_type) || "name"; - frappe.db.get_value(party_type, party, fieldname, function(value) { - frappe.query_report.set_filter_value('party_name', value[fieldname]); + frappe.db.get_value(party_type, party, fieldname, function (value) { + frappe.query_report.set_filter_value("party_name", value[fieldname]); }); if (party_type === "Customer" || party_type === "Supplier") { - frappe.db.get_value(party_type, party, "tax_id", function(value) { - frappe.query_report.set_filter_value('tax_id', value["tax_id"]); + frappe.db.get_value(party_type, party, "tax_id", function (value) { + frappe.query_report.set_filter_value("tax_id", value["tax_id"]); }); } } - } + }, }, { - "fieldname":"party_name", - "label": __("Party Name"), - "fieldtype": "Data", - "hidden": 1 + fieldname: "party_name", + label: __("Party Name"), + fieldtype: "Data", + hidden: 1, }, { - "fieldname":"group_by", - "label": __("Group by"), - "fieldtype": "Select", - "options": [ + fieldname: "group_by", + label: __("Group by"), + fieldtype: "Select", + options: [ "", { label: __("Group by Voucher"), @@ -128,80 +128,78 @@ frappe.query_reports["General Ledger"] = { value: "Group by Party", }, ], - "default": "Group by Voucher (Consolidated)" + default: "Group by Voucher (Consolidated)", }, { - "fieldname":"tax_id", - "label": __("Tax Id"), - "fieldtype": "Data", - "hidden": 1 + fieldname: "tax_id", + label: __("Tax Id"), + fieldtype: "Data", + hidden: 1, }, { - "fieldname": "presentation_currency", - "label": __("Currency"), - "fieldtype": "Select", - "options": erpnext.get_presentation_currency_list() + fieldname: "presentation_currency", + label: __("Currency"), + fieldtype: "Select", + options: erpnext.get_presentation_currency_list(), }, { - "fieldname":"cost_center", - "label": __("Cost Center"), - "fieldtype": "MultiSelectList", - get_data: function(txt) { - return frappe.db.get_link_options('Cost Center', txt, { - company: frappe.query_report.get_filter_value("company") + fieldname: "cost_center", + label: __("Cost Center"), + fieldtype: "MultiSelectList", + get_data: function (txt) { + return frappe.db.get_link_options("Cost Center", txt, { + company: frappe.query_report.get_filter_value("company"), }); - } + }, }, { - "fieldname":"project", - "label": __("Project"), - "fieldtype": "MultiSelectList", - get_data: function(txt) { - return frappe.db.get_link_options('Project', txt, { - company: frappe.query_report.get_filter_value("company") + fieldname: "project", + label: __("Project"), + fieldtype: "MultiSelectList", + get_data: function (txt) { + return frappe.db.get_link_options("Project", txt, { + company: frappe.query_report.get_filter_value("company"), }); - } + }, }, { - "fieldname": "include_dimensions", - "label": __("Consider Accounting Dimensions"), - "fieldtype": "Check", - "default": 1 + fieldname: "include_dimensions", + label: __("Consider Accounting Dimensions"), + fieldtype: "Check", + default: 1, }, { - "fieldname": "show_opening_entries", - "label": __("Show Opening Entries"), - "fieldtype": "Check" + fieldname: "show_opening_entries", + label: __("Show Opening Entries"), + fieldtype: "Check", }, { - "fieldname": "include_default_book_entries", - "label": __("Include Default FB Entries"), - "fieldtype": "Check", - "default": 1 + fieldname: "include_default_book_entries", + label: __("Include Default FB Entries"), + fieldtype: "Check", + default: 1, }, { - "fieldname": "show_cancelled_entries", - "label": __("Show Cancelled Entries"), - "fieldtype": "Check" + fieldname: "show_cancelled_entries", + label: __("Show Cancelled Entries"), + fieldtype: "Check", }, { - "fieldname": "show_net_values_in_party_account", - "label": __("Show Net Values in Party Account"), - "fieldtype": "Check" + fieldname: "show_net_values_in_party_account", + label: __("Show Net Values in Party Account"), + fieldtype: "Check", }, { - "fieldname": "show_remarks", - "label": __("Show Remarks"), - "fieldtype": "Check" + fieldname: "show_remarks", + label: __("Show Remarks"), + fieldtype: "Check", }, { - "fieldname": "ignore_err", - "label": __("Ignore Exchange Rate Revaluation Journals"), - "fieldtype": "Check" - } + fieldname: "ignore_err", + label: __("Ignore Exchange Rate Revaluation Journals"), + fieldtype: "Check", + }, + ], +}; - - ] -} - -erpnext.utils.add_dimensions('General Ledger', 15) +erpnext.utils.add_dimensions("General Ledger", 15); diff --git a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.js b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.js index 92cf36ebc52..3610d1721ea 100644 --- a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.js +++ b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.js @@ -3,19 +3,14 @@ /* eslint-disable */ frappe.query_reports["Gross and Net Profit Report"] = { - "filters": [ + filters: [], +}; +frappe.require("assets/erpnext/js/financial_statements.js", function () { + frappe.query_reports["Gross and Net Profit Report"] = $.extend({}, erpnext.financial_statements); - ] -} -frappe.require("assets/erpnext/js/financial_statements.js", function() { - frappe.query_reports["Gross and Net Profit Report"] = $.extend({}, - erpnext.financial_statements); - - frappe.query_reports["Gross and Net Profit Report"]["filters"].push( - { - "fieldname": "accumulated_values", - "label": __("Accumulated Values"), - "fieldtype": "Check" - } - ); + frappe.query_reports["Gross and Net Profit Report"]["filters"].push({ + fieldname: "accumulated_values", + label: __("Accumulated Values"), + fieldtype: "Check", + }); }); diff --git a/erpnext/accounts/report/gross_profit/gross_profit.js b/erpnext/accounts/report/gross_profit/gross_profit.js index cf4ccef2433..890befe8596 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.js +++ b/erpnext/accounts/report/gross_profit/gross_profit.js @@ -2,60 +2,61 @@ // License: GNU General Public License v3. See license.txt frappe.query_reports["Gross Profit"] = { - "filters": [ + filters: [ { - "fieldname": "company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company"), - "reqd": 1 + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, }, { - "fieldname": "from_date", - "label": __("From Date"), - "fieldtype": "Date", - "default": frappe.defaults.get_user_default("year_start_date"), - "reqd": 1 + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.defaults.get_user_default("year_start_date"), + reqd: 1, }, { - "fieldname": "to_date", - "label": __("To Date"), - "fieldtype": "Date", - "default": frappe.defaults.get_user_default("year_end_date"), - "reqd": 1 + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.defaults.get_user_default("year_end_date"), + reqd: 1, }, { - "fieldname": "sales_invoice", - "label": __("Sales Invoice"), - "fieldtype": "Link", - "options": "Sales Invoice" + fieldname: "sales_invoice", + label: __("Sales Invoice"), + fieldtype: "Link", + options: "Sales Invoice", }, { - "fieldname": "group_by", - "label": __("Group By"), - "fieldtype": "Select", - "options": "Invoice\nItem Code\nItem Group\nBrand\nWarehouse\nCustomer\nCustomer Group\nTerritory\nSales Person\nProject\nMonthly\nPayment Term", - "default": "Invoice" + fieldname: "group_by", + label: __("Group By"), + fieldtype: "Select", + options: + "Invoice\nItem Code\nItem Group\nBrand\nWarehouse\nCustomer\nCustomer Group\nTerritory\nSales Person\nProject\nMonthly\nPayment Term", + default: "Invoice", }, { - "fieldname": "item_group", - "label": __("Item Group"), - "fieldtype": "Link", - "options": "Item Group" + fieldname: "item_group", + label: __("Item Group"), + fieldtype: "Link", + options: "Item Group", }, { - "fieldname": "sales_person", - "label": __("Sales Person"), - "fieldtype": "Link", - "options": "Sales Person" + fieldname: "sales_person", + label: __("Sales Person"), + fieldtype: "Link", + options: "Sales Person", }, ], - "tree": true, - "name_field": "parent", - "parent_field": "parent_invoice", - "initial_depth": 3, - "formatter": function(value, row, column, data, default_formatter) { + tree: true, + name_field: "parent", + parent_field: "parent_invoice", + initial_depth: 3, + formatter: function (value, row, column, data, default_formatter) { if (column.fieldname == "sales_invoice" && column.options == "Item" && data && data.indent == 0) { column._options = "Sales Invoice"; } else { @@ -71,4 +72,4 @@ frappe.query_reports["Gross Profit"] = { return value; }, -} +}; diff --git a/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.js b/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.js index 7908c07a0ad..6aa1943c453 100644 --- a/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.js +++ b/erpnext/accounts/report/inactive_sales_items/inactive_sales_items.js @@ -3,7 +3,7 @@ /* eslint-disable */ frappe.query_reports["Inactive Sales Items"] = { - "filters": [ + filters: [ { fieldname: "territory", label: __("Territory"), @@ -15,27 +15,27 @@ frappe.query_reports["Inactive Sales Items"] = { fieldname: "item", label: __("Item"), fieldtype: "Link", - options: "Item" + options: "Item", }, { fieldname: "item_group", label: __("Item Group"), fieldtype: "Link", - options: "Item Group" + options: "Item Group", }, { fieldname: "based_on", label: __("Based On"), fieldtype: "Select", options: "Sales Order\nSales Invoice", - default: "Sales Order" + default: "Sales Order", }, { fieldname: "days", label: __("Days Since Last order"), fieldtype: "Select", options: [30, 60, 90], - default: 30 + default: 30, }, - ] + ], }; 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 b709ab9b57d..19ce9ffc607 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 @@ -2,59 +2,58 @@ // License: GNU General Public License v3. See license.txt frappe.query_reports["Item-wise Purchase Register"] = { - "filters": [ + filters: [ { - "fieldname":"from_date", - "label": __("From Date"), - "fieldtype": "Date", - "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), - "reqd": 1, + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), + reqd: 1, }, { - "fieldname":"to_date", - "label": __("To Date"), - "fieldtype": "Date", - "default": frappe.datetime.get_today(), - "reqd": 1, + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.datetime.get_today(), + reqd: 1, }, { - "fieldname": "item_code", - "label": __("Item"), - "fieldtype": "Link", - "options": "Item", + fieldname: "item_code", + label: __("Item"), + fieldtype: "Link", + options: "Item", }, { - "fieldname":"supplier", - "label": __("Supplier"), - "fieldtype": "Link", - "options": "Supplier" + fieldname: "supplier", + label: __("Supplier"), + fieldtype: "Link", + options: "Supplier", }, { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company") + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), }, { - "fieldname":"mode_of_payment", - "label": __("Mode of Payment"), - "fieldtype": "Link", - "options": "Mode of Payment" + fieldname: "mode_of_payment", + label: __("Mode of Payment"), + fieldtype: "Link", + options: "Mode of Payment", }, { - "label": __("Group By"), - "fieldname": "group_by", - "fieldtype": "Select", - "options": ["Supplier", "Item Group", "Item", "Invoice"] - } + label: __("Group By"), + fieldname: "group_by", + fieldtype: "Select", + options: ["Supplier", "Item Group", "Item", "Invoice"], + }, ], - "formatter": function(value, row, column, data, default_formatter) { + formatter: function (value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); if (data && data.bold) { value = value.bold(); - } return value; - } -} + }, +}; 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 39fb3ca5ee3..7ed2ebd89ab 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 @@ -2,71 +2,70 @@ // License: GNU General Public License v3. See license.txt frappe.query_reports["Item-wise Sales Register"] = { - "filters": [ + filters: [ { - "fieldname":"from_date", - "label": __("From Date"), - "fieldtype": "Date", - "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), - "reqd": 1, + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), + reqd: 1, }, { - "fieldname":"to_date", - "label": __("To Date"), - "fieldtype": "Date", - "default": frappe.datetime.get_today(), - "reqd": 1, + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.datetime.get_today(), + reqd: 1, }, { - "fieldname": "customer", - "label": __("Customer"), - "fieldtype": "Link", - "options": "Customer" + fieldname: "customer", + label: __("Customer"), + fieldtype: "Link", + options: "Customer", }, { - "fieldname": "company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company") + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), }, { - "fieldname": "mode_of_payment", - "label": __("Mode of Payment"), - "fieldtype": "Link", - "options": "Mode of Payment" + fieldname: "mode_of_payment", + label: __("Mode of Payment"), + fieldtype: "Link", + options: "Mode of Payment", }, { - "fieldname": "warehouse", - "label": __("Warehouse"), - "fieldtype": "Link", - "options": "Warehouse" + fieldname: "warehouse", + label: __("Warehouse"), + fieldtype: "Link", + options: "Warehouse", }, { - "fieldname": "brand", - "label": __("Brand"), - "fieldtype": "Link", - "options": "Brand" + fieldname: "brand", + label: __("Brand"), + fieldtype: "Link", + options: "Brand", }, { - "fieldname": "item_group", - "label": __("Item Group"), - "fieldtype": "Link", - "options": "Item Group" + fieldname: "item_group", + label: __("Item Group"), + fieldtype: "Link", + options: "Item Group", }, { - "label": __("Group By"), - "fieldname": "group_by", - "fieldtype": "Select", - "options": ["Customer Group", "Customer", "Item Group", "Item", "Territory", "Invoice"] - } + label: __("Group By"), + fieldname: "group_by", + fieldtype: "Select", + options: ["Customer Group", "Customer", "Item Group", "Item", "Territory", "Invoice"], + }, ], - "formatter": function(value, row, column, data, default_formatter) { + formatter: function (value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); if (data && data.bold) { value = value.bold(); - } return value; - } -} + }, +}; diff --git a/erpnext/accounts/report/payment_ledger/payment_ledger.js b/erpnext/accounts/report/payment_ledger/payment_ledger.js index a5a4108f1df..e0ea7522b12 100644 --- a/erpnext/accounts/report/payment_ledger/payment_ledger.js +++ b/erpnext/accounts/report/payment_ledger/payment_ledger.js @@ -5,92 +5,89 @@ function get_filters() { let filters = [ { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company"), - "reqd": 1 + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, }, { - "fieldname":"period_start_date", - "label": __("Start Date"), - "fieldtype": "Date", - "reqd": 1, - "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1) + fieldname: "period_start_date", + label: __("Start Date"), + fieldtype: "Date", + reqd: 1, + default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), }, { - "fieldname":"period_end_date", - "label": __("End Date"), - "fieldtype": "Date", - "reqd": 1, - "default": frappe.datetime.get_today() + fieldname: "period_end_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") + 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"), }); - } + }, }, { - "fieldname":"party_type", - "label": __("Party Type"), - "fieldtype": "Link", - "options": "Party Type", - "default": "", - on_change: function() { - frappe.query_report.set_filter_value('party', ""); - } + fieldname: "party_type", + label: __("Party Type"), + fieldtype: "Link", + options: "Party Type", + default: "", + on_change: function () { + frappe.query_report.set_filter_value("party", ""); + }, }, { - "fieldname":"party", - "label": __("Party"), - "fieldtype": "MultiSelectList", - get_data: function(txt) { + fieldname: "party", + label: __("Party"), + fieldtype: "MultiSelectList", + get_data: function (txt) { if (!frappe.query_report.filters) return; - let party_type = frappe.query_report.get_filter_value('party_type'); + let party_type = frappe.query_report.get_filter_value("party_type"); if (!party_type) return; return frappe.db.get_link_options(party_type, txt); }, }, { - "fieldname":"voucher_no", - "label": __("Voucher No"), - "fieldtype": "Data", - "width": 100, + fieldname: "voucher_no", + label: __("Voucher No"), + fieldtype: "Data", + width: 100, }, { - "fieldname":"against_voucher_no", - "label": __("Against Voucher No"), - "fieldtype": "Data", - "width": 100, + fieldname: "against_voucher_no", + label: __("Against Voucher No"), + fieldtype: "Data", + width: 100, }, { - "fieldname":"include_account_currency", - "label": __("Include Account Currency"), - "fieldtype": "Check", - "width": 100, + fieldname: "include_account_currency", + label: __("Include Account Currency"), + fieldtype: "Check", + width: 100, }, { - "fieldname":"group_party", - "label": __("Group by Party"), - "fieldtype": "Check", - "width": 100, + fieldname: "group_party", + label: __("Group by Party"), + fieldtype: "Check", + width: 100, }, - - - - ] + ]; return filters; } frappe.query_reports["Payment Ledger"] = { - "filters": get_filters() + filters: get_filters(), }; diff --git a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.js b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.js index 2343eaa8461..8625c57d46b 100644 --- a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.js +++ b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.js @@ -2,14 +2,14 @@ // License: GNU General Public License v3. See license.txt frappe.query_reports["Payment Period Based On Invoice Date"] = { - "filters": [ + filters: [ { - fieldname:"company", + fieldname: "company", label: __("Company"), fieldtype: "Link", options: "Company", reqd: 1, - default: frappe.defaults.get_user_default("Company") + default: frappe.defaults.get_user_default("Company"), }, { fieldname: "from_date", @@ -18,41 +18,41 @@ frappe.query_reports["Payment Period Based On Invoice Date"] = { default: frappe.defaults.get_user_default("year_start_date"), }, { - fieldname:"to_date", + fieldname: "to_date", label: __("To Date"), fieldtype: "Date", - default: frappe.datetime.get_today() + default: frappe.datetime.get_today(), }, { - fieldname:"payment_type", + fieldname: "payment_type", label: __("Payment Type"), fieldtype: "Select", options: __("Incoming") + "\n" + __("Outgoing"), - default: __("Incoming") + default: __("Incoming"), }, { - "fieldname":"party_type", - "label": __("Party Type"), - "fieldtype": "Link", - "options": "DocType", - "get_query": function() { + fieldname: "party_type", + label: __("Party Type"), + fieldtype: "Link", + options: "DocType", + get_query: function () { return { - filters: {"name": ["in", ["Customer", "Supplier"]]} - } - } + filters: { name: ["in", ["Customer", "Supplier"]] }, + }; + }, }, { - "fieldname":"party", - "label": __("Party"), - "fieldtype": "Dynamic Link", - "get_options": function() { - var party_type = frappe.query_report.get_filter_value('party_type'); - var party = frappe.query_report.get_filter_value('party'); - if(party && !party_type) { + fieldname: "party", + label: __("Party"), + fieldtype: "Dynamic Link", + get_options: function () { + var party_type = frappe.query_report.get_filter_value("party_type"); + var party = frappe.query_report.get_filter_value("party"); + if (party && !party_type) { frappe.throw(__("Please select Party Type first")); } return party_type; - } + }, }, - ] -} + ], +}; diff --git a/erpnext/accounts/report/pos_register/pos_register.js b/erpnext/accounts/report/pos_register/pos_register.js index b8d48d92de0..1200d3e79f2 100644 --- a/erpnext/accounts/report/pos_register/pos_register.js +++ b/erpnext/accounts/report/pos_register/pos_register.js @@ -3,74 +3,73 @@ /* eslint-disable */ frappe.query_reports["POS Register"] = { - "filters": [ + filters: [ { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company"), - "reqd": 1 + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, }, { - "fieldname":"from_date", - "label": __("From Date"), - "fieldtype": "Date", - "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), - "reqd": 1, - "width": "60px" + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), + reqd: 1, + width: "60px", }, { - "fieldname":"to_date", - "label": __("To Date"), - "fieldtype": "Date", - "default": frappe.datetime.get_today(), - "reqd": 1, - "width": "60px" + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.datetime.get_today(), + reqd: 1, + width: "60px", }, { - "fieldname":"pos_profile", - "label": __("POS Profile"), - "fieldtype": "Link", - "options": "POS Profile" + fieldname: "pos_profile", + label: __("POS Profile"), + fieldtype: "Link", + options: "POS Profile", }, { - "fieldname":"cashier", - "label": __("Cashier"), - "fieldtype": "Link", - "options": "User" + fieldname: "cashier", + label: __("Cashier"), + fieldtype: "Link", + options: "User", }, { - "fieldname":"customer", - "label": __("Customer"), - "fieldtype": "Link", - "options": "Customer" + fieldname: "customer", + label: __("Customer"), + fieldtype: "Link", + options: "Customer", }, { - "fieldname":"mode_of_payment", - "label": __("Payment Method"), - "fieldtype": "Link", - "options": "Mode of Payment" + fieldname: "mode_of_payment", + label: __("Payment Method"), + fieldtype: "Link", + options: "Mode of Payment", }, { - "fieldname":"group_by", - "label": __("Group by"), - "fieldtype": "Select", - "options": ["", "POS Profile", "Cashier", "Payment Method", "Customer"], - "default": "POS Profile" + fieldname: "group_by", + label: __("Group by"), + fieldtype: "Select", + options: ["", "POS Profile", "Cashier", "Payment Method", "Customer"], + default: "POS Profile", }, { - "fieldname":"is_return", - "label": __("Is Return"), - "fieldtype": "Check" + fieldname: "is_return", + label: __("Is Return"), + fieldtype: "Check", }, ], - "formatter": function(value, row, column, data, default_formatter) { + formatter: function (value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); if (data && data.bold) { value = value.bold(); - } return value; - } + }, }; diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js index 4e5cd312734..c89a86c37b6 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js @@ -1,34 +1,28 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt +frappe.require("assets/erpnext/js/financial_statements.js", function () { + frappe.query_reports["Profit and Loss Statement"] = $.extend({}, erpnext.financial_statements); -frappe.require("assets/erpnext/js/financial_statements.js", function() { - frappe.query_reports["Profit and Loss Statement"] = $.extend({}, - erpnext.financial_statements); + erpnext.utils.add_dimensions("Profit and Loss Statement", 10); - erpnext.utils.add_dimensions('Profit and Loss Statement', 10); + frappe.query_reports["Profit and Loss Statement"]["filters"].push({ + fieldname: "selected_view", + label: __("Select View"), + fieldtype: "Select", + options: [ + { value: "Report", label: __("Report View") }, + { value: "Growth", label: __("Growth View") }, + { value: "Margin", label: __("Margin View") }, + ], + default: "Report", + reqd: 1, + }); - frappe.query_reports["Profit and Loss Statement"]["filters"].push( - { - "fieldname": "selected_view", - "label": __("Select View"), - "fieldtype": "Select", - "options": [ - { "value": "Report", "label": __("Report View") }, - { "value": "Growth", "label": __("Growth View") }, - { "value": "Margin", "label": __("Margin View") }, - ], - "default": "Report", - "reqd": 1 - }, - ); - - frappe.query_reports["Profit and Loss Statement"]["filters"].push( - { - "fieldname": "include_default_book_entries", - "label": __("Include Default Book Entries"), - "fieldtype": "Check", - "default": 1 - } - ); + frappe.query_reports["Profit and Loss Statement"]["filters"].push({ + fieldname: "include_default_book_entries", + label: __("Include Default Book Entries"), + fieldtype: "Check", + default: 1, + }); }); diff --git a/erpnext/accounts/report/profitability_analysis/profitability_analysis.js b/erpnext/accounts/report/profitability_analysis/profitability_analysis.js index 1ac52fe5206..dcecf7bb47f 100644 --- a/erpnext/accounts/report/profitability_analysis/profitability_analysis.js +++ b/erpnext/accounts/report/profitability_analysis/profitability_analysis.js @@ -1,90 +1,92 @@ // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.require("assets/erpnext/js/financial_statements.js", function() { +frappe.require("assets/erpnext/js/financial_statements.js", function () { frappe.query_reports["Profitability Analysis"] = { - "filters": [ + filters: [ { - "fieldname": "company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company"), - "reqd": 1 + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, }, { - "fieldname": "based_on", - "label": __("Based On"), - "fieldtype": "Select", - "options": ["Cost Center", "Project", "Accounting Dimension"], - "default": "Cost Center", - "reqd": 1, - "on_change": function(query_report){ + fieldname: "based_on", + label: __("Based On"), + fieldtype: "Select", + options: ["Cost Center", "Project", "Accounting Dimension"], + default: "Cost Center", + reqd: 1, + on_change: function (query_report) { let based_on = query_report.get_values().based_on; - if(based_on!='Accounting Dimension'){ + if (based_on != "Accounting Dimension") { frappe.query_report.set_filter_value({ - accounting_dimension: '' + accounting_dimension: "", }); } - } + }, }, { - "fieldname": "accounting_dimension", - "label": __("Accounting Dimension"), - "fieldtype": "Link", - "options": "Accounting Dimension", + fieldname: "accounting_dimension", + label: __("Accounting Dimension"), + fieldtype: "Link", + options: "Accounting Dimension", }, { - "fieldname": "fiscal_year", - "label": __("Fiscal Year"), - "fieldtype": "Link", - "options": "Fiscal Year", - "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), - "reqd": 1, - "on_change": function(query_report) { + fieldname: "fiscal_year", + label: __("Fiscal Year"), + fieldtype: "Link", + options: "Fiscal Year", + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), + reqd: 1, + on_change: function (query_report) { var fiscal_year = query_report.get_values().fiscal_year; if (!fiscal_year) { return; } - frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) { + 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({ from_date: fy.year_start_date, - to_date: fy.year_end_date + to_date: fy.year_end_date, }); }); - } + }, }, { - "fieldname": "from_date", - "label": __("From Date"), - "fieldtype": "Date", - "default": frappe.defaults.get_user_default("year_start_date"), + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.defaults.get_user_default("year_start_date"), }, { - "fieldname": "to_date", - "label": __("To Date"), - "fieldtype": "Date", - "default": frappe.defaults.get_user_default("year_end_date"), + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.defaults.get_user_default("year_end_date"), }, { - "fieldname": "show_zero_values", - "label": __("Show zero values"), - "fieldtype": "Check" - } + fieldname: "show_zero_values", + label: __("Show zero values"), + fieldtype: "Check", + }, ], - "formatter": function(value, row, column, data, default_formatter) { - if (column.fieldname=="account") { + formatter: function (value, row, column, data, default_formatter) { + if (column.fieldname == "account") { value = data.account_name; column.link_onclick = - "frappe.query_reports['Profitability Analysis'].open_profit_and_loss_statement(" + JSON.stringify(data) + ")"; + "frappe.query_reports['Profitability Analysis'].open_profit_and_loss_statement(" + + JSON.stringify(data) + + ")"; column.is_tree = true; } value = default_formatter(value, row, column, data); - if (!data.parent_account && data.based_on != 'project') { + if (!data.parent_account && data.based_on != "project") { value = $(`${value}`); var $value = $(value).css("font-weight", "bold"); if (data.warn_if_negative && data[column.fieldname] < 0) { @@ -96,27 +98,26 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { return value; }, - "open_profit_and_loss_statement": function(data) { + open_profit_and_loss_statement: function (data) { if (!data.account) return; frappe.route_options = { - "company": frappe.query_report.get_filter_value('company'), - "from_fiscal_year": data.fiscal_year, - "to_fiscal_year": data.fiscal_year + company: frappe.query_report.get_filter_value("company"), + from_fiscal_year: data.fiscal_year, + to_fiscal_year: data.fiscal_year, }; - if(data.based_on == 'Cost Center'){ - frappe.route_options["cost_center"] = data.account + if (data.based_on == "Cost Center") { + frappe.route_options["cost_center"] = data.account; } else { - frappe.route_options["project"] = data.account + frappe.route_options["project"] = data.account; } frappe.set_route("query-report", "Profit and Loss Statement"); }, - "tree": true, - "name_field": "account", - "parent_field": "parent_account", - "initial_depth": 3 - } - + tree: true, + name_field: "account", + parent_field: "parent_account", + initial_depth: 3, + }; }); diff --git a/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.js b/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.js index feab96f2652..e3f90f29982 100644 --- a/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.js +++ b/erpnext/accounts/report/purchase_invoice_trends/purchase_invoice_trends.js @@ -1,8 +1,8 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.require("assets/erpnext/js/purchase_trends_filters.js", function() { +frappe.require("assets/erpnext/js/purchase_trends_filters.js", function () { frappe.query_reports["Purchase Invoice Trends"] = { - filters: erpnext.get_purchase_trends_filters() - } + filters: erpnext.get_purchase_trends_filters(), + }; }); diff --git a/erpnext/accounts/report/purchase_register/purchase_register.js b/erpnext/accounts/report/purchase_register/purchase_register.js index 57cb703baea..6366f368552 100644 --- a/erpnext/accounts/report/purchase_register/purchase_register.js +++ b/erpnext/accounts/report/purchase_register/purchase_register.js @@ -2,64 +2,64 @@ // License: GNU General Public License v3. See license.txt frappe.query_reports["Purchase Register"] = { - "filters": [ + filters: [ { - "fieldname":"from_date", - "label": __("From Date"), - "fieldtype": "Date", - "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), - "width": "80" + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), + width: "80", }, { - "fieldname":"to_date", - "label": __("To Date"), - "fieldtype": "Date", - "default": frappe.datetime.get_today() + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.datetime.get_today(), }, { - "fieldname":"supplier", - "label": __("Supplier"), - "fieldtype": "Link", - "options": "Supplier" + fieldname: "supplier", + label: __("Supplier"), + fieldtype: "Link", + options: "Supplier", }, { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company") + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), }, { - "fieldname":"mode_of_payment", - "label": __("Mode of Payment"), - "fieldtype": "Link", - "options": "Mode of Payment" + fieldname: "mode_of_payment", + label: __("Mode of Payment"), + fieldtype: "Link", + options: "Mode of Payment", }, { - "fieldname":"cost_center", - "label": __("Cost Center"), - "fieldtype": "Link", - "options": "Cost Center" + fieldname: "cost_center", + label: __("Cost Center"), + fieldtype: "Link", + options: "Cost Center", }, { - "fieldname":"warehouse", - "label": __("Warehouse"), - "fieldtype": "Link", - "options": "Warehouse" + fieldname: "warehouse", + label: __("Warehouse"), + fieldtype: "Link", + options: "Warehouse", }, { - "fieldname":"item_group", - "label": __("Item Group"), - "fieldtype": "Link", - "options": "Item Group" + fieldname: "item_group", + label: __("Item Group"), + fieldtype: "Link", + options: "Item Group", }, { - "fieldname": "include_payments", - "label": __("Show Ledger View"), - "fieldtype": "Check", - "default": 0 - } - ] -} + fieldname: "include_payments", + label: __("Show Ledger View"), + fieldtype: "Check", + default: 0, + }, + ], +}; -erpnext.utils.add_dimensions('Purchase Register', 7); +erpnext.utils.add_dimensions("Purchase Register", 7); diff --git a/erpnext/accounts/report/received_items_to_be_billed/received_items_to_be_billed.js b/erpnext/accounts/report/received_items_to_be_billed/received_items_to_be_billed.js index 0ee3f577171..ad97f270dd3 100644 --- a/erpnext/accounts/report/received_items_to_be_billed/received_items_to_be_billed.js +++ b/erpnext/accounts/report/received_items_to_be_billed/received_items_to_be_billed.js @@ -2,7 +2,5 @@ // For license information, please see license.txt frappe.query_reports["Received Items To Be Billed"] = { - "filters": [ - - ] -} + filters: [], +}; diff --git a/erpnext/accounts/report/sales_invoice_trends/sales_invoice_trends.js b/erpnext/accounts/report/sales_invoice_trends/sales_invoice_trends.js index e3d43a7de1e..292d827b163 100644 --- a/erpnext/accounts/report/sales_invoice_trends/sales_invoice_trends.js +++ b/erpnext/accounts/report/sales_invoice_trends/sales_invoice_trends.js @@ -1,8 +1,8 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.require("assets/erpnext/js/sales_trends_filters.js", function() { +frappe.require("assets/erpnext/js/sales_trends_filters.js", function () { frappe.query_reports["Sales Invoice Trends"] = { - filters: erpnext.get_sales_trends_filters() - } + filters: erpnext.get_sales_trends_filters(), + }; }); diff --git a/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.js b/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.js index 44e20e83c50..0ec24ab0a2a 100644 --- a/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.js +++ b/erpnext/accounts/report/sales_payment_summary/sales_payment_summary.js @@ -1,45 +1,45 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt frappe.query_reports["Sales Payment Summary"] = { - "filters": [ + filters: [ { - "fieldname":"from_date", - "label": __("From Date"), - "fieldtype": "Date", - "default": frappe.datetime.get_today(), - "reqd": 1, - "width": "80" + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.datetime.get_today(), + reqd: 1, + width: "80", }, { - "fieldname":"to_date", - "label": __("To Date"), - "fieldtype": "Date", - "reqd": 1, - "default": frappe.datetime.get_today() + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + reqd: 1, + default: frappe.datetime.get_today(), }, { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company") + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), }, { - "fieldname":"owner", - "label": __("Owner"), - "fieldtype": "Link", - "options": "User", - "defaults": user + fieldname: "owner", + label: __("Owner"), + fieldtype: "Link", + options: "User", + defaults: user, }, { - "fieldname":"is_pos", - "label": __("Show only POS"), - "fieldtype": "Check" + fieldname: "is_pos", + label: __("Show only POS"), + fieldtype: "Check", }, { - "fieldname":"payment_detail", - "label": __("Show Payment Details"), - "fieldtype": "Check" + fieldname: "payment_detail", + label: __("Show Payment Details"), + fieldtype: "Check", }, - ] + ], }; diff --git a/erpnext/accounts/report/sales_register/sales_register.js b/erpnext/accounts/report/sales_register/sales_register.js index 1a41172a970..b7f82e2367b 100644 --- a/erpnext/accounts/report/sales_register/sales_register.js +++ b/erpnext/accounts/report/sales_register/sales_register.js @@ -2,76 +2,76 @@ // License: GNU General Public License v3. See license.txt frappe.query_reports["Sales Register"] = { - "filters": [ + filters: [ { - "fieldname":"from_date", - "label": __("From Date"), - "fieldtype": "Date", - "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), - "width": "80" + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), + width: "80", }, { - "fieldname":"to_date", - "label": __("To Date"), - "fieldtype": "Date", - "default": frappe.datetime.get_today() + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.datetime.get_today(), }, { - "fieldname":"customer", - "label": __("Customer"), - "fieldtype": "Link", - "options": "Customer" + fieldname: "customer", + label: __("Customer"), + fieldtype: "Link", + options: "Customer", }, { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company") + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), }, { - "fieldname":"mode_of_payment", - "label": __("Mode of Payment"), - "fieldtype": "Link", - "options": "Mode of Payment" + fieldname: "mode_of_payment", + label: __("Mode of Payment"), + fieldtype: "Link", + options: "Mode of Payment", }, { - "fieldname":"owner", - "label": __("Owner"), - "fieldtype": "Link", - "options": "User" + fieldname: "owner", + label: __("Owner"), + fieldtype: "Link", + options: "User", }, { - "fieldname":"cost_center", - "label": __("Cost Center"), - "fieldtype": "Link", - "options": "Cost Center" + fieldname: "cost_center", + label: __("Cost Center"), + fieldtype: "Link", + options: "Cost Center", }, { - "fieldname":"warehouse", - "label": __("Warehouse"), - "fieldtype": "Link", - "options": "Warehouse" + fieldname: "warehouse", + label: __("Warehouse"), + fieldtype: "Link", + options: "Warehouse", }, { - "fieldname":"brand", - "label": __("Brand"), - "fieldtype": "Link", - "options": "Brand" + fieldname: "brand", + label: __("Brand"), + fieldtype: "Link", + options: "Brand", }, { - "fieldname":"item_group", - "label": __("Item Group"), - "fieldtype": "Link", - "options": "Item Group" + fieldname: "item_group", + label: __("Item Group"), + fieldtype: "Link", + options: "Item Group", }, { - "fieldname": "include_payments", - "label": __("Show Ledger View"), - "fieldtype": "Check", - "default": 0 - } - ] -} + fieldname: "include_payments", + label: __("Show Ledger View"), + fieldtype: "Check", + default: 0, + }, + ], +}; -erpnext.utils.add_dimensions('Sales Register', 7); +erpnext.utils.add_dimensions("Sales Register", 7); diff --git a/erpnext/accounts/report/share_balance/share_balance.js b/erpnext/accounts/report/share_balance/share_balance.js index 6db5bdd299e..bcfc2e43f17 100644 --- a/erpnext/accounts/report/share_balance/share_balance.js +++ b/erpnext/accounts/report/share_balance/share_balance.js @@ -4,19 +4,19 @@ /* eslint-disable */ frappe.query_reports["Share Balance"] = { - "filters": [ + filters: [ { - "fieldname":"date", - "label": __("Date"), - "fieldtype": "Date", - "default": frappe.datetime.get_today(), - "reqd": 1 + fieldname: "date", + label: __("Date"), + fieldtype: "Date", + default: frappe.datetime.get_today(), + reqd: 1, }, { - "fieldname":"shareholder", - "label": __("Shareholder"), - "fieldtype": "Link", - "options": "Shareholder" - } - ] -} + fieldname: "shareholder", + label: __("Shareholder"), + fieldtype: "Link", + options: "Shareholder", + }, + ], +}; diff --git a/erpnext/accounts/report/share_ledger/share_ledger.js b/erpnext/accounts/report/share_ledger/share_ledger.js index 6d1c44a6d01..e5e6289a8d3 100644 --- a/erpnext/accounts/report/share_ledger/share_ledger.js +++ b/erpnext/accounts/report/share_ledger/share_ledger.js @@ -4,19 +4,19 @@ /* eslint-disable */ frappe.query_reports["Share Ledger"] = { - "filters": [ + filters: [ { - "fieldname":"date", - "label": __("Date"), - "fieldtype": "Date", - "default": frappe.datetime.get_today(), - "reqd": 1 + fieldname: "date", + label: __("Date"), + fieldtype: "Date", + default: frappe.datetime.get_today(), + reqd: 1, }, { - "fieldname":"shareholder", - "label": __("Shareholder"), - "fieldtype": "Link", - "options": "Shareholder" - } - ] + fieldname: "shareholder", + label: __("Shareholder"), + fieldtype: "Link", + options: "Shareholder", + }, + ], }; diff --git a/erpnext/accounts/report/supplier_ledger_summary/supplier_ledger_summary.js b/erpnext/accounts/report/supplier_ledger_summary/supplier_ledger_summary.js index 5dc4c3d1c15..1244a1b0168 100644 --- a/erpnext/accounts/report/supplier_ledger_summary/supplier_ledger_summary.js +++ b/erpnext/accounts/report/supplier_ledger_summary/supplier_ledger_summary.js @@ -3,77 +3,77 @@ /* eslint-disable */ frappe.query_reports["Supplier Ledger Summary"] = { - "filters": [ + filters: [ { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company") + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), }, { - "fieldname":"from_date", - "label": __("From Date"), - "fieldtype": "Date", - "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), - "reqd": 1, - "width": "60px" + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), + reqd: 1, + width: "60px", }, { - "fieldname":"to_date", - "label": __("To Date"), - "fieldtype": "Date", - "default": frappe.datetime.get_today(), - "reqd": 1, - "width": "60px" + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.datetime.get_today(), + reqd: 1, + width: "60px", }, { - "fieldname":"finance_book", - "label": __("Finance Book"), - "fieldtype": "Link", - "options": "Finance Book" + fieldname: "finance_book", + label: __("Finance Book"), + fieldtype: "Link", + options: "Finance Book", }, { - "fieldname":"party", - "label": __("Supplier"), - "fieldtype": "Link", - "options": "Supplier", + fieldname: "party", + label: __("Supplier"), + fieldtype: "Link", + options: "Supplier", on_change: () => { - var party = frappe.query_report.get_filter_value('party'); + var party = frappe.query_report.get_filter_value("party"); if (party) { - frappe.db.get_value('Supplier', party, ["tax_id", "supplier_name"], function(value) { - frappe.query_report.set_filter_value('tax_id', value["tax_id"]); - frappe.query_report.set_filter_value('supplier_name', value["supplier_name"]); + frappe.db.get_value("Supplier", party, ["tax_id", "supplier_name"], function (value) { + frappe.query_report.set_filter_value("tax_id", value["tax_id"]); + frappe.query_report.set_filter_value("supplier_name", value["supplier_name"]); }); } else { - frappe.query_report.set_filter_value('tax_id', ""); - frappe.query_report.set_filter_value('supplier_name', ""); + frappe.query_report.set_filter_value("tax_id", ""); + frappe.query_report.set_filter_value("supplier_name", ""); } - } + }, }, { - "fieldname":"supplier_group", - "label": __("Supplier Group"), - "fieldtype": "Link", - "options": "Supplier Group" + fieldname: "supplier_group", + label: __("Supplier Group"), + fieldtype: "Link", + options: "Supplier Group", }, { - "fieldname":"payment_terms_template", - "label": __("Payment Terms Template"), - "fieldtype": "Link", - "options": "Payment Terms Template" + fieldname: "payment_terms_template", + label: __("Payment Terms Template"), + fieldtype: "Link", + options: "Payment Terms Template", }, { - "fieldname":"tax_id", - "label": __("Tax Id"), - "fieldtype": "Data", - "hidden": 1 + fieldname: "tax_id", + label: __("Tax Id"), + fieldtype: "Data", + hidden: 1, }, { - "fieldname":"supplier_name", - "label": __("Supplier Name"), - "fieldtype": "Data", - "hidden": 1 - } - ] + fieldname: "supplier_name", + label: __("Supplier Name"), + fieldtype: "Data", + hidden: 1, + }, + ], }; diff --git a/erpnext/accounts/report/tax_detail/tax_detail.js b/erpnext/accounts/report/tax_detail/tax_detail.js index ed6fac4e605..d0e0970daa0 100644 --- a/erpnext/accounts/report/tax_detail/tax_detail.js +++ b/erpnext/accounts/report/tax_detail/tax_detail.js @@ -3,7 +3,7 @@ // Contributed by Case Solved and sponsored by Nulight Studios /* eslint-disable */ -frappe.provide('frappe.query_reports'); +frappe.provide("frappe.query_reports"); frappe.query_reports["Tax Detail"] = { filters: [ @@ -13,7 +13,7 @@ frappe.query_reports["Tax Detail"] = { fieldtype: "Link", options: "Company", default: frappe.defaults.get_user_default("company"), - reqd: 1 + reqd: 1, }, { fieldname: "from_date", @@ -21,7 +21,7 @@ frappe.query_reports["Tax Detail"] = { fieldtype: "Date", default: frappe.datetime.month_start(frappe.datetime.get_today()), reqd: 1, - width: "60px" + width: "60px", }, { fieldname: "to_date", @@ -29,7 +29,7 @@ frappe.query_reports["Tax Detail"] = { fieldtype: "Date", default: frappe.datetime.month_end(frappe.datetime.get_today()), reqd: 1, - width: "60px" + width: "60px", }, { fieldname: "report_name", @@ -37,7 +37,7 @@ frappe.query_reports["Tax Detail"] = { fieldtype: "Read Only", default: frappe.query_report.report_name, hidden: 1, - reqd: 1 + reqd: 1, }, { fieldname: "mode", @@ -45,23 +45,25 @@ frappe.query_reports["Tax Detail"] = { fieldtype: "Read Only", default: "edit", hidden: 1, - reqd: 1 - } + reqd: 1, + }, ], onload: function onload(report) { // Remove Add Column and Save from menu report.page.add_inner_button(__("New Report"), () => new_report(), __("Custom Report")); report.page.add_inner_button(__("Load Report"), () => load_report(), __("Custom Report")); hide_filters(report); - } + }, }; function hide_filters(report) { - report.page.page_form[0].querySelectorAll('.form-group.frappe-control').forEach(function setHidden(field) { - if (field.dataset.fieldtype == "Read Only") { - field.classList.add("hidden"); - } - }); + report.page.page_form[0] + .querySelectorAll(".form-group.frappe-control") + .forEach(function setHidden(field) { + if (field.dataset.fieldtype == "Read Only") { + field.classList.add("hidden"); + } + }); } erpnext.TaxDetail = class TaxDetail { @@ -74,20 +76,20 @@ erpnext.TaxDetail = class TaxDetail { this.qr = frappe.query_report; this.super = { refresh_report: this.qr.refresh_report, - show_footer_message: this.qr.show_footer_message - } + show_footer_message: this.qr.show_footer_message, + }; this.qr.refresh_report = () => this.refresh_report(); this.qr.show_footer_message = () => this.show_footer_message(); } show_footer_message() { // The last thing to run after datatable_render in refresh() this.super.show_footer_message.apply(this.qr); - if (this.qr.report_name !== 'Tax Detail') { + if (this.qr.report_name !== "Tax Detail") { this.show_help(); if (this.loading) { - this.set_section(''); + this.set_section(""); } else { - this.reload_component(''); + this.reload_component(""); } } this.loading = false; @@ -98,16 +100,18 @@ erpnext.TaxDetail = class TaxDetail { // already run within frappe.run_serially this.loading = true; this.super.refresh_report.apply(this.qr); - if (this.qr.report_name !== 'Tax Detail') { - frappe.call({ - method: 'erpnext.accounts.report.tax_detail.tax_detail.get_custom_reports', - args: {name: this.qr.report_name} - }).then((r) => { - const data = JSON.parse(r.message[this.qr.report_name]['json']); - this.create_controls(); - this.sections = data.sections || {}; - this.controls['show_detail'].set_input(data.show_detail); - }); + if (this.qr.report_name !== "Tax Detail") { + frappe + .call({ + method: "erpnext.accounts.report.tax_detail.tax_detail.get_custom_reports", + args: { name: this.qr.report_name }, + }) + .then((r) => { + const data = JSON.parse(r.message[this.qr.report_name]["json"]); + this.create_controls(); + this.sections = data.sections || {}; + this.controls["show_detail"].set_input(data.show_detail); + }); } } load_report() { @@ -118,14 +122,14 @@ erpnext.TaxDetail = class TaxDetail { get_menu_items() { // Replace Save action let new_items = []; - const save = __('Save'); + const save = __("Save"); for (let item of this.qr.menu_items) { if (item.label === save) { new_items.push({ label: save, action: () => this.save_report(), - standard: false + standard: false, }); } else { new_items.push(item); @@ -135,27 +139,29 @@ erpnext.TaxDetail = class TaxDetail { } save_report() { this.check_datatable(); - if (this.qr.report_name !== 'Tax Detail') { - frappe.call({ - method:'erpnext.accounts.report.tax_detail.tax_detail.save_custom_report', - args: { - reference_report: 'Tax Detail', - report_name: this.qr.report_name, - data: { - columns: this.qr.get_visible_columns(), - sections: this.sections, - show_detail: this.controls['show_detail'].get_input_value() - } - }, - freeze: true - }).then((r) => { - this.set_section(''); - }); + if (this.qr.report_name !== "Tax Detail") { + frappe + .call({ + method: "erpnext.accounts.report.tax_detail.tax_detail.save_custom_report", + args: { + reference_report: "Tax Detail", + report_name: this.qr.report_name, + data: { + columns: this.qr.get_visible_columns(), + sections: this.sections, + show_detail: this.controls["show_detail"].get_input_value(), + }, + }, + freeze: true, + }) + .then((r) => { + this.set_section(""); + }); } } check_datatable() { if (!this.qr.datatable) { - frappe.throw(__('Please change the date range to load data first')); + frappe.throw(__("Please change the date range to load data first")); } } set_section(name) { @@ -164,60 +170,62 @@ erpnext.TaxDetail = class TaxDetail { this.sections[name] = {}; } let options = Object.keys(this.sections); - options.unshift(''); - this.controls['section_name'].$wrapper.find("select").empty().add_options(options); - const org_mode = this.qr.get_filter_value('mode'); + options.unshift(""); + this.controls["section_name"].$wrapper.find("select").empty().add_options(options); + const org_mode = this.qr.get_filter_value("mode"); let refresh = false; if (name) { - this.controls['section_name'].set_input(name); - this.qr.set_filter_value('mode', 'edit'); - if (org_mode === 'run') { + this.controls["section_name"].set_input(name); + this.qr.set_filter_value("mode", "edit"); + if (org_mode === "run") { refresh = true; } } else { - this.controls['section_name'].set_input(''); - this.qr.set_filter_value('mode', 'run'); - if (org_mode === 'edit') { + this.controls["section_name"].set_input(""); + this.qr.set_filter_value("mode", "run"); + if (org_mode === "edit") { refresh = true; } } if (refresh) { this.qr.refresh(); } - this.reload_component(''); + this.reload_component(""); } reload_component(component_name) { - const section_name = this.controls['section_name'].get_input_value(); + const section_name = this.controls["section_name"].get_input_value(); if (section_name) { const section = this.sections[section_name]; const component_names = Object.keys(section); - component_names.unshift(''); - this.controls['component'].$wrapper.find("select").empty().add_options(component_names); - this.controls['component'].set_input(component_name); + component_names.unshift(""); + this.controls["component"].$wrapper.find("select").empty().add_options(component_names); + this.controls["component"].set_input(component_name); if (component_name) { - this.controls['component_type'].set_input(section[component_name].type); + this.controls["component_type"].set_input(section[component_name].type); } } else { - this.controls['component'].$wrapper.find("select").empty(); - this.controls['component'].set_input(''); + this.controls["component"].$wrapper.find("select").empty(); + this.controls["component"].set_input(""); } this.set_table_filters(); } set_table_filters() { let filters = {}; - const section_name = this.controls['section_name'].get_input_value(); - const component_name = this.controls['component'].get_input_value(); + const section_name = this.controls["section_name"].get_input_value(); + const component_name = this.controls["component"].get_input_value(); if (section_name && component_name) { const component_type = this.sections[section_name][component_name].type; - if (component_type === 'filter') { - filters = this.sections[section_name][component_name]['filters']; + if (component_type === "filter") { + filters = this.sections[section_name][component_name]["filters"]; } } this.setAppliedFilters(filters); } setAppliedFilters(filters) { if (this.qr.datatable) { - Array.from(this.qr.datatable.header.querySelectorAll('.dt-filter')).map(function setFilters(input) { + Array.from(this.qr.datatable.header.querySelectorAll(".dt-filter")).map(function setFilters( + input + ) { let idx = input.dataset.colIndex; if (filters[idx]) { input.value = filters[idx]; @@ -229,82 +237,89 @@ erpnext.TaxDetail = class TaxDetail { } } delete(name, type) { - if (type === 'section') { + if (type === "section") { delete this.sections[name]; - const new_section = Object.keys(this.sections)[0] || ''; + const new_section = Object.keys(this.sections)[0] || ""; this.set_section(new_section); } - if (type === 'component') { - const cur_section = this.controls['section_name'].get_input_value(); + if (type === "component") { + const cur_section = this.controls["section_name"].get_input_value(); delete this.sections[cur_section][name]; - this.reload_component(''); + this.reload_component(""); } } create_controls() { let controls = {}; // SELECT in data.js - controls['section_name'] = this.qr.page.add_field({ - label: __('Section'), - fieldtype: 'Select', - fieldname: 'section_name', + controls["section_name"] = this.qr.page.add_field({ + label: __("Section"), + fieldtype: "Select", + fieldname: "section_name", change: (e) => { - this.set_section(this.controls['section_name'].get_input_value()); - } + this.set_section(this.controls["section_name"].get_input_value()); + }, }); // BUTTON in button.js - controls['new_section'] = this.qr.page.add_field({ - label: __('New Section'), - fieldtype: 'Button', - fieldname: 'new_section', + controls["new_section"] = this.qr.page.add_field({ + label: __("New Section"), + fieldtype: "Button", + fieldname: "new_section", click: () => { - frappe.prompt({ - label: __('Section Name'), - fieldname: 'name', - fieldtype: 'Data' - }, (values) => { - this.set_section(values.name); - }); - } + frappe.prompt( + { + label: __("Section Name"), + fieldname: "name", + fieldtype: "Data", + }, + (values) => { + this.set_section(values.name); + } + ); + }, }); - controls['delete_section'] = this.qr.page.add_field({ - label: __('Delete Section'), - fieldtype: 'Button', - fieldname: 'delete_section', + controls["delete_section"] = this.qr.page.add_field({ + label: __("Delete Section"), + fieldtype: "Button", + fieldname: "delete_section", click: () => { - let cur_section = this.controls['section_name'].get_input_value(); + let cur_section = this.controls["section_name"].get_input_value(); if (cur_section) { - frappe.confirm(__('Are you sure you want to delete section') + ' ' + cur_section + '?', - () => {this.delete(cur_section, 'section')}); + frappe.confirm( + __("Are you sure you want to delete section") + " " + cur_section + "?", + () => { + this.delete(cur_section, "section"); + } + ); } - } + }, }); - controls['component'] = this.qr.page.add_field({ - label: __('Component'), - fieldtype: 'Select', - fieldname: 'component', + controls["component"] = this.qr.page.add_field({ + label: __("Component"), + fieldtype: "Select", + fieldname: "component", change: (e) => { - this.reload_component(this.controls['component'].get_input_value()); - } + this.reload_component(this.controls["component"].get_input_value()); + }, }); - controls['component_type'] = this.qr.page.add_field({ - label: __('Component Type'), - fieldtype: 'Select', - fieldname: 'component_type', - default: 'filter', + controls["component_type"] = this.qr.page.add_field({ + label: __("Component Type"), + fieldtype: "Select", + fieldname: "component_type", + default: "filter", options: [ - {label: __('Filtered Row Subtotal'), value: 'filter'}, - {label: __('Section Subtotal'), value: 'section'} - ] + { label: __("Filtered Row Subtotal"), value: "filter" }, + { label: __("Section Subtotal"), value: "section" }, + ], }); - controls['add_component'] = this.qr.page.add_field({ - label: __('Add Component'), - fieldtype: 'Button', - fieldname: 'add_component', + controls["add_component"] = this.qr.page.add_field({ + label: __("Add Component"), + fieldtype: "Button", + fieldname: "add_component", click: () => { this.check_datatable(); - let section_name = this.controls['section_name'].get_input_value(); + let section_name = this.controls["section_name"].get_input_value(); if (section_name) { - const component_type = this.controls['component_type'].get_input_value(); + const component_type = this.controls["component_type"].get_input_value(); let idx = 0; const names = Object.keys(this.sections[section_name]); if (names.length > 0) { @@ -312,118 +327,135 @@ erpnext.TaxDetail = class TaxDetail { idx = Math.max(...idxs) + 1; } const filters = this.qr.datatable.columnmanager.getAppliedFilters(); - if (component_type === 'filter') { - const name = 'Filter' + idx.toString(); + if (component_type === "filter") { + const name = "Filter" + idx.toString(); let data = { type: component_type, - filters: filters - } + filters: filters, + }; this.sections[section_name][name] = data; this.reload_component(name); - } else if (component_type === 'section') { + } else if (component_type === "section") { if (filters && Object.keys(filters).length !== 0) { frappe.show_alert({ - message: __('Column filters ignored'), - indicator: 'yellow' + message: __("Column filters ignored"), + indicator: "yellow", }); } let data = { - type: component_type - } - frappe.prompt({ - label: __('Section'), - fieldname: 'section', - fieldtype: 'Select', - options: Object.keys(this.sections) - }, (values) => { - this.sections[section_name][values.section] = data; - this.reload_component(values.section); - }); + type: component_type, + }; + frappe.prompt( + { + label: __("Section"), + fieldname: "section", + fieldtype: "Select", + options: Object.keys(this.sections), + }, + (values) => { + this.sections[section_name][values.section] = data; + this.reload_component(values.section); + } + ); } else { - frappe.throw(__('Please select the Component Type first')); + frappe.throw(__("Please select the Component Type first")); } } else { - frappe.throw(__('Please select the Section first')); + frappe.throw(__("Please select the Section first")); } - } + }, }); - controls['delete_component'] = this.qr.page.add_field({ - label: __('Delete Component'), - fieldtype: 'Button', - fieldname: 'delete_component', + controls["delete_component"] = this.qr.page.add_field({ + label: __("Delete Component"), + fieldtype: "Button", + fieldname: "delete_component", click: () => { - const component = this.controls['component'].get_input_value(); + const component = this.controls["component"].get_input_value(); if (component) { - frappe.confirm(__('Are you sure you want to delete component') + ' ' + component + '?', - () => {this.delete(component, 'component')}); + frappe.confirm( + __("Are you sure you want to delete component") + " " + component + "?", + () => { + this.delete(component, "component"); + } + ); } - } + }, }); - controls['save'] = this.qr.page.add_field({ - label: __('Save & Run'), - fieldtype: 'Button', - fieldname: 'save', + controls["save"] = this.qr.page.add_field({ + label: __("Save & Run"), + fieldtype: "Button", + fieldname: "save", click: () => { this.save_report(); - } + }, }); - controls['show_detail'] = this.qr.page.add_field({ - label: __('Show Detail'), - fieldtype: 'Check', - fieldname: 'show_detail', - default: 1 + controls["show_detail"] = this.qr.page.add_field({ + label: __("Show Detail"), + fieldtype: "Check", + fieldname: "show_detail", + default: 1, }); this.controls = controls; } show_help() { - const help = __('Your custom report is built from General Ledger Entries within the date range. You can add multiple sections to the report using the New Section button. Each component added to a section adds a subset of the data into the specified section. Beware of duplicated data rows. The Filtered Row component type saves the datatable column filters to specify the added data. The Section component type refers to the data in a previously defined section, but it cannot refer to its parent section. The Amount column is summed to give the section subtotal. Use the Show Detail box to see the data rows included in each section in the final report. Once finished, hit Save & Run. Report contributed by'); - this.qr.$report_footer.append('
      ' + __('Help') + `: ${help} Case Solved
      `); + const help = __( + "Your custom report is built from General Ledger Entries within the date range. You can add multiple sections to the report using the New Section button. Each component added to a section adds a subset of the data into the specified section. Beware of duplicated data rows. The Filtered Row component type saves the datatable column filters to specify the added data. The Section component type refers to the data in a previously defined section, but it cannot refer to its parent section. The Amount column is summed to give the section subtotal. Use the Show Detail box to see the data rows included in each section in the final report. Once finished, hit Save & Run. Report contributed by" + ); + this.qr.$report_footer.append( + '
      ' + + __("Help") + + `: ${help} Case Solved
      ` + ); } -} +}; if (!window.taxdetail) { window.taxdetail = new erpnext.TaxDetail(); } function get_reports(cb) { - frappe.call({ - method: 'erpnext.accounts.report.tax_detail.tax_detail.get_custom_reports', - freeze: true - }).then((r) => { - cb(r.message); - }) + frappe + .call({ + method: "erpnext.accounts.report.tax_detail.tax_detail.get_custom_reports", + freeze: true, + }) + .then((r) => { + cb(r.message); + }); } function new_report() { const dialog = new frappe.ui.Dialog({ - title: __('New Report'), + title: __("New Report"), fields: [ { - fieldname: 'report_name', - label: __('Report Name'), - fieldtype: 'Data', - default: 'VAT Return' - } + fieldname: "report_name", + label: __("Report Name"), + fieldtype: "Data", + default: "VAT Return", + }, ], - primary_action_label: __('Create'), + primary_action_label: __("Create"), primary_action: function new_report_pa(values) { - frappe.call({ - method:'erpnext.accounts.report.tax_detail.tax_detail.save_custom_report', - args: { - reference_report: 'Tax Detail', - report_name: values.report_name, - data: { - columns: [], - sections: {}, - show_detail: 1 - } - }, - freeze: true - }).then((r) => { - frappe.set_route('query-report', values.report_name); - }); + frappe + .call({ + method: "erpnext.accounts.report.tax_detail.tax_detail.save_custom_report", + args: { + reference_report: "Tax Detail", + report_name: values.report_name, + data: { + columns: [], + sections: {}, + show_detail: 1, + }, + }, + freeze: true, + }) + .then((r) => { + frappe.set_route("query-report", values.report_name); + }); dialog.hide(); - } + }, }); dialog.show(); } @@ -431,20 +463,20 @@ function new_report() { function load_report() { get_reports(function load_report_cb(reports) { const dialog = new frappe.ui.Dialog({ - title: __('Load Report'), + title: __("Load Report"), fields: [ { - fieldname: 'report_name', - label: __('Report Name'), - fieldtype: 'Select', - options: Object.keys(reports) - } + fieldname: "report_name", + label: __("Report Name"), + fieldtype: "Select", + options: Object.keys(reports), + }, ], - primary_action_label: __('Load'), + primary_action_label: __("Load"), primary_action: function load_report_pa(values) { dialog.hide(); - frappe.set_route('query-report', values.report_name); - } + frappe.set_route("query-report", values.report_name); + }, }); dialog.show(); }); diff --git a/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.js b/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.js index c42028b61f5..054f085e3b7 100644 --- a/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.js +++ b/erpnext/accounts/report/tds_computation_summary/tds_computation_summary.js @@ -3,60 +3,60 @@ /* eslint-disable */ frappe.query_reports["TDS Computation Summary"] = { - "filters": [ + filters: [ { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_default('company') + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_default("company"), }, { - "fieldname":"party_type", - "label": __("Party Type"), - "fieldtype": "Select", - "options": ["Supplier", "Customer"], - "reqd": 1, - "default": "Supplier", - "on_change": function(){ + fieldname: "party_type", + label: __("Party Type"), + fieldtype: "Select", + options: ["Supplier", "Customer"], + reqd: 1, + default: "Supplier", + on_change: function () { frappe.query_report.set_filter_value("party", ""); - } + }, }, { - "fieldname":"party", - "label": __("Party"), - "fieldtype": "Dynamic Link", - "get_options": function() { - var party_type = frappe.query_report.get_filter_value('party_type'); - var party = frappe.query_report.get_filter_value('party'); - if(party && !party_type) { + fieldname: "party", + label: __("Party"), + fieldtype: "Dynamic Link", + get_options: function () { + var party_type = frappe.query_report.get_filter_value("party_type"); + var party = frappe.query_report.get_filter_value("party"); + if (party && !party_type) { frappe.throw(__("Please select Party Type first")); } return party_type; }, - "get_query": function() { + get_query: function () { return { - "filters": { - "tax_withholding_category": ["!=",""], - } - } + filters: { + tax_withholding_category: ["!=", ""], + }, + }; }, }, { - "fieldname":"from_date", - "label": __("From Date"), - "fieldtype": "Date", - "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), - "reqd": 1, - "width": "60px" + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), + reqd: 1, + width: "60px", }, { - "fieldname":"to_date", - "label": __("To Date"), - "fieldtype": "Date", - "default": frappe.datetime.get_today(), - "reqd": 1, - "width": "60px" - } - ] -} + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.datetime.get_today(), + reqd: 1, + width: "60px", + }, + ], +}; diff --git a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.js b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.js index 6585ea0a293..cf7e6669596 100644 --- a/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.js +++ b/erpnext/accounts/report/tds_payable_monthly/tds_payable_monthly.js @@ -3,60 +3,60 @@ /* eslint-disable */ frappe.query_reports["TDS Payable Monthly"] = { - "filters": [ + filters: [ { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_default('company') + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_default("company"), }, { - "fieldname":"party_type", - "label": __("Party Type"), - "fieldtype": "Select", - "options": ["Supplier", "Customer"], - "reqd": 1, - "default": "Supplier", - "on_change": function(){ + fieldname: "party_type", + label: __("Party Type"), + fieldtype: "Select", + options: ["Supplier", "Customer"], + reqd: 1, + default: "Supplier", + on_change: function () { frappe.query_report.set_filter_value("party", ""); - } + }, }, { - "fieldname":"party", - "label": __("Party"), - "fieldtype": "Dynamic Link", - "get_options": function() { - var party_type = frappe.query_report.get_filter_value('party_type'); - var party = frappe.query_report.get_filter_value('party'); - if(party && !party_type) { + fieldname: "party", + label: __("Party"), + fieldtype: "Dynamic Link", + get_options: function () { + var party_type = frappe.query_report.get_filter_value("party_type"); + var party = frappe.query_report.get_filter_value("party"); + if (party && !party_type) { frappe.throw(__("Please select Party Type first")); } return party_type; }, - "get_query": function() { + get_query: function () { return { - "filters": { - "tax_withholding_category": ["!=",""], - } - } + filters: { + tax_withholding_category: ["!=", ""], + }, + }; }, }, { - "fieldname":"from_date", - "label": __("From Date"), - "fieldtype": "Date", - "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), - "reqd": 1, - "width": "60px" + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), + reqd: 1, + width: "60px", }, { - "fieldname":"to_date", - "label": __("To Date"), - "fieldtype": "Date", - "default": frappe.datetime.get_today(), - "reqd": 1, - "width": "60px" - } - ] -} + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.datetime.get_today(), + reqd: 1, + width: "60px", + }, + ], +}; diff --git a/erpnext/accounts/report/trial_balance/trial_balance.js b/erpnext/accounts/report/trial_balance/trial_balance.js index ffb2931e902..59b366a3dca 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.js +++ b/erpnext/accounts/report/trial_balance/trial_balance.js @@ -1,118 +1,118 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.require("assets/erpnext/js/financial_statements.js", function() { +frappe.require("assets/erpnext/js/financial_statements.js", function () { frappe.query_reports["Trial Balance"] = { - "filters": [ + filters: [ { - "fieldname": "company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company"), - "reqd": 1 + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, }, { - "fieldname": "fiscal_year", - "label": __("Fiscal Year"), - "fieldtype": "Link", - "options": "Fiscal Year", - "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), - "reqd": 1, - "on_change": function(query_report) { + fieldname: "fiscal_year", + label: __("Fiscal Year"), + fieldtype: "Link", + options: "Fiscal Year", + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), + reqd: 1, + on_change: function (query_report) { var fiscal_year = query_report.get_values().fiscal_year; if (!fiscal_year) { return; } - frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) { + 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({ from_date: fy.year_start_date, - to_date: fy.year_end_date + to_date: fy.year_end_date, }); }); - } + }, }, { - "fieldname": "from_date", - "label": __("From Date"), - "fieldtype": "Date", - "default": frappe.defaults.get_user_default("year_start_date"), + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.defaults.get_user_default("year_start_date"), }, { - "fieldname": "to_date", - "label": __("To Date"), - "fieldtype": "Date", - "default": frappe.defaults.get_user_default("year_end_date"), + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.defaults.get_user_default("year_end_date"), }, { - "fieldname": "cost_center", - "label": __("Cost Center"), - "fieldtype": "Link", - "options": "Cost Center", - "get_query": function() { - var company = frappe.query_report.get_filter_value('company'); + fieldname: "cost_center", + label: __("Cost Center"), + fieldtype: "Link", + options: "Cost Center", + get_query: function () { + var company = frappe.query_report.get_filter_value("company"); return { - "doctype": "Cost Center", - "filters": { - "company": company, - } - } - } + doctype: "Cost Center", + filters: { + company: company, + }, + }; + }, }, { - "fieldname": "project", - "label": __("Project"), - "fieldtype": "Link", - "options": "Project" + fieldname: "project", + label: __("Project"), + fieldtype: "Link", + options: "Project", }, { - "fieldname": "finance_book", - "label": __("Finance Book"), - "fieldtype": "Link", - "options": "Finance Book", + fieldname: "finance_book", + label: __("Finance Book"), + fieldtype: "Link", + options: "Finance Book", }, { - "fieldname": "presentation_currency", - "label": __("Currency"), - "fieldtype": "Select", - "options": erpnext.get_presentation_currency_list() + fieldname: "presentation_currency", + label: __("Currency"), + fieldtype: "Select", + options: erpnext.get_presentation_currency_list(), }, { - "fieldname": "with_period_closing_entry", - "label": __("Period Closing Entry"), - "fieldtype": "Check", - "default": 1 + fieldname: "with_period_closing_entry", + label: __("Period Closing Entry"), + fieldtype: "Check", + default: 1, }, { - "fieldname": "show_zero_values", - "label": __("Show zero values"), - "fieldtype": "Check" + fieldname: "show_zero_values", + label: __("Show zero values"), + fieldtype: "Check", }, { - "fieldname": "show_unclosed_fy_pl_balances", - "label": __("Show unclosed fiscal year's P&L balances"), - "fieldtype": "Check" + fieldname: "show_unclosed_fy_pl_balances", + label: __("Show unclosed fiscal year's P&L balances"), + fieldtype: "Check", }, { - "fieldname": "include_default_book_entries", - "label": __("Include Default FB Entries"), - "fieldtype": "Check", - "default": 1 + fieldname: "include_default_book_entries", + label: __("Include Default FB Entries"), + fieldtype: "Check", + default: 1, }, { - "fieldname": "show_net_values", - "label": __("Show net values in opening and closing columns"), - "fieldtype": "Check", - "default": 1 - } + fieldname: "show_net_values", + label: __("Show net values in opening and closing columns"), + fieldtype: "Check", + default: 1, + }, ], - "formatter": erpnext.financial_statements.formatter, - "tree": true, - "name_field": "account", - "parent_field": "parent_account", - "initial_depth": 3 - } + formatter: erpnext.financial_statements.formatter, + tree: true, + name_field: "account", + parent_field: "parent_account", + initial_depth: 3, + }; - erpnext.utils.add_dimensions('Trial Balance', 6); + erpnext.utils.add_dimensions("Trial Balance", 6); }); diff --git a/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js b/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js index 0f7578cdc17..ab1508bebd0 100644 --- a/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js +++ b/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js @@ -2,88 +2,88 @@ // For license information, please see license.txt frappe.query_reports["Trial Balance for Party"] = { - "filters": [ + filters: [ { - "fieldname": "company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company"), - "reqd": 1 + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, }, { - "fieldname": "fiscal_year", - "label": __("Fiscal Year"), - "fieldtype": "Link", - "options": "Fiscal Year", - "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), - "reqd": 1, - "on_change": function(query_report) { + fieldname: "fiscal_year", + label: __("Fiscal Year"), + fieldtype: "Link", + options: "Fiscal Year", + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), + reqd: 1, + on_change: function (query_report) { var fiscal_year = query_report.get_values().fiscal_year; if (!fiscal_year) { return; } - frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) { + 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({ from_date: fy.year_start_date, - to_date: fy.year_end_date + to_date: fy.year_end_date, }); }); - } + }, }, { - "fieldname": "from_date", - "label": __("From Date"), - "fieldtype": "Date", - "default": frappe.defaults.get_user_default("year_start_date"), + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.defaults.get_user_default("year_start_date"), }, { - "fieldname": "to_date", - "label": __("To Date"), - "fieldtype": "Date", - "default": frappe.defaults.get_user_default("year_end_date"), + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.defaults.get_user_default("year_end_date"), }, { - "fieldname":"party_type", - "label": __("Party Type"), - "fieldtype": "Link", - "options": "Party Type", - "default": "Customer", - "reqd": 1 + fieldname: "party_type", + label: __("Party Type"), + fieldtype: "Link", + options: "Party Type", + default: "Customer", + reqd: 1, }, { - "fieldname":"party", - "label": __("Party"), - "fieldtype": "Dynamic Link", - "get_options": function() { - var party_type = frappe.query_report.get_filter_value('party_type'); - var party = frappe.query_report.get_filter_value('party'); - if(party && !party_type) { + fieldname: "party", + label: __("Party"), + fieldtype: "Dynamic Link", + get_options: function () { + var party_type = frappe.query_report.get_filter_value("party_type"); + var party = frappe.query_report.get_filter_value("party"); + if (party && !party_type) { frappe.throw(__("Please select Party Type first")); } return party_type; - } + }, }, { - "fieldname": "account", - "label": __("Account"), - "fieldtype": "Link", - "options": "Account", - "get_query": function() { - var company = frappe.query_report.get_filter_value('company'); + fieldname: "account", + label: __("Account"), + fieldtype: "Link", + options: "Account", + get_query: function () { + var company = frappe.query_report.get_filter_value("company"); return { - "doctype": "Account", - "filters": { - "company": company, - } - } - } + doctype: "Account", + filters: { + company: company, + }, + }; + }, }, { - "fieldname": "show_zero_values", - "label": __("Show zero values"), - "fieldtype": "Check" - } - ] -} + fieldname: "show_zero_values", + label: __("Show zero values"), + fieldtype: "Check", + }, + ], +}; diff --git a/erpnext/accounts/report/voucher_wise_balance/voucher_wise_balance.js b/erpnext/accounts/report/voucher_wise_balance/voucher_wise_balance.js index 0c148f85fb8..37c506aea2f 100644 --- a/erpnext/accounts/report/voucher_wise_balance/voucher_wise_balance.js +++ b/erpnext/accounts/report/voucher_wise_balance/voucher_wise_balance.js @@ -3,26 +3,26 @@ /* eslint-disable */ frappe.query_reports["Voucher-wise Balance"] = { - "filters": [ - { - "fieldname": "company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company" - }, - { - "fieldname":"from_date", - "label": __("From Date"), - "fieldtype": "Date", - "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), - "width": "60px" - }, - { - "fieldname":"to_date", - "label": __("To Date"), - "fieldtype": "Date", - "default": frappe.datetime.get_today(), - "width": "60px" - }, - ] + filters: [ + { + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + }, + { + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), + width: "60px", + }, + { + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.datetime.get_today(), + width: "60px", + }, + ], }; diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index 9356484e7a4..49c0b24e4cb 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -4,135 +4,174 @@ frappe.provide("erpnext.asset"); frappe.provide("erpnext.accounts.dimensions"); -frappe.ui.form.on('Asset', { - onload: function(frm) { - frm.set_query("item_code", function() { +frappe.ui.form.on("Asset", { + onload: function (frm) { + frm.set_query("item_code", function () { return { - "filters": { - "is_fixed_asset": 1, - "is_stock_item": 0 - } + filters: { + is_fixed_asset: 1, + is_stock_item: 0, + }, }; }); - frm.set_query("warehouse", function() { + frm.set_query("warehouse", function () { return { - "filters": { - "company": frm.doc.company, - "is_group": 0 - } + filters: { + company: frm.doc.company, + is_group: 0, + }, }; }); - frm.set_query("department", function() { + frm.set_query("department", function () { return { - "filters": { - "company": frm.doc.company, - } + filters: { + company: frm.doc.company, + }, }; }); erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); }, - company: function(frm) { + company: function (frm) { erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); }, - setup: function(frm) { + setup: function (frm) { frm.make_methods = { - 'Asset Movement': () => { + "Asset Movement": () => { frappe.call({ - method: "erpnext.assets.doctype.asset.asset.make_asset_movement", - freeze: true, - args:{ - "assets": [{ name: cur_frm.doc.name }] - }, - callback: function (r) { - if (r.message) { - var doc = frappe.model.sync(r.message)[0]; - frappe.set_route("Form", doc.doctype, doc.name); - } - } - }); + method: "erpnext.assets.doctype.asset.asset.make_asset_movement", + freeze: true, + args: { + assets: [{ name: cur_frm.doc.name }], + }, + callback: function (r) { + if (r.message) { + var doc = frappe.model.sync(r.message)[0]; + frappe.set_route("Form", doc.doctype, doc.name); + } + }, + }); }, - } + }; frm.set_query("purchase_receipt", (doc) => { return { query: "erpnext.controllers.queries.get_purchase_receipts", - filters: { item_code: doc.item_code } - } + filters: { item_code: doc.item_code }, + }; }); frm.set_query("purchase_invoice", (doc) => { return { query: "erpnext.controllers.queries.get_purchase_invoices", - filters: { item_code: doc.item_code } - } + filters: { item_code: doc.item_code }, + }; }); }, - refresh: function(frm) { + refresh: function (frm) { frappe.ui.form.trigger("Asset", "is_existing_asset"); frm.toggle_display("next_depreciation_date", frm.doc.docstatus < 1); frm.events.make_schedules_editable(frm); - if (frm.doc.docstatus==1) { + if (frm.doc.docstatus == 1) { if (in_list(["Submitted", "Partially Depreciated", "Fully Depreciated"], frm.doc.status)) { - frm.add_custom_button(__("Transfer Asset"), function() { - erpnext.asset.transfer_asset(frm); - }, __("Manage")); + frm.add_custom_button( + __("Transfer Asset"), + function () { + erpnext.asset.transfer_asset(frm); + }, + __("Manage") + ); - frm.add_custom_button(__("Scrap Asset"), function() { - erpnext.asset.scrap_asset(frm); - }, __("Manage")); + frm.add_custom_button( + __("Scrap Asset"), + function () { + erpnext.asset.scrap_asset(frm); + }, + __("Manage") + ); - frm.add_custom_button(__("Sell Asset"), function() { - frm.trigger("make_sales_invoice"); - }, __("Manage")); - - } else if (frm.doc.status=='Scrapped') { - frm.add_custom_button(__("Restore Asset"), function() { - erpnext.asset.restore_asset(frm); - }, __("Manage")); + frm.add_custom_button( + __("Sell Asset"), + function () { + frm.trigger("make_sales_invoice"); + }, + __("Manage") + ); + } else if (frm.doc.status == "Scrapped") { + frm.add_custom_button( + __("Restore Asset"), + function () { + erpnext.asset.restore_asset(frm); + }, + __("Manage") + ); } if (frm.doc.maintenance_required && !frm.doc.maintenance_schedule) { - frm.add_custom_button(__("Maintain Asset"), function() { - frm.trigger("create_asset_maintenance"); - }, __("Manage")); + frm.add_custom_button( + __("Maintain Asset"), + function () { + frm.trigger("create_asset_maintenance"); + }, + __("Manage") + ); } - frm.add_custom_button(__("Repair Asset"), function() { - frm.trigger("create_asset_repair"); - }, __("Manage")); + frm.add_custom_button( + __("Repair Asset"), + function () { + frm.trigger("create_asset_repair"); + }, + __("Manage") + ); - frm.add_custom_button(__("Split Asset"), function() { - frm.trigger("split_asset"); - }, __("Manage")); + frm.add_custom_button( + __("Split Asset"), + function () { + frm.trigger("split_asset"); + }, + __("Manage") + ); - if (frm.doc.status != 'Fully Depreciated') { - frm.add_custom_button(__("Adjust Asset Value"), function() { - frm.trigger("create_asset_value_adjustment"); - }, __("Manage")); + if (frm.doc.status != "Fully Depreciated") { + frm.add_custom_button( + __("Adjust Asset Value"), + function () { + frm.trigger("create_asset_value_adjustment"); + }, + __("Manage") + ); } if (!frm.doc.calculate_depreciation) { - frm.add_custom_button(__("Create Depreciation Entry"), function() { - frm.trigger("make_journal_entry"); - }, __("Manage")); + frm.add_custom_button( + __("Create Depreciation Entry"), + function () { + frm.trigger("make_journal_entry"); + }, + __("Manage") + ); } if (frm.doc.purchase_receipt || !frm.doc.is_existing_asset) { - frm.add_custom_button(__("View General Ledger"), function() { - frappe.route_options = { - "voucher_no": frm.doc.name, - "from_date": frm.doc.available_for_use_date, - "to_date": frm.doc.available_for_use_date, - "company": frm.doc.company - }; - frappe.set_route("query-report", "General Ledger"); - }, __("Manage")); + frm.add_custom_button( + __("View General Ledger"), + function () { + frappe.route_options = { + voucher_no: frm.doc.name, + from_date: frm.doc.available_for_use_date, + to_date: frm.doc.available_for_use_date, + company: frm.doc.company, + }; + frappe.set_route("query-report", "General Ledger"); + }, + __("Manage") + ); } if (frm.doc.depr_entry_posting_status === "Failed") { @@ -148,10 +187,10 @@ frappe.ui.form.on('Asset', { frm.toggle_reqd("finance_books", frm.doc.calculate_depreciation); if (frm.doc.is_composite_asset && !frm.doc.capitalized_in) { - $('.primary-action').prop('hidden', true); - $('.form-message').text('Capitalize this asset to confirm'); + $(".primary-action").prop("hidden", true); + $(".form-message").text("Capitalize this asset to confirm"); - frm.add_custom_button(__("Capitalize Asset"), function() { + frm.add_custom_button(__("Capitalize Asset"), function () { frm.trigger("create_asset_capitalization"); }); } @@ -171,70 +210,74 @@ frappe.ui.form.on('Asset', { frm.dashboard.set_headline_alert(alert); }, - toggle_reference_doc: function(frm) { + toggle_reference_doc: function (frm) { if (frm.doc.purchase_receipt && frm.doc.purchase_invoice && frm.doc.docstatus === 1) { - frm.set_df_property('purchase_invoice', 'read_only', 1); - frm.set_df_property('purchase_receipt', 'read_only', 1); - } - else if (frm.doc.is_existing_asset || frm.doc.is_composite_asset) { - frm.toggle_reqd('purchase_receipt', 0); - frm.toggle_reqd('purchase_invoice', 0); - } - else if (frm.doc.purchase_receipt) { + frm.set_df_property("purchase_invoice", "read_only", 1); + frm.set_df_property("purchase_receipt", "read_only", 1); + } else if (frm.doc.is_existing_asset || frm.doc.is_composite_asset) { + frm.toggle_reqd("purchase_receipt", 0); + frm.toggle_reqd("purchase_invoice", 0); + } else if (frm.doc.purchase_receipt) { // if purchase receipt link is set then set PI disabled - frm.toggle_reqd('purchase_invoice', 0); - frm.set_df_property('purchase_invoice', 'read_only', 1); - } - else if (frm.doc.purchase_invoice) { + frm.toggle_reqd("purchase_invoice", 0); + frm.set_df_property("purchase_invoice", "read_only", 1); + } else if (frm.doc.purchase_invoice) { // if purchase invoice link is set then set PR disabled - frm.toggle_reqd('purchase_receipt', 0); - frm.set_df_property('purchase_receipt', 'read_only', 1); - } - else { - frm.toggle_reqd('purchase_receipt', 1); - frm.set_df_property('purchase_receipt', 'read_only', 0); - frm.toggle_reqd('purchase_invoice', 1); - frm.set_df_property('purchase_invoice', 'read_only', 0); + frm.toggle_reqd("purchase_receipt", 0); + frm.set_df_property("purchase_receipt", "read_only", 1); + } else { + frm.toggle_reqd("purchase_receipt", 1); + frm.set_df_property("purchase_receipt", "read_only", 0); + frm.toggle_reqd("purchase_invoice", 1); + frm.set_df_property("purchase_invoice", "read_only", 0); } }, - make_journal_entry: function(frm) { + make_journal_entry: function (frm) { frappe.call({ method: "erpnext.assets.doctype.asset.asset.make_journal_entry", args: { - asset_name: frm.doc.name + asset_name: frm.doc.name, }, - callback: function(r) { + callback: function (r) { if (r.message) { var doclist = frappe.model.sync(r.message); frappe.set_route("Form", doclist[0].doctype, doclist[0].name); } - } - }) + }, + }); }, - setup_chart: async function(frm) { - if(frm.doc.finance_books.length > 1) { - return + setup_chart: async function (frm) { + if (frm.doc.finance_books.length > 1) { + return; } - var x_intervals = [frappe.format(frm.doc.purchase_date, { fieldtype: 'Date' })]; + var x_intervals = [frappe.format(frm.doc.purchase_date, { fieldtype: "Date" })]; var asset_values = [frm.doc.gross_purchase_amount]; - if(frm.doc.calculate_depreciation) { - if(frm.doc.opening_accumulated_depreciation) { + if (frm.doc.calculate_depreciation) { + if (frm.doc.opening_accumulated_depreciation) { var depreciation_date = frappe.datetime.add_months( frm.doc.finance_books[0].depreciation_start_date, -1 * frm.doc.finance_books[0].frequency_of_depreciation ); - x_intervals.push(frappe.format(depreciation_date, { fieldtype: 'Date' })); - asset_values.push(flt(frm.doc.gross_purchase_amount - frm.doc.opening_accumulated_depreciation, precision('gross_purchase_amount'))); + x_intervals.push(frappe.format(depreciation_date, { fieldtype: "Date" })); + asset_values.push( + flt( + frm.doc.gross_purchase_amount - frm.doc.opening_accumulated_depreciation, + precision("gross_purchase_amount") + ) + ); } - $.each(frm.doc.schedules || [], function(i, v) { - x_intervals.push(frappe.format(v.schedule_date, { fieldtype: 'Date' })); - var asset_value = flt(frm.doc.gross_purchase_amount - v.accumulated_depreciation_amount, precision('gross_purchase_amount')); - if(v.journal_entry) { + $.each(frm.doc.schedules || [], function (i, v) { + x_intervals.push(frappe.format(v.schedule_date, { fieldtype: "Date" })); + var asset_value = flt( + frm.doc.gross_purchase_amount - v.accumulated_depreciation_amount, + precision("gross_purchase_amount") + ); + if (v.journal_entry) { asset_values.push(asset_value); } else { if (in_list(["Scrapped", "Sold"], frm.doc.status)) { @@ -245,25 +288,32 @@ frappe.ui.form.on('Asset', { } }); } else { - if(frm.doc.opening_accumulated_depreciation) { - x_intervals.push(frappe.format(frm.doc.creation.split(" ")[0], { fieldtype: 'Date' })); - asset_values.push(flt(frm.doc.gross_purchase_amount - frm.doc.opening_accumulated_depreciation, precision('gross_purchase_amount'))); + if (frm.doc.opening_accumulated_depreciation) { + x_intervals.push(frappe.format(frm.doc.creation.split(" ")[0], { fieldtype: "Date" })); + asset_values.push( + flt( + frm.doc.gross_purchase_amount - frm.doc.opening_accumulated_depreciation, + precision("gross_purchase_amount") + ) + ); } - let depr_entries = (await frappe.call({ - method: "get_manual_depreciation_entries", - doc: frm.doc, - })).message; + let depr_entries = ( + await frappe.call({ + method: "get_manual_depreciation_entries", + doc: frm.doc, + }) + ).message; - $.each(depr_entries || [], function(i, v) { - x_intervals.push(frappe.format(v.posting_date, { fieldtype: 'Date' })); - let last_asset_value = asset_values[asset_values.length - 1] - asset_values.push(flt(last_asset_value - v.value, precision('gross_purchase_amount'))); + $.each(depr_entries || [], function (i, v) { + x_intervals.push(frappe.format(v.posting_date, { fieldtype: "Date" })); + let last_asset_value = asset_values[asset_values.length - 1]; + asset_values.push(flt(last_asset_value - v.value, precision("gross_purchase_amount"))); }); } - if(in_list(["Scrapped", "Sold"], frm.doc.status)) { - x_intervals.push(frappe.format(frm.doc.disposal_date, { fieldtype: 'Date' })); + if (in_list(["Scrapped", "Sold"], frm.doc.status)) { + x_intervals.push(frappe.format(frm.doc.disposal_date, { fieldtype: "Date" })); asset_values.push(0); } @@ -271,62 +321,65 @@ frappe.ui.form.on('Asset', { title: "Asset Value", data: { labels: x_intervals, - datasets: [{ - color: 'green', - values: asset_values, - formatted: asset_values.map(d => d?.toFixed(2)) - }] + datasets: [ + { + color: "green", + values: asset_values, + formatted: asset_values.map((d) => d?.toFixed(2)), + }, + ], }, - type: 'line' + type: "line", }); }, - - item_code: function(frm) { - if(frm.doc.item_code && frm.doc.calculate_depreciation && frm.doc.gross_purchase_amount) { - frm.trigger('set_finance_book'); + item_code: function (frm) { + if (frm.doc.item_code && frm.doc.calculate_depreciation && frm.doc.gross_purchase_amount) { + frm.trigger("set_finance_book"); } else { - frm.set_value('finance_books', []); + frm.set_value("finance_books", []); } }, - set_finance_book: function(frm) { + set_finance_book: function (frm) { frappe.call({ method: "erpnext.assets.doctype.asset.asset.get_item_details", args: { item_code: frm.doc.item_code, asset_category: frm.doc.asset_category, - gross_purchase_amount: frm.doc.gross_purchase_amount + gross_purchase_amount: frm.doc.gross_purchase_amount, }, - callback: function(r, rt) { - if(r.message) { - frm.set_value('finance_books', r.message); + callback: function (r, rt) { + if (r.message) { + frm.set_value("finance_books", r.message); } - } - }) + }, + }); }, - is_existing_asset: function(frm) { + is_existing_asset: function (frm) { frm.trigger("toggle_reference_doc"); }, - is_composite_asset: function(frm) { - if(frm.doc.is_composite_asset) { - frm.set_value('gross_purchase_amount', 0); - frm.set_df_property('gross_purchase_amount', 'read_only', 1); + is_composite_asset: function (frm) { + if (frm.doc.is_composite_asset) { + frm.set_value("gross_purchase_amount", 0); + frm.set_df_property("gross_purchase_amount", "read_only", 1); } else { - frm.set_df_property('gross_purchase_amount', 'read_only', 0); + frm.set_df_property("gross_purchase_amount", "read_only", 0); } frm.trigger("toggle_reference_doc"); }, - make_schedules_editable: function(frm) { + make_schedules_editable: function (frm) { if (frm.doc.finance_books && frm.doc.finance_books.length) { - var is_manual_hence_editable = frm.doc.finance_books.filter(d => d.depreciation_method == "Manual").length > 0 - ? true : false; - var is_shift_hence_editable = frm.doc.finance_books.filter(d => d.shift_based).length > 0 - ? true : false; + var is_manual_hence_editable = + frm.doc.finance_books.filter((d) => d.depreciation_method == "Manual").length > 0 + ? true + : false; + var is_shift_hence_editable = + frm.doc.finance_books.filter((d) => d.shift_based).length > 0 ? true : false; frm.toggle_enable("schedules", is_manual_hence_editable || is_shift_hence_editable); frm.fields_dict["schedules"].grid.toggle_enable("schedule_date", is_manual_hence_editable); @@ -335,95 +388,95 @@ frappe.ui.form.on('Asset', { } }, - make_sales_invoice: function(frm) { + make_sales_invoice: function (frm) { frappe.call({ args: { - "asset": frm.doc.name, - "item_code": frm.doc.item_code, - "company": frm.doc.company, - "serial_no": frm.doc.serial_no + asset: frm.doc.name, + item_code: frm.doc.item_code, + company: frm.doc.company, + serial_no: frm.doc.serial_no, }, method: "erpnext.assets.doctype.asset.asset.make_sales_invoice", - callback: function(r) { + callback: function (r) { var doclist = frappe.model.sync(r.message); frappe.set_route("Form", doclist[0].doctype, doclist[0].name); - } - }) + }, + }); }, - create_asset_maintenance: function(frm) { + create_asset_maintenance: function (frm) { frappe.call({ args: { - "asset": frm.doc.name, - "item_code": frm.doc.item_code, - "item_name": frm.doc.item_name, - "asset_category": frm.doc.asset_category, - "company": frm.doc.company + asset: frm.doc.name, + item_code: frm.doc.item_code, + item_name: frm.doc.item_name, + asset_category: frm.doc.asset_category, + company: frm.doc.company, }, method: "erpnext.assets.doctype.asset.asset.create_asset_maintenance", - callback: function(r) { + callback: function (r) { var doclist = frappe.model.sync(r.message); frappe.set_route("Form", doclist[0].doctype, doclist[0].name); - } - }) + }, + }); }, - create_asset_repair: function(frm) { + create_asset_repair: function (frm) { frappe.call({ args: { - "asset": frm.doc.name, - "asset_name": frm.doc.asset_name + asset: frm.doc.name, + asset_name: frm.doc.asset_name, }, method: "erpnext.assets.doctype.asset.asset.create_asset_repair", - callback: function(r) { + callback: function (r) { var doclist = frappe.model.sync(r.message); frappe.set_route("Form", doclist[0].doctype, doclist[0].name); - } + }, }); }, - create_asset_capitalization: function(frm) { + create_asset_capitalization: function (frm) { frappe.call({ args: { - "asset": frm.doc.name, + asset: frm.doc.name, }, method: "erpnext.assets.doctype.asset.asset.create_asset_capitalization", - callback: function(r) { + callback: function (r) { var doclist = frappe.model.sync(r.message); frappe.set_route("Form", doclist[0].doctype, doclist[0].name); - } + }, }); }, - split_asset: function(frm) { - const title = __('Split Asset'); + split_asset: function (frm) { + const title = __("Split Asset"); const fields = [ { - fieldname: 'split_qty', - fieldtype: 'Int', - label: __('Split Qty'), - reqd: 1 - } + fieldname: "split_qty", + fieldtype: "Int", + label: __("Split Qty"), + reqd: 1, + }, ]; let dialog = new frappe.ui.Dialog({ title: title, - fields: fields + fields: fields, }); - dialog.set_primary_action(__('Split'), function() { + dialog.set_primary_action(__("Split"), function () { const dialog_data = dialog.get_values(); frappe.call({ args: { - "asset_name": frm.doc.name, - "split_qty": cint(dialog_data.split_qty) + asset_name: frm.doc.name, + split_qty: cint(dialog_data.split_qty), }, method: "erpnext.assets.doctype.asset.asset.split_asset", - callback: function(r) { + callback: function (r) { let doclist = frappe.model.sync(r.message); frappe.set_route("Form", doclist[0].doctype, doclist[0].name); - } + }, }); dialog.hide(); @@ -432,23 +485,23 @@ frappe.ui.form.on('Asset', { dialog.show(); }, - create_asset_value_adjustment: function(frm) { + create_asset_value_adjustment: function (frm) { frappe.call({ args: { - "asset": frm.doc.name, - "asset_category": frm.doc.asset_category, - "company": frm.doc.company + asset: frm.doc.name, + asset_category: frm.doc.asset_category, + company: frm.doc.company, }, method: "erpnext.assets.doctype.asset.asset.create_asset_value_adjustment", freeze: 1, - callback: function(r) { + callback: function (r) { var doclist = frappe.model.sync(r.message); frappe.set_route("Form", doclist[0].doctype, doclist[0].name); - } - }) + }, + }); }, - calculate_depreciation: function(frm) { + calculate_depreciation: function (frm) { frm.toggle_reqd("finance_books", frm.doc.calculate_depreciation); if (frm.doc.item_code && frm.doc.calculate_depreciation && frm.doc.gross_purchase_amount) { frm.trigger("set_finance_book"); @@ -457,243 +510,293 @@ frappe.ui.form.on('Asset', { } }, - gross_purchase_amount: function(frm) { + gross_purchase_amount: function (frm) { if (frm.doc.finance_books) { - frm.doc.finance_books.forEach(d => { + frm.doc.finance_books.forEach((d) => { frm.events.set_depreciation_rate(frm, d); - }) + }); } }, purchase_receipt: (frm) => { - frm.trigger('toggle_reference_doc'); + frm.trigger("toggle_reference_doc"); if (frm.doc.purchase_receipt) { if (frm.doc.item_code) { - frappe.db.get_doc('Purchase Receipt', frm.doc.purchase_receipt).then(pr_doc => { - frm.events.set_values_from_purchase_doc(frm, 'Purchase Receipt', pr_doc) + frappe.db.get_doc("Purchase Receipt", frm.doc.purchase_receipt).then((pr_doc) => { + frm.events.set_values_from_purchase_doc(frm, "Purchase Receipt", pr_doc); }); } else { - frm.set_value('purchase_receipt', ''); + frm.set_value("purchase_receipt", ""); frappe.msgprint({ - title: __('Not Allowed'), - message: __("Please select Item Code first") + title: __("Not Allowed"), + message: __("Please select Item Code first"), }); } } }, purchase_invoice: (frm) => { - frm.trigger('toggle_reference_doc'); + frm.trigger("toggle_reference_doc"); if (frm.doc.purchase_invoice) { if (frm.doc.item_code) { - frappe.db.get_doc('Purchase Invoice', frm.doc.purchase_invoice).then(pi_doc => { - frm.events.set_values_from_purchase_doc(frm, 'Purchase Invoice', pi_doc) + frappe.db.get_doc("Purchase Invoice", frm.doc.purchase_invoice).then((pi_doc) => { + frm.events.set_values_from_purchase_doc(frm, "Purchase Invoice", pi_doc); }); } else { - frm.set_value('purchase_invoice', ''); + frm.set_value("purchase_invoice", ""); frappe.msgprint({ - title: __('Not Allowed'), - message: __("Please select Item Code first") + title: __("Not Allowed"), + message: __("Please select Item Code first"), }); } } }, - set_values_from_purchase_doc: function(frm, doctype, purchase_doc) { - frm.set_value('company', purchase_doc.company); + set_values_from_purchase_doc: function (frm, doctype, purchase_doc) { + frm.set_value("company", purchase_doc.company); if (purchase_doc.bill_date) { - frm.set_value('purchase_date', purchase_doc.bill_date); + frm.set_value("purchase_date", purchase_doc.bill_date); } else { - frm.set_value('purchase_date', purchase_doc.posting_date); + frm.set_value("purchase_date", purchase_doc.posting_date); } if (!frm.doc.is_existing_asset && !frm.doc.available_for_use_date) { - frm.set_value('available_for_use_date', frm.doc.purchase_date); + frm.set_value("available_for_use_date", frm.doc.purchase_date); } - const item = purchase_doc.items.find(item => item.item_code === frm.doc.item_code); + const item = purchase_doc.items.find((item) => item.item_code === frm.doc.item_code); if (!item) { - doctype_field = frappe.scrub(doctype) - frm.set_value(doctype_field, ''); + doctype_field = frappe.scrub(doctype); + frm.set_value(doctype_field, ""); frappe.msgprint({ - title: __('Invalid {0}', [__(doctype)]), - message: __('The selected {0} does not contain the selected Asset Item.', [__(doctype)]), - indicator: 'red' + title: __("Invalid {0}", [__(doctype)]), + message: __("The selected {0} does not contain the selected Asset Item.", [__(doctype)]), + indicator: "red", }); } - frappe.db.get_value('Item', item.item_code, 'is_grouped_asset', (r) => { + frappe.db.get_value("Item", item.item_code, "is_grouped_asset", (r) => { var asset_quantity = r.is_grouped_asset ? item.qty : 1; - var purchase_amount = flt(item.valuation_rate * asset_quantity, precision('gross_purchase_amount')); + var purchase_amount = flt( + item.valuation_rate * asset_quantity, + precision("gross_purchase_amount") + ); - frm.set_value('gross_purchase_amount', purchase_amount); - frm.set_value('purchase_receipt_amount', purchase_amount); - frm.set_value('asset_quantity', asset_quantity); - frm.set_value('cost_center', item.cost_center || purchase_doc.cost_center); - if(item.asset_location) { frm.set_value('location', item.asset_location); } + frm.set_value("gross_purchase_amount", purchase_amount); + frm.set_value("purchase_receipt_amount", purchase_amount); + frm.set_value("asset_quantity", asset_quantity); + frm.set_value("cost_center", item.cost_center || purchase_doc.cost_center); + if (item.asset_location) { + frm.set_value("location", item.asset_location); + } }); }, - set_depreciation_rate: function(frm, row) { - if (row.total_number_of_depreciations && row.frequency_of_depreciation - && row.expected_value_after_useful_life) { + set_depreciation_rate: function (frm, row) { + if ( + row.total_number_of_depreciations && + row.frequency_of_depreciation && + row.expected_value_after_useful_life + ) { frappe.call({ method: "get_depreciation_rate", doc: frm.doc, args: row, - callback: function(r) { + callback: function (r) { if (r.message) { frappe.flags.dont_change_rate = true; - frappe.model.set_value(row.doctype, row.name, - "rate_of_depreciation", flt(r.message, precision("rate_of_depreciation", row))); + frappe.model.set_value( + row.doctype, + row.name, + "rate_of_depreciation", + flt(r.message, precision("rate_of_depreciation", row)) + ); } - } + }, }); } }, - set_salvage_value_percentage_or_expected_value_after_useful_life: function(frm, row, salvage_value_percentage_changed, expected_value_after_useful_life_changed) { + set_salvage_value_percentage_or_expected_value_after_useful_life: function ( + frm, + row, + salvage_value_percentage_changed, + expected_value_after_useful_life_changed + ) { if (expected_value_after_useful_life_changed) { frappe.flags.from_set_salvage_value_percentage_or_expected_value_after_useful_life = true; - const new_salvage_value_percentage = flt((row.expected_value_after_useful_life * 100) / frm.doc.gross_purchase_amount, precision("salvage_value_percentage", row)); - frappe.model.set_value(row.doctype, row.name, "salvage_value_percentage", new_salvage_value_percentage); + const new_salvage_value_percentage = flt( + (row.expected_value_after_useful_life * 100) / frm.doc.gross_purchase_amount, + precision("salvage_value_percentage", row) + ); + frappe.model.set_value( + row.doctype, + row.name, + "salvage_value_percentage", + new_salvage_value_percentage + ); frappe.flags.from_set_salvage_value_percentage_or_expected_value_after_useful_life = false; } else if (salvage_value_percentage_changed) { frappe.flags.from_set_salvage_value_percentage_or_expected_value_after_useful_life = true; - const new_expected_value_after_useful_life = flt(frm.doc.gross_purchase_amount * (row.salvage_value_percentage / 100), precision('gross_purchase_amount')); - frappe.model.set_value(row.doctype, row.name, "expected_value_after_useful_life", new_expected_value_after_useful_life); + const new_expected_value_after_useful_life = flt( + frm.doc.gross_purchase_amount * (row.salvage_value_percentage / 100), + precision("gross_purchase_amount") + ); + frappe.model.set_value( + row.doctype, + row.name, + "expected_value_after_useful_life", + new_expected_value_after_useful_life + ); frappe.flags.from_set_salvage_value_percentage_or_expected_value_after_useful_life = false; } }, }); -frappe.ui.form.on('Asset Finance Book', { - depreciation_method: function(frm, cdt, cdn) { +frappe.ui.form.on("Asset Finance Book", { + depreciation_method: function (frm, cdt, cdn) { const row = locals[cdt][cdn]; frm.events.set_depreciation_rate(frm, row); frm.events.make_schedules_editable(frm); }, - expected_value_after_useful_life: function(frm, cdt, cdn) { + expected_value_after_useful_life: function (frm, cdt, cdn) { const row = locals[cdt][cdn]; if (!frappe.flags.from_set_salvage_value_percentage_or_expected_value_after_useful_life) { - frm.events.set_salvage_value_percentage_or_expected_value_after_useful_life(frm, row, false, true); + frm.events.set_salvage_value_percentage_or_expected_value_after_useful_life( + frm, + row, + false, + true + ); } frm.events.set_depreciation_rate(frm, row); }, - salvage_value_percentage: function(frm, cdt, cdn) { + salvage_value_percentage: function (frm, cdt, cdn) { const row = locals[cdt][cdn]; if (!frappe.flags.from_set_salvage_value_percentage_or_expected_value_after_useful_life) { - frm.events.set_salvage_value_percentage_or_expected_value_after_useful_life(frm, row, true, false); + frm.events.set_salvage_value_percentage_or_expected_value_after_useful_life( + frm, + row, + true, + false + ); } }, - frequency_of_depreciation: function(frm, cdt, cdn) { + frequency_of_depreciation: function (frm, cdt, cdn) { const row = locals[cdt][cdn]; frm.events.set_depreciation_rate(frm, row); }, - total_number_of_depreciations: function(frm, cdt, cdn) { + total_number_of_depreciations: function (frm, cdt, cdn) { const row = locals[cdt][cdn]; frm.events.set_depreciation_rate(frm, row); }, - rate_of_depreciation: function(frm, cdt, cdn) { - if(!frappe.flags.dont_change_rate) { + rate_of_depreciation: function (frm, cdt, cdn) { + if (!frappe.flags.dont_change_rate) { frappe.model.set_value(cdt, cdn, "expected_value_after_useful_life", 0); } frappe.flags.dont_change_rate = false; }, - depreciation_start_date: function(frm, cdt, cdn) { + depreciation_start_date: function (frm, cdt, cdn) { const book = locals[cdt][cdn]; - if (frm.doc.available_for_use_date && book.depreciation_start_date == frm.doc.available_for_use_date) { + if ( + frm.doc.available_for_use_date && + book.depreciation_start_date == frm.doc.available_for_use_date + ) { frappe.msgprint(__("Depreciation Posting Date should not be equal to Available for Use Date.")); book.depreciation_start_date = ""; frm.refresh_field("finance_books"); } - } + }, }); -frappe.ui.form.on('Depreciation Schedule', { - make_depreciation_entry: function(frm, cdt, cdn) { +frappe.ui.form.on("Depreciation Schedule", { + make_depreciation_entry: function (frm, cdt, cdn) { var row = locals[cdt][cdn]; if (!row.journal_entry) { frappe.call({ method: "erpnext.assets.doctype.asset.depreciation.make_depreciation_entry", args: { - "asset_name": frm.doc.name, - "date": row.schedule_date + asset_name: frm.doc.name, + date: row.schedule_date, }, - callback: function(r) { + callback: function (r) { frappe.model.sync(r.message); frm.refresh(); - } - }) + }, + }); } }, - depreciation_amount: function(frm, cdt, cdn) { + depreciation_amount: function (frm, cdt, cdn) { erpnext.asset.set_accumulated_depreciation(frm, locals[cdt][cdn].finance_book_id); - } - + }, }); -erpnext.asset.set_accumulated_depreciation = function(frm, finance_book_id) { +erpnext.asset.set_accumulated_depreciation = function (frm, finance_book_id) { var depreciation_method = frm.doc.finance_books[Number(finance_book_id) - 1].depreciation_method; - if(depreciation_method != "Manual") return; + if (depreciation_method != "Manual") return; var accumulated_depreciation = flt(frm.doc.opening_accumulated_depreciation); - $.each(frm.doc.schedules || [], function(i, row) { + $.each(frm.doc.schedules || [], function (i, row) { if (row.finance_book_id === finance_book_id) { - accumulated_depreciation += flt(row.depreciation_amount); - frappe.model.set_value(row.doctype, row.name, "accumulated_depreciation_amount", accumulated_depreciation); - }; - }) + accumulated_depreciation += flt(row.depreciation_amount); + frappe.model.set_value( + row.doctype, + row.name, + "accumulated_depreciation_amount", + accumulated_depreciation + ); + } + }); }; -erpnext.asset.scrap_asset = function(frm) { +erpnext.asset.scrap_asset = function (frm) { frappe.confirm(__("Do you really want to scrap this asset?"), function () { frappe.call({ args: { - "asset_name": frm.doc.name + asset_name: frm.doc.name, }, method: "erpnext.assets.doctype.asset.depreciation.scrap_asset", - callback: function(r) { + callback: function (r) { cur_frm.reload_doc(); - } - }) - }) + }, + }); + }); }; -erpnext.asset.restore_asset = function(frm) { +erpnext.asset.restore_asset = function (frm) { frappe.confirm(__("Do you really want to restore this scrapped asset?"), function () { frappe.call({ args: { - "asset_name": frm.doc.name + asset_name: frm.doc.name, }, method: "erpnext.assets.doctype.asset.depreciation.restore_asset", - callback: function(r) { + callback: function (r) { cur_frm.reload_doc(); - } - }) - }) + }, + }); + }); }; -erpnext.asset.transfer_asset = function() { +erpnext.asset.transfer_asset = function () { frappe.call({ method: "erpnext.assets.doctype.asset.asset.make_asset_movement", freeze: true, - args:{ - "assets": [{ name: cur_frm.doc.name }], - "purpose": "Transfer" + args: { + assets: [{ name: cur_frm.doc.name }], + purpose: "Transfer", }, callback: function (r) { if (r.message) { var doc = frappe.model.sync(r.message)[0]; frappe.set_route("Form", doc.doctype, doc.name); } - } + }, }); }; diff --git a/erpnext/assets/doctype/asset/asset_list.js b/erpnext/assets/doctype/asset/asset_list.js index 5f53b005aaf..712958adcfc 100644 --- a/erpnext/assets/doctype/asset/asset_list.js +++ b/erpnext/assets/doctype/asset/asset_list.js @@ -1,56 +1,46 @@ -frappe.listview_settings['Asset'] = { - add_fields: ['status'], +frappe.listview_settings["Asset"] = { + add_fields: ["status"], get_indicator: function (doc) { if (doc.status === "Fully Depreciated") { return [__("Fully Depreciated"), "green", "status,=,Fully Depreciated"]; - } else if (doc.status === "Partially Depreciated") { return [__("Partially Depreciated"), "grey", "status,=,Partially Depreciated"]; - } else if (doc.status === "Sold") { return [__("Sold"), "green", "status,=,Sold"]; - } else if (["Capitalized", "Decapitalized"].includes(doc.status)) { return [__(doc.status), "grey", "status,=," + doc.status]; - } else if (doc.status === "Scrapped") { return [__("Scrapped"), "grey", "status,=,Scrapped"]; - } else if (doc.status === "In Maintenance") { return [__("In Maintenance"), "orange", "status,=,In Maintenance"]; - } else if (doc.status === "Out of Order") { return [__("Out of Order"), "grey", "status,=,Out of Order"]; - } else if (doc.status === "Issue") { return [__("Issue"), "orange", "status,=,Issue"]; - } else if (doc.status === "Receipt") { return [__("Receipt"), "green", "status,=,Receipt"]; - } else if (doc.status === "Submitted") { return [__("Submitted"), "blue", "status,=,Submitted"]; - } else if (doc.status === "Draft") { return [__("Draft"), "red", "status,=,Draft"]; } }, - onload: function(me) { - me.page.add_action_item(__("Make Asset Movement"), function() { + onload: function (me) { + me.page.add_action_item(__("Make Asset Movement"), function () { const assets = me.get_checked_items(); frappe.call({ method: "erpnext.assets.doctype.asset.asset.make_asset_movement", freeze: true, - args:{ - "assets": assets + args: { + assets: assets, }, callback: function (r) { if (r.message) { var doc = frappe.model.sync(r.message)[0]; frappe.set_route("Form", doc.doctype, doc.name); } - } + }, }); }); }, -} +}; diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js index e3e57ec87b2..6ac6289c837 100644 --- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js +++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js @@ -3,7 +3,6 @@ frappe.provide("erpnext.assets"); - erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.stock.StockController { setup() { this.setup_posting_date_time_check(); @@ -17,7 +16,10 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s refresh() { this.show_general_ledger(); - if ((this.frm.doc.stock_items && this.frm.doc.stock_items.length) || !this.frm.doc.target_is_fixed_asset) { + if ( + (this.frm.doc.stock_items && this.frm.doc.stock_items.length) || + !this.frm.doc.target_is_fixed_asset + ) { this.show_stock_ledger(); } @@ -32,68 +34,77 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s me.setup_warehouse_query(); - me.frm.set_query("target_item_code", function() { + me.frm.set_query("target_item_code", function () { if (me.frm.doc.entry_type == "Capitalization") { - return erpnext.queries.item({"is_stock_item": 0, "is_fixed_asset": 1}); + return erpnext.queries.item({ is_stock_item: 0, is_fixed_asset: 1 }); } else { - return erpnext.queries.item({"is_stock_item": 1, "is_fixed_asset": 0}); + return erpnext.queries.item({ is_stock_item: 1, is_fixed_asset: 0 }); } }); - me.frm.set_query("target_asset", function() { + me.frm.set_query("target_asset", function () { return { - filters: {'is_composite_asset': 1, 'docstatus': 0 } - } + filters: { is_composite_asset: 1, docstatus: 0 }, + }; }); - me.frm.set_query("asset", "asset_items", function() { + me.frm.set_query("asset", "asset_items", function () { var filters = { - 'status': ["not in", ["Draft", "Scrapped", "Sold", "Capitalized", "Decapitalized"]], - 'docstatus': 1 + status: ["not in", ["Draft", "Scrapped", "Sold", "Capitalized", "Decapitalized"]], + docstatus: 1, }; if (me.frm.doc.target_asset) { - filters['name'] = ['!=', me.frm.doc.target_asset]; + filters["name"] = ["!=", me.frm.doc.target_asset]; } return { - filters: filters + filters: filters, }; }); - me.frm.set_query("item_code", "stock_items", function() { - return erpnext.queries.item({"is_stock_item": 1}); + me.frm.set_query("item_code", "stock_items", function () { + return erpnext.queries.item({ is_stock_item: 1 }); }); - me.frm.set_query("item_code", "service_items", function() { - return erpnext.queries.item({"is_stock_item": 0, "is_fixed_asset": 0}); + me.frm.set_query("item_code", "service_items", function () { + return erpnext.queries.item({ is_stock_item: 0, is_fixed_asset: 0 }); }); - me.frm.set_query('batch_no', 'stock_items', function(doc, cdt, cdn) { + me.frm.set_query("batch_no", "stock_items", function (doc, cdt, cdn) { var item = locals[cdt][cdn]; if (!item.item_code) { frappe.throw(__("Please enter Item Code to get Batch Number")); } else { var filters = { - 'item_code': item.item_code, - 'posting_date': me.frm.doc.posting_date || frappe.datetime.nowdate(), - 'warehouse': item.warehouse + item_code: item.item_code, + posting_date: me.frm.doc.posting_date || frappe.datetime.nowdate(), + warehouse: item.warehouse, }; return { query: "erpnext.controllers.queries.get_batch_no", - filters: filters + filters: filters, }; } }); - me.frm.set_query('expense_account', 'service_items', function() { + me.frm.set_query("expense_account", "service_items", function () { return { filters: { - "account_type": ['in', ["Tax", "Expense Account", "Income Account", "Expenses Included In Valuation", "Expenses Included In Asset Valuation"]], - "is_group": 0, - "company": me.frm.doc.company - } + account_type: [ + "in", + [ + "Tax", + "Expense Account", + "Income Account", + "Expenses Included In Valuation", + "Expenses Included In Asset Valuation", + ], + ], + is_group: 0, + company: me.frm.doc.company, + }, }; }); } @@ -103,7 +114,10 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s } target_asset() { - if (this.frm.doc.target_asset && this.frm.doc.capitalization_method === "Choose a WIP composite asset") { + if ( + this.frm.doc.target_asset && + this.frm.doc.capitalization_method === "Choose a WIP composite asset" + ) { this.set_consumed_stock_items_tagged_to_wip_composite_asset(this.frm.doc.target_asset); this.get_target_asset_details(); } @@ -120,7 +134,7 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s }, callback: function (r) { if (!r.exc && r.message) { - if(r.message[0] && r.message[0].length) { + if (r.message[0] && r.message[0].length) { me.frm.clear_table("stock_items"); for (let item of r.message[0]) { me.frm.add_child("stock_items", item); @@ -137,7 +151,7 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s me.calculate_totals(); } - } + }, }); } } @@ -169,7 +183,7 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s if (this.frm.doc.posting_date) { frappe.run_serially([ () => this.get_all_item_warehouse_details(), - () => this.get_all_asset_values() + () => this.get_all_asset_values(), ]); } } @@ -225,15 +239,15 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s } stock_items_add(doc, cdt, cdn) { - erpnext.accounts.dimensions.copy_dimension_from_first_row(this.frm, cdt, cdn, 'stock_items'); + erpnext.accounts.dimensions.copy_dimension_from_first_row(this.frm, cdt, cdn, "stock_items"); } asset_items_add(doc, cdt, cdn) { - erpnext.accounts.dimensions.copy_dimension_from_first_row(this.frm, cdt, cdn, 'asset_items'); + erpnext.accounts.dimensions.copy_dimension_from_first_row(this.frm, cdt, cdn, "asset_items"); } serivce_items_add(doc, cdt, cdn) { - erpnext.accounts.dimensions.copy_dimension_from_first_row(this.frm, cdt, cdn, 'service_items'); + erpnext.accounts.dimensions.copy_dimension_from_first_row(this.frm, cdt, cdn, "service_items"); } get_target_item_details() { @@ -251,7 +265,7 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s if (!r.exc) { me.frm.refresh_fields(); } - } + }, }); } } @@ -271,7 +285,7 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s if (!r.exc) { me.frm.refresh_fields(); } - } + }, }); } } @@ -293,13 +307,13 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s company: me.frm.doc.company, posting_date: me.frm.doc.posting_date, posting_time: me.frm.doc.posting_time, - } + }, }, callback: function (r) { if (!r.exc) { me.calculate_totals(); } - } + }, }); } } @@ -320,13 +334,13 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s finance_book: row.finance_book || me.frm.doc.finance_book, posting_date: me.frm.doc.posting_date, posting_time: me.frm.doc.posting_time, - } + }, }, callback: function (r) { if (!r.exc) { me.calculate_totals(); } - } + }, }); } } @@ -344,13 +358,13 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s qty: flt(row.qty), expense_account: row.expense_account, company: me.frm.doc.company, - } + }, }, callback: function (r) { if (!r.exc) { me.calculate_totals(); } - } + }, }); } } @@ -363,23 +377,23 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s child: item, args: { args: { - 'item_code': item.item_code, - 'warehouse': cstr(item.warehouse), - 'qty': flt(item.stock_qty), - 'serial_no': item.serial_no, - 'posting_date': me.frm.doc.posting_date, - 'posting_time': me.frm.doc.posting_time, - 'company': me.frm.doc.company, - 'voucher_type': me.frm.doc.doctype, - 'voucher_no': me.frm.doc.name, - 'allow_zero_valuation': 1 - } + item_code: item.item_code, + warehouse: cstr(item.warehouse), + qty: flt(item.stock_qty), + serial_no: item.serial_no, + posting_date: me.frm.doc.posting_date, + posting_time: me.frm.doc.posting_time, + company: me.frm.doc.company, + voucher_type: me.frm.doc.doctype, + voucher_no: me.frm.doc.name, + allow_zero_valuation: 1, + }, }, - callback: function(r) { + callback: function (r) { if (!r.exc) { me.calculate_totals(); } - } + }, }); } } @@ -389,11 +403,11 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s return me.frm.call({ method: "set_warehouse_details", doc: me.frm.doc, - callback: function(r) { + callback: function (r) { if (!r.exc) { me.calculate_totals(); } - } + }, }); } @@ -402,11 +416,11 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s return me.frm.call({ method: "set_asset_values", doc: me.frm.doc, - callback: function(r) { + callback: function (r) { if (!r.exc) { me.calculate_totals(); } - } + }, }); } @@ -418,33 +432,38 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s me.frm.doc.service_items_total = 0; $.each(me.frm.doc.stock_items || [], function (i, d) { - d.amount = flt(flt(d.stock_qty) * flt(d.valuation_rate), precision('amount', d)); + d.amount = flt(flt(d.stock_qty) * flt(d.valuation_rate), precision("amount", d)); me.frm.doc.stock_items_total += d.amount; }); $.each(me.frm.doc.asset_items || [], function (i, d) { - d.asset_value = flt(flt(d.asset_value), precision('asset_value', d)); + d.asset_value = flt(flt(d.asset_value), precision("asset_value", d)); me.frm.doc.asset_items_total += d.asset_value; }); $.each(me.frm.doc.service_items || [], function (i, d) { - d.amount = flt(flt(d.qty) * flt(d.rate), precision('amount', d)); + d.amount = flt(flt(d.qty) * flt(d.rate), precision("amount", d)); me.frm.doc.service_items_total += d.amount; }); - me.frm.doc.stock_items_total = flt(me.frm.doc.stock_items_total, precision('stock_items_total')); - me.frm.doc.asset_items_total = flt(me.frm.doc.asset_items_total, precision('asset_items_total')); - me.frm.doc.service_items_total = flt(me.frm.doc.service_items_total, precision('service_items_total')); + me.frm.doc.stock_items_total = flt(me.frm.doc.stock_items_total, precision("stock_items_total")); + me.frm.doc.asset_items_total = flt(me.frm.doc.asset_items_total, precision("asset_items_total")); + me.frm.doc.service_items_total = flt( + me.frm.doc.service_items_total, + precision("service_items_total") + ); - me.frm.doc.total_value = me.frm.doc.stock_items_total + me.frm.doc.asset_items_total + me.frm.doc.service_items_total; - me.frm.doc.total_value = flt(me.frm.doc.total_value, precision('total_value')); + me.frm.doc.total_value = + me.frm.doc.stock_items_total + me.frm.doc.asset_items_total + me.frm.doc.service_items_total; + me.frm.doc.total_value = flt(me.frm.doc.total_value, precision("total_value")); - me.frm.doc.target_qty = flt(me.frm.doc.target_qty, precision('target_qty')); - me.frm.doc.target_incoming_rate = me.frm.doc.target_qty ? me.frm.doc.total_value / flt(me.frm.doc.target_qty) + me.frm.doc.target_qty = flt(me.frm.doc.target_qty, precision("target_qty")); + me.frm.doc.target_incoming_rate = me.frm.doc.target_qty + ? me.frm.doc.total_value / flt(me.frm.doc.target_qty) : me.frm.doc.total_value; me.frm.refresh_fields(); } }; -cur_frm.cscript = new erpnext.assets.AssetCapitalization({frm: cur_frm}); +cur_frm.cscript = new erpnext.assets.AssetCapitalization({ frm: cur_frm }); diff --git a/erpnext/assets/doctype/asset_category/asset_category.js b/erpnext/assets/doctype/asset_category/asset_category.js index 7dde14ea0e6..046b62f244e 100644 --- a/erpnext/assets/doctype/asset_category/asset_category.js +++ b/erpnext/assets/doctype/asset_category/asset_category.js @@ -1,56 +1,55 @@ // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Asset Category', { - onload: function(frm) { - frm.add_fetch('company_name', 'accumulated_depreciation_account', 'accumulated_depreciation_account'); - frm.add_fetch('company_name', 'depreciation_expense_account', 'depreciation_expense_account'); +frappe.ui.form.on("Asset Category", { + onload: function (frm) { + frm.add_fetch("company_name", "accumulated_depreciation_account", "accumulated_depreciation_account"); + frm.add_fetch("company_name", "depreciation_expense_account", "depreciation_expense_account"); - frm.set_query('fixed_asset_account', 'accounts', function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; + frm.set_query("fixed_asset_account", "accounts", function (doc, cdt, cdn) { + var d = locals[cdt][cdn]; return { - "filters": { - "account_type": "Fixed Asset", - "root_type": "Asset", - "is_group": 0, - "company": d.company_name - } + filters: { + account_type: "Fixed Asset", + root_type: "Asset", + is_group: 0, + company: d.company_name, + }, }; }); - frm.set_query('accumulated_depreciation_account', 'accounts', function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; + frm.set_query("accumulated_depreciation_account", "accounts", function (doc, cdt, cdn) { + var d = locals[cdt][cdn]; return { - "filters": { - "account_type": "Accumulated Depreciation", - "is_group": 0, - "company": d.company_name - } + filters: { + account_type: "Accumulated Depreciation", + is_group: 0, + company: d.company_name, + }, }; }); - frm.set_query('depreciation_expense_account', 'accounts', function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; + frm.set_query("depreciation_expense_account", "accounts", function (doc, cdt, cdn) { + var d = locals[cdt][cdn]; return { - "filters": { - "account_type": "Depreciation", - "root_type": ["in", ["Expense", "Income"]], - "is_group": 0, - "company": d.company_name - } + filters: { + account_type: "Depreciation", + root_type: ["in", ["Expense", "Income"]], + is_group: 0, + company: d.company_name, + }, }; }); - frm.set_query('capital_work_in_progress_account', 'accounts', function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; + frm.set_query("capital_work_in_progress_account", "accounts", function (doc, cdt, cdn) { + var d = locals[cdt][cdn]; return { - "filters": { - "account_type": "Capital Work in Progress", - "is_group": 0, - "company": d.company_name - } + filters: { + account_type: "Capital Work in Progress", + is_group: 0, + company: d.company_name, + }, }; }); - - } + }, }); diff --git a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js index 5c03b98873b..83dabab8935 100644 --- a/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js +++ b/erpnext/assets/doctype/asset_maintenance/asset_maintenance.js @@ -1,49 +1,47 @@ // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Asset Maintenance', { +frappe.ui.form.on("Asset Maintenance", { setup: (frm) => { - frm.set_query("assign_to", "asset_maintenance_tasks", function(doc) { + frm.set_query("assign_to", "asset_maintenance_tasks", function (doc) { return { query: "erpnext.assets.doctype.asset_maintenance.asset_maintenance.get_team_members", filters: { - maintenance_team: doc.maintenance_team - } + maintenance_team: doc.maintenance_team, + }, }; }); - frm.set_indicator_formatter('maintenance_status', - function(doc) { - let indicator = 'blue'; - if (doc.maintenance_status == 'Overdue') { - indicator = 'orange'; - } - if (doc.maintenance_status == 'Cancelled') { - indicator = 'red'; - } - return indicator; + frm.set_indicator_formatter("maintenance_status", function (doc) { + let indicator = "blue"; + if (doc.maintenance_status == "Overdue") { + indicator = "orange"; } - ); + if (doc.maintenance_status == "Cancelled") { + indicator = "red"; + } + return indicator; + }); }, refresh: (frm) => { - if(!frm.is_new()) { - frm.trigger('make_dashboard'); + if (!frm.is_new()) { + frm.trigger("make_dashboard"); } }, make_dashboard: (frm) => { - if(!frm.is_new()) { + if (!frm.is_new()) { frappe.call({ - method: 'erpnext.assets.doctype.asset_maintenance.asset_maintenance.get_maintenance_log', - args: {asset_name: frm.doc.asset_name}, + method: "erpnext.assets.doctype.asset_maintenance.asset_maintenance.get_maintenance_log", + args: { asset_name: frm.doc.asset_name }, callback: (r) => { - if(!r.message) { + if (!r.message) { return; } - const section = frm.dashboard.add_section('', __("Maintenance Log")); - var rows = $('
      ').appendTo(section); + const section = frm.dashboard.add_section("", __("Maintenance Log")); + var rows = $("
      ").appendTo(section); // show - (r.message || []).forEach(function(d) { + (r.message || []).forEach(function (d) { $(`
      { get_next_due_date(frm, cdt, cdn); }, - periodicity: (frm, cdt, cdn) => { + periodicity: (frm, cdt, cdn) => { get_next_due_date(frm, cdt, cdn); }, - last_completion_date: (frm, cdt, cdn) => { + last_completion_date: (frm, cdt, cdn) => { get_next_due_date(frm, cdt, cdn); }, - end_date: (frm, cdt, cdn) => { + end_date: (frm, cdt, cdn) => { get_next_due_date(frm, cdt, cdn); - } + }, }); var get_next_due_date = function (frm, cdt, cdn) { var d = locals[cdt][cdn]; if (d.start_date && d.periodicity) { return frappe.call({ - method: 'erpnext.assets.doctype.asset_maintenance.asset_maintenance.calculate_next_due_date', + method: "erpnext.assets.doctype.asset_maintenance.asset_maintenance.calculate_next_due_date", args: { start_date: d.start_date, periodicity: d.periodicity, end_date: d.end_date, last_completion_date: d.last_completion_date, - next_due_date: d.next_due_date + next_due_date: d.next_due_date, }, - callback: function(r) { + callback: function (r) { if (r.message) { frappe.model.set_value(cdt, cdn, "next_due_date", r.message); - } - else { + } else { frappe.model.set_value(cdt, cdn, "next_due_date", ""); } - } + }, }); } }; diff --git a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.js b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.js index bcdc3acf0ac..47a2128c379 100644 --- a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.js +++ b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log.js @@ -1,15 +1,15 @@ // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Asset Maintenance Log', { +frappe.ui.form.on("Asset Maintenance Log", { asset_maintenance: (frm) => { - frm.set_query('task', function(doc) { + frm.set_query("task", function (doc) { return { query: "erpnext.assets.doctype.asset_maintenance_log.asset_maintenance_log.get_maintenance_tasks", filters: { - 'asset_maintenance': doc.asset_maintenance - } + asset_maintenance: doc.asset_maintenance, + }, }; }); - } + }, }); diff --git a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log_calendar.js b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log_calendar.js index c804b31e908..7b7b5085369 100644 --- a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log_calendar.js +++ b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log_calendar.js @@ -3,20 +3,20 @@ frappe.views.calendar["Asset Maintenance Log"] = { field_map: { - "start": "due_date", - "end": "due_date", - "id": "name", - "title": "task", - "allDay": "allDay", - "progress": "progress" + start: "due_date", + end: "due_date", + id: "name", + title: "task", + allDay: "allDay", + progress: "progress", }, filters: [ { - "fieldtype": "Link", - "fieldname": "asset_name", - "options": "Asset Maintenance", - "label": __("Asset Maintenance") - } + fieldtype: "Link", + fieldname: "asset_name", + options: "Asset Maintenance", + label: __("Asset Maintenance"), + }, ], - get_events_method: "frappe.desk.calendar.get_events" + get_events_method: "frappe.desk.calendar.get_events", }; diff --git a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log_list.js b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log_list.js index 23000e60eff..13f2444742d 100644 --- a/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log_list.js +++ b/erpnext/assets/doctype/asset_maintenance_log/asset_maintenance_log_list.js @@ -1,15 +1,15 @@ -frappe.listview_settings['Asset Maintenance Log'] = { +frappe.listview_settings["Asset Maintenance Log"] = { add_fields: ["maintenance_status"], has_indicator_for_draft: 1, - get_indicator: function(doc) { - if (doc.maintenance_status=="Planned") { + get_indicator: function (doc) { + if (doc.maintenance_status == "Planned") { return [__(doc.maintenance_status), "orange", "status,=," + doc.maintenance_status]; - } else if (doc.maintenance_status=="Completed") { + } else if (doc.maintenance_status == "Completed") { return [__(doc.maintenance_status), "green", "status,=," + doc.maintenance_status]; - } else if (doc.maintenance_status=="Cancelled") { + } else if (doc.maintenance_status == "Cancelled") { return [__(doc.maintenance_status), "red", "status,=," + doc.maintenance_status]; - } else if (doc.maintenance_status=="Overdue") { + } else if (doc.maintenance_status == "Overdue") { return [__(doc.maintenance_status), "red", "status,=," + doc.maintenance_status]; } - } + }, }; diff --git a/erpnext/assets/doctype/asset_maintenance_team/asset_maintenance_team.js b/erpnext/assets/doctype/asset_maintenance_team/asset_maintenance_team.js index c94e3dbc3c0..85987651900 100644 --- a/erpnext/assets/doctype/asset_maintenance_team/asset_maintenance_team.js +++ b/erpnext/assets/doctype/asset_maintenance_team/asset_maintenance_team.js @@ -1,8 +1,6 @@ // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Asset Maintenance Team', { - refresh: function() { - - } +frappe.ui.form.on("Asset Maintenance Team", { + refresh: function () {}, }); diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.js b/erpnext/assets/doctype/asset_movement/asset_movement.js index 4ccc3f8013b..e445c90f308 100644 --- a/erpnext/assets/doctype/asset_movement/asset_movement.js +++ b/erpnext/assets/doctype/asset_movement/asset_movement.js @@ -1,104 +1,107 @@ // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Asset Movement', { +frappe.ui.form.on("Asset Movement", { setup: (frm) => { frm.set_query("to_employee", "assets", (doc) => { return { filters: { - company: doc.company - } + company: doc.company, + }, }; - }) + }); frm.set_query("from_employee", "assets", (doc) => { return { filters: { - company: doc.company - } + company: doc.company, + }, }; - }) + }); frm.set_query("reference_name", (doc) => { return { filters: { company: doc.company, - docstatus: 1 - } + docstatus: 1, + }, }; - }) + }); frm.set_query("reference_doctype", () => { return { filters: { - name: ["in", ["Purchase Receipt", "Purchase Invoice"]] - } + name: ["in", ["Purchase Receipt", "Purchase Invoice"]], + }, }; }), - frm.set_query("asset", "assets", () => { - return { - filters: { - status: ["not in", ["Draft"]] - } - } - }) + frm.set_query("asset", "assets", () => { + return { + filters: { + status: ["not in", ["Draft"]], + }, + }; + }); }, onload: (frm) => { - frm.trigger('set_required_fields'); + frm.trigger("set_required_fields"); }, purpose: (frm) => { - frm.trigger('set_required_fields'); + frm.trigger("set_required_fields"); }, set_required_fields: (frm, cdt, cdn) => { let fieldnames_to_be_altered; - if (frm.doc.purpose === 'Transfer') { + if (frm.doc.purpose === "Transfer") { fieldnames_to_be_altered = { target_location: { read_only: 0, reqd: 1 }, source_location: { read_only: 1, reqd: 1 }, from_employee: { read_only: 1, reqd: 0 }, - to_employee: { read_only: 1, reqd: 0 } + to_employee: { read_only: 1, reqd: 0 }, }; - } - else if (frm.doc.purpose === 'Receipt') { + } else if (frm.doc.purpose === "Receipt") { fieldnames_to_be_altered = { target_location: { read_only: 0, reqd: 1 }, source_location: { read_only: 1, reqd: 0 }, from_employee: { read_only: 0, reqd: 0 }, - to_employee: { read_only: 1, reqd: 0 } + to_employee: { read_only: 1, reqd: 0 }, }; - } - else if (frm.doc.purpose === 'Issue') { + } else if (frm.doc.purpose === "Issue") { fieldnames_to_be_altered = { target_location: { read_only: 1, reqd: 0 }, source_location: { read_only: 1, reqd: 0 }, from_employee: { read_only: 1, reqd: 0 }, - to_employee: { read_only: 0, reqd: 1 } + to_employee: { read_only: 0, reqd: 1 }, }; } if (fieldnames_to_be_altered) { - Object.keys(fieldnames_to_be_altered).forEach(fieldname => { + Object.keys(fieldnames_to_be_altered).forEach((fieldname) => { let property_to_be_altered = fieldnames_to_be_altered[fieldname]; - Object.keys(property_to_be_altered).forEach(property => { + Object.keys(property_to_be_altered).forEach((property) => { let value = property_to_be_altered[property]; - frm.fields_dict['assets'].grid.update_docfield_property(fieldname, property, value); + frm.fields_dict["assets"].grid.update_docfield_property(fieldname, property, value); }); }); - frm.refresh_field('assets'); + frm.refresh_field("assets"); } - } + }, }); -frappe.ui.form.on('Asset Movement Item', { - asset: function(frm, cdt, cdn) { +frappe.ui.form.on("Asset Movement Item", { + asset: function (frm, cdt, cdn) { // on manual entry of an asset auto sets their source location / employee const asset_name = locals[cdt][cdn].asset; - if (asset_name){ - frappe.db.get_doc('Asset', asset_name).then((asset_doc) => { - if(asset_doc.location) frappe.model.set_value(cdt, cdn, 'source_location', asset_doc.location); - if(asset_doc.custodian) frappe.model.set_value(cdt, cdn, 'from_employee', asset_doc.custodian); - }).catch((err) => { - console.log(err); // eslint-disable-line - }); + if (asset_name) { + frappe.db + .get_doc("Asset", asset_name) + .then((asset_doc) => { + if (asset_doc.location) + frappe.model.set_value(cdt, cdn, "source_location", asset_doc.location); + if (asset_doc.custodian) + frappe.model.set_value(cdt, cdn, "from_employee", asset_doc.custodian); + }) + .catch((err) => { + console.log(err); // eslint-disable-line + }); } - } + }, }); diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.js b/erpnext/assets/doctype/asset_repair/asset_repair.js index f9ed2cc3448..0ce178f4e6b 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair.js +++ b/erpnext/assets/doctype/asset_repair/asset_repair.js @@ -1,40 +1,40 @@ // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Asset Repair', { - setup: function(frm) { - frm.fields_dict.cost_center.get_query = function(doc) { +frappe.ui.form.on("Asset Repair", { + setup: function (frm) { + frm.fields_dict.cost_center.get_query = function (doc) { return { filters: { - 'is_group': 0, - 'company': doc.company - } + is_group: 0, + company: doc.company, + }, }; }; - frm.fields_dict.project.get_query = function(doc) { + frm.fields_dict.project.get_query = function (doc) { return { filters: { - 'company': doc.company - } + company: doc.company, + }, }; }; - frm.fields_dict.warehouse.get_query = function(doc) { + frm.fields_dict.warehouse.get_query = function (doc) { return { filters: { - 'is_group': 0, - 'company': doc.company - } + is_group: 0, + company: doc.company, + }, }; }; }, - refresh: function(frm) { + refresh: function (frm) { if (frm.doc.docstatus) { - frm.add_custom_button(__("View General Ledger"), function() { + frm.add_custom_button(__("View General Ledger"), function () { frappe.route_options = { - "voucher_no": frm.doc.name + voucher_no: frm.doc.name, }; frappe.set_route("query-report", "General Ledger"); }); @@ -43,55 +43,55 @@ frappe.ui.form.on('Asset Repair', { repair_status: (frm) => { if (frm.doc.completion_date && frm.doc.repair_status == "Completed") { - frappe.call ({ + frappe.call({ method: "erpnext.assets.doctype.asset_repair.asset_repair.get_downtime", args: { - "failure_date":frm.doc.failure_date, - "completion_date":frm.doc.completion_date + failure_date: frm.doc.failure_date, + completion_date: frm.doc.completion_date, }, - callback: function(r) { - if(r.message) { + callback: function (r) { + if (r.message) { frm.set_value("downtime", r.message + " Hrs"); } - } + }, }); } if (frm.doc.repair_status == "Completed") { - frm.set_value('completion_date', frappe.datetime.now_datetime()); + frm.set_value("completion_date", frappe.datetime.now_datetime()); } }, stock_items_on_form_rendered() { erpnext.setup_serial_or_batch_no(); - } + }, }); -frappe.ui.form.on('Asset Repair Consumed Item', { - item_code: function(frm, cdt, cdn) { +frappe.ui.form.on("Asset Repair Consumed Item", { + item_code: function (frm, cdt, cdn) { var item = locals[cdt][cdn]; let item_args = { - 'item_code': item.item_code, - 'warehouse': frm.doc.warehouse, - 'qty': item.consumed_quantity, - 'serial_no': item.serial_no, - 'company': frm.doc.company, + item_code: item.item_code, + warehouse: frm.doc.warehouse, + qty: item.consumed_quantity, + serial_no: item.serial_no, + company: frm.doc.company, }; frappe.call({ - method: 'erpnext.stock.utils.get_incoming_rate', + method: "erpnext.stock.utils.get_incoming_rate", args: { - args: item_args + args: item_args, + }, + callback: function (r) { + frappe.model.set_value(cdt, cdn, "valuation_rate", r.message); }, - callback: function(r) { - frappe.model.set_value(cdt, cdn, 'valuation_rate', r.message); - } }); }, - consumed_quantity: function(frm, cdt, cdn) { + consumed_quantity: function (frm, cdt, cdn) { var row = locals[cdt][cdn]; - frappe.model.set_value(cdt, cdn, 'total_value', row.consumed_quantity * row.valuation_rate); + frappe.model.set_value(cdt, cdn, "total_value", row.consumed_quantity * row.valuation_rate); }, }); diff --git a/erpnext/assets/doctype/asset_repair/asset_repair_list.js b/erpnext/assets/doctype/asset_repair/asset_repair_list.js index 86376f40046..633c39ba77f 100644 --- a/erpnext/assets/doctype/asset_repair/asset_repair_list.js +++ b/erpnext/assets/doctype/asset_repair/asset_repair_list.js @@ -1,12 +1,12 @@ -frappe.listview_settings['Asset Repair'] = { +frappe.listview_settings["Asset Repair"] = { add_fields: ["repair_status"], - get_indicator: function(doc) { - if(doc.repair_status=="Pending") { + get_indicator: function (doc) { + if (doc.repair_status == "Pending") { return [__("Pending"), "orange"]; - } else if(doc.repair_status=="Completed") { + } else if (doc.repair_status == "Completed") { return [__("Completed"), "green"]; - } else if(doc.repair_status=="Cancelled") { + } else if (doc.repair_status == "Cancelled") { return [__("Cancelled"), "red"]; } - } + }, }; diff --git a/erpnext/assets/doctype/asset_shift_allocation/asset_shift_allocation.js b/erpnext/assets/doctype/asset_shift_allocation/asset_shift_allocation.js index 2bd41c13773..fd92ce7f2fe 100644 --- a/erpnext/assets/doctype/asset_shift_allocation/asset_shift_allocation.js +++ b/erpnext/assets/doctype/asset_shift_allocation/asset_shift_allocation.js @@ -1,15 +1,15 @@ // Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Asset Shift Allocation', { - onload: function(frm) { +frappe.ui.form.on("Asset Shift Allocation", { + onload: function (frm) { frm.events.make_schedules_editable(frm); }, - make_schedules_editable: function(frm) { + make_schedules_editable: function (frm) { frm.toggle_enable("depreciation_schedule", true); frm.fields_dict["depreciation_schedule"].grid.toggle_enable("schedule_date", false); frm.fields_dict["depreciation_schedule"].grid.toggle_enable("depreciation_amount", false); frm.fields_dict["depreciation_schedule"].grid.toggle_enable("shift", true); - } -}); \ No newline at end of file + }, +}); diff --git a/erpnext/assets/doctype/asset_shift_factor/asset_shift_factor.js b/erpnext/assets/doctype/asset_shift_factor/asset_shift_factor.js index e6552d8370d..4795b0672ae 100644 --- a/erpnext/assets/doctype/asset_shift_factor/asset_shift_factor.js +++ b/erpnext/assets/doctype/asset_shift_factor/asset_shift_factor.js @@ -1,8 +1,7 @@ // Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Asset Shift Factor', { +frappe.ui.form.on("Asset Shift Factor", { // refresh: function(frm) { - // } }); diff --git a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.js b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.js index d07f40cdf42..7fbe6d0bd12 100644 --- a/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.js +++ b/erpnext/assets/doctype/asset_value_adjustment/asset_value_adjustment.js @@ -3,61 +3,61 @@ frappe.provide("erpnext.accounts.dimensions"); -frappe.ui.form.on('Asset Value Adjustment', { - setup: function(frm) { - frm.add_fetch('company', 'cost_center', 'cost_center'); - frm.set_query('cost_center', function() { +frappe.ui.form.on("Asset Value Adjustment", { + setup: function (frm) { + frm.add_fetch("company", "cost_center", "cost_center"); + frm.set_query("cost_center", function () { return { filters: { company: frm.doc.company, - is_group: 0 - } - } + is_group: 0, + }, + }; }); - frm.set_query('asset', function() { + frm.set_query("asset", function () { return { filters: { calculate_depreciation: 1, - docstatus: 1 - } + docstatus: 1, + }, }; }); }, - onload: function(frm) { - if(frm.is_new() && frm.doc.asset) { + onload: function (frm) { + if (frm.is_new() && frm.doc.asset) { frm.trigger("set_current_asset_value"); } erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); }, - company: function(frm) { + company: function (frm) { erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); }, - asset: function(frm) { + asset: function (frm) { frm.trigger("set_current_asset_value"); }, - finance_book: function(frm) { + finance_book: function (frm) { frm.trigger("set_current_asset_value"); }, - set_current_asset_value: function(frm) { + set_current_asset_value: function (frm) { if (frm.doc.asset) { frm.call({ method: "erpnext.assets.doctype.asset.asset.get_asset_value_after_depreciation", args: { asset_name: frm.doc.asset, - finance_book: frm.doc.finance_book + finance_book: frm.doc.finance_book, }, - callback: function(r) { + callback: function (r) { if (r.message) { - frm.set_value('current_asset_value', r.message); + frm.set_value("current_asset_value", r.message); } - } + }, }); } - } + }, }); diff --git a/erpnext/assets/doctype/location/location.js b/erpnext/assets/doctype/location/location.js index 0f069b2fd8e..97d72391588 100644 --- a/erpnext/assets/doctype/location/location.js +++ b/erpnext/assets/doctype/location/location.js @@ -1,13 +1,13 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Location', { +frappe.ui.form.on("Location", { setup: function (frm) { frm.set_query("parent_location", function () { return { - "filters": { - "is_group": 1 - } + filters: { + is_group: 1, + }, }; }); }, @@ -15,10 +15,9 @@ frappe.ui.form.on('Location', { onload_post_render(frm) { if (!frm.doc.location && frm.doc.latitude && frm.doc.longitude) { frm.fields_dict.location.map.setView([frm.doc.latitude, frm.doc.longitude], 13); - } - else { - frm.doc.latitude = frm.fields_dict.location.map.getCenter()['lat']; - frm.doc.longitude = frm.fields_dict.location.map.getCenter()['lng']; + } else { + frm.doc.latitude = frm.fields_dict.location.map.getCenter()["lat"]; + frm.doc.longitude = frm.fields_dict.location.map.getCenter()["lng"]; } }, }); diff --git a/erpnext/assets/doctype/location/location_tree.js b/erpnext/assets/doctype/location/location_tree.js index 3e105f6ca49..c3484c2469b 100644 --- a/erpnext/assets/doctype/location/location_tree.js +++ b/erpnext/assets/doctype/location/location_tree.js @@ -1,7 +1,7 @@ frappe.treeview_settings["Location"] = { ignore_fields: ["parent_location"], - get_tree_nodes: 'erpnext.assets.doctype.location.location.get_children', - add_tree_node: 'erpnext.assets.doctype.location.location.add_node', + get_tree_nodes: "erpnext.assets.doctype.location.location.get_children", + add_tree_node: "erpnext.assets.doctype.location.location.add_node", filters: [ { fieldname: "location", @@ -10,9 +10,9 @@ frappe.treeview_settings["Location"] = { label: __("Location"), get_query: function () { return { - filters: [["Location", "is_group", "=", 1]] + filters: [["Location", "is_group", "=", 1]], }; - } + }, }, ], breadcrumb: "Assets", @@ -24,10 +24,10 @@ frappe.treeview_settings["Location"] = { action: function () { frappe.new_doc("Location", true); }, - condition: 'frappe.boot.user.can_create.indexOf("Location") !== -1' - } + condition: 'frappe.boot.user.can_create.indexOf("Location") !== -1', + }, ], onload: function (treeview) { treeview.make_tree(); - } + }, }; diff --git a/erpnext/assets/doctype/maintenance_team_member/maintenance_team_member.js b/erpnext/assets/doctype/maintenance_team_member/maintenance_team_member.js index 2db712546c6..6f3521ffd85 100644 --- a/erpnext/assets/doctype/maintenance_team_member/maintenance_team_member.js +++ b/erpnext/assets/doctype/maintenance_team_member/maintenance_team_member.js @@ -1,8 +1,6 @@ // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Maintenance Team Member', { - refresh: function() { - - } +frappe.ui.form.on("Maintenance Team Member", { + refresh: function () {}, }); diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js index 0497addf44a..7e9ecd4d8fc 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js @@ -3,103 +3,103 @@ /* eslint-disable */ frappe.query_reports["Fixed Asset Register"] = { - "filters": [ + filters: [ { - fieldname:"company", + fieldname: "company", label: __("Company"), fieldtype: "Link", options: "Company", default: frappe.defaults.get_user_default("Company"), - reqd: 1 + reqd: 1, }, { - fieldname:"status", + fieldname: "status", label: __("Status"), fieldtype: "Select", options: "\nIn Location\nDisposed", - default: 'In Location' + default: "In Location", }, { - fieldname:"asset_category", + fieldname: "asset_category", label: __("Asset Category"), fieldtype: "Link", - options: "Asset Category" + options: "Asset Category", }, { - fieldname:"cost_center", + fieldname: "cost_center", label: __("Cost Center"), fieldtype: "Link", - options: "Cost Center" + options: "Cost Center", }, { - fieldname:"group_by", + fieldname: "group_by", label: __("Group By"), fieldtype: "Select", options: ["--Select a group--", "Asset Category", "Location"], default: "--Select a group--", - reqd: 1 + reqd: 1, }, { - fieldname:"only_existing_assets", + fieldname: "only_existing_assets", label: __("Only existing assets"), - fieldtype: "Check" + fieldtype: "Check", }, { - fieldname:"finance_book", + fieldname: "finance_book", label: __("Finance Book"), fieldtype: "Link", options: "Finance Book", }, { - "fieldname": "include_default_book_assets", - "label": __("Include Default FB Assets"), - "fieldtype": "Check", - "default": 1 + fieldname: "include_default_book_assets", + label: __("Include Default FB Assets"), + fieldtype: "Check", + default: 1, }, { - "fieldname":"filter_based_on", - "label": __("Period Based On"), - "fieldtype": "Select", - "options": ["--Select a period--", "Fiscal Year", "Date Range"], - "default": "--Select a period--", + fieldname: "filter_based_on", + label: __("Period Based On"), + fieldtype: "Select", + options: ["--Select a period--", "Fiscal Year", "Date Range"], + default: "--Select a period--", }, { - "fieldname":"from_date", - "label": __("Start Date"), - "fieldtype": "Date", - "default": frappe.datetime.add_months(frappe.datetime.nowdate(), -12), - "depends_on": "eval: doc.filter_based_on == 'Date Range'", + fieldname: "from_date", + label: __("Start Date"), + fieldtype: "Date", + default: frappe.datetime.add_months(frappe.datetime.nowdate(), -12), + depends_on: "eval: doc.filter_based_on == 'Date Range'", }, { - "fieldname":"to_date", - "label": __("End Date"), - "fieldtype": "Date", - "default": frappe.datetime.nowdate(), - "depends_on": "eval: doc.filter_based_on == 'Date Range'", + fieldname: "to_date", + label: __("End Date"), + fieldtype: "Date", + default: frappe.datetime.nowdate(), + depends_on: "eval: doc.filter_based_on == 'Date Range'", }, { - "fieldname":"from_fiscal_year", - "label": __("Start Year"), - "fieldtype": "Link", - "options": "Fiscal Year", - "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), - "depends_on": "eval: doc.filter_based_on == 'Fiscal Year'", + fieldname: "from_fiscal_year", + label: __("Start Year"), + fieldtype: "Link", + options: "Fiscal Year", + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), + depends_on: "eval: doc.filter_based_on == 'Fiscal Year'", }, { - "fieldname":"to_fiscal_year", - "label": __("End Year"), - "fieldtype": "Link", - "options": "Fiscal Year", - "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), - "depends_on": "eval: doc.filter_based_on == 'Fiscal Year'", + fieldname: "to_fiscal_year", + label: __("End Year"), + fieldtype: "Link", + options: "Fiscal Year", + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), + depends_on: "eval: doc.filter_based_on == 'Fiscal Year'", }, { - "fieldname":"date_based_on", - "label": __("Date Based On"), - "fieldtype": "Select", - "options": ["Purchase Date", "Available For Use Date"], - "default": "Purchase Date", - "depends_on": "eval: doc.filter_based_on == 'Date Range' || doc.filter_based_on == 'Fiscal Year'", + fieldname: "date_based_on", + label: __("Date Based On"), + fieldtype: "Select", + options: ["Purchase Date", "Available For Use Date"], + default: "Purchase Date", + depends_on: "eval: doc.filter_based_on == 'Date Range' || doc.filter_based_on == 'Fiscal Year'", }, - ] + ], }; diff --git a/erpnext/bulk_transaction/doctype/bulk_transaction_log/bulk_transaction_log.js b/erpnext/bulk_transaction/doctype/bulk_transaction_log/bulk_transaction_log.js index dc54d606e7a..9ee0c499d98 100644 --- a/erpnext/bulk_transaction/doctype/bulk_transaction_log/bulk_transaction_log.js +++ b/erpnext/bulk_transaction/doctype/bulk_transaction_log/bulk_transaction_log.js @@ -3,17 +3,31 @@ frappe.ui.form.on("Bulk Transaction Log", { refresh(frm) { - frm.add_custom_button(__('Succeeded Entries'), function() { - frappe.set_route('List', 'Bulk Transaction Log Detail', {'date': frm.doc.date, 'transaction_status': "Success"}); - }, __("View")); - frm.add_custom_button(__('Failed Entries'), function() { - frappe.set_route('List', 'Bulk Transaction Log Detail', {'date': frm.doc.date, 'transaction_status': "Failed"}); - }, __("View")); + frm.add_custom_button( + __("Succeeded Entries"), + function () { + frappe.set_route("List", "Bulk Transaction Log Detail", { + date: frm.doc.date, + transaction_status: "Success", + }); + }, + __("View") + ); + frm.add_custom_button( + __("Failed Entries"), + function () { + frappe.set_route("List", "Bulk Transaction Log Detail", { + date: frm.doc.date, + transaction_status: "Failed", + }); + }, + __("View") + ); if (frm.doc.failed) { - frm.add_custom_button(__('Retry Failed Transactions'), function() { + frm.add_custom_button(__("Retry Failed Transactions"), function () { frappe.call({ method: "erpnext.utilities.bulk_transaction.retry", - args: {date: frm.doc.date} + args: { date: frm.doc.date }, }); }); } diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.js b/erpnext/buying/doctype/buying_settings/buying_settings.js index 32431fc3910..11b920aea71 100644 --- a/erpnext/buying/doctype/buying_settings/buying_settings.js +++ b/erpnext/buying/doctype/buying_settings/buying_settings.js @@ -1,31 +1,38 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Buying Settings', { +frappe.ui.form.on("Buying Settings", { // refresh: function(frm) { - // } }); -frappe.tour['Buying Settings'] = [ +frappe.tour["Buying Settings"] = [ { fieldname: "supp_master_name", title: "Supplier Naming By", - description: __("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."), + description: __( + "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." + ), }, { fieldname: "buying_price_list", title: "Default Buying Price List", - description: __("Configure the default Price List when creating a new Purchase transaction. Item prices will be fetched from this Price List.") + description: __( + "Configure the default Price List when creating a new Purchase transaction. Item prices will be fetched from this Price List." + ), }, { fieldname: "po_required", title: "Purchase Order Required for Purchase Invoice & Receipt Creation", - description: __("If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice or Receipt without creating a Purchase Order first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Order' checkbox in the Supplier master.") + description: __( + "If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice or Receipt without creating a Purchase Order first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Order' checkbox in the Supplier master." + ), }, { fieldname: "pr_required", title: "Purchase Receipt Required for Purchase Invoice Creation", - description: __("If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice without creating a Purchase Receipt first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Receipt' checkbox in the Supplier master.") - } + description: __( + "If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice without creating a Purchase Receipt first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Receipt' checkbox in the Supplier master." + ), + }, ]; diff --git a/erpnext/buying/doctype/purchase_order/purchase_order_list.js b/erpnext/buying/doctype/purchase_order/purchase_order_list.js index 6594746cfc5..c1bf1f3b8d9 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order_list.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order_list.js @@ -1,6 +1,14 @@ -frappe.listview_settings['Purchase Order'] = { - add_fields: ["base_grand_total", "company", "currency", "supplier", - "supplier_name", "per_received", "per_billed", "status"], +frappe.listview_settings["Purchase Order"] = { + add_fields: [ + "base_grand_total", + "company", + "currency", + "supplier", + "supplier_name", + "per_received", + "per_billed", + "status", + ], get_indicator: function (doc) { if (doc.status === "Closed") { return [__("Closed"), "green", "status,=,Closed"]; @@ -10,15 +18,25 @@ frappe.listview_settings['Purchase Order'] = { return [__("Delivered"), "green", "status,=,Closed"]; } else if (flt(doc.per_received, 2) < 100 && doc.status !== "Closed") { if (flt(doc.per_billed, 2) < 100) { - return [__("To Receive and Bill"), "orange", - "per_received,<,100|per_billed,<,100|status,!=,Closed"]; + return [ + __("To Receive and Bill"), + "orange", + "per_received,<,100|per_billed,<,100|status,!=,Closed", + ]; } else { - return [__("To Receive"), "orange", - "per_received,<,100|per_billed,=,100|status,!=,Closed"]; + 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, 2) >= 100 && + flt(doc.per_billed, 2) < 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, 2) >= 100 && + flt(doc.per_billed, 2) == 100 && + doc.status !== "Closed" + ) { return [__("Completed"), "green", "per_received,=,100|per_billed,=,100|status,!=,Closed"]; } }, @@ -26,25 +44,23 @@ frappe.listview_settings['Purchase Order'] = { var method = "erpnext.buying.doctype.purchase_order.purchase_order.close_or_unclose_purchase_orders"; listview.page.add_menu_item(__("Close"), function () { - listview.call_for_selected_items(method, { "status": "Closed" }); + listview.call_for_selected_items(method, { status: "Closed" }); }); listview.page.add_menu_item(__("Reopen"), function () { - listview.call_for_selected_items(method, { "status": "Submitted" }); + listview.call_for_selected_items(method, { status: "Submitted" }); }); - - listview.page.add_action_item(__("Purchase Invoice"), ()=>{ + listview.page.add_action_item(__("Purchase Invoice"), () => { erpnext.bulk_transaction_processing.create(listview, "Purchase Order", "Purchase Invoice"); }); - listview.page.add_action_item(__("Purchase Receipt"), ()=>{ + listview.page.add_action_item(__("Purchase Receipt"), () => { erpnext.bulk_transaction_processing.create(listview, "Purchase Order", "Purchase Receipt"); }); - listview.page.add_action_item(__("Advance Payment"), ()=>{ + listview.page.add_action_item(__("Advance Payment"), () => { erpnext.bulk_transaction_processing.create(listview, "Purchase Order", "Payment Entry"); }); - - } + }, }; diff --git a/erpnext/buying/doctype/supplier/supplier.js b/erpnext/buying/doctype/supplier/supplier.js index e0e33b6848b..23f25b28876 100644 --- a/erpnext/buying/doctype/supplier/supplier.js +++ b/erpnext/buying/doctype/supplier/supplier.js @@ -3,49 +3,49 @@ frappe.ui.form.on("Supplier", { setup: function (frm) { - frm.set_query('default_price_list', { 'buying': 1 }); + frm.set_query("default_price_list", { buying: 1 }); if (frm.doc.__islocal == 1) { frm.set_value("represents_company", ""); } - frm.set_query('account', 'accounts', function (doc, cdt, cdn) { + frm.set_query("account", "accounts", function (doc, cdt, cdn) { var d = locals[cdt][cdn]; return { filters: { - 'account_type': 'Payable', - 'company': d.company, - "is_group": 0 - } - } + account_type: "Payable", + company: d.company, + is_group: 0, + }, + }; }); - frm.set_query("default_bank_account", function() { + frm.set_query("default_bank_account", function () { return { filters: { - "is_company_account":1 - } - } - }); - - frm.set_query("supplier_primary_contact", function(doc) { - return { - query: "erpnext.buying.doctype.supplier.supplier.get_supplier_primary_contact", - filters: { - "supplier": doc.name - } + is_company_account: 1, + }, }; }); - frm.set_query("supplier_primary_address", function(doc) { + frm.set_query("supplier_primary_contact", function (doc) { + return { + query: "erpnext.buying.doctype.supplier.supplier.get_supplier_primary_contact", + filters: { + supplier: doc.name, + }, + }; + }); + + frm.set_query("supplier_primary_address", function (doc) { return { filters: { - "link_doctype": "Supplier", - "link_name": doc.name - } + link_doctype: "Supplier", + link_name: doc.name, + }, }; }); }, refresh: function (frm) { - frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Supplier' } + frappe.dynamic_link = { doc: frm.doc, fieldname: "name", doctype: "Supplier" }; if (frappe.defaults.get_default("supp_master_name") != "Naming Series") { frm.toggle_display("naming_series", false); @@ -54,65 +54,94 @@ frappe.ui.form.on("Supplier", { } if (frm.doc.__islocal) { - hide_field(['address_html','contact_html']); + hide_field(["address_html", "contact_html"]); frappe.contacts.clear_address_and_contact(frm); - } - else { - unhide_field(['address_html','contact_html']); + } else { + unhide_field(["address_html", "contact_html"]); frappe.contacts.render_address_and_contact(frm); // custom buttons - frm.add_custom_button(__('Accounting Ledger'), function () { - frappe.set_route('query-report', 'General Ledger', - { party_type: 'Supplier', party: frm.doc.name, party_name: frm.doc.supplier_name }); - }, __("View")); + frm.add_custom_button( + __("Accounting Ledger"), + function () { + frappe.set_route("query-report", "General Ledger", { + party_type: "Supplier", + party: frm.doc.name, + party_name: frm.doc.supplier_name, + }); + }, + __("View") + ); - frm.add_custom_button(__('Accounts Payable'), function () { - frappe.set_route('query-report', 'Accounts Payable', { party_type: "Supplier", party: frm.doc.name }); - }, __("View")); + frm.add_custom_button( + __("Accounts Payable"), + function () { + frappe.set_route("query-report", "Accounts Payable", { + party_type: "Supplier", + party: frm.doc.name, + }); + }, + __("View") + ); - frm.add_custom_button(__('Bank Account'), function () { - erpnext.utils.make_bank_account(frm.doc.doctype, frm.doc.name); - }, __('Create')); + frm.add_custom_button( + __("Bank Account"), + function () { + erpnext.utils.make_bank_account(frm.doc.doctype, frm.doc.name); + }, + __("Create") + ); - frm.add_custom_button(__('Pricing Rule'), function () { - erpnext.utils.make_pricing_rule(frm.doc.doctype, frm.doc.name); - }, __('Create')); + frm.add_custom_button( + __("Pricing Rule"), + function () { + erpnext.utils.make_pricing_rule(frm.doc.doctype, frm.doc.name); + }, + __("Create") + ); - frm.add_custom_button(__('Get Supplier Group Details'), function () { - frm.trigger("get_supplier_group_details"); - }, __('Actions')); + frm.add_custom_button( + __("Get Supplier Group Details"), + function () { + frm.trigger("get_supplier_group_details"); + }, + __("Actions") + ); if (cint(frappe.defaults.get_default("enable_common_party_accounting"))) { - frm.add_custom_button(__('Link with Customer'), function () { - frm.trigger('show_party_link_dialog'); - }, __('Actions')); + frm.add_custom_button( + __("Link with Customer"), + function () { + frm.trigger("show_party_link_dialog"); + }, + __("Actions") + ); } // indicators erpnext.utils.set_party_dashboard_indicators(frm); } }, - get_supplier_group_details: function(frm) { + get_supplier_group_details: function (frm) { frappe.call({ method: "get_supplier_group_details", doc: frm.doc, - callback: function() { + callback: function () { frm.refresh(); - } + }, }); }, - supplier_primary_address: function(frm) { + supplier_primary_address: function (frm) { if (frm.doc.supplier_primary_address) { frappe.call({ - method: 'frappe.contacts.doctype.address.address.get_address_display', + method: "frappe.contacts.doctype.address.address.get_address_display", args: { - "address_dict": frm.doc.supplier_primary_address + address_dict: frm.doc.supplier_primary_address, }, - callback: function(r) { + callback: function (r) { frm.set_value("primary_address", r.message); - } + }, }); } if (!frm.doc.supplier_primary_address) { @@ -120,56 +149,60 @@ frappe.ui.form.on("Supplier", { } }, - supplier_primary_contact: function(frm) { + supplier_primary_contact: function (frm) { if (!frm.doc.supplier_primary_contact) { frm.set_value("mobile_no", ""); frm.set_value("email_id", ""); } }, - is_internal_supplier: function(frm) { + is_internal_supplier: function (frm) { if (frm.doc.is_internal_supplier == 1) { frm.toggle_reqd("represents_company", true); - } - else { + } else { frm.toggle_reqd("represents_company", false); } }, - show_party_link_dialog: function(frm) { + show_party_link_dialog: function (frm) { const dialog = new frappe.ui.Dialog({ - title: __('Select a Customer'), - fields: [{ - fieldtype: 'Link', label: __('Customer'), - options: 'Customer', fieldname: 'customer', reqd: 1 - }], - primary_action: function({ customer }) { + title: __("Select a Customer"), + fields: [ + { + fieldtype: "Link", + label: __("Customer"), + options: "Customer", + fieldname: "customer", + reqd: 1, + }, + ], + primary_action: function ({ customer }) { frappe.call({ - method: 'erpnext.accounts.doctype.party_link.party_link.create_party_link', + method: "erpnext.accounts.doctype.party_link.party_link.create_party_link", args: { - primary_role: 'Supplier', + primary_role: "Supplier", primary_party: frm.doc.name, - secondary_party: customer + secondary_party: customer, }, freeze: true, - callback: function() { + callback: function () { dialog.hide(); frappe.msgprint({ - message: __('Successfully linked to Customer'), - alert: true + message: __("Successfully linked to Customer"), + alert: true, }); }, - error: function() { + error: function () { dialog.hide(); frappe.msgprint({ - message: __('Linking to Customer Failed. Please try again.'), - title: __('Linking Failed'), - indicator: 'red' + message: __("Linking to Customer Failed. Please try again."), + title: __("Linking Failed"), + indicator: "red", }); - } + }, }); }, - primary_action_label: __('Create Link') + primary_action_label: __("Create Link"), }); dialog.show(); - } + }, }); diff --git a/erpnext/buying/doctype/supplier/supplier_list.js b/erpnext/buying/doctype/supplier/supplier_list.js index c776b001a5a..987c1e02038 100644 --- a/erpnext/buying/doctype/supplier/supplier_list.js +++ b/erpnext/buying/doctype/supplier/supplier_list.js @@ -1,8 +1,8 @@ -frappe.listview_settings['Supplier'] = { +frappe.listview_settings["Supplier"] = { add_fields: ["supplier_name", "supplier_group", "image", "on_hold"], - get_indicator: function(doc) { - if(cint(doc.on_hold)) { + get_indicator: function (doc) { + if (cint(doc.on_hold)) { return [__("On Hold"), "red"]; } - } + }, }; diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation_list.js b/erpnext/buying/doctype/supplier_quotation/supplier_quotation_list.js index 73685caa0b4..99fe24d8770 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation_list.js +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation_list.js @@ -1,22 +1,22 @@ -frappe.listview_settings['Supplier Quotation'] = { +frappe.listview_settings["Supplier Quotation"] = { add_fields: ["supplier", "base_grand_total", "status", "company", "currency"], - get_indicator: function(doc) { - if(doc.status==="Ordered") { + get_indicator: function (doc) { + if (doc.status === "Ordered") { return [__("Ordered"), "green", "status,=,Ordered"]; - } else if(doc.status==="Rejected") { + } else if (doc.status === "Rejected") { return [__("Lost"), "gray", "status,=,Lost"]; - } else if(doc.status==="Expired") { + } else if (doc.status === "Expired") { return [__("Expired"), "gray", "status,=,Expired"]; } }, - onload: function(listview) { - listview.page.add_action_item(__("Purchase Order"), ()=>{ + onload: function (listview) { + listview.page.add_action_item(__("Purchase Order"), () => { erpnext.bulk_transaction_processing.create(listview, "Supplier Quotation", "Purchase Order"); }); - listview.page.add_action_item(__("Purchase Invoice"), ()=>{ + listview.page.add_action_item(__("Purchase Invoice"), () => { erpnext.bulk_transaction_processing.create(listview, "Supplier Quotation", "Purchase Invoice"); }); - } + }, }; diff --git a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.js b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.js index b4cd852c32f..0ff0e862259 100644 --- a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.js +++ b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.js @@ -4,55 +4,51 @@ /* global frappe, refresh_field */ frappe.ui.form.on("Supplier Scorecard", { - setup: function(frm) { - if (frm.doc.indicator_color !== "") { - frm.set_indicator_formatter("status", function(doc) { + setup: function (frm) { + if (frm.doc.indicator_color !== "") { + frm.set_indicator_formatter("status", function (doc) { return doc.indicator_color.toLowerCase(); }); } }, - onload: function(frm) { - if (frm.doc.__unsaved == 1) { + onload: function (frm) { + if (frm.doc.__unsaved == 1) { loadAllStandings(frm); } }, - load_criteria: function(frm) { + load_criteria: function (frm) { frappe.call({ method: "erpnext.buying.doctype.supplier_scorecard_criteria.supplier_scorecard_criteria.get_criteria_list", - callback: function(r) { - frm.set_value('criteria', []); - for (var i = 0; i < r.message.length; i++) - { + callback: function (r) { + frm.set_value("criteria", []); + for (var i = 0; i < r.message.length; i++) { var row = frm.add_child("criteria"); row.criteria_name = r.message[i].name; frm.script_manager.trigger("criteria_name", row.doctype, row.name); } refresh_field("criteria"); - } + }, }); - } - + }, }); frappe.ui.form.on("Supplier Scorecard Scoring Standing", { - - standing_name: function(frm, cdt, cdn) { + standing_name: function (frm, cdt, cdn) { var d = frappe.get_doc(cdt, cdn); if (d.standing_name) { return frm.call({ method: "erpnext.buying.doctype.supplier_scorecard_standing.supplier_scorecard_standing.get_scoring_standing", child: d, args: { - standing_name: d.standing_name - } + standing_name: d.standing_name, + }, }); } - } + }, }); frappe.ui.form.on("Supplier Scorecard Scoring Criteria", { - - criteria_name: function(frm, cdt, cdn) { + criteria_name: function (frm, cdt, cdn) { var d = frappe.get_doc(cdt, cdn); if (d.criteria_name) { return frm.call({ @@ -60,36 +56,34 @@ frappe.ui.form.on("Supplier Scorecard Scoring Criteria", { args: { fieldname: "weight", doctype: "Supplier Scorecard Criteria", - filters: {name: d.criteria_name} + filters: { name: d.criteria_name }, }, - callback: function(r) { - if(r.message){ + callback: function (r) { + if (r.message) { d.weight = r.message.weight; - frm.refresh_field('criteria', 'weight'); + frm.refresh_field("criteria", "weight"); } - } + }, }); } - } + }, }); -var loadAllStandings = function(frm) { +var loadAllStandings = function (frm) { frappe.call({ method: "erpnext.buying.doctype.supplier_scorecard_standing.supplier_scorecard_standing.get_standings_list", - callback: function(r) { - for (var j = 0; j < frm.doc.standings.length; j++) - { - if(!frm.doc.standings[j].hasOwnProperty("standing_name")) { + callback: function (r) { + for (var j = 0; j < frm.doc.standings.length; j++) { + if (!frm.doc.standings[j].hasOwnProperty("standing_name")) { frm.get_field("standings").grid.grid_rows[j].remove(); } } - for (var i = 0; i < r.message.length; i++) - { + for (var i = 0; i < r.message.length; i++) { var new_row = frm.add_child("standings"); new_row.standing_name = r.message[i].name; frm.script_manager.trigger("standing_name", new_row.doctype, new_row.name); } refresh_field("standings"); - } + }, }); }; diff --git a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_list.js b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_list.js index dc5474e3b43..f6af3d456d4 100644 --- a/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_list.js +++ b/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard_list.js @@ -5,13 +5,11 @@ frappe.listview_settings["Supplier Scorecard"] = { add_fields: ["indicator_color", "status"], - get_indicator: function(doc) { - + get_indicator: function (doc) { if (doc.indicator_color) { return [__(doc.status), doc.indicator_color.toLowerCase(), "status,=," + doc.status]; } else { return [__("Unknown"), "gray", "status,=,''"]; } }, - }; diff --git a/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.js b/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.js index 9f8a2dee81d..4bd05676f8c 100644 --- a/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.js +++ b/erpnext/buying/doctype/supplier_scorecard_criteria/supplier_scorecard_criteria.js @@ -4,5 +4,5 @@ /* global frappe */ frappe.ui.form.on("Supplier Scorecard Criteria", { - refresh: function() {} + refresh: function () {}, }); diff --git a/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.js b/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.js index a4cdeb31957..267822f8ff4 100644 --- a/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.js +++ b/erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.js @@ -3,14 +3,13 @@ /* global frappe */ - frappe.ui.form.on("Supplier Scorecard Period", { - onload: function(frm) { + onload: function (frm) { let criteria_grid = frm.get_field("criteria").grid; criteria_grid.toggle_enable("criteria_name", false); criteria_grid.toggle_enable("weight", false); criteria_grid.toggle_display("max_score", true); criteria_grid.toggle_display("formula", true); criteria_grid.toggle_display("score", true); - } + }, }); diff --git a/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.js b/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.js index dccfcc34bb9..025b23a812f 100644 --- a/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.js +++ b/erpnext/buying/doctype/supplier_scorecard_standing/supplier_scorecard_standing.js @@ -4,7 +4,5 @@ /* global frappe */ frappe.ui.form.on("Supplier Scorecard Standing", { - refresh: function() { - - } + refresh: function () {}, }); diff --git a/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.js b/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.js index 2d74fdd190a..67af0edd27f 100644 --- a/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.js +++ b/erpnext/buying/doctype/supplier_scorecard_variable/supplier_scorecard_variable.js @@ -4,7 +4,5 @@ /* global frappe */ frappe.ui.form.on("Supplier Scorecard Variable", { - refresh: function() { - - } + refresh: function () {}, }); diff --git a/erpnext/buying/report/procurement_tracker/procurement_tracker.js b/erpnext/buying/report/procurement_tracker/procurement_tracker.js index 283d56c9469..7127c60b286 100644 --- a/erpnext/buying/report/procurement_tracker/procurement_tracker.js +++ b/erpnext/buying/report/procurement_tracker/procurement_tracker.js @@ -3,7 +3,7 @@ /* eslint-disable */ frappe.query_reports["Procurement Tracker"] = { - "filters": [ + filters: [ { fieldname: "company", label: __("Company"), @@ -30,10 +30,10 @@ frappe.query_reports["Procurement Tracker"] = { default: frappe.defaults.get_user_default("year_start_date"), }, { - fieldname:"to_date", + fieldname: "to_date", label: __("To Date"), fieldtype: "Date", default: frappe.defaults.get_user_default("year_end_date"), }, - ] -} + ], +}; diff --git a/erpnext/buying/report/purchase_analytics/purchase_analytics.js b/erpnext/buying/report/purchase_analytics/purchase_analytics.js index a884f06d2c2..91958d520a7 100644 --- a/erpnext/buying/report/purchase_analytics/purchase_analytics.js +++ b/erpnext/buying/report/purchase_analytics/purchase_analytics.js @@ -3,47 +3,47 @@ /* eslint-disable */ frappe.query_reports["Purchase Analytics"] = { - "filters": [ + filters: [ { fieldname: "tree_type", label: __("Tree Type"), fieldtype: "Select", - options: ["Supplier Group","Supplier","Item Group","Item"], + options: ["Supplier Group", "Supplier", "Item Group", "Item"], default: "Supplier", - reqd: 1 + reqd: 1, }, { fieldname: "doc_type", label: __("based_on"), fieldtype: "Select", - options: ["Purchase Order","Purchase Receipt","Purchase Invoice"], + options: ["Purchase Order", "Purchase Receipt", "Purchase Invoice"], default: "Purchase Invoice", - reqd: 1 + reqd: 1, }, { fieldname: "value_quantity", label: __("Value Or Qty"), fieldtype: "Select", options: [ - { "value": "Value", "label": __("Value") }, - { "value": "Quantity", "label": __("Quantity") }, + { value: "Value", label: __("Value") }, + { value: "Quantity", label: __("Quantity") }, ], default: "Value", - reqd: 1 + reqd: 1, }, { fieldname: "from_date", label: __("From Date"), fieldtype: "Date", default: frappe.defaults.get_user_default("year_start_date"), - reqd: 1 + reqd: 1, }, { - fieldname:"to_date", + fieldname: "to_date", label: __("To Date"), fieldtype: "Date", default: frappe.defaults.get_user_default("year_end_date"), - reqd: 1 + reqd: 1, }, { fieldname: "company", @@ -51,22 +51,21 @@ frappe.query_reports["Purchase Analytics"] = { fieldtype: "Link", options: "Company", default: frappe.defaults.get_user_default("Company"), - reqd: 1 + reqd: 1, }, { fieldname: "range", label: __("Range"), fieldtype: "Select", options: [ - { "value": "Weekly", "label": __("Weekly") }, - { "value": "Monthly", "label": __("Monthly") }, - { "value": "Quarterly", "label": __("Quarterly") }, - { "value": "Yearly", "label": __("Yearly") } + { value: "Weekly", label: __("Weekly") }, + { value: "Monthly", label: __("Monthly") }, + { value: "Quarterly", label: __("Quarterly") }, + { value: "Yearly", label: __("Yearly") }, ], default: "Monthly", - reqd: 1 - } - + reqd: 1, + }, ], get_datatable_options(options) { return Object.assign(options, { @@ -75,9 +74,7 @@ frappe.query_reports["Purchase Analytics"] = { onCheckRow: function (data) { if (!data) return; - const data_doctype = $( - data[2].html - )[0].attributes.getNamedItem("data-doctype").value; + const data_doctype = $(data[2].html)[0].attributes.getNamedItem("data-doctype").value; const tree_type = frappe.query_report.filters[0].value; if (data_doctype != tree_type) return; @@ -85,23 +82,17 @@ frappe.query_reports["Purchase Analytics"] = { length = data.length; if (tree_type == "Supplier") { - row_values = data - .slice(4, length - 1) - .map(function (column) { - return column.content; - }); + row_values = data.slice(4, length - 1).map(function (column) { + return column.content; + }); } else if (tree_type == "Item") { - row_values = data - .slice(5, length - 1) - .map(function (column) { - return column.content; - }); + row_values = data.slice(5, length - 1).map(function (column) { + return column.content; + }); } else { - row_values = data - .slice(3, length - 1) - .map(function (column) { - return column.content; - }); + row_values = data.slice(3, length - 1).map(function (column) { + return column.content; + }); } entry = { @@ -112,13 +103,13 @@ frappe.query_reports["Purchase Analytics"] = { let raw_data = frappe.query_report.chart.data; let new_datasets = raw_data.datasets; - let element_found = new_datasets.some((element, index, array)=>{ - if(element.name == row_name){ - array.splice(index, 1) - return true + let element_found = new_datasets.some((element, index, array) => { + if (element.name == row_name) { + array.splice(index, 1); + return true; } - return false - }) + return false; + }); if (!element_found) { new_datasets.push(entry); @@ -127,12 +118,14 @@ frappe.query_reports["Purchase Analytics"] = { labels: raw_data.labels, datasets: new_datasets, }; - const new_options = Object.assign({}, frappe.query_report.chart_options, {data: new_data}); + const new_options = Object.assign({}, frappe.query_report.chart_options, { + data: new_data, + }); frappe.query_report.render_chart(new_options); frappe.query_report.raw_chart_data = new_data; }, }, }); - } -} + }, +}; diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js index 721e54e46f5..ad2a9e88737 100644 --- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js @@ -3,78 +3,78 @@ /* eslint-disable */ frappe.query_reports["Purchase Order Analysis"] = { - "filters": [ + filters: [ { - "fieldname": "company", - "label": __("Company"), - "fieldtype": "Link", - "width": "80", - "options": "Company", - "reqd": 1, - "default": frappe.defaults.get_default("company") + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + width: "80", + options: "Company", + reqd: 1, + default: frappe.defaults.get_default("company"), }, { - "fieldname":"from_date", - "label": __("From Date"), - "fieldtype": "Date", - "width": "80", - "reqd": 1, - "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + width: "80", + reqd: 1, + default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), }, { - "fieldname":"to_date", - "label": __("To Date"), - "fieldtype": "Date", - "width": "80", - "reqd": 1, - "default": frappe.datetime.get_today() + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + width: "80", + reqd: 1, + default: frappe.datetime.get_today(), }, { - "fieldname":"project", - "label": __("Project"), - "fieldtype": "Link", - "width": "80", - "options": "Project" + fieldname: "project", + label: __("Project"), + fieldtype: "Link", + width: "80", + options: "Project", }, { - "fieldname": "name", - "label": __("Purchase Order"), - "fieldtype": "Link", - "width": "80", - "options": "Purchase Order", - "get_query": () =>{ + fieldname: "name", + label: __("Purchase Order"), + fieldtype: "Link", + width: "80", + options: "Purchase Order", + get_query: () => { return { - filters: { "docstatus": 1 } - } - } + filters: { docstatus: 1 }, + }; + }, }, { - "fieldname": "status", - "label": __("Status"), - "fieldtype": "MultiSelectList", - "width": "80", - get_data: function(txt) { - let status = ["To Bill", "To Receive", "To Receive and Bill", "Completed"] - let options = [] - for (let option of status){ + fieldname: "status", + label: __("Status"), + fieldtype: "MultiSelectList", + width: "80", + get_data: function (txt) { + let status = ["To Bill", "To Receive", "To Receive and Bill", "Completed"]; + let options = []; + for (let option of status) { options.push({ - "value": option, - "label": __(option), - "description": "" - }) + value: option, + label: __(option), + description: "", + }); } - return options - } + return options; + }, }, { - "fieldname": "group_by_po", - "label": __("Group by Purchase Order"), - "fieldtype": "Check", - "default": 0 - } + fieldname: "group_by_po", + label: __("Group by Purchase Order"), + fieldtype: "Check", + default: 0, + }, ], - "formatter": function (value, row, column, data, default_formatter) { + formatter: function (value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); let format_fields = ["received_qty", "billed_amount"]; @@ -82,5 +82,5 @@ frappe.query_reports["Purchase Order Analysis"] = { value = "" + value + ""; } return value; - } + }, }; 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 90919dcc6a3..366fff191a0 100644 --- a/erpnext/buying/report/purchase_order_trends/purchase_order_trends.js +++ b/erpnext/buying/report/purchase_order_trends/purchase_order_trends.js @@ -1,8 +1,8 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.require("assets/erpnext/js/purchase_trends_filters.js", function() { +frappe.require("assets/erpnext/js/purchase_trends_filters.js", function () { frappe.query_reports["Purchase Order Trends"] = { - filters: erpnext.get_purchase_trends_filters() - } + filters: erpnext.get_purchase_trends_filters(), + }; }); diff --git a/erpnext/buying/report/requested_items_to_order_and_receive/requested_items_to_order_and_receive.js b/erpnext/buying/report/requested_items_to_order_and_receive/requested_items_to_order_and_receive.js index d727584d0aa..5dd309bc915 100644 --- a/erpnext/buying/report/requested_items_to_order_and_receive/requested_items_to_order_and_receive.js +++ b/erpnext/buying/report/requested_items_to_order_and_receive/requested_items_to_order_and_receive.js @@ -3,74 +3,74 @@ /* eslint-disable */ frappe.query_reports["Requested Items to Order and Receive"] = { - "filters": [ + filters: [ { - "fieldname": "company", - "label": __("Company"), - "fieldtype": "Link", - "width": "80", - "options": "Company", - "reqd": 1, - "default": frappe.defaults.get_default("company") + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + width: "80", + options: "Company", + reqd: 1, + default: frappe.defaults.get_default("company"), }, { - "fieldname":"from_date", - "label": __("From Date"), - "fieldtype": "Date", - "width": "80", - "reqd": 1, - "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + width: "80", + reqd: 1, + default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), }, { - "fieldname":"to_date", - "label": __("To Date"), - "fieldtype": "Date", - "width": "80", - "reqd": 1, - "default": frappe.datetime.get_today() + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + width: "80", + reqd: 1, + default: frappe.datetime.get_today(), }, { - "fieldname": "material_request", - "label": __("Material Request"), - "fieldtype": "Link", - "width": "80", - "options": "Material Request", - "get_query": () => { + fieldname: "material_request", + label: __("Material Request"), + fieldtype: "Link", + width: "80", + options: "Material Request", + get_query: () => { return { filters: { - "docstatus": 1, - "material_request_type": "Purchase", - "per_received": ["<", 100] - } - } - } + docstatus: 1, + material_request_type: "Purchase", + per_received: ["<", 100], + }, + }; + }, }, { - "fieldname": "item_code", - "label": __("Item"), - "fieldtype": "Link", - "width": "80", - "options": "Item", - "get_query": () => { + fieldname: "item_code", + label: __("Item"), + fieldtype: "Link", + width: "80", + options: "Item", + get_query: () => { return { - query: "erpnext.controllers.queries.item_query" - } - } + query: "erpnext.controllers.queries.item_query", + }; + }, }, { - "fieldname": "group_by_mr", - "label": __("Group by Material Request"), - "fieldtype": "Check", - "default": 0 - } + fieldname: "group_by_mr", + label: __("Group by Material Request"), + fieldtype: "Check", + default: 0, + }, ], - "formatter": function (value, row, column, data, default_formatter) { + formatter: function (value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); if (column.fieldname == "ordered_qty" && data && data.ordered_qty > 0) { value = "" + value + ""; } return value; - } + }, }; diff --git a/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.js b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.js index 075671f4ec6..ab413925d5f 100644 --- a/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.js +++ b/erpnext/buying/report/subcontract_order_summary/subcontract_order_summary.js @@ -3,40 +3,40 @@ /* eslint-disable */ frappe.query_reports["Subcontract Order Summary"] = { - "filters": [ + filters: [ { label: __("Company"), fieldname: "company", fieldtype: "Link", options: "Company", default: frappe.defaults.get_user_default("Company"), - reqd: 1 + reqd: 1, }, { label: __("From Date"), fieldname: "from_date", fieldtype: "Date", default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), - reqd: 1 + reqd: 1, }, { label: __("To Date"), fieldname: "to_date", fieldtype: "Date", default: frappe.datetime.get_today(), - reqd: 1 + reqd: 1, }, { label: __("Order Type"), fieldname: "order_type", fieldtype: "Select", options: ["Purchase Order", "Subcontracting Order"], - default: "Subcontracting Order" + default: "Subcontracting Order", }, { label: __("Subcontract Order"), fieldname: "name", - fieldtype: "Data" - } - ] -}; \ No newline at end of file + fieldtype: "Data", + }, + ], +}; diff --git a/erpnext/buying/report/subcontracted_item_to_be_received/subcontracted_item_to_be_received.js b/erpnext/buying/report/subcontracted_item_to_be_received/subcontracted_item_to_be_received.js index 9db769d59bf..44fbb3408ff 100644 --- a/erpnext/buying/report/subcontracted_item_to_be_received/subcontracted_item_to_be_received.js +++ b/erpnext/buying/report/subcontracted_item_to_be_received/subcontracted_item_to_be_received.js @@ -3,34 +3,34 @@ /* eslint-disable */ frappe.query_reports["Subcontracted Item To Be Received"] = { - "filters": [ + filters: [ { label: __("Order Type"), fieldname: "order_type", fieldtype: "Select", options: ["Purchase Order", "Subcontracting Order"], - default: "Subcontracting Order" + default: "Subcontracting Order", }, { fieldname: "supplier", label: __("Supplier"), fieldtype: "Link", options: "Supplier", - reqd: 1 + reqd: 1, }, { - fieldname:"from_date", + fieldname: "from_date", label: __("From Date"), fieldtype: "Date", default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), - reqd: 1 + reqd: 1, }, { - fieldname:"to_date", + fieldname: "to_date", label: __("To Date"), fieldtype: "Date", default: frappe.datetime.get_today(), - reqd: 1 + reqd: 1, }, - ] + ], }; diff --git a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.js b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.js index 7e5338f353b..da19b5bbd36 100644 --- a/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.js +++ b/erpnext/buying/report/subcontracted_raw_materials_to_be_transferred/subcontracted_raw_materials_to_be_transferred.js @@ -3,34 +3,34 @@ /* eslint-disable */ frappe.query_reports["Subcontracted Raw Materials To Be Transferred"] = { - "filters": [ + filters: [ { label: __("Order Type"), fieldname: "order_type", fieldtype: "Select", options: ["Purchase Order", "Subcontracting Order"], - default: "Subcontracting Order" + default: "Subcontracting Order", }, { fieldname: "supplier", label: __("Supplier"), fieldtype: "Link", options: "Supplier", - reqd: 1 + reqd: 1, }, { - fieldname:"from_date", + fieldname: "from_date", label: __("From Date"), fieldtype: "Date", default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), - reqd: 1 + reqd: 1, }, { - fieldname:"to_date", + fieldname: "to_date", label: __("To Date"), fieldtype: "Date", default: frappe.datetime.get_today(), - reqd: 1 + reqd: 1, }, - ] -} + ], +}; diff --git a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js index 579c0a65ad9..c109abd8146 100644 --- a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js +++ b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js @@ -9,23 +9,23 @@ frappe.query_reports["Supplier Quotation Comparison"] = { options: "Company", fieldname: "company", default: frappe.defaults.get_user_default("Company"), - "reqd": 1 + reqd: 1, }, { - "fieldname":"from_date", - "label": __("From Date"), - "fieldtype": "Date", - "width": "80", - "reqd": 1, - "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + width: "80", + reqd: 1, + default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), }, { - "fieldname":"to_date", - "label": __("To Date"), - "fieldtype": "Date", - "width": "80", - "reqd": 1, - "default": frappe.datetime.get_today() + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + width: "80", + reqd: 1, + default: frappe.datetime.get_today(), }, { default: "", @@ -34,34 +34,34 @@ frappe.query_reports["Supplier Quotation Comparison"] = { fieldname: "item_code", fieldtype: "Link", get_query: () => { - let quote = frappe.query_report.get_filter_value('supplier_quotation'); + let quote = frappe.query_report.get_filter_value("supplier_quotation"); if (quote != "") { return { query: "erpnext.stock.doctype.quality_inspection.quality_inspection.item_query", filters: { - "from": "Supplier Quotation Item", - "parent": quote - } - } + from: "Supplier Quotation Item", + parent: quote, + }, + }; } - } + }, }, { fieldname: "supplier", label: __("Supplier"), fieldtype: "MultiSelectList", - get_data: function(txt) { - return frappe.db.get_link_options('Supplier', txt); - } + get_data: function (txt) { + return frappe.db.get_link_options("Supplier", txt); + }, }, { fieldtype: "MultiSelectList", label: __("Supplier Quotation"), fieldname: "supplier_quotation", default: "", - get_data: function(txt) { - return frappe.db.get_link_options('Supplier Quotation', txt, {'docstatus': ["<", 2]}); - } + get_data: function (txt) { + return frappe.db.get_link_options("Supplier Quotation", txt, { docstatus: ["<", 2] }); + }, }, { fieldtype: "Link", @@ -70,37 +70,36 @@ frappe.query_reports["Supplier Quotation Comparison"] = { fieldname: "request_for_quotation", default: "", get_query: () => { - return { filters: { "docstatus": ["<", 2] } } - } + return { filters: { docstatus: ["<", 2] } }; + }, }, { - "fieldname":"group_by", - "label": __("Group by"), - "fieldtype": "Select", - "options": [__("Group by Supplier"), __("Group by Item")], - "default": __("Group by Supplier") + fieldname: "group_by", + label: __("Group by"), + fieldtype: "Select", + options: [__("Group by Supplier"), __("Group by Item")], + default: __("Group by Supplier"), }, { fieldtype: "Check", label: __("Include Expired"), fieldname: "include_expired", - default: 0 - } + default: 0, + }, ], formatter: (value, row, column, data, default_formatter) => { value = default_formatter(value, row, column, data); - if(column.fieldname === "valid_till" && data.valid_till){ - if(frappe.datetime.get_diff(data.valid_till, frappe.datetime.nowdate()) <= 1){ + if (column.fieldname === "valid_till" && data.valid_till) { + if (frappe.datetime.get_diff(data.valid_till, frappe.datetime.nowdate()) <= 1) { value = `
      ${value}
      `; - } - else if (frappe.datetime.get_diff(data.valid_till, frappe.datetime.nowdate()) <= 7){ + } else if (frappe.datetime.get_diff(data.valid_till, frappe.datetime.nowdate()) <= 7) { value = `
      ${value}
      `; } } - if(column.fieldname === "price_per_unit" && data.price_per_unit && data.min && data.min === 1){ + if (column.fieldname === "price_per_unit" && data.price_per_unit && data.min && data.min === 1) { value = `
      ${value}
      `; } return value; @@ -108,48 +107,53 @@ frappe.query_reports["Supplier Quotation Comparison"] = { onload: (report) => { // Create a button for setting the default supplier - report.page.add_inner_button(__("Select Default Supplier"), () => { - let reporter = frappe.query_reports["Supplier Quotation Comparison"]; - - //Always make a new one so that the latest values get updated - reporter.make_default_supplier_dialog(report); - }, __("Tools")); + report.page.add_inner_button( + __("Select Default Supplier"), + () => { + let reporter = frappe.query_reports["Supplier Quotation Comparison"]; + //Always make a new one so that the latest values get updated + reporter.make_default_supplier_dialog(report); + }, + __("Tools") + ); }, make_default_supplier_dialog: (report) => { // Get the name of the item to change - if(!report.data) return; + if (!report.data) return; let filters = report.get_values(); let item_code = filters.item_code; // Get a list of the suppliers (with a blank as well) for the user to select - let suppliers = $.map(report.data, (row, idx)=>{ return row.supplier_name }) + let suppliers = $.map(report.data, (row, idx) => { + return row.supplier_name; + }); // Create a dialog window for the user to pick their supplier let dialog = new frappe.ui.Dialog({ - title: __('Select Default Supplier'), + title: __("Select Default Supplier"), fields: [ { reqd: 1, - label: 'Supplier', - fieldtype: 'Link', - options: 'Supplier', - fieldname: 'supplier', + label: "Supplier", + fieldtype: "Link", + options: "Supplier", + fieldname: "supplier", get_query: () => { return { filters: { - 'name': ['in', suppliers] - } - } - } - } - ] + name: ["in", suppliers], + }, + }; + }, + }, + ], }); dialog.set_primary_action(__("Set Default Supplier"), () => { let values = dialog.get_values(); - if(values) { + if (values) { // Set the default_supplier field of the appropriate Item to the selected supplier frappe.call({ method: "frappe.client.set_value", @@ -163,10 +167,10 @@ frappe.query_reports["Supplier Quotation Comparison"] = { callback: (r) => { frappe.msgprint(__("Successfully Set Supplier")); dialog.hide(); - } + }, }); } }); dialog.show(); - } -} + }, +}; diff --git a/erpnext/crm/doctype/appointment/appointment.js b/erpnext/crm/doctype/appointment/appointment.js index ca38121b1cf..54aa9a6b716 100644 --- a/erpnext/crm/doctype/appointment/appointment.js +++ b/erpnext/crm/doctype/appointment/appointment.js @@ -1,26 +1,26 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Appointment', { - refresh: function(frm) { - if(frm.doc.lead){ - frm.add_custom_button(frm.doc.lead,()=>{ +frappe.ui.form.on("Appointment", { + refresh: function (frm) { + if (frm.doc.lead) { + frm.add_custom_button(frm.doc.lead, () => { frappe.set_route("Form", "Lead", frm.doc.lead); }); } - if(frm.doc.calendar_event){ - frm.add_custom_button(__(frm.doc.calendar_event),()=>{ + if (frm.doc.calendar_event) { + frm.add_custom_button(__(frm.doc.calendar_event), () => { frappe.set_route("Form", "Event", frm.doc.calendar_event); }); } }, - onload: function(frm){ - frm.set_query("appointment_with", function(){ + onload: function (frm) { + frm.set_query("appointment_with", function () { return { - filters : { - "name": ["in", ["Customer", "Lead"]] - } + filters: { + name: ["in", ["Customer", "Lead"]], + }, }; }); - } + }, }); diff --git a/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.js b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.js index 0c64eb8e822..255e665a4d2 100644 --- a/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.js +++ b/erpnext/crm/doctype/appointment_booking_settings/appointment_booking_settings.js @@ -1,10 +1,14 @@ -frappe.ui.form.on('Appointment Booking Settings', 'validate',check_times); +frappe.ui.form.on("Appointment Booking Settings", "validate", check_times); function check_times(frm) { $.each(frm.doc.availability_of_slots || [], function (i, d) { - let from_time = Date.parse('01/01/2019 ' + d.from_time); - let to_time = Date.parse('01/01/2019 ' + d.to_time); + let from_time = Date.parse("01/01/2019 " + d.from_time); + let to_time = Date.parse("01/01/2019 " + d.to_time); if (from_time > to_time) { - frappe.throw(__('In row {0} of Appointment Booking Slots: "To Time" must be later than "From Time".', [i + 1])); + frappe.throw( + __('In row {0} of Appointment Booking Slots: "To Time" must be later than "From Time".', [ + i + 1, + ]) + ); } }); } diff --git a/erpnext/crm/doctype/campaign/campaign.js b/erpnext/crm/doctype/campaign/campaign.js index cac45c682cb..9e4a0a95362 100644 --- a/erpnext/crm/doctype/campaign/campaign.js +++ b/erpnext/crm/doctype/campaign/campaign.js @@ -1,17 +1,25 @@ // Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Campaign', { - refresh: function(frm) { +frappe.ui.form.on("Campaign", { + refresh: function (frm) { erpnext.toggle_naming_series(); if (frm.is_new()) { - frm.toggle_display("naming_series", frappe.boot.sysdefaults.campaign_naming_by=="Naming Series"); + frm.toggle_display( + "naming_series", + frappe.boot.sysdefaults.campaign_naming_by == "Naming Series" + ); } else { - cur_frm.add_custom_button(__("View Leads"), function() { - frappe.route_options = {"source": "Campaign", "campaign_name": frm.doc.name}; - frappe.set_route("List", "Lead"); - }, "fa fa-list", true); + cur_frm.add_custom_button( + __("View Leads"), + function () { + frappe.route_options = { source: "Campaign", campaign_name: frm.doc.name }; + frappe.set_route("List", "Lead"); + }, + "fa fa-list", + true + ); } - } + }, }); diff --git a/erpnext/crm/doctype/competitor/competitor.js b/erpnext/crm/doctype/competitor/competitor.js index a5b617dc74c..abd59346b8a 100644 --- a/erpnext/crm/doctype/competitor/competitor.js +++ b/erpnext/crm/doctype/competitor/competitor.js @@ -1,8 +1,7 @@ // Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Competitor', { +frappe.ui.form.on("Competitor", { // refresh: function(frm) { - // } }); diff --git a/erpnext/crm/doctype/contract/contract.js b/erpnext/crm/doctype/contract/contract.js index 7848de7a727..8d44c22db28 100644 --- a/erpnext/crm/doctype/contract/contract.js +++ b/erpnext/crm/doctype/contract/contract.js @@ -5,12 +5,12 @@ frappe.ui.form.on("Contract", { contract_template: function (frm) { if (frm.doc.contract_template) { frappe.call({ - method: 'erpnext.crm.doctype.contract_template.contract_template.get_contract_template', + method: "erpnext.crm.doctype.contract_template.contract_template.get_contract_template", args: { template_name: frm.doc.contract_template, - doc: frm.doc + doc: frm.doc, }, - callback: function(r) { + callback: function (r) { if (r && r.message) { let contract_template = r.message.contract_template; frm.set_value("contract_terms", r.message.contract_terms); @@ -18,15 +18,15 @@ frappe.ui.form.on("Contract", { if (frm.doc.requires_fulfilment) { // Populate the fulfilment terms table from a contract template, if any - r.message.contract_template.fulfilment_terms.forEach(element => { + r.message.contract_template.fulfilment_terms.forEach((element) => { let d = frm.add_child("fulfilment_terms"); d.requirement = element.requirement; }); frm.refresh_field("fulfilment_terms"); } } - } + }, }); } - } + }, }); diff --git a/erpnext/crm/doctype/contract/contract_list.js b/erpnext/crm/doctype/contract/contract_list.js index 7d5609651a1..e1f8c22f70e 100644 --- a/erpnext/crm/doctype/contract/contract_list.js +++ b/erpnext/crm/doctype/contract/contract_list.js @@ -1,4 +1,4 @@ -frappe.listview_settings['Contract'] = { +frappe.listview_settings["Contract"] = { add_fields: ["status"], get_indicator: function (doc) { if (doc.status == "Unsigned") { diff --git a/erpnext/crm/doctype/contract_fulfilment_checklist/contract_fulfilment_checklist.js b/erpnext/crm/doctype/contract_fulfilment_checklist/contract_fulfilment_checklist.js index f0525b13e22..bdc3fcc4696 100644 --- a/erpnext/crm/doctype/contract_fulfilment_checklist/contract_fulfilment_checklist.js +++ b/erpnext/crm/doctype/contract_fulfilment_checklist/contract_fulfilment_checklist.js @@ -1,8 +1,6 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Contract Fulfilment Checklist', { - refresh: function(frm) { - - } +frappe.ui.form.on("Contract Fulfilment Checklist", { + refresh: function (frm) {}, }); diff --git a/erpnext/crm/doctype/contract_template/contract_template.js b/erpnext/crm/doctype/contract_template/contract_template.js index 4f7c9a8dc97..5044bbe996c 100644 --- a/erpnext/crm/doctype/contract_template/contract_template.js +++ b/erpnext/crm/doctype/contract_template/contract_template.js @@ -1,8 +1,6 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Contract Template', { - refresh: function(frm) { - - } +frappe.ui.form.on("Contract Template", { + refresh: function (frm) {}, }); diff --git a/erpnext/crm/doctype/crm_settings/crm_settings.js b/erpnext/crm/doctype/crm_settings/crm_settings.js index c6569d8122e..0fb695a3da4 100644 --- a/erpnext/crm/doctype/crm_settings/crm_settings.js +++ b/erpnext/crm/doctype/crm_settings/crm_settings.js @@ -1,8 +1,7 @@ // Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('CRM Settings', { +frappe.ui.form.on("CRM Settings", { // refresh: function(frm) { - // } }); diff --git a/erpnext/crm/doctype/email_campaign/email_campaign.js b/erpnext/crm/doctype/email_campaign/email_campaign.js index b0e93536094..7b090a06ef5 100644 --- a/erpnext/crm/doctype/email_campaign/email_campaign.js +++ b/erpnext/crm/doctype/email_campaign/email_campaign.js @@ -1,8 +1,8 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Email Campaign', { - email_campaign_for: function(frm) { - frm.set_value('recipient', ''); - } +frappe.ui.form.on("Email Campaign", { + email_campaign_for: function (frm) { + frm.set_value("recipient", ""); + }, }); diff --git a/erpnext/crm/doctype/email_campaign/email_campaign_list.js b/erpnext/crm/doctype/email_campaign/email_campaign_list.js index adc399da0f0..3020921e2e4 100644 --- a/erpnext/crm/doctype/email_campaign/email_campaign_list.js +++ b/erpnext/crm/doctype/email_campaign/email_campaign_list.js @@ -1,11 +1,11 @@ -frappe.listview_settings['Email Campaign'] = { - get_indicator: function(doc) { +frappe.listview_settings["Email Campaign"] = { + get_indicator: function (doc) { var colors = { - "Unsubscribed": "red", - "Scheduled": "blue", + Unsubscribed: "red", + Scheduled: "blue", "In Progress": "orange", - "Completed": "green" + Completed: "green", }; return [__(doc.status), colors[doc.status], "status,=," + doc.status]; - } + }, }; diff --git a/erpnext/crm/doctype/lead/lead.js b/erpnext/crm/doctype/lead/lead.js index b98a27ede8e..8336130d50b 100644 --- a/erpnext/crm/doctype/lead/lead.js +++ b/erpnext/crm/doctype/lead/lead.js @@ -5,46 +5,50 @@ frappe.provide("erpnext"); cur_frm.email_field = "email_id"; erpnext.LeadController = class LeadController extends frappe.ui.form.Controller { - setup () { + setup() { this.frm.make_methods = { - 'Customer': this.make_customer, - 'Quotation': this.make_quotation, - 'Opportunity': this.make_opportunity + Customer: this.make_customer, + Quotation: this.make_quotation, + Opportunity: this.make_opportunity, }; // For avoiding integration issues. - this.frm.set_df_property('first_name', 'reqd', true); + this.frm.set_df_property("first_name", "reqd", true); } - onload () { + onload() { this.frm.set_query("customer", function (doc, cdt, cdn) { - return { query: "erpnext.controllers.queries.customer_query" } + return { query: "erpnext.controllers.queries.customer_query" }; }); this.frm.set_query("lead_owner", function (doc, cdt, cdn) { - return { query: "frappe.core.doctype.user.user.user_query" } + return { query: "frappe.core.doctype.user.user.user_query" }; }); } - refresh () { + refresh() { var me = this; let doc = this.frm.doc; erpnext.toggle_naming_series(); frappe.dynamic_link = { doc: doc, - fieldname: 'name', - doctype: 'Lead' + fieldname: "name", + doctype: "Lead", }; if (!this.frm.is_new() && doc.__onload && !doc.__onload.is_customer) { this.frm.add_custom_button(__("Customer"), this.make_customer, __("Create")); - this.frm.add_custom_button(__("Opportunity"), function() { - me.frm.trigger("make_opportunity"); - }, __("Create")); + this.frm.add_custom_button( + __("Opportunity"), + function () { + me.frm.trigger("make_opportunity"); + }, + __("Create") + ); this.frm.add_custom_button(__("Quotation"), this.make_quotation, __("Create")); if (!doc.__onload.linked_prospects.length) { this.frm.add_custom_button(__("Prospect"), this.make_prospect, __("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, __("Action")); } } @@ -58,50 +62,54 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller this.show_activities(); } - add_lead_to_prospect () { - frappe.prompt([ - { - fieldname: 'prospect', - label: __('Prospect'), - fieldtype: 'Link', - options: 'Prospect', - reqd: 1 - } - ], - function(data) { - frappe.call({ - method: 'erpnext.crm.doctype.lead.lead.add_lead_to_prospect', - args: { - 'lead': cur_frm.doc.name, - 'prospect': data.prospect + add_lead_to_prospect() { + frappe.prompt( + [ + { + fieldname: "prospect", + label: __("Prospect"), + fieldtype: "Link", + options: "Prospect", + reqd: 1, }, - callback: function(r) { - if (!r.exc) { - frm.reload_doc(); - } - }, - freeze: true, - freeze_message: __('Adding Lead to Prospect...') - }); - }, __('Add Lead to Prospect'), __('Add')); + ], + function (data) { + frappe.call({ + method: "erpnext.crm.doctype.lead.lead.add_lead_to_prospect", + args: { + lead: cur_frm.doc.name, + prospect: data.prospect, + }, + callback: function (r) { + if (!r.exc) { + frm.reload_doc(); + } + }, + freeze: true, + freeze_message: __("Adding Lead to Prospect..."), + }); + }, + __("Add Lead to Prospect"), + __("Add") + ); } - make_customer () { + make_customer() { frappe.model.open_mapped_doc({ method: "erpnext.crm.doctype.lead.lead.make_customer", - frm: cur_frm - }) + frm: cur_frm, + }); } - make_quotation () { + make_quotation() { frappe.model.open_mapped_doc({ method: "erpnext.crm.doctype.lead.lead.make_quotation", - frm: cur_frm - }) + frm: cur_frm, + }); } - make_prospect () { - frappe.model.with_doctype("Prospect", function() { + make_prospect() { + 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; @@ -113,14 +121,14 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller prospect.prospect_owner = cur_frm.doc.lead_owner; prospect.notes = cur_frm.doc.notes; - let leads_row = frappe.model.add_child(prospect, 'leads'); + let leads_row = frappe.model.add_child(prospect, "leads"); leads_row.lead = cur_frm.doc.name; frappe.set_route("Form", "Prospect", prospect.name); }); } - company_name () { + company_name() { if (!this.frm.doc.lead_name) { this.frm.set_value("lead_name", this.frm.doc.company_name); } @@ -149,86 +157,91 @@ 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; + 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: "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" - } + 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; + 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" - } - ); + fields.push({ + label: "Create Contact", + fieldname: "create_contact", + fieldtype: "Check", + default: "1", + }); } if (fields) { var d = new frappe.ui.Dialog({ - title: __('Create Opportunity'), + title: __("Create Opportunity"), fields: fields, - primary_action: function() { + primary_action: function () { var data = d.get_values(); frappe.call({ - method: 'create_prospect_and_contact', + method: "create_prospect_and_contact", doc: frm.doc, args: { 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", - frm: frm + frm: frm, }); } d.hide(); - } + }, }); }, - primary_action_label: __('Create') + primary_action_label: __("Create"), }); d.show(); } else { frappe.model.open_mapped_doc({ method: "erpnext.crm.doctype.lead.lead.make_opportunity", - frm: frm + frm: frm, }); } - } -}); \ No newline at end of file + }, +}); diff --git a/erpnext/crm/doctype/lead/lead_list.js b/erpnext/crm/doctype/lead/lead_list.js index dbeaf608ffc..97415251a93 100644 --- a/erpnext/crm/doctype/lead/lead_list.js +++ b/erpnext/crm/doctype/lead/lead_list.js @@ -1,28 +1,42 @@ -frappe.listview_settings['Lead'] = { - onload: function(listview) { +frappe.listview_settings["Lead"] = { + onload: function (listview) { if (frappe.boot.user.can_create.includes("Prospect")) { - listview.page.add_action_item(__("Create Prospect"), function() { - frappe.model.with_doctype("Prospect", function() { + listview.page.add_action_item(__("Create Prospect"), function () { + frappe.model.with_doctype("Prospect", function () { let prospect = frappe.model.get_new_doc("Prospect"); let leads = listview.get_checked_items(); - frappe.db.get_value("Lead", leads[0].name, ["company_name", "no_of_employees", "industry", "market_segment", "territory", "fax", "website", "lead_owner"], (r) => { - prospect.company_name = r.company_name; - prospect.no_of_employees = r.no_of_employees; - prospect.industry = r.industry; - prospect.market_segment = r.market_segment; - prospect.territory = r.territory; - prospect.fax = r.fax; - prospect.website = r.website; - prospect.prospect_owner = r.lead_owner; + frappe.db.get_value( + "Lead", + leads[0].name, + [ + "company_name", + "no_of_employees", + "industry", + "market_segment", + "territory", + "fax", + "website", + "lead_owner", + ], + (r) => { + prospect.company_name = r.company_name; + prospect.no_of_employees = r.no_of_employees; + prospect.industry = r.industry; + prospect.market_segment = r.market_segment; + prospect.territory = r.territory; + prospect.fax = r.fax; + prospect.website = r.website; + prospect.prospect_owner = r.lead_owner; - leads.forEach(function(lead) { - let lead_prospect_row = frappe.model.add_child(prospect, 'leads'); - lead_prospect_row.lead = lead.name; - }); - frappe.set_route("Form", "Prospect", prospect.name); - }); + leads.forEach(function (lead) { + let lead_prospect_row = frappe.model.add_child(prospect, "leads"); + lead_prospect_row.lead = lead.name; + }); + frappe.set_route("Form", "Prospect", prospect.name); + } + ); }); }); } - } + }, }; diff --git a/erpnext/crm/doctype/lead_source/lead_source.js b/erpnext/crm/doctype/lead_source/lead_source.js index 3cbe6492090..5efc750601a 100644 --- a/erpnext/crm/doctype/lead_source/lead_source.js +++ b/erpnext/crm/doctype/lead_source/lead_source.js @@ -1,8 +1,7 @@ // Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Lead Source', { +frappe.ui.form.on("Lead Source", { // refresh: function(frm) { - // } }); diff --git a/erpnext/crm/doctype/linkedin_settings/linkedin_settings.js b/erpnext/crm/doctype/linkedin_settings/linkedin_settings.js index 7d6b3955cde..70cf4df9519 100644 --- a/erpnext/crm/doctype/linkedin_settings/linkedin_settings.js +++ b/erpnext/crm/doctype/linkedin_settings/linkedin_settings.js @@ -1,23 +1,29 @@ // Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('LinkedIn Settings', { - onload: function(frm) { - if (frm.doc.session_status == 'Expired' && frm.doc.consumer_key && frm.doc.consumer_secret) { +frappe.ui.form.on("LinkedIn Settings", { + onload: function (frm) { + if (frm.doc.session_status == "Expired" && frm.doc.consumer_key && frm.doc.consumer_secret) { frappe.confirm( - __('Session not valid. Do you want to login?'), - function(){ + __("Session not valid. Do you want to login?"), + function () { frm.trigger("login"); }, - function(){ + function () { window.close(); } ); } - frm.dashboard.set_headline(__("For more information, {0}.", [`${__('click here')}`])); + frm.dashboard.set_headline( + __("For more information, {0}.", [ + `${__( + "click here" + )}`, + ]) + ); }, - refresh: function(frm) { - if (frm.doc.session_status=="Expired"){ + refresh: function (frm) { + if (frm.doc.session_status == "Expired") { let msg = __("Session not active. Save document to login."); frm.dashboard.set_headline_alert( `
      @@ -28,19 +34,18 @@ frappe.ui.form.on('LinkedIn Settings', { ); } - if (frm.doc.session_status=="Active"){ + if (frm.doc.session_status == "Active") { let d = new Date(frm.doc.modified); - d.setDate(d.getDate()+60); + d.setDate(d.getDate() + 60); let dn = new Date(); let days = d.getTime() - dn.getTime(); - days = Math.floor(days/(1000 * 3600 * 24)); - let msg,color; + days = Math.floor(days / (1000 * 3600 * 24)); + let msg, color; - if (days>0){ + if (days > 0) { msg = __("Your session will be expire in {0} days.", [days]); color = "green"; - } - else { + } else { msg = __("Session is expired. Save doc to login."); color = "red"; } @@ -54,21 +59,23 @@ frappe.ui.form.on('LinkedIn Settings', { ); } }, - login: function(frm) { - if (frm.doc.consumer_key && frm.doc.consumer_secret){ + login: function (frm) { + if (frm.doc.consumer_key && frm.doc.consumer_secret) { frappe.dom.freeze(); - frappe.call({ - doc: frm.doc, - method: "get_authorization_url", - callback : function(r) { - window.location.href = r.message; - } - }).fail(function() { - frappe.dom.unfreeze(); - }); + frappe + .call({ + doc: frm.doc, + method: "get_authorization_url", + callback: function (r) { + window.location.href = r.message; + }, + }) + .fail(function () { + frappe.dom.unfreeze(); + }); } }, - after_save: function(frm) { + after_save: function (frm) { frm.trigger("login"); - } + }, }); diff --git a/erpnext/crm/doctype/lost_reason_detail/lost_reason_detail.js b/erpnext/crm/doctype/lost_reason_detail/lost_reason_detail.js index 08fbdad4a5a..c3081f8a4d0 100644 --- a/erpnext/crm/doctype/lost_reason_detail/lost_reason_detail.js +++ b/erpnext/crm/doctype/lost_reason_detail/lost_reason_detail.js @@ -1,8 +1,6 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Lost Reason Detail', { - refresh: function() { - - } +frappe.ui.form.on("Lost Reason Detail", { + refresh: function () {}, }); diff --git a/erpnext/crm/doctype/market_segment/market_segment.js b/erpnext/crm/doctype/market_segment/market_segment.js index 94ffdee75d9..b089c52b5c9 100644 --- a/erpnext/crm/doctype/market_segment/market_segment.js +++ b/erpnext/crm/doctype/market_segment/market_segment.js @@ -1,8 +1,6 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Market Segment', { - refresh: function(frm) { - - } +frappe.ui.form.on("Market Segment", { + refresh: function (frm) {}, }); diff --git a/erpnext/crm/doctype/opportunity/opportunity_list.js b/erpnext/crm/doctype/opportunity/opportunity_list.js index 24b05145fd2..5028ae178c7 100644 --- a/erpnext/crm/doctype/opportunity/opportunity_list.js +++ b/erpnext/crm/doctype/opportunity/opportunity_list.js @@ -1,31 +1,31 @@ -frappe.listview_settings['Opportunity'] = { +frappe.listview_settings["Opportunity"] = { add_fields: ["customer_name", "opportunity_type", "opportunity_from", "status"], - get_indicator: function(doc) { + get_indicator: function (doc) { var indicator = [__(doc.status), frappe.utils.guess_colour(doc.status), "status,=," + doc.status]; - if(doc.status=="Quotation") { + if (doc.status == "Quotation") { indicator[1] = "green"; } return indicator; }, - onload: function(listview) { + onload: function (listview) { var method = "erpnext.crm.doctype.opportunity.opportunity.set_multiple_status"; - listview.page.add_menu_item(__("Set as Open"), function() { - listview.call_for_selected_items(method, {"status": "Open"}); + listview.page.add_menu_item(__("Set as Open"), function () { + listview.call_for_selected_items(method, { status: "Open" }); }); - listview.page.add_menu_item(__("Set as Closed"), function() { - listview.call_for_selected_items(method, {"status": "Closed"}); + listview.page.add_menu_item(__("Set as Closed"), function () { + listview.call_for_selected_items(method, { status: "Closed" }); }); - if(listview.page.fields_dict.opportunity_from) { - listview.page.fields_dict.opportunity_from.get_query = function() { + if (listview.page.fields_dict.opportunity_from) { + listview.page.fields_dict.opportunity_from.get_query = function () { return { - "filters": { - "name": ["in", ["Customer", "Lead"]], - } + filters: { + name: ["in", ["Customer", "Lead"]], + }, }; }; } - } + }, }; diff --git a/erpnext/crm/doctype/opportunity_lost_reason/opportunity_lost_reason.js b/erpnext/crm/doctype/opportunity_lost_reason/opportunity_lost_reason.js index 877dd592b22..3f08355442f 100644 --- a/erpnext/crm/doctype/opportunity_lost_reason/opportunity_lost_reason.js +++ b/erpnext/crm/doctype/opportunity_lost_reason/opportunity_lost_reason.js @@ -1,8 +1,6 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Opportunity Lost Reason', { - refresh: function() { - - } +frappe.ui.form.on("Opportunity Lost Reason", { + refresh: function () {}, }); diff --git a/erpnext/crm/doctype/opportunity_type/opportunity_type.js b/erpnext/crm/doctype/opportunity_type/opportunity_type.js index 174625e73c6..18213ad0cdf 100644 --- a/erpnext/crm/doctype/opportunity_type/opportunity_type.js +++ b/erpnext/crm/doctype/opportunity_type/opportunity_type.js @@ -1,8 +1,6 @@ // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Opportunity Type', { - refresh: function(frm) { - - } +frappe.ui.form.on("Opportunity Type", { + refresh: function (frm) {}, }); diff --git a/erpnext/crm/doctype/prospect/prospect.js b/erpnext/crm/doctype/prospect/prospect.js index 495ed291ae9..62039b33b98 100644 --- a/erpnext/crm/doctype/prospect/prospect.js +++ b/erpnext/crm/doctype/prospect/prospect.js @@ -1,25 +1,33 @@ // Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Prospect', { - refresh (frm) { +frappe.ui.form.on("Prospect", { + refresh(frm) { frappe.dynamic_link = { doc: frm.doc, fieldname: "name", doctype: frm.doctype }; if (!frm.is_new() && frappe.boot.user.can_create.includes("Customer")) { - frm.add_custom_button(__("Customer"), function() { - frappe.model.open_mapped_doc({ - method: "erpnext.crm.doctype.prospect.prospect.make_customer", - frm: frm - }); - }, __("Create")); + frm.add_custom_button( + __("Customer"), + function () { + frappe.model.open_mapped_doc({ + method: "erpnext.crm.doctype.prospect.prospect.make_customer", + frm: frm, + }); + }, + __("Create") + ); } if (!frm.is_new() && frappe.boot.user.can_create.includes("Opportunity")) { - frm.add_custom_button(__("Opportunity"), function() { - frappe.model.open_mapped_doc({ - method: "erpnext.crm.doctype.prospect.prospect.make_opportunity", - frm: frm - }); - }, __("Create")); + frm.add_custom_button( + __("Opportunity"), + function () { + frappe.model.open_mapped_doc({ + method: "erpnext.crm.doctype.prospect.prospect.make_opportunity", + frm: frm, + }); + }, + __("Create") + ); } if (!frm.is_new()) { @@ -31,7 +39,7 @@ frappe.ui.form.on('Prospect', { frm.trigger("show_activities"); }, - show_notes (frm) { + show_notes(frm) { const crm_notes = new erpnext.utils.CRMNotes({ frm: frm, notes_wrapper: $(frm.fields_dict.notes_html.wrapper), @@ -39,7 +47,7 @@ frappe.ui.form.on('Prospect', { crm_notes.refresh(); }, - show_activities (frm) { + show_activities(frm) { const crm_activities = new erpnext.utils.CRMActivities({ frm: frm, open_activities_wrapper: $(frm.fields_dict.open_activities_html.wrapper), @@ -47,6 +55,5 @@ frappe.ui.form.on('Prospect', { form_wrapper: $(frm.wrapper), }); crm_activities.refresh(); - } - + }, }); diff --git a/erpnext/crm/doctype/sales_stage/sales_stage.js b/erpnext/crm/doctype/sales_stage/sales_stage.js index 0447f783ce6..c6811665a63 100644 --- a/erpnext/crm/doctype/sales_stage/sales_stage.js +++ b/erpnext/crm/doctype/sales_stage/sales_stage.js @@ -1,8 +1,6 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Sales Stage', { - refresh: function(frm) { - - } +frappe.ui.form.on("Sales Stage", { + refresh: function (frm) {}, }); diff --git a/erpnext/crm/doctype/social_media_post/social_media_post.js b/erpnext/crm/doctype/social_media_post/social_media_post.js index d4ac0bad16c..f7d0efc83d7 100644 --- a/erpnext/crm/doctype/social_media_post/social_media_post.js +++ b/erpnext/crm/doctype/social_media_post/social_media_post.js @@ -1,7 +1,7 @@ // Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Social Media Post', { - validate: function(frm) { +frappe.ui.form.on("Social Media Post", { + validate: function (frm) { if (frm.doc.twitter === 0 && frm.doc.linkedin === 0) { frappe.throw(__("Select atleast one Social Media Platform to Share on.")); } @@ -12,111 +12,116 @@ frappe.ui.form.on('Social Media Post', { frappe.throw(__("Scheduled Time must be a future time.")); } } - frm.trigger('validate_tweet_length'); + frm.trigger("validate_tweet_length"); }, - text: function(frm) { + text: function (frm) { if (frm.doc.text) { - frm.set_df_property('text', 'description', `${frm.doc.text.length}/280`); - frm.refresh_field('text'); - frm.trigger('validate_tweet_length'); + frm.set_df_property("text", "description", `${frm.doc.text.length}/280`); + frm.refresh_field("text"); + frm.trigger("validate_tweet_length"); } }, - validate_tweet_length: function(frm) { + validate_tweet_length: function (frm) { if (frm.doc.text && frm.doc.text.length > 280) { frappe.throw(__("Tweet length Must be less than 280.")); } }, - onload: function(frm) { - frm.trigger('make_dashboard'); + onload: function (frm) { + frm.trigger("make_dashboard"); }, - make_dashboard: function(frm) { + make_dashboard: function (frm) { if (frm.doc.post_status == "Posted") { frappe.call({ doc: frm.doc, - method: 'get_post', + method: "get_post", freeze: true, callback: (r) => { if (!r.message) { return; } - let datasets = [], colors = []; + let datasets = [], + colors = []; if (r.message && r.message.twitter) { - colors.push('#1DA1F2'); + colors.push("#1DA1F2"); datasets.push({ - name: 'Twitter', - values: [r.message.twitter.favorite_count, r.message.twitter.retweet_count] + name: "Twitter", + values: [r.message.twitter.favorite_count, r.message.twitter.retweet_count], }); } if (r.message && r.message.linkedin) { - colors.push('#0077b5'); + colors.push("#0077b5"); datasets.push({ - name: 'LinkedIn', - values: [r.message.linkedin.totalShareStatistics.likeCount, r.message.linkedin.totalShareStatistics.shareCount] + name: "LinkedIn", + values: [ + r.message.linkedin.totalShareStatistics.likeCount, + r.message.linkedin.totalShareStatistics.shareCount, + ], }); } if (datasets.length) { frm.dashboard.render_graph({ data: { - labels: ['Likes', 'Retweets/Shares'], - datasets: datasets + labels: ["Likes", "Retweets/Shares"], + datasets: datasets, }, title: __("Post Metrics"), - type: 'bar', + type: "bar", height: 300, - colors: colors + colors: colors, }); } - } + }, }); } }, - refresh: function(frm) { - frm.trigger('text'); + refresh: function (frm) { + frm.trigger("text"); if (frm.doc.docstatus === 1) { - if (!['Posted', 'Deleted'].includes(frm.doc.post_status)) { - frm.trigger('add_post_btn'); + if (!["Posted", "Deleted"].includes(frm.doc.post_status)) { + frm.trigger("add_post_btn"); } - if (frm.doc.post_status !='Deleted') { - frm.add_custom_button(__('Delete Post'), function() { - frappe.confirm(__('Are you sure want to delete the Post from Social Media platforms?'), - function() { + if (frm.doc.post_status != "Deleted") { + frm.add_custom_button(__("Delete Post"), function () { + frappe.confirm( + __("Are you sure want to delete the Post from Social Media platforms?"), + function () { frappe.call({ doc: frm.doc, - method: 'delete_post', + method: "delete_post", freeze: true, callback: () => { frm.reload_doc(); - } + }, }); } ); }); } - if (frm.doc.post_status !='Deleted') { - let html=''; + if (frm.doc.post_status != "Deleted") { + let html = ""; if (frm.doc.twitter) { let color = frm.doc.twitter_post_id ? "green" : "red"; let status = frm.doc.twitter_post_id ? "Posted" : "Not Posted"; html += `
      Twitter : ${status} -
      ` ; +
      `; } if (frm.doc.linkedin) { let color = frm.doc.linkedin_post_id ? "green" : "red"; let status = frm.doc.linkedin_post_id ? "Posted" : "Not Posted"; html += `
      LinkedIn : ${status} -
      ` ; +
      `; } html = `
      ${html}
      `; frm.dashboard.set_headline_alert(html); @@ -124,16 +129,16 @@ frappe.ui.form.on('Social Media Post', { } }, - add_post_btn: function(frm) { - frm.add_custom_button(__('Post Now'), function() { + add_post_btn: function (frm) { + frm.add_custom_button(__("Post Now"), function () { frappe.call({ doc: frm.doc, - method: 'post', + method: "post", freeze: true, - callback: function() { + callback: function () { frm.reload_doc(); - } + }, }); }); - } + }, }); diff --git a/erpnext/crm/doctype/social_media_post/social_media_post_list.js b/erpnext/crm/doctype/social_media_post/social_media_post_list.js index a8c8272ad08..ab7b44c9eaa 100644 --- a/erpnext/crm/doctype/social_media_post/social_media_post_list.js +++ b/erpnext/crm/doctype/social_media_post/social_media_post_list.js @@ -1,11 +1,14 @@ -frappe.listview_settings['Social Media Post'] = { +frappe.listview_settings["Social Media Post"] = { add_fields: ["status", "post_status"], - get_indicator: function(doc) { - return [__(doc.post_status), { - "Scheduled": "orange", - "Posted": "green", - "Error": "red", - "Deleted": "red" - }[doc.post_status]]; - } -} + get_indicator: function (doc) { + return [ + __(doc.post_status), + { + Scheduled: "orange", + Posted: "green", + Error: "red", + Deleted: "red", + }[doc.post_status], + ]; + }, +}; diff --git a/erpnext/crm/doctype/twitter_settings/twitter_settings.js b/erpnext/crm/doctype/twitter_settings/twitter_settings.js index c322092d6f3..a84e01d07c4 100644 --- a/erpnext/crm/doctype/twitter_settings/twitter_settings.js +++ b/erpnext/crm/doctype/twitter_settings/twitter_settings.js @@ -1,31 +1,38 @@ // Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Twitter Settings', { - onload: function(frm) { - if (frm.doc.session_status == 'Expired' && frm.doc.consumer_key && frm.doc.consumer_secret){ +frappe.ui.form.on("Twitter Settings", { + onload: function (frm) { + if (frm.doc.session_status == "Expired" && frm.doc.consumer_key && frm.doc.consumer_secret) { frappe.confirm( - __('Session not valid, Do you want to login?'), - function(){ + __("Session not valid, Do you want to login?"), + function () { frm.trigger("login"); }, - function(){ + function () { window.close(); } ); } - frm.dashboard.set_headline(__("For more information, {0}.", [`${__('click here')}`])); + frm.dashboard.set_headline( + __("For more information, {0}.", [ + `${__( + "click here" + )}`, + ]) + ); }, - refresh: function(frm) { - let msg, color, flag=false; + refresh: function (frm) { + let msg, + color, + flag = false; if (frm.doc.session_status == "Active") { msg = __("Session Active"); - color = 'green'; + color = "green"; flag = true; - } - else if(frm.doc.consumer_key && frm.doc.consumer_secret) { + } else if (frm.doc.consumer_key && frm.doc.consumer_secret) { msg = __("Session Not Active. Save doc to login."); - color = 'red'; + color = "red"; flag = true; } @@ -39,21 +46,23 @@ frappe.ui.form.on('Twitter Settings', { ); } }, - login: function(frm) { - if (frm.doc.consumer_key && frm.doc.consumer_secret){ + login: function (frm) { + if (frm.doc.consumer_key && frm.doc.consumer_secret) { frappe.dom.freeze(); - frappe.call({ - doc: frm.doc, - method: "get_authorize_url", - callback : function(r) { - window.location.href = r.message; - } - }).fail(function() { - frappe.dom.unfreeze(); - }); + frappe + .call({ + doc: frm.doc, + method: "get_authorize_url", + callback: function (r) { + window.location.href = r.message; + }, + }) + .fail(function () { + frappe.dom.unfreeze(); + }); } }, - after_save: function(frm) { + after_save: function (frm) { frm.trigger("login"); - } + }, }); diff --git a/erpnext/crm/report/campaign_efficiency/campaign_efficiency.js b/erpnext/crm/report/campaign_efficiency/campaign_efficiency.js index f29c2c64e14..d2c52938bde 100644 --- a/erpnext/crm/report/campaign_efficiency/campaign_efficiency.js +++ b/erpnext/crm/report/campaign_efficiency/campaign_efficiency.js @@ -1,18 +1,18 @@ // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt frappe.query_reports["Campaign Efficiency"] = { - "filters": [ + filters: [ { - "fieldname": "from_date", - "label": __("From Date"), - "fieldtype": "Date", - "default": frappe.defaults.get_user_default("year_start_date"), + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.defaults.get_user_default("year_start_date"), }, { - "fieldname": "to_date", - "label": __("To Date"), - "fieldtype": "Date", - "default": frappe.defaults.get_user_default("year_end_date"), - } - ] + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.defaults.get_user_default("year_end_date"), + }, + ], }; diff --git a/erpnext/crm/report/first_response_time_for_opportunity/first_response_time_for_opportunity.js b/erpnext/crm/report/first_response_time_for_opportunity/first_response_time_for_opportunity.js index fe5707af296..b2ebe2914a9 100644 --- a/erpnext/crm/report/first_response_time_for_opportunity/first_response_time_for_opportunity.js +++ b/erpnext/crm/report/first_response_time_for_opportunity/first_response_time_for_opportunity.js @@ -3,41 +3,43 @@ /* eslint-disable */ frappe.query_reports["First Response Time for Opportunity"] = { - "filters": [ + filters: [ { - "fieldname": "from_date", - "label": __("From Date"), - "fieldtype": "Date", - "reqd": 1, - "default": frappe.datetime.add_days(frappe.datetime.nowdate(), -30) + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + reqd: 1, + default: frappe.datetime.add_days(frappe.datetime.nowdate(), -30), }, { - "fieldname": "to_date", - "label": __("To Date"), - "fieldtype": "Date", - "reqd": 1, - "default": frappe.datetime.nowdate() + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + reqd: 1, + default: frappe.datetime.nowdate(), }, ], get_chart_data: function (_columns, result) { return { data: { - labels: result.map(d => d.creation_date), - datasets: [{ - name: "First Response Time", - values: result.map(d => d.first_response_time) - }] + labels: result.map((d) => d.creation_date), + datasets: [ + { + name: "First Response Time", + values: result.map((d) => d.first_response_time), + }, + ], }, type: "line", tooltipOptions: { - formatTooltipY: d => { + formatTooltipY: (d) => { let duration_options = { hide_days: 0, - hide_seconds: 0 + hide_seconds: 0, }; return frappe.utils.get_formatted_duration(d, duration_options); - } - } - } - } + }, + }, + }; + }, }; diff --git a/erpnext/crm/report/lead_conversion_time/lead_conversion_time.js b/erpnext/crm/report/lead_conversion_time/lead_conversion_time.js index eeb8984513e..baa34aebd10 100644 --- a/erpnext/crm/report/lead_conversion_time/lead_conversion_time.js +++ b/erpnext/crm/report/lead_conversion_time/lead_conversion_time.js @@ -3,20 +3,20 @@ /* eslint-disable */ frappe.query_reports["Lead Conversion Time"] = { - "filters": [ + filters: [ { - "fieldname":"from_date", - "label": __("From Date"), - "fieldtype": "Date", - 'reqd': 1, - "default": frappe.datetime.add_days(frappe.datetime.nowdate(), -30) + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + reqd: 1, + default: frappe.datetime.add_days(frappe.datetime.nowdate(), -30), }, { - "fieldname":"to_date", - "label": __("To Date"), - "fieldtype": "Date", - 'reqd': 1, - "default":frappe.datetime.nowdate() + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + reqd: 1, + default: frappe.datetime.nowdate(), }, - ] + ], }; diff --git a/erpnext/crm/report/lead_details/lead_details.js b/erpnext/crm/report/lead_details/lead_details.js index 2f6d24224fb..f2866e17800 100644 --- a/erpnext/crm/report/lead_details/lead_details.js +++ b/erpnext/crm/report/lead_details/lead_details.js @@ -3,50 +3,50 @@ /* eslint-disable */ frappe.query_reports["Lead Details"] = { - "filters": [ + filters: [ { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company"), - "reqd": 1 + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, }, { - "fieldname":"from_date", - "label": __("From Date"), - "fieldtype": "Date", - "default": frappe.datetime.add_months(frappe.datetime.get_today(), -12), - "reqd": 1 + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.datetime.add_months(frappe.datetime.get_today(), -12), + reqd: 1, }, { - "fieldname":"to_date", - "label": __("To Date"), - "fieldtype": "Date", - "default": frappe.datetime.get_today(), - "reqd": 1 + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.datetime.get_today(), + reqd: 1, }, { - "fieldname":"status", - "label": __("Status"), - "fieldtype": "Select", + fieldname: "status", + label: __("Status"), + fieldtype: "Select", options: [ - { "value": "Lead", "label": __("Lead") }, - { "value": "Open", "label": __("Open") }, - { "value": "Replied", "label": __("Replied") }, - { "value": "Opportunity", "label": __("Opportunity") }, - { "value": "Quotation", "label": __("Quotation") }, - { "value": "Lost Quotation", "label": __("Lost Quotation") }, - { "value": "Interested", "label": __("Interested") }, - { "value": "Converted", "label": __("Converted") }, - { "value": "Do Not Contact", "label": __("Do Not Contact") }, + { value: "Lead", label: __("Lead") }, + { value: "Open", label: __("Open") }, + { value: "Replied", label: __("Replied") }, + { value: "Opportunity", label: __("Opportunity") }, + { value: "Quotation", label: __("Quotation") }, + { value: "Lost Quotation", label: __("Lost Quotation") }, + { value: "Interested", label: __("Interested") }, + { value: "Converted", label: __("Converted") }, + { value: "Do Not Contact", label: __("Do Not Contact") }, ], }, { - "fieldname":"territory", - "label": __("Territory"), - "fieldtype": "Link", - "options": "Territory", - } - ] + fieldname: "territory", + label: __("Territory"), + fieldtype: "Link", + options: "Territory", + }, + ], }; diff --git a/erpnext/crm/report/lead_owner_efficiency/lead_owner_efficiency.js b/erpnext/crm/report/lead_owner_efficiency/lead_owner_efficiency.js index bbfd6ac9ff7..c140567fed5 100644 --- a/erpnext/crm/report/lead_owner_efficiency/lead_owner_efficiency.js +++ b/erpnext/crm/report/lead_owner_efficiency/lead_owner_efficiency.js @@ -1,17 +1,18 @@ // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt - frappe.query_reports["Lead Owner Efficiency"] = { - "filters": [ - { - "fieldname": "from_date", - "label": __("From Date"), - "fieldtype": "Date", - "default": frappe.defaults.get_user_default("year_start_date"), - }, - { - "fieldname": "to_date", - "label": __("To Date"), - "fieldtype": "Date", - "default": frappe.defaults.get_user_default("year_end_date"), - } - ]}; +frappe.query_reports["Lead Owner Efficiency"] = { + filters: [ + { + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.defaults.get_user_default("year_start_date"), + }, + { + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.defaults.get_user_default("year_end_date"), + }, + ], +}; diff --git a/erpnext/crm/report/lost_opportunity/lost_opportunity.js b/erpnext/crm/report/lost_opportunity/lost_opportunity.js index 927c54df072..1e8e13bf24d 100644 --- a/erpnext/crm/report/lost_opportunity/lost_opportunity.js +++ b/erpnext/crm/report/lost_opportunity/lost_opportunity.js @@ -3,59 +3,59 @@ /* eslint-disable */ frappe.query_reports["Lost Opportunity"] = { - "filters": [ + filters: [ { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company"), - "reqd": 1 + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, }, { - "fieldname":"from_date", - "label": __("From Date"), - "fieldtype": "Date", - "default": frappe.datetime.add_months(frappe.datetime.get_today(), -12), - "reqd": 1 + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.datetime.add_months(frappe.datetime.get_today(), -12), + reqd: 1, }, { - "fieldname":"to_date", - "label": __("To Date"), - "fieldtype": "Date", - "default": frappe.datetime.get_today(), - "reqd": 1 + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.datetime.get_today(), + reqd: 1, }, { - "fieldname":"lost_reason", - "label": __("Lost Reason"), - "fieldtype": "Link", - "options": "Opportunity Lost Reason" + fieldname: "lost_reason", + label: __("Lost Reason"), + fieldtype: "Link", + options: "Opportunity Lost Reason", }, { - "fieldname":"territory", - "label": __("Territory"), - "fieldtype": "Link", - "options": "Territory" + fieldname: "territory", + label: __("Territory"), + fieldtype: "Link", + options: "Territory", }, { - "fieldname":"opportunity_from", - "label": __("Opportunity From"), - "fieldtype": "Link", - "options": "DocType", - "get_query": function() { + fieldname: "opportunity_from", + label: __("Opportunity From"), + fieldtype: "Link", + options: "DocType", + get_query: function () { return { - "filters": { - "name": ["in", ["Customer", "Lead"]], - } - } - } + filters: { + name: ["in", ["Customer", "Lead"]], + }, + }; + }, }, { - "fieldname":"party_name", - "label": __("Party"), - "fieldtype": "Dynamic Link", - "options": "opportunity_from" + fieldname: "party_name", + label: __("Party"), + fieldtype: "Dynamic Link", + options: "opportunity_from", }, - ] + ], }; diff --git a/erpnext/crm/report/opportunity_summary_by_sales_stage/opportunity_summary_by_sales_stage.js b/erpnext/crm/report/opportunity_summary_by_sales_stage/opportunity_summary_by_sales_stage.js index 7cd1710a7f2..5c449284ab6 100644 --- a/erpnext/crm/report/opportunity_summary_by_sales_stage/opportunity_summary_by_sales_stage.js +++ b/erpnext/crm/report/opportunity_summary_by_sales_stage/opportunity_summary_by_sales_stage.js @@ -3,26 +3,25 @@ /* eslint-disable */ frappe.query_reports["Opportunity Summary by Sales Stage"] = { - "filters": [ + filters: [ { fieldname: "based_on", label: __("Based On"), fieldtype: "Select", options: "Opportunity Owner\nSource\nOpportunity Type", - default: "Opportunity Owner" + default: "Opportunity Owner", }, { fieldname: "data_based_on", label: __("Data Based On"), fieldtype: "Select", options: "Number\nAmount", - default: "Number" + default: "Number", }, { fieldname: "from_date", label: __("From Date"), fieldtype: "Date", - }, { fieldname: "to_date", @@ -33,14 +32,14 @@ frappe.query_reports["Opportunity Summary by Sales Stage"] = { fieldname: "status", label: __("Status"), fieldtype: "MultiSelectList", - get_data: function() { + get_data: function () { return [ - {value: "Open", description: "Status"}, - {value: "Converted", description: "Status"}, - {value: "Quotation", description: "Status"}, - {value: "Replied", description: "Status"} - ] - } + { value: "Open", description: "Status" }, + { value: "Converted", description: "Status" }, + { value: "Quotation", description: "Status" }, + { value: "Replied", description: "Status" }, + ]; + }, }, { fieldname: "opportunity_source", @@ -59,7 +58,7 @@ frappe.query_reports["Opportunity Summary by Sales Stage"] = { label: __("Company"), fieldtype: "Link", options: "Company", - default: frappe.defaults.get_user_default("Company") - } - ] + default: frappe.defaults.get_user_default("Company"), + }, + ], }; diff --git a/erpnext/crm/report/prospects_engaged_but_not_converted/prospects_engaged_but_not_converted.js b/erpnext/crm/report/prospects_engaged_but_not_converted/prospects_engaged_but_not_converted.js index 6f37719f639..d6259414823 100644 --- a/erpnext/crm/report/prospects_engaged_but_not_converted/prospects_engaged_but_not_converted.js +++ b/erpnext/crm/report/prospects_engaged_but_not_converted/prospects_engaged_but_not_converted.js @@ -2,24 +2,24 @@ // For license information, please see license.txt frappe.query_reports["Prospects Engaged But Not Converted"] = { - "filters": [ + filters: [ { - "fieldname": "lead", - "label": __("Lead"), - "fieldtype": "Link", - "options": "Lead" + fieldname: "lead", + label: __("Lead"), + fieldtype: "Link", + options: "Lead", }, { - "fieldname": "no_of_interaction", - "label": __("Number of Interaction"), - "fieldtype": "Int", - "default": 1 + fieldname: "no_of_interaction", + label: __("Number of Interaction"), + fieldtype: "Int", + default: 1, }, { - "fieldname": "lead_age", - "label": __("Minimum Lead Age (Days)"), - "fieldtype": "Int", - "default": 60 + fieldname: "lead_age", + label: __("Minimum Lead Age (Days)"), + fieldtype: "Int", + default: 60, }, - ] -} + ], +}; diff --git a/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.js b/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.js index 1426f4b6fd2..149a97a907a 100644 --- a/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.js +++ b/erpnext/crm/report/sales_pipeline_analytics/sales_pipeline_analytics.js @@ -3,68 +3,68 @@ /* eslint-disable */ frappe.query_reports["Sales Pipeline Analytics"] = { - "filters": [ + filters: [ { fieldname: "pipeline_by", label: __("Pipeline By"), fieldtype: "Select", options: "Owner\nSales Stage", - default: "Owner" + default: "Owner", }, { fieldname: "from_date", label: __("From Date"), - fieldtype: "Date" + fieldtype: "Date", }, { fieldname: "to_date", label: __("To Date"), - fieldtype: "Date" + fieldtype: "Date", }, { fieldname: "range", label: __("Range"), fieldtype: "Select", options: "Monthly\nQuarterly", - default: "Monthly" + default: "Monthly", }, { fieldname: "assigned_to", label: __("Assigned To"), fieldtype: "Link", - options: "User" + options: "User", }, { fieldname: "status", label: __("Status"), fieldtype: "Select", - options: "Open\nQuotation\nConverted\nReplied" + options: "Open\nQuotation\nConverted\nReplied", }, { fieldname: "based_on", label: __("Based On"), fieldtype: "Select", options: "Number\nAmount", - default: "Number" + default: "Number", }, { fieldname: "company", label: __("Company"), fieldtype: "Link", options: "Company", - default: frappe.defaults.get_user_default("Company") + default: frappe.defaults.get_user_default("Company"), }, { fieldname: "opportunity_source", label: __("Opportunity Source"), fieldtype: "Link", - options: "Lead Source" + options: "Lead Source", }, { fieldname: "opportunity_type", label: __("Opportunity Type"), fieldtype: "Link", - options: "Opportunity Type" + options: "Opportunity Type", }, - ] + ], }; diff --git a/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.js b/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.js index c37fa2f6eae..d533eb82450 100644 --- a/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.js +++ b/erpnext/e_commerce/doctype/e_commerce_settings/e_commerce_settings.js @@ -2,57 +2,58 @@ // For license information, please see license.txt frappe.ui.form.on("E Commerce Settings", { - onload: function(frm) { - if(frm.doc.__onload && frm.doc.__onload.quotation_series) { + onload: function (frm) { + if (frm.doc.__onload && frm.doc.__onload.quotation_series) { frm.fields_dict.quotation_series.df.options = frm.doc.__onload.quotation_series; frm.refresh_field("quotation_series"); } - frm.set_query('payment_gateway_account', function() { - return { 'filters': { 'payment_channel': "Email" } }; + frm.set_query("payment_gateway_account", function () { + return { filters: { payment_channel: "Email" } }; }); }, - refresh: function(frm) { + refresh: function (frm) { if (frm.doc.enabled) { - frm.get_field('store_page_docs').$wrapper.removeClass('hide-control').html( - `
      ${__("Follow these steps to create a landing page for your store")}: + frm.get_field("store_page_docs") + .$wrapper.removeClass("hide-control") + .html( + `
      ${__("Follow these steps to create a landing page for your store")}: docs/store-landing-page
      ` - ); + ); } frappe.model.with_doctype("Website Item", () => { - const web_item_meta = frappe.get_meta('Website Item'); + const web_item_meta = frappe.get_meta("Website Item"); - const valid_fields = web_item_meta.fields.filter(df => - ["Link", "Table MultiSelect"].includes(df.fieldtype) && !df.hidden - ).map(df => - ({ label: df.label, value: df.fieldname }) - ); + const valid_fields = web_item_meta.fields + .filter((df) => ["Link", "Table MultiSelect"].includes(df.fieldtype) && !df.hidden) + .map((df) => ({ label: df.label, value: df.fieldname })); frm.get_field("filter_fields").grid.update_docfield_property( - 'fieldname', 'options', valid_fields + "fieldname", + "options", + valid_fields ); }); }, - enabled: function(frm) { + enabled: function (frm) { if (frm.doc.enabled === 1) { - frm.set_value('enable_variants', 1); - } - else { - frm.set_value('company', ''); - frm.set_value('price_list', ''); - frm.set_value('default_customer_group', ''); - frm.set_value('quotation_series', ''); + frm.set_value("enable_variants", 1); + } else { + frm.set_value("company", ""); + frm.set_value("price_list", ""); + frm.set_value("default_customer_group", ""); + frm.set_value("quotation_series", ""); } }, - enable_checkout: function(frm) { + enable_checkout: function (frm) { if (frm.doc.enable_checkout) { erpnext.utils.check_payments_app(); } - } + }, }); diff --git a/erpnext/e_commerce/doctype/item_review/item_review.js b/erpnext/e_commerce/doctype/item_review/item_review.js index a57c370287b..69fc1cfff4e 100644 --- a/erpnext/e_commerce/doctype/item_review/item_review.js +++ b/erpnext/e_commerce/doctype/item_review/item_review.js @@ -1,8 +1,7 @@ // Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Item Review', { +frappe.ui.form.on("Item Review", { // refresh: function(frm) { - // } }); diff --git a/erpnext/e_commerce/doctype/website_item/website_item.js b/erpnext/e_commerce/doctype/website_item/website_item.js index b6595cce8a9..74dd7b8c90f 100644 --- a/erpnext/e_commerce/doctype/website_item/website_item.js +++ b/erpnext/e_commerce/doctype/website_item/website_item.js @@ -1,37 +1,49 @@ // Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Website Item', { +frappe.ui.form.on("Website Item", { onload: (frm) => { // should never check Private frm.fields_dict["website_image"].df.is_private = 0; }, refresh: (frm) => { - frm.add_custom_button(__("Prices"), function() { - frappe.set_route("List", "Item Price", {"item_code": frm.doc.item_code}); - }, __("View")); + frm.add_custom_button( + __("Prices"), + function () { + frappe.set_route("List", "Item Price", { item_code: frm.doc.item_code }); + }, + __("View") + ); - frm.add_custom_button(__("Stock"), function() { - frappe.route_options = { - "item_code": frm.doc.item_code - }; - frappe.set_route("query-report", "Stock Balance"); - }, __("View")); + frm.add_custom_button( + __("Stock"), + function () { + frappe.route_options = { + item_code: frm.doc.item_code, + }; + frappe.set_route("query-report", "Stock Balance"); + }, + __("View") + ); - frm.add_custom_button(__("E Commerce Settings"), function() { - frappe.set_route("Form", "E Commerce Settings"); - }, __("View")); + frm.add_custom_button( + __("E Commerce Settings"), + function () { + frappe.set_route("Form", "E Commerce Settings"); + }, + __("View") + ); }, copy_from_item_group: (frm) => { return frm.call({ doc: frm.doc, - method: "copy_specification_from_item_group" + method: "copy_specification_from_item_group", }); }, set_meta_tags: (frm) => { frappe.utils.set_meta_tag(frm.doc.route); - } + }, }); diff --git a/erpnext/e_commerce/doctype/website_item/website_item_list.js b/erpnext/e_commerce/doctype/website_item/website_item_list.js index b9dd9214a38..220746c9845 100644 --- a/erpnext/e_commerce/doctype/website_item/website_item_list.js +++ b/erpnext/e_commerce/doctype/website_item/website_item_list.js @@ -1,20 +1,20 @@ -frappe.listview_settings['Website Item'] = { +frappe.listview_settings["Website Item"] = { add_fields: ["item_name", "web_item_name", "published", "website_image", "has_variants", "variant_of"], filters: [["published", "=", "1"]], - get_indicator: function(doc) { + get_indicator: function (doc) { if (doc.has_variants && doc.published) { return [__("Template"), "orange", "has_variants,=,Yes|published,=,1"]; } else if (doc.has_variants && !doc.published) { return [__("Template"), "grey", "has_variants,=,Yes|published,=,0"]; - } else if (doc.variant_of && doc.published) { + } else if (doc.variant_of && doc.published) { return [__("Variant"), "blue", "published,=,1|variant_of,=," + doc.variant_of]; - } else if (doc.variant_of && !doc.published) { + } else if (doc.variant_of && !doc.published) { return [__("Variant"), "grey", "published,=,0|variant_of,=," + doc.variant_of]; } else if (doc.published) { return [__("Published"), "green", "published,=,1"]; } else { return [__("Not Published"), "grey", "published,=,0"]; } - } -}; \ No newline at end of file + }, +}; diff --git a/erpnext/e_commerce/doctype/wishlist/wishlist.js b/erpnext/e_commerce/doctype/wishlist/wishlist.js index d96e552ecdb..54a0a2381b1 100644 --- a/erpnext/e_commerce/doctype/wishlist/wishlist.js +++ b/erpnext/e_commerce/doctype/wishlist/wishlist.js @@ -1,8 +1,7 @@ // Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Wishlist', { +frappe.ui.form.on("Wishlist", { // refresh: function(frm) { - // } }); diff --git a/erpnext/e_commerce/product_ui/grid.js b/erpnext/e_commerce/product_ui/grid.js index 20a6c30b52b..1f0d1fbf43a 100644 --- a/erpnext/e_commerce/product_ui/grid.js +++ b/erpnext/e_commerce/product_ui/grid.js @@ -20,9 +20,9 @@ erpnext.ProductGrid = class { let me = this; let html = ``; - this.items.forEach(item => { + this.items.forEach((item) => { let title = item.web_item_name || item.item_name || item.item_code || ""; - title = title.length > 90 ? title.substr(0, 90) + "..." : title; + title = title.length > 90 ? title.substr(0, 90) + "..." : title; html += `
      `; html += me.get_image_html(item, title); @@ -40,17 +40,17 @@ erpnext.ProductGrid = class { if (image) { return ` `; } else { return ` @@ -73,11 +73,10 @@ erpnext.ProductGrid = class { if (settings.enabled) { body_html += this.get_cart_indicator(item); } - } body_html += `
      `; - body_html += `
      ${ item.item_group || '' }
      `; + body_html += `
      ${item.item_group || ""}
      `; if (item.formatted_price) { body_html += this.get_price_html(item); @@ -92,9 +91,9 @@ erpnext.ProductGrid = class { get_title(item, title) { let title_html = ` - +
      - ${ title || '' } + ${title || ""}
      `; @@ -104,10 +103,10 @@ erpnext.ProductGrid = class { get_wishlist_icon(item) { let icon_class = item.wished ? "wished" : "not-wished"; return ` -
    And ${hidden_logs.length} more others
    ${__("Meta Data")}${__("Unresolve")}${__("Unresolve")}${__("Error Message")}${__("Create")}${__("Create")}
    - + @@ -115,28 +116,30 @@ erpnext.BOMComparisonTool = class BOMComparisonTool {
    ${__('Field')}${__("Field")} ${name1} ${name2}
    `; - } + }; - let value_changes = change_html(__('Values Changed'), doctype, diff.changed); + let value_changes = change_html(__("Values Changed"), doctype, diff.changed); - let row_changes_by_fieldname = group_items(diff.row_changed, change => change[0]); + let row_changes_by_fieldname = group_items(diff.row_changed, (change) => change[0]); - let table_changes = Object.keys(row_changes_by_fieldname).map(fieldname => { - let changes = row_changes_by_fieldname[fieldname]; - let df = frappe.meta.get_docfield(doctype, fieldname); - - let html = changes.map(change => { - let [fieldname,, item_code, changes] = change; + let table_changes = Object.keys(row_changes_by_fieldname) + .map((fieldname) => { + let changes = row_changes_by_fieldname[fieldname]; let df = frappe.meta.get_docfield(doctype, fieldname); - let child_doctype = df.options; - let values_changed = this.get_changed_values(child_doctype, changes); - return values_changed.map((change, i) => { - let [fieldname, value1, value2] = change; - let th = i === 0 - ? `${item_code}` - : ''; - return ` + let html = changes + .map((change) => { + let [fieldname, , item_code, changes] = change; + let df = frappe.meta.get_docfield(doctype, fieldname); + let child_doctype = df.options; + let values_changed = this.get_changed_values(child_doctype, changes); + + return values_changed + .map((change, i) => { + let [fieldname, value1, value2] = change; + let th = + i === 0 ? `${item_code}` : ""; + return ` ${th} ${frappe.meta.get_label(child_doctype, fieldname)} @@ -144,54 +147,58 @@ erpnext.BOMComparisonTool = class BOMComparisonTool { ${value2} `; - }).join(''); - }).join(''); + }) + .join(""); + }) + .join(""); - return ` -

    ${__('Changes in {0}', [df.label])}

    + return ` +

    ${__("Changes in {0}", [df.label])}

    - - + + ${html}
    ${__('Item Code')}${__('Field')}${__("Item Code")}${__("Field")} ${name1} ${name2}
    `; - }).join(''); + }) + .join(""); let get_added_removed_html = (title, grouped_items) => { - return Object.keys(grouped_items).map(fieldname => { - let rows = grouped_items[fieldname]; - let df = frappe.meta.get_docfield(doctype, fieldname); - let fields = frappe.meta.get_docfields(df.options) - .filter(df => df.in_list_view); + return Object.keys(grouped_items) + .map((fieldname) => { + let rows = grouped_items[fieldname]; + let df = frappe.meta.get_docfield(doctype, fieldname); + let fields = frappe.meta.get_docfields(df.options).filter((df) => df.in_list_view); - let html = rows.map(row => { - let [, doc] = row; - let cells = fields - .map(df => `${doc[df.fieldname]}`) - .join(''); - return `${cells}`; - }).join(''); + let html = rows + .map((row) => { + let [, doc] = row; + let cells = fields.map((df) => `${doc[df.fieldname]}`).join(""); + return `${cells}`; + }) + .join(""); - let header = fields.map(df => `${df.label}`).join(''); - return ` + let header = fields.map((df) => `${df.label}`).join(""); + return `

    ${$.format(title, [df.label])}

    ${header} ${html}
    `; - }).join(''); + }) + .join(""); }; - let added_by_fieldname = group_items(diff.added, change => change[0]); - let removed_by_fieldname = group_items(diff.removed, change => change[0]); + let added_by_fieldname = group_items(diff.added, (change) => change[0]); + let removed_by_fieldname = group_items(diff.removed, (change) => change[0]); - let added_html = get_added_removed_html(__('Rows Added in {0}'), added_by_fieldname); - let removed_html = get_added_removed_html(__('Rows Removed in {0}'), removed_by_fieldname); + let added_html = get_added_removed_html(__("Rows Added in {0}"), added_by_fieldname); + let removed_html = get_added_removed_html(__("Rows Removed in {0}"), removed_by_fieldname); let html = ` ${value_changes} @@ -200,14 +207,14 @@ erpnext.BOMComparisonTool = class BOMComparisonTool { ${removed_html} `; - this.form.get_field('preview').html(html); + this.form.get_field("preview").html(html); } get_changed_values(doctype, changed) { - return changed.filter(change => { + return changed.filter((change) => { let [fieldname, value1, value2] = change; - if (!value1) value1 = ''; - if (!value2) value2 = ''; + if (!value1) value1 = ""; + if (!value2) value2 = ""; if (value1 === value2) return false; let df = frappe.meta.get_docfield(doctype, fieldname); if (!df) return false; diff --git a/erpnext/manufacturing/report/bom_explorer/bom_explorer.js b/erpnext/manufacturing/report/bom_explorer/bom_explorer.js index b94d3f37704..073b670e8c1 100644 --- a/erpnext/manufacturing/report/bom_explorer/bom_explorer.js +++ b/erpnext/manufacturing/report/bom_explorer/bom_explorer.js @@ -3,13 +3,13 @@ /* eslint-disable */ frappe.query_reports["BOM Explorer"] = { - "filters": [ + filters: [ { fieldname: "bom", label: __("BOM"), fieldtype: "Link", options: "BOM", - reqd: 1 + reqd: 1, }, - ] + ], }; diff --git a/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.js b/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.js index 30974f170c4..ce93208243f 100644 --- a/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.js +++ b/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.js @@ -3,40 +3,40 @@ /* eslint-disable */ frappe.query_reports["BOM Operations Time"] = { - "filters": [ + filters: [ { - "fieldname": "item_code", - "label": __("Item Code"), - "fieldtype": "Link", - "width": "100", - "options": "Item", - "get_query": () =>{ + fieldname: "item_code", + label: __("Item Code"), + fieldtype: "Link", + width: "100", + options: "Item", + get_query: () => { return { - filters: { "is_stock_item": 1 } - } - } + filters: { is_stock_item: 1 }, + }; + }, }, { - "fieldname": "bom_id", - "label": __("BOM ID"), - "fieldtype": "MultiSelectList", - "width": "100", - "options": "BOM", - "get_data": function(txt) { + fieldname: "bom_id", + label: __("BOM ID"), + fieldtype: "MultiSelectList", + width: "100", + options: "BOM", + get_data: function (txt) { return frappe.db.get_link_options("BOM", txt); }, - "get_query": () =>{ + get_query: () => { return { - filters: { "docstatus": 1, "is_active": 1, "with_operations": 1 } - } - } + filters: { docstatus: 1, is_active: 1, with_operations: 1 }, + }; + }, }, { - "fieldname": "workstation", - "label": __("Workstation"), - "fieldtype": "Link", - "width": "100", - "options": "Workstation" + fieldname: "workstation", + label: __("Workstation"), + fieldtype: "Link", + width: "100", + options: "Workstation", }, - ] + ], }; diff --git a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.js b/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.js index a0fd91e866f..d6b86595ef4 100644 --- a/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.js +++ b/erpnext/manufacturing/report/bom_stock_calculated/bom_stock_calculated.js @@ -3,32 +3,32 @@ /* eslint-disable */ frappe.query_reports["BOM Stock Calculated"] = { - "filters": [ + filters: [ { - "fieldname": "bom", - "label": __("BOM"), - "fieldtype": "Link", - "options": "BOM", - "reqd": 1 + fieldname: "bom", + label: __("BOM"), + fieldtype: "Link", + options: "BOM", + reqd: 1, }, { - "fieldname": "warehouse", - "label": __("Warehouse"), - "fieldtype": "Link", - "options": "Warehouse", + fieldname: "warehouse", + label: __("Warehouse"), + fieldtype: "Link", + options: "Warehouse", }, { - "fieldname": "qty_to_make", - "label": __("Quantity to Make"), - "fieldtype": "Float", - "default": "1.0", - "reqd": 1 + fieldname: "qty_to_make", + label: __("Quantity to Make"), + fieldtype: "Float", + default: "1.0", + reqd: 1, }, { - "fieldname": "show_exploded_view", - "label": __("Show exploded view"), - "fieldtype": "Check", - "default": false, - } - ] -} + fieldname: "show_exploded_view", + label: __("Show exploded view"), + fieldtype: "Check", + default: false, + }, + ], +}; diff --git a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js index e7f67caf249..91d73d0101c 100644 --- a/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js +++ b/erpnext/manufacturing/report/bom_stock_report/bom_stock_report.js @@ -1,38 +1,41 @@ frappe.query_reports["BOM Stock Report"] = { - "filters": [ + filters: [ { - "fieldname": "bom", - "label": __("BOM"), - "fieldtype": "Link", - "options": "BOM", - "reqd": 1 - }, { - "fieldname": "warehouse", - "label": __("Warehouse"), - "fieldtype": "Link", - "options": "Warehouse", - "reqd": 1 - }, { - "fieldname": "show_exploded_view", - "label": __("Show exploded view"), - "fieldtype": "Check" - }, { - "fieldname": "qty_to_produce", - "label": __("Quantity to Produce"), - "fieldtype": "Int", - "default": "1" - }, + fieldname: "bom", + label: __("BOM"), + fieldtype: "Link", + options: "BOM", + reqd: 1, + }, + { + fieldname: "warehouse", + label: __("Warehouse"), + fieldtype: "Link", + options: "Warehouse", + reqd: 1, + }, + { + fieldname: "show_exploded_view", + label: __("Show exploded view"), + fieldtype: "Check", + }, + { + fieldname: "qty_to_produce", + label: __("Quantity to Produce"), + fieldtype: "Int", + default: "1", + }, ], - "formatter": function(value, row, column, data, default_formatter) { + formatter: function (value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); if (column.id == "item") { if (data["in_stock_qty"] >= data["required_qty"]) { - value = `${data['item']}`; + value = `${data["item"]}`; } else { - value = `${data['item']}`; + value = `${data["item"]}`; } } - return value - } -} + return value; + }, +}; diff --git a/erpnext/manufacturing/report/bom_variance_report/bom_variance_report.js b/erpnext/manufacturing/report/bom_variance_report/bom_variance_report.js index c6ecaef2fa3..fb3a29538b8 100644 --- a/erpnext/manufacturing/report/bom_variance_report/bom_variance_report.js +++ b/erpnext/manufacturing/report/bom_variance_report/bom_variance_report.js @@ -3,27 +3,27 @@ /* eslint-disable */ frappe.query_reports["BOM Variance Report"] = { - "filters": [ + filters: [ { - "fieldname":"bom_no", - "label": __("BOM No"), - "fieldtype": "Link", - "options": "BOM" + fieldname: "bom_no", + label: __("BOM No"), + fieldtype: "Link", + options: "BOM", }, { - "fieldname":"work_order", - "label": __("Work Order"), - "fieldtype": "Link", - "options": "Work Order", - "get_query": function() { - var bom_no = frappe.query_report.get_filter_value('bom_no'); - return{ + fieldname: "work_order", + label: __("Work Order"), + fieldtype: "Link", + options: "Work Order", + get_query: function () { + var bom_no = frappe.query_report.get_filter_value("bom_no"); + return { query: "erpnext.manufacturing.report.bom_variance_report.bom_variance_report.get_work_orders", filters: { - 'bom_no': bom_no - } - } - } + bom_no: bom_no, + }, + }; + }, }, - ] -} + ], +}; diff --git a/erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.js b/erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.js index 72eed5e0d7c..6092b39b6c5 100644 --- a/erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.js +++ b/erpnext/manufacturing/report/cost_of_poor_quality_report/cost_of_poor_quality_report.js @@ -3,24 +3,26 @@ /* eslint-disable */ frappe.query_reports["Cost of Poor Quality Report"] = { - "filters": [ + filters: [ { label: __("Company"), fieldname: "company", fieldtype: "Link", options: "Company", default: frappe.defaults.get_user_default("Company"), - reqd: 1 + reqd: 1, }, { label: __("From Date"), - fieldname:"from_date", + fieldname: "from_date", fieldtype: "Datetime", - default: frappe.datetime.convert_to_system_tz(frappe.datetime.add_months(frappe.datetime.now_datetime(), -1)), + default: frappe.datetime.convert_to_system_tz( + frappe.datetime.add_months(frappe.datetime.now_datetime(), -1) + ), }, { label: __("To Date"), - fieldname:"to_date", + fieldname: "to_date", fieldtype: "Datetime", default: frappe.datetime.now_datetime(), }, @@ -29,45 +31,45 @@ frappe.query_reports["Cost of Poor Quality Report"] = { fieldname: "name", fieldtype: "Link", options: "Job Card", - get_query: function() { + get_query: function () { return { filters: { is_corrective_job_card: 1, - docstatus: 1 - } - } - } + docstatus: 1, + }, + }; + }, }, { label: __("Work Order"), fieldname: "work_order", fieldtype: "Link", - options: "Work Order" + options: "Work Order", }, { label: __("Operation"), fieldname: "operation", fieldtype: "Link", options: "Operation", - get_query: function() { + get_query: function () { return { filters: { - is_corrective_operation: 1 - } - } - } + is_corrective_operation: 1, + }, + }; + }, }, { label: __("Workstation"), fieldname: "workstation", fieldtype: "Link", - options: "Workstation" + options: "Workstation", }, { label: __("Item"), fieldname: "production_item", fieldtype: "Link", - options: "Item" + options: "Item", }, { label: __("Serial No"), @@ -75,14 +77,14 @@ frappe.query_reports["Cost of Poor Quality Report"] = { fieldtype: "Link", options: "Serial No", depends_on: "eval: doc.production_item", - get_query: function() { - var item_code = frappe.query_report.get_filter_value('production_item'); + get_query: function () { + var item_code = frappe.query_report.get_filter_value("production_item"); return { filters: { - item_code: item_code - } - } - } + item_code: item_code, + }, + }; + }, }, { label: __("Batch No"), @@ -90,14 +92,14 @@ frappe.query_reports["Cost of Poor Quality Report"] = { fieldtype: "Link", options: "Batch No", depends_on: "eval: doc.production_item", - get_query: function() { - var item_code = frappe.query_report.get_filter_value('production_item'); + get_query: function () { + var item_code = frappe.query_report.get_filter_value("production_item"); return { filters: { - item: item_code - } - } - } + item: item_code, + }, + }; + }, }, - ] + ], }; diff --git a/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.js b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.js index f6486743aa3..22cf59c572a 100644 --- a/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.js +++ b/erpnext/manufacturing/report/downtime_analysis/downtime_analysis.js @@ -3,17 +3,19 @@ /* eslint-disable */ frappe.query_reports["Downtime Analysis"] = { - "filters": [ + filters: [ { label: __("From Date"), - fieldname:"from_date", + fieldname: "from_date", fieldtype: "Datetime", - default: frappe.datetime.convert_to_system_tz(frappe.datetime.add_months(frappe.datetime.now_datetime(), -1)), - reqd: 1 + default: frappe.datetime.convert_to_system_tz( + frappe.datetime.add_months(frappe.datetime.now_datetime(), -1) + ), + reqd: 1, }, { label: __("To Date"), - fieldname:"to_date", + fieldname: "to_date", fieldtype: "Datetime", default: frappe.datetime.now_datetime(), reqd: 1, @@ -22,7 +24,7 @@ frappe.query_reports["Downtime Analysis"] = { label: __("Machine"), fieldname: "workstation", fieldtype: "Link", - options: "Workstation" - } - ] + options: "Workstation", + }, + ], }; diff --git a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.js b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.js index 123a82a3882..f9e2b6cfcb0 100644 --- a/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.js +++ b/erpnext/manufacturing/report/exponential_smoothing_forecasting/exponential_smoothing_forecasting.js @@ -3,95 +3,95 @@ /* eslint-disable */ frappe.query_reports["Exponential Smoothing Forecasting"] = { - "filters": [ + filters: [ { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "reqd": 1, - "default": frappe.defaults.get_user_default("Company") + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + reqd: 1, + default: frappe.defaults.get_user_default("Company"), }, { - "fieldname":"from_date", - "label": __("From Date"), - "fieldtype": "Date", - "default": frappe.datetime.get_today(), - "reqd": 1 + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.datetime.get_today(), + reqd: 1, }, { - "fieldname":"to_date", - "label": __("To Date"), - "fieldtype": "Date", - "default": frappe.datetime.add_months(frappe.datetime.get_today(), 12), - "reqd": 1 + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.datetime.add_months(frappe.datetime.get_today(), 12), + reqd: 1, }, { - "fieldname":"based_on_document", - "label": __("Based On Document"), - "fieldtype": "Select", - "options": ["Sales Order", "Delivery Note", "Quotation"], - "default": "Sales Order", - "reqd": 1 + fieldname: "based_on_document", + label: __("Based On Document"), + fieldtype: "Select", + options: ["Sales Order", "Delivery Note", "Quotation"], + default: "Sales Order", + reqd: 1, }, { - "fieldname":"based_on_field", - "label": __("Based On"), - "fieldtype": "Select", - "options": ["Qty", "Amount"], - "default": "Qty", - "reqd": 1 + fieldname: "based_on_field", + label: __("Based On"), + fieldtype: "Select", + options: ["Qty", "Amount"], + default: "Qty", + reqd: 1, }, { - "fieldname":"no_of_years", - "label": __("Based On Data ( in years )"), - "fieldtype": "Select", - "options": [3, 6, 9], - "default": 3, - "reqd": 1 + fieldname: "no_of_years", + label: __("Based On Data ( in years )"), + fieldtype: "Select", + options: [3, 6, 9], + default: 3, + reqd: 1, }, { - "fieldname": "periodicity", - "label": __("Periodicity"), - "fieldtype": "Select", - "options": [ - { "value": "Monthly", "label": __("Monthly") }, - { "value": "Quarterly", "label": __("Quarterly") }, - { "value": "Half-Yearly", "label": __("Half-Yearly") }, - { "value": "Yearly", "label": __("Yearly") } + fieldname: "periodicity", + label: __("Periodicity"), + fieldtype: "Select", + options: [ + { value: "Monthly", label: __("Monthly") }, + { value: "Quarterly", label: __("Quarterly") }, + { value: "Half-Yearly", label: __("Half-Yearly") }, + { value: "Yearly", label: __("Yearly") }, ], - "default": "Yearly", - "reqd": 1 + default: "Yearly", + reqd: 1, }, { - "fieldname":"smoothing_constant", - "label": __("Smoothing Constant"), - "fieldtype": "Select", - "options": [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0], - "reqd": 1, - "default": 0.3 + fieldname: "smoothing_constant", + label: __("Smoothing Constant"), + fieldtype: "Select", + options: [0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0], + reqd: 1, + default: 0.3, }, { - "fieldname":"item_code", - "label": __("Item Code"), - "fieldtype": "Link", - "options": "Item" + fieldname: "item_code", + label: __("Item Code"), + fieldtype: "Link", + options: "Item", }, { - "fieldname":"warehouse", - "label": __("Warehouse"), - "fieldtype": "Link", - "options": "Warehouse", + fieldname: "warehouse", + label: __("Warehouse"), + fieldtype: "Link", + options: "Warehouse", get_query: () => { - var company = frappe.query_report.get_filter_value('company'); + var company = frappe.query_report.get_filter_value("company"); if (company) { return { filters: { - 'company': company - } + company: company, + }, }; } - } - } - ] + }, + }, + ], }; diff --git a/erpnext/manufacturing/report/job_card_summary/job_card_summary.js b/erpnext/manufacturing/report/job_card_summary/job_card_summary.js index a874f224820..0d6a52f4b8e 100644 --- a/erpnext/manufacturing/report/job_card_summary/job_card_summary.js +++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.js @@ -3,14 +3,14 @@ /* eslint-disable */ frappe.query_reports["Job Card Summary"] = { - "filters": [ + filters: [ { label: __("Company"), fieldname: "company", fieldtype: "Link", options: "Company", default: frappe.defaults.get_user_default("Company"), - reqd: 1 + reqd: 1, }, { fieldname: "fiscal_year", @@ -19,30 +19,30 @@ frappe.query_reports["Job Card Summary"] = { options: "Fiscal Year", default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), reqd: 1, - on_change: function(query_report) { + on_change: function (query_report) { var fiscal_year = query_report.get_values().fiscal_year; if (!fiscal_year) { return; } - frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) { + 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({ from_date: fy.year_start_date, - to_date: fy.year_end_date + to_date: fy.year_end_date, }); }); - } + }, }, { label: __("From Posting Date"), - fieldname:"from_date", + fieldname: "from_date", fieldtype: "Date", default: frappe.defaults.get_user_default("year_start_date"), - reqd: 1 + reqd: 1, }, { label: __("To Posting Date"), - fieldname:"to_date", + fieldname: "to_date", fieldtype: "Date", default: frappe.defaults.get_user_default("year_end_date"), reqd: 1, @@ -51,35 +51,35 @@ frappe.query_reports["Job Card Summary"] = { label: __("Status"), fieldname: "status", fieldtype: "Select", - options: ["", "Open", "Work In Progress", "Completed", "On Hold"] + options: ["", "Open", "Work In Progress", "Completed", "On Hold"], }, { label: __("Work Orders"), fieldname: "work_order", fieldtype: "MultiSelectList", - get_data: function(txt) { - return frappe.db.get_link_options('Work Order', txt); - } + get_data: function (txt) { + return frappe.db.get_link_options("Work Order", txt); + }, }, { label: __("Production Item"), fieldname: "production_item", fieldtype: "MultiSelectList", - get_data: function(txt) { - return frappe.db.get_link_options('Item', txt); - } + get_data: function (txt) { + return frappe.db.get_link_options("Item", txt); + }, }, { label: __("Workstation"), fieldname: "workstation", fieldtype: "Link", - options: "Workstation" + options: "Workstation", }, { label: __("Operation"), fieldname: "operation", fieldtype: "Link", - options: "Operation" - } - ] + options: "Operation", + }, + ], }; diff --git a/erpnext/manufacturing/report/process_loss_report/process_loss_report.js b/erpnext/manufacturing/report/process_loss_report/process_loss_report.js index b0c2b94a254..ab6f64dc5c2 100644 --- a/erpnext/manufacturing/report/process_loss_report/process_loss_report.js +++ b/erpnext/manufacturing/report/process_loss_report/process_loss_report.js @@ -4,41 +4,41 @@ frappe.query_reports["Process Loss Report"] = { filters: [ - { - label: __("Company"), - fieldname: "company", - fieldtype: "Link", - options: "Company", - mandatory: true, - default: frappe.defaults.get_user_default("Company"), - }, { - label: __("Item"), - fieldname: "item", - fieldtype: "Link", - options: "Item", - mandatory: false, + label: __("Company"), + fieldname: "company", + fieldtype: "Link", + options: "Company", + mandatory: true, + default: frappe.defaults.get_user_default("Company"), }, { - label: __("Work Order"), - fieldname: "work_order", - fieldtype: "Link", - options: "Work Order", - mandatory: false, + label: __("Item"), + fieldname: "item", + fieldtype: "Link", + options: "Item", + mandatory: false, }, - { - label: __("From Date"), - fieldname: "from_date", - fieldtype: "Date", - mandatory: true, - default: frappe.datetime.year_start(), - }, - { - label: __("To Date"), - fieldname: "to_date", - fieldtype: "Date", - mandatory: true, - default: frappe.datetime.get_today(), - }, - ] + { + label: __("Work Order"), + fieldname: "work_order", + fieldtype: "Link", + options: "Work Order", + mandatory: false, + }, + { + label: __("From Date"), + fieldname: "from_date", + fieldtype: "Date", + mandatory: true, + default: frappe.datetime.year_start(), + }, + { + label: __("To Date"), + fieldname: "to_date", + fieldtype: "Date", + mandatory: true, + default: frappe.datetime.get_today(), + }, + ], }; diff --git a/erpnext/manufacturing/report/production_analytics/production_analytics.js b/erpnext/manufacturing/report/production_analytics/production_analytics.js index 99f9b1260a2..6f2c38295e9 100644 --- a/erpnext/manufacturing/report/production_analytics/production_analytics.js +++ b/erpnext/manufacturing/report/production_analytics/production_analytics.js @@ -3,41 +3,41 @@ /* eslint-disable */ frappe.query_reports["Production Analytics"] = { - "filters": [ + filters: [ { fieldname: "company", label: __("Company"), fieldtype: "Link", options: "Company", default: frappe.defaults.get_user_default("Company"), - reqd: 1 + reqd: 1, }, { fieldname: "from_date", label: __("From Date"), fieldtype: "Date", default: frappe.defaults.get_user_default("year_start_date"), - reqd: 1 + reqd: 1, }, { - fieldname:"to_date", + fieldname: "to_date", label: __("To Date"), fieldtype: "Date", default: frappe.defaults.get_user_default("year_end_date"), - reqd: 1 + reqd: 1, }, { fieldname: "range", label: __("Range"), fieldtype: "Select", options: [ - { "value": "Weekly", "label": __("Weekly") }, - { "value": "Monthly", "label": __("Monthly") }, - { "value": "Quarterly", "label": __("Quarterly") }, - { "value": "Yearly", "label": __("Yearly") } + { value: "Weekly", label: __("Weekly") }, + { value: "Monthly", label: __("Monthly") }, + { value: "Quarterly", label: __("Quarterly") }, + { value: "Yearly", label: __("Yearly") }, ], default: "Monthly", - reqd: 1 - } - ] -} + reqd: 1, + }, + ], +}; diff --git a/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.js b/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.js index a4d7fae4560..567e8ebc6c3 100644 --- a/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.js +++ b/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.js @@ -3,28 +3,28 @@ /* eslint-disable */ frappe.query_reports["Production Plan Summary"] = { - "filters": [ + filters: [ { fieldname: "production_plan", label: __("Production Plan"), fieldtype: "Link", options: "Production Plan", reqd: 1, - get_query: function() { + get_query: function () { return { filters: { - "docstatus": 1 - } + docstatus: 1, + }, }; - } - } + }, + }, ], - "formatter": function(value, row, column, data, default_formatter) { + formatter: function (value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); if (column.fieldname == "item_code") { - var color = data.pending_qty > 0 ? 'red': 'green'; - value = `${data['item_code']}`; + var color = data.pending_qty > 0 ? "red" : "green"; + value = `${data["item_code"]}`; } return value; diff --git a/erpnext/manufacturing/report/production_planning_report/production_planning_report.js b/erpnext/manufacturing/report/production_planning_report/production_planning_report.js index 675b8a11008..bde90504e67 100644 --- a/erpnext/manufacturing/report/production_planning_report/production_planning_report.js +++ b/erpnext/manufacturing/report/production_planning_report/production_planning_report.js @@ -3,106 +3,110 @@ /* eslint-disable */ frappe.query_reports["Production Planning Report"] = { - "filters": [ + filters: [ { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "reqd": 1, - "default": frappe.defaults.get_user_default("Company") + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + reqd: 1, + default: frappe.defaults.get_user_default("Company"), }, { - "fieldname":"based_on", - "label": __("Based On"), - "fieldtype": "Select", - "options": ["Sales Order", "Material Request", "Work Order"], - "default": "Sales Order", - "reqd": 1, - on_change: function() { + fieldname: "based_on", + label: __("Based On"), + fieldtype: "Select", + options: ["Sales Order", "Material Request", "Work Order"], + default: "Sales Order", + reqd: 1, + on_change: function () { let filters = frappe.query_report.filters; - let based_on = frappe.query_report.get_filter_value('based_on'); + let based_on = frappe.query_report.get_filter_value("based_on"); let options = { "Sales Order": ["Delivery Date", "Total Amount"], "Material Request": ["Required Date"], - "Work Order": ["Planned Start Date"] - } + "Work Order": ["Planned Start Date"], + }; - filters.forEach(d => { + filters.forEach((d) => { if (d.fieldname == "order_by") { d.df.options = options[based_on]; - d.set_input(d.df.options) + d.set_input(d.df.options); } }); frappe.query_report.refresh(); - } + }, }, { - "fieldname":"docnames", - "label": __("Document Name"), - "fieldtype": "MultiSelectList", - "options": "Sales Order", - "get_data": function(txt) { + fieldname: "docnames", + label: __("Document Name"), + fieldtype: "MultiSelectList", + options: "Sales Order", + get_data: function (txt) { if (!frappe.query_report.filters) return; - let based_on = frappe.query_report.get_filter_value('based_on'); + let based_on = frappe.query_report.get_filter_value("based_on"); if (!based_on) return; return frappe.db.get_link_options(based_on, txt); }, - "get_query": function() { - var company = frappe.query_report.get_filter_value('company'); + get_query: function () { + var company = frappe.query_report.get_filter_value("company"); return { filters: { - "docstatus": 1, - "company": company - } + docstatus: 1, + company: company, + }, }; - } + }, }, { - "fieldname":"raw_material_warehouse", - "label": __("Raw Material Warehouse"), - "fieldtype": "Link", - "options": "Warehouse", - "depends_on": "eval: doc.based_on != 'Work Order'", - "get_query": function() { - var company = frappe.query_report.get_filter_value('company'); + fieldname: "raw_material_warehouse", + label: __("Raw Material Warehouse"), + fieldtype: "Link", + options: "Warehouse", + depends_on: "eval: doc.based_on != 'Work Order'", + get_query: function () { + var company = frappe.query_report.get_filter_value("company"); return { filters: { - "company": company - } + company: company, + }, }; - } + }, }, { - "fieldname":"order_by", - "label": __("Order By"), - "fieldtype": "Select", - "options": ["Delivery Date", "Total Amount"], - "default": "Delivery Date" + fieldname: "order_by", + label: __("Order By"), + fieldtype: "Select", + options: ["Delivery Date", "Total Amount"], + default: "Delivery Date", }, { - "fieldname":"include_subassembly_raw_materials", - "label": __("Include Sub-assembly Raw Materials"), - "fieldtype": "Check", - "depends_on": "eval: doc.based_on != 'Work Order'", - "default": 0 + fieldname: "include_subassembly_raw_materials", + label: __("Include Sub-assembly Raw Materials"), + fieldtype: "Check", + depends_on: "eval: doc.based_on != 'Work Order'", + default: 0, }, ], - "formatter": function(value, row, column, data, default_formatter) { + formatter: function (value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); - if (column.fieldname == "production_item_name" && data && data.qty_to_manufacture > data.available_qty ) { + if ( + column.fieldname == "production_item_name" && + data && + data.qty_to_manufacture > data.available_qty + ) { value = `
    ${value}
    `; } - if (column.fieldname == "production_item" && !data.name ) { + if (column.fieldname == "production_item" && !data.name) { value = ""; } - if (column.fieldname == "raw_material_name" && data && data.required_qty > data.allotted_qty ) { + if (column.fieldname == "raw_material_name" && data && data.required_qty > data.allotted_qty) { value = `
    ${value}
    `; } diff --git a/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.js b/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.js index d4587aa6619..1e90955ebb1 100644 --- a/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.js +++ b/erpnext/manufacturing/report/quality_inspection_summary/quality_inspection_summary.js @@ -3,17 +3,17 @@ /* eslint-disable */ frappe.query_reports["Quality Inspection Summary"] = { - "filters": [ + filters: [ { label: __("From Date"), - fieldname:"from_date", + fieldname: "from_date", fieldtype: "Date", default: frappe.datetime.add_months(frappe.datetime.get_today(), -12), - reqd: 1 + reqd: 1, }, { label: __("To Date"), - fieldname:"to_date", + fieldname: "to_date", fieldtype: "Date", default: frappe.datetime.get_today(), reqd: 1, @@ -22,19 +22,19 @@ frappe.query_reports["Quality Inspection Summary"] = { label: __("Status"), fieldname: "status", fieldtype: "Select", - options: ["", "Accepted", "Rejected"] + options: ["", "Accepted", "Rejected"], }, { label: __("Item Code"), fieldname: "item_code", fieldtype: "Link", - options: "Item" + options: "Item", }, { label: __("Inspected By"), fieldname: "inspected_by", fieldtype: "Link", - options: "User" - } - ] + options: "User", + }, + ], }; diff --git a/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.js b/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.js index 2fb4ec67913..51654b90fa9 100644 --- a/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.js +++ b/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.js @@ -3,65 +3,65 @@ /* eslint-disable */ frappe.query_reports["Work Order Consumed Materials"] = { - "filters": [ + filters: [ { label: __("Company"), fieldname: "company", fieldtype: "Link", options: "Company", default: frappe.defaults.get_user_default("Company"), - reqd: 1 + reqd: 1, }, { label: __("From Date"), - fieldname:"from_date", + fieldname: "from_date", fieldtype: "Date", default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), - reqd: 1 + reqd: 1, }, { - fieldname:"to_date", + fieldname: "to_date", label: __("To Date"), fieldtype: "Date", default: frappe.datetime.get_today(), - reqd: 1 + reqd: 1, }, { label: __("Work Order"), fieldname: "name", fieldtype: "Link", options: "Work Order", - get_query: function() { + get_query: function () { return { filters: { - status: ["in", ["In Process", "Completed", "Stopped"]] - } - } - } + status: ["in", ["In Process", "Completed", "Stopped"]], + }, + }; + }, }, { label: __("Production Item"), fieldname: "production_item", fieldtype: "Link", depends_on: "eval: !doc.name", - options: "Item" + options: "Item", }, { label: __("Status"), fieldname: "status", fieldtype: "Select", - options: ["", "In Process", "Completed", "Stopped"] + options: ["", "In Process", "Completed", "Stopped"], }, { label: __("Excess Materials Consumed"), fieldname: "show_extra_consumed_materials", - fieldtype: "Check" - } + fieldtype: "Check", + }, ], - "formatter": function(value, row, column, data, default_formatter) { + formatter: function (value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); - if (column.fieldname == "raw_material_name" && data && data.extra_consumed_qty > 0 ) { + if (column.fieldname == "raw_material_name" && data && data.extra_consumed_qty > 0) { value = `
    ${value}
    `; } diff --git a/erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.js b/erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.js index dbb7c234101..ec8651ddf7d 100644 --- a/erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.js +++ b/erpnext/manufacturing/report/work_order_stock_report/work_order_stock_report.js @@ -3,12 +3,12 @@ /* eslint-disable */ frappe.query_reports["Work Order Stock Report"] = { - "filters": [ + filters: [ { - "fieldname": "warehouse", - "label": __("Warehouse"), - "fieldtype": "Link", - "options": "Warehouse" - } - ] -} + fieldname: "warehouse", + label: __("Warehouse"), + fieldtype: "Link", + options: "Warehouse", + }, + ], +}; diff --git a/erpnext/manufacturing/report/work_order_summary/work_order_summary.js b/erpnext/manufacturing/report/work_order_summary/work_order_summary.js index 67bd24dd805..67e6e706c59 100644 --- a/erpnext/manufacturing/report/work_order_summary/work_order_summary.js +++ b/erpnext/manufacturing/report/work_order_summary/work_order_summary.js @@ -3,32 +3,32 @@ /* eslint-disable */ frappe.query_reports["Work Order Summary"] = { - "filters": [ + filters: [ { label: __("Company"), fieldname: "company", fieldtype: "Link", options: "Company", default: frappe.defaults.get_user_default("Company"), - reqd: 1 + reqd: 1, }, { label: __("Based On"), - fieldname:"based_on", + fieldname: "based_on", fieldtype: "Select", options: "Creation Date\nPlanned Date\nActual Date", - default: "Creation Date" + default: "Creation Date", }, { label: __("From Posting Date"), - fieldname:"from_date", + fieldname: "from_date", fieldtype: "Date", default: frappe.datetime.add_months(frappe.datetime.get_today(), -3), - reqd: 1 + reqd: 1, }, { label: __("To Posting Date"), - fieldname:"to_date", + fieldname: "to_date", fieldtype: "Date", default: frappe.datetime.get_today(), reqd: 1, @@ -37,36 +37,36 @@ frappe.query_reports["Work Order Summary"] = { label: __("Status"), fieldname: "status", fieldtype: "Select", - options: ["", "Not Started", "In Process", "Completed", "Stopped", "Closed"] + options: ["", "Not Started", "In Process", "Completed", "Stopped", "Closed"], }, { label: __("Sales Orders"), fieldname: "sales_order", fieldtype: "MultiSelectList", - get_data: function(txt) { - return frappe.db.get_link_options('Sales Order', txt); - } + get_data: function (txt) { + return frappe.db.get_link_options("Sales Order", txt); + }, }, { label: __("Production Item"), fieldname: "production_item", fieldtype: "MultiSelectList", - get_data: function(txt) { - return frappe.db.get_link_options('Item', txt); - } + get_data: function (txt) { + return frappe.db.get_link_options("Item", txt); + }, }, { label: __("Age"), - fieldname:"age", + fieldname: "age", fieldtype: "Int", - default: "0" + default: "0", }, { label: __("Charts Based On"), - fieldname:"charts_based_on", + fieldname: "charts_based_on", fieldtype: "Select", options: ["Status", "Age", "Quantity"], - default: "Status" + default: "Status", }, - ] + ], }; diff --git a/erpnext/portal/doctype/homepage/homepage.js b/erpnext/portal/doctype/homepage/homepage.js index 59f808a3158..e0d7af001c0 100644 --- a/erpnext/portal/doctype/homepage/homepage.js +++ b/erpnext/portal/doctype/homepage/homepage.js @@ -1,30 +1,30 @@ // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Homepage', { - setup: function(frm) { - frm.fields_dict["products"].grid.get_field("item").get_query = function() { +frappe.ui.form.on("Homepage", { + setup: function (frm) { + frm.fields_dict["products"].grid.get_field("item").get_query = function () { return { - filters: {'published': 1} - } - } + filters: { published: 1 }, + }; + }; }, - refresh: function(frm) { - frm.add_custom_button(__('Set Meta Tags'), () => { - frappe.utils.set_meta_tag('home'); + refresh: function (frm) { + frm.add_custom_button(__("Set Meta Tags"), () => { + frappe.utils.set_meta_tag("home"); }); - frm.add_custom_button(__('Customize Homepage Sections'), () => { - frappe.set_route('List', 'Homepage Section', 'List'); + frm.add_custom_button(__("Customize Homepage Sections"), () => { + frappe.set_route("List", "Homepage Section", "List"); }); }, }); -frappe.ui.form.on('Homepage Featured Product', { - view: function(frm, cdt, cdn) { - var child= locals[cdt][cdn]; +frappe.ui.form.on("Homepage Featured Product", { + view: function (frm, cdt, cdn) { + var child = locals[cdt][cdn]; if (child.item_code && child.route) { - window.open('/' + child.route, '_blank'); + window.open("/" + child.route, "_blank"); } - } + }, }); diff --git a/erpnext/portal/doctype/homepage_section/homepage_section.js b/erpnext/portal/doctype/homepage_section/homepage_section.js index 68859eb10b6..270b73aafdf 100644 --- a/erpnext/portal/doctype/homepage_section/homepage_section.js +++ b/erpnext/portal/doctype/homepage_section/homepage_section.js @@ -1,6 +1,4 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Homepage Section', { - -}); +frappe.ui.form.on("Homepage Section", {}); diff --git a/erpnext/projects/doctype/activity_cost/activity_cost.js b/erpnext/projects/doctype/activity_cost/activity_cost.js index 2d22caad8e2..d47b574265f 100644 --- a/erpnext/projects/doctype/activity_cost/activity_cost.js +++ b/erpnext/projects/doctype/activity_cost/activity_cost.js @@ -1 +1 @@ -cur_frm.add_fetch('employee', 'employee_name', 'employee_name'); +cur_frm.add_fetch("employee", "employee_name", "employee_name"); diff --git a/erpnext/projects/doctype/activity_type/activity_type.js b/erpnext/projects/doctype/activity_type/activity_type.js index f1ba882812e..b620cdd6c64 100644 --- a/erpnext/projects/doctype/activity_type/activity_type.js +++ b/erpnext/projects/doctype/activity_type/activity_type.js @@ -1,12 +1,15 @@ frappe.ui.form.on("Activity Type", { - onload: function(frm) { - frm.set_currency_labels(["billing_rate", "costing_rate"], frappe.defaults.get_global_default('currency')); + onload: function (frm) { + frm.set_currency_labels( + ["billing_rate", "costing_rate"], + frappe.defaults.get_global_default("currency") + ); }, - refresh: function(frm) { - frm.add_custom_button(__("Activity Cost per Employee"), function() { - frappe.route_options = {"activity_type": frm.doc.name}; + refresh: function (frm) { + frm.add_custom_button(__("Activity Cost per Employee"), function () { + frappe.route_options = { activity_type: frm.doc.name }; frappe.set_route("List", "Activity Cost"); }); - } + }, }); diff --git a/erpnext/projects/doctype/project/project.js b/erpnext/projects/doctype/project/project.js index 0cdd9c10e7f..3ea8189feec 100644 --- a/erpnext/projects/doctype/project/project.js +++ b/erpnext/projects/doctype/project/project.js @@ -3,16 +3,16 @@ frappe.ui.form.on("Project", { setup(frm) { frm.make_methods = { - 'Timesheet': () => { + Timesheet: () => { open_form(frm, "Timesheet", "Timesheet Detail", "time_logs"); }, - 'Purchase Order': () => { + "Purchase Order": () => { open_form(frm, "Purchase Order", "Purchase Order Item", "items"); }, - 'Purchase Receipt': () => { + "Purchase Receipt": () => { open_form(frm, "Purchase Receipt", "Purchase Receipt Item", "items"); }, - 'Purchase Invoice': () => { + "Purchase Invoice": () => { open_form(frm, "Purchase Invoice", "Purchase Invoice Item", "items"); }, }; @@ -22,31 +22,31 @@ frappe.ui.form.on("Project", { so.get_route_options_for_new_doc = () => { if (frm.is_new()) return; return { - "customer": frm.doc.customer, - "project_name": frm.doc.name + customer: frm.doc.customer, + project_name: frm.doc.name, }; }; - frm.set_query('customer', 'erpnext.controllers.queries.customer_query'); + frm.set_query("customer", "erpnext.controllers.queries.customer_query"); frm.set_query("user", "users", function () { return { - query: "erpnext.projects.doctype.project.project.get_users_for_project" + query: "erpnext.projects.doctype.project.project.get_users_for_project", }; }); frm.set_query("department", function (doc) { return { filters: { - "company": doc.company, - } + company: doc.company, + }, }; }); // sales order - frm.set_query('sales_order', function () { + frm.set_query("sales_order", function () { var filters = { - 'project': ["in", frm.doc.__islocal ? [""] : [frm.doc.name, ""]] + project: ["in", frm.doc.__islocal ? [""] : [frm.doc.name, ""]], }; if (frm.doc.customer) { @@ -54,7 +54,7 @@ frappe.ui.form.on("Project", { } return { - filters: filters + filters: filters, }; }); }, @@ -65,108 +65,133 @@ frappe.ui.form.on("Project", { } else { frm.add_web_link("/projects?project=" + encodeURIComponent(frm.doc.name)); - frm.trigger('show_dashboard'); + frm.trigger("show_dashboard"); } frm.trigger("set_custom_buttons"); }, - set_custom_buttons: function(frm) { + set_custom_buttons: function (frm) { if (!frm.is_new()) { - frm.add_custom_button(__('Duplicate Project with Tasks'), () => { - frm.events.create_duplicate(frm); - }, __("Actions")); + frm.add_custom_button( + __("Duplicate Project with Tasks"), + () => { + frm.events.create_duplicate(frm); + }, + __("Actions") + ); - frm.add_custom_button(__('Update Total Purchase Cost'), () => { - frm.events.update_total_purchase_cost(frm); - }, __("Actions")); + frm.add_custom_button( + __("Update Total Purchase Cost"), + () => { + frm.events.update_total_purchase_cost(frm); + }, + __("Actions") + ); frm.trigger("set_project_status_button"); - if (frappe.model.can_read("Task")) { - frm.add_custom_button(__("Gantt Chart"), function () { - frappe.route_options = { - "project": frm.doc.name - }; - frappe.set_route("List", "Task", "Gantt"); - }, __("View")); + frm.add_custom_button( + __("Gantt Chart"), + function () { + frappe.route_options = { + project: frm.doc.name, + }; + frappe.set_route("List", "Task", "Gantt"); + }, + __("View") + ); - frm.add_custom_button(__("Kanban Board"), () => { - frappe.call('erpnext.projects.doctype.project.project.create_kanban_board_if_not_exists', { - project: frm.doc.name - }).then(() => { - frappe.set_route('List', 'Task', 'Kanban', frm.doc.project_name); - }); - }, __("View")); + frm.add_custom_button( + __("Kanban Board"), + () => { + frappe + .call( + "erpnext.projects.doctype.project.project.create_kanban_board_if_not_exists", + { + project: frm.doc.name, + } + ) + .then(() => { + frappe.set_route("List", "Task", "Kanban", frm.doc.project_name); + }); + }, + __("View") + ); } } - - }, - update_total_purchase_cost: function(frm) { + update_total_purchase_cost: function (frm) { frappe.call({ method: "erpnext.projects.doctype.project.project.recalculate_project_total_purchase_cost", - args: {project: frm.doc.name}, + args: { project: frm.doc.name }, freeze: true, - freeze_message: __('Recalculating Purchase Cost against this Project...'), - callback: function(r) { + freeze_message: __("Recalculating Purchase Cost against this Project..."), + callback: function (r) { if (r && !r.exc) { - frappe.msgprint(__('Total Purchase Cost has been updated')); + frappe.msgprint(__("Total Purchase Cost has been updated")); frm.refresh(); } - } - + }, }); }, - set_project_status_button: function(frm) { - frm.add_custom_button(__('Set Project Status'), () => { - let d = new frappe.ui.Dialog({ - "title": __("Set Project Status"), - "fields": [ - { - "fieldname": "status", - "fieldtype": "Select", - "label": "Status", - "reqd": 1, - "options": "Completed\nCancelled", + set_project_status_button: function (frm) { + frm.add_custom_button( + __("Set Project Status"), + () => { + let d = new frappe.ui.Dialog({ + title: __("Set Project Status"), + fields: [ + { + fieldname: "status", + fieldtype: "Select", + label: "Status", + reqd: 1, + options: "Completed\nCancelled", + }, + ], + primary_action: function () { + frm.events.set_status(frm, d.get_values().status); + d.hide(); }, - ], - primary_action: function() { - frm.events.set_status(frm, d.get_values().status); - d.hide(); - }, - primary_action_label: __("Set Project Status") - }).show(); - }, __("Actions")); + primary_action_label: __("Set Project Status"), + }).show(); + }, + __("Actions") + ); }, - create_duplicate: function(frm) { - return new Promise(resolve => { - frappe.prompt('Project Name', (data) => { - frappe.xcall('erpnext.projects.doctype.project.project.create_duplicate_project', - { + create_duplicate: function (frm) { + return new Promise((resolve) => { + frappe.prompt("Project Name", (data) => { + frappe + .xcall("erpnext.projects.doctype.project.project.create_duplicate_project", { prev_doc: frm.doc, - project_name: data.value - }).then(() => { - frappe.set_route('Form', "Project", data.value); - frappe.show_alert(__("Duplicate project has been created")); - }); + project_name: data.value, + }) + .then(() => { + frappe.set_route("Form", "Project", data.value); + frappe.show_alert(__("Duplicate project has been created")); + }); resolve(); }); }); }, - set_status: function(frm, status) { - frappe.confirm(__('Set Project and all Tasks to status {0}?', [status.bold()]), () => { - frappe.xcall('erpnext.projects.doctype.project.project.set_project_status', - {project: frm.doc.name, status: status}).then(() => { - frm.reload_doc(); - }); + set_status: function (frm, status) { + frappe.confirm(__("Set Project and all Tasks to status {0}?", [status.bold()]), () => { + frappe + .xcall("erpnext.projects.doctype.project.project.set_project_status", { + project: frm.doc.name, + status: status, + }) + .then(() => { + frm.reload_doc(); + }); }); }, - }); function open_form(frm, doctype, child_doctype, parentfield) { @@ -184,5 +209,4 @@ function open_form(frm, doctype, child_doctype, parentfield) { frappe.ui.form.make_quick_entry(doctype, null, null, new_doc); }); - } diff --git a/erpnext/projects/doctype/project/project_list.js b/erpnext/projects/doctype/project/project_list.js index 5ad4bb7f93b..1503b1ee5d3 100644 --- a/erpnext/projects/doctype/project/project_list.js +++ b/erpnext/projects/doctype/project/project_list.js @@ -1,11 +1,11 @@ -frappe.listview_settings['Project'] = { +frappe.listview_settings["Project"] = { add_fields: ["status", "priority", "is_active", "percent_complete", "expected_end_date", "project_name"], - filters:[["status","=", "Open"]], - get_indicator: function(doc) { - if(doc.status=="Open" && doc.percent_complete) { + filters: [["status", "=", "Open"]], + get_indicator: function (doc) { + if (doc.status == "Open" && doc.percent_complete) { return [__("{0}%", [cint(doc.percent_complete)]), "orange", "percent_complete,>,0|status,=,Open"]; } else { return [__(doc.status), frappe.utils.guess_colour(doc.status), "status,=," + doc.status]; } - } + }, }; diff --git a/erpnext/projects/doctype/project/project_timesheet.js b/erpnext/projects/doctype/project/project_timesheet.js index 32df04ff98b..bc6de3134da 100644 --- a/erpnext/projects/doctype/project/project_timesheet.js +++ b/erpnext/projects/doctype/project/project_timesheet.js @@ -1,53 +1,63 @@ - -QUnit.test("test project", function(assert) { +QUnit.test("test project", function (assert) { assert.expect(6); let done = assert.async(); - var task_title = ["Documentation","Implementation","Testing"]; + var task_title = ["Documentation", "Implementation", "Testing"]; // To create a timesheet with different tasks and costs - let timesheet = (title,start_time,end_time,bill_rate,cost_rate) => { + let timesheet = (title, start_time, end_time, bill_rate, cost_rate) => { return frappe.run_serially([ - () => frappe.db.get_value('Task', {'subject': title}, 'name'), + () => frappe.db.get_value("Task", { subject: title }, "name"), (task) => { // Creating timesheet for a project - return frappe.tests.make('Timesheet', [ - {time_logs:[ - [ - {activity_type: 'Communication'}, - {from_time: start_time}, - {to_time: end_time}, - {hours: 2}, - {project: 'Test App'}, - {task: task.name}, - {billable: '1'}, - {billing_rate: bill_rate}, - {costing_rate: cost_rate} - ] - ]} + return frappe.tests.make("Timesheet", [ + { + time_logs: [ + [ + { activity_type: "Communication" }, + { from_time: start_time }, + { to_time: end_time }, + { hours: 2 }, + { project: "Test App" }, + { task: task.name }, + { billable: "1" }, + { billing_rate: bill_rate }, + { costing_rate: cost_rate }, + ], + ], + }, ]); }, // To check if a correct billable and costing amount is calculated for every task () => { - if(title=== 'Documentation') - { - assert.ok(cur_frm.get_field('total_billable_amount').get_value()==20, - 'Billable amount for Documentation task is correctly calculated'); - assert.ok(cur_frm.get_field('total_costing_amount').get_value()==16, - 'Costing amount for Documentation task is correctly calculated'); + if (title === "Documentation") { + assert.ok( + cur_frm.get_field("total_billable_amount").get_value() == 20, + "Billable amount for Documentation task is correctly calculated" + ); + assert.ok( + cur_frm.get_field("total_costing_amount").get_value() == 16, + "Costing amount for Documentation task is correctly calculated" + ); } - if(title=== 'Implementation') - { - assert.ok(cur_frm.get_field('total_billable_amount').get_value()==40, - 'Billable amount for Implementation task is correctly calculated'); - assert.ok(cur_frm.get_field('total_costing_amount').get_value()==32, - 'Costing amount for Implementation task is correctly calculated'); + if (title === "Implementation") { + assert.ok( + cur_frm.get_field("total_billable_amount").get_value() == 40, + "Billable amount for Implementation task is correctly calculated" + ); + assert.ok( + cur_frm.get_field("total_costing_amount").get_value() == 32, + "Costing amount for Implementation task is correctly calculated" + ); } - if(title=== 'Testing') - { - assert.ok(cur_frm.get_field('total_billable_amount').get_value()==60, - 'Billable amount for Testing task correctly calculated'); - assert.ok(cur_frm.get_field('total_costing_amount').get_value()==50, - 'Costing amount for Testing task is correctly calculated'); + if (title === "Testing") { + assert.ok( + cur_frm.get_field("total_billable_amount").get_value() == 60, + "Billable amount for Testing task correctly calculated" + ); + assert.ok( + cur_frm.get_field("total_costing_amount").get_value() == 50, + "Costing amount for Testing task is correctly calculated" + ); } }, ]); @@ -55,37 +65,39 @@ QUnit.test("test project", function(assert) { frappe.run_serially([ () => { // Creating project with task - return frappe.tests.make('Project', [ - { project_name: 'Test App'}, - { expected_start_date: '2017-07-22'}, - { expected_end_date: '2017-09-22'}, - { estimated_costing: '10,000.00'}, - { tasks:[ - [ - {title: 'Documentation'}, - {start_date: '2017-07-24'}, - {end_date: '2017-07-31'}, - {description: 'To make a proper documentation defining requirements etc'} + return frappe.tests.make("Project", [ + { project_name: "Test App" }, + { expected_start_date: "2017-07-22" }, + { expected_end_date: "2017-09-22" }, + { estimated_costing: "10,000.00" }, + { + tasks: [ + [ + { title: "Documentation" }, + { start_date: "2017-07-24" }, + { end_date: "2017-07-31" }, + { description: "To make a proper documentation defining requirements etc" }, + ], + [ + { title: "Implementation" }, + { start_date: "2017-08-01" }, + { end_date: "2017-08-01" }, + { description: "Writing algorithms and to code the functionalities" }, + ], + [ + { title: "Testing" }, + { start_date: "2017-08-01" }, + { end_date: "2017-08-15" }, + { description: "To make the test cases and test the functionalities" }, + ], ], - [ - {title: 'Implementation'}, - {start_date: '2017-08-01'}, - {end_date: '2017-08-01'}, - {description: 'Writing algorithms and to code the functionalities'} - ], - [ - {title: 'Testing'}, - {start_date: '2017-08-01'}, - {end_date: '2017-08-15'}, - {description: 'To make the test cases and test the functionalities'} - ] - ]} + }, ]); }, // Creating Timesheet with different tasks - () => timesheet(task_title[0],'2017-07-24 13:00:00','2017-07-24 13:00:00',10,8), - () => timesheet(task_title[1],'2017-07-25 13:00:00','2017-07-25 15:00:00',20,16), - () => timesheet(task_title[2],'2017-07-26 13:00:00','2017-07-26 15:00:00',30,25), - () => done() + () => timesheet(task_title[0], "2017-07-24 13:00:00", "2017-07-24 13:00:00", 10, 8), + () => timesheet(task_title[1], "2017-07-25 13:00:00", "2017-07-25 15:00:00", 20, 16), + () => timesheet(task_title[2], "2017-07-26 13:00:00", "2017-07-26 15:00:00", 30, 25), + () => done(), ]); }); diff --git a/erpnext/projects/doctype/project_template/project_template.js b/erpnext/projects/doctype/project_template/project_template.js index 3d3c15c6e05..a2d008af027 100644 --- a/erpnext/projects/doctype/project_template/project_template.js +++ b/erpnext/projects/doctype/project_template/project_template.js @@ -1,7 +1,7 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Project Template', { +frappe.ui.form.on("Project Template", { // refresh: function(frm) { // } @@ -9,19 +9,19 @@ frappe.ui.form.on('Project Template', { frm.set_query("task", "tasks", function () { return { filters: { - "is_template": 1 - } + is_template: 1, + }, }; }); - } + }, }); -frappe.ui.form.on('Project Template Task', { +frappe.ui.form.on("Project Template Task", { task: function (frm, cdt, cdn) { var row = locals[cdt][cdn]; frappe.db.get_value("Task", row.task, "subject", (value) => { row.subject = value.subject; refresh_field("tasks"); }); - } + }, }); diff --git a/erpnext/projects/doctype/project_type/project_type.js b/erpnext/projects/doctype/project_type/project_type.js index e3dda5eccc5..8506c787ece 100644 --- a/erpnext/projects/doctype/project_type/project_type.js +++ b/erpnext/projects/doctype/project_type/project_type.js @@ -1,6 +1,4 @@ // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Project Type', { - -}); +frappe.ui.form.on("Project Type", {}); diff --git a/erpnext/projects/doctype/project_update/project_update.js b/erpnext/projects/doctype/project_update/project_update.js index 990c1afd9ab..4e3f0fa97d2 100644 --- a/erpnext/projects/doctype/project_update/project_update.js +++ b/erpnext/projects/doctype/project_update/project_update.js @@ -1,10 +1,8 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Project Update', { - refresh: function() { - - }, +frappe.ui.form.on("Project Update", { + refresh: function () {}, onload: function (frm) { frm.set_value("naming_series", "UPDATE-.project.-.YY.MM.DD.-.####"); @@ -13,5 +11,5 @@ frappe.ui.form.on('Project Update', { validate: function (frm) { frm.set_value("time", frappe.datetime.now_time()); frm.set_value("date", frappe.datetime.nowdate()); - } + }, }); diff --git a/erpnext/projects/doctype/projects_settings/projects_settings.js b/erpnext/projects/doctype/projects_settings/projects_settings.js index 9902b834920..08f50a6d989 100644 --- a/erpnext/projects/doctype/projects_settings/projects_settings.js +++ b/erpnext/projects/doctype/projects_settings/projects_settings.js @@ -1,8 +1,6 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Projects Settings', { - refresh: function(frm) { - - } +frappe.ui.form.on("Projects Settings", { + refresh: function (frm) {}, }); diff --git a/erpnext/projects/doctype/task/task.js b/erpnext/projects/doctype/task/task.js index 3cd92ee719d..c56c998a518 100644 --- a/erpnext/projects/doctype/task/task.js +++ b/erpnext/projects/doctype/task/task.js @@ -6,33 +6,34 @@ frappe.provide("erpnext.projects"); frappe.ui.form.on("Task", { setup: function (frm) { frm.make_methods = { - 'Timesheet': () => frappe.model.open_mapped_doc({ - method: 'erpnext.projects.doctype.task.task.make_timesheet', - frm: frm - }) - } + Timesheet: () => + frappe.model.open_mapped_doc({ + method: "erpnext.projects.doctype.task.task.make_timesheet", + frm: frm, + }), + }; }, onload: function (frm) { frm.set_query("task", "depends_on", function () { let filters = { - name: ["!=", frm.doc.name] + name: ["!=", frm.doc.name], }; if (frm.doc.project) filters["project"] = frm.doc.project; return { - filters: filters + filters: filters, }; - }) + }); frm.set_query("parent_task", function () { let filters = { - "is_group": 1, - "name": ["!=", frm.doc.name] + is_group: 1, + name: ["!=", frm.doc.name], }; if (frm.doc.project) filters["project"] = frm.doc.project; return { - filters: filters - } + filters: filters, + }; }); }, @@ -40,22 +41,22 @@ frappe.ui.form.on("Task", { frappe.call({ method: "erpnext.projects.doctype.task.task.check_if_child_exists", args: { - name: frm.doc.name + name: frm.doc.name, }, callback: function (r) { if (r.message.length > 0) { - let message = __('Cannot convert Task to non-group because the following child Tasks exist: {0}.', + let message = __( + "Cannot convert Task to non-group because the following child Tasks exist: {0}.", [r.message.join(", ")] ); frappe.msgprint(message); frm.reload_doc(); } - } - }) + }, + }); }, validate: function (frm) { - frm.doc.project && frappe.model.remove_from_locals("Project", - frm.doc.project); - } + frm.doc.project && frappe.model.remove_from_locals("Project", frm.doc.project); + }, }); diff --git a/erpnext/projects/doctype/task/task_calendar.js b/erpnext/projects/doctype/task/task_calendar.js index 49dbb76a1a5..768eb15b157 100644 --- a/erpnext/projects/doctype/task/task_calendar.js +++ b/erpnext/projects/doctype/task/task_calendar.js @@ -3,21 +3,21 @@ frappe.views.calendar["Task"] = { field_map: { - "start": "exp_start_date", - "end": "exp_end_date", - "id": "name", - "title": "subject", - "allDay": "allDay", - "progress": "progress" + start: "exp_start_date", + end: "exp_end_date", + id: "name", + title: "subject", + allDay: "allDay", + progress: "progress", }, gantt: true, filters: [ { - "fieldtype": "Link", - "fieldname": "project", - "options": "Project", - "label": __("Project") - } + fieldtype: "Link", + fieldname: "project", + options: "Project", + label: __("Project"), + }, ], - get_events_method: "frappe.desk.calendar.get_events" -} + get_events_method: "frappe.desk.calendar.get_events", +}; diff --git a/erpnext/projects/doctype/task/task_list.js b/erpnext/projects/doctype/task/task_list.js index 5ab8bae2e1d..17b0ed2c7fa 100644 --- a/erpnext/projects/doctype/task/task_list.js +++ b/erpnext/projects/doctype/task/task_list.js @@ -1,28 +1,36 @@ -frappe.listview_settings['Task'] = { - add_fields: ["project", "status", "priority", "exp_start_date", - "exp_end_date", "subject", "progress", "depends_on_tasks"], +frappe.listview_settings["Task"] = { + add_fields: [ + "project", + "status", + "priority", + "exp_start_date", + "exp_end_date", + "subject", + "progress", + "depends_on_tasks", + ], filters: [["status", "=", "Open"]], - onload: function(listview) { + onload: function (listview) { var method = "erpnext.projects.doctype.task.task.set_multiple_status"; - listview.page.add_menu_item(__("Set as Open"), function() { - listview.call_for_selected_items(method, {"status": "Open"}); + listview.page.add_menu_item(__("Set as Open"), function () { + listview.call_for_selected_items(method, { status: "Open" }); }); - listview.page.add_menu_item(__("Set as Completed"), function() { - listview.call_for_selected_items(method, {"status": "Completed"}); + listview.page.add_menu_item(__("Set as Completed"), function () { + listview.call_for_selected_items(method, { status: "Completed" }); }); }, - get_indicator: function(doc) { + get_indicator: function (doc) { var colors = { - "Open": "orange", - "Overdue": "red", + Open: "orange", + Overdue: "red", "Pending Review": "orange", - "Working": "orange", - "Completed": "green", - "Cancelled": "dark grey", - "Template": "blue" - } + Working: "orange", + Completed: "green", + Cancelled: "dark grey", + Template: "blue", + }; return [__(doc.status), colors[doc.status], "status,=," + doc.status]; }, gantt_custom_popup_html: function (ganttobj, task) { diff --git a/erpnext/projects/doctype/task/task_tree.js b/erpnext/projects/doctype/task/task_tree.js index 9ebfcdd180c..fba1b309260 100644 --- a/erpnext/projects/doctype/task/task_tree.js +++ b/erpnext/projects/doctype/task/task_tree.js @@ -1,84 +1,88 @@ frappe.provide("frappe.treeview_settings"); -frappe.treeview_settings['Task'] = { +frappe.treeview_settings["Task"] = { get_tree_nodes: "erpnext.projects.doctype.task.task.get_children", add_tree_node: "erpnext.projects.doctype.task.task.add_node", filters: [ { fieldname: "project", - fieldtype:"Link", + fieldtype: "Link", options: "Project", label: __("Project"), }, { fieldname: "task", - fieldtype:"Link", + fieldtype: "Link", options: "Task", label: __("Task"), - get_query: function() { - var me = frappe.treeview_settings['Task']; + get_query: function () { + var me = frappe.treeview_settings["Task"]; var project = me.page.fields_dict.project.get_value(); - var args = [["Task", 'is_group', '=', 1]]; - if(project){ - args.push(["Task", 'project', "=", project]); + var args = [["Task", "is_group", "=", 1]]; + if (project) { + args.push(["Task", "project", "=", project]); } return { - filters: args + filters: args, }; - } - } + }, + }, ], breadcrumb: "Projects", get_tree_root: false, root_label: "All Tasks", ignore_fields: ["parent_task"], - onload: function(me) { - frappe.treeview_settings['Task'].page = {}; - $.extend(frappe.treeview_settings['Task'].page, me.page); + onload: function (me) { + frappe.treeview_settings["Task"].page = {}; + $.extend(frappe.treeview_settings["Task"].page, me.page); me.make_tree(); }, toolbar: [ { - label:__("Add Multiple"), - condition: function(node) { + label: __("Add Multiple"), + condition: function (node) { return node.expandable; }, - click: function(node) { + click: function (node) { this.data = []; const dialog = new frappe.ui.Dialog({ title: __("Add Multiple Tasks"), fields: [ { - fieldname: "multiple_tasks", fieldtype: "Table", - in_place_edit: true, data: this.data, + fieldname: "multiple_tasks", + fieldtype: "Table", + in_place_edit: true, + data: this.data, get_data: () => { return this.data; }, - fields: [{ - fieldtype:'Data', - fieldname:"subject", - in_list_view: 1, - reqd: 1, - label: __("Subject") - }] + fields: [ + { + fieldtype: "Data", + fieldname: "subject", + in_list_view: 1, + reqd: 1, + label: __("Subject"), + }, + ], }, ], - primary_action: function() { + primary_action: function () { dialog.hide(); return frappe.call({ method: "erpnext.projects.doctype.task.task.add_multiple_tasks", args: { data: dialog.get_values()["multiple_tasks"], - parent: node.data.value + parent: node.data.value, }, - callback: function() { } + callback: function () {}, }); }, - primary_action_label: __('Create') + primary_action_label: __("Create"), }); dialog.show(); - } - } + }, + }, ], - extend_toolbar: true + extend_toolbar: true, }; diff --git a/erpnext/projects/doctype/task_type/task_type.js b/erpnext/projects/doctype/task_type/task_type.js index c1be5da4f64..9c6176dbf55 100644 --- a/erpnext/projects/doctype/task_type/task_type.js +++ b/erpnext/projects/doctype/task_type/task_type.js @@ -1,8 +1,7 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Task Type', { +frappe.ui.form.on("Task Type", { // refresh: function(frm) { - // } }); diff --git a/erpnext/projects/doctype/timesheet/timesheet.js b/erpnext/projects/doctype/timesheet/timesheet.js index d1d07a79d67..0d03d6085d9 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.js +++ b/erpnext/projects/doctype/timesheet/timesheet.js @@ -2,39 +2,39 @@ // License: GNU General Public License v3. See license.txt frappe.ui.form.on("Timesheet", { - setup: function(frm) { + setup: function (frm) { frappe.require("/assets/erpnext/js/projects/timer.js"); - frm.ignore_doctypes_on_cancel_all = ['Sales Invoice']; + frm.ignore_doctypes_on_cancel_all = ["Sales Invoice"]; - frm.fields_dict.employee.get_query = function() { + frm.fields_dict.employee.get_query = function () { return { - filters:{ - 'status': 'Active' - } + filters: { + status: "Active", + }, }; }; - frm.fields_dict['time_logs'].grid.get_field('task').get_query = function(frm, cdt, cdn) { + frm.fields_dict["time_logs"].grid.get_field("task").get_query = function (frm, cdt, cdn) { var child = locals[cdt][cdn]; - return{ + return { filters: { - 'project': child.project, - 'status': ["!=", "Cancelled"] - } + project: child.project, + status: ["!=", "Cancelled"], + }, }; }; - frm.fields_dict['time_logs'].grid.get_field('project').get_query = function() { - return{ + frm.fields_dict["time_logs"].grid.get_field("project").get_query = function () { + return { filters: { - 'company': frm.doc.company - } + company: frm.doc.company, + }, }; }; }, - onload: function(frm) { + onload: function (frm) { if (frm.doc.__islocal && frm.doc.time_logs) { calculate_time_and_amount(frm); } @@ -44,33 +44,32 @@ frappe.ui.form.on("Timesheet", { } }, - refresh: function(frm) { + refresh: function (frm) { if (frm.doc.docstatus == 1) { if ( - (frm.doc.per_billed < 100) - && (frm.doc.total_billable_hours) - && (frm.doc.total_billable_hours > frm.doc.total_billed_hours) + frm.doc.per_billed < 100 && + frm.doc.total_billable_hours && + frm.doc.total_billable_hours > frm.doc.total_billed_hours ) { - frm.add_custom_button(__("Create Sales Invoice"), function() { + frm.add_custom_button(__("Create Sales Invoice"), function () { frm.trigger("make_invoice"); }); } } if (frm.doc.docstatus < 1) { - - 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'; + 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"; } }); - frm.add_custom_button(__(button), function() { + frm.add_custom_button(__(button), function () { var flag = true; - $.each(frm.doc.time_logs || [], function(i, row) { + $.each(frm.doc.time_logs || [], function (i, row) { // Fetch the row for which from_time is not present - if (flag && row.activity_type && !row.from_time){ + if (flag && row.activity_type && !row.from_time) { erpnext.timesheet.timer(frm, row); row.from_time = frappe.datetime.now_datetime(); frm.refresh_fields("time_logs"); @@ -79,7 +78,10 @@ frappe.ui.form.on("Timesheet", { } // Fetch the row for timer where activity is not completed and from_time is before now_time if (flag && row.from_time <= frappe.datetime.now_datetime() && !row.completed) { - let timestamp = moment(frappe.datetime.now_datetime()).diff(moment(row.from_time),"seconds"); + let timestamp = moment(frappe.datetime.now_datetime()).diff( + moment(row.from_time), + "seconds" + ); erpnext.timesheet.timer(frm, row, timestamp); flag = false; } @@ -90,143 +92,168 @@ frappe.ui.form.on("Timesheet", { } }).addClass("btn-primary"); } - if(frm.doc.per_billed > 0) { + if (frm.doc.per_billed > 0) { frm.fields_dict["time_logs"].grid.toggle_enable("billing_hours", false); frm.fields_dict["time_logs"].grid.toggle_enable("is_billable", false); } let filters = { - "status": "Open" + status: "Open", }; if (frm.doc.customer) { filters["customer"] = frm.doc.customer; } - frm.set_query('parent_project', function(doc) { + frm.set_query("parent_project", function (doc) { return { - filters: filters + filters: filters, }; }); - frm.trigger('setup_filters'); - frm.trigger('set_dynamic_field_label'); + frm.trigger("setup_filters"); + frm.trigger("set_dynamic_field_label"); }, - customer: function(frm) { - frm.set_query('project', 'time_logs', function(doc) { + customer: function (frm) { + frm.set_query("project", "time_logs", function (doc) { return { filters: { - "customer": doc.customer - } + customer: doc.customer, + }, }; }); frm.refresh(); }, - currency: function(frm) { - let base_currency = frappe.defaults.get_global_default('currency'); - if (frm.doc.currency && (base_currency != frm.doc.currency)) { + currency: function (frm) { + let base_currency = frappe.defaults.get_global_default("currency"); + if (frm.doc.currency && base_currency != frm.doc.currency) { frappe.call({ method: "erpnext.setup.utils.get_exchange_rate", args: { from_currency: frm.doc.currency, - to_currency: base_currency + to_currency: base_currency, }, - callback: function(r) { + callback: function (r) { if (r.message) { - frm.set_value('exchange_rate', flt(r.message)); - frm.set_df_property("exchange_rate", "description", "1 " + frm.doc.currency + " = [?] " + base_currency); + frm.set_value("exchange_rate", flt(r.message)); + frm.set_df_property( + "exchange_rate", + "description", + "1 " + frm.doc.currency + " = [?] " + base_currency + ); } - } + }, }); } - frm.trigger('set_dynamic_field_label'); + frm.trigger("set_dynamic_field_label"); }, - exchange_rate: function(frm) { - $.each(frm.doc.time_logs, function(i, d) { + exchange_rate: function (frm) { + $.each(frm.doc.time_logs, function (i, d) { calculate_billing_costing_amount(frm, d.doctype, d.name); }); calculate_time_and_amount(frm); }, - set_dynamic_field_label: function(frm) { - let base_currency = frappe.defaults.get_global_default('currency'); - frm.set_currency_labels(["base_total_costing_amount", "base_total_billable_amount", "base_total_billed_amount"], base_currency); - frm.set_currency_labels(["total_costing_amount", "total_billable_amount", "total_billed_amount"], frm.doc.currency); + set_dynamic_field_label: function (frm) { + let base_currency = frappe.defaults.get_global_default("currency"); + frm.set_currency_labels( + ["base_total_costing_amount", "base_total_billable_amount", "base_total_billed_amount"], + base_currency + ); + frm.set_currency_labels( + ["total_costing_amount", "total_billable_amount", "total_billed_amount"], + frm.doc.currency + ); - frm.toggle_display(["base_total_costing_amount", "base_total_billable_amount", "base_total_billed_amount"], - frm.doc.currency != base_currency); + frm.toggle_display( + ["base_total_costing_amount", "base_total_billable_amount", "base_total_billed_amount"], + frm.doc.currency != base_currency + ); if (frm.doc.time_logs.length > 0) { - frm.set_currency_labels(["base_billing_rate", "base_billing_amount", "base_costing_rate", "base_costing_amount"], base_currency, "time_logs"); - frm.set_currency_labels(["billing_rate", "billing_amount", "costing_rate", "costing_amount"], frm.doc.currency, "time_logs"); + frm.set_currency_labels( + ["base_billing_rate", "base_billing_amount", "base_costing_rate", "base_costing_amount"], + base_currency, + "time_logs" + ); + frm.set_currency_labels( + ["billing_rate", "billing_amount", "costing_rate", "costing_amount"], + frm.doc.currency, + "time_logs" + ); let time_logs_grid = frm.fields_dict.time_logs.grid; - $.each(["base_billing_rate", "base_billing_amount", "base_costing_rate", "base_costing_amount"], function(i, d) { - if (frappe.meta.get_docfield(time_logs_grid.doctype, d)) - time_logs_grid.set_column_disp(d, frm.doc.currency != base_currency); - }); + $.each( + ["base_billing_rate", "base_billing_amount", "base_costing_rate", "base_costing_amount"], + function (i, d) { + if (frappe.meta.get_docfield(time_logs_grid.doctype, d)) + time_logs_grid.set_column_disp(d, frm.doc.currency != base_currency); + } + ); } frm.refresh_fields(); }, - make_invoice: function(frm) { - let fields = [{ - "fieldtype": "Link", - "label": __("Item Code"), - "fieldname": "item_code", - "options": "Item" - }]; + make_invoice: function (frm) { + let fields = [ + { + fieldtype: "Link", + label: __("Item Code"), + fieldname: "item_code", + options: "Item", + }, + ]; if (!frm.doc.customer) { fields.push({ - "fieldtype": "Link", - "label": __("Customer"), - "fieldname": "customer", - "options": "Customer", - "default": frm.doc.customer + fieldtype: "Link", + label: __("Customer"), + fieldname: "customer", + options: "Customer", + default: frm.doc.customer, }); } let dialog = new frappe.ui.Dialog({ title: __("Create Sales Invoice"), - fields: fields + fields: fields, }); - dialog.set_primary_action(__('Create Sales Invoice'), () => { + dialog.set_primary_action(__("Create Sales Invoice"), () => { var args = dialog.get_values(); - if(!args) return; + if (!args) return; dialog.hide(); return frappe.call({ type: "GET", method: "erpnext.projects.doctype.timesheet.timesheet.make_sales_invoice", args: { - "source_name": frm.doc.name, - "item_code": args.item_code, - "customer": frm.doc.customer || args.customer, - "currency": frm.doc.currency + source_name: frm.doc.name, + item_code: args.item_code, + customer: frm.doc.customer || args.customer, + currency: frm.doc.currency, }, freeze: true, - callback: function(r) { - if(!r.exc) { + callback: function (r) { + if (!r.exc) { frappe.model.sync(r.message); frappe.set_route("Form", r.message.doctype, r.message.name); } - } + }, }); }); dialog.show(); }, - parent_project: function(frm) { + parent_project: function (frm) { set_project_in_timelog(frm); - } + }, }); frappe.ui.form.on("Timesheet Detail", { - time_logs_remove: function(frm) { + time_logs_remove: function (frm) { calculate_time_and_amount(frm); }, @@ -239,47 +266,47 @@ frappe.ui.form.on("Timesheet Detail", { } }, - from_time: function(frm, cdt, cdn) { + from_time: function (frm, cdt, cdn) { calculate_end_time(frm, cdt, cdn); }, - to_time: function(frm, cdt, cdn) { + to_time: function (frm, cdt, cdn) { var child = locals[cdt][cdn]; - if(frm._setting_hours) return; + if (frm._setting_hours) return; var hours = moment(child.to_time).diff(moment(child.from_time), "seconds") / 3600; frappe.model.set_value(cdt, cdn, "hours", hours); }, - time_logs_add: function(frm, cdt, cdn) { - if(frm.doc.parent_project) { - frappe.model.set_value(cdt, cdn, 'project', frm.doc.parent_project); + time_logs_add: function (frm, cdt, cdn) { + if (frm.doc.parent_project) { + frappe.model.set_value(cdt, cdn, "project", frm.doc.parent_project); } }, - hours: function(frm, cdt, cdn) { + hours: function (frm, cdt, cdn) { calculate_end_time(frm, cdt, cdn); calculate_billing_costing_amount(frm, cdt, cdn); calculate_time_and_amount(frm); }, - billing_hours: function(frm, cdt, cdn) { + billing_hours: function (frm, cdt, cdn) { calculate_billing_costing_amount(frm, cdt, cdn); calculate_time_and_amount(frm); }, - billing_rate: function(frm, cdt, cdn) { + billing_rate: function (frm, cdt, cdn) { calculate_billing_costing_amount(frm, cdt, cdn); calculate_time_and_amount(frm); }, - costing_rate: function(frm, cdt, cdn) { + costing_rate: function (frm, cdt, cdn) { calculate_billing_costing_amount(frm, cdt, cdn); calculate_time_and_amount(frm); }, - is_billable: function(frm, cdt, cdn) { + is_billable: function (frm, cdt, cdn) { update_billing_hours(frm, cdt, cdn); update_time_rates(frm, cdt, cdn); calculate_billing_costing_amount(frm, cdt, cdn); @@ -294,7 +321,7 @@ frappe.ui.form.on("Timesheet Detail", { args: { employee: frm.doc.employee, activity_type: frm.selected_doc.activity_type, - currency: frm.doc.currency + currency: frm.doc.currency, }, callback: function (r) { if (r.message) { @@ -302,72 +329,71 @@ frappe.ui.form.on("Timesheet Detail", { frappe.model.set_value(cdt, cdn, "costing_rate", r.message["costing_rate"]); calculate_billing_costing_amount(frm, cdt, cdn); } - } + }, }); - } + }, }); -var calculate_end_time = function(frm, cdt, cdn) { +var calculate_end_time = function (frm, cdt, cdn) { let child = locals[cdt][cdn]; - if(!child.from_time) { + if (!child.from_time) { // if from_time value is not available then set the current datetime frappe.model.set_value(cdt, cdn, "from_time", frappe.datetime.get_datetime_as_string()); } let d = moment(child.from_time); - if(child.hours) { + if (child.hours) { d.add(child.hours, "hours"); frm._setting_hours = true; - frappe.model.set_value(cdt, cdn, "to_time", - d.format(frappe.defaultDatetimeFormat)).then(() => { + frappe.model.set_value(cdt, cdn, "to_time", d.format(frappe.defaultDatetimeFormat)).then(() => { frm._setting_hours = false; }); } }; -var update_billing_hours = function(frm, cdt, cdn) { +var update_billing_hours = function (frm, cdt, cdn) { let child = frappe.get_doc(cdt, cdn); if (!child.is_billable) { - frappe.model.set_value(cdt, cdn, 'billing_hours', 0.0); + frappe.model.set_value(cdt, cdn, "billing_hours", 0.0); } else { // bill all hours by default frappe.model.set_value(cdt, cdn, "billing_hours", child.hours); } }; -var update_time_rates = function(frm, cdt, cdn) { +var update_time_rates = function (frm, cdt, cdn) { let child = frappe.get_doc(cdt, cdn); if (!child.is_billable) { - frappe.model.set_value(cdt, cdn, 'billing_rate', 0.0); + frappe.model.set_value(cdt, cdn, "billing_rate", 0.0); } }; -var calculate_billing_costing_amount = function(frm, cdt, cdn) { +var calculate_billing_costing_amount = function (frm, cdt, cdn) { let row = frappe.get_doc(cdt, cdn); let billing_amount = 0.0; let base_billing_amount = 0.0; let exchange_rate = flt(frm.doc.exchange_rate); - frappe.model.set_value(cdt, cdn, 'base_billing_rate', flt(row.billing_rate) * exchange_rate); - frappe.model.set_value(cdt, cdn, 'base_costing_rate', flt(row.costing_rate) * exchange_rate); + frappe.model.set_value(cdt, cdn, "base_billing_rate", flt(row.billing_rate) * exchange_rate); + frappe.model.set_value(cdt, cdn, "base_costing_rate", flt(row.costing_rate) * exchange_rate); if (row.billing_hours && row.is_billable) { base_billing_amount = flt(row.billing_hours) * flt(row.base_billing_rate); billing_amount = flt(row.billing_hours) * flt(row.billing_rate); } - frappe.model.set_value(cdt, cdn, 'base_billing_amount', base_billing_amount); - frappe.model.set_value(cdt, cdn, 'base_costing_amount', flt(row.base_costing_rate) * flt(row.hours)); - frappe.model.set_value(cdt, cdn, 'billing_amount', billing_amount); - frappe.model.set_value(cdt, cdn, 'costing_amount', flt(row.costing_rate) * flt(row.hours)); + frappe.model.set_value(cdt, cdn, "base_billing_amount", base_billing_amount); + frappe.model.set_value(cdt, cdn, "base_costing_amount", flt(row.base_costing_rate) * flt(row.hours)); + frappe.model.set_value(cdt, cdn, "billing_amount", billing_amount); + frappe.model.set_value(cdt, cdn, "costing_amount", flt(row.costing_rate) * flt(row.hours)); }; -var calculate_time_and_amount = function(frm) { +var calculate_time_and_amount = function (frm) { let tl = frm.doc.time_logs || []; let total_working_hr = 0; let total_billing_hr = 0; let total_billable_amount = 0; let total_costing_amount = 0; - for(var i=0; i { + const fields = ["name", "company"]; + frappe.db.get_value("Employee", options, fields).then(({ message }) => { if (message) { // there is an employee with the currently logged in user_id frm.set_value("employee", message.name); @@ -399,8 +425,8 @@ const set_employee_and_company = function(frm) { }; function set_project_in_timelog(frm) { - if(frm.doc.parent_project) { - $.each(frm.doc.time_logs || [], function(i, item) { + if (frm.doc.parent_project) { + $.each(frm.doc.time_logs || [], function (i, item) { frappe.model.set_value(item.doctype, item.name, "project", frm.doc.parent_project); }); } diff --git a/erpnext/projects/doctype/timesheet/timesheet_calendar.js b/erpnext/projects/doctype/timesheet/timesheet_calendar.js index 80967ede1ce..27992a107cb 100644 --- a/erpnext/projects/doctype/timesheet/timesheet_calendar.js +++ b/erpnext/projects/doctype/timesheet/timesheet_calendar.js @@ -1,32 +1,32 @@ frappe.views.calendar["Timesheet"] = { field_map: { - "start": "start_date", - "end": "end_date", - "name": "parent", - "id": "name", - "allDay": "allDay", - "child_name": "name", - "title": "title" + start: "start_date", + end: "end_date", + name: "parent", + id: "name", + allDay: "allDay", + child_name: "name", + title: "title", }, style_map: { - "0": "info", - "1": "standard", - "2": "danger" + 0: "info", + 1: "standard", + 2: "danger", }, gantt: true, filters: [ { - "fieldtype": "Link", - "fieldname": "project", - "options": "Project", - "label": __("Project") + fieldtype: "Link", + fieldname: "project", + options: "Project", + label: __("Project"), }, { - "fieldtype": "Link", - "fieldname": "employee", - "options": "Employee", - "label": __("Employee") - } + fieldtype: "Link", + fieldname: "employee", + options: "Employee", + label: __("Employee"), + }, ], - get_events_method: "erpnext.projects.doctype.timesheet.timesheet.get_events" -} + get_events_method: "erpnext.projects.doctype.timesheet.timesheet.get_events", +}; diff --git a/erpnext/projects/doctype/timesheet/timesheet_list.js b/erpnext/projects/doctype/timesheet/timesheet_list.js index b59fdc96fe8..0de568ce589 100644 --- a/erpnext/projects/doctype/timesheet/timesheet_list.js +++ b/erpnext/projects/doctype/timesheet/timesheet_list.js @@ -1,16 +1,16 @@ -frappe.listview_settings['Timesheet'] = { +frappe.listview_settings["Timesheet"] = { add_fields: ["status", "total_hours", "start_date", "end_date"], - get_indicator: function(doc) { - if (doc.status== "Billed") { - return [__("Billed"), "green", "status,=," + "Billed"] + get_indicator: function (doc) { + if (doc.status == "Billed") { + return [__("Billed"), "green", "status,=," + "Billed"]; } - if (doc.status== "Payslip") { - return [__("Payslip"), "green", "status,=," + "Payslip"] + if (doc.status == "Payslip") { + return [__("Payslip"), "green", "status,=," + "Payslip"]; } - if (doc.status== "Completed") { - return [__("Completed"), "green", "status,=," + "Completed"] + if (doc.status == "Completed") { + return [__("Completed"), "green", "status,=," + "Completed"]; } - } + }, }; diff --git a/erpnext/projects/report/daily_timesheet_summary/daily_timesheet_summary.js b/erpnext/projects/report/daily_timesheet_summary/daily_timesheet_summary.js index 93cb9402fa8..60525a1ae41 100644 --- a/erpnext/projects/report/daily_timesheet_summary/daily_timesheet_summary.js +++ b/erpnext/projects/report/daily_timesheet_summary/daily_timesheet_summary.js @@ -2,18 +2,18 @@ // For license information, please see license.txt frappe.query_reports["Daily Timesheet Summary"] = { - "filters": [ + filters: [ { - "fieldname":"from_date", - "label": __("From Date"), - "fieldtype": "Date", - "default": frappe.datetime.get_today() + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.datetime.get_today(), }, { - "fieldname":"to_date", - "label": __("To Date"), - "fieldtype": "Date", - "default": frappe.datetime.get_today() + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.datetime.get_today(), }, - ] -} + ], +}; diff --git a/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.js b/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.js index 5aa44c0a8c9..983eb448cb5 100644 --- a/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.js +++ b/erpnext/projects/report/delayed_tasks_summary/delayed_tasks_summary.js @@ -3,31 +3,31 @@ /* eslint-disable */ frappe.query_reports["Delayed Tasks Summary"] = { - "filters": [ + filters: [ { - "fieldname": "from_date", - "label": __("From Date"), - "fieldtype": "Date" + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", }, { - "fieldname": "to_date", - "label": __("To Date"), - "fieldtype": "Date" + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", }, { - "fieldname": "priority", - "label": __("Priority"), - "fieldtype": "Select", - "options": ["", "Low", "Medium", "High", "Urgent"] + fieldname: "priority", + label: __("Priority"), + fieldtype: "Select", + options: ["", "Low", "Medium", "High", "Urgent"], }, { - "fieldname": "status", - "label": __("Status"), - "fieldtype": "Select", - "options": ["", "Open", "Working","Pending Review","Overdue","Completed"] + fieldname: "status", + label: __("Status"), + fieldtype: "Select", + options: ["", "Open", "Working", "Pending Review", "Overdue", "Completed"], }, ], - "formatter": function(value, row, column, data, default_formatter) { + formatter: function (value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); if (column.id == "delay") { if (data["delay"] > 0) { @@ -36,6 +36,6 @@ frappe.query_reports["Delayed Tasks Summary"] = { value = `

    ${value}

    `; } } - return value - } + return value; + }, }; diff --git a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.js b/erpnext/projects/report/employee_billing_summary/employee_billing_summary.js index 9c904c57872..4bdbf659a5c 100644 --- a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.js +++ b/erpnext/projects/report/employee_billing_summary/employee_billing_summary.js @@ -3,32 +3,32 @@ /* eslint-disable */ frappe.query_reports["Employee Billing Summary"] = { - "filters": [ + filters: [ { fieldname: "employee", label: __("Employee"), fieldtype: "Link", options: "Employee", - reqd: 1 + reqd: 1, }, { - fieldname:"from_date", + fieldname: "from_date", label: __("From Date"), fieldtype: "Date", default: frappe.datetime.add_months(frappe.datetime.month_start(), -1), - reqd: 1 + reqd: 1, }, { - fieldname:"to_date", + fieldname: "to_date", label: __("To Date"), fieldtype: "Date", default: frappe.datetime.add_days(frappe.datetime.month_start(), -1), - reqd: 1 + reqd: 1, }, { - fieldname:"include_draft_timesheets", + fieldname: "include_draft_timesheets", label: __("Include Timesheets in Draft Status"), fieldtype: "Check", }, - ] -} + ], +}; diff --git a/erpnext/projects/report/project_billing_summary/project_billing_summary.js b/erpnext/projects/report/project_billing_summary/project_billing_summary.js index 6a6f3677e3f..86c54e519a5 100644 --- a/erpnext/projects/report/project_billing_summary/project_billing_summary.js +++ b/erpnext/projects/report/project_billing_summary/project_billing_summary.js @@ -3,32 +3,32 @@ /* eslint-disable */ frappe.query_reports["Project Billing Summary"] = { - "filters": [ + filters: [ { fieldname: "project", label: __("Project"), fieldtype: "Link", options: "Project", - reqd: 1 + reqd: 1, }, { - fieldname:"from_date", + fieldname: "from_date", label: __("From Date"), fieldtype: "Date", default: frappe.datetime.add_months(frappe.datetime.month_start(), -1), - reqd: 1 + reqd: 1, }, { - fieldname:"to_date", + fieldname: "to_date", label: __("To Date"), fieldtype: "Date", - default: frappe.datetime.add_days(frappe.datetime.month_start(),-1), - reqd: 1 + default: frappe.datetime.add_days(frappe.datetime.month_start(), -1), + reqd: 1, }, { - fieldname:"include_draft_timesheets", + fieldname: "include_draft_timesheets", label: __("Include Timesheets in Draft Status"), fieldtype: "Check", }, - ] -} + ], +}; diff --git a/erpnext/projects/report/project_summary/project_summary.js b/erpnext/projects/report/project_summary/project_summary.js index 414b7b206a1..b5bf4bedd1b 100644 --- a/erpnext/projects/report/project_summary/project_summary.js +++ b/erpnext/projects/report/project_summary/project_summary.js @@ -3,40 +3,40 @@ /* eslint-disable */ frappe.query_reports["Project Summary"] = { - "filters": [ + filters: [ { - "fieldname": "company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company"), - "reqd": 1 + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, }, { - "fieldname": "is_active", - "label": __("Is Active"), - "fieldtype": "Select", - "options": "\nYes\nNo", - "default": "Yes", + fieldname: "is_active", + label: __("Is Active"), + fieldtype: "Select", + options: "\nYes\nNo", + default: "Yes", }, { - "fieldname": "status", - "label": __("Status"), - "fieldtype": "Select", - "options": "\nOpen\nCompleted\nCancelled", - "default": "Open" + fieldname: "status", + label: __("Status"), + fieldtype: "Select", + options: "\nOpen\nCompleted\nCancelled", + default: "Open", }, { - "fieldname": "project_type", - "label": __("Project Type"), - "fieldtype": "Link", - "options": "Project Type" + fieldname: "project_type", + label: __("Project Type"), + fieldtype: "Link", + options: "Project Type", }, { - "fieldname": "priority", - "label": __("Priority"), - "fieldtype": "Select", - "options": "\nLow\nMedium\nHigh" - } - ] + fieldname: "priority", + label: __("Priority"), + fieldtype: "Select", + options: "\nLow\nMedium\nHigh", + }, + ], }; diff --git a/erpnext/projects/report/project_wise_stock_tracking/project_wise_stock_tracking.js b/erpnext/projects/report/project_wise_stock_tracking/project_wise_stock_tracking.js index ccca26439d0..dcadcc0852e 100644 --- a/erpnext/projects/report/project_wise_stock_tracking/project_wise_stock_tracking.js +++ b/erpnext/projects/report/project_wise_stock_tracking/project_wise_stock_tracking.js @@ -2,7 +2,5 @@ // For license information, please see license.txt frappe.query_reports["Project wise Stock Tracking"] = { - "filters": [ - - ] -} + filters: [], +}; diff --git a/erpnext/projects/web_form/tasks/tasks.js b/erpnext/projects/web_form/tasks/tasks.js index ffc5e984253..8f56ebb353d 100644 --- a/erpnext/projects/web_form/tasks/tasks.js +++ b/erpnext/projects/web_form/tasks/tasks.js @@ -1,3 +1,3 @@ -frappe.ready(function() { +frappe.ready(function () { // bind events here -}) +}); diff --git a/erpnext/public/js/account_tree_grid.js b/erpnext/public/js/account_tree_grid.js index 413a5ee9719..6b4cdf1177a 100644 --- a/erpnext/public/js/account_tree_grid.js +++ b/erpnext/public/js/account_tree_grid.js @@ -18,53 +18,96 @@ erpnext.AccountTreeGrid = class AccountTreeGrid extends frappe.views.TreeGridRep constructor(wrapper, title) { super({ title: title, - parent: $(wrapper).find('.layout-main'), + parent: $(wrapper).find(".layout-main"), page: wrapper.page, doctypes: ["Company", "Fiscal Year", "Account", "GL Entry", "Cost Center"], tree_grid: { show: true, parent_field: "parent_account", - formatter: function(item) { - return repl("\ - %(value)s", { + %(value)s", + { value: item.name, - }); - } + } + ); + }, }, }); this.filters = [ - {fieldtype: "Select", label: __("Company"), link:"Company", fieldname: "company", + { + fieldtype: "Select", + label: __("Company"), + link: "Company", + fieldname: "company", default_value: __("Select Company..."), - filter: function(val, item, opts, me) { + filter: function (val, item, opts, me) { if (item.company == val || val == opts.default_value) { return me.apply_zero_filter(val, item, opts, me); } return false; - }}, - {fieldtype: "Select", label: "Fiscal Year", link:"Fiscal Year", fieldname: "fiscal_year", - default_value: __("Select Fiscal Year...")}, - {fieldtype: "Date", label: __("From Date"), fieldname: "from_date"}, - {fieldtype: "Label", label: __("To")}, - {fieldtype: "Date", label: __("To Date"), fieldname: "to_date"} - ] + }, + }, + { + fieldtype: "Select", + label: "Fiscal Year", + link: "Fiscal Year", + fieldname: "fiscal_year", + default_value: __("Select Fiscal Year..."), + }, + { fieldtype: "Date", label: __("From Date"), fieldname: "from_date" }, + { fieldtype: "Label", label: __("To") }, + { fieldtype: "Date", label: __("To Date"), fieldname: "to_date" }, + ]; } setup_columns() { this.columns = [ - {id: "name", name: __("Account"), field: "name", width: 300, cssClass: "cell-title"}, - {id: "opening_dr", name: __("Opening (Dr)"), field: "opening_dr", width: 100, - formatter: this.currency_formatter}, - {id: "opening_cr", name: __("Opening (Cr)"), field: "opening_cr", width: 100, - formatter: this.currency_formatter}, - {id: "debit", name: __("Debit"), field: "debit", width: 100, - formatter: this.currency_formatter}, - {id: "credit", name: __("Credit"), field: "credit", width: 100, - formatter: this.currency_formatter}, - {id: "closing_dr", name: __("Closing (Dr)"), field: "closing_dr", width: 100, - formatter: this.currency_formatter}, - {id: "closing_cr", name: __("Closing (Cr)"), field: "closing_cr", width: 100, - formatter: this.currency_formatter} + { id: "name", name: __("Account"), field: "name", width: 300, cssClass: "cell-title" }, + { + id: "opening_dr", + name: __("Opening (Dr)"), + field: "opening_dr", + width: 100, + formatter: this.currency_formatter, + }, + { + id: "opening_cr", + name: __("Opening (Cr)"), + field: "opening_cr", + width: 100, + formatter: this.currency_formatter, + }, + { + id: "debit", + name: __("Debit"), + field: "debit", + width: 100, + formatter: this.currency_formatter, + }, + { + id: "credit", + name: __("Credit"), + field: "credit", + width: 100, + formatter: this.currency_formatter, + }, + { + id: "closing_dr", + name: __("Closing (Dr)"), + field: "closing_dr", + width: 100, + formatter: this.currency_formatter, + }, + { + id: "closing_cr", + name: __("Closing (Cr)"), + field: "closing_cr", + width: 100, + formatter: this.currency_formatter, + }, ]; } @@ -72,33 +115,33 @@ erpnext.AccountTreeGrid = class AccountTreeGrid extends frappe.views.TreeGridRep super.setup_filters(); var me = this; // default filters - this.filter_inputs.fiscal_year.change(function() { + this.filter_inputs.fiscal_year.change(function () { var fy = $(this).val(); - $.each(frappe.report_dump.data["Fiscal Year"], function(i, v) { - if (v.name==fy) { + $.each(frappe.report_dump.data["Fiscal Year"], function (i, v) { + if (v.name == fy) { me.filter_inputs.from_date.val(frappe.datetime.str_to_user(v.year_start_date)); me.filter_inputs.to_date.val(frappe.datetime.str_to_user(v.year_end_date)); } }); me.refresh(); }); - me.show_zero_check() - if(me.ignore_closing_entry) me.ignore_closing_entry(); + me.show_zero_check(); + if (me.ignore_closing_entry) me.ignore_closing_entry(); } prepare_data() { var me = this; - if(!this.primary_data) { + if (!this.primary_data) { // make accounts list me.data = []; me.parent_map = {}; me.item_by_name = {}; - $.each(frappe.report_dump.data["Account"], function(i, v) { + $.each(frappe.report_dump.data["Account"], function (i, v) { var d = copy_dict(v); me.data.push(d); me.item_by_name[d.name] = d; - if(d.parent_account) { + if (d.parent_account) { me.parent_map[d.name] = d.parent_account; } }); @@ -107,20 +150,19 @@ erpnext.AccountTreeGrid = class AccountTreeGrid extends frappe.views.TreeGridRep } me.data = [].concat(me.primary_data); - $.each(me.data, function(i, d) { + $.each(me.data, function (i, d) { me.init_account(d); }); this.set_indent(); this.prepare_balances(); - } init_account(d) { this.reset_item_values(d); } prepare_balances() { - var gl = frappe.report_dump.data['GL Entry']; + var gl = frappe.report_dump.data["GL Entry"]; var me = this; this.opening_date = frappe.datetime.user_to_obj(this.filter_inputs.from_date.val()); @@ -128,12 +170,11 @@ erpnext.AccountTreeGrid = class AccountTreeGrid extends frappe.views.TreeGridRep this.set_fiscal_year(); if (!this.fiscal_year) return; - $.each(this.data, function(i, v) { - v.opening_dr = v.opening_cr = v.debit - = v.credit = v.closing_dr = v.closing_cr = 0; + $.each(this.data, function (i, v) { + v.opening_dr = v.opening_cr = v.debit = v.credit = v.closing_dr = v.closing_cr = 0; }); - $.each(gl, function(i, v) { + $.each(gl, function (i, v) { var posting_date = frappe.datetime.str_to_obj(v.posting_date); var account = me.item_by_name[v.account]; me.update_balances(account, posting_date, v); @@ -144,13 +185,15 @@ erpnext.AccountTreeGrid = class AccountTreeGrid extends frappe.views.TreeGridRep update_balances(account, posting_date, v) { // opening if (posting_date < this.opening_date || v.is_opening === "Yes") { - if (account.report_type === "Profit and Loss" && - posting_date <= frappe.datetime.str_to_obj(this.fiscal_year[1])) { + if ( + account.report_type === "Profit and Loss" && + posting_date <= frappe.datetime.str_to_obj(this.fiscal_year[1]) + ) { // balance of previous fiscal_year should // not be part of opening of pl account balance } else { - var opening_bal = flt(account.opening_dr) - flt(account.opening_cr) + - flt(v.debit) - flt(v.credit); + var opening_bal = + flt(account.opening_dr) - flt(account.opening_cr) + flt(v.debit) - flt(v.credit); this.set_debit_or_credit(account, "opening", opening_bal); } } else if (this.opening_date <= posting_date && posting_date <= this.closing_date) { @@ -159,43 +202,47 @@ erpnext.AccountTreeGrid = class AccountTreeGrid extends frappe.views.TreeGridRep account.credit += flt(v.credit); } // closing - var closing_bal = flt(account.opening_dr) - flt(account.opening_cr) + - flt(account.debit) - flt(account.credit); + var closing_bal = + flt(account.opening_dr) - flt(account.opening_cr) + flt(account.debit) - flt(account.credit); this.set_debit_or_credit(account, "closing", closing_bal); } set_debit_or_credit(account, field, balance) { - if(balance > 0) { - account[field+"_dr"] = balance; - account[field+"_cr"] = 0; + if (balance > 0) { + account[field + "_dr"] = balance; + account[field + "_cr"] = 0; } else { - account[field+"_cr"] = Math.abs(balance); - account[field+"_dr"] = 0; + account[field + "_cr"] = Math.abs(balance); + account[field + "_dr"] = 0; } } update_groups() { // update groups - var me= this; - $.each(this.data, function(i, account) { + var me = this; + $.each(this.data, function (i, account) { // update groups - if((account.is_group == 0) || (account.rgt - account.lft == 1)) { + if (account.is_group == 0 || account.rgt - account.lft == 1) { var parent = me.parent_map[account.name]; - while(parent) { + while (parent) { var parent_account = me.item_by_name[parent]; - $.each(me.columns, function(c, col) { + $.each(me.columns, function (c, col) { if (col.formatter == me.currency_formatter) { - if(col.field=="opening_dr") { - var bal = flt(parent_account.opening_dr) - + if (col.field == "opening_dr") { + var bal = + flt(parent_account.opening_dr) - flt(parent_account.opening_cr) + - flt(account.opening_dr) - flt(account.opening_cr); + flt(account.opening_dr) - + flt(account.opening_cr); me.set_debit_or_credit(parent_account, "opening", bal); - } else if(col.field=="closing_dr") { - var bal = flt(parent_account.closing_dr) - + } else if (col.field == "closing_dr") { + var bal = + flt(parent_account.closing_dr) - flt(parent_account.closing_cr) + - flt(account.closing_dr) - flt(account.closing_cr); + flt(account.closing_dr) - + flt(account.closing_cr); me.set_debit_or_credit(parent_account, "closing", bal); - } else if(in_list(["debit", "credit"], col.field)) { - parent_account[col.field] = flt(parent_account[col.field]) + - flt(account[col.field]); + } else if (in_list(["debit", "credit"], col.field)) { + parent_account[col.field] = + flt(parent_account[col.field]) + flt(account[col.field]); } } }); @@ -213,9 +260,11 @@ erpnext.AccountTreeGrid = class AccountTreeGrid extends frappe.views.TreeGridRep this.fiscal_year = null; var me = this; - $.each(frappe.report_dump.data["Fiscal Year"], function(i, v) { - if (me.opening_date >= frappe.datetime.str_to_obj(v.year_start_date) && - me.closing_date <= frappe.datetime.str_to_obj(v.year_end_date)) { + $.each(frappe.report_dump.data["Fiscal Year"], function (i, v) { + if ( + me.opening_date >= frappe.datetime.str_to_obj(v.year_start_date) && + me.closing_date <= frappe.datetime.str_to_obj(v.year_end_date) + ) { me.fiscal_year = v; } }); @@ -231,7 +280,7 @@ erpnext.AccountTreeGrid = class AccountTreeGrid extends frappe.views.TreeGridRep account: account, company: this.company, from_date: this.from_date, - to_date: this.to_date + to_date: this.to_date, }; frappe.set_route("query-report", "General Ledger"); } diff --git a/erpnext/public/js/address.js b/erpnext/public/js/address.js index 57f7163bbb2..606e0b119fd 100644 --- a/erpnext/public/js/address.js +++ b/erpnext/public/js/address.js @@ -2,24 +2,23 @@ // For license information, please see license.txt frappe.ui.form.on("Address", { - is_your_company_address: function(frm) { - frm.clear_table('links'); - if(frm.doc.is_your_company_address) { - frm.add_child('links', { - link_doctype: 'Company', - link_name: frappe.defaults.get_user_default('Company') + is_your_company_address: function (frm) { + frm.clear_table("links"); + if (frm.doc.is_your_company_address) { + frm.add_child("links", { + link_doctype: "Company", + link_name: frappe.defaults.get_user_default("Company"), }); - frm.set_query('link_doctype', 'links', () => { + frm.set_query("link_doctype", "links", () => { return { filters: { - name: 'Company' - } + name: "Company", + }, }; }); - frm.refresh_field('links'); + frm.refresh_field("links"); + } else { + frm.trigger("refresh"); } - else { - frm.trigger('refresh'); - } - } + }, }); diff --git a/erpnext/public/js/agriculture/ternary_plot.js b/erpnext/public/js/agriculture/ternary_plot.js index b06a1fd7c8c..4fc1c4fc3f1 100644 --- a/erpnext/public/js/agriculture/ternary_plot.js +++ b/erpnext/public/js/agriculture/ternary_plot.js @@ -1,10 +1,10 @@ -frappe.provide('agriculture'); +frappe.provide("agriculture"); agriculture.TernaryPlot = class TernaryPlot { constructor(opts) { Object.assign(this, opts); - frappe.require('assets/frappe/js/lib/snap.svg-min.js', () => { + frappe.require("assets/frappe/js/lib/snap.svg-min.js", () => { this.make_svg(); this.init_snap(); this.init_config(); @@ -29,124 +29,170 @@ agriculture.TernaryPlot = class TernaryPlot { triangle_side: 300, spacing: 50, strokeWidth: 1, - stroke: frappe.ui.color.get('black') + stroke: frappe.ui.color.get("black"), }; this.config.scaling_factor = this.config.triangle_side / 100; let { triangle_side: t, spacing: s, scaling_factor: p } = this.config; this.coords = { sand: { - points: [ - s + t * Snap.cos(60), s, - s, s + t * Snap.cos(30), - s + t, s + t * Snap.cos(30) - ], - color: frappe.ui.color.get('peach') + points: [s + t * Snap.cos(60), s, s, s + t * Snap.cos(30), s + t, s + t * Snap.cos(30)], + color: frappe.ui.color.get("peach"), }, loamy_sand: { points: [ - s + 15 * p * Snap.cos(60), s + (100 - 15) * p * Snap.cos(30), - s + 10 * p * Snap.cos(60), s + (100 - 10) * p * Snap.cos(30), - s + (100 - 85) * p, s + t * Snap.cos(30), - s + (100 - 70) * p, s + t * Snap.cos(30) + s + 15 * p * Snap.cos(60), + s + (100 - 15) * p * Snap.cos(30), + s + 10 * p * Snap.cos(60), + s + (100 - 10) * p * Snap.cos(30), + s + (100 - 85) * p, + s + t * Snap.cos(30), + s + (100 - 70) * p, + s + t * Snap.cos(30), ], - color: frappe.ui.color.get('pink') + color: frappe.ui.color.get("pink"), }, sandy_loam: { points: [ - s + 20 * p * Snap.cos(60) + 27.5 * p, s + (100 - 20) * p * Snap.cos(30), - s + 20 * p * Snap.cos(60), s + (100 - 20) * p * Snap.cos(30), - s + 15 * p * Snap.cos(60), s + (100 - 15) * p * Snap.cos(30), - s + (100 - 75) * p, s + t * Snap.cos(30), - s + (100 - 50) * p, s + t * Snap.cos(30), - s + (100 - 50) * p + 7.5 * p * Snap.cos(60), s + t * Snap.cos(30) - 7.5 * p * Snap.cos(30), - s + (100 - 50) * p + 7.5 * p * Snap.cos(60) - 10 * p, s + t * Snap.cos(30) - 7.5 * p * Snap.cos(30) + s + 20 * p * Snap.cos(60) + 27.5 * p, + s + (100 - 20) * p * Snap.cos(30), + s + 20 * p * Snap.cos(60), + s + (100 - 20) * p * Snap.cos(30), + s + 15 * p * Snap.cos(60), + s + (100 - 15) * p * Snap.cos(30), + s + (100 - 75) * p, + s + t * Snap.cos(30), + s + (100 - 50) * p, + s + t * Snap.cos(30), + s + (100 - 50) * p + 7.5 * p * Snap.cos(60), + s + t * Snap.cos(30) - 7.5 * p * Snap.cos(30), + s + (100 - 50) * p + 7.5 * p * Snap.cos(60) - 10 * p, + s + t * Snap.cos(30) - 7.5 * p * Snap.cos(30), ], - color: frappe.ui.color.get('pink', 'light') + color: frappe.ui.color.get("pink", "light"), }, loam: { points: [ - s + (100 - 50) * p + 27.5 * p * Snap.cos(60), s + t * Snap.cos(30) - 27.5 * p * Snap.cos(30), - s + (100 - 50) * p + 27.5 * p * Snap.cos(60) - 22.5 * p, s + t * Snap.cos(30) - 27.5 * p * Snap.cos(30), - s + 20 * p * Snap.cos(60) + 27.5 * p, s + (100 - 20) * p * Snap.cos(30), - s + (100 - 50) * p + 7.5 * p * Snap.cos(60) - 10 * p, s + t * Snap.cos(30) - 7.5 * p * Snap.cos(30), - s + (100 - 50) * p + 7.5 * p * Snap.cos(60), s + t * Snap.cos(30) - 7.5 * p * Snap.cos(30) + s + (100 - 50) * p + 27.5 * p * Snap.cos(60), + s + t * Snap.cos(30) - 27.5 * p * Snap.cos(30), + s + (100 - 50) * p + 27.5 * p * Snap.cos(60) - 22.5 * p, + s + t * Snap.cos(30) - 27.5 * p * Snap.cos(30), + s + 20 * p * Snap.cos(60) + 27.5 * p, + s + (100 - 20) * p * Snap.cos(30), + s + (100 - 50) * p + 7.5 * p * Snap.cos(60) - 10 * p, + s + t * Snap.cos(30) - 7.5 * p * Snap.cos(30), + s + (100 - 50) * p + 7.5 * p * Snap.cos(60), + s + t * Snap.cos(30) - 7.5 * p * Snap.cos(30), ], - color: frappe.ui.color.get('brown') + color: frappe.ui.color.get("brown"), }, silt_loam: { points: [ - s + t - 27.5 * p * Snap.cos(60), s + 72.5 * p * Snap.cos(30), - s + (100 - 50) * p + 27.5 * p * Snap.cos(60), s + t * Snap.cos(30) - 27.5 * p * Snap.cos(30), - s + (100 - 50) * p, s + t * Snap.cos(30), - s + (100 - 20) * p, s + t * Snap.cos(30), - s + (100 - 20) * p + 12.5 * p * Snap.cos(60), s + 90 * p * Snap.cos(30), - s + t - 12.5 * p * Snap.cos(60), s + (100 - 12.5) * p * Snap.cos(30) + s + t - 27.5 * p * Snap.cos(60), + s + 72.5 * p * Snap.cos(30), + s + (100 - 50) * p + 27.5 * p * Snap.cos(60), + s + t * Snap.cos(30) - 27.5 * p * Snap.cos(30), + s + (100 - 50) * p, + s + t * Snap.cos(30), + s + (100 - 20) * p, + s + t * Snap.cos(30), + s + (100 - 20) * p + 12.5 * p * Snap.cos(60), + s + 90 * p * Snap.cos(30), + s + t - 12.5 * p * Snap.cos(60), + s + (100 - 12.5) * p * Snap.cos(30), ], - color: frappe.ui.color.get('green', 'dark') + color: frappe.ui.color.get("green", "dark"), }, silt: { points: [ - s + t - 12.5 * p * Snap.cos(60), s + (100 - 12.5) * p * Snap.cos(30), - s + (100 - 20) * p + 12.5 * p * Snap.cos(60), s + 90 * p * Snap.cos(30), - s + (100 - 20) * p, s + t * Snap.cos(30), - s + t, s + t * Snap.cos(30) + s + t - 12.5 * p * Snap.cos(60), + s + (100 - 12.5) * p * Snap.cos(30), + s + (100 - 20) * p + 12.5 * p * Snap.cos(60), + s + 90 * p * Snap.cos(30), + s + (100 - 20) * p, + s + t * Snap.cos(30), + s + t, + s + t * Snap.cos(30), ], - color: frappe.ui.color.get('green') + color: frappe.ui.color.get("green"), }, silty_clay_loam: { points: [ - s + t - 40 * p * Snap.cos(60), s + 60 * p * Snap.cos(30), - s + t - 40 * p * Snap.cos(60) - 20 * p, s + 60 * p * Snap.cos(30), - s + t - 27.5 * p * Snap.cos(60) - 20 * p, s + 72.5 * p * Snap.cos(30), - s + t - 27.5 * p * Snap.cos(60), s + 72.5 * p * Snap.cos(30) + s + t - 40 * p * Snap.cos(60), + s + 60 * p * Snap.cos(30), + s + t - 40 * p * Snap.cos(60) - 20 * p, + s + 60 * p * Snap.cos(30), + s + t - 27.5 * p * Snap.cos(60) - 20 * p, + s + 72.5 * p * Snap.cos(30), + s + t - 27.5 * p * Snap.cos(60), + s + 72.5 * p * Snap.cos(30), ], - color: frappe.ui.color.get('cyan', 'dark') + color: frappe.ui.color.get("cyan", "dark"), }, silty_clay: { points: [ - s + t - 60 * p * Snap.cos(60), s + 40 * p * Snap.cos(30), - s + t - 40 * p * Snap.cos(60) - 20 * p, s + 60 * p * Snap.cos(30), - s + t - 40 * p * Snap.cos(60), s + 60 * p * Snap.cos(30) + s + t - 60 * p * Snap.cos(60), + s + 40 * p * Snap.cos(30), + s + t - 40 * p * Snap.cos(60) - 20 * p, + s + 60 * p * Snap.cos(30), + s + t - 40 * p * Snap.cos(60), + s + 60 * p * Snap.cos(30), ], - color: frappe.ui.color.get('cyan') + color: frappe.ui.color.get("cyan"), }, clay_loam: { points: [ - s + t - 40 * p * Snap.cos(60) - 20 * p, s + 60 * p * Snap.cos(30), - s + t - 40 * p * Snap.cos(60) - 45 * p, s + 60 * p * Snap.cos(30), - s + t - 27.5 * p * Snap.cos(60) - 45 * p, s + 72.5 * p * Snap.cos(30), - s + t - 27.5 * p * Snap.cos(60) - 20 * p, s + 72.5 * p * Snap.cos(30) + s + t - 40 * p * Snap.cos(60) - 20 * p, + s + 60 * p * Snap.cos(30), + s + t - 40 * p * Snap.cos(60) - 45 * p, + s + 60 * p * Snap.cos(30), + s + t - 27.5 * p * Snap.cos(60) - 45 * p, + s + 72.5 * p * Snap.cos(30), + s + t - 27.5 * p * Snap.cos(60) - 20 * p, + s + 72.5 * p * Snap.cos(30), ], - color: frappe.ui.color.get('green', 'light') + color: frappe.ui.color.get("green", "light"), }, sandy_clay_loam: { points: [ - s + 35 * p * Snap.cos(60) + 20 * p, s + (100 - 35) * p * Snap.cos(30), - s + 35 * p * Snap.cos(60), s + (100 - 35) * p * Snap.cos(30), - s + 20 * p * Snap.cos(60), s + (100 - 20) * p * Snap.cos(30), - s + 20 * p * Snap.cos(60) + 27.5 * p, s + (100 - 20) * p * Snap.cos(30), - s + t - 27.5 * p * Snap.cos(60) - 45 * p, s + 72.5 * p * Snap.cos(30) + s + 35 * p * Snap.cos(60) + 20 * p, + s + (100 - 35) * p * Snap.cos(30), + s + 35 * p * Snap.cos(60), + s + (100 - 35) * p * Snap.cos(30), + s + 20 * p * Snap.cos(60), + s + (100 - 20) * p * Snap.cos(30), + s + 20 * p * Snap.cos(60) + 27.5 * p, + s + (100 - 20) * p * Snap.cos(30), + s + t - 27.5 * p * Snap.cos(60) - 45 * p, + s + 72.5 * p * Snap.cos(30), ], - color: frappe.ui.color.get('pink', 'dark') + color: frappe.ui.color.get("pink", "dark"), }, sandy_clay: { points: [ - s + 55 * p * Snap.cos(60), s + (100 - 55) * p * Snap.cos(30), - s + 35 * p * Snap.cos(60), s + (100 - 35) * p * Snap.cos(30), - s + 35 * p * Snap.cos(60) + 20 * p, s + (100 - 35) * p * Snap.cos(30) + s + 55 * p * Snap.cos(60), + s + (100 - 55) * p * Snap.cos(30), + s + 35 * p * Snap.cos(60), + s + (100 - 35) * p * Snap.cos(30), + s + 35 * p * Snap.cos(60) + 20 * p, + s + (100 - 35) * p * Snap.cos(30), ], - color: frappe.ui.color.get('red') + color: frappe.ui.color.get("red"), }, clay: { points: [ - s + t * Snap.cos(60), s, - s + 55 * p * Snap.cos(60), s + (100 - 55) * p * Snap.cos(30), - s + t - 40 * p * Snap.cos(60) - 45 * p, s + 60 * p * Snap.cos(30), - s + t - 40 * p * Snap.cos(60) - 20 * p, s + 60 * p * Snap.cos(30), - s + t - 60 * p * Snap.cos(60), s + 40 * p * Snap.cos(30) + s + t * Snap.cos(60), + s, + s + 55 * p * Snap.cos(60), + s + (100 - 55) * p * Snap.cos(30), + s + t - 40 * p * Snap.cos(60) - 45 * p, + s + 60 * p * Snap.cos(30), + s + t - 40 * p * Snap.cos(60) - 20 * p, + s + 60 * p * Snap.cos(30), + s + t - 60 * p * Snap.cos(60), + s + 40 * p * Snap.cos(30), ], - color: frappe.ui.color.get('yellow') + color: frappe.ui.color.get("yellow"), }, }; } @@ -164,7 +210,7 @@ agriculture.TernaryPlot = class TernaryPlot { this.paper.polygon(this.get_coords(soil_type)).attr({ fill: this.get_color(soil_type), stroke: this.config.stroke, - strokeWidth: this.config.strokeWidth + strokeWidth: this.config.strokeWidth, }); } } @@ -172,18 +218,18 @@ agriculture.TernaryPlot = class TernaryPlot { make_plot_marking() { let { triangle_side: t, spacing: s, scaling_factor: p } = this.config; - let clay = this.paper.text(t * Snap.cos(60) / 2, s + t * Snap.cos(30) / 2, __("Clay")).attr({ - fill: frappe.ui.color.get('black') + let clay = this.paper.text((t * Snap.cos(60)) / 2, s + (t * Snap.cos(30)) / 2, __("Clay")).attr({ + fill: frappe.ui.color.get("black"), }); clay.transform("r300"); - let silt = this.paper.text(t, s + t * Snap.cos(30) / 2, __("Silt")).attr({ - fill: frappe.ui.color.get('black') + let silt = this.paper.text(t, s + (t * Snap.cos(30)) / 2, __("Silt")).attr({ + fill: frappe.ui.color.get("black"), }); silt.transform("r60"); let sand = this.paper.text(35 + t * Snap.cos(60), 90 + t * Snap.cos(30), __("Sand")).attr({ - fill: frappe.ui.color.get('black') + fill: frappe.ui.color.get("black"), }); sand.transform("r0"); } @@ -194,25 +240,25 @@ agriculture.TernaryPlot = class TernaryPlot { let offset = 0; let exec_once = true; for (let soil_type in this.coords) { - if (index > 6 && exec_once){ + if (index > 6 && exec_once) { offset = 300; index = 1; exec_once = false; } - let rect = this.paper.rect(0+offset, 0+index*20, 100, 19, 5, 5).attr({ + let rect = this.paper.rect(0 + offset, 0 + index * 20, 100, 19, 5, 5).attr({ fill: this.get_color(soil_type), - stroke: frappe.ui.color.get('black') + stroke: frappe.ui.color.get("black"), }); - let text = this.paper.text(5+offset, 16+index*20, soil_type).attr({ - fill: frappe.ui.color.get('black'), - 'font-size': 12 + let text = this.paper.text(5 + offset, 16 + index * 20, soil_type).attr({ + fill: frappe.ui.color.get("black"), + "font-size": 12, }); index++; } } - mark_blip({clay, sand, silt} = this) { - if (clay + sand + silt != 0){ + mark_blip({ clay, sand, silt } = this) { + if (clay + sand + silt != 0) { let { triangle_side: t, spacing: s, scaling_factor: p } = this.config; let x_blip = s + clay * p * Snap.cos(60) + silt * p; @@ -220,13 +266,12 @@ agriculture.TernaryPlot = class TernaryPlot { this.blip = this.paper.circle(x_blip, y_blip, 4).attr({ fill: frappe.ui.color.get("orange"), stroke: frappe.ui.color.get("orange"), - strokeWidth: 2 + strokeWidth: 2, }); } } remove_blip() { - if (typeof this.blip !== 'undefined') - this.blip.remove(); + if (typeof this.blip !== "undefined") this.blip.remove(); } }; diff --git a/erpnext/public/js/bank_reconciliation_tool/data_table_manager.js b/erpnext/public/js/bank_reconciliation_tool/data_table_manager.js index 0cda93880fa..a88d00c5d35 100644 --- a/erpnext/public/js/bank_reconciliation_tool/data_table_manager.js +++ b/erpnext/public/js/bank_reconciliation_tool/data_table_manager.js @@ -18,12 +18,11 @@ erpnext.accounts.bank_reconciliation.DataTableManager = class DataTableManager { make_dt() { var me = this; frappe.call({ - method: - "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_bank_transactions", + method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_bank_transactions", args: { bank_account: this.bank_account, from_date: this.bank_statement_from_date, - to_date: this.bank_statement_to_date + to_date: this.bank_statement_to_date, }, callback: function (response) { me.format_data(response.message); @@ -62,18 +61,14 @@ erpnext.accounts.bank_reconciliation.DataTableManager = class DataTableManager { editable: false, width: 100, format: (value) => - "" + - format_currency(value, this.currency) + - "", + "" + format_currency(value, this.currency) + "", }, { name: __("Withdrawal"), editable: false, width: 100, format: (value) => - "" + - format_currency(value, this.currency) + - "", + "" + format_currency(value, this.currency) + "", }, { name: __("Unallocated Amount"), @@ -139,14 +134,8 @@ erpnext.accounts.bank_reconciliation.DataTableManager = class DataTableManager { checkboxColumn: false, inlineFilters: true, }; - this.datatable = new frappe.DataTable( - this.$reconciliation_tool_dt.get(0), - datatable_options - ); - $(`.${this.datatable.style.scopeClass} .dt-scrollable`).css( - "max-height", - "calc(100vh - 400px)" - ); + this.datatable = new frappe.DataTable(this.$reconciliation_tool_dt.get(0), datatable_options); + $(`.${this.datatable.style.scopeClass} .dt-scrollable`).css("max-height", "calc(100vh - 400px)"); if (this.transactions.length > 0) { this.$reconciliation_tool_dt.show(); @@ -159,27 +148,18 @@ erpnext.accounts.bank_reconciliation.DataTableManager = class DataTableManager { set_listeners() { var me = this; - $(`.${this.datatable.style.scopeClass} .dt-scrollable`).on( - "click", - `.btn`, - function () { - me.dialog_manager.show_dialog( - $(this).attr("data-name"), - (bank_transaction) => me.update_dt_cards(bank_transaction) - ); - return true; - } - ); + $(`.${this.datatable.style.scopeClass} .dt-scrollable`).on("click", `.btn`, function () { + me.dialog_manager.show_dialog($(this).attr("data-name"), (bank_transaction) => + me.update_dt_cards(bank_transaction) + ); + return true; + }); } update_dt_cards(bank_transaction) { - const transaction_index = this.transaction_dt_map[ - bank_transaction.name - ]; + const transaction_index = this.transaction_dt_map[bank_transaction.name]; if (bank_transaction.unallocated_amount > 0) { - this.transactions[transaction_index] = this.format_row( - bank_transaction - ); + this.transactions[transaction_index] = this.format_row(bank_transaction); } else { this.transactions.splice(transaction_index, 1); for (const [k, v] of Object.entries(this.transaction_dt_map)) { @@ -195,14 +175,9 @@ erpnext.accounts.bank_reconciliation.DataTableManager = class DataTableManager { // this.make_dt(); this.get_cleared_balance().then(() => { - this.cards_manager.$cards[1].set_value( - format_currency(this.cleared_balance), - this.currency - ); + this.cards_manager.$cards[1].set_value(format_currency(this.cleared_balance), this.currency); this.cards_manager.$cards[2].set_value( - format_currency( - this.bank_statement_closing_balance - this.cleared_balance - ), + format_currency(this.bank_statement_closing_balance - this.cleared_balance), this.currency ); this.cards_manager.$cards[2].set_value_color( @@ -216,14 +191,12 @@ erpnext.accounts.bank_reconciliation.DataTableManager = class DataTableManager { get_cleared_balance() { if (this.bank_account && this.bank_statement_to_date) { return frappe.call({ - method: - "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_account_balance", + method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_account_balance", args: { bank_account: this.bank_account, till_date: this.bank_statement_to_date, }, - callback: (response) => - (this.cleared_balance = response.message), + callback: (response) => (this.cleared_balance = response.message), }); } } diff --git a/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js b/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js index ddc10530581..5cab0efa0e1 100644 --- a/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js +++ b/erpnext/public/js/bank_reconciliation_tool/dialog_manager.js @@ -1,7 +1,15 @@ frappe.provide("erpnext.accounts.bank_reconciliation"); erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { - constructor(company, bank_account, bank_statement_from_date, bank_statement_to_date, filter_by_reference_date, from_reference_date, to_reference_date) { + constructor( + company, + bank_account, + bank_statement_from_date, + bank_statement_to_date, + filter_by_reference_date, + from_reference_date, + to_reference_date + ) { this.bank_account = bank_account; this.company = company; this.make_dialog(); @@ -61,22 +69,20 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { get_linked_vouchers(document_types) { frappe.call({ - method: - "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_linked_payments", + method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_linked_payments", args: { bank_transaction_name: this.bank_transaction_name, document_types: document_types, from_date: this.bank_statement_from_date, to_date: this.bank_statement_to_date, filter_by_reference_date: this.filter_by_reference_date, - from_reference_date:this.from_reference_date, - to_reference_date:this.to_reference_date + from_reference_date: this.from_reference_date, + to_reference_date: this.to_reference_date, }, callback: (result) => { const data = result.message; - if (data && data.length > 0) { const proposals_wrapper = this.dialog.fields_dict.payment_proposals.$wrapper; proposals_wrapper.show(); @@ -99,7 +105,6 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { const proposals_wrapper = this.dialog.fields_dict.payment_proposals.$wrapper; proposals_wrapper.hide(); this.dialog.fields_dict.no_matching_vouchers.$wrapper.show(); - } this.dialog.show(); }, @@ -118,7 +123,7 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { editable: false, width: 1, format: (value, row) => { - return frappe.form.formatters.Link(value, {options: row[2].content}); + return frappe.form.formatters.Link(value, { options: row[2].content }); }, }, { @@ -153,10 +158,7 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { checkboxColumn: true, inlineFilters: true, }; - this.datatable = new frappe.DataTable( - proposals_wrapper.get(0), - datatable_options - ); + this.datatable = new frappe.DataTable(proposals_wrapper.get(0), datatable_options); } else { this.datatable.refresh(this.data, this.columns); this.datatable.rowmanager.checkMap = []; @@ -218,10 +220,9 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { title: __("Reconcile the Bank Transaction"), fields: fields, size: "large", - primary_action: (values) => - this.reconciliation_dialog_primary_action(values), + primary_action: (values) => this.reconciliation_dialog_primary_action(values), }); - } + }, }); } @@ -256,7 +257,9 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { { fieldtype: "HTML", fieldname: "no_matching_vouchers", - options: __("
    {0}
    ", [__("No Matching Vouchers Found")]) + options: __('
    {0}
    ', [ + __("No Matching Vouchers Found"), + ]), }, { fieldtype: "Section Break", @@ -300,8 +303,7 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { click: () => { this.edit_in_full_page(); }, - depends_on: - "eval:doc.action=='Create Voucher'", + depends_on: "eval:doc.action=='Create Voucher'", }, { fieldname: "column_break_7", @@ -314,8 +316,7 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { label: "Journal Entry Type", options: "Journal Entry\nInter Company Journal Entry\nBank Entry\nCash Entry\nCredit Card Entry\nDebit Note\nCredit Note\nContra Entry\nExcise Entry\nWrite Off Entry\nOpening Entry\nDepreciation Entry\nExchange Rate Revaluation\nDeferred Revenue\nDeferred Expense", - depends_on: - "eval:doc.action=='Create Voucher' && doc.document_type=='Journal Entry'", + depends_on: "eval:doc.action=='Create Voucher' && doc.document_type=='Journal Entry'", mandatory_depends_on: "eval:doc.action=='Create Voucher' && doc.document_type=='Journal Entry'", }, @@ -324,8 +325,7 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { fieldtype: "Link", label: "Account", options: "Account", - depends_on: - "eval:doc.action=='Create Voucher' && doc.document_type=='Journal Entry'", + depends_on: "eval:doc.action=='Create Voucher' && doc.document_type=='Journal Entry'", mandatory_depends_on: "eval:doc.action=='Create Voucher' && doc.document_type=='Journal Entry'", get_query: () => { @@ -343,14 +343,11 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { label: "Party Type", options: "DocType", mandatory_depends_on: - "eval:doc.action=='Create Voucher' && doc.document_type=='Payment Entry'", + "eval:doc.action=='Create Voucher' && doc.document_type=='Payment Entry'", get_query: function () { return { filters: { - name: [ - "in", - Object.keys(frappe.boot.party_account_types), - ], + name: ["in", Object.keys(frappe.boot.party_account_types)], }, }; }, @@ -368,16 +365,14 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { fieldtype: "Link", label: "Project", options: "Project", - depends_on: - "eval:doc.action=='Create Voucher' && doc.document_type=='Payment Entry'", + depends_on: "eval:doc.action=='Create Voucher' && doc.document_type=='Payment Entry'", }, { fieldname: "cost_center", fieldtype: "Link", label: "Cost Center", options: "Cost Center", - depends_on: - "eval:doc.action=='Create Voucher' && doc.document_type=='Payment Entry'", + depends_on: "eval:doc.action=='Create Voucher' && doc.document_type=='Payment Entry'", }, { fieldtype: "Section Break", @@ -436,7 +431,7 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { options: "Currency", read_only: 1, hidden: 1, - } + }, ]; } @@ -458,18 +453,11 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { reconciliation_dialog_primary_action(values) { if (values.action == "Match Against Voucher") this.match(values); - if ( - values.action == "Create Voucher" && - values.document_type == "Payment Entry" - ) + if (values.action == "Create Voucher" && values.document_type == "Payment Entry") this.add_payment_entry(values); - if ( - values.action == "Create Voucher" && - values.document_type == "Journal Entry" - ) + if (values.action == "Create Voucher" && values.document_type == "Journal Entry") this.add_journal_entry(values); - else if (values.action == "Update Bank Transaction") - this.update_transaction(values); + else if (values.action == "Update Bank Transaction") this.update_transaction(values); } match() { @@ -487,8 +475,7 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { }); }); frappe.call({ - method: - "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.reconcile_vouchers", + method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.reconcile_vouchers", args: { bank_transaction_name: this.bank_transaction.name, vouchers: vouchers, @@ -504,8 +491,7 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { add_payment_entry(values) { frappe.call({ - method: - "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.create_payment_entry_bts", + method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.create_payment_entry_bts", args: { bank_transaction_name: this.bank_transaction.name, reference_number: values.reference_number, @@ -518,7 +504,9 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { cost_center: values.cost_center, }, callback: (response) => { - const alert_string = __("Bank Transaction {0} added as Payment Entry", [this.bank_transaction.name]); + const alert_string = __("Bank Transaction {0} added as Payment Entry", [ + this.bank_transaction.name, + ]); frappe.show_alert(alert_string); this.update_dt_cards(response.message); this.dialog.hide(); @@ -528,8 +516,7 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { add_journal_entry(values) { frappe.call({ - method: - "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.create_journal_entry_bts", + method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.create_journal_entry_bts", args: { bank_transaction_name: this.bank_transaction.name, reference_number: values.reference_number, @@ -542,7 +529,9 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { second_account: values.second_account, }, callback: (response) => { - const alert_string = __("Bank Transaction {0} added as Journal Entry", [this.bank_transaction.name]); + const alert_string = __("Bank Transaction {0} added as Journal Entry", [ + this.bank_transaction.name, + ]); frappe.show_alert(alert_string); this.update_dt_cards(response.message); this.dialog.hide(); @@ -552,8 +541,7 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { update_transaction(values) { frappe.call({ - method: - "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.update_bank_transaction", + method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.update_bank_transaction", args: { bank_transaction_name: this.bank_transaction.name, reference_number: values.reference_number, @@ -573,8 +561,7 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { const values = this.dialog.get_values(true); if (values.document_type == "Payment Entry") { frappe.call({ - method: - "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.create_payment_entry_bts", + method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.create_payment_entry_bts", args: { bank_transaction_name: this.bank_transaction.name, reference_number: values.reference_number, @@ -585,7 +572,7 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { mode_of_payment: values.mode_of_payment, project: values.project, cost_center: values.cost_center, - allow_edit: true + allow_edit: true, }, callback: (r) => { const doc = frappe.model.sync(r.message); @@ -594,8 +581,7 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { }); } else { frappe.call({ - method: - "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.create_journal_entry_bts", + method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.create_journal_entry_bts", args: { bank_transaction_name: this.bank_transaction.name, reference_number: values.reference_number, @@ -606,7 +592,7 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { mode_of_payment: values.mode_of_payment, entry_type: values.journal_entry_type, second_account: values.second_account, - allow_edit: true + allow_edit: true, }, callback: (r) => { var doc = frappe.model.sync(r.message); @@ -615,5 +601,4 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager { }); } } - }; diff --git a/erpnext/public/js/bank_reconciliation_tool/number_card.js b/erpnext/public/js/bank_reconciliation_tool/number_card.js index 7e1b2690a80..d39884c381e 100644 --- a/erpnext/public/js/bank_reconciliation_tool/number_card.js +++ b/erpnext/public/js/bank_reconciliation_tool/number_card.js @@ -26,8 +26,7 @@ erpnext.accounts.bank_reconciliation.NumberCardManager = class NumberCardManager currency: this.currency, }, { - value: - this.bank_statement_closing_balance - this.cleared_balance, + value: this.bank_statement_closing_balance - this.cleared_balance, label: __("Difference"), datatype: "Currency", currency: this.currency, @@ -41,11 +40,9 @@ erpnext.accounts.bank_reconciliation.NumberCardManager = class NumberCardManager number_card.$card.appendTo(this.$summary); }); this.$cards[2].set_value_color( - this.bank_statement_closing_balance - this.cleared_balance == 0 - ? "text-success" - : "text-danger" + this.bank_statement_closing_balance - this.cleared_balance == 0 ? "text-success" : "text-danger" ); - this.$summary.css({"border-bottom": "0px", "margin-left": "0px", "margin-right": "0px"}); + this.$summary.css({ "border-bottom": "0px", "margin-left": "0px", "margin-right": "0px" }); this.$summary.show(); } }; @@ -60,16 +57,10 @@ erpnext.accounts.NumberCard = class NumberCard { } set_value_color(color) { - this.$card - .find("div") - .removeClass("text-danger text-success") - .addClass(`${color}`); + this.$card.find("div").removeClass("text-danger text-success").addClass(`${color}`); } set_indicator(color) { - this.$card - .find("span") - .removeClass("indicator red green") - .addClass(`indicator ${color}`); + this.$card.find("span").removeClass("indicator red green").addClass(`indicator ${color}`); } }; diff --git a/erpnext/public/js/bulk_transaction_processing.js b/erpnext/public/js/bulk_transaction_processing.js index 101f50c64aa..f360bf5cd2c 100644 --- a/erpnext/public/js/bulk_transaction_processing.js +++ b/erpnext/public/js/bulk_transaction_processing.js @@ -1,30 +1,33 @@ frappe.provide("erpnext.bulk_transaction_processing"); $.extend(erpnext.bulk_transaction_processing, { - create: function(listview, from_doctype, to_doctype) { + create: function (listview, from_doctype, to_doctype) { let checked_items = listview.get_checked_items(); const doc_name = []; - checked_items.forEach((Item)=> { + checked_items.forEach((Item) => { if (Item.docstatus == 0) { doc_name.push(Item.name); } }); let count_of_rows = checked_items.length; - frappe.confirm(__("Create {0} {1} ?", [count_of_rows, to_doctype]), ()=>{ + frappe.confirm(__("Create {0} {1} ?", [count_of_rows, to_doctype]), () => { if (doc_name.length == 0) { - frappe.call({ - method: "erpnext.utilities.bulk_transaction.transaction_processing", - args: {data: checked_items, from_doctype: from_doctype, to_doctype: to_doctype} - }).then(()=> { - - }); + frappe + .call({ + method: "erpnext.utilities.bulk_transaction.transaction_processing", + args: { data: checked_items, from_doctype: from_doctype, to_doctype: to_doctype }, + }) + .then(() => {}); if (count_of_rows > 10) { - frappe.show_alert("Starting a background job to create {0} {1}", [count_of_rows, to_doctype]); + frappe.show_alert("Starting a background job to create {0} {1}", [ + count_of_rows, + to_doctype, + ]); } } else { frappe.msgprint(__("Selected document must be in submitted state")); } }); - } -}); \ No newline at end of file + }, +}); diff --git a/erpnext/public/js/call_popup/call_popup.js b/erpnext/public/js/call_popup/call_popup.js index 2dbe999e05b..7155c799ddb 100644 --- a/erpnext/public/js/call_popup/call_popup.js +++ b/erpnext/public/js/call_popup/call_popup.js @@ -7,17 +7,17 @@ class CallPopup { } make() { - frappe.utils.play_sound('incoming-call'); + frappe.utils.play_sound("incoming-call"); this.dialog = new frappe.ui.Dialog({ - 'static': true, - 'minimizable': true + static: true, + minimizable: true, }); this.dialog.get_close_btn().show(); this.setup_dialog(); this.set_call_status(); frappe.utils.bind_actions_with_object(this.dialog.$body, this); - this.dialog.$wrapper.addClass('call-popup'); - this.dialog.get_close_btn().unbind('click').click(this.close_modal.bind(this)); + this.dialog.$wrapper.addClass("call-popup"); + this.dialog.get_close_btn().unbind("click").click(this.close_modal.bind(this)); this.dialog.show(); } @@ -26,28 +26,28 @@ class CallPopup { this.dialog.$body.empty().append(this.caller_info); } - set_indicator(color, blink=false) { - let classes = `indicator ${color} ${blink ? 'blink': ''}`; - this.dialog.header.find('.indicator').attr('class', classes); + set_indicator(color, blink = false) { + let classes = `indicator ${color} ${blink ? "blink" : ""}`; + this.dialog.header.find(".indicator").attr("class", classes); } set_call_status(call_status) { - let title = ''; + let title = ""; call_status = call_status || this.call_log.status; - if (['Ringing'].includes(call_status) || !call_status) { - title = __('Incoming call from {0}', [this.get_caller_name() || this.caller_number]); - this.set_indicator('blue', true); - } else if (call_status === 'In Progress') { - title = __('Call Connected'); - this.set_indicator('green'); - } else if (['No Answer', 'Missed'].includes(call_status)) { - this.set_indicator('yellow'); - title = __('Call Missed'); - } else if (['Completed', 'Busy', 'Failed'].includes(call_status)) { - this.set_indicator('red'); - title = __('Call Ended'); + if (["Ringing"].includes(call_status) || !call_status) { + title = __("Incoming call from {0}", [this.get_caller_name() || this.caller_number]); + this.set_indicator("blue", true); + } else if (call_status === "In Progress") { + title = __("Call Connected"); + this.set_indicator("green"); + } else if (["No Answer", "Missed"].includes(call_status)) { + this.set_indicator("yellow"); + title = __("Call Missed"); + } else if (["Completed", "Busy", "Failed"].includes(call_status)) { + this.set_indicator("red"); + title = __("Call Ended"); } else { - this.set_indicator('blue'); + this.set_indicator("blue"); title = call_status; } this.dialog.set_title(title); @@ -55,7 +55,7 @@ class CallPopup { update_call_log(call_log, missed) { this.call_log = call_log; - this.set_call_status(missed ? 'Missed': null); + this.set_call_status(missed ? "Missed" : null); } close_modal() { @@ -64,10 +64,10 @@ class CallPopup { } call_ended(call_log, missed) { - frappe.utils.play_sound('call-disconnect'); + frappe.utils.play_sound("call-disconnect"); this.update_call_log(call_log, missed); setTimeout(() => { - if (!this.dialog.get_value('call_summary')) { + if (!this.dialog.get_value("call_summary")) { this.close_modal(); } }, 60000); @@ -81,16 +81,16 @@ class CallPopup { get_contact_link() { let log = this.call_log; - let contact_link = log.links.find(d => d.link_doctype === 'Contact'); + let contact_link = log.links.find((d) => d.link_doctype === "Contact"); return contact_link || {}; } setup_listener() { - frappe.realtime.on(`call_${this.call_log.id}_ended`, call_log => { + frappe.realtime.on(`call_${this.call_log.id}_ended`, (call_log) => { this.call_ended(call_log); }); - frappe.realtime.on(`call_${this.call_log.id}_missed`, call_log => { + frappe.realtime.on(`call_${this.call_log.id}_missed`, (call_log) => { this.call_ended(call_log, true); }); } @@ -103,84 +103,102 @@ class CallPopup { setup_call_details() { this.caller_info = $(`
    `); this.call_details = new frappe.ui.FieldGroup({ - fields: [{ - 'fieldname': 'name', - 'label': 'Name', - 'default': this.get_caller_name() || __('Unknown Caller'), - 'fieldtype': 'Data', - 'read_only': 1 - }, { - 'fieldtype': 'Button', - 'label': __('Open Contact'), - 'click': () => frappe.set_route('Form', 'Contact', this.get_contact_link().link_name), - 'depends_on': () => this.get_caller_name() - }, { - 'fieldtype': 'Button', - 'label': __('Create New Contact'), - 'click': this.create_new_contact.bind(this), - 'depends_on': () => !this.get_caller_name() - }, { - 'fieldtype': 'Button', - 'label': __('Create New Customer'), - 'click': this.create_new_customer.bind(this), - 'depends_on': () => !this.get_caller_name() - }, { - 'fieldtype': 'Button', - 'label': __('Create New Lead'), - 'click': () => frappe.new_doc('Lead', { 'mobile_no': this.caller_number }), - 'depends_on': () => !this.get_caller_name() - }, { - 'fieldtype': 'Column Break', - }, { - 'fieldname': 'number', - 'label': 'Phone Number', - 'fieldtype': 'Data', - 'default': this.caller_number, - 'read_only': 1 - }, { - 'fieldtype': 'Section Break', - 'hide_border': 1, - }, { - 'fieldname': 'call_type', - 'label': 'Call Type', - 'fieldtype': 'Link', - 'options': 'Telephony Call Type', - }, { - 'fieldtype': 'Section Break', - 'hide_border': 1, - }, { - 'fieldtype': 'Small Text', - 'label': __('Call Summary'), - 'fieldname': 'call_summary', - }, { - 'fieldtype': 'Button', - 'label': __('Save'), - 'click': () => { - const call_summary = this.call_details.get_value('call_summary'); - const call_type = this.call_details.get_value('call_type'); - if (!call_summary) return; - frappe.xcall('erpnext.telephony.doctype.call_log.call_log.add_call_summary_and_call_type', { - 'call_log': this.call_log.name, - 'summary': call_summary, - 'call_type': call_type, - }).then(() => { - this.close_modal(); - frappe.show_alert({ - message: ` - ${__('Call Summary Saved')} + fields: [ + { + fieldname: "name", + label: "Name", + default: this.get_caller_name() || __("Unknown Caller"), + fieldtype: "Data", + read_only: 1, + }, + { + fieldtype: "Button", + label: __("Open Contact"), + click: () => frappe.set_route("Form", "Contact", this.get_contact_link().link_name), + depends_on: () => this.get_caller_name(), + }, + { + fieldtype: "Button", + label: __("Create New Contact"), + click: this.create_new_contact.bind(this), + depends_on: () => !this.get_caller_name(), + }, + { + fieldtype: "Button", + label: __("Create New Customer"), + click: this.create_new_customer.bind(this), + depends_on: () => !this.get_caller_name(), + }, + { + fieldtype: "Button", + label: __("Create New Lead"), + click: () => frappe.new_doc("Lead", { mobile_no: this.caller_number }), + depends_on: () => !this.get_caller_name(), + }, + { + fieldtype: "Column Break", + }, + { + fieldname: "number", + label: "Phone Number", + fieldtype: "Data", + default: this.caller_number, + read_only: 1, + }, + { + fieldtype: "Section Break", + hide_border: 1, + }, + { + fieldname: "call_type", + label: "Call Type", + fieldtype: "Link", + options: "Telephony Call Type", + }, + { + fieldtype: "Section Break", + hide_border: 1, + }, + { + fieldtype: "Small Text", + label: __("Call Summary"), + fieldname: "call_summary", + }, + { + fieldtype: "Button", + label: __("Save"), + click: () => { + const call_summary = this.call_details.get_value("call_summary"); + const call_type = this.call_details.get_value("call_type"); + if (!call_summary) return; + frappe + .xcall( + "erpnext.telephony.doctype.call_log.call_log.add_call_summary_and_call_type", + { + call_log: this.call_log.name, + summary: call_summary, + call_type: call_type, + } + ) + .then(() => { + this.close_modal(); + frappe.show_alert({ + message: ` + ${__("Call Summary Saved")}
    - ${__('View call log')} + ${__("View call log")} `, - indicator: 'green' - }); - }); - } - }], - body: this.caller_info + indicator: "green", + }); + }); + }, + }, + ], + body: this.caller_info, }); this.call_details.make(); } @@ -191,23 +209,23 @@ class CallPopup { create_new_customer() { // to avoid quick entry form - const new_customer = frappe.model.get_new_doc('Customer'); + const new_customer = frappe.model.get_new_doc("Customer"); new_customer.mobile_no = this.caller_number; - frappe.set_route('Form', new_customer.doctype, new_customer.name); + frappe.set_route("Form", new_customer.doctype, new_customer.name); } create_new_contact() { // TODO: fix new_doc, it should accept child table values - const new_contact = frappe.model.get_new_doc('Contact'); - const phone_no = frappe.model.add_child(new_contact, 'Contact Phone', 'phone_nos'); + const new_contact = frappe.model.get_new_doc("Contact"); + const phone_no = frappe.model.add_child(new_contact, "Contact Phone", "phone_nos"); phone_no.phone = this.caller_number; phone_no.is_primary_mobile_no = 1; - frappe.set_route('Form', new_contact.doctype, new_contact.name); + frappe.set_route("Form", new_contact.doctype, new_contact.name); } } -$(document).on('app_ready', function () { - frappe.realtime.on('show_call_popup', call_log => { +$(document).on("app_ready", function () { + frappe.realtime.on("show_call_popup", (call_log) => { let call_popup = erpnext.call_popup; if (call_popup && call_log.name === call_popup.call_log.name) { call_popup.update_call_log(call_log); diff --git a/erpnext/public/js/communication.js b/erpnext/public/js/communication.js index f205d889658..d9187f8b678 100644 --- a/erpnext/public/js/communication.js +++ b/erpnext/public/js/communication.js @@ -1,33 +1,45 @@ frappe.ui.form.on("Communication", { refresh: (frm) => { // setup custom Make button only if Communication is Email - if(frm.doc.communication_medium == "Email" && frm.doc.sent_or_received == "Received") { + if (frm.doc.communication_medium == "Email" && frm.doc.sent_or_received == "Received") { frm.events.setup_custom_buttons(frm); } }, setup_custom_buttons: (frm) => { let confirm_msg = "Are you sure you want to create {0} from this email?"; - if(frm.doc.reference_doctype !== "Issue") { - frm.add_custom_button(__("Issue"), () => { - frappe.confirm(__(confirm_msg, [__("Issue")]), () => { - frm.trigger('make_issue_from_communication'); - }) - }, __("Create")); + if (frm.doc.reference_doctype !== "Issue") { + frm.add_custom_button( + __("Issue"), + () => { + frappe.confirm(__(confirm_msg, [__("Issue")]), () => { + frm.trigger("make_issue_from_communication"); + }); + }, + __("Create") + ); } - if(!in_list(["Lead", "Opportunity"], frm.doc.reference_doctype)) { - frm.add_custom_button(__("Lead"), () => { - frappe.confirm(__(confirm_msg, [__("Lead")]), () => { - frm.trigger('make_lead_from_communication'); - }) - }, __('Create')); + if (!in_list(["Lead", "Opportunity"], frm.doc.reference_doctype)) { + frm.add_custom_button( + __("Lead"), + () => { + frappe.confirm(__(confirm_msg, [__("Lead")]), () => { + frm.trigger("make_lead_from_communication"); + }); + }, + __("Create") + ); - frm.add_custom_button(__("Opportunity"), () => { - frappe.confirm(__(confirm_msg, [__("Opportunity")]), () => { - frm.trigger('make_opportunity_from_communication'); - }) - }, __('Create')); + frm.add_custom_button( + __("Opportunity"), + () => { + frappe.confirm(__(confirm_msg, [__("Opportunity")]), () => { + frm.trigger("make_opportunity_from_communication"); + }); + }, + __("Create") + ); } }, @@ -35,63 +47,69 @@ frappe.ui.form.on("Communication", { return frappe.call({ method: "erpnext.crm.doctype.lead.lead.make_lead_from_communication", args: { - communication: frm.doc.name + communication: frm.doc.name, }, freeze: true, callback: (r) => { - if(r.message) { - frm.reload_doc() + if (r.message) { + frm.reload_doc(); } - } - }) + }, + }); }, make_issue_from_communication: (frm) => { return frappe.call({ method: "erpnext.support.doctype.issue.issue.make_issue_from_communication", args: { - communication: frm.doc.name + communication: frm.doc.name, }, freeze: true, callback: (r) => { - if(r.message) { - frm.reload_doc() + if (r.message) { + frm.reload_doc(); } - } - }) + }, + }); }, make_opportunity_from_communication: (frm) => { - const fields = [{ - fieldtype: 'Link', - label: __('Select a Company'), - fieldname: 'company', - options: 'Company', - reqd: 1, - default: frappe.defaults.get_user_default("Company") - }]; + const fields = [ + { + fieldtype: "Link", + label: __("Select a Company"), + fieldname: "company", + options: "Company", + reqd: 1, + default: frappe.defaults.get_user_default("Company"), + }, + ]; - frappe.prompt(fields, data => { - frappe.call({ - method: "erpnext.crm.doctype.opportunity.opportunity.make_opportunity_from_communication", - args: { - communication: frm.doc.name, - company: data.company - }, - freeze: true, - callback: (r) => { - if(r.message) { - frm.reload_doc(); - frappe.show_alert({ - message: __("Opportunity {0} created", - ['' + r.message + '']), - indicator: 'green' - }); - } - } - }); - }, - 'Create an Opportunity', - 'Create'); - } + frappe.prompt( + fields, + (data) => { + frappe.call({ + method: "erpnext.crm.doctype.opportunity.opportunity.make_opportunity_from_communication", + args: { + communication: frm.doc.name, + company: data.company, + }, + freeze: true, + callback: (r) => { + if (r.message) { + frm.reload_doc(); + frappe.show_alert({ + message: __("Opportunity {0} created", [ + '' + r.message + "", + ]), + indicator: "green", + }); + } + }, + }); + }, + "Create an Opportunity", + "Create" + ); + }, }); diff --git a/erpnext/public/js/conf.js b/erpnext/public/js/conf.js index a0f56a2d07f..2e6e9ba1ad4 100644 --- a/erpnext/public/js/conf.js +++ b/erpnext/public/js/conf.js @@ -1,26 +1,26 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.provide('erpnext'); +frappe.provide("erpnext"); // preferred modules for breadcrumbs $.extend(frappe.breadcrumbs.preferred, { "Item Group": "Stock", "Customer Group": "Selling", "Supplier Group": "Buying", - "Territory": "Selling", + Territory: "Selling", "Sales Person": "Selling", "Sales Partner": "Selling", - "Brand": "Stock", + Brand: "Stock", "Maintenance Schedule": "Support", - "Maintenance Visit": "Support" + "Maintenance Visit": "Support", }); $.extend(frappe.breadcrumbs.module_map, { - 'ERPNext Integrations': 'Integrations', - 'Geo': 'Settings', - 'Portal': 'Website', - 'Utilities': 'Settings', - 'E-commerce': 'Website', - 'Contacts': 'CRM' + "ERPNext Integrations": "Integrations", + Geo: "Settings", + Portal: "Website", + Utilities: "Settings", + "E-commerce": "Website", + Contacts: "CRM", }); diff --git a/erpnext/public/js/contact.js b/erpnext/public/js/contact.js index 41a0e8a9f99..ecfc4eeef93 100644 --- a/erpnext/public/js/contact.js +++ b/erpnext/public/js/contact.js @@ -1,16 +1,14 @@ - - frappe.ui.form.on("Contact", { refresh(frm) { - frm.set_query('link_doctype', "links", function() { + frm.set_query("link_doctype", "links", function () { return { query: "frappe.contacts.address_and_contact.filter_dynamic_link_doctypes", filters: { fieldtype: ["in", ["HTML", "Text Editor"]], fieldname: ["in", ["contact_html", "company_description"]], - } + }, }; }); frm.refresh_field("links"); - } + }, }); diff --git a/erpnext/public/js/customer_reviews.js b/erpnext/public/js/customer_reviews.js index e13ded6b489..26deb9a6dd0 100644 --- a/erpnext/public/js/customer_reviews.js +++ b/erpnext/public/js/customer_reviews.js @@ -13,27 +13,27 @@ $(() => { write_review() { //TODO: make dialog popup on stray page - $('.page_content').on('click', '.btn-write-review', (e) => { + $(".page_content").on("click", ".btn-write-review", (e) => { // Bind action on write a review button const $btn = $(e.currentTarget); let d = new frappe.ui.Dialog({ title: __("Write a Review"), fields: [ - {fieldname: "title", fieldtype: "Data", label: "Headline", reqd: 1}, - {fieldname: "rating", fieldtype: "Rating", label: "Overall Rating", reqd: 1}, - {fieldtype: "Section Break"}, - {fieldname: "comment", fieldtype: "Small Text", label: "Your Review"} + { fieldname: "title", fieldtype: "Data", label: "Headline", reqd: 1 }, + { fieldname: "rating", fieldtype: "Rating", label: "Overall Rating", reqd: 1 }, + { fieldtype: "Section Break" }, + { fieldname: "comment", fieldtype: "Small Text", label: "Your Review" }, ], - primary_action: function() { + primary_action: function () { let data = d.get_values(); frappe.call({ method: "erpnext.e_commerce.doctype.item_review.item_review.add_item_review", args: { - web_item: $btn.attr('data-web-item'), + web_item: $btn.attr("data-web-item"), title: data.title, rating: data.rating, - comment: data.comment + comment: data.comment, }, freeze: true, freeze_message: __("Submitting Review ..."), @@ -42,25 +42,25 @@ $(() => { frappe.msgprint({ message: __("Thank you for submitting your review"), title: __("Review Submitted"), - indicator: "green" + indicator: "green", }); d.hide(); location.reload(); } - } + }, }); }, - primary_action_label: __('Submit') + primary_action_label: __("Submit"), }); d.show(); }); } view_more() { - $('.page_content').on('click', '.btn-view-more', (e) => { + $(".page_content").on("click", ".btn-view-more", (e) => { // Bind action on view more button const $btn = $(e.currentTarget); - $btn.prop('disabled', true); + $btn.prop("disabled", true); this.start += this.page_length; let me = this; @@ -68,30 +68,28 @@ $(() => { frappe.call({ method: "erpnext.e_commerce.doctype.item_review.item_review.get_item_reviews", args: { - web_item: $btn.attr('data-web-item'), + web_item: $btn.attr("data-web-item"), start: me.start, - end: me.page_length + end: me.page_length, }, callback: (result) => { if (result.message) { let res = result.message; me.get_user_review_html(res.reviews); - $btn.prop('disabled', false); - if (res.total_reviews <= (me.start + me.page_length)) { + $btn.prop("disabled", false); + if (res.total_reviews <= me.start + me.page_length) { $btn.hide(); } - } - } + }, }); }); - } get_user_review_html(reviews) { let me = this; - let $content = $('.user-reviews'); + let $content = $(".user-reviews"); reviews.forEach((review) => { $content.append(` @@ -123,7 +121,7 @@ $(() => { get_review_stars(rating) { let stars = ``; for (let i = 1; i < 6; i++) { - let fill_class = i <= rating ? 'star-click' : ''; + let fill_class = i <= rating ? "star-click" : ""; stars += ` @@ -135,4 +133,4 @@ $(() => { } new CustomerReviews(); -}); \ No newline at end of file +}); diff --git a/erpnext/public/js/erpnext-web.bundle.js b/erpnext/public/js/erpnext-web.bundle.js index cbe899dc060..7c20d6eb8d6 100644 --- a/erpnext/public/js/erpnext-web.bundle.js +++ b/erpnext/public/js/erpnext-web.bundle.js @@ -5,4 +5,4 @@ import "./customer_reviews"; import "../../e_commerce/product_ui/list"; import "../../e_commerce/product_ui/views"; import "../../e_commerce/product_ui/grid"; -import "../../e_commerce/product_ui/search"; \ No newline at end of file +import "../../e_commerce/product_ui/search"; diff --git a/erpnext/public/js/event.js b/erpnext/public/js/event.js index 665c8fbe1f4..a6733915a5c 100644 --- a/erpnext/public/js/event.js +++ b/erpnext/public/js/event.js @@ -3,33 +3,53 @@ frappe.provide("frappe.desk"); frappe.ui.form.on("Event", { - refresh: function(frm) { - frm.set_query('reference_doctype', "event_participants", function() { + refresh: function (frm) { + frm.set_query("reference_doctype", "event_participants", function () { return { - "filters": { - "name": ["in", ["Contact", "Lead", "Customer", "Supplier", "Employee", "Sales Partner"]] - } + filters: { + name: ["in", ["Contact", "Lead", "Customer", "Supplier", "Employee", "Sales Partner"]], + }, }; }); - frm.add_custom_button(__('Add Leads'), function() { - new frappe.desk.eventParticipants(frm, "Lead"); - }, __("Add Participants")); + frm.add_custom_button( + __("Add Leads"), + function () { + new frappe.desk.eventParticipants(frm, "Lead"); + }, + __("Add Participants") + ); - frm.add_custom_button(__('Add Customers'), function() { - new frappe.desk.eventParticipants(frm, "Customer"); - }, __("Add Participants")); + frm.add_custom_button( + __("Add Customers"), + function () { + new frappe.desk.eventParticipants(frm, "Customer"); + }, + __("Add Participants") + ); - frm.add_custom_button(__('Add Suppliers'), function() { - new frappe.desk.eventParticipants(frm, "Supplier"); - }, __("Add Participants")); + frm.add_custom_button( + __("Add Suppliers"), + function () { + new frappe.desk.eventParticipants(frm, "Supplier"); + }, + __("Add Participants") + ); - frm.add_custom_button(__('Add Employees'), function() { - new frappe.desk.eventParticipants(frm, "Employee"); - }, __("Add Participants")); + frm.add_custom_button( + __("Add Employees"), + function () { + new frappe.desk.eventParticipants(frm, "Employee"); + }, + __("Add Participants") + ); - frm.add_custom_button(__('Add Sales Partners'), function() { - new frappe.desk.eventParticipants(frm, "Sales Partners"); - }, __("Add Participants")); - } + frm.add_custom_button( + __("Add Sales Partners"), + function () { + new frappe.desk.eventParticipants(frm, "Sales Partners"); + }, + __("Add Participants") + ); + }, }); diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index 666505e2e38..d489c91c305 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -1,59 +1,57 @@ frappe.provide("erpnext.financial_statements"); erpnext.financial_statements = { - "filters": get_filters(), - "baseData": null, - "formatter": function(value, row, column, data, default_formatter, filter) { - if(frappe.query_report.get_filter_value("selected_view") == "Growth" && data && column.colIndex >= 3){ + filters: get_filters(), + baseData: null, + formatter: function (value, row, column, data, default_formatter, filter) { + if ( + frappe.query_report.get_filter_value("selected_view") == "Growth" && + 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 + if (currentAnnualvalue == undefined) return "NA"; //making this not applicable for undefined/null values let annualGrowth = 0; - if(lastAnnualValue == 0 && currentAnnualvalue > 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; + } else if (lastAnnualValue > 0) { + annualGrowth = (currentAnnualvalue - lastAnnualValue) / lastAnnualValue; } - const growthPercent = (Math.round(annualGrowth*10000)/100); //calculating the rounded off percentage + const growthPercent = Math.round(annualGrowth * 10000) / 100; //calculating the rounded off percentage - value = $(`${((growthPercent >=0)? '+':'' )+growthPercent+'%'}`); - if(growthPercent < 0){ + value = $(`${(growthPercent >= 0 ? "+" : "") + growthPercent + "%"}`); + if (growthPercent < 0) { value = $(value).addClass("text-danger"); - } - else{ + } 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")){ + } 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){ + 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; + if (currentAnnualvalue == undefined || baseValue <= 0) return "NA"; + const marginPercent = Math.round((currentAnnualvalue / baseValue) * 10000) / 100; - value = $(`${marginPercent+'%'}`); - if(marginPercent < 0) - value = $(value).addClass("text-danger"); - else - value = $(value).addClass("text-success"); + value = $(`${marginPercent + "%"}`); + if (marginPercent < 0) value = $(value).addClass("text-danger"); + else value = $(value).addClass("text-success"); value = $(value).wrap("

    ").parent().html(); return value; } - } - if (data && column.fieldname=="account") { + if (data && column.fieldname == "account") { value = data.account_name || value; if (filter && filter?.text && filter?.type == "contains") { @@ -84,16 +82,18 @@ erpnext.financial_statements = { return value; }, - "open_general_ledger": function(data) { + open_general_ledger: function (data) { if (!data.account) return; - let project = $.grep(frappe.query_report.filters, function(e){ return e.df.fieldname == 'project'; }); + let project = $.grep(frappe.query_report.filters, function (e) { + return e.df.fieldname == "project"; + }); frappe.route_options = { - "account": data.account, - "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, - "project": (project && project.length > 0) ? project[0].$input.val() : "" + account: data.account, + 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, + project: project && project.length > 0 ? project[0].$input.val() : "", }; let report = "General Ledger"; @@ -106,152 +106,161 @@ erpnext.financial_statements = { frappe.set_route("query-report", report); }, - "tree": true, - "name_field": "account", - "parent_field": "parent_account", - "initial_depth": 3, - onload: function(report) { + tree: true, + name_field: "account", + parent_field: "parent_account", + initial_depth: 3, + onload: function (report) { // dropdown for links to other financial statements - erpnext.financial_statements.filters = get_filters() + erpnext.financial_statements.filters = get_filters(); let fiscal_year = erpnext.utils.get_fiscal_year(frappe.datetime.get_today()); - frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) { + 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 + period_end_date: fy.year_end_date, }); }); - const views_menu = report.page.add_custom_button_group(__('Financial Statements')); + const views_menu = report.page.add_custom_button_group(__("Financial Statements")); - report.page.add_custom_menu_item(views_menu, __("Balance Sheet"), function() { + report.page.add_custom_menu_item(views_menu, __("Balance Sheet"), function () { var filters = report.get_values(); - frappe.set_route('query-report', 'Balance Sheet', {company: filters.company}); + frappe.set_route("query-report", "Balance Sheet", { company: filters.company }); }); - report.page.add_custom_menu_item(views_menu, __("Profit and Loss"), function() { + report.page.add_custom_menu_item(views_menu, __("Profit and Loss"), function () { var filters = report.get_values(); - frappe.set_route('query-report', 'Profit and Loss Statement', {company: filters.company}); + frappe.set_route("query-report", "Profit and Loss Statement", { company: filters.company }); }); - report.page.add_custom_menu_item(views_menu, __("Cash Flow Statement"), function() { + report.page.add_custom_menu_item(views_menu, __("Cash Flow Statement"), function () { var filters = report.get_values(); - frappe.set_route('query-report', 'Cash Flow', {company: filters.company}); + frappe.set_route("query-report", "Cash Flow", { company: filters.company }); }); - } + }, }; function get_filters() { let filters = [ { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company"), - "reqd": 1 + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, }, { - "fieldname":"finance_book", - "label": __("Finance Book"), - "fieldtype": "Link", - "options": "Finance Book" + fieldname: "finance_book", + label: __("Finance Book"), + fieldtype: "Link", + options: "Finance Book", }, { - "fieldname":"filter_based_on", - "label": __("Filter Based On"), - "fieldtype": "Select", - "options": ["Fiscal Year", "Date Range"], - "default": ["Fiscal Year"], - "reqd": 1, - on_change: function() { - let filter_based_on = frappe.query_report.get_filter_value('filter_based_on'); - frappe.query_report.toggle_filter_display('from_fiscal_year', filter_based_on === 'Date Range'); - frappe.query_report.toggle_filter_display('to_fiscal_year', filter_based_on === 'Date Range'); - frappe.query_report.toggle_filter_display('period_start_date', filter_based_on === 'Fiscal Year'); - frappe.query_report.toggle_filter_display('period_end_date', filter_based_on === 'Fiscal Year'); + fieldname: "filter_based_on", + label: __("Filter Based On"), + fieldtype: "Select", + options: ["Fiscal Year", "Date Range"], + default: ["Fiscal Year"], + reqd: 1, + on_change: function () { + let filter_based_on = frappe.query_report.get_filter_value("filter_based_on"); + frappe.query_report.toggle_filter_display( + "from_fiscal_year", + filter_based_on === "Date Range" + ); + frappe.query_report.toggle_filter_display("to_fiscal_year", filter_based_on === "Date Range"); + frappe.query_report.toggle_filter_display( + "period_start_date", + filter_based_on === "Fiscal Year" + ); + frappe.query_report.toggle_filter_display( + "period_end_date", + filter_based_on === "Fiscal Year" + ); frappe.query_report.refresh(); - } + }, }, { - "fieldname":"period_start_date", - "label": __("Start Date"), - "fieldtype": "Date", - "reqd": 1, - "depends_on": "eval:doc.filter_based_on == 'Date Range'" + fieldname: "period_start_date", + label: __("Start Date"), + fieldtype: "Date", + reqd: 1, + depends_on: "eval:doc.filter_based_on == 'Date Range'", }, { - "fieldname":"period_end_date", - "label": __("End Date"), - "fieldtype": "Date", - "reqd": 1, - "depends_on": "eval:doc.filter_based_on == 'Date Range'" + fieldname: "period_end_date", + label: __("End Date"), + fieldtype: "Date", + reqd: 1, + depends_on: "eval:doc.filter_based_on == 'Date Range'", }, { - "fieldname":"from_fiscal_year", - "label": __("Start Year"), - "fieldtype": "Link", - "options": "Fiscal Year", - "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), - "reqd": 1, - "depends_on": "eval:doc.filter_based_on == 'Fiscal Year'" + fieldname: "from_fiscal_year", + label: __("Start Year"), + fieldtype: "Link", + options: "Fiscal Year", + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), + reqd: 1, + depends_on: "eval:doc.filter_based_on == 'Fiscal Year'", }, { - "fieldname":"to_fiscal_year", - "label": __("End Year"), - "fieldtype": "Link", - "options": "Fiscal Year", - "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), - "reqd": 1, - "depends_on": "eval:doc.filter_based_on == 'Fiscal Year'" + fieldname: "to_fiscal_year", + label: __("End Year"), + fieldtype: "Link", + options: "Fiscal Year", + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), + reqd: 1, + depends_on: "eval:doc.filter_based_on == 'Fiscal Year'", }, { - "fieldname": "periodicity", - "label": __("Periodicity"), - "fieldtype": "Select", - "options": [ - { "value": "Monthly", "label": __("Monthly") }, - { "value": "Quarterly", "label": __("Quarterly") }, - { "value": "Half-Yearly", "label": __("Half-Yearly") }, - { "value": "Yearly", "label": __("Yearly") } + fieldname: "periodicity", + label: __("Periodicity"), + fieldtype: "Select", + options: [ + { value: "Monthly", label: __("Monthly") }, + { value: "Quarterly", label: __("Quarterly") }, + { value: "Half-Yearly", label: __("Half-Yearly") }, + { value: "Yearly", label: __("Yearly") }, ], - "default": "Yearly", - "reqd": 1 + default: "Yearly", + reqd: 1, }, // Note: // If you are modifying this array such that the presentation_currency object // is no longer the last object, please make adjustments in cash_flow.js // accordingly. { - "fieldname": "presentation_currency", - "label": __("Currency"), - "fieldtype": "Select", - "options": erpnext.get_presentation_currency_list() + fieldname: "presentation_currency", + label: __("Currency"), + fieldtype: "Select", + options: erpnext.get_presentation_currency_list(), }, { - "fieldname": "cost_center", - "label": __("Cost Center"), - "fieldtype": "MultiSelectList", - get_data: function(txt) { - return frappe.db.get_link_options('Cost Center', txt, { - company: frappe.query_report.get_filter_value("company") - }); - } - }, - { - "fieldname": "project", - "label": __("Project"), - "fieldtype": "MultiSelectList", - get_data: function(txt) { - return frappe.db.get_link_options('Project', txt, { - company: frappe.query_report.get_filter_value("company") + fieldname: "cost_center", + label: __("Cost Center"), + fieldtype: "MultiSelectList", + get_data: function (txt) { + return frappe.db.get_link_options("Cost Center", txt, { + company: frappe.query_report.get_filter_value("company"), }); }, - } - ] + }, + { + fieldname: "project", + label: __("Project"), + fieldtype: "MultiSelectList", + get_data: function (txt) { + return frappe.db.get_link_options("Project", txt, { + company: frappe.query_report.get_filter_value("company"), + }); + }, + }, + ]; return filters; } diff --git a/erpnext/public/js/help_links.js b/erpnext/public/js/help_links.js index b643ccae947..b37bd11ead0 100644 --- a/erpnext/public/js/help_links.js +++ b/erpnext/public/js/help_links.js @@ -14,9 +14,7 @@ frappe.help.help_links["Form/Rename Tool"] = [ frappe.help.help_links["List/User"] = [ { label: "New User", - url: - docsUrl + - "user/manual/en/setting-up/users-and-permissions/adding-users", + url: docsUrl + "user/manual/en/setting-up/users-and-permissions/adding-users", }, { label: "Rename User", @@ -27,9 +25,7 @@ frappe.help.help_links["List/User"] = [ frappe.help.help_links["permission-manager"] = [ { label: "Role Permissions Manager", - url: - docsUrl + - "user/manual/en/setting-up/users-and-permissions/role-based-permissions", + url: docsUrl + "user/manual/en/setting-up/users-and-permissions/role-based-permissions", }, { label: "Managing Perm Level in Permissions Manager", @@ -37,14 +33,11 @@ frappe.help.help_links["permission-manager"] = [ }, { label: "User Permissions", - url: - docsUrl + - "user/manual/en/setting-up/users-and-permissions/user-permissions", + url: docsUrl + "user/manual/en/setting-up/users-and-permissions/user-permissions", }, { label: "Sharing", - url: - docsUrl + "user/manual/en/setting-up/users-and-permissions/sharing", + url: docsUrl + "user/manual/en/setting-up/users-and-permissions/sharing", }, { label: "Password", @@ -66,9 +59,7 @@ frappe.help.help_links["Form/Data Import"] = [ }, { label: "Overwriting Data from Data Import Tool", - url: - docsUrl + - "user/manual/en/setting-up/articles/overwriting-data-from-data-import-tool", + url: docsUrl + "user/manual/en/setting-up/articles/overwriting-data-from-data-import-tool", }, ]; @@ -79,18 +70,14 @@ frappe.help.help_links["List/Data Import"] = [ }, { label: "Overwriting Data from Data Import Tool", - url: - docsUrl + - "user/manual/en/setting-up/articles/overwriting-data-from-data-import-tool", + url: docsUrl + "user/manual/en/setting-up/articles/overwriting-data-from-data-import-tool", }, ]; frappe.help.help_links["module_setup"] = [ { label: "Role Permissions Manager", - url: - docsUrl + - "user/manual/en/setting-up/users-and-permissions/role-based-permissions", + url: docsUrl + "user/manual/en/setting-up/users-and-permissions/role-based-permissions", }, ]; @@ -101,9 +88,7 @@ frappe.help.help_links["Form/Naming Series"] = [ }, { label: "Setting the Current Value for Naming Series", - url: - docsUrl + - "user/manual/en/setting-up/articles/naming-series-current-value", + url: docsUrl + "user/manual/en/setting-up/articles/naming-series-current-value", }, ]; @@ -217,18 +202,14 @@ frappe.help.help_links["print-format-builder"] = [ frappe.help.help_links["Form/PayPal Settings"] = [ { label: "PayPal Settings", - url: - docsUrl + - "user/manual/en/erpnext_integration/paypal-integration", + url: docsUrl + "user/manual/en/erpnext_integration/paypal-integration", }, ]; frappe.help.help_links["Form/Razorpay Settings"] = [ { label: "Razorpay Settings", - url: - docsUrl + - "user/manual/en/erpnext_integration/razorpay-integration", + url: docsUrl + "user/manual/en/erpnext_integration/razorpay-integration", }, ]; @@ -242,17 +223,14 @@ frappe.help.help_links["Form/Dropbox Settings"] = [ frappe.help.help_links["Form/LDAP Settings"] = [ { label: "LDAP Settings", - url: - docsUrl + "user/manual/en/erpnext_integration/ldap-integration", + url: docsUrl + "user/manual/en/erpnext_integration/ldap-integration", }, ]; frappe.help.help_links["Form/Stripe Settings"] = [ { label: "Stripe Settings", - url: - docsUrl + - "user/manual/en/erpnext_integration/stripe-integration", + url: docsUrl + "user/manual/en/erpnext_integration/stripe-integration", }, ]; @@ -266,9 +244,7 @@ frappe.help.help_links["Form/Quotation"] = [ }, { label: "Sales Person", - url: - docsUrl + - "user/manual/en/selling/articles/sales-persons-in-the-sales-transactions", + url: docsUrl + "user/manual/en/selling/articles/sales-persons-in-the-sales-transactions", }, { label: "Applying Margin", @@ -340,9 +316,7 @@ frappe.help.help_links["Form/Sales Order"] = [ }, { label: "Sales Person", - url: - docsUrl + - "user/manual/en/selling/articles/sales-persons-in-the-sales-transactions", + url: docsUrl + "user/manual/en/selling/articles/sales-persons-in-the-sales-transactions", }, { label: "Close Sales Order", @@ -421,15 +395,11 @@ frappe.help.help_links["Form/Purchase Order"] = [ }, { label: "Item UoM", - url: - docsUrl + - "user/manual/en/buying/articles/purchasing-in-different-unit", + url: docsUrl + "user/manual/en/buying/articles/purchasing-in-different-unit", }, { label: "Supplier Item Code", - url: - docsUrl + - "user/manual/en/buying/articles/maintaining-suppliers-part-no-in-item", + url: docsUrl + "user/manual/en/buying/articles/maintaining-suppliers-part-no-in-item", }, { label: "Recurring Purchase Order", @@ -472,9 +442,7 @@ frappe.help.help_links["Form/SMS Settings"] = [ frappe.help.help_links["List/Stock Reconciliation"] = [ { label: "Stock Reconciliation", - url: - docsUrl + - "user/manual/en/stock/stock-reconciliation", + url: docsUrl + "user/manual/en/stock/stock-reconciliation", }, ]; @@ -496,9 +464,7 @@ frappe.help.help_links["List/Company"] = [ }, { label: "Delete All Related Transactions for a Company", - url: - docsUrl + - "user/manual/en/setting-up/articles/delete-a-company-and-all-related-transactions", + url: docsUrl + "user/manual/en/setting-up/articles/delete-a-company-and-all-related-transactions", }, ]; @@ -511,9 +477,7 @@ frappe.help.help_links["Tree/Account"] = [ }, { label: "Managing Tree Mastes", - url: - docsUrl + - "user/manual/en/setting-up/articles/managing-tree-structure-masters", + url: docsUrl + "user/manual/en/setting-up/articles/managing-tree-structure-masters", }, ]; @@ -648,8 +612,7 @@ frappe.help.help_links["List/Item"] = [ }, { label: "Barcode", - url: - docsUrl + "user/manual/en/stock/articles/track-items-using-barcode", + url: docsUrl + "user/manual/en/stock/articles/track-items-using-barcode", }, { label: "Item Wise Taxation", @@ -669,9 +632,7 @@ frappe.help.help_links["List/Item"] = [ }, { label: "Item Valuation", - url: - docsUrl + - "user/manual/en/stock/articles/item-valuation-fifo-and-moving-average", + url: docsUrl + "user/manual/en/stock/articles/item-valuation-fifo-and-moving-average", }, ]; @@ -683,8 +644,7 @@ frappe.help.help_links["Form/Item"] = [ }, { label: "Barcode", - url: - docsUrl + "user/manual/en/stock/articles/track-items-using-barcode", + url: docsUrl + "user/manual/en/stock/articles/track-items-using-barcode", }, { label: "Item Wise Taxation", @@ -704,9 +664,7 @@ frappe.help.help_links["Form/Item"] = [ }, { label: "Item Valuation", - url: - docsUrl + - "user/manual/en/stock/item/item-valuation-fifo-and-moving-average", + url: docsUrl + "user/manual/en/stock/item/item-valuation-fifo-and-moving-average", }, ]; @@ -717,8 +675,7 @@ frappe.help.help_links["List/Purchase Receipt"] = [ }, { label: "Barcode", - url: - docsUrl + "user/manual/en/stock/articles/track-items-using-barcode", + url: docsUrl + "user/manual/en/stock/articles/track-items-using-barcode", }, ]; @@ -729,8 +686,7 @@ frappe.help.help_links["List/Delivery Note"] = [ }, { label: "Barcode", - url: - docsUrl + "user/manual/en/stock/articles/track-items-using-barcode", + url: docsUrl + "user/manual/en/stock/articles/track-items-using-barcode", }, { label: "Sales Return", @@ -749,8 +705,7 @@ frappe.help.help_links["Form/Delivery Note"] = [ }, { label: "Barcode", - url: - docsUrl + "user/manual/en/stock/articles/track-items-using-barcode", + url: docsUrl + "user/manual/en/stock/articles/track-items-using-barcode", }, ]; @@ -772,9 +727,7 @@ frappe.help.help_links["List/Material Request"] = [ }, { label: "Auto-creation of Material Request", - url: - docsUrl + - "user/manual/en/stock/articles/auto-creation-of-material-request", + url: docsUrl + "user/manual/en/stock/articles/auto-creation-of-material-request", }, ]; @@ -785,9 +738,7 @@ frappe.help.help_links["Form/Material Request"] = [ }, { label: "Auto-creation of Material Request", - url: - docsUrl + - "user/manual/en/stock/articles/auto-creation-of-material-request", + url: docsUrl + "user/manual/en/stock/articles/auto-creation-of-material-request", }, ]; @@ -827,13 +778,9 @@ frappe.help.help_links["Form/Serial No"] = [ { label: "Serial No", url: docsUrl + "user/manual/en/stock/serial-no" }, ]; -frappe.help.help_links["List/Batch"] = [ - { label: "Batch", url: docsUrl + "user/manual/en/stock/batch" }, -]; +frappe.help.help_links["List/Batch"] = [{ label: "Batch", url: docsUrl + "user/manual/en/stock/batch" }]; -frappe.help.help_links["Form/Batch"] = [ - { label: "Batch", url: docsUrl + "user/manual/en/stock/batch" }, -]; +frappe.help.help_links["Form/Batch"] = [{ label: "Batch", url: docsUrl + "user/manual/en/stock/batch" }]; frappe.help.help_links["Form/Packing Slip"] = [ { @@ -873,8 +820,7 @@ frappe.help.help_links["Form/Item Attribute"] = [ frappe.help.help_links["Form/UOM"] = [ { label: "Fractions in UOM", - url: - docsUrl + "user/manual/en/stock/articles/managing-fractions-in-uom", + url: docsUrl + "user/manual/en/stock/articles/managing-fractions-in-uom", }, ]; @@ -887,21 +833,15 @@ frappe.help.help_links["Form/Stock Reconciliation"] = [ //CRM -frappe.help.help_links["Form/Lead"] = [ - { label: "Lead", url: docsUrl + "user/manual/en/CRM/lead" }, -]; +frappe.help.help_links["Form/Lead"] = [{ label: "Lead", url: docsUrl + "user/manual/en/CRM/lead" }]; frappe.help.help_links["Form/Opportunity"] = [ { label: "Opportunity", url: docsUrl + "user/manual/en/CRM/opportunity" }, ]; -frappe.help.help_links["Form/Address"] = [ - { label: "Address", url: docsUrl + "user/manual/en/CRM/address" }, -]; +frappe.help.help_links["Form/Address"] = [{ label: "Address", url: docsUrl + "user/manual/en/CRM/address" }]; -frappe.help.help_links["Form/Contact"] = [ - { label: "Contact", url: docsUrl + "user/manual/en/CRM/contact" }, -]; +frappe.help.help_links["Form/Contact"] = [{ label: "Contact", url: docsUrl + "user/manual/en/CRM/contact" }]; frappe.help.help_links["Form/Newsletter"] = [ { label: "Newsletter", url: docsUrl + "user/manual/en/CRM/newsletter" }, @@ -921,15 +861,11 @@ frappe.help.help_links["Tree/Sales Person"] = [ frappe.help.help_links["Form/Sales Person"] = [ { label: "Sales Person Target", - url: - docsUrl + - "user/manual/en/selling/sales-person-target-allocation", + url: docsUrl + "user/manual/en/selling/sales-person-target-allocation", }, { label: "Sales Person in Transactions", - url: - docsUrl + - "user/manual/en/selling/articles/sales-persons-in-the-sales-transactions", + url: docsUrl + "user/manual/en/selling/articles/sales-persons-in-the-sales-transactions", }, ]; @@ -942,9 +878,7 @@ frappe.help.help_links["Form/BOM"] = [ }, { label: "Nested BOM Structure", - url: - docsUrl + - "user/manual/en/manufacturing/articles/managing-multi-level-bom", + url: docsUrl + "user/manual/en/manufacturing/articles/managing-multi-level-bom", }, ]; diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js index 516c98a143d..013ace62d74 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_desktop.js @@ -1,4 +1,4 @@ -import html2canvas from 'html2canvas'; +import html2canvas from "html2canvas"; erpnext.HierarchyChart = class { /* Options: - doctype @@ -13,7 +13,7 @@ erpnext.HierarchyChart = class { this.doctype = doctype; this.setup_page_style(); - this.page.main.addClass('frappe-card'); + this.page.main.addClass("frappe-card"); this.nodes = {}; this.setup_node_class(); @@ -21,10 +21,10 @@ erpnext.HierarchyChart = class { setup_page_style() { this.page.main.css({ - 'min-height': '300px', - 'max-height': '600px', - 'overflow': 'auto', - 'position': 'relative' + "min-height": "300px", + "max-height": "600px", + overflow: "auto", + position: "relative", }); } @@ -32,7 +32,15 @@ erpnext.HierarchyChart = class { let me = this; this.Node = class { constructor({ - id, parent, parent_id, image, name, title, expandable, connections, is_root // eslint-disable-line + id, + parent, + parent_id, + image, + name, + title, + expandable, + connections, + is_root, // eslint-disable-line }) { // to setup values passed via constructor $.extend(this, arguments[0]); @@ -52,14 +60,14 @@ erpnext.HierarchyChart = class { } make_node_element(node) { - let node_card = frappe.render_template('node_card', { + let node_card = frappe.render_template("node_card", { id: node.id, name: node.name, title: node.title, image: node.image, parent: node.parent_id, connections: node.connections, - is_mobile: false + is_mobile: false, }); node.parent.append(node_card); @@ -72,16 +80,16 @@ erpnext.HierarchyChart = class { let me = this; let company = this.page.add_field({ - fieldtype: 'Link', - options: 'Company', - fieldname: 'company', - placeholder: __('Select Company'), - default: frappe.defaults.get_default('company'), + fieldtype: "Link", + options: "Company", + fieldname: "company", + placeholder: __("Select Company"), + default: frappe.defaults.get_default("company"), only_select: true, reqd: 1, change: () => { - me.company = ''; - $('#hierarchy-chart-wrapper').remove(); + me.company = ""; + $("#hierarchy-chart-wrapper").remove(); if (company.get_value()) { me.company = company.get_value(); @@ -92,75 +100,76 @@ erpnext.HierarchyChart = class { me.render_root_nodes(); me.all_nodes_expanded = false; } else { - frappe.throw(__('Please select a company first.')); + frappe.throw(__("Please select a company first.")); } - } + }, }); company.refresh(); - $(`[data-fieldname="company"]`).trigger('change'); - $(`[data-fieldname="company"] .link-field`).css('z-index', 2); + $(`[data-fieldname="company"]`).trigger("change"); + $(`[data-fieldname="company"] .link-field`).css("z-index", 2); } setup_actions() { let me = this; this.page.clear_inner_toolbar(); - this.page.add_inner_button(__('Export'), function() { + this.page.add_inner_button(__("Export"), function () { me.export_chart(); }); - this.page.add_inner_button(__('Expand All'), function() { + this.page.add_inner_button(__("Expand All"), function () { me.load_children(me.root_node, true); me.all_nodes_expanded = true; - me.page.remove_inner_button(__('Expand All')); - me.page.add_inner_button(__('Collapse All'), function() { + me.page.remove_inner_button(__("Expand All")); + me.page.add_inner_button(__("Collapse All"), function () { me.setup_hierarchy(); me.render_root_nodes(); me.all_nodes_expanded = false; - me.page.remove_inner_button(__('Collapse All')); + me.page.remove_inner_button(__("Collapse All")); me.setup_actions(); }); }); } export_chart() { - frappe.dom.freeze(__('Exporting...')); + frappe.dom.freeze(__("Exporting...")); this.page.main.css({ - 'min-height': '', - 'max-height': '', - 'overflow': 'visible', - 'position': 'fixed', - 'left': '0', - 'top': '0' + "min-height": "", + "max-height": "", + overflow: "visible", + position: "fixed", + left: "0", + top: "0", }); - $('.node-card').addClass('exported'); + $(".node-card").addClass("exported"); - html2canvas(document.querySelector('#hierarchy-chart-wrapper'), { + html2canvas(document.querySelector("#hierarchy-chart-wrapper"), { scrollY: -window.scrollY, - scrollX: 0 - }).then(function(canvas) { - // Export the canvas to its data URI representation - let dataURL = canvas.toDataURL('image/png'); + scrollX: 0, + }) + .then(function (canvas) { + // Export the canvas to its data URI representation + let dataURL = canvas.toDataURL("image/png"); - // download the image - let a = document.createElement('a'); - a.href = dataURL; - a.download = 'hierarchy_chart'; - a.click(); - }).finally(() => { - frappe.dom.unfreeze(); - }); + // download the image + let a = document.createElement("a"); + a.href = dataURL; + a.download = "hierarchy_chart"; + a.click(); + }) + .finally(() => { + frappe.dom.unfreeze(); + }); this.setup_page_style(); - $('.node-card').removeClass('exported'); + $(".node-card").removeClass("exported"); } setup_hierarchy() { - if (this.$hierarchy) - this.$hierarchy.remove(); + if (this.$hierarchy) this.$hierarchy.remove(); $(`#connectors`).empty(); @@ -170,18 +179,16 @@ erpnext.HierarchyChart = class {
    • - `); + ` + ); - this.page.main - .find('#hierarchy-chart') - .empty() - .append(this.$hierarchy); + this.page.main.find("#hierarchy-chart").empty().append(this.$hierarchy); this.nodes = {}; } make_svg_markers() { - $('#hierarchy-chart-wrapper').remove(); + $("#hierarchy-chart-wrapper").remove(); this.page.main.append(`
      @@ -209,45 +216,47 @@ erpnext.HierarchyChart = class {
      `); } - render_root_nodes(expanded_view=false) { + render_root_nodes(expanded_view = false) { let me = this; - return frappe.call({ - method: me.method, - args: { - company: me.company - } - }).then(r => { - if (r.message.length) { - let expand_node; - let node; + return frappe + .call({ + method: me.method, + args: { + company: me.company, + }, + }) + .then((r) => { + if (r.message.length) { + let expand_node; + let node; - $.each(r.message, (_i, data) => { - if ($(`[id="${data.id}"]`).length) - return; + $.each(r.message, (_i, data) => { + if ($(`[id="${data.id}"]`).length) return; - node = new me.Node({ - id: data.id, - parent: $('
    • ').appendTo(me.$hierarchy.find('.node-children')), - parent_id: '', - image: data.image, - name: data.name, - title: data.title, - expandable: true, - connections: data.connections, - is_root: true + node = new me.Node({ + id: data.id, + parent: $('
    • ').appendTo( + me.$hierarchy.find(".node-children") + ), + parent_id: "", + image: data.image, + name: data.name, + title: data.title, + expandable: true, + connections: data.connections, + is_root: true, + }); + + if (!expand_node && data.connections) expand_node = node; }); - if (!expand_node && data.connections) - expand_node = node; - }); - - me.root_node = expand_node; - if (!expanded_view) { - me.expand_node(expand_node); + me.root_node = expand_node; + if (!expanded_view) { + me.expand_node(expand_node); + } } - } - }); + }); } expand_node(node) { @@ -263,7 +272,7 @@ erpnext.HierarchyChart = class { this.refresh_connectors(node.parent_id); // rebuild incoming connections - let grandparent = $(`[id="${node.parent_id}"]`).attr('data-parent'); + let grandparent = $(`[id="${node.parent_id}"]`).attr("data-parent"); this.refresh_connectors(grandparent); } @@ -282,18 +291,18 @@ erpnext.HierarchyChart = class { show_active_path(node) { // mark node parent on active path - $(`[id="${node.parent_id}"]`).addClass('active-path'); + $(`[id="${node.parent_id}"]`).addClass("active-path"); } - load_children(node, deep=false) { + load_children(node, deep = false) { if (!this.company) { - frappe.throw(__('Please select a company first.')); + frappe.throw(__("Please select a company first.")); } if (!deep) { frappe.run_serially([ () => this.get_child_nodes(node.id), - (child_nodes) => this.render_child_nodes(node, child_nodes) + (child_nodes) => this.render_child_nodes(node, child_nodes), ]); } else { frappe.run_serially([ @@ -302,26 +311,28 @@ erpnext.HierarchyChart = class { () => this.render_root_nodes(true), () => this.get_all_nodes(), (data_list) => this.render_children_of_all_nodes(data_list), - () => frappe.dom.unfreeze() + () => frappe.dom.unfreeze(), ]); } } get_child_nodes(node_id) { let me = this; - return new Promise(resolve => { - frappe.call({ - method: me.method, - args: { - parent: node_id, - company: me.company - } - }).then(r => resolve(r.message)); + return new Promise((resolve) => { + frappe + .call({ + method: me.method, + args: { + parent: node_id, + company: me.company, + }, + }) + .then((r) => resolve(r.message)); }); } render_child_nodes(node, child_nodes) { - const last_level = this.$hierarchy.find('.level:last').index(); + const last_level = this.$hierarchy.find(".level:last").index(); const current_level = $(`[id="${node.id}"]`).parent().parent().parent().index(); if (last_level === current_level) { @@ -333,7 +344,7 @@ erpnext.HierarchyChart = class { if (!node.$children) { node.$children = $('
        ') .hide() - .appendTo(this.$hierarchy.find('.level:last')); + .appendTo(this.$hierarchy.find(".level:last")); node.$children.empty(); @@ -356,16 +367,16 @@ erpnext.HierarchyChart = class { get_all_nodes() { let me = this; - return new Promise(resolve => { + return new Promise((resolve) => { frappe.call({ - method: 'erpnext.utilities.hierarchy_chart.get_all_nodes', + method: "erpnext.utilities.hierarchy_chart.get_all_nodes", args: { method: me.method, - company: me.company + company: me.company, }, callback: (r) => { resolve(r.message); - } + }, }); }); } @@ -389,16 +400,16 @@ erpnext.HierarchyChart = class { render_child_nodes_for_expanded_view(node, child_nodes) { node.$children = $('
          '); - const last_level = this.$hierarchy.find('.level:last').index(); + const last_level = this.$hierarchy.find(".level:last").index(); const node_level = $(`[id="${node.id}"]`).parent().parent().parent().index(); if (last_level === node_level) { this.$hierarchy.append(`
        • `); - node.$children.appendTo(this.$hierarchy.find('.level:last')); + node.$children.appendTo(this.$hierarchy.find(".level:last")); } else { - node.$children.appendTo(this.$hierarchy.find('.level:eq(' + (node_level + 1) + ')')); + node.$children.appendTo(this.$hierarchy.find(".level:eq(" + (node_level + 1) + ")")); } node.$children.hide().empty(); @@ -436,33 +447,40 @@ erpnext.HierarchyChart = class { const parent_node = document.getElementById(`${parent_id}`); const child_node = document.getElementById(`${child_id}`); - let path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + let path = document.createElementNS("http://www.w3.org/2000/svg", "path"); // we need to connect right side of the parent to the left side of the child node const pos_parent_right = { x: parent_node.offsetLeft + parent_node.offsetWidth, - y: parent_node.offsetTop + parent_node.offsetHeight / 2 + y: parent_node.offsetTop + parent_node.offsetHeight / 2, }; const pos_child_left = { x: child_node.offsetLeft - 5, - y: child_node.offsetTop + child_node.offsetHeight / 2 + y: child_node.offsetTop + child_node.offsetHeight / 2, }; const connector = this.get_connector(pos_parent_right, pos_child_left); - path.setAttribute('d', connector); + path.setAttribute("d", connector); this.set_path_attributes(path, parent_id, child_id); - document.getElementById('connectors').appendChild(path); + document.getElementById("connectors").appendChild(path); } get_connector(pos_parent_right, pos_child_left) { if (pos_parent_right.y === pos_child_left.y) { // don't add arcs if it's a straight line - return "M" + - (pos_parent_right.x) + "," + (pos_parent_right.y) + " " + - "L"+ - (pos_child_left.x) + "," + (pos_child_left.y); + return ( + "M" + + pos_parent_right.x + + "," + + pos_parent_right.y + + " " + + "L" + + pos_child_left.x + + "," + + pos_child_left.y + ); } else { let arc_1 = ""; let arc_2 = ""; @@ -482,15 +500,29 @@ erpnext.HierarchyChart = class { offset = -10; } - return "M" + (pos_parent_right.x) + "," + (pos_parent_right.y) + " " + + return ( + "M" + + pos_parent_right.x + + "," + + pos_parent_right.y + + " " + "L" + - (pos_parent_right.x + 40) + "," + (pos_parent_right.y) + " " + + (pos_parent_right.x + 40) + + "," + + pos_parent_right.y + + " " + arc_1 + "L" + - (pos_parent_right.x + 50) + "," + (pos_child_left.y + offset) + " " + + (pos_parent_right.x + 50) + + "," + + (pos_child_left.y + offset) + + " " + arc_2 + - "L"+ - (pos_child_left.x) + "," + (pos_child_left.y); + "L" + + pos_child_left.x + + "," + + pos_child_left.y + ); } } @@ -499,7 +531,7 @@ erpnext.HierarchyChart = class { path.setAttribute("data-child", child_id); const parent = $(`[id="${parent_id}"]`); - if (parent.hasClass('active')) { + if (parent.hasClass("active")) { path.setAttribute("class", "active-connector"); path.setAttribute("marker-start", "url(#arrowstart-active)"); path.setAttribute("marker-end", "url(#arrowhead-active)"); @@ -512,24 +544,23 @@ erpnext.HierarchyChart = class { set_selected_node(node) { // remove active class from the current node - if (this.selected_node) - this.selected_node.$link.removeClass('active'); + if (this.selected_node) this.selected_node.$link.removeClass("active"); // add active class to the newly selected node this.selected_node = node; - node.$link.addClass('active'); + node.$link.addClass("active"); } collapse_previous_level_nodes(node) { let node_parent = $(`[id="${node.parent_id}"]`); - let previous_level_nodes = node_parent.parent().parent().children('li'); + let previous_level_nodes = node_parent.parent().parent().children("li"); let node_card; - previous_level_nodes.each(function() { - node_card = $(this).find('.node-card'); + previous_level_nodes.each(function () { + node_card = $(this).find(".node-card"); - if (!node_card.hasClass('active-path')) { - node_card.addClass('collapsed'); + if (!node_card.hasClass("active-path")) { + node_card.addClass("collapsed"); } }); } @@ -547,7 +578,7 @@ erpnext.HierarchyChart = class { this.add_connector(node_parent, data.id); }); } - } + }, ]); } @@ -555,13 +586,15 @@ erpnext.HierarchyChart = class { let me = this; let node_element = $(`[id="${node.id}"]`); - node_element.click(function() { + node_element.click(function () { const is_sibling = me.selected_node.parent_id === node.parent_id; if (is_sibling) { me.collapse_node(); - } else if (node_element.is(':visible') - && (node_element.hasClass('collapsed') || node_element.hasClass('active-path'))) { + } else if ( + node_element.is(":visible") && + (node_element.hasClass("collapsed") || node_element.hasClass("active-path")) + ) { me.remove_levels_after_node(node); me.remove_orphaned_connectors(); } @@ -574,18 +607,18 @@ erpnext.HierarchyChart = class { let node_element = $(`[id="${node.id}"]`); let me = this; - node_element.find('.btn-edit-node').click(function() { - frappe.set_route('Form', me.doctype, node.id); + node_element.find(".btn-edit-node").click(function () { + frappe.set_route("Form", me.doctype, node.id); }); } remove_levels_after_node(node) { let level = $(`[id="${node.id}"]`).parent().parent().parent().index(); - level = $('.hierarchy > li:eq('+ level + ')'); - level.nextAll('li').remove(); + level = $(".hierarchy > li:eq(" + level + ")"); + level.nextAll("li").remove(); - let nodes = level.find('.node-card'); + let nodes = level.find(".node-card"); let node_object; $.each(nodes, (_i, element) => { @@ -594,17 +627,16 @@ erpnext.HierarchyChart = class { node_object.$children = null; }); - nodes.removeClass('collapsed active-path'); + nodes.removeClass("collapsed active-path"); } remove_orphaned_connectors() { - let paths = $('#connectors > path'); + let paths = $("#connectors > path"); $.each(paths, (_i, path) => { - const parent = $(path).data('parent'); - const child = $(path).data('child'); + const parent = $(path).data("parent"); + const child = $(path).data("child"); - if ($(`[id="${parent}"]`).length && $(`[id="${child}"]`).length) - return; + if ($(`[id="${parent}"]`).length && $(`[id="${child}"]`).length) return; $(path).remove(); }); diff --git a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js index 512ea508a9d..e8fa078b6fe 100644 --- a/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js +++ b/erpnext/public/js/hierarchy_chart/hierarchy_chart_mobile.js @@ -12,12 +12,12 @@ erpnext.HierarchyChartMobile = class { this.doctype = doctype; this.page.main.css({ - 'min-height': '300px', - 'max-height': '600px', - 'overflow': 'auto', - 'position': 'relative' + "min-height": "300px", + "max-height": "600px", + overflow: "auto", + position: "relative", }); - this.page.main.addClass('frappe-card'); + this.page.main.addClass("frappe-card"); this.nodes = {}; this.setup_node_class(); @@ -27,7 +27,15 @@ erpnext.HierarchyChartMobile = class { let me = this; this.Node = class { constructor({ - id, parent, parent_id, image, name, title, expandable, connections, is_root // eslint-disable-line + id, + parent, + parent_id, + image, + name, + title, + expandable, + connections, + is_root, // eslint-disable-line }) { // to setup values passed via constructor $.extend(this, arguments[0]); @@ -43,19 +51,19 @@ erpnext.HierarchyChartMobile = class { } make_node_element(node) { - let node_card = frappe.render_template('node_card', { + let node_card = frappe.render_template("node_card", { id: node.id, name: node.name, title: node.title, image: node.image, parent: node.parent_id, connections: node.connections, - is_mobile: true + is_mobile: true, }); node.parent.append(node_card); node.$link = $(`[id="${node.id}"]`); - node.$link.addClass('mobile-node'); + node.$link.addClass("mobile-node"); } show() { @@ -63,15 +71,15 @@ erpnext.HierarchyChartMobile = class { let me = this; let company = this.page.add_field({ - fieldtype: 'Link', - options: 'Company', - fieldname: 'company', - placeholder: __('Select Company'), - default: frappe.defaults.get_default('company'), + fieldtype: "Link", + options: "Company", + fieldname: "company", + placeholder: __("Select Company"), + default: frappe.defaults.get_default("company"), only_select: true, reqd: 1, change: () => { - me.company = ''; + me.company = ""; if (company.get_value() && me.company != company.get_value()) { me.company = company.get_value(); @@ -79,8 +87,7 @@ erpnext.HierarchyChartMobile = class { // svg for connectors me.make_svg_markers(); - if (me.$sibling_group) - me.$sibling_group.remove(); + if (me.$sibling_group) me.$sibling_group.remove(); // setup sibling group wrapper me.$sibling_group = $(`
          `); @@ -89,15 +96,15 @@ erpnext.HierarchyChartMobile = class { me.setup_hierarchy(); me.render_root_nodes(); } - } + }, }); company.refresh(); - $(`[data-fieldname="company"]`).trigger('change'); + $(`[data-fieldname="company"]`).trigger("change"); } make_svg_markers() { - $('#arrows').remove(); + $("#arrows").remove(); this.page.main.prepend(` @@ -123,16 +130,15 @@ erpnext.HierarchyChartMobile = class { setup_hierarchy() { $(`#connectors`).empty(); - if (this.$hierarchy) - this.$hierarchy.remove(); + if (this.$hierarchy) this.$hierarchy.remove(); - if (this.$sibling_group) - this.$sibling_group.empty(); + if (this.$sibling_group) this.$sibling_group.empty(); this.$hierarchy = $( `
          • -
          `); + ` + ); this.page.main.append(this.$hierarchy); } @@ -140,42 +146,43 @@ erpnext.HierarchyChartMobile = class { render_root_nodes() { let me = this; - frappe.call({ - method: me.method, - args: { - company: me.company - }, - }).then(r => { - if (r.message.length) { - let root_level = me.$hierarchy.find('.root-level'); - root_level.empty(); + frappe + .call({ + method: me.method, + args: { + company: me.company, + }, + }) + .then((r) => { + if (r.message.length) { + let root_level = me.$hierarchy.find(".root-level"); + root_level.empty(); - $.each(r.message, (_i, data) => { - return new me.Node({ - id: data.id, - parent: root_level, - parent_id: '', - image: data.image, - name: data.name, - title: data.title, - expandable: true, - connections: data.connections, - is_root: true + $.each(r.message, (_i, data) => { + return new me.Node({ + id: data.id, + parent: root_level, + parent_id: "", + image: data.image, + name: data.name, + title: data.title, + expandable: true, + connections: data.connections, + is_root: true, + }); }); - }); - } - }); + } + }); } expand_node(node) { - const is_same_node = (this.selected_node && this.selected_node.id === node.id); + const is_same_node = this.selected_node && this.selected_node.id === node.id; this.set_selected_node(node); this.show_active_path(node); if (this.$sibling_group) { - const sibling_parent = this.$sibling_group.find('.node-group').attr('data-parent'); - if (node.parent_id != '' && node.parent_id != sibling_parent) - this.$sibling_group.empty(); + const sibling_parent = this.$sibling_group.find(".node-group").attr("data-parent"); + if (node.parent_id != "" && node.parent_id != sibling_parent) this.$sibling_group.empty(); } if (!is_same_node) { @@ -184,7 +191,7 @@ erpnext.HierarchyChartMobile = class { this.refresh_connectors(node.parent_id, node.id); // rebuild incoming connections of parent - let grandparent = $(`[id="${node.parent_id}"]`).attr('data-parent'); + let grandparent = $(`[id="${node.parent_id}"]`).attr("data-parent"); this.refresh_connectors(grandparent, node.parent_id); } @@ -202,65 +209,61 @@ erpnext.HierarchyChartMobile = class { // add a collapsed level to show the collapsed parent // and a button beside it to move to that level let node_parent = node.$link.parent(); - node_parent.prepend( - `
          ` - ); + node_parent.prepend(`
          `); - node_parent - .find('.collapsed-level') - .append(node.$link); + node_parent.find(".collapsed-level").append(node.$link); frappe.run_serially([ () => this.get_child_nodes(node.parent_id, node.id), (child_nodes) => this.get_node_group(child_nodes, node.parent_id), - (node_group) => node_parent.find('.collapsed-level').append(node_group), - () => this.setup_node_group_action() + (node_group) => node_parent.find(".collapsed-level").append(node_group), + () => this.setup_node_group_action(), ]); } } show_active_path(node) { // mark node parent on active path - $(`[id="${node.parent_id}"]`).addClass('active-path'); + $(`[id="${node.parent_id}"]`).addClass("active-path"); } load_children(node) { if (!this.company) { - frappe.throw(__('Please select a company first')); + frappe.throw(__("Please select a company first")); } frappe.run_serially([ () => this.get_child_nodes(node.id), - (child_nodes) => this.render_child_nodes(node, child_nodes) + (child_nodes) => this.render_child_nodes(node, child_nodes), ]); } - get_child_nodes(node_id, exclude_node=null) { + get_child_nodes(node_id, exclude_node = null) { let me = this; - return new Promise(resolve => { - frappe.call({ - method: me.method, - args: { - parent: node_id, - company: me.company, - exclude_node: exclude_node - } - }).then(r => resolve(r.message)); + return new Promise((resolve) => { + frappe + .call({ + method: me.method, + args: { + parent: node_id, + company: me.company, + exclude_node: exclude_node, + }, + }) + .then((r) => resolve(r.message)); }); } render_child_nodes(node, child_nodes) { if (!node.$children) { - node.$children = $('
            ') - .hide() - .appendTo(node.$link.parent()); + node.$children = $('
              ').hide().appendTo(node.$link.parent()); node.$children.empty(); if (child_nodes) { $.each(child_nodes, (_i, data) => { this.add_node(node, data); - $(`[id="${data.id}"]`).addClass('active-child'); + $(`[id="${data.id}"]`).addClass("active-child"); setTimeout(() => { this.add_connector(node.id, data.id); @@ -285,7 +288,7 @@ erpnext.HierarchyChartMobile = class { title: data.title, expandable: data.expandable, connections: data.connections, - children: null + children: null, }); } @@ -293,41 +296,49 @@ erpnext.HierarchyChartMobile = class { const parent_node = document.getElementById(`${parent_id}`); const child_node = document.getElementById(`${child_id}`); - const path = document.createElementNS('http://www.w3.org/2000/svg', 'path'); + const path = document.createElementNS("http://www.w3.org/2000/svg", "path"); let connector = null; - if ($(`[id="${parent_id}"]`).hasClass('active')) { + if ($(`[id="${parent_id}"]`).hasClass("active")) { connector = this.get_connector_for_active_node(parent_node, child_node); - } else if ($(`[id="${parent_id}"]`).hasClass('active-path')) { + } else if ($(`[id="${parent_id}"]`).hasClass("active-path")) { connector = this.get_connector_for_collapsed_node(parent_node, child_node); } - path.setAttribute('d', connector); + path.setAttribute("d", connector); this.set_path_attributes(path, parent_id, child_id); - document.getElementById('connectors').appendChild(path); + document.getElementById("connectors").appendChild(path); } get_connector_for_active_node(parent_node, child_node) { // we need to connect the bottom left of the parent to the left side of the child node let pos_parent_bottom = { x: parent_node.offsetLeft + 20, - y: parent_node.offsetTop + parent_node.offsetHeight + y: parent_node.offsetTop + parent_node.offsetHeight, }; let pos_child_left = { x: child_node.offsetLeft - 5, - y: child_node.offsetTop + child_node.offsetHeight / 2 + y: child_node.offsetTop + child_node.offsetHeight / 2, }; let connector = "M" + - (pos_parent_bottom.x) + "," + (pos_parent_bottom.y) + " " + + pos_parent_bottom.x + + "," + + pos_parent_bottom.y + + " " + "L" + - (pos_parent_bottom.x) + "," + (pos_child_left.y - 10) + " " + + pos_parent_bottom.x + + "," + + (pos_child_left.y - 10) + + " " + "a10,10 1 0 0 10,10 " + "L" + - (pos_child_left.x) + "," + (pos_child_left.y); + pos_child_left.x + + "," + + pos_child_left.y; return connector; } @@ -336,18 +347,23 @@ erpnext.HierarchyChartMobile = class { // we need to connect the bottom left of the parent to the top left of the child node let pos_parent_bottom = { x: parent_node.offsetLeft + 20, - y: parent_node.offsetTop + parent_node.offsetHeight + y: parent_node.offsetTop + parent_node.offsetHeight, }; let pos_child_top = { x: child_node.offsetLeft + 20, - y: child_node.offsetTop + y: child_node.offsetTop, }; let connector = "M" + - (pos_parent_bottom.x) + "," + (pos_parent_bottom.y) + " " + + pos_parent_bottom.x + + "," + + pos_parent_bottom.y + + " " + "L" + - (pos_child_top.x) + "," + (pos_child_top.y); + pos_child_top.x + + "," + + pos_child_top.y; return connector; } @@ -357,30 +373,29 @@ erpnext.HierarchyChartMobile = class { path.setAttribute("data-child", child_id); const parent = $(`[id="${parent_id}"]`); - if (parent.hasClass('active')) { + if (parent.hasClass("active")) { path.setAttribute("class", "active-connector"); path.setAttribute("marker-start", "url(#arrowstart-active)"); path.setAttribute("marker-end", "url(#arrowhead-active)"); - } else if (parent.hasClass('active-path')) { + } else if (parent.hasClass("active-path")) { path.setAttribute("class", "collapsed-connector"); } } set_selected_node(node) { // remove .active class from the current node - if (this.selected_node) - this.selected_node.$link.removeClass('active'); + if (this.selected_node) this.selected_node.$link.removeClass("active"); // add active class to the newly selected node this.selected_node = node; - node.$link.addClass('active'); + node.$link.addClass("active"); } setup_node_click_action(node) { let me = this; let node_element = $(`[id="${node.id}"]`); - node_element.click(function() { + node_element.click(function () { let el = null; if (node.is_root) { @@ -388,7 +403,7 @@ erpnext.HierarchyChartMobile = class { me.$hierarchy.empty(); $(`#connectors`).empty(); me.add_node_to_hierarchy(el, node); - } else if (node_element.is(':visible') && node_element.hasClass('active-path')) { + } else if (node_element.is(":visible") && node_element.hasClass("active-path")) { me.remove_levels_after_node(node); me.remove_orphaned_connectors(); } else { @@ -405,17 +420,17 @@ erpnext.HierarchyChartMobile = class { let node_element = $(`[id="${node.id}"]`); let me = this; - node_element.find('.btn-edit-node').click(function() { - frappe.set_route('Form', me.doctype, node.id); + node_element.find(".btn-edit-node").click(function () { + frappe.set_route("Form", me.doctype, node.id); }); } setup_node_group_action() { let me = this; - $('.node-group').on('click', function() { - let parent = $(this).attr('data-parent'); - if (parent == '') { + $(".node-group").on("click", function () { + let parent = $(this).attr("data-parent"); + if (parent == "") { me.setup_hierarchy(); me.render_root_nodes(); } else { @@ -426,8 +441,8 @@ erpnext.HierarchyChartMobile = class { add_node_to_hierarchy(node_element, node) { this.$hierarchy.append(`
            • `); - node_element.removeClass('active-child active-path'); - this.$hierarchy.find('.level:last').append(node_element); + node_element.removeClass("active-child active-path"); + this.$hierarchy.find(".level:last").append(node_element); let node_object = this.nodes[node.id]; node_object.expanded = 0; @@ -435,14 +450,12 @@ erpnext.HierarchyChartMobile = class { this.nodes[node.id] = node_object; } - get_node_group(nodes, parent, collapsed=true) { + get_node_group(nodes, parent, collapsed = true) { let limit = 2; const display_nodes = nodes.slice(0, limit); const extra_nodes = nodes.slice(limit); - let html = display_nodes.map(node => - this.get_avatar(node) - ).join(''); + let html = display_nodes.map((node) => this.get_avatar(node)).join(""); if (extra_nodes.length === 1) { let node = extra_nodes[0]; @@ -452,7 +465,7 @@ erpnext.HierarchyChartMobile = class { ${html}
              + title="${extra_nodes.map((node) => node.name).join(", ")}"> +${extra_nodes.length}
              @@ -460,15 +473,13 @@ erpnext.HierarchyChartMobile = class { } if (html) { - const $node_group = - $(`
              + const $node_group = $(`
              ${html}
              `); - if (collapsed) - $node_group.addClass('collapsed'); + if (collapsed) $node_group.addClass("collapsed"); return $node_group; } @@ -486,7 +497,7 @@ erpnext.HierarchyChartMobile = class { let node_object = this.nodes[parent]; let node = node_object.$link; - node.removeClass('active-child active-path'); + node.removeClass("active-child active-path"); node_object.expanded = 0; node_object.$children = null; this.nodes[node.id] = node_object; @@ -496,11 +507,10 @@ erpnext.HierarchyChartMobile = class { () => this.get_child_nodes(node_object.parent_id, node_object.id), (child_nodes) => this.get_node_group(child_nodes, node_object.parent_id, false), (node_group) => { - if (node_group) - this.$sibling_group.empty().append(node_group); + if (node_group) this.$sibling_group.empty().append(node_group); }, () => this.setup_node_group_action(), - () => this.reattach_and_expand_node(node, node_object) + () => this.reattach_and_expand_node(node, node_object), ]); } @@ -510,7 +520,7 @@ erpnext.HierarchyChartMobile = class { this.$hierarchy.empty().append(`
            • `); - this.$hierarchy.find('.level').append(el); + this.$hierarchy.find(".level").append(el); $(`#connectors`).empty(); this.expand_node(node_object); } @@ -518,13 +528,13 @@ erpnext.HierarchyChartMobile = class { remove_levels_after_node(node) { let level = $(`[id="${node.id}"]`).parent().parent().index(); - level = $('.hierarchy-mobile > li:eq('+ level + ')'); - level.nextAll('li').remove(); + level = $(".hierarchy-mobile > li:eq(" + level + ")"); + level.nextAll("li").remove(); let node_object = this.nodes[node.id]; let current_node = level.find(`[id="${node.id}"]`).detach(); - current_node.removeClass('active-child active-path'); + current_node.removeClass("active-child active-path"); node_object.expanded = 0; node_object.$children = null; @@ -533,13 +543,12 @@ erpnext.HierarchyChartMobile = class { } remove_orphaned_connectors() { - let paths = $('#connectors > path'); + let paths = $("#connectors > path"); $.each(paths, (_i, path) => { - const parent = $(path).data('parent'); - const child = $(path).data('child'); + const parent = $(path).data("parent"); + const child = $(path).data("child"); - if ($(`[id="${parent}"]`).length && $(`[id="${child}"]`).length) - return; + if ($(`[id="${parent}"]`).length && $(`[id="${child}"]`).length) return; $(path).remove(); }); diff --git a/erpnext/public/js/leaflet/leaflet.draw.js b/erpnext/public/js/leaflet/leaflet.draw.js index 26f1e19da5a..53450618780 100755 --- a/erpnext/public/js/leaflet/leaflet.draw.js +++ b/erpnext/public/js/leaflet/leaflet.draw.js @@ -6,138 +6,1731 @@ http://leafletjs.com https://github.com/jacobtoye */ -! function(t, e) { - L.drawVersion = "0.2.3", L.drawLocal = { draw: { toolbar: { actions: { title: "Cancel drawing", text: "Cancel" }, undo: { title: "Delete last point drawn", text: "Delete last point" }, buttons: { polyline: "Draw a polyline", polygon: "Draw a polygon", rectangle: "Draw a rectangle", circle: "Draw a circle", marker: "Draw a marker" } }, handlers: { circle: { tooltip: { start: "Click and drag to draw circle." } }, marker: { tooltip: { start: "Click map to place marker." } }, polygon: { tooltip: { start: "Click to start drawing shape.", cont: "Click to continue drawing shape.", end: "Click first point to close this shape." } }, polyline: { error: "Error: shape edges cannot cross!", tooltip: { start: "Click to start drawing line.", cont: "Click to continue drawing line.", end: "Click last point to finish line." } }, rectangle: { tooltip: { start: "Click and drag to draw rectangle." } }, simpleshape: { tooltip: { end: "Release mouse to finish drawing." } } } }, edit: { toolbar: { actions: { save: { title: "Save changes.", text: "Save" }, cancel: { title: "Cancel editing, discards all changes.", text: "Cancel" } }, buttons: { edit: "Edit layers.", editDisabled: "No layers to edit.", remove: "Delete layers.", removeDisabled: "No layers to delete." } }, handlers: { edit: { tooltip: { text: "Drag handles, or marker to edit feature.", subtext: "Click cancel to undo changes." } }, remove: { tooltip: { text: "Click on a feature to remove" } } } } }, L.Draw = {}, L.Draw.Feature = L.Handler.extend({ includes: L.Mixin.Events, initialize: function(t, e) { this._map = t, this._container = t._container, this._overlayPane = t._panes.overlayPane, this._popupPane = t._panes.popupPane, e && e.shapeOptions && (e.shapeOptions = L.Util.extend({}, this.options.shapeOptions, e.shapeOptions)), L.setOptions(this, e) }, enable: function() { this._enabled || (this.fire("enabled", { handler: this.type }), this._map.fire("draw:drawstart", { layerType: this.type }), L.Handler.prototype.enable.call(this)) }, disable: function() { this._enabled && (L.Handler.prototype.disable.call(this), this._map.fire("draw:drawstop", { layerType: this.type }), this.fire("disabled", { handler: this.type })) }, addHooks: function() { var t = this._map; - t && (L.DomUtil.disableTextSelection(), t.getContainer().focus(), this._tooltip = new L.Tooltip(this._map), L.DomEvent.on(this._container, "keyup", this._cancelDrawing, this)) }, removeHooks: function() { this._map && (L.DomUtil.enableTextSelection(), this._tooltip.dispose(), this._tooltip = null, L.DomEvent.off(this._container, "keyup", this._cancelDrawing, this)) }, setOptions: function(t) { L.setOptions(this, t) }, _fireCreatedEvent: function(t) { this._map.fire("draw:created", { layer: t, layerType: this.type }) }, _cancelDrawing: function(t) { 27 === t.keyCode && this.disable() } }), L.Draw.Polyline = L.Draw.Feature.extend({ statics: { TYPE: "polyline" }, Poly: L.Polyline, options: { allowIntersection: !0, repeatMode: !1, drawError: { color: "#b00b00", timeout: 2500 }, icon: new L.DivIcon({ iconSize: new L.Point(8, 8), className: "leaflet-div-icon leaflet-editing-icon" }), guidelineDistance: 20, maxGuideLineLength: 4e3, shapeOptions: { stroke: !0, color: "#f06eaa", weight: 4, opacity: .5, fill: !1, clickable: !0 }, metric: !0, showLength: !0, zIndexOffset: 2e3 }, initialize: function(t, e) { this.options.drawError.message = L.drawLocal.draw.handlers.polyline.error, e && e.drawError && (e.drawError = L.Util.extend({}, this.options.drawError, e.drawError)), this.type = L.Draw.Polyline.TYPE, L.Draw.Feature.prototype.initialize.call(this, t, e) }, addHooks: function() { L.Draw.Feature.prototype.addHooks.call(this), this._map && (this._markers = [], this._markerGroup = new L.LayerGroup, this._map.addLayer(this._markerGroup), this._poly = new L.Polyline([], this.options.shapeOptions), this._tooltip.updateContent(this._getTooltipText()), this._mouseMarker || (this._mouseMarker = L.marker(this._map.getCenter(), { icon: L.divIcon({ className: "leaflet-mouse-marker", iconAnchor: [20, 20], iconSize: [40, 40] }), opacity: 0, zIndexOffset: this.options.zIndexOffset })), this._mouseMarker.on("mousedown", this._onMouseDown, this).addTo(this._map), this._map.on("mousemove", this._onMouseMove, this).on("mouseup", this._onMouseUp, this).on("zoomend", this._onZoomEnd, this)) }, removeHooks: function() { L.Draw.Feature.prototype.removeHooks.call(this), this._clearHideErrorTimeout(), this._cleanUpShape(), this._map.removeLayer(this._markerGroup), delete this._markerGroup, delete this._markers, this._map.removeLayer(this._poly), delete this._poly, this._mouseMarker.off("mousedown", this._onMouseDown, this).off("mouseup", this._onMouseUp, this), this._map.removeLayer(this._mouseMarker), delete this._mouseMarker, this._clearGuides(), this._map.off("mousemove", this._onMouseMove, this).off("zoomend", this._onZoomEnd, this) }, deleteLastVertex: function() { if (!(this._markers.length <= 1)) { var t = this._markers.pop(), - e = this._poly, - i = this._poly.spliceLatLngs(e.getLatLngs().length - 1, 1)[0]; - this._markerGroup.removeLayer(t), e.getLatLngs().length < 2 && this._map.removeLayer(e), this._vertexChanged(i, !1) } }, addVertex: function(t) { var e = this._markers.length; return e > 0 && !this.options.allowIntersection && this._poly.newLatLngIntersects(t) ? void this._showErrorTooltip() : (this._errorShown && this._hideErrorTooltip(), this._markers.push(this._createMarker(t)), this._poly.addLatLng(t), 2 === this._poly.getLatLngs().length && this._map.addLayer(this._poly), void this._vertexChanged(t, !0)) }, _finishShape: function() { var t = this._poly.newLatLngIntersects(this._poly.getLatLngs()[0], !0); return !this.options.allowIntersection && t || !this._shapeIsValid() ? void this._showErrorTooltip() : (this._fireCreatedEvent(), this.disable(), void(this.options.repeatMode && this.enable())) }, _shapeIsValid: function() { return !0 }, _onZoomEnd: function() { this._updateGuide() }, _onMouseMove: function(t) { var e = t.layerPoint, - i = t.latlng; - this._currentLatLng = i, this._updateTooltip(i), this._updateGuide(e), this._mouseMarker.setLatLng(i), L.DomEvent.preventDefault(t.originalEvent) }, _vertexChanged: function(t, e) { this._updateFinishHandler(), this._updateRunningMeasure(t, e), this._clearGuides(), this._updateTooltip() }, _onMouseDown: function(t) { var e = t.originalEvent; - this._mouseDownOrigin = L.point(e.clientX, e.clientY) }, _onMouseUp: function(e) { if (this._mouseDownOrigin) { var i = L.point(e.originalEvent.clientX, e.originalEvent.clientY).distanceTo(this._mouseDownOrigin); - Math.abs(i) < 9 * (t.devicePixelRatio || 1) && this.addVertex(e.latlng) } - this._mouseDownOrigin = null }, _updateFinishHandler: function() { var t = this._markers.length; - t > 1 && this._markers[t - 1].on("click", this._finishShape, this), t > 2 && this._markers[t - 2].off("click", this._finishShape, this) }, _createMarker: function(t) { var e = new L.Marker(t, { icon: this.options.icon, zIndexOffset: 2 * this.options.zIndexOffset }); return this._markerGroup.addLayer(e), e }, _updateGuide: function(t) { var e = this._markers.length; - e > 0 && (t = t || this._map.latLngToLayerPoint(this._currentLatLng), this._clearGuides(), this._drawGuide(this._map.latLngToLayerPoint(this._markers[e - 1].getLatLng()), t)) }, _updateTooltip: function(t) { var e = this._getTooltipText(); - t && this._tooltip.updatePosition(t), this._errorShown || this._tooltip.updateContent(e) }, _drawGuide: function(t, e) { var i, o, a, s = Math.floor(Math.sqrt(Math.pow(e.x - t.x, 2) + Math.pow(e.y - t.y, 2))), - r = this.options.guidelineDistance, - n = this.options.maxGuideLineLength, - l = s > n ? s - n : r; for (this._guidesContainer || (this._guidesContainer = L.DomUtil.create("div", "leaflet-draw-guides", this._overlayPane)); s > l; l += this.options.guidelineDistance) i = l / s, o = { x: Math.floor(t.x * (1 - i) + i * e.x), y: Math.floor(t.y * (1 - i) + i * e.y) }, a = L.DomUtil.create("div", "leaflet-draw-guide-dash", this._guidesContainer), a.style.backgroundColor = this._errorShown ? this.options.drawError.color : this.options.shapeOptions.color, L.DomUtil.setPosition(a, o) }, _updateGuideColor: function(t) { if (this._guidesContainer) - for (var e = 0, i = this._guidesContainer.childNodes.length; i > e; e++) this._guidesContainer.childNodes[e].style.backgroundColor = t }, _clearGuides: function() { if (this._guidesContainer) - for (; this._guidesContainer.firstChild;) this._guidesContainer.removeChild(this._guidesContainer.firstChild) }, _getTooltipText: function() { var t, e, i = this.options.showLength; return 0 === this._markers.length ? t = { text: L.drawLocal.draw.handlers.polyline.tooltip.start } : (e = i ? this._getMeasurementString() : "", t = 1 === this._markers.length ? { text: L.drawLocal.draw.handlers.polyline.tooltip.cont, subtext: e } : { text: L.drawLocal.draw.handlers.polyline.tooltip.end, subtext: e }), t }, _updateRunningMeasure: function(t, e) { var i, o, a = this._markers.length; - 1 === this._markers.length ? this._measurementRunningTotal = 0 : (i = a - (e ? 2 : 1), o = t.distanceTo(this._markers[i].getLatLng()), this._measurementRunningTotal += o * (e ? 1 : -1)) }, _getMeasurementString: function() { var t, e = this._currentLatLng, - i = this._markers[this._markers.length - 1].getLatLng(); return t = this._measurementRunningTotal + e.distanceTo(i), L.GeometryUtil.readableDistance(t, this.options.metric) }, _showErrorTooltip: function() { this._errorShown = !0, this._tooltip.showAsError().updateContent({ text: this.options.drawError.message }), this._updateGuideColor(this.options.drawError.color), this._poly.setStyle({ color: this.options.drawError.color }), this._clearHideErrorTimeout(), this._hideErrorTimeout = setTimeout(L.Util.bind(this._hideErrorTooltip, this), this.options.drawError.timeout) }, _hideErrorTooltip: function() { this._errorShown = !1, this._clearHideErrorTimeout(), this._tooltip.removeError().updateContent(this._getTooltipText()), this._updateGuideColor(this.options.shapeOptions.color), this._poly.setStyle({ color: this.options.shapeOptions.color }) }, _clearHideErrorTimeout: function() { this._hideErrorTimeout && (clearTimeout(this._hideErrorTimeout), this._hideErrorTimeout = null) }, _cleanUpShape: function() { this._markers.length > 1 && this._markers[this._markers.length - 1].off("click", this._finishShape, this) }, _fireCreatedEvent: function() { var t = new this.Poly(this._poly.getLatLngs(), this.options.shapeOptions); - L.Draw.Feature.prototype._fireCreatedEvent.call(this, t) } }), L.Draw.Polygon = L.Draw.Polyline.extend({ statics: { TYPE: "polygon" }, Poly: L.Polygon, options: { showArea: !1, shapeOptions: { stroke: !0, color: "#f06eaa", weight: 4, opacity: .5, fill: !0, fillColor: null, fillOpacity: .2, clickable: !0 } }, initialize: function(t, e) { L.Draw.Polyline.prototype.initialize.call(this, t, e), this.type = L.Draw.Polygon.TYPE }, _updateFinishHandler: function() { var t = this._markers.length; - 1 === t && this._markers[0].on("click", this._finishShape, this), t > 2 && (this._markers[t - 1].on("dblclick", this._finishShape, this), t > 3 && this._markers[t - 2].off("dblclick", this._finishShape, this)) }, _getTooltipText: function() { var t, e; return 0 === this._markers.length ? t = L.drawLocal.draw.handlers.polygon.tooltip.start : this._markers.length < 3 ? t = L.drawLocal.draw.handlers.polygon.tooltip.cont : (t = L.drawLocal.draw.handlers.polygon.tooltip.end, e = this._getMeasurementString()), { text: t, subtext: e } }, _getMeasurementString: function() { var t = this._area; return t ? L.GeometryUtil.readableArea(t, this.options.metric) : null }, _shapeIsValid: function() { return this._markers.length >= 3 }, _vertexAdded: function() { if (!this.options.allowIntersection && this.options.showArea) { var t = this._poly.getLatLngs(); - this._area = L.GeometryUtil.geodesicArea(t) } }, _cleanUpShape: function() { var t = this._markers.length; - t > 0 && (this._markers[0].off("click", this._finishShape, this), t > 2 && this._markers[t - 1].off("dblclick", this._finishShape, this)) } }), L.SimpleShape = {}, L.Draw.SimpleShape = L.Draw.Feature.extend({ options: { repeatMode: !1 }, initialize: function(t, e) { this._endLabelText = L.drawLocal.draw.handlers.simpleshape.tooltip.end, L.Draw.Feature.prototype.initialize.call(this, t, e) }, addHooks: function() { L.Draw.Feature.prototype.addHooks.call(this), this._map && (this._mapDraggable = this._map.dragging.enabled(), this._mapDraggable && this._map.dragging.disable(), this._container.style.cursor = "crosshair", this._tooltip.updateContent({ text: this._initialLabelText }), this._map.on("mousedown", this._onMouseDown, this).on("mousemove", this._onMouseMove, this)) }, removeHooks: function() { L.Draw.Feature.prototype.removeHooks.call(this), this._map && (this._mapDraggable && this._map.dragging.enable(), this._container.style.cursor = "", this._map.off("mousedown", this._onMouseDown, this).off("mousemove", this._onMouseMove, this), L.DomEvent.off(e, "mouseup", this._onMouseUp, this), this._shape && (this._map.removeLayer(this._shape), delete this._shape)), this._isDrawing = !1 }, _onMouseDown: function(t) { this._isDrawing = !0, this._startLatLng = t.latlng, L.DomEvent.on(e, "mouseup", this._onMouseUp, this).preventDefault(t.originalEvent) }, _onMouseMove: function(t) { var e = t.latlng; - this._tooltip.updatePosition(e), this._isDrawing && (this._tooltip.updateContent({ text: this._endLabelText }), this._drawShape(e)) }, _onMouseUp: function() { this._shape && this._fireCreatedEvent(), this.disable(), this.options.repeatMode && this.enable() } }), L.Draw.Rectangle = L.Draw.SimpleShape.extend({ statics: { TYPE: "rectangle" }, options: { shapeOptions: { stroke: !0, color: "#f06eaa", weight: 4, opacity: .5, fill: !0, fillColor: null, fillOpacity: .2, clickable: !0 } }, initialize: function(t, e) { this.type = L.Draw.Rectangle.TYPE, this._initialLabelText = L.drawLocal.draw.handlers.rectangle.tooltip.start, L.Draw.SimpleShape.prototype.initialize.call(this, t, e) }, _drawShape: function(t) { this._shape ? this._shape.setBounds(new L.LatLngBounds(this._startLatLng, t)) : (this._shape = new L.Rectangle(new L.LatLngBounds(this._startLatLng, t), this.options.shapeOptions), this._map.addLayer(this._shape)) }, _fireCreatedEvent: function() { var t = new L.Rectangle(this._shape.getBounds(), this.options.shapeOptions); - L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this, t) } }), L.Draw.Circle = L.Draw.SimpleShape.extend({ statics: { TYPE: "circle" }, options: { shapeOptions: { stroke: !0, color: "#f06eaa", weight: 4, opacity: .5, fill: !0, fillColor: null, fillOpacity: .2, clickable: !0 }, showRadius: !0, metric: !0 }, initialize: function(t, e) { this.type = L.Draw.Circle.TYPE, this._initialLabelText = L.drawLocal.draw.handlers.circle.tooltip.start, L.Draw.SimpleShape.prototype.initialize.call(this, t, e) }, _drawShape: function(t) { this._shape ? this._shape.setRadius(this._startLatLng.distanceTo(t)) : (this._shape = new L.Circle(this._startLatLng, this._startLatLng.distanceTo(t), this.options.shapeOptions), this._map.addLayer(this._shape)) }, _fireCreatedEvent: function() { var t = new L.Circle(this._startLatLng, this._shape.getRadius(), this.options.shapeOptions); - L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this, t) }, _onMouseMove: function(t) { var e, i = t.latlng, - o = this.options.showRadius, - a = this.options.metric; - this._tooltip.updatePosition(i), this._isDrawing && (this._drawShape(i), e = this._shape.getRadius().toFixed(1), this._tooltip.updateContent({ text: this._endLabelText, subtext: o ? "Radius: " + L.GeometryUtil.readableDistance(e, a) : "" })) } }), L.Draw.Marker = L.Draw.Feature.extend({ statics: { TYPE: "marker" }, options: { icon: new L.Icon.Default, repeatMode: !1, zIndexOffset: 2e3 }, initialize: function(t, e) { this.type = L.Draw.Marker.TYPE, L.Draw.Feature.prototype.initialize.call(this, t, e) }, addHooks: function() { L.Draw.Feature.prototype.addHooks.call(this), this._map && (this._tooltip.updateContent({ text: L.drawLocal.draw.handlers.marker.tooltip.start }), this._mouseMarker || (this._mouseMarker = L.marker(this._map.getCenter(), { icon: L.divIcon({ className: "leaflet-mouse-marker", iconAnchor: [20, 20], iconSize: [40, 40] }), opacity: 0, zIndexOffset: this.options.zIndexOffset })), this._mouseMarker.on("click", this._onClick, this).addTo(this._map), this._map.on("mousemove", this._onMouseMove, this)) }, removeHooks: function() { L.Draw.Feature.prototype.removeHooks.call(this), this._map && (this._marker && (this._marker.off("click", this._onClick, this), this._map.off("click", this._onClick, this).removeLayer(this._marker), delete this._marker), this._mouseMarker.off("click", this._onClick, this), this._map.removeLayer(this._mouseMarker), delete this._mouseMarker, this._map.off("mousemove", this._onMouseMove, this)) }, _onMouseMove: function(t) { var e = t.latlng; - this._tooltip.updatePosition(e), this._mouseMarker.setLatLng(e), this._marker ? (e = this._mouseMarker.getLatLng(), this._marker.setLatLng(e)) : (this._marker = new L.Marker(e, { icon: this.options.icon, zIndexOffset: this.options.zIndexOffset }), this._marker.on("click", this._onClick, this), this._map.on("click", this._onClick, this).addLayer(this._marker)) }, _onClick: function() { this._fireCreatedEvent(), this.disable(), this.options.repeatMode && this.enable() }, _fireCreatedEvent: function() { var t = new L.Marker(this._marker.getLatLng(), { icon: this.options.icon }); - L.Draw.Feature.prototype._fireCreatedEvent.call(this, t) } }), L.Edit = L.Edit || {}, L.Edit.Poly = L.Handler.extend({ options: { icon: new L.DivIcon({ iconSize: new L.Point(8, 8), className: "leaflet-div-icon leaflet-editing-icon" }) }, initialize: function(t, e) { this._poly = t, L.setOptions(this, e) }, addHooks: function() { this._poly._map && (this._markerGroup || this._initMarkers(), this._poly._map.addLayer(this._markerGroup)) }, removeHooks: function() { this._poly._map && (this._poly._map.removeLayer(this._markerGroup), delete this._markerGroup, delete this._markers) }, updateMarkers: function() { this._markerGroup.clearLayers(), this._initMarkers() }, _initMarkers: function() { this._markerGroup || (this._markerGroup = new L.LayerGroup), this._markers = []; var t, e, i, o, a = this._poly._latlngs; for (t = 0, i = a.length; i > t; t++) o = this._createMarker(a[t], t), o.on("click", this._onMarkerClick, this), this._markers.push(o); var s, r; for (t = 0, e = i - 1; i > t; e = t++)(0 !== t || L.Polygon && this._poly instanceof L.Polygon) && (s = this._markers[e], r = this._markers[t], this._createMiddleMarker(s, r), this._updatePrevNext(s, r)) }, _createMarker: function(t, e) { var i = new L.Marker(t, { draggable: !0, icon: this.options.icon }); return i._origLatLng = t, i._index = e, i.on("drag", this._onMarkerDrag, this), i.on("dragend", this._fireEdit, this), this._markerGroup.addLayer(i), i }, _removeMarker: function(t) { var e = t._index; - this._markerGroup.removeLayer(t), this._markers.splice(e, 1), this._poly.spliceLatLngs(e, 1), this._updateIndexes(e, -1), t.off("drag", this._onMarkerDrag, this).off("dragend", this._fireEdit, this).off("click", this._onMarkerClick, this) }, _fireEdit: function() { this._poly.edited = !0, this._poly.fire("edit") }, _onMarkerDrag: function(t) { var e = t.target; - L.extend(e._origLatLng, e._latlng), e._middleLeft && e._middleLeft.setLatLng(this._getMiddleLatLng(e._prev, e)), e._middleRight && e._middleRight.setLatLng(this._getMiddleLatLng(e, e._next)), this._poly.redraw() }, _onMarkerClick: function(t) { var e = L.Polygon && this._poly instanceof L.Polygon ? 4 : 3, - i = t.target; - this._poly._latlngs.length < e || (this._removeMarker(i), this._updatePrevNext(i._prev, i._next), i._middleLeft && this._markerGroup.removeLayer(i._middleLeft), i._middleRight && this._markerGroup.removeLayer(i._middleRight), i._prev && i._next ? this._createMiddleMarker(i._prev, i._next) : i._prev ? i._next || (i._prev._middleRight = null) : i._next._middleLeft = null, this._fireEdit()) }, _updateIndexes: function(t, e) { this._markerGroup.eachLayer(function(i) { i._index > t && (i._index += e) }) }, _createMiddleMarker: function(t, e) { var i, o, a, s = this._getMiddleLatLng(t, e), - r = this._createMarker(s); - r.setOpacity(.6), t._middleRight = e._middleLeft = r, o = function() { var o = e._index; - r._index = o, r.off("click", i, this).on("click", this._onMarkerClick, this), s.lat = r.getLatLng().lat, s.lng = r.getLatLng().lng, this._poly.spliceLatLngs(o, 0, s), this._markers.splice(o, 0, r), r.setOpacity(1), this._updateIndexes(o, 1), e._index++, this._updatePrevNext(t, r), this._updatePrevNext(r, e), this._poly.fire("editstart") }, a = function() { r.off("dragstart", o, this), r.off("dragend", a, this), this._createMiddleMarker(t, r), this._createMiddleMarker(r, e) }, i = function() { o.call(this), a.call(this), this._fireEdit() }, r.on("click", i, this).on("dragstart", o, this).on("dragend", a, this), this._markerGroup.addLayer(r) }, _updatePrevNext: function(t, e) { t && (t._next = e), e && (e._prev = t) }, _getMiddleLatLng: function(t, e) { var i = this._poly._map, - o = i.project(t.getLatLng()), - a = i.project(e.getLatLng()); return i.unproject(o._add(a)._divideBy(2)) } }), L.Polyline.addInitHook(function() { this.editing || (L.Edit.Poly && (this.editing = new L.Edit.Poly(this), this.options.editable && this.editing.enable()), this.on("add", function() { this.editing && this.editing.enabled() && this.editing.addHooks() }), this.on("remove", function() { this.editing && this.editing.enabled() && this.editing.removeHooks() })) }), L.Edit = L.Edit || {}, L.Edit.SimpleShape = L.Handler.extend({ options: { moveIcon: new L.DivIcon({ iconSize: new L.Point(8, 8), className: "leaflet-div-icon leaflet-editing-icon leaflet-edit-move" }), resizeIcon: new L.DivIcon({ iconSize: new L.Point(8, 8), className: "leaflet-div-icon leaflet-editing-icon leaflet-edit-resize" }) }, initialize: function(t, e) { this._shape = t, L.Util.setOptions(this, e) }, addHooks: function() { this._shape._map && (this._map = this._shape._map, this._markerGroup || this._initMarkers(), this._map.addLayer(this._markerGroup)) }, removeHooks: function() { if (this._shape._map) { this._unbindMarker(this._moveMarker); for (var t = 0, e = this._resizeMarkers.length; e > t; t++) this._unbindMarker(this._resizeMarkers[t]); - this._resizeMarkers = null, this._map.removeLayer(this._markerGroup), delete this._markerGroup } - this._map = null }, updateMarkers: function() { this._markerGroup.clearLayers(), this._initMarkers() }, _initMarkers: function() { this._markerGroup || (this._markerGroup = new L.LayerGroup), this._createMoveMarker(), this._createResizeMarker() }, _createMoveMarker: function() {}, _createResizeMarker: function() {}, _createMarker: function(t, e) { var i = new L.Marker(t, { draggable: !0, icon: e, zIndexOffset: 10 }); return this._bindMarker(i), this._markerGroup.addLayer(i), i }, _bindMarker: function(t) { t.on("dragstart", this._onMarkerDragStart, this).on("drag", this._onMarkerDrag, this).on("dragend", this._onMarkerDragEnd, this) }, _unbindMarker: function(t) { t.off("dragstart", this._onMarkerDragStart, this).off("drag", this._onMarkerDrag, this).off("dragend", this._onMarkerDragEnd, this) }, _onMarkerDragStart: function(t) { var e = t.target; - e.setOpacity(0), this._shape.fire("editstart") }, _fireEdit: function() { this._shape.edited = !0, this._shape.fire("edit") }, _onMarkerDrag: function(t) { var e = t.target, - i = e.getLatLng(); - e === this._moveMarker ? this._move(i) : this._resize(i), this._shape.redraw() }, _onMarkerDragEnd: function(t) { var e = t.target; - e.setOpacity(1), this._fireEdit() }, _move: function() {}, _resize: function() {} }), L.Edit = L.Edit || {}, L.Edit.Rectangle = L.Edit.SimpleShape.extend({ _createMoveMarker: function() { var t = this._shape.getBounds(), - e = t.getCenter(); - this._moveMarker = this._createMarker(e, this.options.moveIcon) }, _createResizeMarker: function() { var t = this._getCorners(); - this._resizeMarkers = []; for (var e = 0, i = t.length; i > e; e++) this._resizeMarkers.push(this._createMarker(t[e], this.options.resizeIcon)), this._resizeMarkers[e]._cornerIndex = e }, _onMarkerDragStart: function(t) { L.Edit.SimpleShape.prototype._onMarkerDragStart.call(this, t); var e = this._getCorners(), - i = t.target, - o = i._cornerIndex; - this._oppositeCorner = e[(o + 2) % 4], this._toggleCornerMarkers(0, o) }, _onMarkerDragEnd: function(t) { var e, i, o = t.target; - o === this._moveMarker && (e = this._shape.getBounds(), i = e.getCenter(), o.setLatLng(i)), this._toggleCornerMarkers(1), this._repositionCornerMarkers(), L.Edit.SimpleShape.prototype._onMarkerDragEnd.call(this, t) }, _move: function(t) { for (var e, i = this._shape.getLatLngs(), o = this._shape.getBounds(), a = o.getCenter(), s = [], r = 0, n = i.length; n > r; r++) e = [i[r].lat - a.lat, i[r].lng - a.lng], s.push([t.lat + e[0], t.lng + e[1]]); - this._shape.setLatLngs(s), this._repositionCornerMarkers() }, _resize: function(t) { var e; - this._shape.setBounds(L.latLngBounds(t, this._oppositeCorner)), e = this._shape.getBounds(), this._moveMarker.setLatLng(e.getCenter()) }, _getCorners: function() { var t = this._shape.getBounds(), - e = t.getNorthWest(), - i = t.getNorthEast(), - o = t.getSouthEast(), - a = t.getSouthWest(); return [e, i, o, a] }, _toggleCornerMarkers: function(t) { for (var e = 0, i = this._resizeMarkers.length; i > e; e++) this._resizeMarkers[e].setOpacity(t) }, _repositionCornerMarkers: function() { for (var t = this._getCorners(), e = 0, i = this._resizeMarkers.length; i > e; e++) this._resizeMarkers[e].setLatLng(t[e]) } }), L.Rectangle.addInitHook(function() { L.Edit.Rectangle && (this.editing = new L.Edit.Rectangle(this), this.options.editable && this.editing.enable()) }), L.Edit = L.Edit || {}, L.Edit.Circle = L.Edit.SimpleShape.extend({ _createMoveMarker: function() { var t = this._shape.getLatLng(); - this._moveMarker = this._createMarker(t, this.options.moveIcon) }, _createResizeMarker: function() { var t = this._shape.getLatLng(), - e = this._getResizeMarkerPoint(t); - this._resizeMarkers = [], this._resizeMarkers.push(this._createMarker(e, this.options.resizeIcon)) }, _getResizeMarkerPoint: function(t) { var e = this._shape._radius * Math.cos(Math.PI / 4), - i = this._map.project(t); return this._map.unproject([i.x + e, i.y - e]) }, _move: function(t) { var e = this._getResizeMarkerPoint(t); - this._resizeMarkers[0].setLatLng(e), this._shape.setLatLng(t) }, _resize: function(t) { var e = this._moveMarker.getLatLng(), - i = e.distanceTo(t); - this._shape.setRadius(i) } }), L.Circle.addInitHook(function() { L.Edit.Circle && (this.editing = new L.Edit.Circle(this), this.options.editable && this.editing.enable()), this.on("add", function() { this.editing && this.editing.enabled() && this.editing.addHooks() }), this.on("remove", function() { this.editing && this.editing.enabled() && this.editing.removeHooks() }) }), L.LatLngUtil = { cloneLatLngs: function(t) { for (var e = [], i = 0, o = t.length; o > i; i++) e.push(this.cloneLatLng(t[i])); return e }, cloneLatLng: function(t) { return L.latLng(t.lat, t.lng) } }, L.GeometryUtil = L.extend(L.GeometryUtil || {}, { geodesicArea: function(t) { var e, i, o = t.length, - a = 0, - s = L.LatLng.DEG_TO_RAD; if (o > 2) { for (var r = 0; o > r; r++) e = t[r], i = t[(r + 1) % o], a += (i.lng - e.lng) * s * (2 + Math.sin(e.lat * s) + Math.sin(i.lat * s)); - a = 6378137 * a * 6378137 / 2 } return Math.abs(a) }, readableArea: function(t, e) { var i; return e ? i = t >= 1e4 ? (1e-4 * t).toFixed(2) + " ha" : t.toFixed(2) + " m²" : (t *= .836127, i = t >= 3097600 ? (t / 3097600).toFixed(2) + " mi²" : t >= 4840 ? (t / 4840).toFixed(2) + " acres" : Math.ceil(t) + " yd²"), i }, readableDistance: function(t, e) { var i; return e ? i = t > 1e3 ? (t / 1e3).toFixed(2) + " km" : Math.ceil(t) + " m" : (t *= 1.09361, i = t > 1760 ? (t / 1760).toFixed(2) + " miles" : Math.ceil(t) + " yd"), i } }), L.Util.extend(L.LineUtil, { segmentsIntersect: function(t, e, i, o) { return this._checkCounterclockwise(t, i, o) !== this._checkCounterclockwise(e, i, o) && this._checkCounterclockwise(t, e, i) !== this._checkCounterclockwise(t, e, o) }, _checkCounterclockwise: function(t, e, i) { return (i.y - t.y) * (e.x - t.x) > (e.y - t.y) * (i.x - t.x) } }), L.Polyline.include({ intersects: function() { var t, e, i, o = this._originalPoints, - a = o ? o.length : 0; if (this._tooFewPointsForIntersection()) return !1; for (t = a - 1; t >= 3; t--) - if (e = o[t - 1], i = o[t], this._lineSegmentsIntersectsRange(e, i, t - 2)) return !0; - return !1 }, newLatLngIntersects: function(t, e) { return this._map ? this.newPointIntersects(this._map.latLngToLayerPoint(t), e) : !1 }, newPointIntersects: function(t, e) { var i = this._originalPoints, - o = i ? i.length : 0, - a = i ? i[o - 1] : null, - s = o - 2; return this._tooFewPointsForIntersection(1) ? !1 : this._lineSegmentsIntersectsRange(a, t, s, e ? 1 : 0) }, _tooFewPointsForIntersection: function(t) { var e = this._originalPoints, - i = e ? e.length : 0; return i += t || 0, !this._originalPoints || 3 >= i }, _lineSegmentsIntersectsRange: function(t, e, i, o) { var a, s, r = this._originalPoints; - o = o || 0; for (var n = i; n > o; n--) - if (a = r[n - 1], s = r[n], L.LineUtil.segmentsIntersect(t, e, a, s)) return !0; - return !1 } }), L.Polygon.include({ intersects: function() { var t, e, i, o, a, s = this._originalPoints; return this._tooFewPointsForIntersection() ? !1 : (t = L.Polyline.prototype.intersects.call(this)) ? !0 : (e = s.length, i = s[0], o = s[e - 1], a = e - 2, this._lineSegmentsIntersectsRange(o, i, a, 1)) } }), L.Control.Draw = L.Control.extend({ options: { position: "topleft", draw: {}, edit: !1 }, initialize: function(t) { if (L.version < "0.7") throw new Error("Leaflet.draw 0.2.3+ requires Leaflet 0.7.0+. Download latest from https://github.com/Leaflet/Leaflet/"); - L.Control.prototype.initialize.call(this, t); var e, i; - this._toolbars = {}, L.DrawToolbar && this.options.draw && (i = new L.DrawToolbar(this.options.draw), e = L.stamp(i), this._toolbars[e] = i, this._toolbars[e].on("enable", this._toolbarEnabled, this)), L.EditToolbar && this.options.edit && (i = new L.EditToolbar(this.options.edit), e = L.stamp(i), this._toolbars[e] = i, this._toolbars[e].on("enable", this._toolbarEnabled, this)) }, onAdd: function(t) { var e, i = L.DomUtil.create("div", "leaflet-draw"), - o = !1, - a = "leaflet-draw-toolbar-top"; for (var s in this._toolbars) this._toolbars.hasOwnProperty(s) && (e = this._toolbars[s].addToolbar(t), e && (o || (L.DomUtil.hasClass(e, a) || L.DomUtil.addClass(e.childNodes[0], a), o = !0), i.appendChild(e))); return i }, onRemove: function() { for (var t in this._toolbars) this._toolbars.hasOwnProperty(t) && this._toolbars[t].removeToolbar() }, setDrawingOptions: function(t) { for (var e in this._toolbars) this._toolbars[e] instanceof L.DrawToolbar && this._toolbars[e].setOptions(t) }, _toolbarEnabled: function(t) { var e = "" + L.stamp(t.target); for (var i in this._toolbars) this._toolbars.hasOwnProperty(i) && i !== e && this._toolbars[i].disable() } }), L.Map.mergeOptions({ drawControlTooltips: !0, drawControl: !1 }), L.Map.addInitHook(function() { this.options.drawControl && (this.drawControl = new L.Control.Draw, this.addControl(this.drawControl)) }), L.Toolbar = L.Class.extend({ - includes: [L.Mixin.Events], - initialize: function(t) { L.setOptions(this, t), this._modes = {}, this._actionButtons = [], this._activeMode = null }, - enabled: function() { return null !== this._activeMode }, - disable: function() { this.enabled() && this._activeMode.handler.disable() }, - addToolbar: function(t) { var e, i = L.DomUtil.create("div", "leaflet-draw-section"), - o = 0, - a = this._toolbarClass || "", - s = this.getModeHandlers(t); for (this._toolbarContainer = L.DomUtil.create("div", "leaflet-draw-toolbar leaflet-bar"), this._map = t, e = 0; e < s.length; e++) s[e].enabled && this._initModeHandler(s[e].handler, this._toolbarContainer, o++, a, s[e].title); return o ? (this._lastButtonIndex = --o, this._actionsContainer = L.DomUtil.create("ul", "leaflet-draw-actions"), i.appendChild(this._toolbarContainer), i.appendChild(this._actionsContainer), i) : void 0 }, - removeToolbar: function() { for (var t in this._modes) this._modes.hasOwnProperty(t) && (this._disposeButton(this._modes[t].button, this._modes[t].handler.enable, this._modes[t].handler), this._modes[t].handler.disable(), this._modes[t].handler.off("enabled", this._handlerActivated, this).off("disabled", this._handlerDeactivated, this)); - this._modes = {}; for (var e = 0, i = this._actionButtons.length; i > e; e++) this._disposeButton(this._actionButtons[e].button, this._actionButtons[e].callback, this); - this._actionButtons = [], this._actionsContainer = null }, - _initModeHandler: function(t, e, i, o, a) { var s = t.type; - this._modes[s] = {}, this._modes[s].handler = t, this._modes[s].button = this._createButton({ title: a, className: o + "-" + s, container: e, callback: this._modes[s].handler.enable, context: this._modes[s].handler }), this._modes[s].buttonIndex = i, this._modes[s].handler.on("enabled", this._handlerActivated, this).on("disabled", this._handlerDeactivated, this) }, - _createButton: function(t) { var e = L.DomUtil.create("a", t.className || "", t.container); return e.href = "#", t.text && (e.innerHTML = t.text), t.title && (e.title = t.title), L.DomEvent.on(e, "click", L.DomEvent.stopPropagation).on(e, "mousedown", L.DomEvent.stopPropagation).on(e, "dblclick", L.DomEvent.stopPropagation).on(e, "click", L.DomEvent.preventDefault).on(e, "click", t.callback, t.context), e }, - _disposeButton: function(t, e) { L.DomEvent.off(t, "click", L.DomEvent.stopPropagation).off(t, "mousedown", L.DomEvent.stopPropagation).off(t, "dblclick", L.DomEvent.stopPropagation).off(t, "click", L.DomEvent.preventDefault).off(t, "click", e) }, - _handlerActivated: function(t) { this.disable(), this._activeMode = this._modes[t.handler], L.DomUtil.addClass(this._activeMode.button, "leaflet-draw-toolbar-button-enabled"), this._showActionsToolbar(), this.fire("enable") }, - _handlerDeactivated: function() { this._hideActionsToolbar(), L.DomUtil.removeClass(this._activeMode.button, "leaflet-draw-toolbar-button-enabled"), this._activeMode = null, this.fire("disable") }, - _createActions: function(t) { var e, i, o, a, s = this._actionsContainer, - r = this.getActions(t), - n = r.length; for (i = 0, o = this._actionButtons.length; o > i; i++) this._disposeButton(this._actionButtons[i].button, this._actionButtons[i].callback); for (this._actionButtons = []; s.firstChild;) s.removeChild(s.firstChild); for (var l = 0; n > l; l++) "enabled" in r[l] && !r[l].enabled || (e = L.DomUtil.create("li", "", s), a = this._createButton({ title: r[l].title, text: r[l].text, container: e, callback: r[l].callback, context: r[l].context }), this._actionButtons.push({ button: a, callback: r[l].callback })) }, - _showActionsToolbar: function() { - var t = this._activeMode.buttonIndex, - e = this._lastButtonIndex, - i = this._activeMode.button.offsetTop - 1; - this._createActions(this._activeMode.handler), this._actionsContainer.style.top = i + "px", 0 === t && (L.DomUtil.addClass(this._toolbarContainer, "leaflet-draw-toolbar-notop"), L.DomUtil.addClass(this._actionsContainer, "leaflet-draw-actions-top")), t === e && (L.DomUtil.addClass(this._toolbarContainer, "leaflet-draw-toolbar-nobottom"), L.DomUtil.addClass(this._actionsContainer, "leaflet-draw-actions-bottom")), this._actionsContainer.style.display = "block" - }, - _hideActionsToolbar: function() { this._actionsContainer.style.display = "none", L.DomUtil.removeClass(this._toolbarContainer, "leaflet-draw-toolbar-notop"), L.DomUtil.removeClass(this._toolbarContainer, "leaflet-draw-toolbar-nobottom"), L.DomUtil.removeClass(this._actionsContainer, "leaflet-draw-actions-top"), L.DomUtil.removeClass(this._actionsContainer, "leaflet-draw-actions-bottom") } - }), L.Tooltip = L.Class.extend({ initialize: function(t) { this._map = t, this._popupPane = t._panes.popupPane, this._container = t.options.drawControlTooltips ? L.DomUtil.create("div", "leaflet-draw-tooltip", this._popupPane) : null, this._singleLineLabel = !1 }, dispose: function() { this._container && (this._popupPane.removeChild(this._container), this._container = null) }, updateContent: function(t) { return this._container ? (t.subtext = t.subtext || "", 0 !== t.subtext.length || this._singleLineLabel ? t.subtext.length > 0 && this._singleLineLabel && (L.DomUtil.removeClass(this._container, "leaflet-draw-tooltip-single"), this._singleLineLabel = !1) : (L.DomUtil.addClass(this._container, "leaflet-draw-tooltip-single"), this._singleLineLabel = !0), this._container.innerHTML = (t.subtext.length > 0 ? '' + t.subtext + "
              " : "") + "" + t.text + "", this) : this }, updatePosition: function(t) { var e = this._map.latLngToLayerPoint(t), - i = this._container; return this._container && (i.style.visibility = "inherit", L.DomUtil.setPosition(i, e)), this }, showAsError: function() { return this._container && L.DomUtil.addClass(this._container, "leaflet-error-draw-tooltip"), this }, removeError: function() { return this._container && L.DomUtil.removeClass(this._container, "leaflet-error-draw-tooltip"), this } }), L.DrawToolbar = L.Toolbar.extend({ options: { polyline: {}, polygon: {}, rectangle: {}, circle: {}, marker: {} }, initialize: function(t) { for (var e in this.options) this.options.hasOwnProperty(e) && t[e] && (t[e] = L.extend({}, this.options[e], t[e])); - this._toolbarClass = "leaflet-draw-draw", L.Toolbar.prototype.initialize.call(this, t) }, getModeHandlers: function(t) { return [{ enabled: this.options.polyline, handler: new L.Draw.Polyline(t, this.options.polyline), title: L.drawLocal.draw.toolbar.buttons.polyline }, { enabled: this.options.polygon, handler: new L.Draw.Polygon(t, this.options.polygon), title: L.drawLocal.draw.toolbar.buttons.polygon }, { enabled: this.options.rectangle, handler: new L.Draw.Rectangle(t, this.options.rectangle), title: L.drawLocal.draw.toolbar.buttons.rectangle }, { enabled: this.options.circle, handler: new L.Draw.Circle(t, this.options.circle), title: L.drawLocal.draw.toolbar.buttons.circle }, { enabled: this.options.marker, handler: new L.Draw.Marker(t, this.options.marker), title: L.drawLocal.draw.toolbar.buttons.marker }] }, getActions: function(t) { return [{ enabled: t.deleteLastVertex, title: L.drawLocal.draw.toolbar.undo.title, text: L.drawLocal.draw.toolbar.undo.text, callback: t.deleteLastVertex, context: t }, { title: L.drawLocal.draw.toolbar.actions.title, text: L.drawLocal.draw.toolbar.actions.text, callback: this.disable, context: this }] }, setOptions: function(t) { L.setOptions(this, t); for (var e in this._modes) this._modes.hasOwnProperty(e) && t.hasOwnProperty(e) && this._modes[e].handler.setOptions(t[e]) } }), L.EditToolbar = L.Toolbar.extend({ options: { edit: { selectedPathOptions: { color: "#fe57a1", opacity: .6, dashArray: "10, 10", fill: !0, fillColor: "#fe57a1", fillOpacity: .1 } }, remove: {}, featureGroup: null }, initialize: function(t) { t.edit && ("undefined" == typeof t.edit.selectedPathOptions && (t.edit.selectedPathOptions = this.options.edit.selectedPathOptions), t.edit = L.extend({}, this.options.edit, t.edit)), t.remove && (t.remove = L.extend({}, this.options.remove, t.remove)), this._toolbarClass = "leaflet-draw-edit", L.Toolbar.prototype.initialize.call(this, t), this._selectedFeatureCount = 0 }, getModeHandlers: function(t) { var e = this.options.featureGroup; return [{ enabled: this.options.edit, handler: new L.EditToolbar.Edit(t, { featureGroup: e, selectedPathOptions: this.options.edit.selectedPathOptions }), title: L.drawLocal.edit.toolbar.buttons.edit }, { enabled: this.options.remove, handler: new L.EditToolbar.Delete(t, { featureGroup: e }), title: L.drawLocal.edit.toolbar.buttons.remove }] }, getActions: function() { return [{ title: L.drawLocal.edit.toolbar.actions.save.title, text: L.drawLocal.edit.toolbar.actions.save.text, callback: this._save, context: this }, { title: L.drawLocal.edit.toolbar.actions.cancel.title, text: L.drawLocal.edit.toolbar.actions.cancel.text, callback: this.disable, context: this }] }, addToolbar: function(t) { var e = L.Toolbar.prototype.addToolbar.call(this, t); return this._checkDisabled(), this.options.featureGroup.on("layeradd layerremove", this._checkDisabled, this), e }, removeToolbar: function() { this.options.featureGroup.off("layeradd layerremove", this._checkDisabled, this), L.Toolbar.prototype.removeToolbar.call(this) }, disable: function() { this.enabled() && (this._activeMode.handler.revertLayers(), L.Toolbar.prototype.disable.call(this)) }, _save: function() { this._activeMode.handler.save(), this._activeMode.handler.disable() }, _checkDisabled: function() { var t, e = this.options.featureGroup, - i = 0 !== e.getLayers().length; - this.options.edit && (t = this._modes[L.EditToolbar.Edit.TYPE].button, i ? L.DomUtil.removeClass(t, "leaflet-disabled") : L.DomUtil.addClass(t, "leaflet-disabled"), t.setAttribute("title", i ? L.drawLocal.edit.toolbar.buttons.edit : L.drawLocal.edit.toolbar.buttons.editDisabled)), this.options.remove && (t = this._modes[L.EditToolbar.Delete.TYPE].button, i ? L.DomUtil.removeClass(t, "leaflet-disabled") : L.DomUtil.addClass(t, "leaflet-disabled"), t.setAttribute("title", i ? L.drawLocal.edit.toolbar.buttons.remove : L.drawLocal.edit.toolbar.buttons.removeDisabled)) } }), L.EditToolbar.Edit = L.Handler.extend({ statics: { TYPE: "edit" }, includes: L.Mixin.Events, initialize: function(t, e) { if (L.Handler.prototype.initialize.call(this, t), this._selectedPathOptions = e.selectedPathOptions, this._featureGroup = e.featureGroup, !(this._featureGroup instanceof L.FeatureGroup)) throw new Error("options.featureGroup must be a L.FeatureGroup"); - this._uneditedLayerProps = {}, this.type = L.EditToolbar.Edit.TYPE }, enable: function() {!this._enabled && this._hasAvailableLayers() && (this.fire("enabled", { handler: this.type }), this._map.fire("draw:editstart", { handler: this.type }), L.Handler.prototype.enable.call(this), this._featureGroup.on("layeradd", this._enableLayerEdit, this).on("layerremove", this._disableLayerEdit, this)) }, disable: function() { this._enabled && (this._featureGroup.off("layeradd", this._enableLayerEdit, this).off("layerremove", this._disableLayerEdit, this), L.Handler.prototype.disable.call(this), this._map.fire("draw:editstop", { handler: this.type }), this.fire("disabled", { handler: this.type })) }, addHooks: function() { var t = this._map; - t && (t.getContainer().focus(), this._featureGroup.eachLayer(this._enableLayerEdit, this), this._tooltip = new L.Tooltip(this._map), this._tooltip.updateContent({ text: L.drawLocal.edit.handlers.edit.tooltip.text, subtext: L.drawLocal.edit.handlers.edit.tooltip.subtext }), this._map.on("mousemove", this._onMouseMove, this)) }, removeHooks: function() { this._map && (this._featureGroup.eachLayer(this._disableLayerEdit, this), this._uneditedLayerProps = {}, this._tooltip.dispose(), this._tooltip = null, this._map.off("mousemove", this._onMouseMove, this)) }, revertLayers: function() { this._featureGroup.eachLayer(function(t) { this._revertLayer(t) }, this) }, save: function() { var t = new L.LayerGroup; - this._featureGroup.eachLayer(function(e) { e.edited && (t.addLayer(e), e.edited = !1) }), this._map.fire("draw:edited", { layers: t }) }, _backupLayer: function(t) { var e = L.Util.stamp(t); - this._uneditedLayerProps[e] || (t instanceof L.Polyline || t instanceof L.Polygon || t instanceof L.Rectangle ? this._uneditedLayerProps[e] = { latlngs: L.LatLngUtil.cloneLatLngs(t.getLatLngs()) } : t instanceof L.Circle ? this._uneditedLayerProps[e] = { latlng: L.LatLngUtil.cloneLatLng(t.getLatLng()), radius: t.getRadius() } : t instanceof L.Marker && (this._uneditedLayerProps[e] = { latlng: L.LatLngUtil.cloneLatLng(t.getLatLng()) })) }, _revertLayer: function(t) { var e = L.Util.stamp(t); - t.edited = !1, this._uneditedLayerProps.hasOwnProperty(e) && (t instanceof L.Polyline || t instanceof L.Polygon || t instanceof L.Rectangle ? t.setLatLngs(this._uneditedLayerProps[e].latlngs) : t instanceof L.Circle ? (t.setLatLng(this._uneditedLayerProps[e].latlng), t.setRadius(this._uneditedLayerProps[e].radius)) : t instanceof L.Marker && t.setLatLng(this._uneditedLayerProps[e].latlng)) }, _toggleMarkerHighlight: function(t) { if (t._icon) { var e = t._icon; - e.style.display = "none", L.DomUtil.hasClass(e, "leaflet-edit-marker-selected") ? (L.DomUtil.removeClass(e, "leaflet-edit-marker-selected"), this._offsetMarker(e, -4)) : (L.DomUtil.addClass(e, "leaflet-edit-marker-selected"), this._offsetMarker(e, 4)), e.style.display = "" } }, _offsetMarker: function(t, e) { var i = parseInt(t.style.marginTop, 10) - e, - o = parseInt(t.style.marginLeft, 10) - e; - t.style.marginTop = i + "px", t.style.marginLeft = o + "px" }, _enableLayerEdit: function(t) { var e, i = t.layer || t.target || t, - o = i instanceof L.Marker; - (!o || i._icon) && (this._backupLayer(i), this._selectedPathOptions && (e = L.Util.extend({}, this._selectedPathOptions), o ? this._toggleMarkerHighlight(i) : (i.options.previousOptions = L.Util.extend({ dashArray: null }, i.options), i instanceof L.Circle || i instanceof L.Polygon || i instanceof L.Rectangle || (e.fill = !1), i.setStyle(e))), o ? (i.dragging.enable(), i.on("dragend", this._onMarkerDragEnd)) : i.editing.enable()) }, _disableLayerEdit: function(t) { var e = t.layer || t.target || t; - e.edited = !1, this._selectedPathOptions && (e instanceof L.Marker ? this._toggleMarkerHighlight(e) : (e.setStyle(e.options.previousOptions), delete e.options.previousOptions)), e instanceof L.Marker ? (e.dragging.disable(), e.off("dragend", this._onMarkerDragEnd, this)) : e.editing.disable() }, _onMarkerDragEnd: function(t) { var e = t.target; - e.edited = !0 }, _onMouseMove: function(t) { this._tooltip.updatePosition(t.latlng) }, _hasAvailableLayers: function() { return 0 !== this._featureGroup.getLayers().length } }), L.EditToolbar.Delete = L.Handler.extend({ statics: { TYPE: "remove" }, includes: L.Mixin.Events, initialize: function(t, e) { if (L.Handler.prototype.initialize.call(this, t), L.Util.setOptions(this, e), this._deletableLayers = this.options.featureGroup, !(this._deletableLayers instanceof L.FeatureGroup)) throw new Error("options.featureGroup must be a L.FeatureGroup"); - this.type = L.EditToolbar.Delete.TYPE }, enable: function() {!this._enabled && this._hasAvailableLayers() && (this.fire("enabled", { handler: this.type }), this._map.fire("draw:deletestart", { handler: this.type }), L.Handler.prototype.enable.call(this), this._deletableLayers.on("layeradd", this._enableLayerDelete, this).on("layerremove", this._disableLayerDelete, this)) }, disable: function() { this._enabled && (this._deletableLayers.off("layeradd", this._enableLayerDelete, this).off("layerremove", this._disableLayerDelete, this), L.Handler.prototype.disable.call(this), this._map.fire("draw:deletestop", { handler: this.type }), this.fire("disabled", { handler: this.type })) }, addHooks: function() { var t = this._map; - t && (t.getContainer().focus(), this._deletableLayers.eachLayer(this._enableLayerDelete, this), this._deletedLayers = new L.layerGroup, this._tooltip = new L.Tooltip(this._map), this._tooltip.updateContent({ text: L.drawLocal.edit.handlers.remove.tooltip.text }), this._map.on("mousemove", this._onMouseMove, this)) }, removeHooks: function() { this._map && (this._deletableLayers.eachLayer(this._disableLayerDelete, this), this._deletedLayers = null, this._tooltip.dispose(), this._tooltip = null, this._map.off("mousemove", this._onMouseMove, this)) }, revertLayers: function() { this._deletedLayers.eachLayer(function(t) { this._deletableLayers.addLayer(t) }, this) }, save: function() { this._map.fire("draw:deleted", { layers: this._deletedLayers }) }, _enableLayerDelete: function(t) { var e = t.layer || t.target || t; - e.on("click", this._removeLayer, this) }, _disableLayerDelete: function(t) { var e = t.layer || t.target || t; - e.off("click", this._removeLayer, this), this._deletedLayers.removeLayer(e) }, _removeLayer: function(t) { var e = t.layer || t.target || t; - this._deletableLayers.removeLayer(e), this._deletedLayers.addLayer(e) }, _onMouseMove: function(t) { this._tooltip.updatePosition(t.latlng) }, _hasAvailableLayers: function() { return 0 !== this._deletableLayers.getLayers().length } }) -}(window, document); +!(function (t, e) { + (L.drawVersion = "0.2.3"), + (L.drawLocal = { + draw: { + toolbar: { + actions: { title: "Cancel drawing", text: "Cancel" }, + undo: { title: "Delete last point drawn", text: "Delete last point" }, + buttons: { + polyline: "Draw a polyline", + polygon: "Draw a polygon", + rectangle: "Draw a rectangle", + circle: "Draw a circle", + marker: "Draw a marker", + }, + }, + handlers: { + circle: { tooltip: { start: "Click and drag to draw circle." } }, + marker: { tooltip: { start: "Click map to place marker." } }, + polygon: { + tooltip: { + start: "Click to start drawing shape.", + cont: "Click to continue drawing shape.", + end: "Click first point to close this shape.", + }, + }, + polyline: { + error: "Error: shape edges cannot cross!", + tooltip: { + start: "Click to start drawing line.", + cont: "Click to continue drawing line.", + end: "Click last point to finish line.", + }, + }, + rectangle: { tooltip: { start: "Click and drag to draw rectangle." } }, + simpleshape: { tooltip: { end: "Release mouse to finish drawing." } }, + }, + }, + edit: { + toolbar: { + actions: { + save: { title: "Save changes.", text: "Save" }, + cancel: { title: "Cancel editing, discards all changes.", text: "Cancel" }, + }, + buttons: { + edit: "Edit layers.", + editDisabled: "No layers to edit.", + remove: "Delete layers.", + removeDisabled: "No layers to delete.", + }, + }, + handlers: { + edit: { + tooltip: { + text: "Drag handles, or marker to edit feature.", + subtext: "Click cancel to undo changes.", + }, + }, + remove: { tooltip: { text: "Click on a feature to remove" } }, + }, + }, + }), + (L.Draw = {}), + (L.Draw.Feature = L.Handler.extend({ + includes: L.Mixin.Events, + initialize: function (t, e) { + (this._map = t), + (this._container = t._container), + (this._overlayPane = t._panes.overlayPane), + (this._popupPane = t._panes.popupPane), + e && + e.shapeOptions && + (e.shapeOptions = L.Util.extend({}, this.options.shapeOptions, e.shapeOptions)), + L.setOptions(this, e); + }, + enable: function () { + this._enabled || + (this.fire("enabled", { handler: this.type }), + this._map.fire("draw:drawstart", { layerType: this.type }), + L.Handler.prototype.enable.call(this)); + }, + disable: function () { + this._enabled && + (L.Handler.prototype.disable.call(this), + this._map.fire("draw:drawstop", { layerType: this.type }), + this.fire("disabled", { handler: this.type })); + }, + addHooks: function () { + var t = this._map; + t && + (L.DomUtil.disableTextSelection(), + t.getContainer().focus(), + (this._tooltip = new L.Tooltip(this._map)), + L.DomEvent.on(this._container, "keyup", this._cancelDrawing, this)); + }, + removeHooks: function () { + this._map && + (L.DomUtil.enableTextSelection(), + this._tooltip.dispose(), + (this._tooltip = null), + L.DomEvent.off(this._container, "keyup", this._cancelDrawing, this)); + }, + setOptions: function (t) { + L.setOptions(this, t); + }, + _fireCreatedEvent: function (t) { + this._map.fire("draw:created", { layer: t, layerType: this.type }); + }, + _cancelDrawing: function (t) { + 27 === t.keyCode && this.disable(); + }, + })), + (L.Draw.Polyline = L.Draw.Feature.extend({ + statics: { TYPE: "polyline" }, + Poly: L.Polyline, + options: { + allowIntersection: !0, + repeatMode: !1, + drawError: { color: "#b00b00", timeout: 2500 }, + icon: new L.DivIcon({ + iconSize: new L.Point(8, 8), + className: "leaflet-div-icon leaflet-editing-icon", + }), + guidelineDistance: 20, + maxGuideLineLength: 4e3, + shapeOptions: { + stroke: !0, + color: "#f06eaa", + weight: 4, + opacity: 0.5, + fill: !1, + clickable: !0, + }, + metric: !0, + showLength: !0, + zIndexOffset: 2e3, + }, + initialize: function (t, e) { + (this.options.drawError.message = L.drawLocal.draw.handlers.polyline.error), + e && + e.drawError && + (e.drawError = L.Util.extend({}, this.options.drawError, e.drawError)), + (this.type = L.Draw.Polyline.TYPE), + L.Draw.Feature.prototype.initialize.call(this, t, e); + }, + addHooks: function () { + L.Draw.Feature.prototype.addHooks.call(this), + this._map && + ((this._markers = []), + (this._markerGroup = new L.LayerGroup()), + this._map.addLayer(this._markerGroup), + (this._poly = new L.Polyline([], this.options.shapeOptions)), + this._tooltip.updateContent(this._getTooltipText()), + this._mouseMarker || + (this._mouseMarker = L.marker(this._map.getCenter(), { + icon: L.divIcon({ + className: "leaflet-mouse-marker", + iconAnchor: [20, 20], + iconSize: [40, 40], + }), + opacity: 0, + zIndexOffset: this.options.zIndexOffset, + })), + this._mouseMarker.on("mousedown", this._onMouseDown, this).addTo(this._map), + this._map + .on("mousemove", this._onMouseMove, this) + .on("mouseup", this._onMouseUp, this) + .on("zoomend", this._onZoomEnd, this)); + }, + removeHooks: function () { + L.Draw.Feature.prototype.removeHooks.call(this), + this._clearHideErrorTimeout(), + this._cleanUpShape(), + this._map.removeLayer(this._markerGroup), + delete this._markerGroup, + delete this._markers, + this._map.removeLayer(this._poly), + delete this._poly, + this._mouseMarker + .off("mousedown", this._onMouseDown, this) + .off("mouseup", this._onMouseUp, this), + this._map.removeLayer(this._mouseMarker), + delete this._mouseMarker, + this._clearGuides(), + this._map.off("mousemove", this._onMouseMove, this).off("zoomend", this._onZoomEnd, this); + }, + deleteLastVertex: function () { + if (!(this._markers.length <= 1)) { + var t = this._markers.pop(), + e = this._poly, + i = this._poly.spliceLatLngs(e.getLatLngs().length - 1, 1)[0]; + this._markerGroup.removeLayer(t), + e.getLatLngs().length < 2 && this._map.removeLayer(e), + this._vertexChanged(i, !1); + } + }, + addVertex: function (t) { + var e = this._markers.length; + return e > 0 && !this.options.allowIntersection && this._poly.newLatLngIntersects(t) + ? void this._showErrorTooltip() + : (this._errorShown && this._hideErrorTooltip(), + this._markers.push(this._createMarker(t)), + this._poly.addLatLng(t), + 2 === this._poly.getLatLngs().length && this._map.addLayer(this._poly), + void this._vertexChanged(t, !0)); + }, + _finishShape: function () { + var t = this._poly.newLatLngIntersects(this._poly.getLatLngs()[0], !0); + return (!this.options.allowIntersection && t) || !this._shapeIsValid() + ? void this._showErrorTooltip() + : (this._fireCreatedEvent(), + this.disable(), + void (this.options.repeatMode && this.enable())); + }, + _shapeIsValid: function () { + return !0; + }, + _onZoomEnd: function () { + this._updateGuide(); + }, + _onMouseMove: function (t) { + var e = t.layerPoint, + i = t.latlng; + (this._currentLatLng = i), + this._updateTooltip(i), + this._updateGuide(e), + this._mouseMarker.setLatLng(i), + L.DomEvent.preventDefault(t.originalEvent); + }, + _vertexChanged: function (t, e) { + this._updateFinishHandler(), + this._updateRunningMeasure(t, e), + this._clearGuides(), + this._updateTooltip(); + }, + _onMouseDown: function (t) { + var e = t.originalEvent; + this._mouseDownOrigin = L.point(e.clientX, e.clientY); + }, + _onMouseUp: function (e) { + if (this._mouseDownOrigin) { + var i = L.point(e.originalEvent.clientX, e.originalEvent.clientY).distanceTo( + this._mouseDownOrigin + ); + Math.abs(i) < 9 * (t.devicePixelRatio || 1) && this.addVertex(e.latlng); + } + this._mouseDownOrigin = null; + }, + _updateFinishHandler: function () { + var t = this._markers.length; + t > 1 && this._markers[t - 1].on("click", this._finishShape, this), + t > 2 && this._markers[t - 2].off("click", this._finishShape, this); + }, + _createMarker: function (t) { + var e = new L.Marker(t, { + icon: this.options.icon, + zIndexOffset: 2 * this.options.zIndexOffset, + }); + return this._markerGroup.addLayer(e), e; + }, + _updateGuide: function (t) { + var e = this._markers.length; + e > 0 && + ((t = t || this._map.latLngToLayerPoint(this._currentLatLng)), + this._clearGuides(), + this._drawGuide(this._map.latLngToLayerPoint(this._markers[e - 1].getLatLng()), t)); + }, + _updateTooltip: function (t) { + var e = this._getTooltipText(); + t && this._tooltip.updatePosition(t), this._errorShown || this._tooltip.updateContent(e); + }, + _drawGuide: function (t, e) { + var i, + o, + a, + s = Math.floor(Math.sqrt(Math.pow(e.x - t.x, 2) + Math.pow(e.y - t.y, 2))), + r = this.options.guidelineDistance, + n = this.options.maxGuideLineLength, + l = s > n ? s - n : r; + for ( + this._guidesContainer || + (this._guidesContainer = L.DomUtil.create( + "div", + "leaflet-draw-guides", + this._overlayPane + )); + s > l; + l += this.options.guidelineDistance + ) + (i = l / s), + (o = { + x: Math.floor(t.x * (1 - i) + i * e.x), + y: Math.floor(t.y * (1 - i) + i * e.y), + }), + (a = L.DomUtil.create("div", "leaflet-draw-guide-dash", this._guidesContainer)), + (a.style.backgroundColor = this._errorShown + ? this.options.drawError.color + : this.options.shapeOptions.color), + L.DomUtil.setPosition(a, o); + }, + _updateGuideColor: function (t) { + if (this._guidesContainer) + for (var e = 0, i = this._guidesContainer.childNodes.length; i > e; e++) + this._guidesContainer.childNodes[e].style.backgroundColor = t; + }, + _clearGuides: function () { + if (this._guidesContainer) + for (; this._guidesContainer.firstChild; ) + this._guidesContainer.removeChild(this._guidesContainer.firstChild); + }, + _getTooltipText: function () { + var t, + e, + i = this.options.showLength; + return ( + 0 === this._markers.length + ? (t = { text: L.drawLocal.draw.handlers.polyline.tooltip.start }) + : ((e = i ? this._getMeasurementString() : ""), + (t = + 1 === this._markers.length + ? { text: L.drawLocal.draw.handlers.polyline.tooltip.cont, subtext: e } + : { text: L.drawLocal.draw.handlers.polyline.tooltip.end, subtext: e })), + t + ); + }, + _updateRunningMeasure: function (t, e) { + var i, + o, + a = this._markers.length; + 1 === this._markers.length + ? (this._measurementRunningTotal = 0) + : ((i = a - (e ? 2 : 1)), + (o = t.distanceTo(this._markers[i].getLatLng())), + (this._measurementRunningTotal += o * (e ? 1 : -1))); + }, + _getMeasurementString: function () { + var t, + e = this._currentLatLng, + i = this._markers[this._markers.length - 1].getLatLng(); + return ( + (t = this._measurementRunningTotal + e.distanceTo(i)), + L.GeometryUtil.readableDistance(t, this.options.metric) + ); + }, + _showErrorTooltip: function () { + (this._errorShown = !0), + this._tooltip.showAsError().updateContent({ text: this.options.drawError.message }), + this._updateGuideColor(this.options.drawError.color), + this._poly.setStyle({ color: this.options.drawError.color }), + this._clearHideErrorTimeout(), + (this._hideErrorTimeout = setTimeout( + L.Util.bind(this._hideErrorTooltip, this), + this.options.drawError.timeout + )); + }, + _hideErrorTooltip: function () { + (this._errorShown = !1), + this._clearHideErrorTimeout(), + this._tooltip.removeError().updateContent(this._getTooltipText()), + this._updateGuideColor(this.options.shapeOptions.color), + this._poly.setStyle({ color: this.options.shapeOptions.color }); + }, + _clearHideErrorTimeout: function () { + this._hideErrorTimeout && + (clearTimeout(this._hideErrorTimeout), (this._hideErrorTimeout = null)); + }, + _cleanUpShape: function () { + this._markers.length > 1 && + this._markers[this._markers.length - 1].off("click", this._finishShape, this); + }, + _fireCreatedEvent: function () { + var t = new this.Poly(this._poly.getLatLngs(), this.options.shapeOptions); + L.Draw.Feature.prototype._fireCreatedEvent.call(this, t); + }, + })), + (L.Draw.Polygon = L.Draw.Polyline.extend({ + statics: { TYPE: "polygon" }, + Poly: L.Polygon, + options: { + showArea: !1, + shapeOptions: { + stroke: !0, + color: "#f06eaa", + weight: 4, + opacity: 0.5, + fill: !0, + fillColor: null, + fillOpacity: 0.2, + clickable: !0, + }, + }, + initialize: function (t, e) { + L.Draw.Polyline.prototype.initialize.call(this, t, e), (this.type = L.Draw.Polygon.TYPE); + }, + _updateFinishHandler: function () { + var t = this._markers.length; + 1 === t && this._markers[0].on("click", this._finishShape, this), + t > 2 && + (this._markers[t - 1].on("dblclick", this._finishShape, this), + t > 3 && this._markers[t - 2].off("dblclick", this._finishShape, this)); + }, + _getTooltipText: function () { + var t, e; + return ( + 0 === this._markers.length + ? (t = L.drawLocal.draw.handlers.polygon.tooltip.start) + : this._markers.length < 3 + ? (t = L.drawLocal.draw.handlers.polygon.tooltip.cont) + : ((t = L.drawLocal.draw.handlers.polygon.tooltip.end), + (e = this._getMeasurementString())), + { text: t, subtext: e } + ); + }, + _getMeasurementString: function () { + var t = this._area; + return t ? L.GeometryUtil.readableArea(t, this.options.metric) : null; + }, + _shapeIsValid: function () { + return this._markers.length >= 3; + }, + _vertexAdded: function () { + if (!this.options.allowIntersection && this.options.showArea) { + var t = this._poly.getLatLngs(); + this._area = L.GeometryUtil.geodesicArea(t); + } + }, + _cleanUpShape: function () { + var t = this._markers.length; + t > 0 && + (this._markers[0].off("click", this._finishShape, this), + t > 2 && this._markers[t - 1].off("dblclick", this._finishShape, this)); + }, + })), + (L.SimpleShape = {}), + (L.Draw.SimpleShape = L.Draw.Feature.extend({ + options: { repeatMode: !1 }, + initialize: function (t, e) { + (this._endLabelText = L.drawLocal.draw.handlers.simpleshape.tooltip.end), + L.Draw.Feature.prototype.initialize.call(this, t, e); + }, + addHooks: function () { + L.Draw.Feature.prototype.addHooks.call(this), + this._map && + ((this._mapDraggable = this._map.dragging.enabled()), + this._mapDraggable && this._map.dragging.disable(), + (this._container.style.cursor = "crosshair"), + this._tooltip.updateContent({ text: this._initialLabelText }), + this._map + .on("mousedown", this._onMouseDown, this) + .on("mousemove", this._onMouseMove, this)); + }, + removeHooks: function () { + L.Draw.Feature.prototype.removeHooks.call(this), + this._map && + (this._mapDraggable && this._map.dragging.enable(), + (this._container.style.cursor = ""), + this._map + .off("mousedown", this._onMouseDown, this) + .off("mousemove", this._onMouseMove, this), + L.DomEvent.off(e, "mouseup", this._onMouseUp, this), + this._shape && (this._map.removeLayer(this._shape), delete this._shape)), + (this._isDrawing = !1); + }, + _onMouseDown: function (t) { + (this._isDrawing = !0), + (this._startLatLng = t.latlng), + L.DomEvent.on(e, "mouseup", this._onMouseUp, this).preventDefault(t.originalEvent); + }, + _onMouseMove: function (t) { + var e = t.latlng; + this._tooltip.updatePosition(e), + this._isDrawing && + (this._tooltip.updateContent({ text: this._endLabelText }), this._drawShape(e)); + }, + _onMouseUp: function () { + this._shape && this._fireCreatedEvent(), + this.disable(), + this.options.repeatMode && this.enable(); + }, + })), + (L.Draw.Rectangle = L.Draw.SimpleShape.extend({ + statics: { TYPE: "rectangle" }, + options: { + shapeOptions: { + stroke: !0, + color: "#f06eaa", + weight: 4, + opacity: 0.5, + fill: !0, + fillColor: null, + fillOpacity: 0.2, + clickable: !0, + }, + }, + initialize: function (t, e) { + (this.type = L.Draw.Rectangle.TYPE), + (this._initialLabelText = L.drawLocal.draw.handlers.rectangle.tooltip.start), + L.Draw.SimpleShape.prototype.initialize.call(this, t, e); + }, + _drawShape: function (t) { + this._shape + ? this._shape.setBounds(new L.LatLngBounds(this._startLatLng, t)) + : ((this._shape = new L.Rectangle( + new L.LatLngBounds(this._startLatLng, t), + this.options.shapeOptions + )), + this._map.addLayer(this._shape)); + }, + _fireCreatedEvent: function () { + var t = new L.Rectangle(this._shape.getBounds(), this.options.shapeOptions); + L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this, t); + }, + })), + (L.Draw.Circle = L.Draw.SimpleShape.extend({ + statics: { TYPE: "circle" }, + options: { + shapeOptions: { + stroke: !0, + color: "#f06eaa", + weight: 4, + opacity: 0.5, + fill: !0, + fillColor: null, + fillOpacity: 0.2, + clickable: !0, + }, + showRadius: !0, + metric: !0, + }, + initialize: function (t, e) { + (this.type = L.Draw.Circle.TYPE), + (this._initialLabelText = L.drawLocal.draw.handlers.circle.tooltip.start), + L.Draw.SimpleShape.prototype.initialize.call(this, t, e); + }, + _drawShape: function (t) { + this._shape + ? this._shape.setRadius(this._startLatLng.distanceTo(t)) + : ((this._shape = new L.Circle( + this._startLatLng, + this._startLatLng.distanceTo(t), + this.options.shapeOptions + )), + this._map.addLayer(this._shape)); + }, + _fireCreatedEvent: function () { + var t = new L.Circle(this._startLatLng, this._shape.getRadius(), this.options.shapeOptions); + L.Draw.SimpleShape.prototype._fireCreatedEvent.call(this, t); + }, + _onMouseMove: function (t) { + var e, + i = t.latlng, + o = this.options.showRadius, + a = this.options.metric; + this._tooltip.updatePosition(i), + this._isDrawing && + (this._drawShape(i), + (e = this._shape.getRadius().toFixed(1)), + this._tooltip.updateContent({ + text: this._endLabelText, + subtext: o ? "Radius: " + L.GeometryUtil.readableDistance(e, a) : "", + })); + }, + })), + (L.Draw.Marker = L.Draw.Feature.extend({ + statics: { TYPE: "marker" }, + options: { icon: new L.Icon.Default(), repeatMode: !1, zIndexOffset: 2e3 }, + initialize: function (t, e) { + (this.type = L.Draw.Marker.TYPE), L.Draw.Feature.prototype.initialize.call(this, t, e); + }, + addHooks: function () { + L.Draw.Feature.prototype.addHooks.call(this), + this._map && + (this._tooltip.updateContent({ + text: L.drawLocal.draw.handlers.marker.tooltip.start, + }), + this._mouseMarker || + (this._mouseMarker = L.marker(this._map.getCenter(), { + icon: L.divIcon({ + className: "leaflet-mouse-marker", + iconAnchor: [20, 20], + iconSize: [40, 40], + }), + opacity: 0, + zIndexOffset: this.options.zIndexOffset, + })), + this._mouseMarker.on("click", this._onClick, this).addTo(this._map), + this._map.on("mousemove", this._onMouseMove, this)); + }, + removeHooks: function () { + L.Draw.Feature.prototype.removeHooks.call(this), + this._map && + (this._marker && + (this._marker.off("click", this._onClick, this), + this._map.off("click", this._onClick, this).removeLayer(this._marker), + delete this._marker), + this._mouseMarker.off("click", this._onClick, this), + this._map.removeLayer(this._mouseMarker), + delete this._mouseMarker, + this._map.off("mousemove", this._onMouseMove, this)); + }, + _onMouseMove: function (t) { + var e = t.latlng; + this._tooltip.updatePosition(e), + this._mouseMarker.setLatLng(e), + this._marker + ? ((e = this._mouseMarker.getLatLng()), this._marker.setLatLng(e)) + : ((this._marker = new L.Marker(e, { + icon: this.options.icon, + zIndexOffset: this.options.zIndexOffset, + })), + this._marker.on("click", this._onClick, this), + this._map.on("click", this._onClick, this).addLayer(this._marker)); + }, + _onClick: function () { + this._fireCreatedEvent(), this.disable(), this.options.repeatMode && this.enable(); + }, + _fireCreatedEvent: function () { + var t = new L.Marker(this._marker.getLatLng(), { icon: this.options.icon }); + L.Draw.Feature.prototype._fireCreatedEvent.call(this, t); + }, + })), + (L.Edit = L.Edit || {}), + (L.Edit.Poly = L.Handler.extend({ + options: { + icon: new L.DivIcon({ + iconSize: new L.Point(8, 8), + className: "leaflet-div-icon leaflet-editing-icon", + }), + }, + initialize: function (t, e) { + (this._poly = t), L.setOptions(this, e); + }, + addHooks: function () { + this._poly._map && + (this._markerGroup || this._initMarkers(), this._poly._map.addLayer(this._markerGroup)); + }, + removeHooks: function () { + this._poly._map && + (this._poly._map.removeLayer(this._markerGroup), + delete this._markerGroup, + delete this._markers); + }, + updateMarkers: function () { + this._markerGroup.clearLayers(), this._initMarkers(); + }, + _initMarkers: function () { + this._markerGroup || (this._markerGroup = new L.LayerGroup()), (this._markers = []); + var t, + e, + i, + o, + a = this._poly._latlngs; + for (t = 0, i = a.length; i > t; t++) + (o = this._createMarker(a[t], t)), + o.on("click", this._onMarkerClick, this), + this._markers.push(o); + var s, r; + for (t = 0, e = i - 1; i > t; e = t++) + (0 !== t || (L.Polygon && this._poly instanceof L.Polygon)) && + ((s = this._markers[e]), + (r = this._markers[t]), + this._createMiddleMarker(s, r), + this._updatePrevNext(s, r)); + }, + _createMarker: function (t, e) { + var i = new L.Marker(t, { draggable: !0, icon: this.options.icon }); + return ( + (i._origLatLng = t), + (i._index = e), + i.on("drag", this._onMarkerDrag, this), + i.on("dragend", this._fireEdit, this), + this._markerGroup.addLayer(i), + i + ); + }, + _removeMarker: function (t) { + var e = t._index; + this._markerGroup.removeLayer(t), + this._markers.splice(e, 1), + this._poly.spliceLatLngs(e, 1), + this._updateIndexes(e, -1), + t + .off("drag", this._onMarkerDrag, this) + .off("dragend", this._fireEdit, this) + .off("click", this._onMarkerClick, this); + }, + _fireEdit: function () { + (this._poly.edited = !0), this._poly.fire("edit"); + }, + _onMarkerDrag: function (t) { + var e = t.target; + L.extend(e._origLatLng, e._latlng), + e._middleLeft && e._middleLeft.setLatLng(this._getMiddleLatLng(e._prev, e)), + e._middleRight && e._middleRight.setLatLng(this._getMiddleLatLng(e, e._next)), + this._poly.redraw(); + }, + _onMarkerClick: function (t) { + var e = L.Polygon && this._poly instanceof L.Polygon ? 4 : 3, + i = t.target; + this._poly._latlngs.length < e || + (this._removeMarker(i), + this._updatePrevNext(i._prev, i._next), + i._middleLeft && this._markerGroup.removeLayer(i._middleLeft), + i._middleRight && this._markerGroup.removeLayer(i._middleRight), + i._prev && i._next + ? this._createMiddleMarker(i._prev, i._next) + : i._prev + ? i._next || (i._prev._middleRight = null) + : (i._next._middleLeft = null), + this._fireEdit()); + }, + _updateIndexes: function (t, e) { + this._markerGroup.eachLayer(function (i) { + i._index > t && (i._index += e); + }); + }, + _createMiddleMarker: function (t, e) { + var i, + o, + a, + s = this._getMiddleLatLng(t, e), + r = this._createMarker(s); + r.setOpacity(0.6), + (t._middleRight = e._middleLeft = r), + (o = function () { + var o = e._index; + (r._index = o), + r.off("click", i, this).on("click", this._onMarkerClick, this), + (s.lat = r.getLatLng().lat), + (s.lng = r.getLatLng().lng), + this._poly.spliceLatLngs(o, 0, s), + this._markers.splice(o, 0, r), + r.setOpacity(1), + this._updateIndexes(o, 1), + e._index++, + this._updatePrevNext(t, r), + this._updatePrevNext(r, e), + this._poly.fire("editstart"); + }), + (a = function () { + r.off("dragstart", o, this), + r.off("dragend", a, this), + this._createMiddleMarker(t, r), + this._createMiddleMarker(r, e); + }), + (i = function () { + o.call(this), a.call(this), this._fireEdit(); + }), + r.on("click", i, this).on("dragstart", o, this).on("dragend", a, this), + this._markerGroup.addLayer(r); + }, + _updatePrevNext: function (t, e) { + t && (t._next = e), e && (e._prev = t); + }, + _getMiddleLatLng: function (t, e) { + var i = this._poly._map, + o = i.project(t.getLatLng()), + a = i.project(e.getLatLng()); + return i.unproject(o._add(a)._divideBy(2)); + }, + })), + L.Polyline.addInitHook(function () { + this.editing || + (L.Edit.Poly && + ((this.editing = new L.Edit.Poly(this)), this.options.editable && this.editing.enable()), + this.on("add", function () { + this.editing && this.editing.enabled() && this.editing.addHooks(); + }), + this.on("remove", function () { + this.editing && this.editing.enabled() && this.editing.removeHooks(); + })); + }), + (L.Edit = L.Edit || {}), + (L.Edit.SimpleShape = L.Handler.extend({ + options: { + moveIcon: new L.DivIcon({ + iconSize: new L.Point(8, 8), + className: "leaflet-div-icon leaflet-editing-icon leaflet-edit-move", + }), + resizeIcon: new L.DivIcon({ + iconSize: new L.Point(8, 8), + className: "leaflet-div-icon leaflet-editing-icon leaflet-edit-resize", + }), + }, + initialize: function (t, e) { + (this._shape = t), L.Util.setOptions(this, e); + }, + addHooks: function () { + this._shape._map && + ((this._map = this._shape._map), + this._markerGroup || this._initMarkers(), + this._map.addLayer(this._markerGroup)); + }, + removeHooks: function () { + if (this._shape._map) { + this._unbindMarker(this._moveMarker); + for (var t = 0, e = this._resizeMarkers.length; e > t; t++) + this._unbindMarker(this._resizeMarkers[t]); + (this._resizeMarkers = null), + this._map.removeLayer(this._markerGroup), + delete this._markerGroup; + } + this._map = null; + }, + updateMarkers: function () { + this._markerGroup.clearLayers(), this._initMarkers(); + }, + _initMarkers: function () { + this._markerGroup || (this._markerGroup = new L.LayerGroup()), + this._createMoveMarker(), + this._createResizeMarker(); + }, + _createMoveMarker: function () {}, + _createResizeMarker: function () {}, + _createMarker: function (t, e) { + var i = new L.Marker(t, { draggable: !0, icon: e, zIndexOffset: 10 }); + return this._bindMarker(i), this._markerGroup.addLayer(i), i; + }, + _bindMarker: function (t) { + t.on("dragstart", this._onMarkerDragStart, this) + .on("drag", this._onMarkerDrag, this) + .on("dragend", this._onMarkerDragEnd, this); + }, + _unbindMarker: function (t) { + t.off("dragstart", this._onMarkerDragStart, this) + .off("drag", this._onMarkerDrag, this) + .off("dragend", this._onMarkerDragEnd, this); + }, + _onMarkerDragStart: function (t) { + var e = t.target; + e.setOpacity(0), this._shape.fire("editstart"); + }, + _fireEdit: function () { + (this._shape.edited = !0), this._shape.fire("edit"); + }, + _onMarkerDrag: function (t) { + var e = t.target, + i = e.getLatLng(); + e === this._moveMarker ? this._move(i) : this._resize(i), this._shape.redraw(); + }, + _onMarkerDragEnd: function (t) { + var e = t.target; + e.setOpacity(1), this._fireEdit(); + }, + _move: function () {}, + _resize: function () {}, + })), + (L.Edit = L.Edit || {}), + (L.Edit.Rectangle = L.Edit.SimpleShape.extend({ + _createMoveMarker: function () { + var t = this._shape.getBounds(), + e = t.getCenter(); + this._moveMarker = this._createMarker(e, this.options.moveIcon); + }, + _createResizeMarker: function () { + var t = this._getCorners(); + this._resizeMarkers = []; + for (var e = 0, i = t.length; i > e; e++) + this._resizeMarkers.push(this._createMarker(t[e], this.options.resizeIcon)), + (this._resizeMarkers[e]._cornerIndex = e); + }, + _onMarkerDragStart: function (t) { + L.Edit.SimpleShape.prototype._onMarkerDragStart.call(this, t); + var e = this._getCorners(), + i = t.target, + o = i._cornerIndex; + (this._oppositeCorner = e[(o + 2) % 4]), this._toggleCornerMarkers(0, o); + }, + _onMarkerDragEnd: function (t) { + var e, + i, + o = t.target; + o === this._moveMarker && + ((e = this._shape.getBounds()), (i = e.getCenter()), o.setLatLng(i)), + this._toggleCornerMarkers(1), + this._repositionCornerMarkers(), + L.Edit.SimpleShape.prototype._onMarkerDragEnd.call(this, t); + }, + _move: function (t) { + for ( + var e, + i = this._shape.getLatLngs(), + o = this._shape.getBounds(), + a = o.getCenter(), + s = [], + r = 0, + n = i.length; + n > r; + r++ + ) + (e = [i[r].lat - a.lat, i[r].lng - a.lng]), s.push([t.lat + e[0], t.lng + e[1]]); + this._shape.setLatLngs(s), this._repositionCornerMarkers(); + }, + _resize: function (t) { + var e; + this._shape.setBounds(L.latLngBounds(t, this._oppositeCorner)), + (e = this._shape.getBounds()), + this._moveMarker.setLatLng(e.getCenter()); + }, + _getCorners: function () { + var t = this._shape.getBounds(), + e = t.getNorthWest(), + i = t.getNorthEast(), + o = t.getSouthEast(), + a = t.getSouthWest(); + return [e, i, o, a]; + }, + _toggleCornerMarkers: function (t) { + for (var e = 0, i = this._resizeMarkers.length; i > e; e++) + this._resizeMarkers[e].setOpacity(t); + }, + _repositionCornerMarkers: function () { + for (var t = this._getCorners(), e = 0, i = this._resizeMarkers.length; i > e; e++) + this._resizeMarkers[e].setLatLng(t[e]); + }, + })), + L.Rectangle.addInitHook(function () { + L.Edit.Rectangle && + ((this.editing = new L.Edit.Rectangle(this)), this.options.editable && this.editing.enable()); + }), + (L.Edit = L.Edit || {}), + (L.Edit.Circle = L.Edit.SimpleShape.extend({ + _createMoveMarker: function () { + var t = this._shape.getLatLng(); + this._moveMarker = this._createMarker(t, this.options.moveIcon); + }, + _createResizeMarker: function () { + var t = this._shape.getLatLng(), + e = this._getResizeMarkerPoint(t); + (this._resizeMarkers = []), + this._resizeMarkers.push(this._createMarker(e, this.options.resizeIcon)); + }, + _getResizeMarkerPoint: function (t) { + var e = this._shape._radius * Math.cos(Math.PI / 4), + i = this._map.project(t); + return this._map.unproject([i.x + e, i.y - e]); + }, + _move: function (t) { + var e = this._getResizeMarkerPoint(t); + this._resizeMarkers[0].setLatLng(e), this._shape.setLatLng(t); + }, + _resize: function (t) { + var e = this._moveMarker.getLatLng(), + i = e.distanceTo(t); + this._shape.setRadius(i); + }, + })), + L.Circle.addInitHook(function () { + L.Edit.Circle && + ((this.editing = new L.Edit.Circle(this)), this.options.editable && this.editing.enable()), + this.on("add", function () { + this.editing && this.editing.enabled() && this.editing.addHooks(); + }), + this.on("remove", function () { + this.editing && this.editing.enabled() && this.editing.removeHooks(); + }); + }), + (L.LatLngUtil = { + cloneLatLngs: function (t) { + for (var e = [], i = 0, o = t.length; o > i; i++) e.push(this.cloneLatLng(t[i])); + return e; + }, + cloneLatLng: function (t) { + return L.latLng(t.lat, t.lng); + }, + }), + (L.GeometryUtil = L.extend(L.GeometryUtil || {}, { + geodesicArea: function (t) { + var e, + i, + o = t.length, + a = 0, + s = L.LatLng.DEG_TO_RAD; + if (o > 2) { + for (var r = 0; o > r; r++) + (e = t[r]), + (i = t[(r + 1) % o]), + (a += (i.lng - e.lng) * s * (2 + Math.sin(e.lat * s) + Math.sin(i.lat * s))); + a = (6378137 * a * 6378137) / 2; + } + return Math.abs(a); + }, + readableArea: function (t, e) { + var i; + return ( + e + ? (i = t >= 1e4 ? (1e-4 * t).toFixed(2) + " ha" : t.toFixed(2) + " m²") + : ((t *= 0.836127), + (i = + t >= 3097600 + ? (t / 3097600).toFixed(2) + " mi²" + : t >= 4840 + ? (t / 4840).toFixed(2) + " acres" + : Math.ceil(t) + " yd²")), + i + ); + }, + readableDistance: function (t, e) { + var i; + return ( + e + ? (i = t > 1e3 ? (t / 1e3).toFixed(2) + " km" : Math.ceil(t) + " m") + : ((t *= 1.09361), + (i = t > 1760 ? (t / 1760).toFixed(2) + " miles" : Math.ceil(t) + " yd")), + i + ); + }, + })), + L.Util.extend(L.LineUtil, { + segmentsIntersect: function (t, e, i, o) { + return ( + this._checkCounterclockwise(t, i, o) !== this._checkCounterclockwise(e, i, o) && + this._checkCounterclockwise(t, e, i) !== this._checkCounterclockwise(t, e, o) + ); + }, + _checkCounterclockwise: function (t, e, i) { + return (i.y - t.y) * (e.x - t.x) > (e.y - t.y) * (i.x - t.x); + }, + }), + L.Polyline.include({ + intersects: function () { + var t, + e, + i, + o = this._originalPoints, + a = o ? o.length : 0; + if (this._tooFewPointsForIntersection()) return !1; + for (t = a - 1; t >= 3; t--) + if (((e = o[t - 1]), (i = o[t]), this._lineSegmentsIntersectsRange(e, i, t - 2))) + return !0; + return !1; + }, + newLatLngIntersects: function (t, e) { + return this._map ? this.newPointIntersects(this._map.latLngToLayerPoint(t), e) : !1; + }, + newPointIntersects: function (t, e) { + var i = this._originalPoints, + o = i ? i.length : 0, + a = i ? i[o - 1] : null, + s = o - 2; + return this._tooFewPointsForIntersection(1) + ? !1 + : this._lineSegmentsIntersectsRange(a, t, s, e ? 1 : 0); + }, + _tooFewPointsForIntersection: function (t) { + var e = this._originalPoints, + i = e ? e.length : 0; + return (i += t || 0), !this._originalPoints || 3 >= i; + }, + _lineSegmentsIntersectsRange: function (t, e, i, o) { + var a, + s, + r = this._originalPoints; + o = o || 0; + for (var n = i; n > o; n--) + if (((a = r[n - 1]), (s = r[n]), L.LineUtil.segmentsIntersect(t, e, a, s))) return !0; + return !1; + }, + }), + L.Polygon.include({ + intersects: function () { + var t, + e, + i, + o, + a, + s = this._originalPoints; + return this._tooFewPointsForIntersection() + ? !1 + : (t = L.Polyline.prototype.intersects.call(this)) + ? !0 + : ((e = s.length), + (i = s[0]), + (o = s[e - 1]), + (a = e - 2), + this._lineSegmentsIntersectsRange(o, i, a, 1)); + }, + }), + (L.Control.Draw = L.Control.extend({ + options: { position: "topleft", draw: {}, edit: !1 }, + initialize: function (t) { + if (L.version < "0.7") + throw new Error( + "Leaflet.draw 0.2.3+ requires Leaflet 0.7.0+. Download latest from https://github.com/Leaflet/Leaflet/" + ); + L.Control.prototype.initialize.call(this, t); + var e, i; + (this._toolbars = {}), + L.DrawToolbar && + this.options.draw && + ((i = new L.DrawToolbar(this.options.draw)), + (e = L.stamp(i)), + (this._toolbars[e] = i), + this._toolbars[e].on("enable", this._toolbarEnabled, this)), + L.EditToolbar && + this.options.edit && + ((i = new L.EditToolbar(this.options.edit)), + (e = L.stamp(i)), + (this._toolbars[e] = i), + this._toolbars[e].on("enable", this._toolbarEnabled, this)); + }, + onAdd: function (t) { + var e, + i = L.DomUtil.create("div", "leaflet-draw"), + o = !1, + a = "leaflet-draw-toolbar-top"; + for (var s in this._toolbars) + this._toolbars.hasOwnProperty(s) && + ((e = this._toolbars[s].addToolbar(t)), + e && + (o || + (L.DomUtil.hasClass(e, a) || L.DomUtil.addClass(e.childNodes[0], a), + (o = !0)), + i.appendChild(e))); + return i; + }, + onRemove: function () { + for (var t in this._toolbars) + this._toolbars.hasOwnProperty(t) && this._toolbars[t].removeToolbar(); + }, + setDrawingOptions: function (t) { + for (var e in this._toolbars) + this._toolbars[e] instanceof L.DrawToolbar && this._toolbars[e].setOptions(t); + }, + _toolbarEnabled: function (t) { + var e = "" + L.stamp(t.target); + for (var i in this._toolbars) + this._toolbars.hasOwnProperty(i) && i !== e && this._toolbars[i].disable(); + }, + })), + L.Map.mergeOptions({ drawControlTooltips: !0, drawControl: !1 }), + L.Map.addInitHook(function () { + this.options.drawControl && + ((this.drawControl = new L.Control.Draw()), this.addControl(this.drawControl)); + }), + (L.Toolbar = L.Class.extend({ + includes: [L.Mixin.Events], + initialize: function (t) { + L.setOptions(this, t), + (this._modes = {}), + (this._actionButtons = []), + (this._activeMode = null); + }, + enabled: function () { + return null !== this._activeMode; + }, + disable: function () { + this.enabled() && this._activeMode.handler.disable(); + }, + addToolbar: function (t) { + var e, + i = L.DomUtil.create("div", "leaflet-draw-section"), + o = 0, + a = this._toolbarClass || "", + s = this.getModeHandlers(t); + for ( + this._toolbarContainer = L.DomUtil.create("div", "leaflet-draw-toolbar leaflet-bar"), + this._map = t, + e = 0; + e < s.length; + e++ + ) + s[e].enabled && + this._initModeHandler(s[e].handler, this._toolbarContainer, o++, a, s[e].title); + return o + ? ((this._lastButtonIndex = --o), + (this._actionsContainer = L.DomUtil.create("ul", "leaflet-draw-actions")), + i.appendChild(this._toolbarContainer), + i.appendChild(this._actionsContainer), + i) + : void 0; + }, + removeToolbar: function () { + for (var t in this._modes) + this._modes.hasOwnProperty(t) && + (this._disposeButton( + this._modes[t].button, + this._modes[t].handler.enable, + this._modes[t].handler + ), + this._modes[t].handler.disable(), + this._modes[t].handler + .off("enabled", this._handlerActivated, this) + .off("disabled", this._handlerDeactivated, this)); + this._modes = {}; + for (var e = 0, i = this._actionButtons.length; i > e; e++) + this._disposeButton(this._actionButtons[e].button, this._actionButtons[e].callback, this); + (this._actionButtons = []), (this._actionsContainer = null); + }, + _initModeHandler: function (t, e, i, o, a) { + var s = t.type; + (this._modes[s] = {}), + (this._modes[s].handler = t), + (this._modes[s].button = this._createButton({ + title: a, + className: o + "-" + s, + container: e, + callback: this._modes[s].handler.enable, + context: this._modes[s].handler, + })), + (this._modes[s].buttonIndex = i), + this._modes[s].handler + .on("enabled", this._handlerActivated, this) + .on("disabled", this._handlerDeactivated, this); + }, + _createButton: function (t) { + var e = L.DomUtil.create("a", t.className || "", t.container); + return ( + (e.href = "#"), + t.text && (e.innerHTML = t.text), + t.title && (e.title = t.title), + L.DomEvent.on(e, "click", L.DomEvent.stopPropagation) + .on(e, "mousedown", L.DomEvent.stopPropagation) + .on(e, "dblclick", L.DomEvent.stopPropagation) + .on(e, "click", L.DomEvent.preventDefault) + .on(e, "click", t.callback, t.context), + e + ); + }, + _disposeButton: function (t, e) { + L.DomEvent.off(t, "click", L.DomEvent.stopPropagation) + .off(t, "mousedown", L.DomEvent.stopPropagation) + .off(t, "dblclick", L.DomEvent.stopPropagation) + .off(t, "click", L.DomEvent.preventDefault) + .off(t, "click", e); + }, + _handlerActivated: function (t) { + this.disable(), + (this._activeMode = this._modes[t.handler]), + L.DomUtil.addClass(this._activeMode.button, "leaflet-draw-toolbar-button-enabled"), + this._showActionsToolbar(), + this.fire("enable"); + }, + _handlerDeactivated: function () { + this._hideActionsToolbar(), + L.DomUtil.removeClass(this._activeMode.button, "leaflet-draw-toolbar-button-enabled"), + (this._activeMode = null), + this.fire("disable"); + }, + _createActions: function (t) { + var e, + i, + o, + a, + s = this._actionsContainer, + r = this.getActions(t), + n = r.length; + for (i = 0, o = this._actionButtons.length; o > i; i++) + this._disposeButton(this._actionButtons[i].button, this._actionButtons[i].callback); + for (this._actionButtons = []; s.firstChild; ) s.removeChild(s.firstChild); + for (var l = 0; n > l; l++) + ("enabled" in r[l] && !r[l].enabled) || + ((e = L.DomUtil.create("li", "", s)), + (a = this._createButton({ + title: r[l].title, + text: r[l].text, + container: e, + callback: r[l].callback, + context: r[l].context, + })), + this._actionButtons.push({ button: a, callback: r[l].callback })); + }, + _showActionsToolbar: function () { + var t = this._activeMode.buttonIndex, + e = this._lastButtonIndex, + i = this._activeMode.button.offsetTop - 1; + this._createActions(this._activeMode.handler), + (this._actionsContainer.style.top = i + "px"), + 0 === t && + (L.DomUtil.addClass(this._toolbarContainer, "leaflet-draw-toolbar-notop"), + L.DomUtil.addClass(this._actionsContainer, "leaflet-draw-actions-top")), + t === e && + (L.DomUtil.addClass(this._toolbarContainer, "leaflet-draw-toolbar-nobottom"), + L.DomUtil.addClass(this._actionsContainer, "leaflet-draw-actions-bottom")), + (this._actionsContainer.style.display = "block"); + }, + _hideActionsToolbar: function () { + (this._actionsContainer.style.display = "none"), + L.DomUtil.removeClass(this._toolbarContainer, "leaflet-draw-toolbar-notop"), + L.DomUtil.removeClass(this._toolbarContainer, "leaflet-draw-toolbar-nobottom"), + L.DomUtil.removeClass(this._actionsContainer, "leaflet-draw-actions-top"), + L.DomUtil.removeClass(this._actionsContainer, "leaflet-draw-actions-bottom"); + }, + })), + (L.Tooltip = L.Class.extend({ + initialize: function (t) { + (this._map = t), + (this._popupPane = t._panes.popupPane), + (this._container = t.options.drawControlTooltips + ? L.DomUtil.create("div", "leaflet-draw-tooltip", this._popupPane) + : null), + (this._singleLineLabel = !1); + }, + dispose: function () { + this._container && (this._popupPane.removeChild(this._container), (this._container = null)); + }, + updateContent: function (t) { + return this._container + ? ((t.subtext = t.subtext || ""), + 0 !== t.subtext.length || this._singleLineLabel + ? t.subtext.length > 0 && + this._singleLineLabel && + (L.DomUtil.removeClass(this._container, "leaflet-draw-tooltip-single"), + (this._singleLineLabel = !1)) + : (L.DomUtil.addClass(this._container, "leaflet-draw-tooltip-single"), + (this._singleLineLabel = !0)), + (this._container.innerHTML = + (t.subtext.length > 0 + ? '' + t.subtext + "
              " + : "") + + "" + + t.text + + ""), + this) + : this; + }, + updatePosition: function (t) { + var e = this._map.latLngToLayerPoint(t), + i = this._container; + return ( + this._container && ((i.style.visibility = "inherit"), L.DomUtil.setPosition(i, e)), this + ); + }, + showAsError: function () { + return ( + this._container && L.DomUtil.addClass(this._container, "leaflet-error-draw-tooltip"), this + ); + }, + removeError: function () { + return ( + this._container && L.DomUtil.removeClass(this._container, "leaflet-error-draw-tooltip"), + this + ); + }, + })), + (L.DrawToolbar = L.Toolbar.extend({ + options: { polyline: {}, polygon: {}, rectangle: {}, circle: {}, marker: {} }, + initialize: function (t) { + for (var e in this.options) + this.options.hasOwnProperty(e) && t[e] && (t[e] = L.extend({}, this.options[e], t[e])); + (this._toolbarClass = "leaflet-draw-draw"), L.Toolbar.prototype.initialize.call(this, t); + }, + getModeHandlers: function (t) { + return [ + { + enabled: this.options.polyline, + handler: new L.Draw.Polyline(t, this.options.polyline), + title: L.drawLocal.draw.toolbar.buttons.polyline, + }, + { + enabled: this.options.polygon, + handler: new L.Draw.Polygon(t, this.options.polygon), + title: L.drawLocal.draw.toolbar.buttons.polygon, + }, + { + enabled: this.options.rectangle, + handler: new L.Draw.Rectangle(t, this.options.rectangle), + title: L.drawLocal.draw.toolbar.buttons.rectangle, + }, + { + enabled: this.options.circle, + handler: new L.Draw.Circle(t, this.options.circle), + title: L.drawLocal.draw.toolbar.buttons.circle, + }, + { + enabled: this.options.marker, + handler: new L.Draw.Marker(t, this.options.marker), + title: L.drawLocal.draw.toolbar.buttons.marker, + }, + ]; + }, + getActions: function (t) { + return [ + { + enabled: t.deleteLastVertex, + title: L.drawLocal.draw.toolbar.undo.title, + text: L.drawLocal.draw.toolbar.undo.text, + callback: t.deleteLastVertex, + context: t, + }, + { + title: L.drawLocal.draw.toolbar.actions.title, + text: L.drawLocal.draw.toolbar.actions.text, + callback: this.disable, + context: this, + }, + ]; + }, + setOptions: function (t) { + L.setOptions(this, t); + for (var e in this._modes) + this._modes.hasOwnProperty(e) && + t.hasOwnProperty(e) && + this._modes[e].handler.setOptions(t[e]); + }, + })), + (L.EditToolbar = L.Toolbar.extend({ + options: { + edit: { + selectedPathOptions: { + color: "#fe57a1", + opacity: 0.6, + dashArray: "10, 10", + fill: !0, + fillColor: "#fe57a1", + fillOpacity: 0.1, + }, + }, + remove: {}, + featureGroup: null, + }, + initialize: function (t) { + t.edit && + ("undefined" == typeof t.edit.selectedPathOptions && + (t.edit.selectedPathOptions = this.options.edit.selectedPathOptions), + (t.edit = L.extend({}, this.options.edit, t.edit))), + t.remove && (t.remove = L.extend({}, this.options.remove, t.remove)), + (this._toolbarClass = "leaflet-draw-edit"), + L.Toolbar.prototype.initialize.call(this, t), + (this._selectedFeatureCount = 0); + }, + getModeHandlers: function (t) { + var e = this.options.featureGroup; + return [ + { + enabled: this.options.edit, + handler: new L.EditToolbar.Edit(t, { + featureGroup: e, + selectedPathOptions: this.options.edit.selectedPathOptions, + }), + title: L.drawLocal.edit.toolbar.buttons.edit, + }, + { + enabled: this.options.remove, + handler: new L.EditToolbar.Delete(t, { featureGroup: e }), + title: L.drawLocal.edit.toolbar.buttons.remove, + }, + ]; + }, + getActions: function () { + return [ + { + title: L.drawLocal.edit.toolbar.actions.save.title, + text: L.drawLocal.edit.toolbar.actions.save.text, + callback: this._save, + context: this, + }, + { + title: L.drawLocal.edit.toolbar.actions.cancel.title, + text: L.drawLocal.edit.toolbar.actions.cancel.text, + callback: this.disable, + context: this, + }, + ]; + }, + addToolbar: function (t) { + var e = L.Toolbar.prototype.addToolbar.call(this, t); + return ( + this._checkDisabled(), + this.options.featureGroup.on("layeradd layerremove", this._checkDisabled, this), + e + ); + }, + removeToolbar: function () { + this.options.featureGroup.off("layeradd layerremove", this._checkDisabled, this), + L.Toolbar.prototype.removeToolbar.call(this); + }, + disable: function () { + this.enabled() && + (this._activeMode.handler.revertLayers(), L.Toolbar.prototype.disable.call(this)); + }, + _save: function () { + this._activeMode.handler.save(), this._activeMode.handler.disable(); + }, + _checkDisabled: function () { + var t, + e = this.options.featureGroup, + i = 0 !== e.getLayers().length; + this.options.edit && + ((t = this._modes[L.EditToolbar.Edit.TYPE].button), + i + ? L.DomUtil.removeClass(t, "leaflet-disabled") + : L.DomUtil.addClass(t, "leaflet-disabled"), + t.setAttribute( + "title", + i + ? L.drawLocal.edit.toolbar.buttons.edit + : L.drawLocal.edit.toolbar.buttons.editDisabled + )), + this.options.remove && + ((t = this._modes[L.EditToolbar.Delete.TYPE].button), + i + ? L.DomUtil.removeClass(t, "leaflet-disabled") + : L.DomUtil.addClass(t, "leaflet-disabled"), + t.setAttribute( + "title", + i + ? L.drawLocal.edit.toolbar.buttons.remove + : L.drawLocal.edit.toolbar.buttons.removeDisabled + )); + }, + })), + (L.EditToolbar.Edit = L.Handler.extend({ + statics: { TYPE: "edit" }, + includes: L.Mixin.Events, + initialize: function (t, e) { + if ( + (L.Handler.prototype.initialize.call(this, t), + (this._selectedPathOptions = e.selectedPathOptions), + (this._featureGroup = e.featureGroup), + !(this._featureGroup instanceof L.FeatureGroup)) + ) + throw new Error("options.featureGroup must be a L.FeatureGroup"); + (this._uneditedLayerProps = {}), (this.type = L.EditToolbar.Edit.TYPE); + }, + enable: function () { + !this._enabled && + this._hasAvailableLayers() && + (this.fire("enabled", { handler: this.type }), + this._map.fire("draw:editstart", { handler: this.type }), + L.Handler.prototype.enable.call(this), + this._featureGroup + .on("layeradd", this._enableLayerEdit, this) + .on("layerremove", this._disableLayerEdit, this)); + }, + disable: function () { + this._enabled && + (this._featureGroup + .off("layeradd", this._enableLayerEdit, this) + .off("layerremove", this._disableLayerEdit, this), + L.Handler.prototype.disable.call(this), + this._map.fire("draw:editstop", { handler: this.type }), + this.fire("disabled", { handler: this.type })); + }, + addHooks: function () { + var t = this._map; + t && + (t.getContainer().focus(), + this._featureGroup.eachLayer(this._enableLayerEdit, this), + (this._tooltip = new L.Tooltip(this._map)), + this._tooltip.updateContent({ + text: L.drawLocal.edit.handlers.edit.tooltip.text, + subtext: L.drawLocal.edit.handlers.edit.tooltip.subtext, + }), + this._map.on("mousemove", this._onMouseMove, this)); + }, + removeHooks: function () { + this._map && + (this._featureGroup.eachLayer(this._disableLayerEdit, this), + (this._uneditedLayerProps = {}), + this._tooltip.dispose(), + (this._tooltip = null), + this._map.off("mousemove", this._onMouseMove, this)); + }, + revertLayers: function () { + this._featureGroup.eachLayer(function (t) { + this._revertLayer(t); + }, this); + }, + save: function () { + var t = new L.LayerGroup(); + this._featureGroup.eachLayer(function (e) { + e.edited && (t.addLayer(e), (e.edited = !1)); + }), + this._map.fire("draw:edited", { layers: t }); + }, + _backupLayer: function (t) { + var e = L.Util.stamp(t); + this._uneditedLayerProps[e] || + (t instanceof L.Polyline || t instanceof L.Polygon || t instanceof L.Rectangle + ? (this._uneditedLayerProps[e] = { + latlngs: L.LatLngUtil.cloneLatLngs(t.getLatLngs()), + }) + : t instanceof L.Circle + ? (this._uneditedLayerProps[e] = { + latlng: L.LatLngUtil.cloneLatLng(t.getLatLng()), + radius: t.getRadius(), + }) + : t instanceof L.Marker && + (this._uneditedLayerProps[e] = { + latlng: L.LatLngUtil.cloneLatLng(t.getLatLng()), + })); + }, + _revertLayer: function (t) { + var e = L.Util.stamp(t); + (t.edited = !1), + this._uneditedLayerProps.hasOwnProperty(e) && + (t instanceof L.Polyline || t instanceof L.Polygon || t instanceof L.Rectangle + ? t.setLatLngs(this._uneditedLayerProps[e].latlngs) + : t instanceof L.Circle + ? (t.setLatLng(this._uneditedLayerProps[e].latlng), + t.setRadius(this._uneditedLayerProps[e].radius)) + : t instanceof L.Marker && t.setLatLng(this._uneditedLayerProps[e].latlng)); + }, + _toggleMarkerHighlight: function (t) { + if (t._icon) { + var e = t._icon; + (e.style.display = "none"), + L.DomUtil.hasClass(e, "leaflet-edit-marker-selected") + ? (L.DomUtil.removeClass(e, "leaflet-edit-marker-selected"), + this._offsetMarker(e, -4)) + : (L.DomUtil.addClass(e, "leaflet-edit-marker-selected"), + this._offsetMarker(e, 4)), + (e.style.display = ""); + } + }, + _offsetMarker: function (t, e) { + var i = parseInt(t.style.marginTop, 10) - e, + o = parseInt(t.style.marginLeft, 10) - e; + (t.style.marginTop = i + "px"), (t.style.marginLeft = o + "px"); + }, + _enableLayerEdit: function (t) { + var e, + i = t.layer || t.target || t, + o = i instanceof L.Marker; + (!o || i._icon) && + (this._backupLayer(i), + this._selectedPathOptions && + ((e = L.Util.extend({}, this._selectedPathOptions)), + o + ? this._toggleMarkerHighlight(i) + : ((i.options.previousOptions = L.Util.extend({ dashArray: null }, i.options)), + i instanceof L.Circle || + i instanceof L.Polygon || + i instanceof L.Rectangle || + (e.fill = !1), + i.setStyle(e))), + o ? (i.dragging.enable(), i.on("dragend", this._onMarkerDragEnd)) : i.editing.enable()); + }, + _disableLayerEdit: function (t) { + var e = t.layer || t.target || t; + (e.edited = !1), + this._selectedPathOptions && + (e instanceof L.Marker + ? this._toggleMarkerHighlight(e) + : (e.setStyle(e.options.previousOptions), delete e.options.previousOptions)), + e instanceof L.Marker + ? (e.dragging.disable(), e.off("dragend", this._onMarkerDragEnd, this)) + : e.editing.disable(); + }, + _onMarkerDragEnd: function (t) { + var e = t.target; + e.edited = !0; + }, + _onMouseMove: function (t) { + this._tooltip.updatePosition(t.latlng); + }, + _hasAvailableLayers: function () { + return 0 !== this._featureGroup.getLayers().length; + }, + })), + (L.EditToolbar.Delete = L.Handler.extend({ + statics: { TYPE: "remove" }, + includes: L.Mixin.Events, + initialize: function (t, e) { + if ( + (L.Handler.prototype.initialize.call(this, t), + L.Util.setOptions(this, e), + (this._deletableLayers = this.options.featureGroup), + !(this._deletableLayers instanceof L.FeatureGroup)) + ) + throw new Error("options.featureGroup must be a L.FeatureGroup"); + this.type = L.EditToolbar.Delete.TYPE; + }, + enable: function () { + !this._enabled && + this._hasAvailableLayers() && + (this.fire("enabled", { handler: this.type }), + this._map.fire("draw:deletestart", { handler: this.type }), + L.Handler.prototype.enable.call(this), + this._deletableLayers + .on("layeradd", this._enableLayerDelete, this) + .on("layerremove", this._disableLayerDelete, this)); + }, + disable: function () { + this._enabled && + (this._deletableLayers + .off("layeradd", this._enableLayerDelete, this) + .off("layerremove", this._disableLayerDelete, this), + L.Handler.prototype.disable.call(this), + this._map.fire("draw:deletestop", { handler: this.type }), + this.fire("disabled", { handler: this.type })); + }, + addHooks: function () { + var t = this._map; + t && + (t.getContainer().focus(), + this._deletableLayers.eachLayer(this._enableLayerDelete, this), + (this._deletedLayers = new L.layerGroup()), + (this._tooltip = new L.Tooltip(this._map)), + this._tooltip.updateContent({ text: L.drawLocal.edit.handlers.remove.tooltip.text }), + this._map.on("mousemove", this._onMouseMove, this)); + }, + removeHooks: function () { + this._map && + (this._deletableLayers.eachLayer(this._disableLayerDelete, this), + (this._deletedLayers = null), + this._tooltip.dispose(), + (this._tooltip = null), + this._map.off("mousemove", this._onMouseMove, this)); + }, + revertLayers: function () { + this._deletedLayers.eachLayer(function (t) { + this._deletableLayers.addLayer(t); + }, this); + }, + save: function () { + this._map.fire("draw:deleted", { layers: this._deletedLayers }); + }, + _enableLayerDelete: function (t) { + var e = t.layer || t.target || t; + e.on("click", this._removeLayer, this); + }, + _disableLayerDelete: function (t) { + var e = t.layer || t.target || t; + e.off("click", this._removeLayer, this), this._deletedLayers.removeLayer(e); + }, + _removeLayer: function (t) { + var e = t.layer || t.target || t; + this._deletableLayers.removeLayer(e), this._deletedLayers.addLayer(e); + }, + _onMouseMove: function (t) { + this._tooltip.updatePosition(t.latlng); + }, + _hasAvailableLayers: function () { + return 0 !== this._deletableLayers.getLayers().length; + }, + })); +})(window, document); diff --git a/erpnext/public/js/leaflet/leaflet.js b/erpnext/public/js/leaflet/leaflet.js index 91dd3d434c7..cef823a2e1a 100755 --- a/erpnext/public/js/leaflet/leaflet.js +++ b/erpnext/public/js/leaflet/leaflet.js @@ -2,770 +2,5633 @@ Leaflet 1.0.0-beta.2 (dd0faa1), a JS library for interactive maps. http://leafletjs.com (c) 2010-2015 Vladimir Agafonkin, (c) 2010-2011 CloudMade */ -! function(t, e, i) { - function n() { var e = t.L; - o.noConflict = function() { return t.L = e, this }, t.L = o } - var o = { version: "1.0.0-beta.2" }; - "object" == typeof module && "object" == typeof module.exports ? module.exports = o : "function" == typeof define && define.amd && define(o), "undefined" != typeof t && n(), o.Util = { extend: function(t) { var e, i, n, o; for (i = 1, n = arguments.length; n > i; i++) { o = arguments[i]; for (e in o) t[e] = o[e] } return t }, create: Object.create || function() { - function t() {} return function(e) { return t.prototype = e, new t } }(), bind: function(t, e) { var i = Array.prototype.slice; if (t.bind) return t.bind.apply(t, i.call(arguments, 1)); var n = i.call(arguments, 2); return function() { return t.apply(e, n.length ? n.concat(i.call(arguments)) : arguments) } }, stamp: function(t) { return t._leaflet_id = t._leaflet_id || ++o.Util.lastId, t._leaflet_id }, lastId: 0, throttle: function(t, e, i) { var n, o, s, r; return r = function() { n = !1, o && (s.apply(i, o), o = !1) }, s = function() { n ? o = arguments : (t.apply(i, arguments), setTimeout(r, e), n = !0) } }, wrapNum: function(t, e, i) { var n = e[1], - o = e[0], - s = n - o; return t === n && i ? t : ((t - o) % s + s) % s + o }, falseFn: function() { return !1 }, formatNum: function(t, e) { var i = Math.pow(10, e || 5); return Math.round(t * i) / i }, trim: function(t) { return t.trim ? t.trim() : t.replace(/^\s+|\s+$/g, "") }, splitWords: function(t) { return o.Util.trim(t).split(/\s+/) }, setOptions: function(t, e) { t.hasOwnProperty("options") || (t.options = t.options ? o.Util.create(t.options) : {}); for (var i in e) t.options[i] = e[i]; return t.options }, getParamString: function(t, e, i) { var n = []; for (var o in t) n.push(encodeURIComponent(i ? o.toUpperCase() : o) + "=" + encodeURIComponent(t[o])); return (e && -1 !== e.indexOf("?") ? "&" : "?") + n.join("&") }, template: function(t, e) { return t.replace(o.Util.templateRe, function(t, n) { var o = e[n]; if (o === i) throw new Error("No value provided for variable " + t); return "function" == typeof o && (o = o(e)), o }) }, templateRe: /\{ *([\w_]+) *\}/g, isArray: Array.isArray || function(t) { return "[object Array]" === Object.prototype.toString.call(t) }, indexOf: function(t, e) { for (var i = 0; i < t.length; i++) - if (t[i] === e) return i; - return -1 }, emptyImageUrl: "data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=" }, - function() { - function e(e) { return t["webkit" + e] || t["moz" + e] || t["ms" + e] } +!(function (t, e, i) { + function n() { + var e = t.L; + (o.noConflict = function () { + return (t.L = e), this; + }), + (t.L = o); + } + var o = { version: "1.0.0-beta.2" }; + "object" == typeof module && "object" == typeof module.exports + ? (module.exports = o) + : "function" == typeof define && define.amd && define(o), + "undefined" != typeof t && n(), + (o.Util = { + extend: function (t) { + var e, i, n, o; + for (i = 1, n = arguments.length; n > i; i++) { + o = arguments[i]; + for (e in o) t[e] = o[e]; + } + return t; + }, + create: + Object.create || + (function () { + function t() {} + return function (e) { + return (t.prototype = e), new t(); + }; + })(), + bind: function (t, e) { + var i = Array.prototype.slice; + if (t.bind) return t.bind.apply(t, i.call(arguments, 1)); + var n = i.call(arguments, 2); + return function () { + return t.apply(e, n.length ? n.concat(i.call(arguments)) : arguments); + }; + }, + stamp: function (t) { + return (t._leaflet_id = t._leaflet_id || ++o.Util.lastId), t._leaflet_id; + }, + lastId: 0, + throttle: function (t, e, i) { + var n, o, s, r; + return ( + (r = function () { + (n = !1), o && (s.apply(i, o), (o = !1)); + }), + (s = function () { + n ? (o = arguments) : (t.apply(i, arguments), setTimeout(r, e), (n = !0)); + }) + ); + }, + wrapNum: function (t, e, i) { + var n = e[1], + o = e[0], + s = n - o; + return t === n && i ? t : ((((t - o) % s) + s) % s) + o; + }, + falseFn: function () { + return !1; + }, + formatNum: function (t, e) { + var i = Math.pow(10, e || 5); + return Math.round(t * i) / i; + }, + trim: function (t) { + return t.trim ? t.trim() : t.replace(/^\s+|\s+$/g, ""); + }, + splitWords: function (t) { + return o.Util.trim(t).split(/\s+/); + }, + setOptions: function (t, e) { + t.hasOwnProperty("options") || (t.options = t.options ? o.Util.create(t.options) : {}); + for (var i in e) t.options[i] = e[i]; + return t.options; + }, + getParamString: function (t, e, i) { + var n = []; + for (var o in t) + n.push(encodeURIComponent(i ? o.toUpperCase() : o) + "=" + encodeURIComponent(t[o])); + return (e && -1 !== e.indexOf("?") ? "&" : "?") + n.join("&"); + }, + template: function (t, e) { + return t.replace(o.Util.templateRe, function (t, n) { + var o = e[n]; + if (o === i) throw new Error("No value provided for variable " + t); + return "function" == typeof o && (o = o(e)), o; + }); + }, + templateRe: /\{ *([\w_]+) *\}/g, + isArray: + Array.isArray || + function (t) { + return "[object Array]" === Object.prototype.toString.call(t); + }, + indexOf: function (t, e) { + for (var i = 0; i < t.length; i++) if (t[i] === e) return i; + return -1; + }, + emptyImageUrl: "data:image/gif;base64,R0lGODlhAQABAAD/ACwAAAAAAQABAAACADs=", + }), + (function () { + function e(e) { + return t["webkit" + e] || t["moz" + e] || t["ms" + e]; + } - function i(e) { var i = +new Date, - o = Math.max(0, 16 - (i - n)); return n = i + o, t.setTimeout(e, o) } var n = 0, - s = t.requestAnimationFrame || e("RequestAnimationFrame") || i, - r = t.cancelAnimationFrame || e("CancelAnimationFrame") || e("CancelRequestAnimationFrame") || function(e) { t.clearTimeout(e) }; - o.Util.requestAnimFrame = function(e, n, r) { return r && s === i ? void e.call(n) : s.call(t, o.bind(e, n)) }, o.Util.cancelAnimFrame = function(e) { e && r.call(t, e) } }(), o.extend = o.Util.extend, o.bind = o.Util.bind, o.stamp = o.Util.stamp, o.setOptions = o.Util.setOptions, o.Class = function() {}, o.Class.extend = function(t) { var e = function() { this.initialize && this.initialize.apply(this, arguments), this.callInitHooks() }, - i = e.__super__ = this.prototype, - n = o.Util.create(i); - n.constructor = e, e.prototype = n; for (var s in this) this.hasOwnProperty(s) && "prototype" !== s && (e[s] = this[s]); return t.statics && (o.extend(e, t.statics), delete t.statics), t.includes && (o.Util.extend.apply(null, [n].concat(t.includes)), delete t.includes), n.options && (t.options = o.Util.extend(o.Util.create(n.options), t.options)), o.extend(n, t), n._initHooks = [], n.callInitHooks = function() { if (!this._initHooksCalled) { i.callInitHooks && i.callInitHooks.call(this), this._initHooksCalled = !0; for (var t = 0, e = n._initHooks.length; e > t; t++) n._initHooks[t].call(this) } }, e }, o.Class.include = function(t) { o.extend(this.prototype, t) }, o.Class.mergeOptions = function(t) { o.extend(this.prototype.options, t) }, o.Class.addInitHook = function(t) { var e = Array.prototype.slice.call(arguments, 1), - i = "function" == typeof t ? t : function() { this[t].apply(this, e) }; - this.prototype._initHooks = this.prototype._initHooks || [], this.prototype._initHooks.push(i) }, o.Evented = o.Class.extend({ on: function(t, e, i) { if ("object" == typeof t) - for (var n in t) this._on(n, t[n], e); - else { t = o.Util.splitWords(t); for (var s = 0, r = t.length; r > s; s++) this._on(t[s], e, i) } return this }, off: function(t, e, i) { if (t) - if ("object" == typeof t) - for (var n in t) this._off(n, t[n], e); - else { t = o.Util.splitWords(t); for (var s = 0, r = t.length; r > s; s++) this._off(t[s], e, i) } - else delete this._events; return this }, _on: function(t, e, i) { var n = this._events = this._events || {}, - s = i && i !== this && o.stamp(i); if (s) { var r = t + "_idx", - a = t + "_len", - h = n[r] = n[r] || {}, - l = o.stamp(e) + "_" + s; - h[l] || (h[l] = { fn: e, ctx: i }, n[a] = (n[a] || 0) + 1) } else n[t] = n[t] || [], n[t].push({ fn: e }) }, _off: function(t, e, i) { var n = this._events, - s = t + "_idx", - r = t + "_len"; if (n) { if (!e) return delete n[t], delete n[s], void delete n[r]; var a, h, l, u, c, d = i && i !== this && o.stamp(i); if (d) c = o.stamp(e) + "_" + d, a = n[s], a && a[c] && (u = a[c], delete a[c], n[r]--); - else if (a = n[t]) - for (h = 0, l = a.length; l > h; h++) - if (a[h].fn === e) { u = a[h], a.splice(h, 1); break } - u && (u.fn = o.Util.falseFn) } }, fire: function(t, e, i) { if (!this.listens(t, i)) return this; var n = o.Util.extend({}, e, { type: t, target: this }), - s = this._events; if (s) { var r, a, h, l, u = s[t + "_idx"]; if (s[t]) - for (h = s[t].slice(), r = 0, a = h.length; a > r; r++) h[r].fn.call(this, n); for (l in u) u[l].fn.call(u[l].ctx, n) } return i && this._propagateEvent(n), this }, listens: function(t, e) { var i = this._events; if (i && (i[t] || i[t + "_len"])) return !0; if (e) - for (var n in this._eventParents) - if (this._eventParents[n].listens(t, e)) return !0; - return !1 }, once: function(t, e, i) { if ("object" == typeof t) { for (var n in t) this.once(n, t[n], e); return this } var s = o.bind(function() { this.off(t, e, i).off(t, s, i) }, this); return this.on(t, e, i).on(t, s, i) }, addEventParent: function(t) { return this._eventParents = this._eventParents || {}, this._eventParents[o.stamp(t)] = t, this }, removeEventParent: function(t) { return this._eventParents && delete this._eventParents[o.stamp(t)], this }, _propagateEvent: function(t) { for (var e in this._eventParents) this._eventParents[e].fire(t.type, o.extend({ layer: t.target }, t), !0) } }); - var s = o.Evented.prototype; - s.addEventListener = s.on, s.removeEventListener = s.clearAllEventListeners = s.off, s.addOneTimeEventListener = s.once, s.fireEvent = s.fire, s.hasEventListeners = s.listens, o.Mixin = { Events: s }, - function() { var i = navigator.userAgent.toLowerCase(), - n = e.documentElement, - s = "ActiveXObject" in t, - r = -1 !== i.indexOf("webkit"), - a = -1 !== i.indexOf("phantom"), - h = -1 !== i.search("android [23]"), - l = -1 !== i.indexOf("chrome"), - u = -1 !== i.indexOf("gecko") && !r && !t.opera && !s, - c = "undefined" != typeof orientation || -1 !== i.indexOf("mobile"), - d = !t.PointerEvent && t.MSPointerEvent, - _ = t.PointerEvent && navigator.pointerEnabled || d, - m = s && "transition" in n.style, - p = "WebKitCSSMatrix" in t && "m11" in new t.WebKitCSSMatrix && !h, - f = "MozPerspective" in n.style, - g = "OTransition" in n.style, - v = !t.L_NO_TOUCH && !a && (_ || "ontouchstart" in t || t.DocumentTouch && e instanceof t.DocumentTouch); - o.Browser = { ie: s, ielt9: s && !e.addEventListener, webkit: r, gecko: u, android: -1 !== i.indexOf("android"), android23: h, chrome: l, safari: !l && -1 !== i.indexOf("safari"), ie3d: m, webkit3d: p, gecko3d: f, opera12: g, any3d: !t.L_DISABLE_3D && (m || p || f) && !g && !a, mobile: c, mobileWebkit: c && r, mobileWebkit3d: c && p, mobileOpera: c && t.opera, mobileGecko: c && u, touch: !!v, msPointer: !!d, pointer: !!_, retina: (t.devicePixelRatio || t.screen.deviceXDPI / t.screen.logicalXDPI) > 1 } }(), o.Point = function(t, e, i) { this.x = i ? Math.round(t) : t, this.y = i ? Math.round(e) : e }, o.Point.prototype = { clone: function() { return new o.Point(this.x, this.y) }, add: function(t) { return this.clone()._add(o.point(t)) }, _add: function(t) { return this.x += t.x, this.y += t.y, this }, subtract: function(t) { return this.clone()._subtract(o.point(t)) }, _subtract: function(t) { return this.x -= t.x, this.y -= t.y, this }, divideBy: function(t) { return this.clone()._divideBy(t) }, _divideBy: function(t) { return this.x /= t, this.y /= t, this }, multiplyBy: function(t) { return this.clone()._multiplyBy(t) }, _multiplyBy: function(t) { return this.x *= t, this.y *= t, this }, scaleBy: function(t) { return new o.Point(this.x * t.x, this.y * t.y) }, unscaleBy: function(t) { return new o.Point(this.x / t.x, this.y / t.y) }, round: function() { return this.clone()._round() }, _round: function() { return this.x = Math.round(this.x), this.y = Math.round(this.y), this }, floor: function() { return this.clone()._floor() }, _floor: function() { return this.x = Math.floor(this.x), this.y = Math.floor(this.y), this }, ceil: function() { return this.clone()._ceil() }, _ceil: function() { return this.x = Math.ceil(this.x), this.y = Math.ceil(this.y), this }, distanceTo: function(t) { t = o.point(t); var e = t.x - this.x, - i = t.y - this.y; return Math.sqrt(e * e + i * i) }, equals: function(t) { return t = o.point(t), t.x === this.x && t.y === this.y }, contains: function(t) { return t = o.point(t), Math.abs(t.x) <= Math.abs(this.x) && Math.abs(t.y) <= Math.abs(this.y) }, toString: function() { return "Point(" + o.Util.formatNum(this.x) + ", " + o.Util.formatNum(this.y) + ")" } }, o.point = function(t, e, n) { return t instanceof o.Point ? t : o.Util.isArray(t) ? new o.Point(t[0], t[1]) : t === i || null === t ? t : new o.Point(t, e, n) }, o.Bounds = function(t, e) { if (t) - for (var i = e ? [t, e] : t, n = 0, o = i.length; o > n; n++) this.extend(i[n]) }, o.Bounds.prototype = { extend: function(t) { return t = o.point(t), this.min || this.max ? (this.min.x = Math.min(t.x, this.min.x), this.max.x = Math.max(t.x, this.max.x), this.min.y = Math.min(t.y, this.min.y), this.max.y = Math.max(t.y, this.max.y)) : (this.min = t.clone(), this.max = t.clone()), this }, getCenter: function(t) { return new o.Point((this.min.x + this.max.x) / 2, (this.min.y + this.max.y) / 2, t) }, getBottomLeft: function() { return new o.Point(this.min.x, this.max.y) }, getTopRight: function() { return new o.Point(this.max.x, this.min.y) }, getSize: function() { return this.max.subtract(this.min) }, contains: function(t) { var e, i; return t = "number" == typeof t[0] || t instanceof o.Point ? o.point(t) : o.bounds(t), t instanceof o.Bounds ? (e = t.min, i = t.max) : e = i = t, e.x >= this.min.x && i.x <= this.max.x && e.y >= this.min.y && i.y <= this.max.y }, intersects: function(t) { t = o.bounds(t); var e = this.min, - i = this.max, - n = t.min, - s = t.max, - r = s.x >= e.x && n.x <= i.x, - a = s.y >= e.y && n.y <= i.y; return r && a }, overlaps: function(t) { t = o.bounds(t); var e = this.min, - i = this.max, - n = t.min, - s = t.max, - r = s.x > e.x && n.x < i.x, - a = s.y > e.y && n.y < i.y; return r && a }, isValid: function() { return !(!this.min || !this.max) } }, o.bounds = function(t, e) { return !t || t instanceof o.Bounds ? t : new o.Bounds(t, e) }, o.Transformation = function(t, e, i, n) { this._a = t, this._b = e, this._c = i, this._d = n }, o.Transformation.prototype = { transform: function(t, e) { return this._transform(t.clone(), e) }, _transform: function(t, e) { return e = e || 1, t.x = e * (this._a * t.x + this._b), t.y = e * (this._c * t.y + this._d), t }, untransform: function(t, e) { return e = e || 1, new o.Point((t.x / e - this._b) / this._a, (t.y / e - this._d) / this._c) } }, o.DomUtil = { get: function(t) { return "string" == typeof t ? e.getElementById(t) : t }, getStyle: function(t, i) { var n = t.style[i] || t.currentStyle && t.currentStyle[i]; if ((!n || "auto" === n) && e.defaultView) { var o = e.defaultView.getComputedStyle(t, null); - n = o ? o[i] : null } return "auto" === n ? null : n }, create: function(t, i, n) { var o = e.createElement(t); return o.className = i, n && n.appendChild(o), o }, remove: function(t) { var e = t.parentNode; - e && e.removeChild(t) }, empty: function(t) { for (; t.firstChild;) t.removeChild(t.firstChild) }, toFront: function(t) { t.parentNode.appendChild(t) }, toBack: function(t) { var e = t.parentNode; - e.insertBefore(t, e.firstChild) }, hasClass: function(t, e) { if (t.classList !== i) return t.classList.contains(e); var n = o.DomUtil.getClass(t); return n.length > 0 && new RegExp("(^|\\s)" + e + "(\\s|$)").test(n) }, addClass: function(t, e) { if (t.classList !== i) - for (var n = o.Util.splitWords(e), s = 0, r = n.length; r > s; s++) t.classList.add(n[s]); - else if (!o.DomUtil.hasClass(t, e)) { var a = o.DomUtil.getClass(t); - o.DomUtil.setClass(t, (a ? a + " " : "") + e) } }, removeClass: function(t, e) { t.classList !== i ? t.classList.remove(e) : o.DomUtil.setClass(t, o.Util.trim((" " + o.DomUtil.getClass(t) + " ").replace(" " + e + " ", " "))) }, setClass: function(t, e) { t.className.baseVal === i ? t.className = e : t.className.baseVal = e }, getClass: function(t) { return t.className.baseVal === i ? t.className : t.className.baseVal }, setOpacity: function(t, e) { "opacity" in t.style ? t.style.opacity = e : "filter" in t.style && o.DomUtil._setOpacityIE(t, e) }, _setOpacityIE: function(t, e) { var i = !1, - n = "DXImageTransform.Microsoft.Alpha"; try { i = t.filters.item(n) } catch (o) { if (1 === e) return } - e = Math.round(100 * e), i ? (i.Enabled = 100 !== e, i.Opacity = e) : t.style.filter += " progid:" + n + "(opacity=" + e + ")" }, testProp: function(t) { for (var i = e.documentElement.style, n = 0; n < t.length; n++) - if (t[n] in i) return t[n]; - return !1 }, setTransform: function(t, e, i) { var n = e || new o.Point(0, 0); - t.style[o.DomUtil.TRANSFORM] = (o.Browser.ie3d ? "translate(" + n.x + "px," + n.y + "px)" : "translate3d(" + n.x + "px," + n.y + "px,0)") + (i ? " scale(" + i + ")" : "") }, setPosition: function(t, e) { t._leaflet_pos = e, o.Browser.any3d ? o.DomUtil.setTransform(t, e) : (t.style.left = e.x + "px", t.style.top = e.y + "px") }, getPosition: function(t) { return t._leaflet_pos } }, - function() { o.DomUtil.TRANSFORM = o.DomUtil.testProp(["transform", "WebkitTransform", "OTransform", "MozTransform", "msTransform"]); var i = o.DomUtil.TRANSITION = o.DomUtil.testProp(["webkitTransition", "transition", "OTransition", "MozTransition", "msTransition"]); if (o.DomUtil.TRANSITION_END = "webkitTransition" === i || "OTransition" === i ? i + "End" : "transitionend", "onselectstart" in e) o.DomUtil.disableTextSelection = function() { o.DomEvent.on(t, "selectstart", o.DomEvent.preventDefault) }, o.DomUtil.enableTextSelection = function() { o.DomEvent.off(t, "selectstart", o.DomEvent.preventDefault) }; - else { var n = o.DomUtil.testProp(["userSelect", "WebkitUserSelect", "OUserSelect", "MozUserSelect", "msUserSelect"]); - o.DomUtil.disableTextSelection = function() { if (n) { var t = e.documentElement.style; - this._userSelect = t[n], t[n] = "none" } }, o.DomUtil.enableTextSelection = function() { n && (e.documentElement.style[n] = this._userSelect, delete this._userSelect) } } - o.DomUtil.disableImageDrag = function() { o.DomEvent.on(t, "dragstart", o.DomEvent.preventDefault) }, o.DomUtil.enableImageDrag = function() { o.DomEvent.off(t, "dragstart", o.DomEvent.preventDefault) }, o.DomUtil.preventOutline = function(e) { for (; - 1 === e.tabIndex;) e = e.parentNode; - e && e.style && (o.DomUtil.restoreOutline(), this._outlineElement = e, this._outlineStyle = e.style.outline, e.style.outline = "none", o.DomEvent.on(t, "keydown", o.DomUtil.restoreOutline, this)) }, o.DomUtil.restoreOutline = function() { this._outlineElement && (this._outlineElement.style.outline = this._outlineStyle, delete this._outlineElement, delete this._outlineStyle, o.DomEvent.off(t, "keydown", o.DomUtil.restoreOutline, this)) } }(), o.LatLng = function(t, e, n) { if (isNaN(t) || isNaN(e)) throw new Error("Invalid LatLng object: (" + t + ", " + e + ")"); - this.lat = +t, this.lng = +e, n !== i && (this.alt = +n) }, o.LatLng.prototype = { equals: function(t, e) { if (!t) return !1; - t = o.latLng(t); var n = Math.max(Math.abs(this.lat - t.lat), Math.abs(this.lng - t.lng)); return (e === i ? 1e-9 : e) >= n }, toString: function(t) { return "LatLng(" + o.Util.formatNum(this.lat, t) + ", " + o.Util.formatNum(this.lng, t) + ")" }, distanceTo: function(t) { return o.CRS.Earth.distance(this, o.latLng(t)) }, wrap: function() { return o.CRS.Earth.wrapLatLng(this) }, toBounds: function(t) { var e = 180 * t / 40075017, - i = e / Math.cos(Math.PI / 180 * this.lat); return o.latLngBounds([this.lat - e, this.lng - i], [this.lat + e, this.lng + i]) }, clone: function() { return new o.LatLng(this.lat, this.lng, this.alt) } }, o.latLng = function(t, e, n) { return t instanceof o.LatLng ? t : o.Util.isArray(t) && "object" != typeof t[0] ? 3 === t.length ? new o.LatLng(t[0], t[1], t[2]) : 2 === t.length ? new o.LatLng(t[0], t[1]) : null : t === i || null === t ? t : "object" == typeof t && "lat" in t ? new o.LatLng(t.lat, "lng" in t ? t.lng : t.lon, t.alt) : e === i ? null : new o.LatLng(t, e, n) }, o.LatLngBounds = function(t, e) { if (t) - for (var i = e ? [t, e] : t, n = 0, o = i.length; o > n; n++) this.extend(i[n]) }, o.LatLngBounds.prototype = { extend: function(t) { var e, i, n = this._southWest, - s = this._northEast; if (t instanceof o.LatLng) e = t, i = t; - else { if (!(t instanceof o.LatLngBounds)) return t ? this.extend(o.latLng(t) || o.latLngBounds(t)) : this; if (e = t._southWest, i = t._northEast, !e || !i) return this } return n || s ? (n.lat = Math.min(e.lat, n.lat), n.lng = Math.min(e.lng, n.lng), s.lat = Math.max(i.lat, s.lat), s.lng = Math.max(i.lng, s.lng)) : (this._southWest = new o.LatLng(e.lat, e.lng), this._northEast = new o.LatLng(i.lat, i.lng)), this }, pad: function(t) { var e = this._southWest, - i = this._northEast, - n = Math.abs(e.lat - i.lat) * t, - s = Math.abs(e.lng - i.lng) * t; return new o.LatLngBounds(new o.LatLng(e.lat - n, e.lng - s), new o.LatLng(i.lat + n, i.lng + s)) }, getCenter: function() { return new o.LatLng((this._southWest.lat + this._northEast.lat) / 2, (this._southWest.lng + this._northEast.lng) / 2) }, getSouthWest: function() { return this._southWest }, getNorthEast: function() { return this._northEast }, getNorthWest: function() { return new o.LatLng(this.getNorth(), this.getWest()) }, getSouthEast: function() { return new o.LatLng(this.getSouth(), this.getEast()) }, getWest: function() { return this._southWest.lng }, getSouth: function() { return this._southWest.lat }, getEast: function() { return this._northEast.lng }, getNorth: function() { return this._northEast.lat }, contains: function(t) { t = "number" == typeof t[0] || t instanceof o.LatLng ? o.latLng(t) : o.latLngBounds(t); var e, i, n = this._southWest, - s = this._northEast; return t instanceof o.LatLngBounds ? (e = t.getSouthWest(), i = t.getNorthEast()) : e = i = t, e.lat >= n.lat && i.lat <= s.lat && e.lng >= n.lng && i.lng <= s.lng }, intersects: function(t) { t = o.latLngBounds(t); var e = this._southWest, - i = this._northEast, - n = t.getSouthWest(), - s = t.getNorthEast(), - r = s.lat >= e.lat && n.lat <= i.lat, - a = s.lng >= e.lng && n.lng <= i.lng; return r && a }, overlaps: function(t) { t = o.latLngBounds(t); var e = this._southWest, - i = this._northEast, - n = t.getSouthWest(), - s = t.getNorthEast(), - r = s.lat > e.lat && n.lat < i.lat, - a = s.lng > e.lng && n.lng < i.lng; return r && a }, toBBoxString: function() { return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(",") }, equals: function(t) { return t ? (t = o.latLngBounds(t), this._southWest.equals(t.getSouthWest()) && this._northEast.equals(t.getNorthEast())) : !1 }, isValid: function() { return !(!this._southWest || !this._northEast) } }, o.latLngBounds = function(t, e) { return !t || t instanceof o.LatLngBounds ? t : new o.LatLngBounds(t, e) }, o.Projection = {}, o.Projection.LonLat = { project: function(t) { return new o.Point(t.lng, t.lat) }, unproject: function(t) { return new o.LatLng(t.y, t.x) }, bounds: o.bounds([-180, -90], [180, 90]) }, o.Projection.SphericalMercator = { R: 6378137, MAX_LATITUDE: 85.0511287798, project: function(t) { var e = Math.PI / 180, - i = this.MAX_LATITUDE, - n = Math.max(Math.min(i, t.lat), -i), - s = Math.sin(n * e); return new o.Point(this.R * t.lng * e, this.R * Math.log((1 + s) / (1 - s)) / 2) }, unproject: function(t) { var e = 180 / Math.PI; return new o.LatLng((2 * Math.atan(Math.exp(t.y / this.R)) - Math.PI / 2) * e, t.x * e / this.R) }, bounds: function() { var t = 6378137 * Math.PI; return o.bounds([-t, -t], [t, t]) }() }, o.CRS = { latLngToPoint: function(t, e) { var i = this.projection.project(t), - n = this.scale(e); return this.transformation._transform(i, n) }, pointToLatLng: function(t, e) { var i = this.scale(e), - n = this.transformation.untransform(t, i); return this.projection.unproject(n) }, project: function(t) { return this.projection.project(t) }, unproject: function(t) { return this.projection.unproject(t) }, scale: function(t) { return 256 * Math.pow(2, t) }, zoom: function(t) { return Math.log(t / 256) / Math.LN2 }, getProjectedBounds: function(t) { if (this.infinite) return null; var e = this.projection.bounds, - i = this.scale(t), - n = this.transformation.transform(e.min, i), - s = this.transformation.transform(e.max, i); return o.bounds(n, s) }, wrapLatLng: function(t) { var e = this.wrapLng ? o.Util.wrapNum(t.lng, this.wrapLng, !0) : t.lng, - i = this.wrapLat ? o.Util.wrapNum(t.lat, this.wrapLat, !0) : t.lat, - n = t.alt; return o.latLng(i, e, n) } }, o.CRS.Simple = o.extend({}, o.CRS, { projection: o.Projection.LonLat, transformation: new o.Transformation(1, 0, -1, 0), scale: function(t) { return Math.pow(2, t) }, zoom: function(t) { return Math.log(t) / Math.LN2 }, distance: function(t, e) { var i = e.lng - t.lng, - n = e.lat - t.lat; return Math.sqrt(i * i + n * n) }, infinite: !0 }), o.CRS.Earth = o.extend({}, o.CRS, { wrapLng: [-180, 180], R: 6378137, distance: function(t, e) { var i = Math.PI / 180, - n = t.lat * i, - o = e.lat * i, - s = Math.sin(n) * Math.sin(o) + Math.cos(n) * Math.cos(o) * Math.cos((e.lng - t.lng) * i); return this.R * Math.acos(Math.min(s, 1)) } }), o.CRS.EPSG3857 = o.extend({}, o.CRS.Earth, { code: "EPSG:3857", projection: o.Projection.SphericalMercator, transformation: function() { var t = .5 / (Math.PI * o.Projection.SphericalMercator.R); return new o.Transformation(t, .5, -t, .5) }() }), o.CRS.EPSG900913 = o.extend({}, o.CRS.EPSG3857, { code: "EPSG:900913" }), o.CRS.EPSG4326 = o.extend({}, o.CRS.Earth, { code: "EPSG:4326", projection: o.Projection.LonLat, transformation: new o.Transformation(1 / 180, 1, -1 / 180, .5) }), o.Map = o.Evented.extend({ - options: { crs: o.CRS.EPSG3857, fadeAnimation: !0, trackResize: !0, markerZoomAnimation: !0, maxBoundsViscosity: 0, transform3DLimit: 8388608 }, - initialize: function(t, e) { e = o.setOptions(this, e), this._initContainer(t), this._initLayout(), this._onResize = o.bind(this._onResize, this), this._initEvents(), e.maxBounds && this.setMaxBounds(e.maxBounds), e.zoom !== i && (this._zoom = this._limitZoom(e.zoom)), e.center && e.zoom !== i && this.setView(o.latLng(e.center), e.zoom, { reset: !0 }), this._handlers = [], this._layers = {}, this._zoomBoundLayers = {}, this._sizeChanged = !0, this.callInitHooks(), this._addLayers(this.options.layers) }, - setView: function(t, e) { return e = e === i ? this.getZoom() : e, this._resetView(o.latLng(t), e), this }, - setZoom: function(t, e) { return this._loaded ? this.setView(this.getCenter(), t, { zoom: e }) : (this._zoom = t, this) }, - zoomIn: function(t, e) { return this.setZoom(this._zoom + (t || 1), e) }, - zoomOut: function(t, e) { return this.setZoom(this._zoom - (t || 1), e) }, - setZoomAround: function(t, e, i) { var n = this.getZoomScale(e), - s = this.getSize().divideBy(2), - r = t instanceof o.Point ? t : this.latLngToContainerPoint(t), - a = r.subtract(s).multiplyBy(1 - 1 / n), - h = this.containerPointToLatLng(s.add(a)); return this.setView(h, e, { zoom: i }) }, - _getBoundsCenterZoom: function(t, e) { e = e || {}, t = t.getBounds ? t.getBounds() : o.latLngBounds(t); var i = o.point(e.paddingTopLeft || e.padding || [0, 0]), - n = o.point(e.paddingBottomRight || e.padding || [0, 0]), - s = this.getBoundsZoom(t, !1, i.add(n)); - s = e.maxZoom ? Math.min(e.maxZoom, s) : s; var r = n.subtract(i).divideBy(2), - a = this.project(t.getSouthWest(), s), - h = this.project(t.getNorthEast(), s), - l = this.unproject(a.add(h).divideBy(2).add(r), s); return { center: l, zoom: s } }, - fitBounds: function(t, e) { var i = this._getBoundsCenterZoom(t, e); return this.setView(i.center, i.zoom, e) }, - fitWorld: function(t) { return this.fitBounds([ - [-90, -180], - [90, 180] - ], t) }, - panTo: function(t, e) { return this.setView(t, this._zoom, { pan: e }) }, - panBy: function(t) { return this.fire("movestart"), this._rawPanBy(o.point(t)), this.fire("move"), this.fire("moveend") }, - setMaxBounds: function(t) { return (t = o.latLngBounds(t)) ? (this.options.maxBounds && this.off("moveend", this._panInsideMaxBounds), this.options.maxBounds = t, this._loaded && this._panInsideMaxBounds(), this.on("moveend", this._panInsideMaxBounds)) : this.off("moveend", this._panInsideMaxBounds) }, - setMinZoom: function(t) { return this.options.minZoom = t, this._loaded && this.getZoom() < this.options.minZoom ? this.setZoom(t) : this }, - setMaxZoom: function(t) { return this.options.maxZoom = t, this._loaded && this.getZoom() > this.options.maxZoom ? this.setZoom(t) : this }, - panInsideBounds: function(t, e) { this._enforcingBounds = !0; var i = this.getCenter(), - n = this._limitCenter(i, this._zoom, o.latLngBounds(t)); return i.equals(n) ? this : (this.panTo(n, e), this._enforcingBounds = !1, this) }, - invalidateSize: function(t) { if (!this._loaded) return this; - t = o.extend({ animate: !1, pan: !0 }, t === !0 ? { animate: !0 } : t); var e = this.getSize(); - this._sizeChanged = !0, this._lastCenter = null; var i = this.getSize(), - n = e.divideBy(2).round(), - s = i.divideBy(2).round(), - r = n.subtract(s); return r.x || r.y ? (t.animate && t.pan ? this.panBy(r) : (t.pan && this._rawPanBy(r), this.fire("move"), t.debounceMoveend ? (clearTimeout(this._sizeTimer), this._sizeTimer = setTimeout(o.bind(this.fire, this, "moveend"), 200)) : this.fire("moveend")), this.fire("resize", { oldSize: e, newSize: i })) : this }, - stop: function() { return o.Util.cancelAnimFrame(this._flyToFrame), this._panAnim && this._panAnim.stop(), this }, - addHandler: function(t, e) { if (!e) return this; var i = this[t] = new e(this); return this._handlers.push(i), this.options[t] && i.enable(), this }, - remove: function() { this._initEvents(!0); try { delete this._container._leaflet } catch (t) { this._container._leaflet = i } - o.DomUtil.remove(this._mapPane), this._clearControlPos && this._clearControlPos(), this._clearHandlers(), this._loaded && this.fire("unload"); for (var e in this._layers) this._layers[e].remove(); return this }, - createPane: function(t, e) { var i = "leaflet-pane" + (t ? " leaflet-" + t.replace("Pane", "") + "-pane" : ""), - n = o.DomUtil.create("div", i, e || this._mapPane); return t && (this._panes[t] = n), n }, - getCenter: function() { return this._checkIfLoaded(), this._lastCenter && !this._moved() ? this._lastCenter : this.layerPointToLatLng(this._getCenterLayerPoint()) }, - getZoom: function() { return this._zoom }, - getBounds: function() { var t = this.getPixelBounds(), - e = this.unproject(t.getBottomLeft()), - i = this.unproject(t.getTopRight()); return new o.LatLngBounds(e, i) }, - getMinZoom: function() { return this.options.minZoom === i ? this._layersMinZoom || 0 : this.options.minZoom }, - getMaxZoom: function() { return this.options.maxZoom === i ? this._layersMaxZoom === i ? 1 / 0 : this._layersMaxZoom : this.options.maxZoom }, - getBoundsZoom: function(t, e, i) { t = o.latLngBounds(t); var n, s = this.getMinZoom() - (e ? 1 : 0), - r = this.getMaxZoom(), - a = this.getSize(), - h = t.getNorthWest(), - l = t.getSouthEast(), - u = !0; - i = o.point(i || [0, 0]); - do s++, n = this.project(l, s).subtract(this.project(h, s)).add(i).floor(), u = e ? n.x < a.x || n.y < a.y : a.contains(n); while (u && r >= s); return u && e ? null : e ? s : s - 1 }, - getSize: function() { return (!this._size || this._sizeChanged) && (this._size = new o.Point(this._container.clientWidth, this._container.clientHeight), this._sizeChanged = !1), this._size.clone() }, - getPixelBounds: function(t, e) { var i = this._getTopLeftPoint(t, e); return new o.Bounds(i, i.add(this.getSize())) }, - getPixelOrigin: function() { return this._checkIfLoaded(), this._pixelOrigin }, - getPixelWorldBounds: function(t) { return this.options.crs.getProjectedBounds(t === i ? this.getZoom() : t) }, - getPane: function(t) { return "string" == typeof t ? this._panes[t] : t }, - getPanes: function() { return this._panes }, - getContainer: function() { return this._container }, - getZoomScale: function(t, e) { var n = this.options.crs; return e = e === i ? this._zoom : e, n.scale(t) / n.scale(e) }, - getScaleZoom: function(t, e) { var n = this.options.crs; return e = e === i ? this._zoom : e, n.zoom(t * n.scale(e)) }, - project: function(t, e) { return e = e === i ? this._zoom : e, this.options.crs.latLngToPoint(o.latLng(t), e) }, - unproject: function(t, e) { return e = e === i ? this._zoom : e, this.options.crs.pointToLatLng(o.point(t), e) }, - layerPointToLatLng: function(t) { var e = o.point(t).add(this.getPixelOrigin()); return this.unproject(e) }, - latLngToLayerPoint: function(t) { var e = this.project(o.latLng(t))._round(); return e._subtract(this.getPixelOrigin()) }, - wrapLatLng: function(t) { return this.options.crs.wrapLatLng(o.latLng(t)) }, - distance: function(t, e) { return this.options.crs.distance(o.latLng(t), o.latLng(e)) }, - containerPointToLayerPoint: function(t) { return o.point(t).subtract(this._getMapPanePos()) }, - layerPointToContainerPoint: function(t) { return o.point(t).add(this._getMapPanePos()) }, - containerPointToLatLng: function(t) { var e = this.containerPointToLayerPoint(o.point(t)); return this.layerPointToLatLng(e) }, - latLngToContainerPoint: function(t) { return this.layerPointToContainerPoint(this.latLngToLayerPoint(o.latLng(t))) }, - mouseEventToContainerPoint: function(t) { return o.DomEvent.getMousePosition(t, this._container) }, - mouseEventToLayerPoint: function(t) { return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(t)) }, - mouseEventToLatLng: function(t) { return this.layerPointToLatLng(this.mouseEventToLayerPoint(t)) }, - _initContainer: function(t) { var e = this._container = o.DomUtil.get(t); if (!e) throw new Error("Map container not found."); if (e._leaflet) throw new Error("Map container is already initialized."); - o.DomEvent.addListener(e, "scroll", this._onScroll, this), e._leaflet = !0 }, - _initLayout: function() { var t = this._container; - this._fadeAnimated = this.options.fadeAnimation && o.Browser.any3d, o.DomUtil.addClass(t, "leaflet-container" + (o.Browser.touch ? " leaflet-touch" : "") + (o.Browser.retina ? " leaflet-retina" : "") + (o.Browser.ielt9 ? " leaflet-oldie" : "") + (o.Browser.safari ? " leaflet-safari" : "") + (this._fadeAnimated ? " leaflet-fade-anim" : "")); var e = o.DomUtil.getStyle(t, "position"); "absolute" !== e && "relative" !== e && "fixed" !== e && (t.style.position = "relative"), this._initPanes(), this._initControlPos && this._initControlPos() }, - _initPanes: function() { var t = this._panes = {}; - this._paneRenderers = {}, this._mapPane = this.createPane("mapPane", this._container), o.DomUtil.setPosition(this._mapPane, new o.Point(0, 0)), this.createPane("tilePane"), this.createPane("shadowPane"), this.createPane("overlayPane"), this.createPane("markerPane"), this.createPane("popupPane"), this.options.markerZoomAnimation || (o.DomUtil.addClass(t.markerPane, "leaflet-zoom-hide"), o.DomUtil.addClass(t.shadowPane, "leaflet-zoom-hide")) }, - _resetView: function(t, e) { o.DomUtil.setPosition(this._mapPane, new o.Point(0, 0)); var i = !this._loaded; - this._loaded = !0, e = this._limitZoom(e); var n = this._zoom !== e; - this._moveStart(n)._move(t, e)._moveEnd(n), this.fire("viewreset"), i && this.fire("load") }, - _moveStart: function(t) { return t && this.fire("zoomstart"), this.fire("movestart") }, - _move: function(t, e, n) { e === i && (e = this._zoom); var o = this._zoom !== e; return this._zoom = e, this._lastCenter = t, this._pixelOrigin = this._getNewPixelOrigin(t), o && this.fire("zoom", n), this.fire("move", n) }, - _moveEnd: function(t) { return t && this.fire("zoomend"), this.fire("moveend") }, - _rawPanBy: function(t) { o.DomUtil.setPosition(this._mapPane, this._getMapPanePos().subtract(t)) }, - _getZoomSpan: function() { return this.getMaxZoom() - this.getMinZoom() }, - _panInsideMaxBounds: function() { this._enforcingBounds || this.panInsideBounds(this.options.maxBounds) }, - _checkIfLoaded: function() { if (!this._loaded) throw new Error("Set map center and zoom first.") }, - _initEvents: function(e) { if (o.DomEvent) { this._targets = {}, this._targets[o.stamp(this._container)] = this; var i = e ? "off" : "on"; - o.DomEvent[i](this._container, "click dblclick mousedown mouseup mouseover mouseout mousemove contextmenu keypress", this._handleDOMEvent, this), this.options.trackResize && o.DomEvent[i](t, "resize", this._onResize, this), o.Browser.any3d && this.options.transform3DLimit && this[i]("moveend", this._onMoveEnd) } }, - _onResize: function() { o.Util.cancelAnimFrame(this._resizeRequest), this._resizeRequest = o.Util.requestAnimFrame(function() { this.invalidateSize({ debounceMoveend: !0 }) }, this) }, - _onScroll: function() { this._container.scrollTop = 0, this._container.scrollLeft = 0 }, - _onMoveEnd: function() { var t = this._getMapPanePos(); - Math.max(Math.abs(t.x), Math.abs(t.y)) >= this.options.transform3DLimit && this._resetView(this.getCenter(), this.getZoom()) }, - _findEventTargets: function(t, e) { for (var i, n = [], s = "mouseout" === e || "mouseover" === e, r = t.target || t.srcElement; r;) { if (i = this._targets[o.stamp(r)], i && i.listens(e, !0)) { if (s && !o.DomEvent._isExternalTarget(r, t)) break; if (n.push(i), s) break } if (r === this._container) break; - r = r.parentNode } return n.length || s || !o.DomEvent._isExternalTarget(r, t) || (n = [this]), n }, - _handleDOMEvent: function(t) { if (this._loaded && !o.DomEvent._skipped(t)) { var e = "keypress" === t.type && 13 === t.keyCode ? "click" : t.type; if ("click" === t.type) { var i = o.Util.extend({}, t); - i.type = "preclick", this._handleDOMEvent(i) } "mousedown" === e && o.DomUtil.preventOutline(t.target || t.srcElement), this._fireDOMEvent(t, e) } }, - _fireDOMEvent: function(t, e, i) { if (!t._stopped && (i = (i || []).concat(this._findEventTargets(t, e)), i.length)) { var n = i[0]; if ("contextmenu" === e && n.listens(e, !0) && o.DomEvent.preventDefault(t), "click" !== t.type && "preclick" !== t.type || t._simulated || !this._draggableMoved(n)) { var s = { originalEvent: t }; if ("keypress" !== t.type) { var r = n instanceof o.Marker; - s.containerPoint = r ? this.latLngToContainerPoint(n.getLatLng()) : this.mouseEventToContainerPoint(t), s.layerPoint = this.containerPointToLayerPoint(s.containerPoint), s.latlng = r ? n.getLatLng() : this.layerPointToLatLng(s.layerPoint) } for (var a = 0; a < i.length; a++) - if (i[a].fire(e, s, !0), s.originalEvent._stopped || i[a].options.nonBubblingEvents && -1 !== o.Util.indexOf(i[a].options.nonBubblingEvents, e)) return } } }, - _draggableMoved: function(t) { return t = t.options.draggable ? t : this, t.dragging && t.dragging.moved() || this.boxZoom && this.boxZoom.moved() }, - _clearHandlers: function() { for (var t = 0, e = this._handlers.length; e > t; t++) this._handlers[t].disable() }, - whenReady: function(t, e) { return this._loaded ? t.call(e || this, { target: this }) : this.on("load", t, e), this }, - _getMapPanePos: function() { return o.DomUtil.getPosition(this._mapPane) || new o.Point(0, 0) }, - _moved: function() { var t = this._getMapPanePos(); return t && !t.equals([0, 0]) }, - _getTopLeftPoint: function(t, e) { var n = t && e !== i ? this._getNewPixelOrigin(t, e) : this.getPixelOrigin(); return n.subtract(this._getMapPanePos()) }, - _getNewPixelOrigin: function(t, e) { var i = this.getSize()._divideBy(2); return this.project(t, e)._subtract(i)._add(this._getMapPanePos())._round() }, - _latLngToNewLayerPoint: function(t, e, i) { - var n = this._getNewPixelOrigin(i, e); - return this.project(t, e)._subtract(n) - }, - _getCenterLayerPoint: function() { return this.containerPointToLayerPoint(this.getSize()._divideBy(2)) }, - _getCenterOffset: function(t) { return this.latLngToLayerPoint(t).subtract(this._getCenterLayerPoint()) }, - _limitCenter: function(t, e, i) { if (!i) return t; var n = this.project(t, e), - s = this.getSize().divideBy(2), - r = new o.Bounds(n.subtract(s), n.add(s)), - a = this._getBoundsOffset(r, i, e); return this.unproject(n.add(a), e) }, - _limitOffset: function(t, e) { if (!e) return t; var i = this.getPixelBounds(), - n = new o.Bounds(i.min.add(t), i.max.add(t)); return t.add(this._getBoundsOffset(n, e)) }, - _getBoundsOffset: function(t, e, i) { var n = this.project(e.getNorthWest(), i).subtract(t.min), - s = this.project(e.getSouthEast(), i).subtract(t.max), - r = this._rebound(n.x, -s.x), - a = this._rebound(n.y, -s.y); return new o.Point(r, a) }, - _rebound: function(t, e) { return t + e > 0 ? Math.round(t - e) / 2 : Math.max(0, Math.ceil(t)) - Math.max(0, Math.floor(e)) }, - _limitZoom: function(t) { var e = this.getMinZoom(), - i = this.getMaxZoom(); return o.Browser.any3d || (t = Math.round(t)), Math.max(e, Math.min(i, t)) } - }), o.map = function(t, e) { return new o.Map(t, e) }, o.Layer = o.Evented.extend({ options: { pane: "overlayPane", nonBubblingEvents: [] }, addTo: function(t) { return t.addLayer(this), this }, remove: function() { return this.removeFrom(this._map || this._mapToAdd) }, removeFrom: function(t) { return t && t.removeLayer(this), this }, getPane: function(t) { return this._map.getPane(t ? this.options[t] || t : this.options.pane) }, addInteractiveTarget: function(t) { return this._map._targets[o.stamp(t)] = this, this }, removeInteractiveTarget: function(t) { return delete this._map._targets[o.stamp(t)], this }, _layerAdd: function(t) { var e = t.target; - e.hasLayer(this) && (this._map = e, this._zoomAnimated = e._zoomAnimated, this.getEvents && e.on(this.getEvents(), this), this.onAdd(e), this.getAttribution && this._map.attributionControl && this._map.attributionControl.addAttribution(this.getAttribution()), this.fire("add"), e.fire("layeradd", { layer: this })) } }), o.Map.include({ addLayer: function(t) { var e = o.stamp(t); return this._layers[e] ? t : (this._layers[e] = t, t._mapToAdd = this, t.beforeAdd && t.beforeAdd(this), this.whenReady(t._layerAdd, t), this) }, removeLayer: function(t) { var e = o.stamp(t); return this._layers[e] ? (this._loaded && t.onRemove(this), t.getAttribution && this.attributionControl && this.attributionControl.removeAttribution(t.getAttribution()), t.getEvents && this.off(t.getEvents(), t), delete this._layers[e], this._loaded && (this.fire("layerremove", { layer: t }), t.fire("remove")), t._map = t._mapToAdd = null, this) : this }, hasLayer: function(t) { return !!t && o.stamp(t) in this._layers }, eachLayer: function(t, e) { for (var i in this._layers) t.call(e, this._layers[i]); return this }, _addLayers: function(t) { t = t ? o.Util.isArray(t) ? t : [t] : []; for (var e = 0, i = t.length; i > e; e++) this.addLayer(t[e]) }, _addZoomLimit: function(t) { - (isNaN(t.options.maxZoom) || !isNaN(t.options.minZoom)) && (this._zoomBoundLayers[o.stamp(t)] = t, this._updateZoomLevels()) }, _removeZoomLimit: function(t) { var e = o.stamp(t); - this._zoomBoundLayers[e] && (delete this._zoomBoundLayers[e], this._updateZoomLevels()) }, _updateZoomLevels: function() { var t = 1 / 0, - e = -(1 / 0), - n = this._getZoomSpan(); for (var o in this._zoomBoundLayers) { var s = this._zoomBoundLayers[o].options; - t = s.minZoom === i ? t : Math.min(t, s.minZoom), e = s.maxZoom === i ? e : Math.max(e, s.maxZoom) } - this._layersMaxZoom = e === -(1 / 0) ? i : e, this._layersMinZoom = t === 1 / 0 ? i : t, n !== this._getZoomSpan() && this.fire("zoomlevelschange") } }), o.Projection.Mercator = { R: 6378137, R_MINOR: 6356752.314245179, bounds: o.bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]), project: function(t) { var e = Math.PI / 180, - i = this.R, - n = t.lat * e, - s = this.R_MINOR / i, - r = Math.sqrt(1 - s * s), - a = r * Math.sin(n), - h = Math.tan(Math.PI / 4 - n / 2) / Math.pow((1 - a) / (1 + a), r / 2); return n = -i * Math.log(Math.max(h, 1e-10)), new o.Point(t.lng * e * i, n) }, unproject: function(t) { for (var e, i = 180 / Math.PI, n = this.R, s = this.R_MINOR / n, r = Math.sqrt(1 - s * s), a = Math.exp(-t.y / n), h = Math.PI / 2 - 2 * Math.atan(a), l = 0, u = .1; 15 > l && Math.abs(u) > 1e-7; l++) e = r * Math.sin(h), e = Math.pow((1 - e) / (1 + e), r / 2), u = Math.PI / 2 - 2 * Math.atan(a * e) - h, h += u; return new o.LatLng(h * i, t.x * i / n) } }, o.CRS.EPSG3395 = o.extend({}, o.CRS.Earth, { code: "EPSG:3395", projection: o.Projection.Mercator, transformation: function() { var t = .5 / (Math.PI * o.Projection.Mercator.R); return new o.Transformation(t, .5, -t, .5) }() }), o.GridLayer = o.Layer.extend({ options: { pane: "tilePane", tileSize: 256, opacity: 1, zIndex: 1, updateWhenIdle: o.Browser.mobile, updateInterval: 200, attribution: null, bounds: null, minZoom: 0 }, initialize: function(t) { t = o.setOptions(this, t) }, onAdd: function() { this._initContainer(), this._levels = {}, this._tiles = {}, this._resetView(), this._update() }, beforeAdd: function(t) { t._addZoomLimit(this) }, onRemove: function(t) { o.DomUtil.remove(this._container), t._removeZoomLimit(this), this._container = null, this._tileZoom = null }, bringToFront: function() { return this._map && (o.DomUtil.toFront(this._container), this._setAutoZIndex(Math.max)), this }, bringToBack: function() { return this._map && (o.DomUtil.toBack(this._container), this._setAutoZIndex(Math.min)), this }, getAttribution: function() { return this.options.attribution }, getContainer: function() { return this._container }, setOpacity: function(t) { return this.options.opacity = t, this._updateOpacity(), this }, setZIndex: function(t) { return this.options.zIndex = t, this._updateZIndex(), this }, isLoading: function() { return this._loading }, redraw: function() { return this._map && (this._removeAllTiles(), this._update()), this }, getEvents: function() { var t = { viewreset: this._resetAll, zoom: this._resetView, moveend: this._onMoveEnd }; return this.options.updateWhenIdle || (this._onMove || (this._onMove = o.Util.throttle(this._onMoveEnd, this.options.updateInterval, this)), t.move = this._onMove), this._zoomAnimated && (t.zoomanim = this._animateZoom), t }, createTile: function() { return e.createElement("div") }, getTileSize: function() { var t = this.options.tileSize; return t instanceof o.Point ? t : new o.Point(t, t) }, _updateZIndex: function() { this._container && this.options.zIndex !== i && null !== this.options.zIndex && (this._container.style.zIndex = this.options.zIndex) }, _setAutoZIndex: function(t) { for (var e, i = this.getPane().children, n = -t(-(1 / 0), 1 / 0), o = 0, s = i.length; s > o; o++) e = i[o].style.zIndex, i[o] !== this._container && e && (n = t(n, +e)); - isFinite(n) && (this.options.zIndex = n + t(-1, 1), this._updateZIndex()) }, _updateOpacity: function() { if (this._map && !o.Browser.ielt9 && this._map._fadeAnimated) { o.DomUtil.setOpacity(this._container, this.options.opacity); var t = +new Date, - e = !1, - i = !1; for (var n in this._tiles) { var s = this._tiles[n]; if (s.current && s.loaded) { var r = Math.min(1, (t - s.loaded) / 200); - o.DomUtil.setOpacity(s.el, r), 1 > r ? e = !0 : (s.active && (i = !0), s.active = !0) } } - i && !this._noPrune && this._pruneTiles(), e && (o.Util.cancelAnimFrame(this._fadeFrame), this._fadeFrame = o.Util.requestAnimFrame(this._updateOpacity, this)) } }, _initContainer: function() { this._container || (this._container = o.DomUtil.create("div", "leaflet-layer"), this._updateZIndex(), this.options.opacity < 1 && this._updateOpacity(), this.getPane().appendChild(this._container)) }, _updateLevels: function() { var t = this._tileZoom, - e = this.options.maxZoom; for (var i in this._levels) this._levels[i].el.children.length || i === t ? this._levels[i].el.style.zIndex = e - Math.abs(t - i) : (o.DomUtil.remove(this._levels[i].el), delete this._levels[i]); var n = this._levels[t], - s = this._map; return n || (n = this._levels[t] = {}, n.el = o.DomUtil.create("div", "leaflet-tile-container leaflet-zoom-animated", this._container), n.el.style.zIndex = e, n.origin = s.project(s.unproject(s.getPixelOrigin()), t).round(), n.zoom = t, this._setZoomTransform(n, s.getCenter(), s.getZoom()), o.Util.falseFn(n.el.offsetWidth)), this._level = n, n }, _pruneTiles: function() { var t, e, i = this._map.getZoom(); if (i > this.options.maxZoom || i < this.options.minZoom) return this._removeAllTiles(); for (t in this._tiles) e = this._tiles[t], e.retain = e.current; for (t in this._tiles) - if (e = this._tiles[t], e.current && !e.active) { var n = e.coords; - this._retainParent(n.x, n.y, n.z, n.z - 5) || this._retainChildren(n.x, n.y, n.z, n.z + 2) } - for (t in this._tiles) this._tiles[t].retain || this._removeTile(t) }, _removeAllTiles: function() { for (var t in this._tiles) this._removeTile(t) }, _resetAll: function() { for (var t in this._levels) o.DomUtil.remove(this._levels[t].el), delete this._levels[t]; - this._removeAllTiles(), this._tileZoom = null, this._resetView() }, _retainParent: function(t, e, i, n) { var o = Math.floor(t / 2), - s = Math.floor(e / 2), - r = i - 1, - a = o + ":" + s + ":" + r, - h = this._tiles[a]; return h && h.active ? (h.retain = !0, !0) : (h && h.loaded && (h.retain = !0), r > n ? this._retainParent(o, s, r, n) : !1) }, _retainChildren: function(t, e, i, n) { for (var o = 2 * t; 2 * t + 2 > o; o++) - for (var s = 2 * e; 2 * e + 2 > s; s++) { var r = o + ":" + s + ":" + (i + 1), - a = this._tiles[r]; - a && a.active ? a.retain = !0 : (a && a.loaded && (a.retain = !0), n > i + 1 && this._retainChildren(o, s, i + 1, n)) } }, _resetView: function(t) { var e = t && (t.pinch || t.flyTo); - this._setView(this._map.getCenter(), this._map.getZoom(), e, e) }, _animateZoom: function(t) { this._setView(t.center, t.zoom, !0, t.noUpdate) }, _setView: function(t, e, n, o) { var s = Math.round(e); - (this.options.maxZoom !== i && s > this.options.maxZoom || this.options.minZoom !== i && s < this.options.minZoom) && (s = i); var r = s !== this._tileZoom; - (!o || r) && (this._tileZoom = s, this._abortLoading && this._abortLoading(), this._updateLevels(), this._resetGrid(), s !== i && this._update(t), n || this._pruneTiles(), this._noPrune = !!n), this._setZoomTransforms(t, e) }, _setZoomTransforms: function(t, e) { for (var i in this._levels) this._setZoomTransform(this._levels[i], t, e) }, _setZoomTransform: function(t, e, i) { var n = this._map.getZoomScale(i, t.zoom), - s = t.origin.multiplyBy(n).subtract(this._map._getNewPixelOrigin(e, i)).round(); - o.Browser.any3d ? o.DomUtil.setTransform(t.el, s, n) : o.DomUtil.setPosition(t.el, s) }, _resetGrid: function() { var t = this._map, - e = t.options.crs, - i = this._tileSize = this.getTileSize(), - n = this._tileZoom, - o = this._map.getPixelWorldBounds(this._tileZoom); - o && (this._globalTileRange = this._pxBoundsToTileRange(o)), this._wrapX = e.wrapLng && !this.options.noWrap && [Math.floor(t.project([0, e.wrapLng[0]], n).x / i.x), Math.ceil(t.project([0, e.wrapLng[1]], n).x / i.y)], this._wrapY = e.wrapLat && !this.options.noWrap && [Math.floor(t.project([e.wrapLat[0], 0], n).y / i.x), Math.ceil(t.project([e.wrapLat[1], 0], n).y / i.y)] }, _onMoveEnd: function() { this._map && !this._map._animatingZoom && this._resetView() }, _getTiledPixelBounds: function(t, e, i) { var n = this._map, - s = n.getZoomScale(e, i), - r = n.project(t, i).floor(), - a = n.getSize().divideBy(2 * s); return new o.Bounds(r.subtract(a), r.add(a)) }, _update: function(t) { var n = this._map; if (n) { var s = n.getZoom(); if (t === i && (t = n.getCenter()), this._tileZoom !== i) { var r = this._getTiledPixelBounds(t, s, this._tileZoom), - a = this._pxBoundsToTileRange(r), - h = a.getCenter(), - l = []; for (var u in this._tiles) this._tiles[u].current = !1; if (Math.abs(s - this._tileZoom) > 1) return void this._setView(t, s); for (var c = a.min.y; c <= a.max.y; c++) - for (var d = a.min.x; d <= a.max.x; d++) { var _ = new o.Point(d, c); if (_.z = this._tileZoom, this._isValidTile(_)) { var m = this._tiles[this._tileCoordsToKey(_)]; - m ? m.current = !0 : l.push(_) } } - if (l.sort(function(t, e) { return t.distanceTo(h) - e.distanceTo(h) }), 0 !== l.length) { this._loading || (this._loading = !0, this.fire("loading")); var p = e.createDocumentFragment(); for (d = 0; d < l.length; d++) this._addTile(l[d], p); - this._level.el.appendChild(p) } } } }, _isValidTile: function(t) { var e = this._map.options.crs; if (!e.infinite) { var i = this._globalTileRange; if (!e.wrapLng && (t.x < i.min.x || t.x > i.max.x) || !e.wrapLat && (t.y < i.min.y || t.y > i.max.y)) return !1 } if (!this.options.bounds) return !0; var n = this._tileCoordsToBounds(t); return o.latLngBounds(this.options.bounds).overlaps(n) }, _keyToBounds: function(t) { return this._tileCoordsToBounds(this._keyToTileCoords(t)) }, _tileCoordsToBounds: function(t) { var e = this._map, - i = this.getTileSize(), - n = t.scaleBy(i), - s = n.add(i), - r = e.wrapLatLng(e.unproject(n, t.z)), - a = e.wrapLatLng(e.unproject(s, t.z)); return new o.LatLngBounds(r, a) }, _tileCoordsToKey: function(t) { return t.x + ":" + t.y + ":" + t.z }, _keyToTileCoords: function(t) { var e = t.split(":"), - i = new o.Point(+e[0], +e[1]); return i.z = +e[2], i }, _removeTile: function(t) { var e = this._tiles[t]; - e && (o.DomUtil.remove(e.el), delete this._tiles[t], this.fire("tileunload", { tile: e.el, coords: this._keyToTileCoords(t) })) }, _initTile: function(t) { o.DomUtil.addClass(t, "leaflet-tile"); var e = this.getTileSize(); - t.style.width = e.x + "px", t.style.height = e.y + "px", t.onselectstart = o.Util.falseFn, t.onmousemove = o.Util.falseFn, o.Browser.ielt9 && this.options.opacity < 1 && o.DomUtil.setOpacity(t, this.options.opacity), o.Browser.android && !o.Browser.android23 && (t.style.WebkitBackfaceVisibility = "hidden") }, _addTile: function(t, e) { var i = this._getTilePos(t), - n = this._tileCoordsToKey(t), - s = this.createTile(this._wrapCoords(t), o.bind(this._tileReady, this, t)); - this._initTile(s), this.createTile.length < 2 && o.Util.requestAnimFrame(o.bind(this._tileReady, this, t, null, s)), o.DomUtil.setPosition(s, i), this._tiles[n] = { el: s, coords: t, current: !0 }, e.appendChild(s), this.fire("tileloadstart", { tile: s, coords: t }) }, _tileReady: function(t, e, i) { if (this._map) { e && this.fire("tileerror", { error: e, tile: i, coords: t }); var n = this._tileCoordsToKey(t); - i = this._tiles[n], i && (i.loaded = +new Date, this._map._fadeAnimated ? (o.DomUtil.setOpacity(i.el, 0), o.Util.cancelAnimFrame(this._fadeFrame), this._fadeFrame = o.Util.requestAnimFrame(this._updateOpacity, this)) : (i.active = !0, this._pruneTiles()), o.DomUtil.addClass(i.el, "leaflet-tile-loaded"), this.fire("tileload", { tile: i.el, coords: t }), this._noTilesToLoad() && (this._loading = !1, this.fire("load"))) } }, _getTilePos: function(t) { return t.scaleBy(this.getTileSize()).subtract(this._level.origin) }, _wrapCoords: function(t) { var e = new o.Point(this._wrapX ? o.Util.wrapNum(t.x, this._wrapX) : t.x, this._wrapY ? o.Util.wrapNum(t.y, this._wrapY) : t.y); return e.z = t.z, e }, _pxBoundsToTileRange: function(t) { var e = this.getTileSize(); return new o.Bounds(t.min.unscaleBy(e).floor(), t.max.unscaleBy(e).ceil().subtract([1, 1])) }, _noTilesToLoad: function() { for (var t in this._tiles) - if (!this._tiles[t].loaded) return !1; - return !0 } }), o.gridLayer = function(t) { return new o.GridLayer(t) }, o.TileLayer = o.GridLayer.extend({ options: { maxZoom: 18, subdomains: "abc", errorTileUrl: "", zoomOffset: 0, maxNativeZoom: null, tms: !1, zoomReverse: !1, detectRetina: !1, crossOrigin: !1 }, initialize: function(t, e) { this._url = t, e = o.setOptions(this, e), e.detectRetina && o.Browser.retina && e.maxZoom > 0 && (e.tileSize = Math.floor(e.tileSize / 2), e.zoomOffset++, e.minZoom = Math.max(0, e.minZoom), e.maxZoom--), "string" == typeof e.subdomains && (e.subdomains = e.subdomains.split("")), o.Browser.android || this.on("tileunload", this._onTileRemove) }, setUrl: function(t, e) { return this._url = t, e || this.redraw(), this }, createTile: function(t, i) { var n = e.createElement("img"); return o.DomEvent.on(n, "load", o.bind(this._tileOnLoad, this, i, n)), o.DomEvent.on(n, "error", o.bind(this._tileOnError, this, i, n)), this.options.crossOrigin && (n.crossOrigin = ""), n.alt = "", n.src = this.getTileUrl(t), n }, getTileUrl: function(t) { return o.Util.template(this._url, o.extend({ r: this.options.detectRetina && o.Browser.retina && this.options.maxZoom > 0 ? "@2x" : "", s: this._getSubdomain(t), x: t.x, y: this.options.tms ? this._globalTileRange.max.y - t.y : t.y, z: this._getZoomForUrl() }, this.options)) }, _tileOnLoad: function(t, e) { o.Browser.ielt9 ? setTimeout(o.bind(t, this, null, e), 0) : t(null, e) }, _tileOnError: function(t, e, i) { var n = this.options.errorTileUrl; - n && (e.src = n), t(i, e) }, getTileSize: function() { var t = this._map, - e = o.GridLayer.prototype.getTileSize.call(this), - i = this._tileZoom + this.options.zoomOffset, - n = this.options.maxNativeZoom; return null !== n && i > n ? e.divideBy(t.getZoomScale(n, i)).round() : e }, _onTileRemove: function(t) { t.tile.onload = null }, _getZoomForUrl: function() { var t = this.options, - e = this._tileZoom; return t.zoomReverse && (e = t.maxZoom - e), e += t.zoomOffset, null !== t.maxNativeZoom ? Math.min(e, t.maxNativeZoom) : e }, _getSubdomain: function(t) { var e = Math.abs(t.x + t.y) % this.options.subdomains.length; return this.options.subdomains[e] }, _abortLoading: function() { var t, e; for (t in this._tiles) this._tiles[t].coords.z !== this._tileZoom && (e = this._tiles[t].el, e.onload = o.Util.falseFn, e.onerror = o.Util.falseFn, e.complete || (e.src = o.Util.emptyImageUrl, o.DomUtil.remove(e))) } }), o.tileLayer = function(t, e) { return new o.TileLayer(t, e) }, o.TileLayer.WMS = o.TileLayer.extend({ defaultWmsParams: { service: "WMS", request: "GetMap", version: "1.1.1", layers: "", styles: "", format: "image/jpeg", transparent: !1 }, options: { crs: null, uppercase: !1 }, initialize: function(t, e) { this._url = t; var i = o.extend({}, this.defaultWmsParams); for (var n in e) n in this.options || (i[n] = e[n]); - e = o.setOptions(this, e), i.width = i.height = e.tileSize * (e.detectRetina && o.Browser.retina ? 2 : 1), this.wmsParams = i }, onAdd: function(t) { this._crs = this.options.crs || t.options.crs, this._wmsVersion = parseFloat(this.wmsParams.version); var e = this._wmsVersion >= 1.3 ? "crs" : "srs"; - this.wmsParams[e] = this._crs.code, o.TileLayer.prototype.onAdd.call(this, t) }, getTileUrl: function(t) { var e = this._tileCoordsToBounds(t), - i = this._crs.project(e.getNorthWest()), - n = this._crs.project(e.getSouthEast()), - s = (this._wmsVersion >= 1.3 && this._crs === o.CRS.EPSG4326 ? [n.y, i.x, i.y, n.x] : [i.x, n.y, n.x, i.y]).join(","), - r = o.TileLayer.prototype.getTileUrl.call(this, t); return r + o.Util.getParamString(this.wmsParams, r, this.options.uppercase) + (this.options.uppercase ? "&BBOX=" : "&bbox=") + s }, setParams: function(t, e) { return o.extend(this.wmsParams, t), e || this.redraw(), this } }), o.tileLayer.wms = function(t, e) { return new o.TileLayer.WMS(t, e) }, o.ImageOverlay = o.Layer.extend({ options: { opacity: 1, alt: "", interactive: !1 }, initialize: function(t, e, i) { this._url = t, this._bounds = o.latLngBounds(e), o.setOptions(this, i) }, onAdd: function() { this._image || (this._initImage(), this.options.opacity < 1 && this._updateOpacity()), this.options.interactive && (o.DomUtil.addClass(this._image, "leaflet-interactive"), this.addInteractiveTarget(this._image)), this.getPane().appendChild(this._image), this._reset() }, onRemove: function() { o.DomUtil.remove(this._image), this.options.interactive && this.removeInteractiveTarget(this._image) }, setOpacity: function(t) { return this.options.opacity = t, this._image && this._updateOpacity(), this }, setStyle: function(t) { return t.opacity && this.setOpacity(t.opacity), this }, bringToFront: function() { return this._map && o.DomUtil.toFront(this._image), this }, bringToBack: function() { return this._map && o.DomUtil.toBack(this._image), this }, setUrl: function(t) { return this._url = t, this._image && (this._image.src = t), this }, setBounds: function(t) { return this._bounds = t, this._map && this._reset(), this }, getAttribution: function() { return this.options.attribution }, getEvents: function() { var t = { zoom: this._reset, viewreset: this._reset }; return this._zoomAnimated && (t.zoomanim = this._animateZoom), t }, getBounds: function() { return this._bounds }, getElement: function() { return this._image }, _initImage: function() { var t = this._image = o.DomUtil.create("img", "leaflet-image-layer " + (this._zoomAnimated ? "leaflet-zoom-animated" : "")); - t.onselectstart = o.Util.falseFn, t.onmousemove = o.Util.falseFn, t.onload = o.bind(this.fire, this, "load"), this.options.crossOrigin && (t.crossOrigin = ""), t.src = this._url, t.alt = this.options.alt }, _animateZoom: function(t) { var e = this._map.getZoomScale(t.zoom), - i = this._map._latLngToNewLayerPoint(this._bounds.getNorthWest(), t.zoom, t.center); - o.DomUtil.setTransform(this._image, i, e) }, _reset: function() { var t = this._image, - e = new o.Bounds(this._map.latLngToLayerPoint(this._bounds.getNorthWest()), this._map.latLngToLayerPoint(this._bounds.getSouthEast())), - i = e.getSize(); - o.DomUtil.setPosition(t, e.min), t.style.width = i.x + "px", t.style.height = i.y + "px" }, _updateOpacity: function() { o.DomUtil.setOpacity(this._image, this.options.opacity) } }), o.imageOverlay = function(t, e, i) { return new o.ImageOverlay(t, e, i) }, o.Icon = o.Class.extend({ initialize: function(t) { o.setOptions(this, t) }, createIcon: function(t) { return this._createIcon("icon", t) }, createShadow: function(t) { return this._createIcon("shadow", t) }, _createIcon: function(t, e) { var i = this._getIconUrl(t); if (!i) { if ("icon" === t) throw new Error("iconUrl not set in Icon options (see the docs)."); return null } var n = this._createImg(i, e && "IMG" === e.tagName ? e : null); return this._setIconStyles(n, t), n }, _setIconStyles: function(t, e) { var i = this.options, - n = o.point(i[e + "Size"]), - s = o.point("shadow" === e && i.shadowAnchor || i.iconAnchor || n && n.divideBy(2, !0)); - t.className = "leaflet-marker-" + e + " " + (i.className || ""), s && (t.style.marginLeft = -s.x + "px", t.style.marginTop = -s.y + "px"), n && (t.style.width = n.x + "px", t.style.height = n.y + "px") }, _createImg: function(t, i) { return i = i || e.createElement("img"), i.src = t, i }, _getIconUrl: function(t) { return o.Browser.retina && this.options[t + "RetinaUrl"] || this.options[t + "Url"] } }), o.icon = function(t) { return new o.Icon(t) }, o.Icon.Default = o.Icon.extend({ options: { iconSize: [25, 41], iconAnchor: [12, 41], popupAnchor: [1, -34], shadowSize: [41, 41] }, _getIconUrl: function(t) { var e = t + "Url"; if (this.options[e]) return this.options[e]; var i = o.Icon.Default.imagePath; if (!i) throw new Error("Couldn't autodetect L.Icon.Default.imagePath, set it manually."); return i + "/marker-" + t + (o.Browser.retina && "icon" === t ? "-2x" : "") + ".png" } }), o.Icon.Default.imagePath = function() { var t, i, n, o, s = e.getElementsByTagName("script"), - r = /[\/^]leaflet[\-\._]?([\w\-\._]*)\.js\??/; for (t = 0, i = s.length; i > t; t++) - if (n = s[t].src || "", n.match(r)) return o = n.split(r)[0], (o ? o + "/" : "") + "images" }(), o.Marker = o.Layer.extend({ options: { pane: "markerPane", nonBubblingEvents: ["click", "dblclick", "mouseover", "mouseout", "contextmenu"], icon: new o.Icon.Default, interactive: !0, keyboard: !0, zIndexOffset: 0, opacity: 1, riseOffset: 250 }, initialize: function(t, e) { o.setOptions(this, e), this._latlng = o.latLng(t) }, onAdd: function(t) { this._zoomAnimated = this._zoomAnimated && t.options.markerZoomAnimation, this._initIcon(), this.update() }, onRemove: function() { this.dragging && this.dragging.enabled() && (this.options.draggable = !0, this.dragging.removeHooks()), this._removeIcon(), this._removeShadow() }, getEvents: function() { var t = { zoom: this.update, viewreset: this.update }; return this._zoomAnimated && (t.zoomanim = this._animateZoom), t }, getLatLng: function() { return this._latlng }, setLatLng: function(t) { var e = this._latlng; return this._latlng = o.latLng(t), this.update(), this.fire("move", { oldLatLng: e, latlng: this._latlng }) }, setZIndexOffset: function(t) { return this.options.zIndexOffset = t, this.update() }, setIcon: function(t) { return this.options.icon = t, this._map && (this._initIcon(), this.update()), this._popup && this.bindPopup(this._popup, this._popup.options), this }, getElement: function() { return this._icon }, update: function() { if (this._icon) { var t = this._map.latLngToLayerPoint(this._latlng).round(); - this._setPos(t) } return this }, _initIcon: function() { var t = this.options, - e = "leaflet-zoom-" + (this._zoomAnimated ? "animated" : "hide"), - i = t.icon.createIcon(this._icon), - n = !1; - i !== this._icon && (this._icon && this._removeIcon(), n = !0, t.title && (i.title = t.title), t.alt && (i.alt = t.alt)), o.DomUtil.addClass(i, e), t.keyboard && (i.tabIndex = "0"), this._icon = i, t.riseOnHover && this.on({ mouseover: this._bringToFront, mouseout: this._resetZIndex }); var s = t.icon.createShadow(this._shadow), - r = !1; - s !== this._shadow && (this._removeShadow(), r = !0), s && o.DomUtil.addClass(s, e), this._shadow = s, t.opacity < 1 && this._updateOpacity(), n && (this.getPane().appendChild(this._icon), this._initInteraction()), s && r && this.getPane("shadowPane").appendChild(this._shadow) }, _removeIcon: function() { this.options.riseOnHover && this.off({ mouseover: this._bringToFront, mouseout: this._resetZIndex }), o.DomUtil.remove(this._icon), this.removeInteractiveTarget(this._icon), this._icon = null }, _removeShadow: function() { this._shadow && o.DomUtil.remove(this._shadow), this._shadow = null }, _setPos: function(t) { o.DomUtil.setPosition(this._icon, t), this._shadow && o.DomUtil.setPosition(this._shadow, t), this._zIndex = t.y + this.options.zIndexOffset, this._resetZIndex() }, _updateZIndex: function(t) { this._icon.style.zIndex = this._zIndex + t }, _animateZoom: function(t) { var e = this._map._latLngToNewLayerPoint(this._latlng, t.zoom, t.center).round(); - this._setPos(e) }, _initInteraction: function() { if (this.options.interactive && (o.DomUtil.addClass(this._icon, "leaflet-interactive"), this.addInteractiveTarget(this._icon), o.Handler.MarkerDrag)) { var t = this.options.draggable; - this.dragging && (t = this.dragging.enabled(), this.dragging.disable()), this.dragging = new o.Handler.MarkerDrag(this), t && this.dragging.enable() } }, setOpacity: function(t) { return this.options.opacity = t, this._map && this._updateOpacity(), this }, _updateOpacity: function() { var t = this.options.opacity; - o.DomUtil.setOpacity(this._icon, t), this._shadow && o.DomUtil.setOpacity(this._shadow, t) }, _bringToFront: function() { this._updateZIndex(this.options.riseOffset) }, _resetZIndex: function() { this._updateZIndex(0) } }), o.marker = function(t, e) { return new o.Marker(t, e) }, o.DivIcon = o.Icon.extend({ options: { iconSize: [12, 12], className: "leaflet-div-icon", html: !1 }, createIcon: function(t) { var i = t && "DIV" === t.tagName ? t : e.createElement("div"), - n = this.options; return i.innerHTML = n.html !== !1 ? n.html : "", n.bgPos && (i.style.backgroundPosition = -n.bgPos.x + "px " + -n.bgPos.y + "px"), this._setIconStyles(i, "icon"), i }, createShadow: function() { return null } }), o.divIcon = function(t) { return new o.DivIcon(t) }, o.Map.mergeOptions({ closePopupOnClick: !0 }), o.Popup = o.Layer.extend({ options: { pane: "popupPane", minWidth: 50, maxWidth: 300, offset: [0, 7], autoPan: !0, autoPanPadding: [5, 5], closeButton: !0, autoClose: !0, zoomAnimation: !0 }, initialize: function(t, e) { o.setOptions(this, t), this._source = e }, onAdd: function(t) { this._zoomAnimated = this._zoomAnimated && this.options.zoomAnimation, this._container || this._initLayout(), t._fadeAnimated && o.DomUtil.setOpacity(this._container, 0), clearTimeout(this._removeTimeout), this.getPane().appendChild(this._container), this.update(), t._fadeAnimated && o.DomUtil.setOpacity(this._container, 1), t.fire("popupopen", { popup: this }), this._source && this._source.fire("popupopen", { popup: this }, !0) }, openOn: function(t) { return t.openPopup(this), this }, onRemove: function(t) { t._fadeAnimated ? (o.DomUtil.setOpacity(this._container, 0), this._removeTimeout = setTimeout(o.bind(o.DomUtil.remove, o.DomUtil, this._container), 200)) : o.DomUtil.remove(this._container), t.fire("popupclose", { popup: this }), this._source && this._source.fire("popupclose", { popup: this }, !0) }, getLatLng: function() { return this._latlng }, setLatLng: function(t) { return this._latlng = o.latLng(t), this._map && (this._updatePosition(), this._adjustPan()), this }, getContent: function() { return this._content }, setContent: function(t) { return this._content = t, this.update(), this }, getElement: function() { return this._container }, update: function() { this._map && (this._container.style.visibility = "hidden", this._updateContent(), this._updateLayout(), this._updatePosition(), this._container.style.visibility = "", this._adjustPan()) }, getEvents: function() { var t = { zoom: this._updatePosition, viewreset: this._updatePosition }; return this._zoomAnimated && (t.zoomanim = this._animateZoom), ("closeOnClick" in this.options ? this.options.closeOnClick : this._map.options.closePopupOnClick) && (t.preclick = this._close), this.options.keepInView && (t.moveend = this._adjustPan), t }, isOpen: function() { return !!this._map && this._map.hasLayer(this) }, bringToFront: function() { return this._map && o.DomUtil.toFront(this._container), this }, bringToBack: function() { return this._map && o.DomUtil.toBack(this._container), this }, _close: function() { this._map && this._map.closePopup(this) }, _initLayout: function() { var t = "leaflet-popup", - e = this._container = o.DomUtil.create("div", t + " " + (this.options.className || "") + " leaflet-zoom-" + (this._zoomAnimated ? "animated" : "hide")); if (this.options.closeButton) { var i = this._closeButton = o.DomUtil.create("a", t + "-close-button", e); - i.href = "#close", i.innerHTML = "×", o.DomEvent.on(i, "click", this._onCloseButtonClick, this) } var n = this._wrapper = o.DomUtil.create("div", t + "-content-wrapper", e); - this._contentNode = o.DomUtil.create("div", t + "-content", n), o.DomEvent.disableClickPropagation(n).disableScrollPropagation(this._contentNode).on(n, "contextmenu", o.DomEvent.stopPropagation), this._tipContainer = o.DomUtil.create("div", t + "-tip-container", e), this._tip = o.DomUtil.create("div", t + "-tip", this._tipContainer) }, _updateContent: function() { if (this._content) { var t = this._contentNode, - e = "function" == typeof this._content ? this._content(this._source || this) : this._content; if ("string" == typeof e) t.innerHTML = e; - else { for (; t.hasChildNodes();) t.removeChild(t.firstChild); - t.appendChild(e) } - this.fire("contentupdate") } }, _updateLayout: function() { var t = this._contentNode, - e = t.style; - e.width = "", e.whiteSpace = "nowrap"; var i = t.offsetWidth; - i = Math.min(i, this.options.maxWidth), i = Math.max(i, this.options.minWidth), e.width = i + 1 + "px", e.whiteSpace = "", e.height = ""; var n = t.offsetHeight, - s = this.options.maxHeight, - r = "leaflet-popup-scrolled"; - s && n > s ? (e.height = s + "px", o.DomUtil.addClass(t, r)) : o.DomUtil.removeClass(t, r), this._containerWidth = this._container.offsetWidth }, _updatePosition: function() { if (this._map) { var t = this._map.latLngToLayerPoint(this._latlng), - e = o.point(this.options.offset); - this._zoomAnimated ? o.DomUtil.setPosition(this._container, t) : e = e.add(t); var i = this._containerBottom = -e.y, - n = this._containerLeft = -Math.round(this._containerWidth / 2) + e.x; - this._container.style.bottom = i + "px", this._container.style.left = n + "px" } }, _animateZoom: function(t) { var e = this._map._latLngToNewLayerPoint(this._latlng, t.zoom, t.center); - o.DomUtil.setPosition(this._container, e) }, _adjustPan: function() { if (!(!this.options.autoPan || this._map._panAnim && this._map._panAnim._inProgress)) { var t = this._map, - e = this._container.offsetHeight, - i = this._containerWidth, - n = new o.Point(this._containerLeft, -e - this._containerBottom); - this._zoomAnimated && n._add(o.DomUtil.getPosition(this._container)); var s = t.layerPointToContainerPoint(n), - r = o.point(this.options.autoPanPadding), - a = o.point(this.options.autoPanPaddingTopLeft || r), - h = o.point(this.options.autoPanPaddingBottomRight || r), - l = t.getSize(), - u = 0, - c = 0; - s.x + i + h.x > l.x && (u = s.x + i - l.x + h.x), s.x - u - a.x < 0 && (u = s.x - a.x), s.y + e + h.y > l.y && (c = s.y + e - l.y + h.y), s.y - c - a.y < 0 && (c = s.y - a.y), (u || c) && t.fire("autopanstart").panBy([u, c]) } }, _onCloseButtonClick: function(t) { this._close(), o.DomEvent.stop(t) } }), o.popup = function(t, e) { return new o.Popup(t, e) }, o.Map.include({ openPopup: function(t, e, i) { return t instanceof o.Popup || (t = new o.Popup(i).setContent(t)), e && t.setLatLng(e), this.hasLayer(t) ? this : (this._popup && this._popup.options.autoClose && this.closePopup(), this._popup = t, this.addLayer(t)) }, closePopup: function(t) { return t && t !== this._popup || (t = this._popup, this._popup = null), t && this.removeLayer(t), this } }), o.Layer.include({ bindPopup: function(t, e) { return t instanceof o.Popup ? (o.setOptions(t, e), this._popup = t, t._source = this) : ((!this._popup || e) && (this._popup = new o.Popup(e, this)), this._popup.setContent(t)), this._popupHandlersAdded || (this.on({ click: this._openPopup, remove: this.closePopup, move: this._movePopup }), this._popupHandlersAdded = !0), this._originalPopupOffset = this._popup.options.offset, this }, unbindPopup: function() { return this._popup && (this.off({ click: this._openPopup, remove: this.closePopup, move: this._movePopup }), this._popupHandlersAdded = !1, this._popup = null), this }, openPopup: function(t, e) { if (t instanceof o.Layer || (e = t, t = this), t instanceof o.FeatureGroup) - for (var i in this._layers) { t = this._layers[i]; break } - return e || (e = t.getCenter ? t.getCenter() : t.getLatLng()), this._popup && this._map && (this._popup.options.offset = this._popupAnchor(t), this._popup._source = t, this._popup.update(), this._map.openPopup(this._popup, e)), this }, closePopup: function() { return this._popup && this._popup._close(), this }, togglePopup: function(t) { return this._popup && (this._popup._map ? this.closePopup() : this.openPopup(t)), this }, isPopupOpen: function() { return this._popup.isOpen() }, setPopupContent: function(t) { return this._popup && this._popup.setContent(t), this }, getPopup: function() { return this._popup }, _openPopup: function(t) { var e = t.layer || t.target; if (this._popup && this._map) return e instanceof o.Path ? void this.openPopup(t.layer || t.target, t.latlng) : void(this._map.hasLayer(this._popup) && this._popup._source === e ? this.closePopup() : this.openPopup(e, t.latlng)) }, _popupAnchor: function(t) { var e = t._getPopupAnchor ? t._getPopupAnchor() : [0, 0], - i = this._originalPopupOffset || o.Popup.prototype.options.offset; return o.point(e).add(i) }, _movePopup: function(t) { this._popup.setLatLng(t.latlng) } }), o.Marker.include({ _getPopupAnchor: function() { return this.options.icon.options.popupAnchor || [0, 0] } }), o.LayerGroup = o.Layer.extend({ - initialize: function(t) { this._layers = {}; var e, i; if (t) - for (e = 0, i = t.length; i > e; e++) this.addLayer(t[e]) }, - addLayer: function(t) { var e = this.getLayerId(t); return this._layers[e] = t, this._map && this._map.addLayer(t), this }, - removeLayer: function(t) { - var e = t in this._layers ? t : this.getLayerId(t); - return this._map && this._layers[e] && this._map.removeLayer(this._layers[e]), - delete this._layers[e], this - }, - hasLayer: function(t) { return !!t && (t in this._layers || this.getLayerId(t) in this._layers) }, - clearLayers: function() { for (var t in this._layers) this.removeLayer(this._layers[t]); return this }, - invoke: function(t) { var e, i, n = Array.prototype.slice.call(arguments, 1); for (e in this._layers) i = this._layers[e], i[t] && i[t].apply(i, n); return this }, - onAdd: function(t) { for (var e in this._layers) t.addLayer(this._layers[e]) }, - onRemove: function(t) { for (var e in this._layers) t.removeLayer(this._layers[e]) }, - eachLayer: function(t, e) { for (var i in this._layers) t.call(e, this._layers[i]); return this }, - getLayer: function(t) { return this._layers[t] }, - getLayers: function() { var t = []; for (var e in this._layers) t.push(this._layers[e]); return t }, - setZIndex: function(t) { return this.invoke("setZIndex", t) }, - getLayerId: function(t) { return o.stamp(t) } - }), o.layerGroup = function(t) { return new o.LayerGroup(t) }, o.FeatureGroup = o.LayerGroup.extend({ addLayer: function(t) { return this.hasLayer(t) ? this : (t.addEventParent(this), o.LayerGroup.prototype.addLayer.call(this, t), this.fire("layeradd", { layer: t })) }, removeLayer: function(t) { return this.hasLayer(t) ? (t in this._layers && (t = this._layers[t]), t.removeEventParent(this), o.LayerGroup.prototype.removeLayer.call(this, t), this.fire("layerremove", { layer: t })) : this }, setStyle: function(t) { return this.invoke("setStyle", t) }, bringToFront: function() { return this.invoke("bringToFront") }, bringToBack: function() { return this.invoke("bringToBack") }, getBounds: function() { var t = new o.LatLngBounds; for (var e in this._layers) { var i = this._layers[e]; - t.extend(i.getBounds ? i.getBounds() : i.getLatLng()) } return t } }), o.featureGroup = function(t) { return new o.FeatureGroup(t) }, o.Renderer = o.Layer.extend({ options: { padding: .1 }, initialize: function(t) { o.setOptions(this, t), o.stamp(this) }, onAdd: function() { this._container || (this._initContainer(), this._zoomAnimated && o.DomUtil.addClass(this._container, "leaflet-zoom-animated")), this.getPane().appendChild(this._container), this._update() }, onRemove: function() { o.DomUtil.remove(this._container) }, getEvents: function() { var t = { viewreset: this._reset, zoomstart: this._onZoomStart, zoom: this._onZoom, moveend: this._update }; return this._zoomAnimated && (t.zoomanim = this._onAnimZoom), t }, _onAnimZoom: function(t) { this._updateTransform(t.center, t.zoom) }, _onZoom: function() { this._updateTransform(this._map.getCenter(), this._map.getZoom()) }, _onZoomStart: function() { this._update() }, _updateTransform: function(t, e) { var i = this._map.getZoomScale(e, this._zoom), - n = o.DomUtil.getPosition(this._container), - s = this._map.getSize().multiplyBy(.5 + this.options.padding), - r = this._map.project(this._center, e), - a = this._map.project(t, e), - h = a.subtract(r), - l = s.multiplyBy(-i).add(n).add(s).subtract(h); - o.DomUtil.setTransform(this._container, l, i) }, _reset: function() { this._update(), this._updateTransform(this._center, this._zoom) }, _update: function() { var t = this.options.padding, - e = this._map.getSize(), - i = this._map.containerPointToLayerPoint(e.multiplyBy(-t)).round(); - this._bounds = new o.Bounds(i, i.add(e.multiplyBy(1 + 2 * t)).round()), this._center = this._map.getCenter(), this._zoom = this._map.getZoom() } }), o.Map.include({ getRenderer: function(t) { var e = t.options.renderer || this._getPaneRenderer(t.options.pane) || this.options.renderer || this._renderer; return e || (e = this._renderer = this.options.preferCanvas && o.canvas() || o.svg()), this.hasLayer(e) || this.addLayer(e), e }, _getPaneRenderer: function(t) { if ("overlayPane" === t || t === i) return !1; var e = this._paneRenderers[t]; return e === i && (e = o.SVG && o.svg({ pane: t }) || o.Canvas && o.canvas({ pane: t }), this._paneRenderers[t] = e), e } }), o.Path = o.Layer.extend({ options: { stroke: !0, color: "#3388ff", weight: 3, opacity: 1, lineCap: "round", lineJoin: "round", fillOpacity: .2, fillRule: "evenodd", interactive: !0 }, beforeAdd: function(t) { this._renderer = t.getRenderer(this) }, onAdd: function() { this._renderer._initPath(this), this._reset(), this._renderer._addPath(this) }, onRemove: function() { this._renderer._removePath(this) }, getEvents: function() { return { zoomend: this._project, moveend: this._update, viewreset: this._reset } }, redraw: function() { return this._map && this._renderer._updatePath(this), this }, setStyle: function(t) { return o.setOptions(this, t), this._renderer && this._renderer._updateStyle(this), this }, bringToFront: function() { return this._renderer && this._renderer._bringToFront(this), this }, bringToBack: function() { return this._renderer && this._renderer._bringToBack(this), this }, getElement: function() { return this._path }, _reset: function() { this._project(), this._update() }, _clickTolerance: function() { return (this.options.stroke ? this.options.weight / 2 : 0) + (o.Browser.touch ? 10 : 0) } }), o.LineUtil = { simplify: function(t, e) { if (!e || !t.length) return t.slice(); var i = e * e; return t = this._reducePoints(t, i), t = this._simplifyDP(t, i) }, pointToSegmentDistance: function(t, e, i) { return Math.sqrt(this._sqClosestPointOnSegment(t, e, i, !0)) }, closestPointOnSegment: function(t, e, i) { return this._sqClosestPointOnSegment(t, e, i) }, _simplifyDP: function(t, e) { var n = t.length, - o = typeof Uint8Array != i + "" ? Uint8Array : Array, - s = new o(n); - s[0] = s[n - 1] = 1, this._simplifyDPStep(t, s, e, 0, n - 1); var r, a = []; for (r = 0; n > r; r++) s[r] && a.push(t[r]); return a }, _simplifyDPStep: function(t, e, i, n, o) { var s, r, a, h = 0; for (r = n + 1; o - 1 >= r; r++) a = this._sqClosestPointOnSegment(t[r], t[n], t[o], !0), a > h && (s = r, h = a); - h > i && (e[s] = 1, this._simplifyDPStep(t, e, i, n, s), this._simplifyDPStep(t, e, i, s, o)) }, _reducePoints: function(t, e) { for (var i = [t[0]], n = 1, o = 0, s = t.length; s > n; n++) this._sqDist(t[n], t[o]) > e && (i.push(t[n]), o = n); return s - 1 > o && i.push(t[s - 1]), i }, clipSegment: function(t, e, i, n, o) { var s, r, a, h = n ? this._lastCode : this._getBitCode(t, i), - l = this._getBitCode(e, i); for (this._lastCode = l;;) { if (!(h | l)) return [t, e]; if (h & l) return !1; - s = h || l, r = this._getEdgeIntersection(t, e, s, i, o), a = this._getBitCode(r, i), s === h ? (t = r, h = a) : (e = r, l = a) } }, _getEdgeIntersection: function(t, e, i, n, s) { var r, a, h = e.x - t.x, - l = e.y - t.y, - u = n.min, - c = n.max; return 8 & i ? (r = t.x + h * (c.y - t.y) / l, a = c.y) : 4 & i ? (r = t.x + h * (u.y - t.y) / l, a = u.y) : 2 & i ? (r = c.x, a = t.y + l * (c.x - t.x) / h) : 1 & i && (r = u.x, a = t.y + l * (u.x - t.x) / h), new o.Point(r, a, s) }, _getBitCode: function(t, e) { var i = 0; return t.x < e.min.x ? i |= 1 : t.x > e.max.x && (i |= 2), t.y < e.min.y ? i |= 4 : t.y > e.max.y && (i |= 8), i }, _sqDist: function(t, e) { var i = e.x - t.x, - n = e.y - t.y; return i * i + n * n }, _sqClosestPointOnSegment: function(t, e, i, n) { var s, r = e.x, - a = e.y, - h = i.x - r, - l = i.y - a, - u = h * h + l * l; return u > 0 && (s = ((t.x - r) * h + (t.y - a) * l) / u, s > 1 ? (r = i.x, a = i.y) : s > 0 && (r += h * s, a += l * s)), h = t.x - r, l = t.y - a, n ? h * h + l * l : new o.Point(r, a) } }, o.Polyline = o.Path.extend({ options: { smoothFactor: 1 }, initialize: function(t, e) { o.setOptions(this, e), this._setLatLngs(t) }, getLatLngs: function() { return this._latlngs }, setLatLngs: function(t) { return this._setLatLngs(t), this.redraw() }, isEmpty: function() { return !this._latlngs.length }, closestLayerPoint: function(t) { for (var e, i, n = 1 / 0, s = null, r = o.LineUtil._sqClosestPointOnSegment, a = 0, h = this._parts.length; h > a; a++) - for (var l = this._parts[a], u = 1, c = l.length; c > u; u++) { e = l[u - 1], i = l[u]; var d = r(t, e, i, !0); - n > d && (n = d, s = r(t, e, i)) } - return s && (s.distance = Math.sqrt(n)), s }, getCenter: function() { var t, e, i, n, o, s, r, a = this._rings[0], - h = a.length; if (!h) return null; for (t = 0, e = 0; h - 1 > t; t++) e += a[t].distanceTo(a[t + 1]) / 2; if (0 === e) return this._map.layerPointToLatLng(a[0]); for (t = 0, n = 0; h - 1 > t; t++) - if (o = a[t], s = a[t + 1], i = o.distanceTo(s), n += i, n > e) return r = (n - e) / i, this._map.layerPointToLatLng([s.x - r * (s.x - o.x), s.y - r * (s.y - o.y)]) }, getBounds: function() { return this._bounds }, addLatLng: function(t, e) { return e = e || this._defaultShape(), t = o.latLng(t), e.push(t), this._bounds.extend(t), this.redraw() }, _setLatLngs: function(t) { this._bounds = new o.LatLngBounds, this._latlngs = this._convertLatLngs(t) }, _defaultShape: function() { return o.Polyline._flat(this._latlngs) ? this._latlngs : this._latlngs[0] }, _convertLatLngs: function(t) { for (var e = [], i = o.Polyline._flat(t), n = 0, s = t.length; s > n; n++) i ? (e[n] = o.latLng(t[n]), this._bounds.extend(e[n])) : e[n] = this._convertLatLngs(t[n]); return e }, _project: function() { this._rings = [], this._projectLatlngs(this._latlngs, this._rings); var t = this._clickTolerance(), - e = new o.Point(t, -t); - this._bounds.isValid() && (this._pxBounds = new o.Bounds(this._map.latLngToLayerPoint(this._bounds.getSouthWest())._subtract(e), this._map.latLngToLayerPoint(this._bounds.getNorthEast())._add(e))) }, _projectLatlngs: function(t, e) { var i, n, s = t[0] instanceof o.LatLng, - r = t.length; if (s) { for (n = [], i = 0; r > i; i++) n[i] = this._map.latLngToLayerPoint(t[i]); - e.push(n) } else - for (i = 0; r > i; i++) this._projectLatlngs(t[i], e) }, _clipPoints: function() { var t = this._renderer._bounds; if (this._parts = [], this._pxBounds && this._pxBounds.intersects(t)) { if (this.options.noClip) return void(this._parts = this._rings); var e, i, n, s, r, a, h, l = this._parts; for (e = 0, n = 0, s = this._rings.length; s > e; e++) - for (h = this._rings[e], i = 0, r = h.length; r - 1 > i; i++) a = o.LineUtil.clipSegment(h[i], h[i + 1], t, i, !0), a && (l[n] = l[n] || [], l[n].push(a[0]), (a[1] !== h[i + 1] || i === r - 2) && (l[n].push(a[1]), n++)) } }, _simplifyPoints: function() { for (var t = this._parts, e = this.options.smoothFactor, i = 0, n = t.length; n > i; i++) t[i] = o.LineUtil.simplify(t[i], e) }, _update: function() { this._map && (this._clipPoints(), this._simplifyPoints(), this._updatePath()) }, _updatePath: function() { this._renderer._updatePoly(this) } }), o.polyline = function(t, e) { return new o.Polyline(t, e) }, o.Polyline._flat = function(t) { return !o.Util.isArray(t[0]) || "object" != typeof t[0][0] && "undefined" != typeof t[0][0] }, o.PolyUtil = {}, o.PolyUtil.clipPolygon = function(t, e, i) { var n, s, r, a, h, l, u, c, d, _ = [1, 4, 2, 8], - m = o.LineUtil; for (s = 0, u = t.length; u > s; s++) t[s]._code = m._getBitCode(t[s], e); for (a = 0; 4 > a; a++) { for (c = _[a], n = [], s = 0, u = t.length, r = u - 1; u > s; r = s++) h = t[s], l = t[r], h._code & c ? l._code & c || (d = m._getEdgeIntersection(l, h, c, e, i), d._code = m._getBitCode(d, e), n.push(d)) : (l._code & c && (d = m._getEdgeIntersection(l, h, c, e, i), d._code = m._getBitCode(d, e), n.push(d)), n.push(h)); - t = n } return t }, o.Polygon = o.Polyline.extend({ options: { fill: !0 }, isEmpty: function() { return !this._latlngs.length || !this._latlngs[0].length }, getCenter: function() { var t, e, i, n, o, s, r, a, h, l = this._rings[0], - u = l.length; if (!u) return null; for (s = r = a = 0, t = 0, e = u - 1; u > t; e = t++) i = l[t], n = l[e], o = i.y * n.x - n.y * i.x, r += (i.x + n.x) * o, a += (i.y + n.y) * o, s += 3 * o; return h = 0 === s ? l[0] : [r / s, a / s], this._map.layerPointToLatLng(h) }, _convertLatLngs: function(t) { var e = o.Polyline.prototype._convertLatLngs.call(this, t), - i = e.length; return i >= 2 && e[0] instanceof o.LatLng && e[0].equals(e[i - 1]) && e.pop(), e }, _setLatLngs: function(t) { o.Polyline.prototype._setLatLngs.call(this, t), o.Polyline._flat(this._latlngs) && (this._latlngs = [this._latlngs]) }, _defaultShape: function() { return o.Polyline._flat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0] }, _clipPoints: function() { var t = this._renderer._bounds, - e = this.options.weight, - i = new o.Point(e, e); if (t = new o.Bounds(t.min.subtract(i), t.max.add(i)), this._parts = [], this._pxBounds && this._pxBounds.intersects(t)) { if (this.options.noClip) return void(this._parts = this._rings); for (var n, s = 0, r = this._rings.length; r > s; s++) n = o.PolyUtil.clipPolygon(this._rings[s], t, !0), n.length && this._parts.push(n) } }, _updatePath: function() { this._renderer._updatePoly(this, !0) } }), o.polygon = function(t, e) { return new o.Polygon(t, e) }, o.Rectangle = o.Polygon.extend({ initialize: function(t, e) { o.Polygon.prototype.initialize.call(this, this._boundsToLatLngs(t), e) }, setBounds: function(t) { return this.setLatLngs(this._boundsToLatLngs(t)) }, _boundsToLatLngs: function(t) { return t = o.latLngBounds(t), [t.getSouthWest(), t.getNorthWest(), t.getNorthEast(), t.getSouthEast()] } }), o.rectangle = function(t, e) { return new o.Rectangle(t, e) }, o.CircleMarker = o.Path.extend({ options: { fill: !0, radius: 10 }, initialize: function(t, e) { o.setOptions(this, e), this._latlng = o.latLng(t), this._radius = this.options.radius }, setLatLng: function(t) { return this._latlng = o.latLng(t), this.redraw(), this.fire("move", { latlng: this._latlng }) }, getLatLng: function() { return this._latlng }, setRadius: function(t) { return this.options.radius = this._radius = t, this.redraw() }, getRadius: function() { return this._radius }, setStyle: function(t) { var e = t && t.radius || this._radius; return o.Path.prototype.setStyle.call(this, t), this.setRadius(e), this }, _project: function() { this._point = this._map.latLngToLayerPoint(this._latlng), this._updateBounds() }, _updateBounds: function() { var t = this._radius, - e = this._radiusY || t, - i = this._clickTolerance(), - n = [t + i, e + i]; - this._pxBounds = new o.Bounds(this._point.subtract(n), this._point.add(n)) }, _update: function() { this._map && this._updatePath() }, _updatePath: function() { this._renderer._updateCircle(this) }, _empty: function() { return this._radius && !this._renderer._bounds.intersects(this._pxBounds) } }), o.circleMarker = function(t, e) { return new o.CircleMarker(t, e) }, o.Circle = o.CircleMarker.extend({ initialize: function(t, e) { o.setOptions(this, e), this._latlng = o.latLng(t), this._mRadius = this.options.radius }, setRadius: function(t) { return this._mRadius = t, this.redraw() }, getRadius: function() { return this._mRadius }, getBounds: function() { var t = [this._radius, this._radiusY || this._radius]; return new o.LatLngBounds(this._map.layerPointToLatLng(this._point.subtract(t)), this._map.layerPointToLatLng(this._point.add(t))) }, setStyle: o.Path.prototype.setStyle, _project: function() { var t = this._latlng.lng, - e = this._latlng.lat, - i = this._map, - n = i.options.crs; if (n.distance === o.CRS.Earth.distance) { var s = Math.PI / 180, - r = this._mRadius / o.CRS.Earth.R / s, - a = i.project([e + r, t]), - h = i.project([e - r, t]), - l = a.add(h).divideBy(2), - u = i.unproject(l).lat, - c = Math.acos((Math.cos(r * s) - Math.sin(e * s) * Math.sin(u * s)) / (Math.cos(e * s) * Math.cos(u * s))) / s; - this._point = l.subtract(i.getPixelOrigin()), this._radius = isNaN(c) ? 0 : Math.max(Math.round(l.x - i.project([u, t - c]).x), 1), this._radiusY = Math.max(Math.round(l.y - a.y), 1) } else { var d = n.unproject(n.project(this._latlng).subtract([this._mRadius, 0])); - this._point = i.latLngToLayerPoint(this._latlng), this._radius = this._point.x - i.latLngToLayerPoint(d).x } - this._updateBounds() } }), o.circle = function(t, e, i) { return "number" == typeof e && (e = o.extend({}, i, { radius: e })), new o.Circle(t, e) }, o.SVG = o.Renderer.extend({ _initContainer: function() { this._container = o.SVG.create("svg"), this._container.setAttribute("pointer-events", "none"), this._rootGroup = o.SVG.create("g"), this._container.appendChild(this._rootGroup) }, _update: function() { if (!this._map._animatingZoom || !this._bounds) { o.Renderer.prototype._update.call(this); var t = this._bounds, - e = t.getSize(), - i = this._container; - this._svgSize && this._svgSize.equals(e) || (this._svgSize = e, i.setAttribute("width", e.x), i.setAttribute("height", e.y)), o.DomUtil.setPosition(i, t.min), i.setAttribute("viewBox", [t.min.x, t.min.y, e.x, e.y].join(" ")) } }, _initPath: function(t) { var e = t._path = o.SVG.create("path"); - t.options.className && o.DomUtil.addClass(e, t.options.className), t.options.interactive && o.DomUtil.addClass(e, "leaflet-interactive"), this._updateStyle(t) }, _addPath: function(t) { this._rootGroup.appendChild(t._path), t.addInteractiveTarget(t._path) }, _removePath: function(t) { o.DomUtil.remove(t._path), t.removeInteractiveTarget(t._path) }, _updatePath: function(t) { t._project(), t._update() }, _updateStyle: function(t) { var e = t._path, - i = t.options; - e && (i.stroke ? (e.setAttribute("stroke", i.color), e.setAttribute("stroke-opacity", i.opacity), e.setAttribute("stroke-width", i.weight), e.setAttribute("stroke-linecap", i.lineCap), e.setAttribute("stroke-linejoin", i.lineJoin), i.dashArray ? e.setAttribute("stroke-dasharray", i.dashArray) : e.removeAttribute("stroke-dasharray"), i.dashOffset ? e.setAttribute("stroke-dashoffset", i.dashOffset) : e.removeAttribute("stroke-dashoffset")) : e.setAttribute("stroke", "none"), i.fill ? (e.setAttribute("fill", i.fillColor || i.color), e.setAttribute("fill-opacity", i.fillOpacity), e.setAttribute("fill-rule", i.fillRule || "evenodd")) : e.setAttribute("fill", "none"), e.setAttribute("pointer-events", i.pointerEvents || (i.interactive ? "visiblePainted" : "none"))) }, _updatePoly: function(t, e) { this._setPath(t, o.SVG.pointsToPath(t._parts, e)) }, _updateCircle: function(t) { var e = t._point, - i = t._radius, - n = t._radiusY || i, - o = "a" + i + "," + n + " 0 1,0 ", - s = t._empty() ? "M0 0" : "M" + (e.x - i) + "," + e.y + o + 2 * i + ",0 " + o + 2 * -i + ",0 "; - this._setPath(t, s) }, _setPath: function(t, e) { t._path.setAttribute("d", e) }, _bringToFront: function(t) { o.DomUtil.toFront(t._path) }, _bringToBack: function(t) { o.DomUtil.toBack(t._path) } }), o.extend(o.SVG, { create: function(t) { return e.createElementNS("http://www.w3.org/2000/svg", t) }, pointsToPath: function(t, e) { var i, n, s, r, a, h, l = ""; for (i = 0, s = t.length; s > i; i++) { for (a = t[i], n = 0, r = a.length; r > n; n++) h = a[n], l += (n ? "L" : "M") + h.x + " " + h.y; - l += e ? o.Browser.svg ? "z" : "x" : "" } return l || "M0 0" } }), o.Browser.svg = !(!e.createElementNS || !o.SVG.create("svg").createSVGRect), o.svg = function(t) { return o.Browser.svg || o.Browser.vml ? new o.SVG(t) : null }, o.Browser.vml = !o.Browser.svg && function() { try { var t = e.createElement("div"); - t.innerHTML = ''; var i = t.firstChild; return i.style.behavior = "url(#default#VML)", i && "object" == typeof i.adj } catch (n) { return !1 } }(), o.SVG.include(o.Browser.vml ? { _initContainer: function() { this._container = o.DomUtil.create("div", "leaflet-vml-container") }, _update: function() { this._map._animatingZoom || o.Renderer.prototype._update.call(this) }, _initPath: function(t) { var e = t._container = o.SVG.create("shape"); - o.DomUtil.addClass(e, "leaflet-vml-shape " + (this.options.className || "")), e.coordsize = "1 1", t._path = o.SVG.create("path"), e.appendChild(t._path), this._updateStyle(t) }, _addPath: function(t) { var e = t._container; - this._container.appendChild(e), t.options.interactive && t.addInteractiveTarget(e) }, _removePath: function(t) { var e = t._container; - o.DomUtil.remove(e), t.removeInteractiveTarget(e) }, _updateStyle: function(t) { var e = t._stroke, - i = t._fill, - n = t.options, - s = t._container; - s.stroked = !!n.stroke, s.filled = !!n.fill, n.stroke ? (e || (e = t._stroke = o.SVG.create("stroke")), s.appendChild(e), e.weight = n.weight + "px", e.color = n.color, e.opacity = n.opacity, n.dashArray ? e.dashStyle = o.Util.isArray(n.dashArray) ? n.dashArray.join(" ") : n.dashArray.replace(/( *, *)/g, " ") : e.dashStyle = "", e.endcap = n.lineCap.replace("butt", "flat"), e.joinstyle = n.lineJoin) : e && (s.removeChild(e), t._stroke = null), n.fill ? (i || (i = t._fill = o.SVG.create("fill")), s.appendChild(i), i.color = n.fillColor || n.color, i.opacity = n.fillOpacity) : i && (s.removeChild(i), t._fill = null) }, _updateCircle: function(t) { var e = t._point.round(), - i = Math.round(t._radius), - n = Math.round(t._radiusY || i); - this._setPath(t, t._empty() ? "M0 0" : "AL " + e.x + "," + e.y + " " + i + "," + n + " 0,23592600") }, _setPath: function(t, e) { t._path.v = e }, _bringToFront: function(t) { o.DomUtil.toFront(t._container) }, _bringToBack: function(t) { o.DomUtil.toBack(t._container) } } : {}), o.Browser.vml && (o.SVG.create = function() { try { return e.namespaces.add("lvml", "urn:schemas-microsoft-com:vml"), - function(t) { return e.createElement("') } } catch (t) { return function(t) { return e.createElement("<" + t + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">') } } }()), o.Canvas = o.Renderer.extend({ onAdd: function() { o.Renderer.prototype.onAdd.call(this), this._layers = this._layers || {}, this._draw() }, _initContainer: function() { var t = this._container = e.createElement("canvas"); - o.DomEvent.on(t, "mousemove", o.Util.throttle(this._onMouseMove, 32, this), this).on(t, "click dblclick mousedown mouseup contextmenu", this._onClick, this).on(t, "mouseout", this._handleMouseOut, this), this._ctx = t.getContext("2d") }, _update: function() { if (!this._map._animatingZoom || !this._bounds) { this._drawnLayers = {}, o.Renderer.prototype._update.call(this); var t = this._bounds, - e = this._container, - i = t.getSize(), - n = o.Browser.retina ? 2 : 1; - o.DomUtil.setPosition(e, t.min), e.width = n * i.x, e.height = n * i.y, e.style.width = i.x + "px", e.style.height = i.y + "px", o.Browser.retina && this._ctx.scale(2, 2), this._ctx.translate(-t.min.x, -t.min.y) } }, _initPath: function(t) { this._layers[o.stamp(t)] = t }, _addPath: o.Util.falseFn, _removePath: function(t) { t._removed = !0, this._requestRedraw(t) }, _updatePath: function(t) { this._redrawBounds = t._pxBounds, this._draw(!0), t._project(), t._update(), this._draw(), this._redrawBounds = null }, _updateStyle: function(t) { this._requestRedraw(t) }, _requestRedraw: function(t) { if (this._map) { var e = (t.options.weight || 0) + 1; - this._redrawBounds = this._redrawBounds || new o.Bounds, this._redrawBounds.extend(t._pxBounds.min.subtract([e, e])), this._redrawBounds.extend(t._pxBounds.max.add([e, e])), this._redrawRequest = this._redrawRequest || o.Util.requestAnimFrame(this._redraw, this) } }, _redraw: function() { this._redrawRequest = null, this._draw(!0), this._draw(), this._redrawBounds = null }, _draw: function(t) { this._clear = t; var e, i = this._redrawBounds; - this._ctx.save(), i && (this._ctx.beginPath(), this._ctx.rect(i.min.x, i.min.y, i.max.x - i.min.x, i.max.y - i.min.y), this._ctx.clip()); for (var n in this._layers) e = this._layers[n], (!i || e._pxBounds.intersects(i)) && e._updatePath(), t && e._removed && (delete e._removed, delete this._layers[n]); - this._ctx.restore() }, _updatePoly: function(t, e) { var i, n, o, s, r = t._parts, - a = r.length, - h = this._ctx; if (a) { for (this._drawnLayers[t._leaflet_id] = t, h.beginPath(), i = 0; a > i; i++) { for (n = 0, o = r[i].length; o > n; n++) s = r[i][n], h[n ? "lineTo" : "moveTo"](s.x, s.y); - e && h.closePath() } - this._fillStroke(h, t) } }, _updateCircle: function(t) { if (!t._empty()) { var e = t._point, - i = this._ctx, - n = t._radius, - o = (t._radiusY || n) / n; - 1 !== o && (i.save(), i.scale(1, o)), i.beginPath(), i.arc(e.x, e.y / o, n, 0, 2 * Math.PI, !1), 1 !== o && i.restore(), this._fillStroke(i, t) } }, _fillStroke: function(t, e) { var i = this._clear, - n = e.options; - t.globalCompositeOperation = i ? "destination-out" : "source-over", n.fill && (t.globalAlpha = i ? 1 : n.fillOpacity, t.fillStyle = n.fillColor || n.color, t.fill(n.fillRule || "evenodd")), n.stroke && 0 !== n.weight && (t.globalAlpha = i ? 1 : n.opacity, e._prevWeight = t.lineWidth = i ? e._prevWeight + 1 : n.weight, t.strokeStyle = n.color, t.lineCap = n.lineCap, t.lineJoin = n.lineJoin, t.stroke()) }, _onClick: function(t) { var e = this._map.mouseEventToLayerPoint(t), - i = []; for (var n in this._layers) this._layers[n]._containsPoint(e) && (o.DomEvent._fakeStop(t), i.push(this._layers[n])); - i.length && this._fireEvent(i, t) }, _onMouseMove: function(t) { if (this._map && !this._map.dragging._draggable._moving && !this._map._animatingZoom) { var e = this._map.mouseEventToLayerPoint(t); - this._handleMouseOut(t, e), this._handleMouseHover(t, e) } }, _handleMouseOut: function(t, e) { var i = this._hoveredLayer;!i || "mouseout" !== t.type && i._containsPoint(e) || (o.DomUtil.removeClass(this._container, "leaflet-interactive"), this._fireEvent([i], t, "mouseout"), this._hoveredLayer = null) }, _handleMouseHover: function(t, e) { var i, n; if (!this._hoveredLayer) - for (i in this._drawnLayers) - if (n = this._drawnLayers[i], n.options.interactive && n._containsPoint(e)) { o.DomUtil.addClass(this._container, "leaflet-interactive"), this._fireEvent([n], t, "mouseover"), this._hoveredLayer = n; break } - this._hoveredLayer && this._fireEvent([this._hoveredLayer], t) }, _fireEvent: function(t, e, i) { this._map._fireDOMEvent(e, i || e.type, t) }, _bringToFront: o.Util.falseFn, _bringToBack: o.Util.falseFn }), o.Browser.canvas = function() { return !!e.createElement("canvas").getContext }(), o.canvas = function(t) { return o.Browser.canvas ? new o.Canvas(t) : null }, o.Polyline.prototype._containsPoint = function(t, e) { var i, n, s, r, a, h, l = this._clickTolerance(); if (!this._pxBounds.contains(t)) return !1; for (i = 0, r = this._parts.length; r > i; i++) - for (h = this._parts[i], n = 0, a = h.length, s = a - 1; a > n; s = n++) - if ((e || 0 !== n) && o.LineUtil.pointToSegmentDistance(t, h[s], h[n]) <= l) return !0; - return !1 }, o.Polygon.prototype._containsPoint = function(t) { var e, i, n, s, r, a, h, l, u = !1; if (!this._pxBounds.contains(t)) return !1; for (s = 0, h = this._parts.length; h > s; s++) - for (e = this._parts[s], r = 0, l = e.length, a = l - 1; l > r; a = r++) i = e[r], n = e[a], i.y > t.y != n.y > t.y && t.x < (n.x - i.x) * (t.y - i.y) / (n.y - i.y) + i.x && (u = !u); return u || o.Polyline.prototype._containsPoint.call(this, t, !0) }, o.CircleMarker.prototype._containsPoint = function(t) { return t.distanceTo(this._point) <= this._radius + this._clickTolerance() }, o.GeoJSON = o.FeatureGroup.extend({ initialize: function(t, e) { o.setOptions(this, e), this._layers = {}, t && this.addData(t) }, addData: function(t) { var e, i, n, s = o.Util.isArray(t) ? t : t.features; if (s) { for (e = 0, i = s.length; i > e; e++) n = s[e], (n.geometries || n.geometry || n.features || n.coordinates) && this.addData(n); return this } var r = this.options; if (r.filter && !r.filter(t)) return this; var a = o.GeoJSON.geometryToLayer(t, r); return a ? (a.feature = o.GeoJSON.asFeature(t), a.defaultOptions = a.options, this.resetStyle(a), r.onEachFeature && r.onEachFeature(t, a), this.addLayer(a)) : this }, resetStyle: function(t) { return t.options = t.defaultOptions, this._setLayerStyle(t, this.options.style), this }, setStyle: function(t) { return this.eachLayer(function(e) { this._setLayerStyle(e, t) }, this) }, _setLayerStyle: function(t, e) { "function" == typeof e && (e = e(t.feature)), t.setStyle && t.setStyle(e) } }), o.extend(o.GeoJSON, { geometryToLayer: function(t, e) { var i, n, s, r, a = "Feature" === t.type ? t.geometry : t, - h = a ? a.coordinates : null, - l = [], - u = e && e.pointToLayer, - c = e && e.coordsToLatLng || this.coordsToLatLng; if (!h && !a) return null; switch (a.type) { - case "Point": - return i = c(h), u ? u(t, i) : new o.Marker(i); - case "MultiPoint": - for (s = 0, r = h.length; r > s; s++) i = c(h[s]), l.push(u ? u(t, i) : new o.Marker(i)); return new o.FeatureGroup(l); - case "LineString": - case "MultiLineString": - return n = this.coordsToLatLngs(h, "LineString" === a.type ? 0 : 1, c), new o.Polyline(n, e); - case "Polygon": - case "MultiPolygon": - return n = this.coordsToLatLngs(h, "Polygon" === a.type ? 1 : 2, c), new o.Polygon(n, e); - case "GeometryCollection": - for (s = 0, r = a.geometries.length; r > s; s++) { var d = this.geometryToLayer({ geometry: a.geometries[s], type: "Feature", properties: t.properties }, e); - d && l.push(d) } return new o.FeatureGroup(l); - default: - throw new Error("Invalid GeoJSON object.") } }, coordsToLatLng: function(t) { return new o.LatLng(t[1], t[0], t[2]) }, coordsToLatLngs: function(t, e, i) { for (var n, o = [], s = 0, r = t.length; r > s; s++) n = e ? this.coordsToLatLngs(t[s], e - 1, i) : (i || this.coordsToLatLng)(t[s]), o.push(n); return o }, latLngToCoords: function(t) { return t.alt !== i ? [t.lng, t.lat, t.alt] : [t.lng, t.lat] }, latLngsToCoords: function(t, e, i) { for (var n = [], s = 0, r = t.length; r > s; s++) n.push(e ? o.GeoJSON.latLngsToCoords(t[s], e - 1, i) : o.GeoJSON.latLngToCoords(t[s])); return !e && i && n.push(n[0]), n }, getFeature: function(t, e) { return t.feature ? o.extend({}, t.feature, { geometry: e }) : o.GeoJSON.asFeature(e) }, asFeature: function(t) { return "Feature" === t.type ? t : { type: "Feature", properties: {}, geometry: t } } }); - var r = { toGeoJSON: function() { return o.GeoJSON.getFeature(this, { type: "Point", coordinates: o.GeoJSON.latLngToCoords(this.getLatLng()) }) } }; - o.Marker.include(r), o.Circle.include(r), o.CircleMarker.include(r), o.Polyline.prototype.toGeoJSON = function() { var t = !o.Polyline._flat(this._latlngs), - e = o.GeoJSON.latLngsToCoords(this._latlngs, t ? 1 : 0); return o.GeoJSON.getFeature(this, { type: (t ? "Multi" : "") + "LineString", coordinates: e }) }, o.Polygon.prototype.toGeoJSON = function() { var t = !o.Polyline._flat(this._latlngs), - e = t && !o.Polyline._flat(this._latlngs[0]), - i = o.GeoJSON.latLngsToCoords(this._latlngs, e ? 2 : t ? 1 : 0, !0); return t || (i = [i]), o.GeoJSON.getFeature(this, { type: (e ? "Multi" : "") + "Polygon", coordinates: i }) }, o.LayerGroup.include({ toMultiPoint: function() { var t = []; return this.eachLayer(function(e) { t.push(e.toGeoJSON().geometry.coordinates) }), o.GeoJSON.getFeature(this, { type: "MultiPoint", coordinates: t }) }, toGeoJSON: function() { var t = this.feature && this.feature.geometry && this.feature.geometry.type; if ("MultiPoint" === t) return this.toMultiPoint(); var e = "GeometryCollection" === t, - i = []; return this.eachLayer(function(t) { if (t.toGeoJSON) { var n = t.toGeoJSON(); - i.push(e ? n.geometry : o.GeoJSON.asFeature(n)) } }), e ? o.GeoJSON.getFeature(this, { geometries: i, type: "GeometryCollection" }) : { type: "FeatureCollection", features: i } } }), o.geoJson = function(t, e) { return new o.GeoJSON(t, e) }; - var a = "_leaflet_events"; - o.DomEvent = { on: function(t, e, i, n) { if ("object" == typeof e) - for (var s in e) this._on(t, s, e[s], i); - else { e = o.Util.splitWords(e); for (var r = 0, a = e.length; a > r; r++) this._on(t, e[r], i, n) } return this }, off: function(t, e, i, n) { if ("object" == typeof e) - for (var s in e) this._off(t, s, e[s], i); - else { e = o.Util.splitWords(e); for (var r = 0, a = e.length; a > r; r++) this._off(t, e[r], i, n) } return this }, _on: function(e, i, n, s) { var r = i + o.stamp(n) + (s ? "_" + o.stamp(s) : ""); if (e[a] && e[a][r]) return this; var h = function(i) { return n.call(s || e, i || t.event) }, - l = h; return o.Browser.pointer && 0 === i.indexOf("touch") ? this.addPointerListener(e, i, h, r) : o.Browser.touch && "dblclick" === i && this.addDoubleTapListener ? this.addDoubleTapListener(e, h, r) : "addEventListener" in e ? "mousewheel" === i ? (e.addEventListener("DOMMouseScroll", h, !1), e.addEventListener(i, h, !1)) : "mouseenter" === i || "mouseleave" === i ? (h = function(i) { i = i || t.event, o.DomEvent._isExternalTarget(e, i) && l(i) }, e.addEventListener("mouseenter" === i ? "mouseover" : "mouseout", h, !1)) : ("click" === i && o.Browser.android && (h = function(t) { return o.DomEvent._filterClick(t, l) }), e.addEventListener(i, h, !1)) : "attachEvent" in e && e.attachEvent("on" + i, h), e[a] = e[a] || {}, e[a][r] = h, this }, _off: function(t, e, i, n) { var s = e + o.stamp(i) + (n ? "_" + o.stamp(n) : ""), - r = t[a] && t[a][s]; return r ? (o.Browser.pointer && 0 === e.indexOf("touch") ? this.removePointerListener(t, e, s) : o.Browser.touch && "dblclick" === e && this.removeDoubleTapListener ? this.removeDoubleTapListener(t, s) : "removeEventListener" in t ? "mousewheel" === e ? (t.removeEventListener("DOMMouseScroll", r, !1), t.removeEventListener(e, r, !1)) : t.removeEventListener("mouseenter" === e ? "mouseover" : "mouseleave" === e ? "mouseout" : e, r, !1) : "detachEvent" in t && t.detachEvent("on" + e, r), t[a][s] = null, this) : this }, stopPropagation: function(t) { return t.stopPropagation ? t.stopPropagation() : t.originalEvent ? t.originalEvent._stopped = !0 : t.cancelBubble = !0, o.DomEvent._skipped(t), this }, disableScrollPropagation: function(t) { return o.DomEvent.on(t, "mousewheel MozMousePixelScroll", o.DomEvent.stopPropagation) }, disableClickPropagation: function(t) { var e = o.DomEvent.stopPropagation; return o.DomEvent.on(t, o.Draggable.START.join(" "), e), o.DomEvent.on(t, { click: o.DomEvent._fakeStop, dblclick: e }) }, preventDefault: function(t) { return t.preventDefault ? t.preventDefault() : t.returnValue = !1, this }, stop: function(t) { return o.DomEvent.preventDefault(t).stopPropagation(t) }, getMousePosition: function(t, e) { if (!e) return new o.Point(t.clientX, t.clientY); var i = e.getBoundingClientRect(); return new o.Point(t.clientX - i.left - e.clientLeft, t.clientY - i.top - e.clientTop) }, getWheelDelta: function(t) { var e = 0; return t.wheelDelta && (e = t.wheelDelta / 120), t.detail && (e = -t.detail / 3), e }, _skipEvents: {}, _fakeStop: function(t) { o.DomEvent._skipEvents[t.type] = !0 }, _skipped: function(t) { var e = this._skipEvents[t.type]; return this._skipEvents[t.type] = !1, e }, _isExternalTarget: function(t, e) { var i = e.relatedTarget; if (!i) return !0; try { for (; i && i !== t;) i = i.parentNode } catch (n) { return !1 } return i !== t }, _filterClick: function(t, e) { var i = t.timeStamp || t.originalEvent.timeStamp, - n = o.DomEvent._lastClick && i - o.DomEvent._lastClick; return n && n > 100 && 500 > n || t.target._simulatedClick && !t._simulated ? void o.DomEvent.stop(t) : (o.DomEvent._lastClick = i, void e(t)) } }, o.DomEvent.addListener = o.DomEvent.on, o.DomEvent.removeListener = o.DomEvent.off, o.Draggable = o.Evented.extend({ - statics: { START: o.Browser.touch ? ["touchstart", "mousedown"] : ["mousedown"], END: { mousedown: "mouseup", touchstart: "touchend", pointerdown: "touchend", MSPointerDown: "touchend" }, MOVE: { mousedown: "mousemove", touchstart: "touchmove", pointerdown: "touchmove", MSPointerDown: "touchmove" } }, - initialize: function(t, e, i) { this._element = t, this._dragStartTarget = e || t, this._preventOutline = i }, - enable: function() { this._enabled || (o.DomEvent.on(this._dragStartTarget, o.Draggable.START.join(" "), this._onDown, this), this._enabled = !0) }, - disable: function() { this._enabled && (o.DomEvent.off(this._dragStartTarget, o.Draggable.START.join(" "), this._onDown, this), this._enabled = !1, this._moved = !1) }, - _onDown: function(t) { if (this._moved = !1, !o.DomUtil.hasClass(this._element, "leaflet-zoom-anim") && !(o.Draggable._dragging || t.shiftKey || 1 !== t.which && 1 !== t.button && !t.touches) && this._enabled && (o.Draggable._dragging = !0, this._preventOutline && o.DomUtil.preventOutline(this._element), o.DomUtil.disableImageDrag(), o.DomUtil.disableTextSelection(), !this._moving)) { this.fire("down"); var i = t.touches ? t.touches[0] : t; - this._startPoint = new o.Point(i.clientX, i.clientY), this._startPos = this._newPos = o.DomUtil.getPosition(this._element), o.DomEvent.on(e, o.Draggable.MOVE[t.type], this._onMove, this).on(e, o.Draggable.END[t.type], this._onUp, this) } }, - _onMove: function(t) { - if (t.touches && t.touches.length > 1) return void(this._moved = !0); - var i = t.touches && 1 === t.touches.length ? t.touches[0] : t, - n = new o.Point(i.clientX, i.clientY), - s = n.subtract(this._startPoint); - (s.x || s.y) && (o.Browser.touch && Math.abs(s.x) + Math.abs(s.y) < 3 || (o.DomEvent.preventDefault(t), this._moved || (this.fire("dragstart"), this._moved = !0, this._startPos = o.DomUtil.getPosition(this._element).subtract(s), o.DomUtil.addClass(e.body, "leaflet-dragging"), this._lastTarget = t.target || t.srcElement, o.DomUtil.addClass(this._lastTarget, "leaflet-drag-target")), - this._newPos = this._startPos.add(s), this._moving = !0, o.Util.cancelAnimFrame(this._animRequest), this._lastEvent = t, this._animRequest = o.Util.requestAnimFrame(this._updatePosition, this, !0))) - }, - _updatePosition: function() { var t = { originalEvent: this._lastEvent }; - this.fire("predrag", t), o.DomUtil.setPosition(this._element, this._newPos), this.fire("drag", t) }, - _onUp: function() { o.DomUtil.removeClass(e.body, "leaflet-dragging"), this._lastTarget && (o.DomUtil.removeClass(this._lastTarget, "leaflet-drag-target"), this._lastTarget = null); for (var t in o.Draggable.MOVE) o.DomEvent.off(e, o.Draggable.MOVE[t], this._onMove, this).off(e, o.Draggable.END[t], this._onUp, this); - o.DomUtil.enableImageDrag(), o.DomUtil.enableTextSelection(), this._moved && this._moving && (o.Util.cancelAnimFrame(this._animRequest), this.fire("dragend", { distance: this._newPos.distanceTo(this._startPos) })), this._moving = !1, o.Draggable._dragging = !1 } - }), o.Handler = o.Class.extend({ initialize: function(t) { this._map = t }, enable: function() { this._enabled || (this._enabled = !0, this.addHooks()) }, disable: function() { this._enabled && (this._enabled = !1, this.removeHooks()) }, enabled: function() { return !!this._enabled } }), o.Map.mergeOptions({ dragging: !0, inertia: !o.Browser.android23, inertiaDeceleration: 3400, inertiaMaxSpeed: 1 / 0, easeLinearity: .2, worldCopyJump: !1 }), o.Map.Drag = o.Handler.extend({ addHooks: function() { if (!this._draggable) { var t = this._map; - this._draggable = new o.Draggable(t._mapPane, t._container), this._draggable.on({ down: this._onDown, dragstart: this._onDragStart, drag: this._onDrag, dragend: this._onDragEnd }, this), this._draggable.on("predrag", this._onPreDragLimit, this), t.options.worldCopyJump && (this._draggable.on("predrag", this._onPreDragWrap, this), t.on("zoomend", this._onZoomEnd, this), t.whenReady(this._onZoomEnd, this)) } - o.DomUtil.addClass(this._map._container, "leaflet-grab"), this._draggable.enable() }, removeHooks: function() { o.DomUtil.removeClass(this._map._container, "leaflet-grab"), this._draggable.disable() }, moved: function() { return this._draggable && this._draggable._moved }, _onDown: function() { this._map.stop() }, _onDragStart: function() { var t = this._map; if (this._map.options.maxBounds && this._map.options.maxBoundsViscosity) { var e = o.latLngBounds(this._map.options.maxBounds); - this._offsetLimit = o.bounds(this._map.latLngToContainerPoint(e.getNorthWest()).multiplyBy(-1), this._map.latLngToContainerPoint(e.getSouthEast()).multiplyBy(-1).add(this._map.getSize())), this._viscosity = Math.min(1, Math.max(0, this._map.options.maxBoundsViscosity)) } else this._offsetLimit = null; - t.fire("movestart").fire("dragstart"), t.options.inertia && (this._positions = [], this._times = []) }, _onDrag: function(t) { if (this._map.options.inertia) { var e = this._lastTime = +new Date, - i = this._lastPos = this._draggable._absPos || this._draggable._newPos; - this._positions.push(i), this._times.push(e), e - this._times[0] > 50 && (this._positions.shift(), this._times.shift()) } - this._map.fire("move", t).fire("drag", t) }, _onZoomEnd: function() { var t = this._map.getSize().divideBy(2), - e = this._map.latLngToLayerPoint([0, 0]); - this._initialWorldOffset = e.subtract(t).x, this._worldWidth = this._map.getPixelWorldBounds().getSize().x }, _viscousLimit: function(t, e) { return t - (t - e) * this._viscosity }, _onPreDragLimit: function() { if (this._viscosity && this._offsetLimit) { var t = this._draggable._newPos.subtract(this._draggable._startPos), - e = this._offsetLimit; - t.x < e.min.x && (t.x = this._viscousLimit(t.x, e.min.x)), t.y < e.min.y && (t.y = this._viscousLimit(t.y, e.min.y)), t.x > e.max.x && (t.x = this._viscousLimit(t.x, e.max.x)), t.y > e.max.y && (t.y = this._viscousLimit(t.y, e.max.y)), this._draggable._newPos = this._draggable._startPos.add(t) } }, _onPreDragWrap: function() { var t = this._worldWidth, - e = Math.round(t / 2), - i = this._initialWorldOffset, - n = this._draggable._newPos.x, - o = (n - e + i) % t + e - i, - s = (n + e + i) % t - e - i, - r = Math.abs(o + i) < Math.abs(s + i) ? o : s; - this._draggable._absPos = this._draggable._newPos.clone(), this._draggable._newPos.x = r }, _onDragEnd: function(t) { var e = this._map, - i = e.options, - n = !i.inertia || this._times.length < 2; if (e.fire("dragend", t), n) e.fire("moveend"); - else { var s = this._lastPos.subtract(this._positions[0]), - r = (this._lastTime - this._times[0]) / 1e3, - a = i.easeLinearity, - h = s.multiplyBy(a / r), - l = h.distanceTo([0, 0]), - u = Math.min(i.inertiaMaxSpeed, l), - c = h.multiplyBy(u / l), - d = u / (i.inertiaDeceleration * a), - _ = c.multiplyBy(-d / 2).round(); - _.x || _.y ? (_ = e._limitOffset(_, e.options.maxBounds), o.Util.requestAnimFrame(function() { e.panBy(_, { duration: d, easeLinearity: a, noMoveStart: !0, animate: !0 }) })) : e.fire("moveend") } } }), o.Map.addInitHook("addHandler", "dragging", o.Map.Drag), o.Map.mergeOptions({ doubleClickZoom: !0 }), o.Map.DoubleClickZoom = o.Handler.extend({ addHooks: function() { this._map.on("dblclick", this._onDoubleClick, this) }, removeHooks: function() { this._map.off("dblclick", this._onDoubleClick, this) }, _onDoubleClick: function(t) { var e = this._map, - i = e.getZoom(), - n = t.originalEvent.shiftKey ? Math.ceil(i) - 1 : Math.floor(i) + 1; "center" === e.options.doubleClickZoom ? e.setZoom(n) : e.setZoomAround(t.containerPoint, n) } }), o.Map.addInitHook("addHandler", "doubleClickZoom", o.Map.DoubleClickZoom), o.Map.mergeOptions({ scrollWheelZoom: !0, wheelDebounceTime: 40 }), o.Map.ScrollWheelZoom = o.Handler.extend({ addHooks: function() { o.DomEvent.on(this._map._container, { mousewheel: this._onWheelScroll, MozMousePixelScroll: o.DomEvent.preventDefault }, this), this._delta = 0 }, removeHooks: function() { o.DomEvent.off(this._map._container, { mousewheel: this._onWheelScroll, MozMousePixelScroll: o.DomEvent.preventDefault }, this) }, _onWheelScroll: function(t) { var e = o.DomEvent.getWheelDelta(t), - i = this._map.options.wheelDebounceTime; - this._delta += e, this._lastMousePos = this._map.mouseEventToContainerPoint(t), this._startTime || (this._startTime = +new Date); var n = Math.max(i - (+new Date - this._startTime), 0); - clearTimeout(this._timer), this._timer = setTimeout(o.bind(this._performZoom, this), n), o.DomEvent.stop(t) }, _performZoom: function() { var t = this._map, - e = this._delta, - i = t.getZoom(); - t.stop(), e = e > 0 ? Math.ceil(e) : Math.floor(e), e = Math.max(Math.min(e, 4), -4), e = t._limitZoom(i + e) - i, this._delta = 0, this._startTime = null, e && ("center" === t.options.scrollWheelZoom ? t.setZoom(i + e) : t.setZoomAround(this._lastMousePos, i + e)) } }), o.Map.addInitHook("addHandler", "scrollWheelZoom", o.Map.ScrollWheelZoom), o.extend(o.DomEvent, { _touchstart: o.Browser.msPointer ? "MSPointerDown" : o.Browser.pointer ? "pointerdown" : "touchstart", _touchend: o.Browser.msPointer ? "MSPointerUp" : o.Browser.pointer ? "pointerup" : "touchend", addDoubleTapListener: function(t, e, i) { - function n(t) { var e; if (e = o.Browser.pointer ? o.DomEvent._pointersCount : t.touches.length, !(e > 1)) { var i = Date.now(), - n = i - (r || i); - a = t.touches ? t.touches[0] : t, h = n > 0 && l >= n, r = i } } + function i(e) { + var i = +new Date(), + o = Math.max(0, 16 - (i - n)); + return (n = i + o), t.setTimeout(e, o); + } + var n = 0, + s = t.requestAnimationFrame || e("RequestAnimationFrame") || i, + r = + t.cancelAnimationFrame || + e("CancelAnimationFrame") || + e("CancelRequestAnimationFrame") || + function (e) { + t.clearTimeout(e); + }; + (o.Util.requestAnimFrame = function (e, n, r) { + return r && s === i ? void e.call(n) : s.call(t, o.bind(e, n)); + }), + (o.Util.cancelAnimFrame = function (e) { + e && r.call(t, e); + }); + })(), + (o.extend = o.Util.extend), + (o.bind = o.Util.bind), + (o.stamp = o.Util.stamp), + (o.setOptions = o.Util.setOptions), + (o.Class = function () {}), + (o.Class.extend = function (t) { + var e = function () { + this.initialize && this.initialize.apply(this, arguments), this.callInitHooks(); + }, + i = (e.__super__ = this.prototype), + n = o.Util.create(i); + (n.constructor = e), (e.prototype = n); + for (var s in this) this.hasOwnProperty(s) && "prototype" !== s && (e[s] = this[s]); + return ( + t.statics && (o.extend(e, t.statics), delete t.statics), + t.includes && (o.Util.extend.apply(null, [n].concat(t.includes)), delete t.includes), + n.options && (t.options = o.Util.extend(o.Util.create(n.options), t.options)), + o.extend(n, t), + (n._initHooks = []), + (n.callInitHooks = function () { + if (!this._initHooksCalled) { + i.callInitHooks && i.callInitHooks.call(this), (this._initHooksCalled = !0); + for (var t = 0, e = n._initHooks.length; e > t; t++) n._initHooks[t].call(this); + } + }), + e + ); + }), + (o.Class.include = function (t) { + o.extend(this.prototype, t); + }), + (o.Class.mergeOptions = function (t) { + o.extend(this.prototype.options, t); + }), + (o.Class.addInitHook = function (t) { + var e = Array.prototype.slice.call(arguments, 1), + i = + "function" == typeof t + ? t + : function () { + this[t].apply(this, e); + }; + (this.prototype._initHooks = this.prototype._initHooks || []), this.prototype._initHooks.push(i); + }), + (o.Evented = o.Class.extend({ + on: function (t, e, i) { + if ("object" == typeof t) for (var n in t) this._on(n, t[n], e); + else { + t = o.Util.splitWords(t); + for (var s = 0, r = t.length; r > s; s++) this._on(t[s], e, i); + } + return this; + }, + off: function (t, e, i) { + if (t) + if ("object" == typeof t) for (var n in t) this._off(n, t[n], e); + else { + t = o.Util.splitWords(t); + for (var s = 0, r = t.length; r > s; s++) this._off(t[s], e, i); + } + else delete this._events; + return this; + }, + _on: function (t, e, i) { + var n = (this._events = this._events || {}), + s = i && i !== this && o.stamp(i); + if (s) { + var r = t + "_idx", + a = t + "_len", + h = (n[r] = n[r] || {}), + l = o.stamp(e) + "_" + s; + h[l] || ((h[l] = { fn: e, ctx: i }), (n[a] = (n[a] || 0) + 1)); + } else (n[t] = n[t] || []), n[t].push({ fn: e }); + }, + _off: function (t, e, i) { + var n = this._events, + s = t + "_idx", + r = t + "_len"; + if (n) { + if (!e) return delete n[t], delete n[s], void delete n[r]; + var a, + h, + l, + u, + c, + d = i && i !== this && o.stamp(i); + if (d) + (c = o.stamp(e) + "_" + d), + (a = n[s]), + a && a[c] && ((u = a[c]), delete a[c], n[r]--); + else if ((a = n[t])) + for (h = 0, l = a.length; l > h; h++) + if (a[h].fn === e) { + (u = a[h]), a.splice(h, 1); + break; + } + u && (u.fn = o.Util.falseFn); + } + }, + fire: function (t, e, i) { + if (!this.listens(t, i)) return this; + var n = o.Util.extend({}, e, { type: t, target: this }), + s = this._events; + if (s) { + var r, + a, + h, + l, + u = s[t + "_idx"]; + if (s[t]) for (h = s[t].slice(), r = 0, a = h.length; a > r; r++) h[r].fn.call(this, n); + for (l in u) u[l].fn.call(u[l].ctx, n); + } + return i && this._propagateEvent(n), this; + }, + listens: function (t, e) { + var i = this._events; + if (i && (i[t] || i[t + "_len"])) return !0; + if (e) for (var n in this._eventParents) if (this._eventParents[n].listens(t, e)) return !0; + return !1; + }, + once: function (t, e, i) { + if ("object" == typeof t) { + for (var n in t) this.once(n, t[n], e); + return this; + } + var s = o.bind(function () { + this.off(t, e, i).off(t, s, i); + }, this); + return this.on(t, e, i).on(t, s, i); + }, + addEventParent: function (t) { + return ( + (this._eventParents = this._eventParents || {}), + (this._eventParents[o.stamp(t)] = t), + this + ); + }, + removeEventParent: function (t) { + return this._eventParents && delete this._eventParents[o.stamp(t)], this; + }, + _propagateEvent: function (t) { + for (var e in this._eventParents) + this._eventParents[e].fire(t.type, o.extend({ layer: t.target }, t), !0); + }, + })); + var s = o.Evented.prototype; + (s.addEventListener = s.on), + (s.removeEventListener = s.clearAllEventListeners = s.off), + (s.addOneTimeEventListener = s.once), + (s.fireEvent = s.fire), + (s.hasEventListeners = s.listens), + (o.Mixin = { Events: s }), + (function () { + var i = navigator.userAgent.toLowerCase(), + n = e.documentElement, + s = "ActiveXObject" in t, + r = -1 !== i.indexOf("webkit"), + a = -1 !== i.indexOf("phantom"), + h = -1 !== i.search("android [23]"), + l = -1 !== i.indexOf("chrome"), + u = -1 !== i.indexOf("gecko") && !r && !t.opera && !s, + c = "undefined" != typeof orientation || -1 !== i.indexOf("mobile"), + d = !t.PointerEvent && t.MSPointerEvent, + _ = (t.PointerEvent && navigator.pointerEnabled) || d, + m = s && "transition" in n.style, + p = "WebKitCSSMatrix" in t && "m11" in new t.WebKitCSSMatrix() && !h, + f = "MozPerspective" in n.style, + g = "OTransition" in n.style, + v = + !t.L_NO_TOUCH && + !a && + (_ || "ontouchstart" in t || (t.DocumentTouch && e instanceof t.DocumentTouch)); + o.Browser = { + ie: s, + ielt9: s && !e.addEventListener, + webkit: r, + gecko: u, + android: -1 !== i.indexOf("android"), + android23: h, + chrome: l, + safari: !l && -1 !== i.indexOf("safari"), + ie3d: m, + webkit3d: p, + gecko3d: f, + opera12: g, + any3d: !t.L_DISABLE_3D && (m || p || f) && !g && !a, + mobile: c, + mobileWebkit: c && r, + mobileWebkit3d: c && p, + mobileOpera: c && t.opera, + mobileGecko: c && u, + touch: !!v, + msPointer: !!d, + pointer: !!_, + retina: (t.devicePixelRatio || t.screen.deviceXDPI / t.screen.logicalXDPI) > 1, + }; + })(), + (o.Point = function (t, e, i) { + (this.x = i ? Math.round(t) : t), (this.y = i ? Math.round(e) : e); + }), + (o.Point.prototype = { + clone: function () { + return new o.Point(this.x, this.y); + }, + add: function (t) { + return this.clone()._add(o.point(t)); + }, + _add: function (t) { + return (this.x += t.x), (this.y += t.y), this; + }, + subtract: function (t) { + return this.clone()._subtract(o.point(t)); + }, + _subtract: function (t) { + return (this.x -= t.x), (this.y -= t.y), this; + }, + divideBy: function (t) { + return this.clone()._divideBy(t); + }, + _divideBy: function (t) { + return (this.x /= t), (this.y /= t), this; + }, + multiplyBy: function (t) { + return this.clone()._multiplyBy(t); + }, + _multiplyBy: function (t) { + return (this.x *= t), (this.y *= t), this; + }, + scaleBy: function (t) { + return new o.Point(this.x * t.x, this.y * t.y); + }, + unscaleBy: function (t) { + return new o.Point(this.x / t.x, this.y / t.y); + }, + round: function () { + return this.clone()._round(); + }, + _round: function () { + return (this.x = Math.round(this.x)), (this.y = Math.round(this.y)), this; + }, + floor: function () { + return this.clone()._floor(); + }, + _floor: function () { + return (this.x = Math.floor(this.x)), (this.y = Math.floor(this.y)), this; + }, + ceil: function () { + return this.clone()._ceil(); + }, + _ceil: function () { + return (this.x = Math.ceil(this.x)), (this.y = Math.ceil(this.y)), this; + }, + distanceTo: function (t) { + t = o.point(t); + var e = t.x - this.x, + i = t.y - this.y; + return Math.sqrt(e * e + i * i); + }, + equals: function (t) { + return (t = o.point(t)), t.x === this.x && t.y === this.y; + }, + contains: function (t) { + return ( + (t = o.point(t)), Math.abs(t.x) <= Math.abs(this.x) && Math.abs(t.y) <= Math.abs(this.y) + ); + }, + toString: function () { + return "Point(" + o.Util.formatNum(this.x) + ", " + o.Util.formatNum(this.y) + ")"; + }, + }), + (o.point = function (t, e, n) { + return t instanceof o.Point + ? t + : o.Util.isArray(t) + ? new o.Point(t[0], t[1]) + : t === i || null === t + ? t + : new o.Point(t, e, n); + }), + (o.Bounds = function (t, e) { + if (t) for (var i = e ? [t, e] : t, n = 0, o = i.length; o > n; n++) this.extend(i[n]); + }), + (o.Bounds.prototype = { + extend: function (t) { + return ( + (t = o.point(t)), + this.min || this.max + ? ((this.min.x = Math.min(t.x, this.min.x)), + (this.max.x = Math.max(t.x, this.max.x)), + (this.min.y = Math.min(t.y, this.min.y)), + (this.max.y = Math.max(t.y, this.max.y))) + : ((this.min = t.clone()), (this.max = t.clone())), + this + ); + }, + getCenter: function (t) { + return new o.Point((this.min.x + this.max.x) / 2, (this.min.y + this.max.y) / 2, t); + }, + getBottomLeft: function () { + return new o.Point(this.min.x, this.max.y); + }, + getTopRight: function () { + return new o.Point(this.max.x, this.min.y); + }, + getSize: function () { + return this.max.subtract(this.min); + }, + contains: function (t) { + var e, i; + return ( + (t = "number" == typeof t[0] || t instanceof o.Point ? o.point(t) : o.bounds(t)), + t instanceof o.Bounds ? ((e = t.min), (i = t.max)) : (e = i = t), + e.x >= this.min.x && i.x <= this.max.x && e.y >= this.min.y && i.y <= this.max.y + ); + }, + intersects: function (t) { + t = o.bounds(t); + var e = this.min, + i = this.max, + n = t.min, + s = t.max, + r = s.x >= e.x && n.x <= i.x, + a = s.y >= e.y && n.y <= i.y; + return r && a; + }, + overlaps: function (t) { + t = o.bounds(t); + var e = this.min, + i = this.max, + n = t.min, + s = t.max, + r = s.x > e.x && n.x < i.x, + a = s.y > e.y && n.y < i.y; + return r && a; + }, + isValid: function () { + return !(!this.min || !this.max); + }, + }), + (o.bounds = function (t, e) { + return !t || t instanceof o.Bounds ? t : new o.Bounds(t, e); + }), + (o.Transformation = function (t, e, i, n) { + (this._a = t), (this._b = e), (this._c = i), (this._d = n); + }), + (o.Transformation.prototype = { + transform: function (t, e) { + return this._transform(t.clone(), e); + }, + _transform: function (t, e) { + return ( + (e = e || 1), + (t.x = e * (this._a * t.x + this._b)), + (t.y = e * (this._c * t.y + this._d)), + t + ); + }, + untransform: function (t, e) { + return ( + (e = e || 1), new o.Point((t.x / e - this._b) / this._a, (t.y / e - this._d) / this._c) + ); + }, + }), + (o.DomUtil = { + get: function (t) { + return "string" == typeof t ? e.getElementById(t) : t; + }, + getStyle: function (t, i) { + var n = t.style[i] || (t.currentStyle && t.currentStyle[i]); + if ((!n || "auto" === n) && e.defaultView) { + var o = e.defaultView.getComputedStyle(t, null); + n = o ? o[i] : null; + } + return "auto" === n ? null : n; + }, + create: function (t, i, n) { + var o = e.createElement(t); + return (o.className = i), n && n.appendChild(o), o; + }, + remove: function (t) { + var e = t.parentNode; + e && e.removeChild(t); + }, + empty: function (t) { + for (; t.firstChild; ) t.removeChild(t.firstChild); + }, + toFront: function (t) { + t.parentNode.appendChild(t); + }, + toBack: function (t) { + var e = t.parentNode; + e.insertBefore(t, e.firstChild); + }, + hasClass: function (t, e) { + if (t.classList !== i) return t.classList.contains(e); + var n = o.DomUtil.getClass(t); + return n.length > 0 && new RegExp("(^|\\s)" + e + "(\\s|$)").test(n); + }, + addClass: function (t, e) { + if (t.classList !== i) + for (var n = o.Util.splitWords(e), s = 0, r = n.length; r > s; s++) t.classList.add(n[s]); + else if (!o.DomUtil.hasClass(t, e)) { + var a = o.DomUtil.getClass(t); + o.DomUtil.setClass(t, (a ? a + " " : "") + e); + } + }, + removeClass: function (t, e) { + t.classList !== i + ? t.classList.remove(e) + : o.DomUtil.setClass( + t, + o.Util.trim((" " + o.DomUtil.getClass(t) + " ").replace(" " + e + " ", " ")) + ); + }, + setClass: function (t, e) { + t.className.baseVal === i ? (t.className = e) : (t.className.baseVal = e); + }, + getClass: function (t) { + return t.className.baseVal === i ? t.className : t.className.baseVal; + }, + setOpacity: function (t, e) { + "opacity" in t.style + ? (t.style.opacity = e) + : "filter" in t.style && o.DomUtil._setOpacityIE(t, e); + }, + _setOpacityIE: function (t, e) { + var i = !1, + n = "DXImageTransform.Microsoft.Alpha"; + try { + i = t.filters.item(n); + } catch (o) { + if (1 === e) return; + } + (e = Math.round(100 * e)), + i + ? ((i.Enabled = 100 !== e), (i.Opacity = e)) + : (t.style.filter += " progid:" + n + "(opacity=" + e + ")"); + }, + testProp: function (t) { + for (var i = e.documentElement.style, n = 0; n < t.length; n++) if (t[n] in i) return t[n]; + return !1; + }, + setTransform: function (t, e, i) { + var n = e || new o.Point(0, 0); + t.style[o.DomUtil.TRANSFORM] = + (o.Browser.ie3d + ? "translate(" + n.x + "px," + n.y + "px)" + : "translate3d(" + n.x + "px," + n.y + "px,0)") + (i ? " scale(" + i + ")" : ""); + }, + setPosition: function (t, e) { + (t._leaflet_pos = e), + o.Browser.any3d + ? o.DomUtil.setTransform(t, e) + : ((t.style.left = e.x + "px"), (t.style.top = e.y + "px")); + }, + getPosition: function (t) { + return t._leaflet_pos; + }, + }), + (function () { + o.DomUtil.TRANSFORM = o.DomUtil.testProp([ + "transform", + "WebkitTransform", + "OTransform", + "MozTransform", + "msTransform", + ]); + var i = (o.DomUtil.TRANSITION = o.DomUtil.testProp([ + "webkitTransition", + "transition", + "OTransition", + "MozTransition", + "msTransition", + ])); + if ( + ((o.DomUtil.TRANSITION_END = + "webkitTransition" === i || "OTransition" === i ? i + "End" : "transitionend"), + "onselectstart" in e) + ) + (o.DomUtil.disableTextSelection = function () { + o.DomEvent.on(t, "selectstart", o.DomEvent.preventDefault); + }), + (o.DomUtil.enableTextSelection = function () { + o.DomEvent.off(t, "selectstart", o.DomEvent.preventDefault); + }); + else { + var n = o.DomUtil.testProp([ + "userSelect", + "WebkitUserSelect", + "OUserSelect", + "MozUserSelect", + "msUserSelect", + ]); + (o.DomUtil.disableTextSelection = function () { + if (n) { + var t = e.documentElement.style; + (this._userSelect = t[n]), (t[n] = "none"); + } + }), + (o.DomUtil.enableTextSelection = function () { + n && ((e.documentElement.style[n] = this._userSelect), delete this._userSelect); + }); + } + (o.DomUtil.disableImageDrag = function () { + o.DomEvent.on(t, "dragstart", o.DomEvent.preventDefault); + }), + (o.DomUtil.enableImageDrag = function () { + o.DomEvent.off(t, "dragstart", o.DomEvent.preventDefault); + }), + (o.DomUtil.preventOutline = function (e) { + for (; -1 === e.tabIndex; ) e = e.parentNode; + e && + e.style && + (o.DomUtil.restoreOutline(), + (this._outlineElement = e), + (this._outlineStyle = e.style.outline), + (e.style.outline = "none"), + o.DomEvent.on(t, "keydown", o.DomUtil.restoreOutline, this)); + }), + (o.DomUtil.restoreOutline = function () { + this._outlineElement && + ((this._outlineElement.style.outline = this._outlineStyle), + delete this._outlineElement, + delete this._outlineStyle, + o.DomEvent.off(t, "keydown", o.DomUtil.restoreOutline, this)); + }); + })(), + (o.LatLng = function (t, e, n) { + if (isNaN(t) || isNaN(e)) throw new Error("Invalid LatLng object: (" + t + ", " + e + ")"); + (this.lat = +t), (this.lng = +e), n !== i && (this.alt = +n); + }), + (o.LatLng.prototype = { + equals: function (t, e) { + if (!t) return !1; + t = o.latLng(t); + var n = Math.max(Math.abs(this.lat - t.lat), Math.abs(this.lng - t.lng)); + return (e === i ? 1e-9 : e) >= n; + }, + toString: function (t) { + return "LatLng(" + o.Util.formatNum(this.lat, t) + ", " + o.Util.formatNum(this.lng, t) + ")"; + }, + distanceTo: function (t) { + return o.CRS.Earth.distance(this, o.latLng(t)); + }, + wrap: function () { + return o.CRS.Earth.wrapLatLng(this); + }, + toBounds: function (t) { + var e = (180 * t) / 40075017, + i = e / Math.cos((Math.PI / 180) * this.lat); + return o.latLngBounds([this.lat - e, this.lng - i], [this.lat + e, this.lng + i]); + }, + clone: function () { + return new o.LatLng(this.lat, this.lng, this.alt); + }, + }), + (o.latLng = function (t, e, n) { + return t instanceof o.LatLng + ? t + : o.Util.isArray(t) && "object" != typeof t[0] + ? 3 === t.length + ? new o.LatLng(t[0], t[1], t[2]) + : 2 === t.length + ? new o.LatLng(t[0], t[1]) + : null + : t === i || null === t + ? t + : "object" == typeof t && "lat" in t + ? new o.LatLng(t.lat, "lng" in t ? t.lng : t.lon, t.alt) + : e === i + ? null + : new o.LatLng(t, e, n); + }), + (o.LatLngBounds = function (t, e) { + if (t) for (var i = e ? [t, e] : t, n = 0, o = i.length; o > n; n++) this.extend(i[n]); + }), + (o.LatLngBounds.prototype = { + extend: function (t) { + var e, + i, + n = this._southWest, + s = this._northEast; + if (t instanceof o.LatLng) (e = t), (i = t); + else { + if (!(t instanceof o.LatLngBounds)) + return t ? this.extend(o.latLng(t) || o.latLngBounds(t)) : this; + if (((e = t._southWest), (i = t._northEast), !e || !i)) return this; + } + return ( + n || s + ? ((n.lat = Math.min(e.lat, n.lat)), + (n.lng = Math.min(e.lng, n.lng)), + (s.lat = Math.max(i.lat, s.lat)), + (s.lng = Math.max(i.lng, s.lng))) + : ((this._southWest = new o.LatLng(e.lat, e.lng)), + (this._northEast = new o.LatLng(i.lat, i.lng))), + this + ); + }, + pad: function (t) { + var e = this._southWest, + i = this._northEast, + n = Math.abs(e.lat - i.lat) * t, + s = Math.abs(e.lng - i.lng) * t; + return new o.LatLngBounds( + new o.LatLng(e.lat - n, e.lng - s), + new o.LatLng(i.lat + n, i.lng + s) + ); + }, + getCenter: function () { + return new o.LatLng( + (this._southWest.lat + this._northEast.lat) / 2, + (this._southWest.lng + this._northEast.lng) / 2 + ); + }, + getSouthWest: function () { + return this._southWest; + }, + getNorthEast: function () { + return this._northEast; + }, + getNorthWest: function () { + return new o.LatLng(this.getNorth(), this.getWest()); + }, + getSouthEast: function () { + return new o.LatLng(this.getSouth(), this.getEast()); + }, + getWest: function () { + return this._southWest.lng; + }, + getSouth: function () { + return this._southWest.lat; + }, + getEast: function () { + return this._northEast.lng; + }, + getNorth: function () { + return this._northEast.lat; + }, + contains: function (t) { + t = "number" == typeof t[0] || t instanceof o.LatLng ? o.latLng(t) : o.latLngBounds(t); + var e, + i, + n = this._southWest, + s = this._northEast; + return ( + t instanceof o.LatLngBounds + ? ((e = t.getSouthWest()), (i = t.getNorthEast())) + : (e = i = t), + e.lat >= n.lat && i.lat <= s.lat && e.lng >= n.lng && i.lng <= s.lng + ); + }, + intersects: function (t) { + t = o.latLngBounds(t); + var e = this._southWest, + i = this._northEast, + n = t.getSouthWest(), + s = t.getNorthEast(), + r = s.lat >= e.lat && n.lat <= i.lat, + a = s.lng >= e.lng && n.lng <= i.lng; + return r && a; + }, + overlaps: function (t) { + t = o.latLngBounds(t); + var e = this._southWest, + i = this._northEast, + n = t.getSouthWest(), + s = t.getNorthEast(), + r = s.lat > e.lat && n.lat < i.lat, + a = s.lng > e.lng && n.lng < i.lng; + return r && a; + }, + toBBoxString: function () { + return [this.getWest(), this.getSouth(), this.getEast(), this.getNorth()].join(","); + }, + equals: function (t) { + return t + ? ((t = o.latLngBounds(t)), + this._southWest.equals(t.getSouthWest()) && this._northEast.equals(t.getNorthEast())) + : !1; + }, + isValid: function () { + return !(!this._southWest || !this._northEast); + }, + }), + (o.latLngBounds = function (t, e) { + return !t || t instanceof o.LatLngBounds ? t : new o.LatLngBounds(t, e); + }), + (o.Projection = {}), + (o.Projection.LonLat = { + project: function (t) { + return new o.Point(t.lng, t.lat); + }, + unproject: function (t) { + return new o.LatLng(t.y, t.x); + }, + bounds: o.bounds([-180, -90], [180, 90]), + }), + (o.Projection.SphericalMercator = { + R: 6378137, + MAX_LATITUDE: 85.0511287798, + project: function (t) { + var e = Math.PI / 180, + i = this.MAX_LATITUDE, + n = Math.max(Math.min(i, t.lat), -i), + s = Math.sin(n * e); + return new o.Point(this.R * t.lng * e, (this.R * Math.log((1 + s) / (1 - s))) / 2); + }, + unproject: function (t) { + var e = 180 / Math.PI; + return new o.LatLng( + (2 * Math.atan(Math.exp(t.y / this.R)) - Math.PI / 2) * e, + (t.x * e) / this.R + ); + }, + bounds: (function () { + var t = 6378137 * Math.PI; + return o.bounds([-t, -t], [t, t]); + })(), + }), + (o.CRS = { + latLngToPoint: function (t, e) { + var i = this.projection.project(t), + n = this.scale(e); + return this.transformation._transform(i, n); + }, + pointToLatLng: function (t, e) { + var i = this.scale(e), + n = this.transformation.untransform(t, i); + return this.projection.unproject(n); + }, + project: function (t) { + return this.projection.project(t); + }, + unproject: function (t) { + return this.projection.unproject(t); + }, + scale: function (t) { + return 256 * Math.pow(2, t); + }, + zoom: function (t) { + return Math.log(t / 256) / Math.LN2; + }, + getProjectedBounds: function (t) { + if (this.infinite) return null; + var e = this.projection.bounds, + i = this.scale(t), + n = this.transformation.transform(e.min, i), + s = this.transformation.transform(e.max, i); + return o.bounds(n, s); + }, + wrapLatLng: function (t) { + var e = this.wrapLng ? o.Util.wrapNum(t.lng, this.wrapLng, !0) : t.lng, + i = this.wrapLat ? o.Util.wrapNum(t.lat, this.wrapLat, !0) : t.lat, + n = t.alt; + return o.latLng(i, e, n); + }, + }), + (o.CRS.Simple = o.extend({}, o.CRS, { + projection: o.Projection.LonLat, + transformation: new o.Transformation(1, 0, -1, 0), + scale: function (t) { + return Math.pow(2, t); + }, + zoom: function (t) { + return Math.log(t) / Math.LN2; + }, + distance: function (t, e) { + var i = e.lng - t.lng, + n = e.lat - t.lat; + return Math.sqrt(i * i + n * n); + }, + infinite: !0, + })), + (o.CRS.Earth = o.extend({}, o.CRS, { + wrapLng: [-180, 180], + R: 6378137, + distance: function (t, e) { + var i = Math.PI / 180, + n = t.lat * i, + o = e.lat * i, + s = Math.sin(n) * Math.sin(o) + Math.cos(n) * Math.cos(o) * Math.cos((e.lng - t.lng) * i); + return this.R * Math.acos(Math.min(s, 1)); + }, + })), + (o.CRS.EPSG3857 = o.extend({}, o.CRS.Earth, { + code: "EPSG:3857", + projection: o.Projection.SphericalMercator, + transformation: (function () { + var t = 0.5 / (Math.PI * o.Projection.SphericalMercator.R); + return new o.Transformation(t, 0.5, -t, 0.5); + })(), + })), + (o.CRS.EPSG900913 = o.extend({}, o.CRS.EPSG3857, { code: "EPSG:900913" })), + (o.CRS.EPSG4326 = o.extend({}, o.CRS.Earth, { + code: "EPSG:4326", + projection: o.Projection.LonLat, + transformation: new o.Transformation(1 / 180, 1, -1 / 180, 0.5), + })), + (o.Map = o.Evented.extend({ + options: { + crs: o.CRS.EPSG3857, + fadeAnimation: !0, + trackResize: !0, + markerZoomAnimation: !0, + maxBoundsViscosity: 0, + transform3DLimit: 8388608, + }, + initialize: function (t, e) { + (e = o.setOptions(this, e)), + this._initContainer(t), + this._initLayout(), + (this._onResize = o.bind(this._onResize, this)), + this._initEvents(), + e.maxBounds && this.setMaxBounds(e.maxBounds), + e.zoom !== i && (this._zoom = this._limitZoom(e.zoom)), + e.center && e.zoom !== i && this.setView(o.latLng(e.center), e.zoom, { reset: !0 }), + (this._handlers = []), + (this._layers = {}), + (this._zoomBoundLayers = {}), + (this._sizeChanged = !0), + this.callInitHooks(), + this._addLayers(this.options.layers); + }, + setView: function (t, e) { + return (e = e === i ? this.getZoom() : e), this._resetView(o.latLng(t), e), this; + }, + setZoom: function (t, e) { + return this._loaded + ? this.setView(this.getCenter(), t, { zoom: e }) + : ((this._zoom = t), this); + }, + zoomIn: function (t, e) { + return this.setZoom(this._zoom + (t || 1), e); + }, + zoomOut: function (t, e) { + return this.setZoom(this._zoom - (t || 1), e); + }, + setZoomAround: function (t, e, i) { + var n = this.getZoomScale(e), + s = this.getSize().divideBy(2), + r = t instanceof o.Point ? t : this.latLngToContainerPoint(t), + a = r.subtract(s).multiplyBy(1 - 1 / n), + h = this.containerPointToLatLng(s.add(a)); + return this.setView(h, e, { zoom: i }); + }, + _getBoundsCenterZoom: function (t, e) { + (e = e || {}), (t = t.getBounds ? t.getBounds() : o.latLngBounds(t)); + var i = o.point(e.paddingTopLeft || e.padding || [0, 0]), + n = o.point(e.paddingBottomRight || e.padding || [0, 0]), + s = this.getBoundsZoom(t, !1, i.add(n)); + s = e.maxZoom ? Math.min(e.maxZoom, s) : s; + var r = n.subtract(i).divideBy(2), + a = this.project(t.getSouthWest(), s), + h = this.project(t.getNorthEast(), s), + l = this.unproject(a.add(h).divideBy(2).add(r), s); + return { center: l, zoom: s }; + }, + fitBounds: function (t, e) { + var i = this._getBoundsCenterZoom(t, e); + return this.setView(i.center, i.zoom, e); + }, + fitWorld: function (t) { + return this.fitBounds( + [ + [-90, -180], + [90, 180], + ], + t + ); + }, + panTo: function (t, e) { + return this.setView(t, this._zoom, { pan: e }); + }, + panBy: function (t) { + return ( + this.fire("movestart"), + this._rawPanBy(o.point(t)), + this.fire("move"), + this.fire("moveend") + ); + }, + setMaxBounds: function (t) { + return (t = o.latLngBounds(t)) + ? (this.options.maxBounds && this.off("moveend", this._panInsideMaxBounds), + (this.options.maxBounds = t), + this._loaded && this._panInsideMaxBounds(), + this.on("moveend", this._panInsideMaxBounds)) + : this.off("moveend", this._panInsideMaxBounds); + }, + setMinZoom: function (t) { + return ( + (this.options.minZoom = t), + this._loaded && this.getZoom() < this.options.minZoom ? this.setZoom(t) : this + ); + }, + setMaxZoom: function (t) { + return ( + (this.options.maxZoom = t), + this._loaded && this.getZoom() > this.options.maxZoom ? this.setZoom(t) : this + ); + }, + panInsideBounds: function (t, e) { + this._enforcingBounds = !0; + var i = this.getCenter(), + n = this._limitCenter(i, this._zoom, o.latLngBounds(t)); + return i.equals(n) ? this : (this.panTo(n, e), (this._enforcingBounds = !1), this); + }, + invalidateSize: function (t) { + if (!this._loaded) return this; + t = o.extend({ animate: !1, pan: !0 }, t === !0 ? { animate: !0 } : t); + var e = this.getSize(); + (this._sizeChanged = !0), (this._lastCenter = null); + var i = this.getSize(), + n = e.divideBy(2).round(), + s = i.divideBy(2).round(), + r = n.subtract(s); + return r.x || r.y + ? (t.animate && t.pan + ? this.panBy(r) + : (t.pan && this._rawPanBy(r), + this.fire("move"), + t.debounceMoveend + ? (clearTimeout(this._sizeTimer), + (this._sizeTimer = setTimeout(o.bind(this.fire, this, "moveend"), 200))) + : this.fire("moveend")), + this.fire("resize", { oldSize: e, newSize: i })) + : this; + }, + stop: function () { + return o.Util.cancelAnimFrame(this._flyToFrame), this._panAnim && this._panAnim.stop(), this; + }, + addHandler: function (t, e) { + if (!e) return this; + var i = (this[t] = new e(this)); + return this._handlers.push(i), this.options[t] && i.enable(), this; + }, + remove: function () { + this._initEvents(!0); + try { + delete this._container._leaflet; + } catch (t) { + this._container._leaflet = i; + } + o.DomUtil.remove(this._mapPane), + this._clearControlPos && this._clearControlPos(), + this._clearHandlers(), + this._loaded && this.fire("unload"); + for (var e in this._layers) this._layers[e].remove(); + return this; + }, + createPane: function (t, e) { + var i = "leaflet-pane" + (t ? " leaflet-" + t.replace("Pane", "") + "-pane" : ""), + n = o.DomUtil.create("div", i, e || this._mapPane); + return t && (this._panes[t] = n), n; + }, + getCenter: function () { + return ( + this._checkIfLoaded(), + this._lastCenter && !this._moved() + ? this._lastCenter + : this.layerPointToLatLng(this._getCenterLayerPoint()) + ); + }, + getZoom: function () { + return this._zoom; + }, + getBounds: function () { + var t = this.getPixelBounds(), + e = this.unproject(t.getBottomLeft()), + i = this.unproject(t.getTopRight()); + return new o.LatLngBounds(e, i); + }, + getMinZoom: function () { + return this.options.minZoom === i ? this._layersMinZoom || 0 : this.options.minZoom; + }, + getMaxZoom: function () { + return this.options.maxZoom === i + ? this._layersMaxZoom === i + ? 1 / 0 + : this._layersMaxZoom + : this.options.maxZoom; + }, + getBoundsZoom: function (t, e, i) { + t = o.latLngBounds(t); + var n, + s = this.getMinZoom() - (e ? 1 : 0), + r = this.getMaxZoom(), + a = this.getSize(), + h = t.getNorthWest(), + l = t.getSouthEast(), + u = !0; + i = o.point(i || [0, 0]); + do + s++, + (n = this.project(l, s).subtract(this.project(h, s)).add(i).floor()), + (u = e ? n.x < a.x || n.y < a.y : a.contains(n)); + while (u && r >= s); + return u && e ? null : e ? s : s - 1; + }, + getSize: function () { + return ( + (!this._size || this._sizeChanged) && + ((this._size = new o.Point( + this._container.clientWidth, + this._container.clientHeight + )), + (this._sizeChanged = !1)), + this._size.clone() + ); + }, + getPixelBounds: function (t, e) { + var i = this._getTopLeftPoint(t, e); + return new o.Bounds(i, i.add(this.getSize())); + }, + getPixelOrigin: function () { + return this._checkIfLoaded(), this._pixelOrigin; + }, + getPixelWorldBounds: function (t) { + return this.options.crs.getProjectedBounds(t === i ? this.getZoom() : t); + }, + getPane: function (t) { + return "string" == typeof t ? this._panes[t] : t; + }, + getPanes: function () { + return this._panes; + }, + getContainer: function () { + return this._container; + }, + getZoomScale: function (t, e) { + var n = this.options.crs; + return (e = e === i ? this._zoom : e), n.scale(t) / n.scale(e); + }, + getScaleZoom: function (t, e) { + var n = this.options.crs; + return (e = e === i ? this._zoom : e), n.zoom(t * n.scale(e)); + }, + project: function (t, e) { + return (e = e === i ? this._zoom : e), this.options.crs.latLngToPoint(o.latLng(t), e); + }, + unproject: function (t, e) { + return (e = e === i ? this._zoom : e), this.options.crs.pointToLatLng(o.point(t), e); + }, + layerPointToLatLng: function (t) { + var e = o.point(t).add(this.getPixelOrigin()); + return this.unproject(e); + }, + latLngToLayerPoint: function (t) { + var e = this.project(o.latLng(t))._round(); + return e._subtract(this.getPixelOrigin()); + }, + wrapLatLng: function (t) { + return this.options.crs.wrapLatLng(o.latLng(t)); + }, + distance: function (t, e) { + return this.options.crs.distance(o.latLng(t), o.latLng(e)); + }, + containerPointToLayerPoint: function (t) { + return o.point(t).subtract(this._getMapPanePos()); + }, + layerPointToContainerPoint: function (t) { + return o.point(t).add(this._getMapPanePos()); + }, + containerPointToLatLng: function (t) { + var e = this.containerPointToLayerPoint(o.point(t)); + return this.layerPointToLatLng(e); + }, + latLngToContainerPoint: function (t) { + return this.layerPointToContainerPoint(this.latLngToLayerPoint(o.latLng(t))); + }, + mouseEventToContainerPoint: function (t) { + return o.DomEvent.getMousePosition(t, this._container); + }, + mouseEventToLayerPoint: function (t) { + return this.containerPointToLayerPoint(this.mouseEventToContainerPoint(t)); + }, + mouseEventToLatLng: function (t) { + return this.layerPointToLatLng(this.mouseEventToLayerPoint(t)); + }, + _initContainer: function (t) { + var e = (this._container = o.DomUtil.get(t)); + if (!e) throw new Error("Map container not found."); + if (e._leaflet) throw new Error("Map container is already initialized."); + o.DomEvent.addListener(e, "scroll", this._onScroll, this), (e._leaflet = !0); + }, + _initLayout: function () { + var t = this._container; + (this._fadeAnimated = this.options.fadeAnimation && o.Browser.any3d), + o.DomUtil.addClass( + t, + "leaflet-container" + + (o.Browser.touch ? " leaflet-touch" : "") + + (o.Browser.retina ? " leaflet-retina" : "") + + (o.Browser.ielt9 ? " leaflet-oldie" : "") + + (o.Browser.safari ? " leaflet-safari" : "") + + (this._fadeAnimated ? " leaflet-fade-anim" : "") + ); + var e = o.DomUtil.getStyle(t, "position"); + "absolute" !== e && "relative" !== e && "fixed" !== e && (t.style.position = "relative"), + this._initPanes(), + this._initControlPos && this._initControlPos(); + }, + _initPanes: function () { + var t = (this._panes = {}); + (this._paneRenderers = {}), + (this._mapPane = this.createPane("mapPane", this._container)), + o.DomUtil.setPosition(this._mapPane, new o.Point(0, 0)), + this.createPane("tilePane"), + this.createPane("shadowPane"), + this.createPane("overlayPane"), + this.createPane("markerPane"), + this.createPane("popupPane"), + this.options.markerZoomAnimation || + (o.DomUtil.addClass(t.markerPane, "leaflet-zoom-hide"), + o.DomUtil.addClass(t.shadowPane, "leaflet-zoom-hide")); + }, + _resetView: function (t, e) { + o.DomUtil.setPosition(this._mapPane, new o.Point(0, 0)); + var i = !this._loaded; + (this._loaded = !0), (e = this._limitZoom(e)); + var n = this._zoom !== e; + this._moveStart(n)._move(t, e)._moveEnd(n), this.fire("viewreset"), i && this.fire("load"); + }, + _moveStart: function (t) { + return t && this.fire("zoomstart"), this.fire("movestart"); + }, + _move: function (t, e, n) { + e === i && (e = this._zoom); + var o = this._zoom !== e; + return ( + (this._zoom = e), + (this._lastCenter = t), + (this._pixelOrigin = this._getNewPixelOrigin(t)), + o && this.fire("zoom", n), + this.fire("move", n) + ); + }, + _moveEnd: function (t) { + return t && this.fire("zoomend"), this.fire("moveend"); + }, + _rawPanBy: function (t) { + o.DomUtil.setPosition(this._mapPane, this._getMapPanePos().subtract(t)); + }, + _getZoomSpan: function () { + return this.getMaxZoom() - this.getMinZoom(); + }, + _panInsideMaxBounds: function () { + this._enforcingBounds || this.panInsideBounds(this.options.maxBounds); + }, + _checkIfLoaded: function () { + if (!this._loaded) throw new Error("Set map center and zoom first."); + }, + _initEvents: function (e) { + if (o.DomEvent) { + (this._targets = {}), (this._targets[o.stamp(this._container)] = this); + var i = e ? "off" : "on"; + o.DomEvent[i]( + this._container, + "click dblclick mousedown mouseup mouseover mouseout mousemove contextmenu keypress", + this._handleDOMEvent, + this + ), + this.options.trackResize && o.DomEvent[i](t, "resize", this._onResize, this), + o.Browser.any3d && + this.options.transform3DLimit && + this[i]("moveend", this._onMoveEnd); + } + }, + _onResize: function () { + o.Util.cancelAnimFrame(this._resizeRequest), + (this._resizeRequest = o.Util.requestAnimFrame(function () { + this.invalidateSize({ debounceMoveend: !0 }); + }, this)); + }, + _onScroll: function () { + (this._container.scrollTop = 0), (this._container.scrollLeft = 0); + }, + _onMoveEnd: function () { + var t = this._getMapPanePos(); + Math.max(Math.abs(t.x), Math.abs(t.y)) >= this.options.transform3DLimit && + this._resetView(this.getCenter(), this.getZoom()); + }, + _findEventTargets: function (t, e) { + for ( + var i, n = [], s = "mouseout" === e || "mouseover" === e, r = t.target || t.srcElement; + r; - function s() { if (h && !a.cancelBubble) { if (o.Browser.pointer) { var t, i, n = {}; for (i in a) t = a[i], n[i] = t && t.bind ? t.bind(a) : t; - a = n } - a.type = "dblclick", e(a), r = null } } var r, a, h = !1, - l = 250, - u = "_leaflet_", - c = this._touchstart, - d = this._touchend; return t[u + c + i] = n, t[u + d + i] = s, t.addEventListener(c, n, !1), t.addEventListener(d, s, !1), this }, removeDoubleTapListener: function(t, e) { var i = "_leaflet_", - n = t[i + this._touchend + e]; return t.removeEventListener(this._touchstart, t[i + this._touchstart + e], !1), t.removeEventListener(this._touchend, n, !1), this } }), o.extend(o.DomEvent, { POINTER_DOWN: o.Browser.msPointer ? "MSPointerDown" : "pointerdown", POINTER_MOVE: o.Browser.msPointer ? "MSPointerMove" : "pointermove", POINTER_UP: o.Browser.msPointer ? "MSPointerUp" : "pointerup", POINTER_CANCEL: o.Browser.msPointer ? "MSPointerCancel" : "pointercancel", _pointers: {}, _pointersCount: 0, addPointerListener: function(t, e, i, n) { return "touchstart" === e ? this._addPointerStart(t, i, n) : "touchmove" === e ? this._addPointerMove(t, i, n) : "touchend" === e && this._addPointerEnd(t, i, n), this }, removePointerListener: function(t, e, i) { var n = t["_leaflet_" + e + i]; return "touchstart" === e ? t.removeEventListener(this.POINTER_DOWN, n, !1) : "touchmove" === e ? t.removeEventListener(this.POINTER_MOVE, n, !1) : "touchend" === e && (t.removeEventListener(this.POINTER_UP, n, !1), t.removeEventListener(this.POINTER_CANCEL, n, !1)), this }, _addPointerStart: function(t, i, n) { var s = o.bind(function(t) { "mouse" !== t.pointerType && t.pointerType !== t.MSPOINTER_TYPE_MOUSE && o.DomEvent.preventDefault(t), this._handlePointer(t, i) }, this); if (t["_leaflet_touchstart" + n] = s, t.addEventListener(this.POINTER_DOWN, s, !1), !this._pointerDocListener) { var r = o.bind(this._globalPointerUp, this); - e.documentElement.addEventListener(this.POINTER_DOWN, o.bind(this._globalPointerDown, this), !0), e.documentElement.addEventListener(this.POINTER_MOVE, o.bind(this._globalPointerMove, this), !0), e.documentElement.addEventListener(this.POINTER_UP, r, !0), e.documentElement.addEventListener(this.POINTER_CANCEL, r, !0), this._pointerDocListener = !0 } }, _globalPointerDown: function(t) { this._pointers[t.pointerId] = t, this._pointersCount++ }, _globalPointerMove: function(t) { this._pointers[t.pointerId] && (this._pointers[t.pointerId] = t) }, _globalPointerUp: function(t) { delete this._pointers[t.pointerId], this._pointersCount-- }, _handlePointer: function(t, e) { t.touches = []; for (var i in this._pointers) t.touches.push(this._pointers[i]); - t.changedTouches = [t], e(t) }, _addPointerMove: function(t, e, i) { var n = o.bind(function(t) { - (t.pointerType !== t.MSPOINTER_TYPE_MOUSE && "mouse" !== t.pointerType || 0 !== t.buttons) && this._handlePointer(t, e) }, this); - t["_leaflet_touchmove" + i] = n, t.addEventListener(this.POINTER_MOVE, n, !1) }, _addPointerEnd: function(t, e, i) { var n = o.bind(function(t) { this._handlePointer(t, e) }, this); - t["_leaflet_touchend" + i] = n, t.addEventListener(this.POINTER_UP, n, !1), t.addEventListener(this.POINTER_CANCEL, n, !1) } }), o.Map.mergeOptions({ touchZoom: o.Browser.touch && !o.Browser.android23, bounceAtZoomLimits: !0 }), o.Map.TouchZoom = o.Handler.extend({ addHooks: function() { o.DomEvent.on(this._map._container, "touchstart", this._onTouchStart, this) }, removeHooks: function() { o.DomEvent.off(this._map._container, "touchstart", this._onTouchStart, this) }, _onTouchStart: function(t) { var i = this._map; if (t.touches && 2 === t.touches.length && !i._animatingZoom && !this._zooming) { var n = i.mouseEventToContainerPoint(t.touches[0]), - s = i.mouseEventToContainerPoint(t.touches[1]); - this._centerPoint = i.getSize()._divideBy(2), this._startLatLng = i.containerPointToLatLng(this._centerPoint), "center" !== i.options.touchZoom && (this._pinchStartLatLng = i.containerPointToLatLng(n.add(s)._divideBy(2))), this._startDist = n.distanceTo(s), this._startZoom = i.getZoom(), this._moved = !1, this._zooming = !0, i.stop(), o.DomEvent.on(e, "touchmove", this._onTouchMove, this).on(e, "touchend", this._onTouchEnd, this), o.DomEvent.preventDefault(t) } }, _onTouchMove: function(t) { if (t.touches && 2 === t.touches.length && this._zooming) { var e = this._map, - i = e.mouseEventToContainerPoint(t.touches[0]), - n = e.mouseEventToContainerPoint(t.touches[1]), - s = i.distanceTo(n) / this._startDist; if (this._zoom = e.getScaleZoom(s, this._startZoom), "center" === e.options.touchZoom) { if (this._center = this._startLatLng, 1 === s) return } else { var r = i._add(n)._divideBy(2)._subtract(this._centerPoint); if (1 === s && 0 === r.x && 0 === r.y) return; - this._center = e.unproject(e.project(this._pinchStartLatLng).subtract(r)) } if (e.options.bounceAtZoomLimits || !(this._zoom <= e.getMinZoom() && 1 > s || this._zoom >= e.getMaxZoom() && s > 1)) { this._moved || (e._moveStart(!0), this._moved = !0), o.Util.cancelAnimFrame(this._animRequest); var a = o.bind(e._move, e, this._center, this._zoom, { pinch: !0, round: !1 }); - this._animRequest = o.Util.requestAnimFrame(a, this, !0), o.DomEvent.preventDefault(t) } } }, _onTouchEnd: function() { if (!this._moved || !this._zooming) return void(this._zooming = !1); - this._zooming = !1, o.Util.cancelAnimFrame(this._animRequest), o.DomEvent.off(e, "touchmove", this._onTouchMove).off(e, "touchend", this._onTouchEnd); var t = this._zoom; - t = this._map._limitZoom(t - this._startZoom > 0 ? Math.ceil(t) : Math.floor(t)), this._map._animateZoom(this._center, t, !0, !0) } }), o.Map.addInitHook("addHandler", "touchZoom", o.Map.TouchZoom), o.Map.mergeOptions({ tap: !0, tapTolerance: 15 }), o.Map.Tap = o.Handler.extend({ addHooks: function() { o.DomEvent.on(this._map._container, "touchstart", this._onDown, this) }, removeHooks: function() { o.DomEvent.off(this._map._container, "touchstart", this._onDown, this) }, _onDown: function(t) { if (t.touches) { if (o.DomEvent.preventDefault(t), this._fireClick = !0, t.touches.length > 1) return this._fireClick = !1, void clearTimeout(this._holdTimeout); var i = t.touches[0], - n = i.target; - this._startPos = this._newPos = new o.Point(i.clientX, i.clientY), n.tagName && "a" === n.tagName.toLowerCase() && o.DomUtil.addClass(n, "leaflet-active"), this._holdTimeout = setTimeout(o.bind(function() { this._isTapValid() && (this._fireClick = !1, this._onUp(), this._simulateEvent("contextmenu", i)) }, this), 1e3), this._simulateEvent("mousedown", i), o.DomEvent.on(e, { touchmove: this._onMove, touchend: this._onUp }, this) } }, _onUp: function(t) { if (clearTimeout(this._holdTimeout), o.DomEvent.off(e, { touchmove: this._onMove, touchend: this._onUp }, this), this._fireClick && t && t.changedTouches) { var i = t.changedTouches[0], - n = i.target; - n && n.tagName && "a" === n.tagName.toLowerCase() && o.DomUtil.removeClass(n, "leaflet-active"), this._simulateEvent("mouseup", i), this._isTapValid() && this._simulateEvent("click", i) } }, _isTapValid: function() { return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance }, _onMove: function(t) { var e = t.touches[0]; - this._newPos = new o.Point(e.clientX, e.clientY), this._simulateEvent("mousemove", e) }, _simulateEvent: function(i, n) { var o = e.createEvent("MouseEvents"); - o._simulated = !0, n.target._simulatedClick = !0, o.initMouseEvent(i, !0, !0, t, 1, n.screenX, n.screenY, n.clientX, n.clientY, !1, !1, !1, !1, 0, null), n.target.dispatchEvent(o) } }), o.Browser.touch && !o.Browser.pointer && o.Map.addInitHook("addHandler", "tap", o.Map.Tap), o.Map.mergeOptions({ boxZoom: !0 }), o.Map.BoxZoom = o.Handler.extend({ initialize: function(t) { this._map = t, this._container = t._container, this._pane = t._panes.overlayPane }, addHooks: function() { o.DomEvent.on(this._container, "mousedown", this._onMouseDown, this) }, removeHooks: function() { o.DomEvent.off(this._container, "mousedown", this._onMouseDown, this) }, moved: function() { return this._moved }, _resetState: function() { this._moved = !1 }, _onMouseDown: function(t) { return !t.shiftKey || 1 !== t.which && 1 !== t.button ? !1 : (this._resetState(), o.DomUtil.disableTextSelection(), o.DomUtil.disableImageDrag(), this._startPoint = this._map.mouseEventToContainerPoint(t), void o.DomEvent.on(e, { contextmenu: o.DomEvent.stop, mousemove: this._onMouseMove, mouseup: this._onMouseUp, keydown: this._onKeyDown }, this)) }, _onMouseMove: function(t) { this._moved || (this._moved = !0, this._box = o.DomUtil.create("div", "leaflet-zoom-box", this._container), o.DomUtil.addClass(this._container, "leaflet-crosshair"), this._map.fire("boxzoomstart")), this._point = this._map.mouseEventToContainerPoint(t); var e = new o.Bounds(this._point, this._startPoint), - i = e.getSize(); - o.DomUtil.setPosition(this._box, e.min), this._box.style.width = i.x + "px", this._box.style.height = i.y + "px" }, _finish: function() { this._moved && (o.DomUtil.remove(this._box), o.DomUtil.removeClass(this._container, "leaflet-crosshair")), o.DomUtil.enableTextSelection(), o.DomUtil.enableImageDrag(), o.DomEvent.off(e, { contextmenu: o.DomEvent.stop, mousemove: this._onMouseMove, mouseup: this._onMouseUp, keydown: this._onKeyDown }, this) }, _onMouseUp: function(t) { if ((1 === t.which || 1 === t.button) && (this._finish(), this._moved)) { setTimeout(o.bind(this._resetState, this), 0); var e = new o.LatLngBounds(this._map.containerPointToLatLng(this._startPoint), this._map.containerPointToLatLng(this._point)); - this._map.fitBounds(e).fire("boxzoomend", { boxZoomBounds: e }) } }, _onKeyDown: function(t) { 27 === t.keyCode && this._finish() } }), o.Map.addInitHook("addHandler", "boxZoom", o.Map.BoxZoom), o.Map.mergeOptions({ keyboard: !0, keyboardPanOffset: 80, keyboardZoomOffset: 1 }), o.Map.Keyboard = o.Handler.extend({ keyCodes: { left: [37], right: [39], down: [40], up: [38], zoomIn: [187, 107, 61, 171], zoomOut: [189, 109, 54, 173] }, initialize: function(t) { this._map = t, this._setPanOffset(t.options.keyboardPanOffset), this._setZoomOffset(t.options.keyboardZoomOffset) }, addHooks: function() { var t = this._map._container; - t.tabIndex <= 0 && (t.tabIndex = "0"), o.DomEvent.on(t, { focus: this._onFocus, blur: this._onBlur, mousedown: this._onMouseDown }, this), this._map.on({ focus: this._addHooks, blur: this._removeHooks }, this) }, removeHooks: function() { this._removeHooks(), o.DomEvent.off(this._map._container, { focus: this._onFocus, blur: this._onBlur, mousedown: this._onMouseDown }, this), this._map.off({ focus: this._addHooks, blur: this._removeHooks }, this) }, _onMouseDown: function() { if (!this._focused) { var i = e.body, - n = e.documentElement, - o = i.scrollTop || n.scrollTop, - s = i.scrollLeft || n.scrollLeft; - this._map._container.focus(), t.scrollTo(s, o) } }, _onFocus: function() { this._focused = !0, this._map.fire("focus") }, _onBlur: function() { this._focused = !1, this._map.fire("blur") }, _setPanOffset: function(t) { var e, i, n = this._panKeys = {}, - o = this.keyCodes; for (e = 0, i = o.left.length; i > e; e++) n[o.left[e]] = [-1 * t, 0]; for (e = 0, i = o.right.length; i > e; e++) n[o.right[e]] = [t, 0]; for (e = 0, i = o.down.length; i > e; e++) n[o.down[e]] = [0, t]; for (e = 0, i = o.up.length; i > e; e++) n[o.up[e]] = [0, -1 * t] }, _setZoomOffset: function(t) { var e, i, n = this._zoomKeys = {}, - o = this.keyCodes; for (e = 0, i = o.zoomIn.length; i > e; e++) n[o.zoomIn[e]] = t; for (e = 0, i = o.zoomOut.length; i > e; e++) n[o.zoomOut[e]] = -t }, _addHooks: function() { o.DomEvent.on(e, "keydown", this._onKeyDown, this) }, _removeHooks: function() { o.DomEvent.off(e, "keydown", this._onKeyDown, this) }, _onKeyDown: function(t) { if (!(t.altKey || t.ctrlKey || t.metaKey)) { var e, i = t.keyCode, - n = this._map; if (i in this._panKeys) { if (n._panAnim && n._panAnim._inProgress) return; - e = this._panKeys[i], t.shiftKey && (e = o.point(e).multiplyBy(3)), n.panBy(e), n.options.maxBounds && n.panInsideBounds(n.options.maxBounds) } else if (i in this._zoomKeys) n.setZoom(n.getZoom() + (t.shiftKey ? 3 : 1) * this._zoomKeys[i]); - else { if (27 !== i) return; - n.closePopup() } - o.DomEvent.stop(t) } } }), o.Map.addInitHook("addHandler", "keyboard", o.Map.Keyboard), o.Handler.MarkerDrag = o.Handler.extend({ initialize: function(t) { this._marker = t }, addHooks: function() { var t = this._marker._icon; - this._draggable || (this._draggable = new o.Draggable(t, t, !0)), this._draggable.on({ dragstart: this._onDragStart, drag: this._onDrag, dragend: this._onDragEnd }, this).enable(), o.DomUtil.addClass(t, "leaflet-marker-draggable") }, removeHooks: function() { this._draggable.off({ dragstart: this._onDragStart, drag: this._onDrag, dragend: this._onDragEnd }, this).disable(), this._marker._icon && o.DomUtil.removeClass(this._marker._icon, "leaflet-marker-draggable") }, moved: function() { return this._draggable && this._draggable._moved }, _onDragStart: function() { this._marker.closePopup().fire("movestart").fire("dragstart") }, _onDrag: function(t) { var e = this._marker, - i = e._shadow, - n = o.DomUtil.getPosition(e._icon), - s = e._map.layerPointToLatLng(n); - i && o.DomUtil.setPosition(i, n), e._latlng = s, t.latlng = s, e.fire("move", t).fire("drag", t) }, _onDragEnd: function(t) { this._marker.fire("moveend").fire("dragend", t) } }), o.Control = o.Class.extend({ options: { position: "topright" }, initialize: function(t) { o.setOptions(this, t) }, getPosition: function() { return this.options.position }, setPosition: function(t) { var e = this._map; return e && e.removeControl(this), this.options.position = t, e && e.addControl(this), this }, getContainer: function() { return this._container }, addTo: function(t) { this.remove(), this._map = t; var e = this._container = this.onAdd(t), - i = this.getPosition(), - n = t._controlCorners[i]; return o.DomUtil.addClass(e, "leaflet-control"), -1 !== i.indexOf("bottom") ? n.insertBefore(e, n.firstChild) : n.appendChild(e), this }, remove: function() { return this._map ? (o.DomUtil.remove(this._container), this.onRemove && this.onRemove(this._map), this._map = null, this) : this }, _refocusOnMap: function(t) { this._map && t && t.screenX > 0 && t.screenY > 0 && this._map.getContainer().focus() } }), o.control = function(t) { return new o.Control(t) }, o.Map.include({ addControl: function(t) { return t.addTo(this), this }, removeControl: function(t) { return t.remove(), this }, _initControlPos: function() { - function t(t, s) { var r = i + t + " " + i + s; - e[t + s] = o.DomUtil.create("div", r, n) } var e = this._controlCorners = {}, - i = "leaflet-", - n = this._controlContainer = o.DomUtil.create("div", i + "control-container", this._container); - t("top", "left"), t("top", "right"), t("bottom", "left"), t("bottom", "right") }, _clearControlPos: function() { o.DomUtil.remove(this._controlContainer) } }), o.Control.Zoom = o.Control.extend({ options: { position: "topleft", zoomInText: "+", zoomInTitle: "Zoom in", zoomOutText: "-", zoomOutTitle: "Zoom out" }, onAdd: function(t) { var e = "leaflet-control-zoom", - i = o.DomUtil.create("div", e + " leaflet-bar"), - n = this.options; return this._zoomInButton = this._createButton(n.zoomInText, n.zoomInTitle, e + "-in", i, this._zoomIn), this._zoomOutButton = this._createButton(n.zoomOutText, n.zoomOutTitle, e + "-out", i, this._zoomOut), this._updateDisabled(), t.on("zoomend zoomlevelschange", this._updateDisabled, this), i }, onRemove: function(t) { t.off("zoomend zoomlevelschange", this._updateDisabled, this) }, disable: function() { return this._disabled = !0, this._updateDisabled(), this }, enable: function() { return this._disabled = !1, this._updateDisabled(), this }, _zoomIn: function(t) { this._disabled || this._map.zoomIn(t.shiftKey ? 3 : 1) }, _zoomOut: function(t) { this._disabled || this._map.zoomOut(t.shiftKey ? 3 : 1) }, _createButton: function(t, e, i, n, s) { var r = o.DomUtil.create("a", i, n); return r.innerHTML = t, r.href = "#", r.title = e, o.DomEvent.on(r, "mousedown dblclick", o.DomEvent.stopPropagation).on(r, "click", o.DomEvent.stop).on(r, "click", s, this).on(r, "click", this._refocusOnMap, this), r }, _updateDisabled: function() { var t = this._map, - e = "leaflet-disabled"; - o.DomUtil.removeClass(this._zoomInButton, e), o.DomUtil.removeClass(this._zoomOutButton, e), (this._disabled || t._zoom === t.getMinZoom()) && o.DomUtil.addClass(this._zoomOutButton, e), (this._disabled || t._zoom === t.getMaxZoom()) && o.DomUtil.addClass(this._zoomInButton, e) } }), o.Map.mergeOptions({ zoomControl: !0 }), o.Map.addInitHook(function() { this.options.zoomControl && (this.zoomControl = new o.Control.Zoom, this.addControl(this.zoomControl)) }), o.control.zoom = function(t) { return new o.Control.Zoom(t) }, o.Control.Attribution = o.Control.extend({ options: { position: "bottomright", prefix: 'Leaflet' }, initialize: function(t) { o.setOptions(this, t), this._attributions = {} }, onAdd: function(t) { this._container = o.DomUtil.create("div", "leaflet-control-attribution"), o.DomEvent && o.DomEvent.disableClickPropagation(this._container); for (var e in t._layers) t._layers[e].getAttribution && this.addAttribution(t._layers[e].getAttribution()); return this._update(), this._container }, setPrefix: function(t) { return this.options.prefix = t, this._update(), this }, addAttribution: function(t) { return t ? (this._attributions[t] || (this._attributions[t] = 0), this._attributions[t]++, this._update(), this) : this }, removeAttribution: function(t) { return t ? (this._attributions[t] && (this._attributions[t]--, this._update()), this) : this }, _update: function() { if (this._map) { var t = []; for (var e in this._attributions) this._attributions[e] && t.push(e); var i = []; - this.options.prefix && i.push(this.options.prefix), t.length && i.push(t.join(", ")), this._container.innerHTML = i.join(" | ") } } }), o.Map.mergeOptions({ attributionControl: !0 }), o.Map.addInitHook(function() { this.options.attributionControl && (this.attributionControl = (new o.Control.Attribution).addTo(this)) }), o.control.attribution = function(t) { return new o.Control.Attribution(t) }, o.Control.Scale = o.Control.extend({ options: { position: "bottomleft", maxWidth: 100, metric: !0, imperial: !0 }, onAdd: function(t) { var e = "leaflet-control-scale", - i = o.DomUtil.create("div", e), - n = this.options; return this._addScales(n, e + "-line", i), t.on(n.updateWhenIdle ? "moveend" : "move", this._update, this), t.whenReady(this._update, this), i }, onRemove: function(t) { t.off(this.options.updateWhenIdle ? "moveend" : "move", this._update, this) }, _addScales: function(t, e, i) { t.metric && (this._mScale = o.DomUtil.create("div", e, i)), t.imperial && (this._iScale = o.DomUtil.create("div", e, i)) }, _update: function() { var t = this._map, - e = t.getSize().y / 2, - i = t.distance(t.containerPointToLatLng([0, e]), t.containerPointToLatLng([this.options.maxWidth, e])); - this._updateScales(i) }, _updateScales: function(t) { this.options.metric && t && this._updateMetric(t), this.options.imperial && t && this._updateImperial(t) }, _updateMetric: function(t) { var e = this._getRoundNum(t), - i = 1e3 > e ? e + " m" : e / 1e3 + " km"; - this._updateScale(this._mScale, i, e / t) }, _updateImperial: function(t) { var e, i, n, o = 3.2808399 * t; - o > 5280 ? (e = o / 5280, i = this._getRoundNum(e), this._updateScale(this._iScale, i + " mi", i / e)) : (n = this._getRoundNum(o), this._updateScale(this._iScale, n + " ft", n / o)) }, _updateScale: function(t, e, i) { t.style.width = Math.round(this.options.maxWidth * i) + "px", t.innerHTML = e }, _getRoundNum: function(t) { var e = Math.pow(10, (Math.floor(t) + "").length - 1), - i = t / e; return i = i >= 10 ? 10 : i >= 5 ? 5 : i >= 3 ? 3 : i >= 2 ? 2 : 1, e * i } }), o.control.scale = function(t) { return new o.Control.Scale(t) }, o.Control.Layers = o.Control.extend({ options: { collapsed: !0, position: "topright", autoZIndex: !0, hideSingleBase: !1 }, initialize: function(t, e, i) { o.setOptions(this, i), this._layers = {}, this._lastZIndex = 0, this._handlingClick = !1; for (var n in t) this._addLayer(t[n], n); for (n in e) this._addLayer(e[n], n, !0) }, onAdd: function(t) { return this._initLayout(), this._update(), this._map = t, t.on("zoomend", this._checkDisabledLayers, this), this._container }, onRemove: function() { this._map.off("zoomend", this._checkDisabledLayers, this) }, addBaseLayer: function(t, e) { return this._addLayer(t, e), this._update() }, addOverlay: function(t, e) { return this._addLayer(t, e, !0), this._update() }, removeLayer: function(t) { return t.off("add remove", this._onLayerChange, this), delete this._layers[o.stamp(t)], this._update() }, _initLayout: function() { var t = "leaflet-control-layers", - e = this._container = o.DomUtil.create("div", t); - e.setAttribute("aria-haspopup", !0), o.DomEvent.disableClickPropagation(e), o.Browser.touch || o.DomEvent.disableScrollPropagation(e); var i = this._form = o.DomUtil.create("form", t + "-list"); if (this.options.collapsed) { o.Browser.android || o.DomEvent.on(e, { mouseenter: this._expand, mouseleave: this._collapse }, this); var n = this._layersLink = o.DomUtil.create("a", t + "-toggle", e); - n.href = "#", n.title = "Layers", o.Browser.touch ? o.DomEvent.on(n, "click", o.DomEvent.stop).on(n, "click", this._expand, this) : o.DomEvent.on(n, "focus", this._expand, this), o.DomEvent.on(i, "click", function() { setTimeout(o.bind(this._onInputClick, this), 0) }, this), this._map.on("click", this._collapse, this) } else this._expand(); - this._baseLayersList = o.DomUtil.create("div", t + "-base", i), this._separator = o.DomUtil.create("div", t + "-separator", i), this._overlaysList = o.DomUtil.create("div", t + "-overlays", i), e.appendChild(i) }, _addLayer: function(t, e, i) { t.on("add remove", this._onLayerChange, this); var n = o.stamp(t); - this._layers[n] = { layer: t, name: e, overlay: i }, this.options.autoZIndex && t.setZIndex && (this._lastZIndex++, t.setZIndex(this._lastZIndex)) }, _update: function() { if (!this._container) return this; - o.DomUtil.empty(this._baseLayersList), o.DomUtil.empty(this._overlaysList); var t, e, i, n, s = 0; for (i in this._layers) n = this._layers[i], this._addItem(n), e = e || n.overlay, t = t || !n.overlay, s += n.overlay ? 0 : 1; return this.options.hideSingleBase && (t = t && s > 1, this._baseLayersList.style.display = t ? "" : "none"), this._separator.style.display = e && t ? "" : "none", this }, _onLayerChange: function(t) { this._handlingClick || this._update(); var e = this._layers[o.stamp(t.target)], - i = e.overlay ? "add" === t.type ? "overlayadd" : "overlayremove" : "add" === t.type ? "baselayerchange" : null; - i && this._map.fire(i, e) }, _createRadioElement: function(t, i) { var n = '", - o = e.createElement("div"); return o.innerHTML = n, o.firstChild }, _addItem: function(t) { var i, n = e.createElement("label"), - s = this._map.hasLayer(t.layer); - t.overlay ? (i = e.createElement("input"), i.type = "checkbox", i.className = "leaflet-control-layers-selector", i.defaultChecked = s) : i = this._createRadioElement("leaflet-base-layers", s), i.layerId = o.stamp(t.layer), o.DomEvent.on(i, "click", this._onInputClick, this); var r = e.createElement("span"); - r.innerHTML = " " + t.name; var a = e.createElement("div"); - n.appendChild(a), a.appendChild(i), a.appendChild(r); var h = t.overlay ? this._overlaysList : this._baseLayersList; return h.appendChild(n), this._checkDisabledLayers(), n }, _onInputClick: function() { var t, e, i, n = this._form.getElementsByTagName("input"), - o = [], - s = []; - this._handlingClick = !0; for (var r = n.length - 1; r >= 0; r--) t = n[r], e = this._layers[t.layerId].layer, i = this._map.hasLayer(e), t.checked && !i ? o.push(e) : !t.checked && i && s.push(e); for (r = 0; r < s.length; r++) this._map.removeLayer(s[r]); for (r = 0; r < o.length; r++) this._map.addLayer(o[r]); - this._handlingClick = !1, this._refocusOnMap() }, _expand: function() { o.DomUtil.addClass(this._container, "leaflet-control-layers-expanded"), this._form.style.height = null; var t = this._map._size.y - (this._container.offsetTop + 50); - t < this._form.clientHeight ? (o.DomUtil.addClass(this._form, "leaflet-control-layers-scrollbar"), this._form.style.height = t + "px") : o.DomUtil.removeClass(this._form, "leaflet-control-layers-scrollbar"), this._checkDisabledLayers() }, _collapse: function() { o.DomUtil.removeClass(this._container, "leaflet-control-layers-expanded") }, _checkDisabledLayers: function() { for (var t, e, n = this._form.getElementsByTagName("input"), o = this._map.getZoom(), s = n.length - 1; s >= 0; s--) t = n[s], e = this._layers[t.layerId].layer, t.disabled = e.options.minZoom !== i && o < e.options.minZoom || e.options.maxZoom !== i && o > e.options.maxZoom } }), o.control.layers = function(t, e, i) { return new o.Control.Layers(t, e, i) }, o.PosAnimation = o.Evented.extend({ run: function(t, e, i, n) { this.stop(), this._el = t, this._inProgress = !0, this._duration = i || .25, this._easeOutPower = 1 / Math.max(n || .5, .2), this._startPos = o.DomUtil.getPosition(t), this._offset = e.subtract(this._startPos), this._startTime = +new Date, this.fire("start"), this._animate() }, stop: function() { this._inProgress && (this._step(!0), this._complete()) }, _animate: function() { this._animId = o.Util.requestAnimFrame(this._animate, this), this._step() }, _step: function(t) { var e = +new Date - this._startTime, - i = 1e3 * this._duration; - i > e ? this._runFrame(this._easeOut(e / i), t) : (this._runFrame(1), this._complete()) }, _runFrame: function(t, e) { var i = this._startPos.add(this._offset.multiplyBy(t)); - e && i._round(), o.DomUtil.setPosition(this._el, i), this.fire("step") }, _complete: function() { o.Util.cancelAnimFrame(this._animId), this._inProgress = !1, this.fire("end") }, _easeOut: function(t) { return 1 - Math.pow(1 - t, this._easeOutPower) } }), o.Map.include({ setView: function(t, e, n) { if (e = e === i ? this._zoom : this._limitZoom(e), t = this._limitCenter(o.latLng(t), e, this.options.maxBounds), n = n || {}, this.stop(), this._loaded && !n.reset && n !== !0) { n.animate !== i && (n.zoom = o.extend({ animate: n.animate }, n.zoom), n.pan = o.extend({ animate: n.animate, duration: n.duration }, n.pan)); var s = this._zoom !== e ? this._tryAnimatedZoom && this._tryAnimatedZoom(t, e, n.zoom) : this._tryAnimatedPan(t, n.pan); if (s) return clearTimeout(this._sizeTimer), this } return this._resetView(t, e), this }, panBy: function(t, e) { if (t = o.point(t).round(), e = e || {}, !t.x && !t.y) return this.fire("moveend"); if (e.animate !== !0 && !this.getSize().contains(t)) return this._resetView(this.unproject(this.project(this.getCenter()).add(t)), this.getZoom()), this; if (this._panAnim || (this._panAnim = new o.PosAnimation, this._panAnim.on({ step: this._onPanTransitionStep, end: this._onPanTransitionEnd }, this)), e.noMoveStart || this.fire("movestart"), e.animate !== !1) { o.DomUtil.addClass(this._mapPane, "leaflet-pan-anim"); var i = this._getMapPanePos().subtract(t); - this._panAnim.run(this._mapPane, i, e.duration || .25, e.easeLinearity) } else this._rawPanBy(t), this.fire("move").fire("moveend"); return this }, _onPanTransitionStep: function() { this.fire("move") }, _onPanTransitionEnd: function() { o.DomUtil.removeClass(this._mapPane, "leaflet-pan-anim"), this.fire("moveend") }, _tryAnimatedPan: function(t, e) { var i = this._getCenterOffset(t)._floor(); return (e && e.animate) === !0 || this.getSize().contains(i) ? (this.panBy(i, e), !0) : !1 } }), o.Map.mergeOptions({ zoomAnimation: !0, zoomAnimationThreshold: 4 }); - var h = o.DomUtil.TRANSITION && o.Browser.any3d && !o.Browser.mobileOpera; - h && o.Map.addInitHook(function() { this._zoomAnimated = this.options.zoomAnimation, this._zoomAnimated && (this._createAnimProxy(), o.DomEvent.on(this._proxy, o.DomUtil.TRANSITION_END, this._catchTransitionEnd, this)) }), o.Map.include(h ? { - _createAnimProxy: function() { - var t = this._proxy = o.DomUtil.create("div", "leaflet-proxy leaflet-zoom-animated"); - this._panes.mapPane.appendChild(t), this.on("zoomanim", function(e) { var i = o.DomUtil.TRANSFORM, - n = t.style[i]; - o.DomUtil.setTransform(t, this.project(e.center, e.zoom), this.getZoomScale(e.zoom, 1)), n === t.style[i] && this._animatingZoom && this._onZoomTransitionEnd() }, this), this.on("load moveend", function() { - var e = this.getCenter(), - i = this.getZoom(); - o.DomUtil.setTransform(t, this.project(e, i), this.getZoomScale(i, 1)) - }, this) - }, - _catchTransitionEnd: function(t) { this._animatingZoom && t.propertyName.indexOf("transform") >= 0 && this._onZoomTransitionEnd() }, - _nothingToAnimate: function() { return !this._container.getElementsByClassName("leaflet-zoom-animated").length }, - _tryAnimatedZoom: function(t, e, i) { if (this._animatingZoom) return !0; if (i = i || {}, !this._zoomAnimated || i.animate === !1 || this._nothingToAnimate() || Math.abs(e - this._zoom) > this.options.zoomAnimationThreshold) return !1; var n = this.getZoomScale(e), - s = this._getCenterOffset(t)._divideBy(1 - 1 / n); return i.animate === !0 || this.getSize().contains(s) ? (o.Util.requestAnimFrame(function() { this._moveStart(!0)._animateZoom(t, e, !0) }, this), !0) : !1 }, - _animateZoom: function(t, e, i, n) { i && (this._animatingZoom = !0, this._animateToCenter = t, this._animateToZoom = e, o.DomUtil.addClass(this._mapPane, "leaflet-zoom-anim")), this.fire("zoomanim", { center: t, zoom: e, noUpdate: n }), setTimeout(o.bind(this._onZoomTransitionEnd, this), 250) }, - _onZoomTransitionEnd: function() { this._animatingZoom && (o.DomUtil.removeClass(this._mapPane, "leaflet-zoom-anim"), o.Util.requestAnimFrame(function() { this._animatingZoom = !1, this._move(this._animateToCenter, this._animateToZoom)._moveEnd(!0) }, this)) } - } : {}), o.Map.include({ flyTo: function(t, e, n) { - function s(t) { var e = (v * v - g * g + (t ? -1 : 1) * L * L * y * y) / (2 * (t ? v : g) * L * y); return Math.log(Math.sqrt(e * e + 1) - e) } + ) { + if (((i = this._targets[o.stamp(r)]), i && i.listens(e, !0))) { + if (s && !o.DomEvent._isExternalTarget(r, t)) break; + if ((n.push(i), s)) break; + } + if (r === this._container) break; + r = r.parentNode; + } + return n.length || s || !o.DomEvent._isExternalTarget(r, t) || (n = [this]), n; + }, + _handleDOMEvent: function (t) { + if (this._loaded && !o.DomEvent._skipped(t)) { + var e = "keypress" === t.type && 13 === t.keyCode ? "click" : t.type; + if ("click" === t.type) { + var i = o.Util.extend({}, t); + (i.type = "preclick"), this._handleDOMEvent(i); + } + "mousedown" === e && o.DomUtil.preventOutline(t.target || t.srcElement), + this._fireDOMEvent(t, e); + } + }, + _fireDOMEvent: function (t, e, i) { + if (!t._stopped && ((i = (i || []).concat(this._findEventTargets(t, e))), i.length)) { + var n = i[0]; + if ( + ("contextmenu" === e && n.listens(e, !0) && o.DomEvent.preventDefault(t), + ("click" !== t.type && "preclick" !== t.type) || + t._simulated || + !this._draggableMoved(n)) + ) { + var s = { originalEvent: t }; + if ("keypress" !== t.type) { + var r = n instanceof o.Marker; + (s.containerPoint = r + ? this.latLngToContainerPoint(n.getLatLng()) + : this.mouseEventToContainerPoint(t)), + (s.layerPoint = this.containerPointToLayerPoint(s.containerPoint)), + (s.latlng = r ? n.getLatLng() : this.layerPointToLatLng(s.layerPoint)); + } + for (var a = 0; a < i.length; a++) + if ( + (i[a].fire(e, s, !0), + s.originalEvent._stopped || + (i[a].options.nonBubblingEvents && + -1 !== o.Util.indexOf(i[a].options.nonBubblingEvents, e))) + ) + return; + } + } + }, + _draggableMoved: function (t) { + return ( + (t = t.options.draggable ? t : this), + (t.dragging && t.dragging.moved()) || (this.boxZoom && this.boxZoom.moved()) + ); + }, + _clearHandlers: function () { + for (var t = 0, e = this._handlers.length; e > t; t++) this._handlers[t].disable(); + }, + whenReady: function (t, e) { + return this._loaded ? t.call(e || this, { target: this }) : this.on("load", t, e), this; + }, + _getMapPanePos: function () { + return o.DomUtil.getPosition(this._mapPane) || new o.Point(0, 0); + }, + _moved: function () { + var t = this._getMapPanePos(); + return t && !t.equals([0, 0]); + }, + _getTopLeftPoint: function (t, e) { + var n = t && e !== i ? this._getNewPixelOrigin(t, e) : this.getPixelOrigin(); + return n.subtract(this._getMapPanePos()); + }, + _getNewPixelOrigin: function (t, e) { + var i = this.getSize()._divideBy(2); + return this.project(t, e)._subtract(i)._add(this._getMapPanePos())._round(); + }, + _latLngToNewLayerPoint: function (t, e, i) { + var n = this._getNewPixelOrigin(i, e); + return this.project(t, e)._subtract(n); + }, + _getCenterLayerPoint: function () { + return this.containerPointToLayerPoint(this.getSize()._divideBy(2)); + }, + _getCenterOffset: function (t) { + return this.latLngToLayerPoint(t).subtract(this._getCenterLayerPoint()); + }, + _limitCenter: function (t, e, i) { + if (!i) return t; + var n = this.project(t, e), + s = this.getSize().divideBy(2), + r = new o.Bounds(n.subtract(s), n.add(s)), + a = this._getBoundsOffset(r, i, e); + return this.unproject(n.add(a), e); + }, + _limitOffset: function (t, e) { + if (!e) return t; + var i = this.getPixelBounds(), + n = new o.Bounds(i.min.add(t), i.max.add(t)); + return t.add(this._getBoundsOffset(n, e)); + }, + _getBoundsOffset: function (t, e, i) { + var n = this.project(e.getNorthWest(), i).subtract(t.min), + s = this.project(e.getSouthEast(), i).subtract(t.max), + r = this._rebound(n.x, -s.x), + a = this._rebound(n.y, -s.y); + return new o.Point(r, a); + }, + _rebound: function (t, e) { + return t + e > 0 + ? Math.round(t - e) / 2 + : Math.max(0, Math.ceil(t)) - Math.max(0, Math.floor(e)); + }, + _limitZoom: function (t) { + var e = this.getMinZoom(), + i = this.getMaxZoom(); + return o.Browser.any3d || (t = Math.round(t)), Math.max(e, Math.min(i, t)); + }, + })), + (o.map = function (t, e) { + return new o.Map(t, e); + }), + (o.Layer = o.Evented.extend({ + options: { pane: "overlayPane", nonBubblingEvents: [] }, + addTo: function (t) { + return t.addLayer(this), this; + }, + remove: function () { + return this.removeFrom(this._map || this._mapToAdd); + }, + removeFrom: function (t) { + return t && t.removeLayer(this), this; + }, + getPane: function (t) { + return this._map.getPane(t ? this.options[t] || t : this.options.pane); + }, + addInteractiveTarget: function (t) { + return (this._map._targets[o.stamp(t)] = this), this; + }, + removeInteractiveTarget: function (t) { + return delete this._map._targets[o.stamp(t)], this; + }, + _layerAdd: function (t) { + var e = t.target; + e.hasLayer(this) && + ((this._map = e), + (this._zoomAnimated = e._zoomAnimated), + this.getEvents && e.on(this.getEvents(), this), + this.onAdd(e), + this.getAttribution && + this._map.attributionControl && + this._map.attributionControl.addAttribution(this.getAttribution()), + this.fire("add"), + e.fire("layeradd", { layer: this })); + }, + })), + o.Map.include({ + addLayer: function (t) { + var e = o.stamp(t); + return this._layers[e] + ? t + : ((this._layers[e] = t), + (t._mapToAdd = this), + t.beforeAdd && t.beforeAdd(this), + this.whenReady(t._layerAdd, t), + this); + }, + removeLayer: function (t) { + var e = o.stamp(t); + return this._layers[e] + ? (this._loaded && t.onRemove(this), + t.getAttribution && + this.attributionControl && + this.attributionControl.removeAttribution(t.getAttribution()), + t.getEvents && this.off(t.getEvents(), t), + delete this._layers[e], + this._loaded && (this.fire("layerremove", { layer: t }), t.fire("remove")), + (t._map = t._mapToAdd = null), + this) + : this; + }, + hasLayer: function (t) { + return !!t && o.stamp(t) in this._layers; + }, + eachLayer: function (t, e) { + for (var i in this._layers) t.call(e, this._layers[i]); + return this; + }, + _addLayers: function (t) { + t = t ? (o.Util.isArray(t) ? t : [t]) : []; + for (var e = 0, i = t.length; i > e; e++) this.addLayer(t[e]); + }, + _addZoomLimit: function (t) { + (isNaN(t.options.maxZoom) || !isNaN(t.options.minZoom)) && + ((this._zoomBoundLayers[o.stamp(t)] = t), this._updateZoomLevels()); + }, + _removeZoomLimit: function (t) { + var e = o.stamp(t); + this._zoomBoundLayers[e] && (delete this._zoomBoundLayers[e], this._updateZoomLevels()); + }, + _updateZoomLevels: function () { + var t = 1 / 0, + e = -(1 / 0), + n = this._getZoomSpan(); + for (var o in this._zoomBoundLayers) { + var s = this._zoomBoundLayers[o].options; + (t = s.minZoom === i ? t : Math.min(t, s.minZoom)), + (e = s.maxZoom === i ? e : Math.max(e, s.maxZoom)); + } + (this._layersMaxZoom = e === -(1 / 0) ? i : e), + (this._layersMinZoom = t === 1 / 0 ? i : t), + n !== this._getZoomSpan() && this.fire("zoomlevelschange"); + }, + }), + (o.Projection.Mercator = { + R: 6378137, + R_MINOR: 6356752.314245179, + bounds: o.bounds([-20037508.34279, -15496570.73972], [20037508.34279, 18764656.23138]), + project: function (t) { + var e = Math.PI / 180, + i = this.R, + n = t.lat * e, + s = this.R_MINOR / i, + r = Math.sqrt(1 - s * s), + a = r * Math.sin(n), + h = Math.tan(Math.PI / 4 - n / 2) / Math.pow((1 - a) / (1 + a), r / 2); + return (n = -i * Math.log(Math.max(h, 1e-10))), new o.Point(t.lng * e * i, n); + }, + unproject: function (t) { + for ( + var e, + i = 180 / Math.PI, + n = this.R, + s = this.R_MINOR / n, + r = Math.sqrt(1 - s * s), + a = Math.exp(-t.y / n), + h = Math.PI / 2 - 2 * Math.atan(a), + l = 0, + u = 0.1; + 15 > l && Math.abs(u) > 1e-7; + l++ + ) + (e = r * Math.sin(h)), + (e = Math.pow((1 - e) / (1 + e), r / 2)), + (u = Math.PI / 2 - 2 * Math.atan(a * e) - h), + (h += u); + return new o.LatLng(h * i, (t.x * i) / n); + }, + }), + (o.CRS.EPSG3395 = o.extend({}, o.CRS.Earth, { + code: "EPSG:3395", + projection: o.Projection.Mercator, + transformation: (function () { + var t = 0.5 / (Math.PI * o.Projection.Mercator.R); + return new o.Transformation(t, 0.5, -t, 0.5); + })(), + })), + (o.GridLayer = o.Layer.extend({ + options: { + pane: "tilePane", + tileSize: 256, + opacity: 1, + zIndex: 1, + updateWhenIdle: o.Browser.mobile, + updateInterval: 200, + attribution: null, + bounds: null, + minZoom: 0, + }, + initialize: function (t) { + t = o.setOptions(this, t); + }, + onAdd: function () { + this._initContainer(), + (this._levels = {}), + (this._tiles = {}), + this._resetView(), + this._update(); + }, + beforeAdd: function (t) { + t._addZoomLimit(this); + }, + onRemove: function (t) { + o.DomUtil.remove(this._container), + t._removeZoomLimit(this), + (this._container = null), + (this._tileZoom = null); + }, + bringToFront: function () { + return this._map && (o.DomUtil.toFront(this._container), this._setAutoZIndex(Math.max)), this; + }, + bringToBack: function () { + return this._map && (o.DomUtil.toBack(this._container), this._setAutoZIndex(Math.min)), this; + }, + getAttribution: function () { + return this.options.attribution; + }, + getContainer: function () { + return this._container; + }, + setOpacity: function (t) { + return (this.options.opacity = t), this._updateOpacity(), this; + }, + setZIndex: function (t) { + return (this.options.zIndex = t), this._updateZIndex(), this; + }, + isLoading: function () { + return this._loading; + }, + redraw: function () { + return this._map && (this._removeAllTiles(), this._update()), this; + }, + getEvents: function () { + var t = { viewreset: this._resetAll, zoom: this._resetView, moveend: this._onMoveEnd }; + return ( + this.options.updateWhenIdle || + (this._onMove || + (this._onMove = o.Util.throttle( + this._onMoveEnd, + this.options.updateInterval, + this + )), + (t.move = this._onMove)), + this._zoomAnimated && (t.zoomanim = this._animateZoom), + t + ); + }, + createTile: function () { + return e.createElement("div"); + }, + getTileSize: function () { + var t = this.options.tileSize; + return t instanceof o.Point ? t : new o.Point(t, t); + }, + _updateZIndex: function () { + this._container && + this.options.zIndex !== i && + null !== this.options.zIndex && + (this._container.style.zIndex = this.options.zIndex); + }, + _setAutoZIndex: function (t) { + for ( + var e, i = this.getPane().children, n = -t(-(1 / 0), 1 / 0), o = 0, s = i.length; + s > o; + o++ + ) + (e = i[o].style.zIndex), i[o] !== this._container && e && (n = t(n, +e)); + isFinite(n) && ((this.options.zIndex = n + t(-1, 1)), this._updateZIndex()); + }, + _updateOpacity: function () { + if (this._map && !o.Browser.ielt9 && this._map._fadeAnimated) { + o.DomUtil.setOpacity(this._container, this.options.opacity); + var t = +new Date(), + e = !1, + i = !1; + for (var n in this._tiles) { + var s = this._tiles[n]; + if (s.current && s.loaded) { + var r = Math.min(1, (t - s.loaded) / 200); + o.DomUtil.setOpacity(s.el, r), + 1 > r ? (e = !0) : (s.active && (i = !0), (s.active = !0)); + } + } + i && !this._noPrune && this._pruneTiles(), + e && + (o.Util.cancelAnimFrame(this._fadeFrame), + (this._fadeFrame = o.Util.requestAnimFrame(this._updateOpacity, this))); + } + }, + _initContainer: function () { + this._container || + ((this._container = o.DomUtil.create("div", "leaflet-layer")), + this._updateZIndex(), + this.options.opacity < 1 && this._updateOpacity(), + this.getPane().appendChild(this._container)); + }, + _updateLevels: function () { + var t = this._tileZoom, + e = this.options.maxZoom; + for (var i in this._levels) + this._levels[i].el.children.length || i === t + ? (this._levels[i].el.style.zIndex = e - Math.abs(t - i)) + : (o.DomUtil.remove(this._levels[i].el), delete this._levels[i]); + var n = this._levels[t], + s = this._map; + return ( + n || + ((n = this._levels[t] = {}), + (n.el = o.DomUtil.create( + "div", + "leaflet-tile-container leaflet-zoom-animated", + this._container + )), + (n.el.style.zIndex = e), + (n.origin = s.project(s.unproject(s.getPixelOrigin()), t).round()), + (n.zoom = t), + this._setZoomTransform(n, s.getCenter(), s.getZoom()), + o.Util.falseFn(n.el.offsetWidth)), + (this._level = n), + n + ); + }, + _pruneTiles: function () { + var t, + e, + i = this._map.getZoom(); + if (i > this.options.maxZoom || i < this.options.minZoom) return this._removeAllTiles(); + for (t in this._tiles) (e = this._tiles[t]), (e.retain = e.current); + for (t in this._tiles) + if (((e = this._tiles[t]), e.current && !e.active)) { + var n = e.coords; + this._retainParent(n.x, n.y, n.z, n.z - 5) || + this._retainChildren(n.x, n.y, n.z, n.z + 2); + } + for (t in this._tiles) this._tiles[t].retain || this._removeTile(t); + }, + _removeAllTiles: function () { + for (var t in this._tiles) this._removeTile(t); + }, + _resetAll: function () { + for (var t in this._levels) o.DomUtil.remove(this._levels[t].el), delete this._levels[t]; + this._removeAllTiles(), (this._tileZoom = null), this._resetView(); + }, + _retainParent: function (t, e, i, n) { + var o = Math.floor(t / 2), + s = Math.floor(e / 2), + r = i - 1, + a = o + ":" + s + ":" + r, + h = this._tiles[a]; + return h && h.active + ? ((h.retain = !0), !0) + : (h && h.loaded && (h.retain = !0), r > n ? this._retainParent(o, s, r, n) : !1); + }, + _retainChildren: function (t, e, i, n) { + for (var o = 2 * t; 2 * t + 2 > o; o++) + for (var s = 2 * e; 2 * e + 2 > s; s++) { + var r = o + ":" + s + ":" + (i + 1), + a = this._tiles[r]; + a && a.active + ? (a.retain = !0) + : (a && a.loaded && (a.retain = !0), + n > i + 1 && this._retainChildren(o, s, i + 1, n)); + } + }, + _resetView: function (t) { + var e = t && (t.pinch || t.flyTo); + this._setView(this._map.getCenter(), this._map.getZoom(), e, e); + }, + _animateZoom: function (t) { + this._setView(t.center, t.zoom, !0, t.noUpdate); + }, + _setView: function (t, e, n, o) { + var s = Math.round(e); + ((this.options.maxZoom !== i && s > this.options.maxZoom) || + (this.options.minZoom !== i && s < this.options.minZoom)) && + (s = i); + var r = s !== this._tileZoom; + (!o || r) && + ((this._tileZoom = s), + this._abortLoading && this._abortLoading(), + this._updateLevels(), + this._resetGrid(), + s !== i && this._update(t), + n || this._pruneTiles(), + (this._noPrune = !!n)), + this._setZoomTransforms(t, e); + }, + _setZoomTransforms: function (t, e) { + for (var i in this._levels) this._setZoomTransform(this._levels[i], t, e); + }, + _setZoomTransform: function (t, e, i) { + var n = this._map.getZoomScale(i, t.zoom), + s = t.origin.multiplyBy(n).subtract(this._map._getNewPixelOrigin(e, i)).round(); + o.Browser.any3d ? o.DomUtil.setTransform(t.el, s, n) : o.DomUtil.setPosition(t.el, s); + }, + _resetGrid: function () { + var t = this._map, + e = t.options.crs, + i = (this._tileSize = this.getTileSize()), + n = this._tileZoom, + o = this._map.getPixelWorldBounds(this._tileZoom); + o && (this._globalTileRange = this._pxBoundsToTileRange(o)), + (this._wrapX = e.wrapLng && + !this.options.noWrap && [ + Math.floor(t.project([0, e.wrapLng[0]], n).x / i.x), + Math.ceil(t.project([0, e.wrapLng[1]], n).x / i.y), + ]), + (this._wrapY = e.wrapLat && + !this.options.noWrap && [ + Math.floor(t.project([e.wrapLat[0], 0], n).y / i.x), + Math.ceil(t.project([e.wrapLat[1], 0], n).y / i.y), + ]); + }, + _onMoveEnd: function () { + this._map && !this._map._animatingZoom && this._resetView(); + }, + _getTiledPixelBounds: function (t, e, i) { + var n = this._map, + s = n.getZoomScale(e, i), + r = n.project(t, i).floor(), + a = n.getSize().divideBy(2 * s); + return new o.Bounds(r.subtract(a), r.add(a)); + }, + _update: function (t) { + var n = this._map; + if (n) { + var s = n.getZoom(); + if ((t === i && (t = n.getCenter()), this._tileZoom !== i)) { + var r = this._getTiledPixelBounds(t, s, this._tileZoom), + a = this._pxBoundsToTileRange(r), + h = a.getCenter(), + l = []; + for (var u in this._tiles) this._tiles[u].current = !1; + if (Math.abs(s - this._tileZoom) > 1) return void this._setView(t, s); + for (var c = a.min.y; c <= a.max.y; c++) + for (var d = a.min.x; d <= a.max.x; d++) { + var _ = new o.Point(d, c); + if (((_.z = this._tileZoom), this._isValidTile(_))) { + var m = this._tiles[this._tileCoordsToKey(_)]; + m ? (m.current = !0) : l.push(_); + } + } + if ( + (l.sort(function (t, e) { + return t.distanceTo(h) - e.distanceTo(h); + }), + 0 !== l.length) + ) { + this._loading || ((this._loading = !0), this.fire("loading")); + var p = e.createDocumentFragment(); + for (d = 0; d < l.length; d++) this._addTile(l[d], p); + this._level.el.appendChild(p); + } + } + } + }, + _isValidTile: function (t) { + var e = this._map.options.crs; + if (!e.infinite) { + var i = this._globalTileRange; + if ( + (!e.wrapLng && (t.x < i.min.x || t.x > i.max.x)) || + (!e.wrapLat && (t.y < i.min.y || t.y > i.max.y)) + ) + return !1; + } + if (!this.options.bounds) return !0; + var n = this._tileCoordsToBounds(t); + return o.latLngBounds(this.options.bounds).overlaps(n); + }, + _keyToBounds: function (t) { + return this._tileCoordsToBounds(this._keyToTileCoords(t)); + }, + _tileCoordsToBounds: function (t) { + var e = this._map, + i = this.getTileSize(), + n = t.scaleBy(i), + s = n.add(i), + r = e.wrapLatLng(e.unproject(n, t.z)), + a = e.wrapLatLng(e.unproject(s, t.z)); + return new o.LatLngBounds(r, a); + }, + _tileCoordsToKey: function (t) { + return t.x + ":" + t.y + ":" + t.z; + }, + _keyToTileCoords: function (t) { + var e = t.split(":"), + i = new o.Point(+e[0], +e[1]); + return (i.z = +e[2]), i; + }, + _removeTile: function (t) { + var e = this._tiles[t]; + e && + (o.DomUtil.remove(e.el), + delete this._tiles[t], + this.fire("tileunload", { tile: e.el, coords: this._keyToTileCoords(t) })); + }, + _initTile: function (t) { + o.DomUtil.addClass(t, "leaflet-tile"); + var e = this.getTileSize(); + (t.style.width = e.x + "px"), + (t.style.height = e.y + "px"), + (t.onselectstart = o.Util.falseFn), + (t.onmousemove = o.Util.falseFn), + o.Browser.ielt9 && + this.options.opacity < 1 && + o.DomUtil.setOpacity(t, this.options.opacity), + o.Browser.android && + !o.Browser.android23 && + (t.style.WebkitBackfaceVisibility = "hidden"); + }, + _addTile: function (t, e) { + var i = this._getTilePos(t), + n = this._tileCoordsToKey(t), + s = this.createTile(this._wrapCoords(t), o.bind(this._tileReady, this, t)); + this._initTile(s), + this.createTile.length < 2 && + o.Util.requestAnimFrame(o.bind(this._tileReady, this, t, null, s)), + o.DomUtil.setPosition(s, i), + (this._tiles[n] = { el: s, coords: t, current: !0 }), + e.appendChild(s), + this.fire("tileloadstart", { tile: s, coords: t }); + }, + _tileReady: function (t, e, i) { + if (this._map) { + e && this.fire("tileerror", { error: e, tile: i, coords: t }); + var n = this._tileCoordsToKey(t); + (i = this._tiles[n]), + i && + ((i.loaded = +new Date()), + this._map._fadeAnimated + ? (o.DomUtil.setOpacity(i.el, 0), + o.Util.cancelAnimFrame(this._fadeFrame), + (this._fadeFrame = o.Util.requestAnimFrame(this._updateOpacity, this))) + : ((i.active = !0), this._pruneTiles()), + o.DomUtil.addClass(i.el, "leaflet-tile-loaded"), + this.fire("tileload", { tile: i.el, coords: t }), + this._noTilesToLoad() && ((this._loading = !1), this.fire("load"))); + } + }, + _getTilePos: function (t) { + return t.scaleBy(this.getTileSize()).subtract(this._level.origin); + }, + _wrapCoords: function (t) { + var e = new o.Point( + this._wrapX ? o.Util.wrapNum(t.x, this._wrapX) : t.x, + this._wrapY ? o.Util.wrapNum(t.y, this._wrapY) : t.y + ); + return (e.z = t.z), e; + }, + _pxBoundsToTileRange: function (t) { + var e = this.getTileSize(); + return new o.Bounds(t.min.unscaleBy(e).floor(), t.max.unscaleBy(e).ceil().subtract([1, 1])); + }, + _noTilesToLoad: function () { + for (var t in this._tiles) if (!this._tiles[t].loaded) return !1; + return !0; + }, + })), + (o.gridLayer = function (t) { + return new o.GridLayer(t); + }), + (o.TileLayer = o.GridLayer.extend({ + options: { + maxZoom: 18, + subdomains: "abc", + errorTileUrl: "", + zoomOffset: 0, + maxNativeZoom: null, + tms: !1, + zoomReverse: !1, + detectRetina: !1, + crossOrigin: !1, + }, + initialize: function (t, e) { + (this._url = t), + (e = o.setOptions(this, e)), + e.detectRetina && + o.Browser.retina && + e.maxZoom > 0 && + ((e.tileSize = Math.floor(e.tileSize / 2)), + e.zoomOffset++, + (e.minZoom = Math.max(0, e.minZoom)), + e.maxZoom--), + "string" == typeof e.subdomains && (e.subdomains = e.subdomains.split("")), + o.Browser.android || this.on("tileunload", this._onTileRemove); + }, + setUrl: function (t, e) { + return (this._url = t), e || this.redraw(), this; + }, + createTile: function (t, i) { + var n = e.createElement("img"); + return ( + o.DomEvent.on(n, "load", o.bind(this._tileOnLoad, this, i, n)), + o.DomEvent.on(n, "error", o.bind(this._tileOnError, this, i, n)), + this.options.crossOrigin && (n.crossOrigin = ""), + (n.alt = ""), + (n.src = this.getTileUrl(t)), + n + ); + }, + getTileUrl: function (t) { + return o.Util.template( + this._url, + o.extend( + { + r: + this.options.detectRetina && o.Browser.retina && this.options.maxZoom > 0 + ? "@2x" + : "", + s: this._getSubdomain(t), + x: t.x, + y: this.options.tms ? this._globalTileRange.max.y - t.y : t.y, + z: this._getZoomForUrl(), + }, + this.options + ) + ); + }, + _tileOnLoad: function (t, e) { + o.Browser.ielt9 ? setTimeout(o.bind(t, this, null, e), 0) : t(null, e); + }, + _tileOnError: function (t, e, i) { + var n = this.options.errorTileUrl; + n && (e.src = n), t(i, e); + }, + getTileSize: function () { + var t = this._map, + e = o.GridLayer.prototype.getTileSize.call(this), + i = this._tileZoom + this.options.zoomOffset, + n = this.options.maxNativeZoom; + return null !== n && i > n ? e.divideBy(t.getZoomScale(n, i)).round() : e; + }, + _onTileRemove: function (t) { + t.tile.onload = null; + }, + _getZoomForUrl: function () { + var t = this.options, + e = this._tileZoom; + return ( + t.zoomReverse && (e = t.maxZoom - e), + (e += t.zoomOffset), + null !== t.maxNativeZoom ? Math.min(e, t.maxNativeZoom) : e + ); + }, + _getSubdomain: function (t) { + var e = Math.abs(t.x + t.y) % this.options.subdomains.length; + return this.options.subdomains[e]; + }, + _abortLoading: function () { + var t, e; + for (t in this._tiles) + this._tiles[t].coords.z !== this._tileZoom && + ((e = this._tiles[t].el), + (e.onload = o.Util.falseFn), + (e.onerror = o.Util.falseFn), + e.complete || ((e.src = o.Util.emptyImageUrl), o.DomUtil.remove(e))); + }, + })), + (o.tileLayer = function (t, e) { + return new o.TileLayer(t, e); + }), + (o.TileLayer.WMS = o.TileLayer.extend({ + defaultWmsParams: { + service: "WMS", + request: "GetMap", + version: "1.1.1", + layers: "", + styles: "", + format: "image/jpeg", + transparent: !1, + }, + options: { crs: null, uppercase: !1 }, + initialize: function (t, e) { + this._url = t; + var i = o.extend({}, this.defaultWmsParams); + for (var n in e) n in this.options || (i[n] = e[n]); + (e = o.setOptions(this, e)), + (i.width = i.height = e.tileSize * (e.detectRetina && o.Browser.retina ? 2 : 1)), + (this.wmsParams = i); + }, + onAdd: function (t) { + (this._crs = this.options.crs || t.options.crs), + (this._wmsVersion = parseFloat(this.wmsParams.version)); + var e = this._wmsVersion >= 1.3 ? "crs" : "srs"; + (this.wmsParams[e] = this._crs.code), o.TileLayer.prototype.onAdd.call(this, t); + }, + getTileUrl: function (t) { + var e = this._tileCoordsToBounds(t), + i = this._crs.project(e.getNorthWest()), + n = this._crs.project(e.getSouthEast()), + s = ( + this._wmsVersion >= 1.3 && this._crs === o.CRS.EPSG4326 + ? [n.y, i.x, i.y, n.x] + : [i.x, n.y, n.x, i.y] + ).join(","), + r = o.TileLayer.prototype.getTileUrl.call(this, t); + return ( + r + + o.Util.getParamString(this.wmsParams, r, this.options.uppercase) + + (this.options.uppercase ? "&BBOX=" : "&bbox=") + + s + ); + }, + setParams: function (t, e) { + return o.extend(this.wmsParams, t), e || this.redraw(), this; + }, + })), + (o.tileLayer.wms = function (t, e) { + return new o.TileLayer.WMS(t, e); + }), + (o.ImageOverlay = o.Layer.extend({ + options: { opacity: 1, alt: "", interactive: !1 }, + initialize: function (t, e, i) { + (this._url = t), (this._bounds = o.latLngBounds(e)), o.setOptions(this, i); + }, + onAdd: function () { + this._image || (this._initImage(), this.options.opacity < 1 && this._updateOpacity()), + this.options.interactive && + (o.DomUtil.addClass(this._image, "leaflet-interactive"), + this.addInteractiveTarget(this._image)), + this.getPane().appendChild(this._image), + this._reset(); + }, + onRemove: function () { + o.DomUtil.remove(this._image), + this.options.interactive && this.removeInteractiveTarget(this._image); + }, + setOpacity: function (t) { + return (this.options.opacity = t), this._image && this._updateOpacity(), this; + }, + setStyle: function (t) { + return t.opacity && this.setOpacity(t.opacity), this; + }, + bringToFront: function () { + return this._map && o.DomUtil.toFront(this._image), this; + }, + bringToBack: function () { + return this._map && o.DomUtil.toBack(this._image), this; + }, + setUrl: function (t) { + return (this._url = t), this._image && (this._image.src = t), this; + }, + setBounds: function (t) { + return (this._bounds = t), this._map && this._reset(), this; + }, + getAttribution: function () { + return this.options.attribution; + }, + getEvents: function () { + var t = { zoom: this._reset, viewreset: this._reset }; + return this._zoomAnimated && (t.zoomanim = this._animateZoom), t; + }, + getBounds: function () { + return this._bounds; + }, + getElement: function () { + return this._image; + }, + _initImage: function () { + var t = (this._image = o.DomUtil.create( + "img", + "leaflet-image-layer " + (this._zoomAnimated ? "leaflet-zoom-animated" : "") + )); + (t.onselectstart = o.Util.falseFn), + (t.onmousemove = o.Util.falseFn), + (t.onload = o.bind(this.fire, this, "load")), + this.options.crossOrigin && (t.crossOrigin = ""), + (t.src = this._url), + (t.alt = this.options.alt); + }, + _animateZoom: function (t) { + var e = this._map.getZoomScale(t.zoom), + i = this._map._latLngToNewLayerPoint(this._bounds.getNorthWest(), t.zoom, t.center); + o.DomUtil.setTransform(this._image, i, e); + }, + _reset: function () { + var t = this._image, + e = new o.Bounds( + this._map.latLngToLayerPoint(this._bounds.getNorthWest()), + this._map.latLngToLayerPoint(this._bounds.getSouthEast()) + ), + i = e.getSize(); + o.DomUtil.setPosition(t, e.min), (t.style.width = i.x + "px"), (t.style.height = i.y + "px"); + }, + _updateOpacity: function () { + o.DomUtil.setOpacity(this._image, this.options.opacity); + }, + })), + (o.imageOverlay = function (t, e, i) { + return new o.ImageOverlay(t, e, i); + }), + (o.Icon = o.Class.extend({ + initialize: function (t) { + o.setOptions(this, t); + }, + createIcon: function (t) { + return this._createIcon("icon", t); + }, + createShadow: function (t) { + return this._createIcon("shadow", t); + }, + _createIcon: function (t, e) { + var i = this._getIconUrl(t); + if (!i) { + if ("icon" === t) throw new Error("iconUrl not set in Icon options (see the docs)."); + return null; + } + var n = this._createImg(i, e && "IMG" === e.tagName ? e : null); + return this._setIconStyles(n, t), n; + }, + _setIconStyles: function (t, e) { + var i = this.options, + n = o.point(i[e + "Size"]), + s = o.point( + ("shadow" === e && i.shadowAnchor) || i.iconAnchor || (n && n.divideBy(2, !0)) + ); + (t.className = "leaflet-marker-" + e + " " + (i.className || "")), + s && ((t.style.marginLeft = -s.x + "px"), (t.style.marginTop = -s.y + "px")), + n && ((t.style.width = n.x + "px"), (t.style.height = n.y + "px")); + }, + _createImg: function (t, i) { + return (i = i || e.createElement("img")), (i.src = t), i; + }, + _getIconUrl: function (t) { + return (o.Browser.retina && this.options[t + "RetinaUrl"]) || this.options[t + "Url"]; + }, + })), + (o.icon = function (t) { + return new o.Icon(t); + }), + (o.Icon.Default = o.Icon.extend({ + options: { + iconSize: [25, 41], + iconAnchor: [12, 41], + popupAnchor: [1, -34], + shadowSize: [41, 41], + }, + _getIconUrl: function (t) { + var e = t + "Url"; + if (this.options[e]) return this.options[e]; + var i = o.Icon.Default.imagePath; + if (!i) throw new Error("Couldn't autodetect L.Icon.Default.imagePath, set it manually."); + return i + "/marker-" + t + (o.Browser.retina && "icon" === t ? "-2x" : "") + ".png"; + }, + })), + (o.Icon.Default.imagePath = (function () { + var t, + i, + n, + o, + s = e.getElementsByTagName("script"), + r = /[\/^]leaflet[\-\._]?([\w\-\._]*)\.js\??/; + for (t = 0, i = s.length; i > t; t++) + if (((n = s[t].src || ""), n.match(r))) + return (o = n.split(r)[0]), (o ? o + "/" : "") + "images"; + })()), + (o.Marker = o.Layer.extend({ + options: { + pane: "markerPane", + nonBubblingEvents: ["click", "dblclick", "mouseover", "mouseout", "contextmenu"], + icon: new o.Icon.Default(), + interactive: !0, + keyboard: !0, + zIndexOffset: 0, + opacity: 1, + riseOffset: 250, + }, + initialize: function (t, e) { + o.setOptions(this, e), (this._latlng = o.latLng(t)); + }, + onAdd: function (t) { + (this._zoomAnimated = this._zoomAnimated && t.options.markerZoomAnimation), + this._initIcon(), + this.update(); + }, + onRemove: function () { + this.dragging && + this.dragging.enabled() && + ((this.options.draggable = !0), this.dragging.removeHooks()), + this._removeIcon(), + this._removeShadow(); + }, + getEvents: function () { + var t = { zoom: this.update, viewreset: this.update }; + return this._zoomAnimated && (t.zoomanim = this._animateZoom), t; + }, + getLatLng: function () { + return this._latlng; + }, + setLatLng: function (t) { + var e = this._latlng; + return ( + (this._latlng = o.latLng(t)), + this.update(), + this.fire("move", { oldLatLng: e, latlng: this._latlng }) + ); + }, + setZIndexOffset: function (t) { + return (this.options.zIndexOffset = t), this.update(); + }, + setIcon: function (t) { + return ( + (this.options.icon = t), + this._map && (this._initIcon(), this.update()), + this._popup && this.bindPopup(this._popup, this._popup.options), + this + ); + }, + getElement: function () { + return this._icon; + }, + update: function () { + if (this._icon) { + var t = this._map.latLngToLayerPoint(this._latlng).round(); + this._setPos(t); + } + return this; + }, + _initIcon: function () { + var t = this.options, + e = "leaflet-zoom-" + (this._zoomAnimated ? "animated" : "hide"), + i = t.icon.createIcon(this._icon), + n = !1; + i !== this._icon && + (this._icon && this._removeIcon(), + (n = !0), + t.title && (i.title = t.title), + t.alt && (i.alt = t.alt)), + o.DomUtil.addClass(i, e), + t.keyboard && (i.tabIndex = "0"), + (this._icon = i), + t.riseOnHover && this.on({ mouseover: this._bringToFront, mouseout: this._resetZIndex }); + var s = t.icon.createShadow(this._shadow), + r = !1; + s !== this._shadow && (this._removeShadow(), (r = !0)), + s && o.DomUtil.addClass(s, e), + (this._shadow = s), + t.opacity < 1 && this._updateOpacity(), + n && (this.getPane().appendChild(this._icon), this._initInteraction()), + s && r && this.getPane("shadowPane").appendChild(this._shadow); + }, + _removeIcon: function () { + this.options.riseOnHover && + this.off({ mouseover: this._bringToFront, mouseout: this._resetZIndex }), + o.DomUtil.remove(this._icon), + this.removeInteractiveTarget(this._icon), + (this._icon = null); + }, + _removeShadow: function () { + this._shadow && o.DomUtil.remove(this._shadow), (this._shadow = null); + }, + _setPos: function (t) { + o.DomUtil.setPosition(this._icon, t), + this._shadow && o.DomUtil.setPosition(this._shadow, t), + (this._zIndex = t.y + this.options.zIndexOffset), + this._resetZIndex(); + }, + _updateZIndex: function (t) { + this._icon.style.zIndex = this._zIndex + t; + }, + _animateZoom: function (t) { + var e = this._map._latLngToNewLayerPoint(this._latlng, t.zoom, t.center).round(); + this._setPos(e); + }, + _initInteraction: function () { + if ( + this.options.interactive && + (o.DomUtil.addClass(this._icon, "leaflet-interactive"), + this.addInteractiveTarget(this._icon), + o.Handler.MarkerDrag) + ) { + var t = this.options.draggable; + this.dragging && ((t = this.dragging.enabled()), this.dragging.disable()), + (this.dragging = new o.Handler.MarkerDrag(this)), + t && this.dragging.enable(); + } + }, + setOpacity: function (t) { + return (this.options.opacity = t), this._map && this._updateOpacity(), this; + }, + _updateOpacity: function () { + var t = this.options.opacity; + o.DomUtil.setOpacity(this._icon, t), this._shadow && o.DomUtil.setOpacity(this._shadow, t); + }, + _bringToFront: function () { + this._updateZIndex(this.options.riseOffset); + }, + _resetZIndex: function () { + this._updateZIndex(0); + }, + })), + (o.marker = function (t, e) { + return new o.Marker(t, e); + }), + (o.DivIcon = o.Icon.extend({ + options: { iconSize: [12, 12], className: "leaflet-div-icon", html: !1 }, + createIcon: function (t) { + var i = t && "DIV" === t.tagName ? t : e.createElement("div"), + n = this.options; + return ( + (i.innerHTML = n.html !== !1 ? n.html : ""), + n.bgPos && (i.style.backgroundPosition = -n.bgPos.x + "px " + -n.bgPos.y + "px"), + this._setIconStyles(i, "icon"), + i + ); + }, + createShadow: function () { + return null; + }, + })), + (o.divIcon = function (t) { + return new o.DivIcon(t); + }), + o.Map.mergeOptions({ closePopupOnClick: !0 }), + (o.Popup = o.Layer.extend({ + options: { + pane: "popupPane", + minWidth: 50, + maxWidth: 300, + offset: [0, 7], + autoPan: !0, + autoPanPadding: [5, 5], + closeButton: !0, + autoClose: !0, + zoomAnimation: !0, + }, + initialize: function (t, e) { + o.setOptions(this, t), (this._source = e); + }, + onAdd: function (t) { + (this._zoomAnimated = this._zoomAnimated && this.options.zoomAnimation), + this._container || this._initLayout(), + t._fadeAnimated && o.DomUtil.setOpacity(this._container, 0), + clearTimeout(this._removeTimeout), + this.getPane().appendChild(this._container), + this.update(), + t._fadeAnimated && o.DomUtil.setOpacity(this._container, 1), + t.fire("popupopen", { popup: this }), + this._source && this._source.fire("popupopen", { popup: this }, !0); + }, + openOn: function (t) { + return t.openPopup(this), this; + }, + onRemove: function (t) { + t._fadeAnimated + ? (o.DomUtil.setOpacity(this._container, 0), + (this._removeTimeout = setTimeout( + o.bind(o.DomUtil.remove, o.DomUtil, this._container), + 200 + ))) + : o.DomUtil.remove(this._container), + t.fire("popupclose", { popup: this }), + this._source && this._source.fire("popupclose", { popup: this }, !0); + }, + getLatLng: function () { + return this._latlng; + }, + setLatLng: function (t) { + return ( + (this._latlng = o.latLng(t)), + this._map && (this._updatePosition(), this._adjustPan()), + this + ); + }, + getContent: function () { + return this._content; + }, + setContent: function (t) { + return (this._content = t), this.update(), this; + }, + getElement: function () { + return this._container; + }, + update: function () { + this._map && + ((this._container.style.visibility = "hidden"), + this._updateContent(), + this._updateLayout(), + this._updatePosition(), + (this._container.style.visibility = ""), + this._adjustPan()); + }, + getEvents: function () { + var t = { zoom: this._updatePosition, viewreset: this._updatePosition }; + return ( + this._zoomAnimated && (t.zoomanim = this._animateZoom), + ("closeOnClick" in this.options + ? this.options.closeOnClick + : this._map.options.closePopupOnClick) && (t.preclick = this._close), + this.options.keepInView && (t.moveend = this._adjustPan), + t + ); + }, + isOpen: function () { + return !!this._map && this._map.hasLayer(this); + }, + bringToFront: function () { + return this._map && o.DomUtil.toFront(this._container), this; + }, + bringToBack: function () { + return this._map && o.DomUtil.toBack(this._container), this; + }, + _close: function () { + this._map && this._map.closePopup(this); + }, + _initLayout: function () { + var t = "leaflet-popup", + e = (this._container = o.DomUtil.create( + "div", + t + + " " + + (this.options.className || "") + + " leaflet-zoom-" + + (this._zoomAnimated ? "animated" : "hide") + )); + if (this.options.closeButton) { + var i = (this._closeButton = o.DomUtil.create("a", t + "-close-button", e)); + (i.href = "#close"), + (i.innerHTML = "×"), + o.DomEvent.on(i, "click", this._onCloseButtonClick, this); + } + var n = (this._wrapper = o.DomUtil.create("div", t + "-content-wrapper", e)); + (this._contentNode = o.DomUtil.create("div", t + "-content", n)), + o.DomEvent.disableClickPropagation(n) + .disableScrollPropagation(this._contentNode) + .on(n, "contextmenu", o.DomEvent.stopPropagation), + (this._tipContainer = o.DomUtil.create("div", t + "-tip-container", e)), + (this._tip = o.DomUtil.create("div", t + "-tip", this._tipContainer)); + }, + _updateContent: function () { + if (this._content) { + var t = this._contentNode, + e = + "function" == typeof this._content + ? this._content(this._source || this) + : this._content; + if ("string" == typeof e) t.innerHTML = e; + else { + for (; t.hasChildNodes(); ) t.removeChild(t.firstChild); + t.appendChild(e); + } + this.fire("contentupdate"); + } + }, + _updateLayout: function () { + var t = this._contentNode, + e = t.style; + (e.width = ""), (e.whiteSpace = "nowrap"); + var i = t.offsetWidth; + (i = Math.min(i, this.options.maxWidth)), + (i = Math.max(i, this.options.minWidth)), + (e.width = i + 1 + "px"), + (e.whiteSpace = ""), + (e.height = ""); + var n = t.offsetHeight, + s = this.options.maxHeight, + r = "leaflet-popup-scrolled"; + s && n > s ? ((e.height = s + "px"), o.DomUtil.addClass(t, r)) : o.DomUtil.removeClass(t, r), + (this._containerWidth = this._container.offsetWidth); + }, + _updatePosition: function () { + if (this._map) { + var t = this._map.latLngToLayerPoint(this._latlng), + e = o.point(this.options.offset); + this._zoomAnimated ? o.DomUtil.setPosition(this._container, t) : (e = e.add(t)); + var i = (this._containerBottom = -e.y), + n = (this._containerLeft = -Math.round(this._containerWidth / 2) + e.x); + (this._container.style.bottom = i + "px"), (this._container.style.left = n + "px"); + } + }, + _animateZoom: function (t) { + var e = this._map._latLngToNewLayerPoint(this._latlng, t.zoom, t.center); + o.DomUtil.setPosition(this._container, e); + }, + _adjustPan: function () { + if (!(!this.options.autoPan || (this._map._panAnim && this._map._panAnim._inProgress))) { + var t = this._map, + e = this._container.offsetHeight, + i = this._containerWidth, + n = new o.Point(this._containerLeft, -e - this._containerBottom); + this._zoomAnimated && n._add(o.DomUtil.getPosition(this._container)); + var s = t.layerPointToContainerPoint(n), + r = o.point(this.options.autoPanPadding), + a = o.point(this.options.autoPanPaddingTopLeft || r), + h = o.point(this.options.autoPanPaddingBottomRight || r), + l = t.getSize(), + u = 0, + c = 0; + s.x + i + h.x > l.x && (u = s.x + i - l.x + h.x), + s.x - u - a.x < 0 && (u = s.x - a.x), + s.y + e + h.y > l.y && (c = s.y + e - l.y + h.y), + s.y - c - a.y < 0 && (c = s.y - a.y), + (u || c) && t.fire("autopanstart").panBy([u, c]); + } + }, + _onCloseButtonClick: function (t) { + this._close(), o.DomEvent.stop(t); + }, + })), + (o.popup = function (t, e) { + return new o.Popup(t, e); + }), + o.Map.include({ + openPopup: function (t, e, i) { + return ( + t instanceof o.Popup || (t = new o.Popup(i).setContent(t)), + e && t.setLatLng(e), + this.hasLayer(t) + ? this + : (this._popup && this._popup.options.autoClose && this.closePopup(), + (this._popup = t), + this.addLayer(t)) + ); + }, + closePopup: function (t) { + return ( + (t && t !== this._popup) || ((t = this._popup), (this._popup = null)), + t && this.removeLayer(t), + this + ); + }, + }), + o.Layer.include({ + bindPopup: function (t, e) { + return ( + t instanceof o.Popup + ? (o.setOptions(t, e), (this._popup = t), (t._source = this)) + : ((!this._popup || e) && (this._popup = new o.Popup(e, this)), + this._popup.setContent(t)), + this._popupHandlersAdded || + (this.on({ click: this._openPopup, remove: this.closePopup, move: this._movePopup }), + (this._popupHandlersAdded = !0)), + (this._originalPopupOffset = this._popup.options.offset), + this + ); + }, + unbindPopup: function () { + return ( + this._popup && + (this.off({ click: this._openPopup, remove: this.closePopup, move: this._movePopup }), + (this._popupHandlersAdded = !1), + (this._popup = null)), + this + ); + }, + openPopup: function (t, e) { + if ((t instanceof o.Layer || ((e = t), (t = this)), t instanceof o.FeatureGroup)) + for (var i in this._layers) { + t = this._layers[i]; + break; + } + return ( + e || (e = t.getCenter ? t.getCenter() : t.getLatLng()), + this._popup && + this._map && + ((this._popup.options.offset = this._popupAnchor(t)), + (this._popup._source = t), + this._popup.update(), + this._map.openPopup(this._popup, e)), + this + ); + }, + closePopup: function () { + return this._popup && this._popup._close(), this; + }, + togglePopup: function (t) { + return this._popup && (this._popup._map ? this.closePopup() : this.openPopup(t)), this; + }, + isPopupOpen: function () { + return this._popup.isOpen(); + }, + setPopupContent: function (t) { + return this._popup && this._popup.setContent(t), this; + }, + getPopup: function () { + return this._popup; + }, + _openPopup: function (t) { + var e = t.layer || t.target; + if (this._popup && this._map) + return e instanceof o.Path + ? void this.openPopup(t.layer || t.target, t.latlng) + : void (this._map.hasLayer(this._popup) && this._popup._source === e + ? this.closePopup() + : this.openPopup(e, t.latlng)); + }, + _popupAnchor: function (t) { + var e = t._getPopupAnchor ? t._getPopupAnchor() : [0, 0], + i = this._originalPopupOffset || o.Popup.prototype.options.offset; + return o.point(e).add(i); + }, + _movePopup: function (t) { + this._popup.setLatLng(t.latlng); + }, + }), + o.Marker.include({ + _getPopupAnchor: function () { + return this.options.icon.options.popupAnchor || [0, 0]; + }, + }), + (o.LayerGroup = o.Layer.extend({ + initialize: function (t) { + this._layers = {}; + var e, i; + if (t) for (e = 0, i = t.length; i > e; e++) this.addLayer(t[e]); + }, + addLayer: function (t) { + var e = this.getLayerId(t); + return (this._layers[e] = t), this._map && this._map.addLayer(t), this; + }, + removeLayer: function (t) { + var e = t in this._layers ? t : this.getLayerId(t); + return ( + this._map && this._layers[e] && this._map.removeLayer(this._layers[e]), + delete this._layers[e], + this + ); + }, + hasLayer: function (t) { + return !!t && (t in this._layers || this.getLayerId(t) in this._layers); + }, + clearLayers: function () { + for (var t in this._layers) this.removeLayer(this._layers[t]); + return this; + }, + invoke: function (t) { + var e, + i, + n = Array.prototype.slice.call(arguments, 1); + for (e in this._layers) (i = this._layers[e]), i[t] && i[t].apply(i, n); + return this; + }, + onAdd: function (t) { + for (var e in this._layers) t.addLayer(this._layers[e]); + }, + onRemove: function (t) { + for (var e in this._layers) t.removeLayer(this._layers[e]); + }, + eachLayer: function (t, e) { + for (var i in this._layers) t.call(e, this._layers[i]); + return this; + }, + getLayer: function (t) { + return this._layers[t]; + }, + getLayers: function () { + var t = []; + for (var e in this._layers) t.push(this._layers[e]); + return t; + }, + setZIndex: function (t) { + return this.invoke("setZIndex", t); + }, + getLayerId: function (t) { + return o.stamp(t); + }, + })), + (o.layerGroup = function (t) { + return new o.LayerGroup(t); + }), + (o.FeatureGroup = o.LayerGroup.extend({ + addLayer: function (t) { + return this.hasLayer(t) + ? this + : (t.addEventParent(this), + o.LayerGroup.prototype.addLayer.call(this, t), + this.fire("layeradd", { layer: t })); + }, + removeLayer: function (t) { + return this.hasLayer(t) + ? (t in this._layers && (t = this._layers[t]), + t.removeEventParent(this), + o.LayerGroup.prototype.removeLayer.call(this, t), + this.fire("layerremove", { layer: t })) + : this; + }, + setStyle: function (t) { + return this.invoke("setStyle", t); + }, + bringToFront: function () { + return this.invoke("bringToFront"); + }, + bringToBack: function () { + return this.invoke("bringToBack"); + }, + getBounds: function () { + var t = new o.LatLngBounds(); + for (var e in this._layers) { + var i = this._layers[e]; + t.extend(i.getBounds ? i.getBounds() : i.getLatLng()); + } + return t; + }, + })), + (o.featureGroup = function (t) { + return new o.FeatureGroup(t); + }), + (o.Renderer = o.Layer.extend({ + options: { padding: 0.1 }, + initialize: function (t) { + o.setOptions(this, t), o.stamp(this); + }, + onAdd: function () { + this._container || + (this._initContainer(), + this._zoomAnimated && o.DomUtil.addClass(this._container, "leaflet-zoom-animated")), + this.getPane().appendChild(this._container), + this._update(); + }, + onRemove: function () { + o.DomUtil.remove(this._container); + }, + getEvents: function () { + var t = { + viewreset: this._reset, + zoomstart: this._onZoomStart, + zoom: this._onZoom, + moveend: this._update, + }; + return this._zoomAnimated && (t.zoomanim = this._onAnimZoom), t; + }, + _onAnimZoom: function (t) { + this._updateTransform(t.center, t.zoom); + }, + _onZoom: function () { + this._updateTransform(this._map.getCenter(), this._map.getZoom()); + }, + _onZoomStart: function () { + this._update(); + }, + _updateTransform: function (t, e) { + var i = this._map.getZoomScale(e, this._zoom), + n = o.DomUtil.getPosition(this._container), + s = this._map.getSize().multiplyBy(0.5 + this.options.padding), + r = this._map.project(this._center, e), + a = this._map.project(t, e), + h = a.subtract(r), + l = s.multiplyBy(-i).add(n).add(s).subtract(h); + o.DomUtil.setTransform(this._container, l, i); + }, + _reset: function () { + this._update(), this._updateTransform(this._center, this._zoom); + }, + _update: function () { + var t = this.options.padding, + e = this._map.getSize(), + i = this._map.containerPointToLayerPoint(e.multiplyBy(-t)).round(); + (this._bounds = new o.Bounds(i, i.add(e.multiplyBy(1 + 2 * t)).round())), + (this._center = this._map.getCenter()), + (this._zoom = this._map.getZoom()); + }, + })), + o.Map.include({ + getRenderer: function (t) { + var e = + t.options.renderer || + this._getPaneRenderer(t.options.pane) || + this.options.renderer || + this._renderer; + return ( + e || (e = this._renderer = (this.options.preferCanvas && o.canvas()) || o.svg()), + this.hasLayer(e) || this.addLayer(e), + e + ); + }, + _getPaneRenderer: function (t) { + if ("overlayPane" === t || t === i) return !1; + var e = this._paneRenderers[t]; + return ( + e === i && + ((e = (o.SVG && o.svg({ pane: t })) || (o.Canvas && o.canvas({ pane: t }))), + (this._paneRenderers[t] = e)), + e + ); + }, + }), + (o.Path = o.Layer.extend({ + options: { + stroke: !0, + color: "#3388ff", + weight: 3, + opacity: 1, + lineCap: "round", + lineJoin: "round", + fillOpacity: 0.2, + fillRule: "evenodd", + interactive: !0, + }, + beforeAdd: function (t) { + this._renderer = t.getRenderer(this); + }, + onAdd: function () { + this._renderer._initPath(this), this._reset(), this._renderer._addPath(this); + }, + onRemove: function () { + this._renderer._removePath(this); + }, + getEvents: function () { + return { zoomend: this._project, moveend: this._update, viewreset: this._reset }; + }, + redraw: function () { + return this._map && this._renderer._updatePath(this), this; + }, + setStyle: function (t) { + return o.setOptions(this, t), this._renderer && this._renderer._updateStyle(this), this; + }, + bringToFront: function () { + return this._renderer && this._renderer._bringToFront(this), this; + }, + bringToBack: function () { + return this._renderer && this._renderer._bringToBack(this), this; + }, + getElement: function () { + return this._path; + }, + _reset: function () { + this._project(), this._update(); + }, + _clickTolerance: function () { + return (this.options.stroke ? this.options.weight / 2 : 0) + (o.Browser.touch ? 10 : 0); + }, + })), + (o.LineUtil = { + simplify: function (t, e) { + if (!e || !t.length) return t.slice(); + var i = e * e; + return (t = this._reducePoints(t, i)), (t = this._simplifyDP(t, i)); + }, + pointToSegmentDistance: function (t, e, i) { + return Math.sqrt(this._sqClosestPointOnSegment(t, e, i, !0)); + }, + closestPointOnSegment: function (t, e, i) { + return this._sqClosestPointOnSegment(t, e, i); + }, + _simplifyDP: function (t, e) { + var n = t.length, + o = typeof Uint8Array != i + "" ? Uint8Array : Array, + s = new o(n); + (s[0] = s[n - 1] = 1), this._simplifyDPStep(t, s, e, 0, n - 1); + var r, + a = []; + for (r = 0; n > r; r++) s[r] && a.push(t[r]); + return a; + }, + _simplifyDPStep: function (t, e, i, n, o) { + var s, + r, + a, + h = 0; + for (r = n + 1; o - 1 >= r; r++) + (a = this._sqClosestPointOnSegment(t[r], t[n], t[o], !0)), a > h && ((s = r), (h = a)); + h > i && + ((e[s] = 1), this._simplifyDPStep(t, e, i, n, s), this._simplifyDPStep(t, e, i, s, o)); + }, + _reducePoints: function (t, e) { + for (var i = [t[0]], n = 1, o = 0, s = t.length; s > n; n++) + this._sqDist(t[n], t[o]) > e && (i.push(t[n]), (o = n)); + return s - 1 > o && i.push(t[s - 1]), i; + }, + clipSegment: function (t, e, i, n, o) { + var s, + r, + a, + h = n ? this._lastCode : this._getBitCode(t, i), + l = this._getBitCode(e, i); + for (this._lastCode = l; ; ) { + if (!(h | l)) return [t, e]; + if (h & l) return !1; + (s = h || l), + (r = this._getEdgeIntersection(t, e, s, i, o)), + (a = this._getBitCode(r, i)), + s === h ? ((t = r), (h = a)) : ((e = r), (l = a)); + } + }, + _getEdgeIntersection: function (t, e, i, n, s) { + var r, + a, + h = e.x - t.x, + l = e.y - t.y, + u = n.min, + c = n.max; + return ( + 8 & i + ? ((r = t.x + (h * (c.y - t.y)) / l), (a = c.y)) + : 4 & i + ? ((r = t.x + (h * (u.y - t.y)) / l), (a = u.y)) + : 2 & i + ? ((r = c.x), (a = t.y + (l * (c.x - t.x)) / h)) + : 1 & i && ((r = u.x), (a = t.y + (l * (u.x - t.x)) / h)), + new o.Point(r, a, s) + ); + }, + _getBitCode: function (t, e) { + var i = 0; + return ( + t.x < e.min.x ? (i |= 1) : t.x > e.max.x && (i |= 2), + t.y < e.min.y ? (i |= 4) : t.y > e.max.y && (i |= 8), + i + ); + }, + _sqDist: function (t, e) { + var i = e.x - t.x, + n = e.y - t.y; + return i * i + n * n; + }, + _sqClosestPointOnSegment: function (t, e, i, n) { + var s, + r = e.x, + a = e.y, + h = i.x - r, + l = i.y - a, + u = h * h + l * l; + return ( + u > 0 && + ((s = ((t.x - r) * h + (t.y - a) * l) / u), + s > 1 ? ((r = i.x), (a = i.y)) : s > 0 && ((r += h * s), (a += l * s))), + (h = t.x - r), + (l = t.y - a), + n ? h * h + l * l : new o.Point(r, a) + ); + }, + }), + (o.Polyline = o.Path.extend({ + options: { smoothFactor: 1 }, + initialize: function (t, e) { + o.setOptions(this, e), this._setLatLngs(t); + }, + getLatLngs: function () { + return this._latlngs; + }, + setLatLngs: function (t) { + return this._setLatLngs(t), this.redraw(); + }, + isEmpty: function () { + return !this._latlngs.length; + }, + closestLayerPoint: function (t) { + for ( + var e, + i, + n = 1 / 0, + s = null, + r = o.LineUtil._sqClosestPointOnSegment, + a = 0, + h = this._parts.length; + h > a; + a++ + ) + for (var l = this._parts[a], u = 1, c = l.length; c > u; u++) { + (e = l[u - 1]), (i = l[u]); + var d = r(t, e, i, !0); + n > d && ((n = d), (s = r(t, e, i))); + } + return s && (s.distance = Math.sqrt(n)), s; + }, + getCenter: function () { + var t, + e, + i, + n, + o, + s, + r, + a = this._rings[0], + h = a.length; + if (!h) return null; + for (t = 0, e = 0; h - 1 > t; t++) e += a[t].distanceTo(a[t + 1]) / 2; + if (0 === e) return this._map.layerPointToLatLng(a[0]); + for (t = 0, n = 0; h - 1 > t; t++) + if (((o = a[t]), (s = a[t + 1]), (i = o.distanceTo(s)), (n += i), n > e)) + return ( + (r = (n - e) / i), + this._map.layerPointToLatLng([s.x - r * (s.x - o.x), s.y - r * (s.y - o.y)]) + ); + }, + getBounds: function () { + return this._bounds; + }, + addLatLng: function (t, e) { + return ( + (e = e || this._defaultShape()), + (t = o.latLng(t)), + e.push(t), + this._bounds.extend(t), + this.redraw() + ); + }, + _setLatLngs: function (t) { + (this._bounds = new o.LatLngBounds()), (this._latlngs = this._convertLatLngs(t)); + }, + _defaultShape: function () { + return o.Polyline._flat(this._latlngs) ? this._latlngs : this._latlngs[0]; + }, + _convertLatLngs: function (t) { + for (var e = [], i = o.Polyline._flat(t), n = 0, s = t.length; s > n; n++) + i + ? ((e[n] = o.latLng(t[n])), this._bounds.extend(e[n])) + : (e[n] = this._convertLatLngs(t[n])); + return e; + }, + _project: function () { + (this._rings = []), this._projectLatlngs(this._latlngs, this._rings); + var t = this._clickTolerance(), + e = new o.Point(t, -t); + this._bounds.isValid() && + (this._pxBounds = new o.Bounds( + this._map.latLngToLayerPoint(this._bounds.getSouthWest())._subtract(e), + this._map.latLngToLayerPoint(this._bounds.getNorthEast())._add(e) + )); + }, + _projectLatlngs: function (t, e) { + var i, + n, + s = t[0] instanceof o.LatLng, + r = t.length; + if (s) { + for (n = [], i = 0; r > i; i++) n[i] = this._map.latLngToLayerPoint(t[i]); + e.push(n); + } else for (i = 0; r > i; i++) this._projectLatlngs(t[i], e); + }, + _clipPoints: function () { + var t = this._renderer._bounds; + if (((this._parts = []), this._pxBounds && this._pxBounds.intersects(t))) { + if (this.options.noClip) return void (this._parts = this._rings); + var e, + i, + n, + s, + r, + a, + h, + l = this._parts; + for (e = 0, n = 0, s = this._rings.length; s > e; e++) + for (h = this._rings[e], i = 0, r = h.length; r - 1 > i; i++) + (a = o.LineUtil.clipSegment(h[i], h[i + 1], t, i, !0)), + a && + ((l[n] = l[n] || []), + l[n].push(a[0]), + (a[1] !== h[i + 1] || i === r - 2) && (l[n].push(a[1]), n++)); + } + }, + _simplifyPoints: function () { + for (var t = this._parts, e = this.options.smoothFactor, i = 0, n = t.length; n > i; i++) + t[i] = o.LineUtil.simplify(t[i], e); + }, + _update: function () { + this._map && (this._clipPoints(), this._simplifyPoints(), this._updatePath()); + }, + _updatePath: function () { + this._renderer._updatePoly(this); + }, + })), + (o.polyline = function (t, e) { + return new o.Polyline(t, e); + }), + (o.Polyline._flat = function (t) { + return !o.Util.isArray(t[0]) || ("object" != typeof t[0][0] && "undefined" != typeof t[0][0]); + }), + (o.PolyUtil = {}), + (o.PolyUtil.clipPolygon = function (t, e, i) { + var n, + s, + r, + a, + h, + l, + u, + c, + d, + _ = [1, 4, 2, 8], + m = o.LineUtil; + for (s = 0, u = t.length; u > s; s++) t[s]._code = m._getBitCode(t[s], e); + for (a = 0; 4 > a; a++) { + for (c = _[a], n = [], s = 0, u = t.length, r = u - 1; u > s; r = s++) + (h = t[s]), + (l = t[r]), + h._code & c + ? l._code & c || + ((d = m._getEdgeIntersection(l, h, c, e, i)), + (d._code = m._getBitCode(d, e)), + n.push(d)) + : (l._code & c && + ((d = m._getEdgeIntersection(l, h, c, e, i)), + (d._code = m._getBitCode(d, e)), + n.push(d)), + n.push(h)); + t = n; + } + return t; + }), + (o.Polygon = o.Polyline.extend({ + options: { fill: !0 }, + isEmpty: function () { + return !this._latlngs.length || !this._latlngs[0].length; + }, + getCenter: function () { + var t, + e, + i, + n, + o, + s, + r, + a, + h, + l = this._rings[0], + u = l.length; + if (!u) return null; + for (s = r = a = 0, t = 0, e = u - 1; u > t; e = t++) + (i = l[t]), + (n = l[e]), + (o = i.y * n.x - n.y * i.x), + (r += (i.x + n.x) * o), + (a += (i.y + n.y) * o), + (s += 3 * o); + return (h = 0 === s ? l[0] : [r / s, a / s]), this._map.layerPointToLatLng(h); + }, + _convertLatLngs: function (t) { + var e = o.Polyline.prototype._convertLatLngs.call(this, t), + i = e.length; + return i >= 2 && e[0] instanceof o.LatLng && e[0].equals(e[i - 1]) && e.pop(), e; + }, + _setLatLngs: function (t) { + o.Polyline.prototype._setLatLngs.call(this, t), + o.Polyline._flat(this._latlngs) && (this._latlngs = [this._latlngs]); + }, + _defaultShape: function () { + return o.Polyline._flat(this._latlngs[0]) ? this._latlngs[0] : this._latlngs[0][0]; + }, + _clipPoints: function () { + var t = this._renderer._bounds, + e = this.options.weight, + i = new o.Point(e, e); + if ( + ((t = new o.Bounds(t.min.subtract(i), t.max.add(i))), + (this._parts = []), + this._pxBounds && this._pxBounds.intersects(t)) + ) { + if (this.options.noClip) return void (this._parts = this._rings); + for (var n, s = 0, r = this._rings.length; r > s; s++) + (n = o.PolyUtil.clipPolygon(this._rings[s], t, !0)), n.length && this._parts.push(n); + } + }, + _updatePath: function () { + this._renderer._updatePoly(this, !0); + }, + })), + (o.polygon = function (t, e) { + return new o.Polygon(t, e); + }), + (o.Rectangle = o.Polygon.extend({ + initialize: function (t, e) { + o.Polygon.prototype.initialize.call(this, this._boundsToLatLngs(t), e); + }, + setBounds: function (t) { + return this.setLatLngs(this._boundsToLatLngs(t)); + }, + _boundsToLatLngs: function (t) { + return ( + (t = o.latLngBounds(t)), + [t.getSouthWest(), t.getNorthWest(), t.getNorthEast(), t.getSouthEast()] + ); + }, + })), + (o.rectangle = function (t, e) { + return new o.Rectangle(t, e); + }), + (o.CircleMarker = o.Path.extend({ + options: { fill: !0, radius: 10 }, + initialize: function (t, e) { + o.setOptions(this, e), (this._latlng = o.latLng(t)), (this._radius = this.options.radius); + }, + setLatLng: function (t) { + return ( + (this._latlng = o.latLng(t)), this.redraw(), this.fire("move", { latlng: this._latlng }) + ); + }, + getLatLng: function () { + return this._latlng; + }, + setRadius: function (t) { + return (this.options.radius = this._radius = t), this.redraw(); + }, + getRadius: function () { + return this._radius; + }, + setStyle: function (t) { + var e = (t && t.radius) || this._radius; + return o.Path.prototype.setStyle.call(this, t), this.setRadius(e), this; + }, + _project: function () { + (this._point = this._map.latLngToLayerPoint(this._latlng)), this._updateBounds(); + }, + _updateBounds: function () { + var t = this._radius, + e = this._radiusY || t, + i = this._clickTolerance(), + n = [t + i, e + i]; + this._pxBounds = new o.Bounds(this._point.subtract(n), this._point.add(n)); + }, + _update: function () { + this._map && this._updatePath(); + }, + _updatePath: function () { + this._renderer._updateCircle(this); + }, + _empty: function () { + return this._radius && !this._renderer._bounds.intersects(this._pxBounds); + }, + })), + (o.circleMarker = function (t, e) { + return new o.CircleMarker(t, e); + }), + (o.Circle = o.CircleMarker.extend({ + initialize: function (t, e) { + o.setOptions(this, e), (this._latlng = o.latLng(t)), (this._mRadius = this.options.radius); + }, + setRadius: function (t) { + return (this._mRadius = t), this.redraw(); + }, + getRadius: function () { + return this._mRadius; + }, + getBounds: function () { + var t = [this._radius, this._radiusY || this._radius]; + return new o.LatLngBounds( + this._map.layerPointToLatLng(this._point.subtract(t)), + this._map.layerPointToLatLng(this._point.add(t)) + ); + }, + setStyle: o.Path.prototype.setStyle, + _project: function () { + var t = this._latlng.lng, + e = this._latlng.lat, + i = this._map, + n = i.options.crs; + if (n.distance === o.CRS.Earth.distance) { + var s = Math.PI / 180, + r = this._mRadius / o.CRS.Earth.R / s, + a = i.project([e + r, t]), + h = i.project([e - r, t]), + l = a.add(h).divideBy(2), + u = i.unproject(l).lat, + c = + Math.acos( + (Math.cos(r * s) - Math.sin(e * s) * Math.sin(u * s)) / + (Math.cos(e * s) * Math.cos(u * s)) + ) / s; + (this._point = l.subtract(i.getPixelOrigin())), + (this._radius = isNaN(c) + ? 0 + : Math.max(Math.round(l.x - i.project([u, t - c]).x), 1)), + (this._radiusY = Math.max(Math.round(l.y - a.y), 1)); + } else { + var d = n.unproject(n.project(this._latlng).subtract([this._mRadius, 0])); + (this._point = i.latLngToLayerPoint(this._latlng)), + (this._radius = this._point.x - i.latLngToLayerPoint(d).x); + } + this._updateBounds(); + }, + })), + (o.circle = function (t, e, i) { + return "number" == typeof e && (e = o.extend({}, i, { radius: e })), new o.Circle(t, e); + }), + (o.SVG = o.Renderer.extend({ + _initContainer: function () { + (this._container = o.SVG.create("svg")), + this._container.setAttribute("pointer-events", "none"), + (this._rootGroup = o.SVG.create("g")), + this._container.appendChild(this._rootGroup); + }, + _update: function () { + if (!this._map._animatingZoom || !this._bounds) { + o.Renderer.prototype._update.call(this); + var t = this._bounds, + e = t.getSize(), + i = this._container; + (this._svgSize && this._svgSize.equals(e)) || + ((this._svgSize = e), i.setAttribute("width", e.x), i.setAttribute("height", e.y)), + o.DomUtil.setPosition(i, t.min), + i.setAttribute("viewBox", [t.min.x, t.min.y, e.x, e.y].join(" ")); + } + }, + _initPath: function (t) { + var e = (t._path = o.SVG.create("path")); + t.options.className && o.DomUtil.addClass(e, t.options.className), + t.options.interactive && o.DomUtil.addClass(e, "leaflet-interactive"), + this._updateStyle(t); + }, + _addPath: function (t) { + this._rootGroup.appendChild(t._path), t.addInteractiveTarget(t._path); + }, + _removePath: function (t) { + o.DomUtil.remove(t._path), t.removeInteractiveTarget(t._path); + }, + _updatePath: function (t) { + t._project(), t._update(); + }, + _updateStyle: function (t) { + var e = t._path, + i = t.options; + e && + (i.stroke + ? (e.setAttribute("stroke", i.color), + e.setAttribute("stroke-opacity", i.opacity), + e.setAttribute("stroke-width", i.weight), + e.setAttribute("stroke-linecap", i.lineCap), + e.setAttribute("stroke-linejoin", i.lineJoin), + i.dashArray + ? e.setAttribute("stroke-dasharray", i.dashArray) + : e.removeAttribute("stroke-dasharray"), + i.dashOffset + ? e.setAttribute("stroke-dashoffset", i.dashOffset) + : e.removeAttribute("stroke-dashoffset")) + : e.setAttribute("stroke", "none"), + i.fill + ? (e.setAttribute("fill", i.fillColor || i.color), + e.setAttribute("fill-opacity", i.fillOpacity), + e.setAttribute("fill-rule", i.fillRule || "evenodd")) + : e.setAttribute("fill", "none"), + e.setAttribute( + "pointer-events", + i.pointerEvents || (i.interactive ? "visiblePainted" : "none") + )); + }, + _updatePoly: function (t, e) { + this._setPath(t, o.SVG.pointsToPath(t._parts, e)); + }, + _updateCircle: function (t) { + var e = t._point, + i = t._radius, + n = t._radiusY || i, + o = "a" + i + "," + n + " 0 1,0 ", + s = t._empty() + ? "M0 0" + : "M" + (e.x - i) + "," + e.y + o + 2 * i + ",0 " + o + 2 * -i + ",0 "; + this._setPath(t, s); + }, + _setPath: function (t, e) { + t._path.setAttribute("d", e); + }, + _bringToFront: function (t) { + o.DomUtil.toFront(t._path); + }, + _bringToBack: function (t) { + o.DomUtil.toBack(t._path); + }, + })), + o.extend(o.SVG, { + create: function (t) { + return e.createElementNS("http://www.w3.org/2000/svg", t); + }, + pointsToPath: function (t, e) { + var i, + n, + s, + r, + a, + h, + l = ""; + for (i = 0, s = t.length; s > i; i++) { + for (a = t[i], n = 0, r = a.length; r > n; n++) + (h = a[n]), (l += (n ? "L" : "M") + h.x + " " + h.y); + l += e ? (o.Browser.svg ? "z" : "x") : ""; + } + return l || "M0 0"; + }, + }), + (o.Browser.svg = !(!e.createElementNS || !o.SVG.create("svg").createSVGRect)), + (o.svg = function (t) { + return o.Browser.svg || o.Browser.vml ? new o.SVG(t) : null; + }), + (o.Browser.vml = + !o.Browser.svg && + (function () { + try { + var t = e.createElement("div"); + t.innerHTML = ''; + var i = t.firstChild; + return (i.style.behavior = "url(#default#VML)"), i && "object" == typeof i.adj; + } catch (n) { + return !1; + } + })()), + o.SVG.include( + o.Browser.vml + ? { + _initContainer: function () { + this._container = o.DomUtil.create("div", "leaflet-vml-container"); + }, + _update: function () { + this._map._animatingZoom || o.Renderer.prototype._update.call(this); + }, + _initPath: function (t) { + var e = (t._container = o.SVG.create("shape")); + o.DomUtil.addClass(e, "leaflet-vml-shape " + (this.options.className || "")), + (e.coordsize = "1 1"), + (t._path = o.SVG.create("path")), + e.appendChild(t._path), + this._updateStyle(t); + }, + _addPath: function (t) { + var e = t._container; + this._container.appendChild(e), + t.options.interactive && t.addInteractiveTarget(e); + }, + _removePath: function (t) { + var e = t._container; + o.DomUtil.remove(e), t.removeInteractiveTarget(e); + }, + _updateStyle: function (t) { + var e = t._stroke, + i = t._fill, + n = t.options, + s = t._container; + (s.stroked = !!n.stroke), + (s.filled = !!n.fill), + n.stroke + ? (e || (e = t._stroke = o.SVG.create("stroke")), + s.appendChild(e), + (e.weight = n.weight + "px"), + (e.color = n.color), + (e.opacity = n.opacity), + n.dashArray + ? (e.dashStyle = o.Util.isArray(n.dashArray) + ? n.dashArray.join(" ") + : n.dashArray.replace(/( *, *)/g, " ")) + : (e.dashStyle = ""), + (e.endcap = n.lineCap.replace("butt", "flat")), + (e.joinstyle = n.lineJoin)) + : e && (s.removeChild(e), (t._stroke = null)), + n.fill + ? (i || (i = t._fill = o.SVG.create("fill")), + s.appendChild(i), + (i.color = n.fillColor || n.color), + (i.opacity = n.fillOpacity)) + : i && (s.removeChild(i), (t._fill = null)); + }, + _updateCircle: function (t) { + var e = t._point.round(), + i = Math.round(t._radius), + n = Math.round(t._radiusY || i); + this._setPath( + t, + t._empty() + ? "M0 0" + : "AL " + e.x + "," + e.y + " " + i + "," + n + " 0,23592600" + ); + }, + _setPath: function (t, e) { + t._path.v = e; + }, + _bringToFront: function (t) { + o.DomUtil.toFront(t._container); + }, + _bringToBack: function (t) { + o.DomUtil.toBack(t._container); + }, + } + : {} + ), + o.Browser.vml && + (o.SVG.create = (function () { + try { + return ( + e.namespaces.add("lvml", "urn:schemas-microsoft-com:vml"), + function (t) { + return e.createElement("'); + } + ); + } catch (t) { + return function (t) { + return e.createElement( + "<" + t + ' xmlns="urn:schemas-microsoft.com:vml" class="lvml">' + ); + }; + } + })()), + (o.Canvas = o.Renderer.extend({ + onAdd: function () { + o.Renderer.prototype.onAdd.call(this), (this._layers = this._layers || {}), this._draw(); + }, + _initContainer: function () { + var t = (this._container = e.createElement("canvas")); + o.DomEvent.on(t, "mousemove", o.Util.throttle(this._onMouseMove, 32, this), this) + .on(t, "click dblclick mousedown mouseup contextmenu", this._onClick, this) + .on(t, "mouseout", this._handleMouseOut, this), + (this._ctx = t.getContext("2d")); + }, + _update: function () { + if (!this._map._animatingZoom || !this._bounds) { + (this._drawnLayers = {}), o.Renderer.prototype._update.call(this); + var t = this._bounds, + e = this._container, + i = t.getSize(), + n = o.Browser.retina ? 2 : 1; + o.DomUtil.setPosition(e, t.min), + (e.width = n * i.x), + (e.height = n * i.y), + (e.style.width = i.x + "px"), + (e.style.height = i.y + "px"), + o.Browser.retina && this._ctx.scale(2, 2), + this._ctx.translate(-t.min.x, -t.min.y); + } + }, + _initPath: function (t) { + this._layers[o.stamp(t)] = t; + }, + _addPath: o.Util.falseFn, + _removePath: function (t) { + (t._removed = !0), this._requestRedraw(t); + }, + _updatePath: function (t) { + (this._redrawBounds = t._pxBounds), + this._draw(!0), + t._project(), + t._update(), + this._draw(), + (this._redrawBounds = null); + }, + _updateStyle: function (t) { + this._requestRedraw(t); + }, + _requestRedraw: function (t) { + if (this._map) { + var e = (t.options.weight || 0) + 1; + (this._redrawBounds = this._redrawBounds || new o.Bounds()), + this._redrawBounds.extend(t._pxBounds.min.subtract([e, e])), + this._redrawBounds.extend(t._pxBounds.max.add([e, e])), + (this._redrawRequest = + this._redrawRequest || o.Util.requestAnimFrame(this._redraw, this)); + } + }, + _redraw: function () { + (this._redrawRequest = null), this._draw(!0), this._draw(), (this._redrawBounds = null); + }, + _draw: function (t) { + this._clear = t; + var e, + i = this._redrawBounds; + this._ctx.save(), + i && + (this._ctx.beginPath(), + this._ctx.rect(i.min.x, i.min.y, i.max.x - i.min.x, i.max.y - i.min.y), + this._ctx.clip()); + for (var n in this._layers) + (e = this._layers[n]), + (!i || e._pxBounds.intersects(i)) && e._updatePath(), + t && e._removed && (delete e._removed, delete this._layers[n]); + this._ctx.restore(); + }, + _updatePoly: function (t, e) { + var i, + n, + o, + s, + r = t._parts, + a = r.length, + h = this._ctx; + if (a) { + for (this._drawnLayers[t._leaflet_id] = t, h.beginPath(), i = 0; a > i; i++) { + for (n = 0, o = r[i].length; o > n; n++) + (s = r[i][n]), h[n ? "lineTo" : "moveTo"](s.x, s.y); + e && h.closePath(); + } + this._fillStroke(h, t); + } + }, + _updateCircle: function (t) { + if (!t._empty()) { + var e = t._point, + i = this._ctx, + n = t._radius, + o = (t._radiusY || n) / n; + 1 !== o && (i.save(), i.scale(1, o)), + i.beginPath(), + i.arc(e.x, e.y / o, n, 0, 2 * Math.PI, !1), + 1 !== o && i.restore(), + this._fillStroke(i, t); + } + }, + _fillStroke: function (t, e) { + var i = this._clear, + n = e.options; + (t.globalCompositeOperation = i ? "destination-out" : "source-over"), + n.fill && + ((t.globalAlpha = i ? 1 : n.fillOpacity), + (t.fillStyle = n.fillColor || n.color), + t.fill(n.fillRule || "evenodd")), + n.stroke && + 0 !== n.weight && + ((t.globalAlpha = i ? 1 : n.opacity), + (e._prevWeight = t.lineWidth = i ? e._prevWeight + 1 : n.weight), + (t.strokeStyle = n.color), + (t.lineCap = n.lineCap), + (t.lineJoin = n.lineJoin), + t.stroke()); + }, + _onClick: function (t) { + var e = this._map.mouseEventToLayerPoint(t), + i = []; + for (var n in this._layers) + this._layers[n]._containsPoint(e) && (o.DomEvent._fakeStop(t), i.push(this._layers[n])); + i.length && this._fireEvent(i, t); + }, + _onMouseMove: function (t) { + if (this._map && !this._map.dragging._draggable._moving && !this._map._animatingZoom) { + var e = this._map.mouseEventToLayerPoint(t); + this._handleMouseOut(t, e), this._handleMouseHover(t, e); + } + }, + _handleMouseOut: function (t, e) { + var i = this._hoveredLayer; + !i || + ("mouseout" !== t.type && i._containsPoint(e)) || + (o.DomUtil.removeClass(this._container, "leaflet-interactive"), + this._fireEvent([i], t, "mouseout"), + (this._hoveredLayer = null)); + }, + _handleMouseHover: function (t, e) { + var i, n; + if (!this._hoveredLayer) + for (i in this._drawnLayers) + if (((n = this._drawnLayers[i]), n.options.interactive && n._containsPoint(e))) { + o.DomUtil.addClass(this._container, "leaflet-interactive"), + this._fireEvent([n], t, "mouseover"), + (this._hoveredLayer = n); + break; + } + this._hoveredLayer && this._fireEvent([this._hoveredLayer], t); + }, + _fireEvent: function (t, e, i) { + this._map._fireDOMEvent(e, i || e.type, t); + }, + _bringToFront: o.Util.falseFn, + _bringToBack: o.Util.falseFn, + })), + (o.Browser.canvas = (function () { + return !!e.createElement("canvas").getContext; + })()), + (o.canvas = function (t) { + return o.Browser.canvas ? new o.Canvas(t) : null; + }), + (o.Polyline.prototype._containsPoint = function (t, e) { + var i, + n, + s, + r, + a, + h, + l = this._clickTolerance(); + if (!this._pxBounds.contains(t)) return !1; + for (i = 0, r = this._parts.length; r > i; i++) + for (h = this._parts[i], n = 0, a = h.length, s = a - 1; a > n; s = n++) + if ((e || 0 !== n) && o.LineUtil.pointToSegmentDistance(t, h[s], h[n]) <= l) return !0; + return !1; + }), + (o.Polygon.prototype._containsPoint = function (t) { + var e, + i, + n, + s, + r, + a, + h, + l, + u = !1; + if (!this._pxBounds.contains(t)) return !1; + for (s = 0, h = this._parts.length; h > s; s++) + for (e = this._parts[s], r = 0, l = e.length, a = l - 1; l > r; a = r++) + (i = e[r]), + (n = e[a]), + i.y > t.y != n.y > t.y && + t.x < ((n.x - i.x) * (t.y - i.y)) / (n.y - i.y) + i.x && + (u = !u); + return u || o.Polyline.prototype._containsPoint.call(this, t, !0); + }), + (o.CircleMarker.prototype._containsPoint = function (t) { + return t.distanceTo(this._point) <= this._radius + this._clickTolerance(); + }), + (o.GeoJSON = o.FeatureGroup.extend({ + initialize: function (t, e) { + o.setOptions(this, e), (this._layers = {}), t && this.addData(t); + }, + addData: function (t) { + var e, + i, + n, + s = o.Util.isArray(t) ? t : t.features; + if (s) { + for (e = 0, i = s.length; i > e; e++) + (n = s[e]), + (n.geometries || n.geometry || n.features || n.coordinates) && this.addData(n); + return this; + } + var r = this.options; + if (r.filter && !r.filter(t)) return this; + var a = o.GeoJSON.geometryToLayer(t, r); + return a + ? ((a.feature = o.GeoJSON.asFeature(t)), + (a.defaultOptions = a.options), + this.resetStyle(a), + r.onEachFeature && r.onEachFeature(t, a), + this.addLayer(a)) + : this; + }, + resetStyle: function (t) { + return (t.options = t.defaultOptions), this._setLayerStyle(t, this.options.style), this; + }, + setStyle: function (t) { + return this.eachLayer(function (e) { + this._setLayerStyle(e, t); + }, this); + }, + _setLayerStyle: function (t, e) { + "function" == typeof e && (e = e(t.feature)), t.setStyle && t.setStyle(e); + }, + })), + o.extend(o.GeoJSON, { + geometryToLayer: function (t, e) { + var i, + n, + s, + r, + a = "Feature" === t.type ? t.geometry : t, + h = a ? a.coordinates : null, + l = [], + u = e && e.pointToLayer, + c = (e && e.coordsToLatLng) || this.coordsToLatLng; + if (!h && !a) return null; + switch (a.type) { + case "Point": + return (i = c(h)), u ? u(t, i) : new o.Marker(i); + case "MultiPoint": + for (s = 0, r = h.length; r > s; s++) + (i = c(h[s])), l.push(u ? u(t, i) : new o.Marker(i)); + return new o.FeatureGroup(l); + case "LineString": + case "MultiLineString": + return ( + (n = this.coordsToLatLngs(h, "LineString" === a.type ? 0 : 1, c)), + new o.Polyline(n, e) + ); + case "Polygon": + case "MultiPolygon": + return ( + (n = this.coordsToLatLngs(h, "Polygon" === a.type ? 1 : 2, c)), + new o.Polygon(n, e) + ); + case "GeometryCollection": + for (s = 0, r = a.geometries.length; r > s; s++) { + var d = this.geometryToLayer( + { geometry: a.geometries[s], type: "Feature", properties: t.properties }, + e + ); + d && l.push(d); + } + return new o.FeatureGroup(l); + default: + throw new Error("Invalid GeoJSON object."); + } + }, + coordsToLatLng: function (t) { + return new o.LatLng(t[1], t[0], t[2]); + }, + coordsToLatLngs: function (t, e, i) { + for (var n, o = [], s = 0, r = t.length; r > s; s++) + (n = e ? this.coordsToLatLngs(t[s], e - 1, i) : (i || this.coordsToLatLng)(t[s])), + o.push(n); + return o; + }, + latLngToCoords: function (t) { + return t.alt !== i ? [t.lng, t.lat, t.alt] : [t.lng, t.lat]; + }, + latLngsToCoords: function (t, e, i) { + for (var n = [], s = 0, r = t.length; r > s; s++) + n.push(e ? o.GeoJSON.latLngsToCoords(t[s], e - 1, i) : o.GeoJSON.latLngToCoords(t[s])); + return !e && i && n.push(n[0]), n; + }, + getFeature: function (t, e) { + return t.feature ? o.extend({}, t.feature, { geometry: e }) : o.GeoJSON.asFeature(e); + }, + asFeature: function (t) { + return "Feature" === t.type ? t : { type: "Feature", properties: {}, geometry: t }; + }, + }); + var r = { + toGeoJSON: function () { + return o.GeoJSON.getFeature(this, { + type: "Point", + coordinates: o.GeoJSON.latLngToCoords(this.getLatLng()), + }); + }, + }; + o.Marker.include(r), + o.Circle.include(r), + o.CircleMarker.include(r), + (o.Polyline.prototype.toGeoJSON = function () { + var t = !o.Polyline._flat(this._latlngs), + e = o.GeoJSON.latLngsToCoords(this._latlngs, t ? 1 : 0); + return o.GeoJSON.getFeature(this, { type: (t ? "Multi" : "") + "LineString", coordinates: e }); + }), + (o.Polygon.prototype.toGeoJSON = function () { + var t = !o.Polyline._flat(this._latlngs), + e = t && !o.Polyline._flat(this._latlngs[0]), + i = o.GeoJSON.latLngsToCoords(this._latlngs, e ? 2 : t ? 1 : 0, !0); + return ( + t || (i = [i]), + o.GeoJSON.getFeature(this, { type: (e ? "Multi" : "") + "Polygon", coordinates: i }) + ); + }), + o.LayerGroup.include({ + toMultiPoint: function () { + var t = []; + return ( + this.eachLayer(function (e) { + t.push(e.toGeoJSON().geometry.coordinates); + }), + o.GeoJSON.getFeature(this, { type: "MultiPoint", coordinates: t }) + ); + }, + toGeoJSON: function () { + var t = this.feature && this.feature.geometry && this.feature.geometry.type; + if ("MultiPoint" === t) return this.toMultiPoint(); + var e = "GeometryCollection" === t, + i = []; + return ( + this.eachLayer(function (t) { + if (t.toGeoJSON) { + var n = t.toGeoJSON(); + i.push(e ? n.geometry : o.GeoJSON.asFeature(n)); + } + }), + e + ? o.GeoJSON.getFeature(this, { geometries: i, type: "GeometryCollection" }) + : { type: "FeatureCollection", features: i } + ); + }, + }), + (o.geoJson = function (t, e) { + return new o.GeoJSON(t, e); + }); + var a = "_leaflet_events"; + (o.DomEvent = { + on: function (t, e, i, n) { + if ("object" == typeof e) for (var s in e) this._on(t, s, e[s], i); + else { + e = o.Util.splitWords(e); + for (var r = 0, a = e.length; a > r; r++) this._on(t, e[r], i, n); + } + return this; + }, + off: function (t, e, i, n) { + if ("object" == typeof e) for (var s in e) this._off(t, s, e[s], i); + else { + e = o.Util.splitWords(e); + for (var r = 0, a = e.length; a > r; r++) this._off(t, e[r], i, n); + } + return this; + }, + _on: function (e, i, n, s) { + var r = i + o.stamp(n) + (s ? "_" + o.stamp(s) : ""); + if (e[a] && e[a][r]) return this; + var h = function (i) { + return n.call(s || e, i || t.event); + }, + l = h; + return ( + o.Browser.pointer && 0 === i.indexOf("touch") + ? this.addPointerListener(e, i, h, r) + : o.Browser.touch && "dblclick" === i && this.addDoubleTapListener + ? this.addDoubleTapListener(e, h, r) + : "addEventListener" in e + ? "mousewheel" === i + ? (e.addEventListener("DOMMouseScroll", h, !1), e.addEventListener(i, h, !1)) + : "mouseenter" === i || "mouseleave" === i + ? ((h = function (i) { + (i = i || t.event), o.DomEvent._isExternalTarget(e, i) && l(i); + }), + e.addEventListener("mouseenter" === i ? "mouseover" : "mouseout", h, !1)) + : ("click" === i && + o.Browser.android && + (h = function (t) { + return o.DomEvent._filterClick(t, l); + }), + e.addEventListener(i, h, !1)) + : "attachEvent" in e && e.attachEvent("on" + i, h), + (e[a] = e[a] || {}), + (e[a][r] = h), + this + ); + }, + _off: function (t, e, i, n) { + var s = e + o.stamp(i) + (n ? "_" + o.stamp(n) : ""), + r = t[a] && t[a][s]; + return r + ? (o.Browser.pointer && 0 === e.indexOf("touch") + ? this.removePointerListener(t, e, s) + : o.Browser.touch && "dblclick" === e && this.removeDoubleTapListener + ? this.removeDoubleTapListener(t, s) + : "removeEventListener" in t + ? "mousewheel" === e + ? (t.removeEventListener("DOMMouseScroll", r, !1), + t.removeEventListener(e, r, !1)) + : t.removeEventListener( + "mouseenter" === e ? "mouseover" : "mouseleave" === e ? "mouseout" : e, + r, + !1 + ) + : "detachEvent" in t && t.detachEvent("on" + e, r), + (t[a][s] = null), + this) + : this; + }, + stopPropagation: function (t) { + return ( + t.stopPropagation + ? t.stopPropagation() + : t.originalEvent + ? (t.originalEvent._stopped = !0) + : (t.cancelBubble = !0), + o.DomEvent._skipped(t), + this + ); + }, + disableScrollPropagation: function (t) { + return o.DomEvent.on(t, "mousewheel MozMousePixelScroll", o.DomEvent.stopPropagation); + }, + disableClickPropagation: function (t) { + var e = o.DomEvent.stopPropagation; + return ( + o.DomEvent.on(t, o.Draggable.START.join(" "), e), + o.DomEvent.on(t, { click: o.DomEvent._fakeStop, dblclick: e }) + ); + }, + preventDefault: function (t) { + return t.preventDefault ? t.preventDefault() : (t.returnValue = !1), this; + }, + stop: function (t) { + return o.DomEvent.preventDefault(t).stopPropagation(t); + }, + getMousePosition: function (t, e) { + if (!e) return new o.Point(t.clientX, t.clientY); + var i = e.getBoundingClientRect(); + return new o.Point(t.clientX - i.left - e.clientLeft, t.clientY - i.top - e.clientTop); + }, + getWheelDelta: function (t) { + var e = 0; + return t.wheelDelta && (e = t.wheelDelta / 120), t.detail && (e = -t.detail / 3), e; + }, + _skipEvents: {}, + _fakeStop: function (t) { + o.DomEvent._skipEvents[t.type] = !0; + }, + _skipped: function (t) { + var e = this._skipEvents[t.type]; + return (this._skipEvents[t.type] = !1), e; + }, + _isExternalTarget: function (t, e) { + var i = e.relatedTarget; + if (!i) return !0; + try { + for (; i && i !== t; ) i = i.parentNode; + } catch (n) { + return !1; + } + return i !== t; + }, + _filterClick: function (t, e) { + var i = t.timeStamp || t.originalEvent.timeStamp, + n = o.DomEvent._lastClick && i - o.DomEvent._lastClick; + return (n && n > 100 && 500 > n) || (t.target._simulatedClick && !t._simulated) + ? void o.DomEvent.stop(t) + : ((o.DomEvent._lastClick = i), void e(t)); + }, + }), + (o.DomEvent.addListener = o.DomEvent.on), + (o.DomEvent.removeListener = o.DomEvent.off), + (o.Draggable = o.Evented.extend({ + statics: { + START: o.Browser.touch ? ["touchstart", "mousedown"] : ["mousedown"], + END: { + mousedown: "mouseup", + touchstart: "touchend", + pointerdown: "touchend", + MSPointerDown: "touchend", + }, + MOVE: { + mousedown: "mousemove", + touchstart: "touchmove", + pointerdown: "touchmove", + MSPointerDown: "touchmove", + }, + }, + initialize: function (t, e, i) { + (this._element = t), (this._dragStartTarget = e || t), (this._preventOutline = i); + }, + enable: function () { + this._enabled || + (o.DomEvent.on(this._dragStartTarget, o.Draggable.START.join(" "), this._onDown, this), + (this._enabled = !0)); + }, + disable: function () { + this._enabled && + (o.DomEvent.off(this._dragStartTarget, o.Draggable.START.join(" "), this._onDown, this), + (this._enabled = !1), + (this._moved = !1)); + }, + _onDown: function (t) { + if ( + ((this._moved = !1), + !o.DomUtil.hasClass(this._element, "leaflet-zoom-anim") && + !( + o.Draggable._dragging || + t.shiftKey || + (1 !== t.which && 1 !== t.button && !t.touches) + ) && + this._enabled && + ((o.Draggable._dragging = !0), + this._preventOutline && o.DomUtil.preventOutline(this._element), + o.DomUtil.disableImageDrag(), + o.DomUtil.disableTextSelection(), + !this._moving)) + ) { + this.fire("down"); + var i = t.touches ? t.touches[0] : t; + (this._startPoint = new o.Point(i.clientX, i.clientY)), + (this._startPos = this._newPos = o.DomUtil.getPosition(this._element)), + o.DomEvent.on(e, o.Draggable.MOVE[t.type], this._onMove, this).on( + e, + o.Draggable.END[t.type], + this._onUp, + this + ); + } + }, + _onMove: function (t) { + if (t.touches && t.touches.length > 1) return void (this._moved = !0); + var i = t.touches && 1 === t.touches.length ? t.touches[0] : t, + n = new o.Point(i.clientX, i.clientY), + s = n.subtract(this._startPoint); + (s.x || s.y) && + ((o.Browser.touch && Math.abs(s.x) + Math.abs(s.y) < 3) || + (o.DomEvent.preventDefault(t), + this._moved || + (this.fire("dragstart"), + (this._moved = !0), + (this._startPos = o.DomUtil.getPosition(this._element).subtract(s)), + o.DomUtil.addClass(e.body, "leaflet-dragging"), + (this._lastTarget = t.target || t.srcElement), + o.DomUtil.addClass(this._lastTarget, "leaflet-drag-target")), + (this._newPos = this._startPos.add(s)), + (this._moving = !0), + o.Util.cancelAnimFrame(this._animRequest), + (this._lastEvent = t), + (this._animRequest = o.Util.requestAnimFrame(this._updatePosition, this, !0)))); + }, + _updatePosition: function () { + var t = { originalEvent: this._lastEvent }; + this.fire("predrag", t), + o.DomUtil.setPosition(this._element, this._newPos), + this.fire("drag", t); + }, + _onUp: function () { + o.DomUtil.removeClass(e.body, "leaflet-dragging"), + this._lastTarget && + (o.DomUtil.removeClass(this._lastTarget, "leaflet-drag-target"), + (this._lastTarget = null)); + for (var t in o.Draggable.MOVE) + o.DomEvent.off(e, o.Draggable.MOVE[t], this._onMove, this).off( + e, + o.Draggable.END[t], + this._onUp, + this + ); + o.DomUtil.enableImageDrag(), + o.DomUtil.enableTextSelection(), + this._moved && + this._moving && + (o.Util.cancelAnimFrame(this._animRequest), + this.fire("dragend", { distance: this._newPos.distanceTo(this._startPos) })), + (this._moving = !1), + (o.Draggable._dragging = !1); + }, + })), + (o.Handler = o.Class.extend({ + initialize: function (t) { + this._map = t; + }, + enable: function () { + this._enabled || ((this._enabled = !0), this.addHooks()); + }, + disable: function () { + this._enabled && ((this._enabled = !1), this.removeHooks()); + }, + enabled: function () { + return !!this._enabled; + }, + })), + o.Map.mergeOptions({ + dragging: !0, + inertia: !o.Browser.android23, + inertiaDeceleration: 3400, + inertiaMaxSpeed: 1 / 0, + easeLinearity: 0.2, + worldCopyJump: !1, + }), + (o.Map.Drag = o.Handler.extend({ + addHooks: function () { + if (!this._draggable) { + var t = this._map; + (this._draggable = new o.Draggable(t._mapPane, t._container)), + this._draggable.on( + { + down: this._onDown, + dragstart: this._onDragStart, + drag: this._onDrag, + dragend: this._onDragEnd, + }, + this + ), + this._draggable.on("predrag", this._onPreDragLimit, this), + t.options.worldCopyJump && + (this._draggable.on("predrag", this._onPreDragWrap, this), + t.on("zoomend", this._onZoomEnd, this), + t.whenReady(this._onZoomEnd, this)); + } + o.DomUtil.addClass(this._map._container, "leaflet-grab"), this._draggable.enable(); + }, + removeHooks: function () { + o.DomUtil.removeClass(this._map._container, "leaflet-grab"), this._draggable.disable(); + }, + moved: function () { + return this._draggable && this._draggable._moved; + }, + _onDown: function () { + this._map.stop(); + }, + _onDragStart: function () { + var t = this._map; + if (this._map.options.maxBounds && this._map.options.maxBoundsViscosity) { + var e = o.latLngBounds(this._map.options.maxBounds); + (this._offsetLimit = o.bounds( + this._map.latLngToContainerPoint(e.getNorthWest()).multiplyBy(-1), + this._map + .latLngToContainerPoint(e.getSouthEast()) + .multiplyBy(-1) + .add(this._map.getSize()) + )), + (this._viscosity = Math.min(1, Math.max(0, this._map.options.maxBoundsViscosity))); + } else this._offsetLimit = null; + t.fire("movestart").fire("dragstart"), + t.options.inertia && ((this._positions = []), (this._times = [])); + }, + _onDrag: function (t) { + if (this._map.options.inertia) { + var e = (this._lastTime = +new Date()), + i = (this._lastPos = this._draggable._absPos || this._draggable._newPos); + this._positions.push(i), + this._times.push(e), + e - this._times[0] > 50 && (this._positions.shift(), this._times.shift()); + } + this._map.fire("move", t).fire("drag", t); + }, + _onZoomEnd: function () { + var t = this._map.getSize().divideBy(2), + e = this._map.latLngToLayerPoint([0, 0]); + (this._initialWorldOffset = e.subtract(t).x), + (this._worldWidth = this._map.getPixelWorldBounds().getSize().x); + }, + _viscousLimit: function (t, e) { + return t - (t - e) * this._viscosity; + }, + _onPreDragLimit: function () { + if (this._viscosity && this._offsetLimit) { + var t = this._draggable._newPos.subtract(this._draggable._startPos), + e = this._offsetLimit; + t.x < e.min.x && (t.x = this._viscousLimit(t.x, e.min.x)), + t.y < e.min.y && (t.y = this._viscousLimit(t.y, e.min.y)), + t.x > e.max.x && (t.x = this._viscousLimit(t.x, e.max.x)), + t.y > e.max.y && (t.y = this._viscousLimit(t.y, e.max.y)), + (this._draggable._newPos = this._draggable._startPos.add(t)); + } + }, + _onPreDragWrap: function () { + var t = this._worldWidth, + e = Math.round(t / 2), + i = this._initialWorldOffset, + n = this._draggable._newPos.x, + o = ((n - e + i) % t) + e - i, + s = ((n + e + i) % t) - e - i, + r = Math.abs(o + i) < Math.abs(s + i) ? o : s; + (this._draggable._absPos = this._draggable._newPos.clone()), (this._draggable._newPos.x = r); + }, + _onDragEnd: function (t) { + var e = this._map, + i = e.options, + n = !i.inertia || this._times.length < 2; + if ((e.fire("dragend", t), n)) e.fire("moveend"); + else { + var s = this._lastPos.subtract(this._positions[0]), + r = (this._lastTime - this._times[0]) / 1e3, + a = i.easeLinearity, + h = s.multiplyBy(a / r), + l = h.distanceTo([0, 0]), + u = Math.min(i.inertiaMaxSpeed, l), + c = h.multiplyBy(u / l), + d = u / (i.inertiaDeceleration * a), + _ = c.multiplyBy(-d / 2).round(); + _.x || _.y + ? ((_ = e._limitOffset(_, e.options.maxBounds)), + o.Util.requestAnimFrame(function () { + e.panBy(_, { duration: d, easeLinearity: a, noMoveStart: !0, animate: !0 }); + })) + : e.fire("moveend"); + } + }, + })), + o.Map.addInitHook("addHandler", "dragging", o.Map.Drag), + o.Map.mergeOptions({ doubleClickZoom: !0 }), + (o.Map.DoubleClickZoom = o.Handler.extend({ + addHooks: function () { + this._map.on("dblclick", this._onDoubleClick, this); + }, + removeHooks: function () { + this._map.off("dblclick", this._onDoubleClick, this); + }, + _onDoubleClick: function (t) { + var e = this._map, + i = e.getZoom(), + n = t.originalEvent.shiftKey ? Math.ceil(i) - 1 : Math.floor(i) + 1; + "center" === e.options.doubleClickZoom ? e.setZoom(n) : e.setZoomAround(t.containerPoint, n); + }, + })), + o.Map.addInitHook("addHandler", "doubleClickZoom", o.Map.DoubleClickZoom), + o.Map.mergeOptions({ scrollWheelZoom: !0, wheelDebounceTime: 40 }), + (o.Map.ScrollWheelZoom = o.Handler.extend({ + addHooks: function () { + o.DomEvent.on( + this._map._container, + { mousewheel: this._onWheelScroll, MozMousePixelScroll: o.DomEvent.preventDefault }, + this + ), + (this._delta = 0); + }, + removeHooks: function () { + o.DomEvent.off( + this._map._container, + { mousewheel: this._onWheelScroll, MozMousePixelScroll: o.DomEvent.preventDefault }, + this + ); + }, + _onWheelScroll: function (t) { + var e = o.DomEvent.getWheelDelta(t), + i = this._map.options.wheelDebounceTime; + (this._delta += e), + (this._lastMousePos = this._map.mouseEventToContainerPoint(t)), + this._startTime || (this._startTime = +new Date()); + var n = Math.max(i - (+new Date() - this._startTime), 0); + clearTimeout(this._timer), + (this._timer = setTimeout(o.bind(this._performZoom, this), n)), + o.DomEvent.stop(t); + }, + _performZoom: function () { + var t = this._map, + e = this._delta, + i = t.getZoom(); + t.stop(), + (e = e > 0 ? Math.ceil(e) : Math.floor(e)), + (e = Math.max(Math.min(e, 4), -4)), + (e = t._limitZoom(i + e) - i), + (this._delta = 0), + (this._startTime = null), + e && + ("center" === t.options.scrollWheelZoom + ? t.setZoom(i + e) + : t.setZoomAround(this._lastMousePos, i + e)); + }, + })), + o.Map.addInitHook("addHandler", "scrollWheelZoom", o.Map.ScrollWheelZoom), + o.extend(o.DomEvent, { + _touchstart: o.Browser.msPointer + ? "MSPointerDown" + : o.Browser.pointer + ? "pointerdown" + : "touchstart", + _touchend: o.Browser.msPointer ? "MSPointerUp" : o.Browser.pointer ? "pointerup" : "touchend", + addDoubleTapListener: function (t, e, i) { + function n(t) { + var e; + if (((e = o.Browser.pointer ? o.DomEvent._pointersCount : t.touches.length), !(e > 1))) { + var i = Date.now(), + n = i - (r || i); + (a = t.touches ? t.touches[0] : t), (h = n > 0 && l >= n), (r = i); + } + } - function r(t) { return (Math.exp(t) - Math.exp(-t)) / 2 } + function s() { + if (h && !a.cancelBubble) { + if (o.Browser.pointer) { + var t, + i, + n = {}; + for (i in a) (t = a[i]), (n[i] = t && t.bind ? t.bind(a) : t); + a = n; + } + (a.type = "dblclick"), e(a), (r = null); + } + } + var r, + a, + h = !1, + l = 250, + u = "_leaflet_", + c = this._touchstart, + d = this._touchend; + return ( + (t[u + c + i] = n), + (t[u + d + i] = s), + t.addEventListener(c, n, !1), + t.addEventListener(d, s, !1), + this + ); + }, + removeDoubleTapListener: function (t, e) { + var i = "_leaflet_", + n = t[i + this._touchend + e]; + return ( + t.removeEventListener(this._touchstart, t[i + this._touchstart + e], !1), + t.removeEventListener(this._touchend, n, !1), + this + ); + }, + }), + o.extend(o.DomEvent, { + POINTER_DOWN: o.Browser.msPointer ? "MSPointerDown" : "pointerdown", + POINTER_MOVE: o.Browser.msPointer ? "MSPointerMove" : "pointermove", + POINTER_UP: o.Browser.msPointer ? "MSPointerUp" : "pointerup", + POINTER_CANCEL: o.Browser.msPointer ? "MSPointerCancel" : "pointercancel", + _pointers: {}, + _pointersCount: 0, + addPointerListener: function (t, e, i, n) { + return ( + "touchstart" === e + ? this._addPointerStart(t, i, n) + : "touchmove" === e + ? this._addPointerMove(t, i, n) + : "touchend" === e && this._addPointerEnd(t, i, n), + this + ); + }, + removePointerListener: function (t, e, i) { + var n = t["_leaflet_" + e + i]; + return ( + "touchstart" === e + ? t.removeEventListener(this.POINTER_DOWN, n, !1) + : "touchmove" === e + ? t.removeEventListener(this.POINTER_MOVE, n, !1) + : "touchend" === e && + (t.removeEventListener(this.POINTER_UP, n, !1), + t.removeEventListener(this.POINTER_CANCEL, n, !1)), + this + ); + }, + _addPointerStart: function (t, i, n) { + var s = o.bind(function (t) { + "mouse" !== t.pointerType && + t.pointerType !== t.MSPOINTER_TYPE_MOUSE && + o.DomEvent.preventDefault(t), + this._handlePointer(t, i); + }, this); + if ( + ((t["_leaflet_touchstart" + n] = s), + t.addEventListener(this.POINTER_DOWN, s, !1), + !this._pointerDocListener) + ) { + var r = o.bind(this._globalPointerUp, this); + e.documentElement.addEventListener( + this.POINTER_DOWN, + o.bind(this._globalPointerDown, this), + !0 + ), + e.documentElement.addEventListener( + this.POINTER_MOVE, + o.bind(this._globalPointerMove, this), + !0 + ), + e.documentElement.addEventListener(this.POINTER_UP, r, !0), + e.documentElement.addEventListener(this.POINTER_CANCEL, r, !0), + (this._pointerDocListener = !0); + } + }, + _globalPointerDown: function (t) { + (this._pointers[t.pointerId] = t), this._pointersCount++; + }, + _globalPointerMove: function (t) { + this._pointers[t.pointerId] && (this._pointers[t.pointerId] = t); + }, + _globalPointerUp: function (t) { + delete this._pointers[t.pointerId], this._pointersCount--; + }, + _handlePointer: function (t, e) { + t.touches = []; + for (var i in this._pointers) t.touches.push(this._pointers[i]); + (t.changedTouches = [t]), e(t); + }, + _addPointerMove: function (t, e, i) { + var n = o.bind(function (t) { + ((t.pointerType !== t.MSPOINTER_TYPE_MOUSE && "mouse" !== t.pointerType) || + 0 !== t.buttons) && + this._handlePointer(t, e); + }, this); + (t["_leaflet_touchmove" + i] = n), t.addEventListener(this.POINTER_MOVE, n, !1); + }, + _addPointerEnd: function (t, e, i) { + var n = o.bind(function (t) { + this._handlePointer(t, e); + }, this); + (t["_leaflet_touchend" + i] = n), + t.addEventListener(this.POINTER_UP, n, !1), + t.addEventListener(this.POINTER_CANCEL, n, !1); + }, + }), + o.Map.mergeOptions({ touchZoom: o.Browser.touch && !o.Browser.android23, bounceAtZoomLimits: !0 }), + (o.Map.TouchZoom = o.Handler.extend({ + addHooks: function () { + o.DomEvent.on(this._map._container, "touchstart", this._onTouchStart, this); + }, + removeHooks: function () { + o.DomEvent.off(this._map._container, "touchstart", this._onTouchStart, this); + }, + _onTouchStart: function (t) { + var i = this._map; + if (t.touches && 2 === t.touches.length && !i._animatingZoom && !this._zooming) { + var n = i.mouseEventToContainerPoint(t.touches[0]), + s = i.mouseEventToContainerPoint(t.touches[1]); + (this._centerPoint = i.getSize()._divideBy(2)), + (this._startLatLng = i.containerPointToLatLng(this._centerPoint)), + "center" !== i.options.touchZoom && + (this._pinchStartLatLng = i.containerPointToLatLng(n.add(s)._divideBy(2))), + (this._startDist = n.distanceTo(s)), + (this._startZoom = i.getZoom()), + (this._moved = !1), + (this._zooming = !0), + i.stop(), + o.DomEvent.on(e, "touchmove", this._onTouchMove, this).on( + e, + "touchend", + this._onTouchEnd, + this + ), + o.DomEvent.preventDefault(t); + } + }, + _onTouchMove: function (t) { + if (t.touches && 2 === t.touches.length && this._zooming) { + var e = this._map, + i = e.mouseEventToContainerPoint(t.touches[0]), + n = e.mouseEventToContainerPoint(t.touches[1]), + s = i.distanceTo(n) / this._startDist; + if ( + ((this._zoom = e.getScaleZoom(s, this._startZoom)), "center" === e.options.touchZoom) + ) { + if (((this._center = this._startLatLng), 1 === s)) return; + } else { + var r = i._add(n)._divideBy(2)._subtract(this._centerPoint); + if (1 === s && 0 === r.x && 0 === r.y) return; + this._center = e.unproject(e.project(this._pinchStartLatLng).subtract(r)); + } + if ( + e.options.bounceAtZoomLimits || + !((this._zoom <= e.getMinZoom() && 1 > s) || (this._zoom >= e.getMaxZoom() && s > 1)) + ) { + this._moved || (e._moveStart(!0), (this._moved = !0)), + o.Util.cancelAnimFrame(this._animRequest); + var a = o.bind(e._move, e, this._center, this._zoom, { pinch: !0, round: !1 }); + (this._animRequest = o.Util.requestAnimFrame(a, this, !0)), + o.DomEvent.preventDefault(t); + } + } + }, + _onTouchEnd: function () { + if (!this._moved || !this._zooming) return void (this._zooming = !1); + (this._zooming = !1), + o.Util.cancelAnimFrame(this._animRequest), + o.DomEvent.off(e, "touchmove", this._onTouchMove).off(e, "touchend", this._onTouchEnd); + var t = this._zoom; + (t = this._map._limitZoom(t - this._startZoom > 0 ? Math.ceil(t) : Math.floor(t))), + this._map._animateZoom(this._center, t, !0, !0); + }, + })), + o.Map.addInitHook("addHandler", "touchZoom", o.Map.TouchZoom), + o.Map.mergeOptions({ tap: !0, tapTolerance: 15 }), + (o.Map.Tap = o.Handler.extend({ + addHooks: function () { + o.DomEvent.on(this._map._container, "touchstart", this._onDown, this); + }, + removeHooks: function () { + o.DomEvent.off(this._map._container, "touchstart", this._onDown, this); + }, + _onDown: function (t) { + if (t.touches) { + if ((o.DomEvent.preventDefault(t), (this._fireClick = !0), t.touches.length > 1)) + return (this._fireClick = !1), void clearTimeout(this._holdTimeout); + var i = t.touches[0], + n = i.target; + (this._startPos = this._newPos = new o.Point(i.clientX, i.clientY)), + n.tagName && + "a" === n.tagName.toLowerCase() && + o.DomUtil.addClass(n, "leaflet-active"), + (this._holdTimeout = setTimeout( + o.bind(function () { + this._isTapValid() && + ((this._fireClick = !1), + this._onUp(), + this._simulateEvent("contextmenu", i)); + }, this), + 1e3 + )), + this._simulateEvent("mousedown", i), + o.DomEvent.on(e, { touchmove: this._onMove, touchend: this._onUp }, this); + } + }, + _onUp: function (t) { + if ( + (clearTimeout(this._holdTimeout), + o.DomEvent.off(e, { touchmove: this._onMove, touchend: this._onUp }, this), + this._fireClick && t && t.changedTouches) + ) { + var i = t.changedTouches[0], + n = i.target; + n && + n.tagName && + "a" === n.tagName.toLowerCase() && + o.DomUtil.removeClass(n, "leaflet-active"), + this._simulateEvent("mouseup", i), + this._isTapValid() && this._simulateEvent("click", i); + } + }, + _isTapValid: function () { + return this._newPos.distanceTo(this._startPos) <= this._map.options.tapTolerance; + }, + _onMove: function (t) { + var e = t.touches[0]; + (this._newPos = new o.Point(e.clientX, e.clientY)), this._simulateEvent("mousemove", e); + }, + _simulateEvent: function (i, n) { + var o = e.createEvent("MouseEvents"); + (o._simulated = !0), + (n.target._simulatedClick = !0), + o.initMouseEvent( + i, + !0, + !0, + t, + 1, + n.screenX, + n.screenY, + n.clientX, + n.clientY, + !1, + !1, + !1, + !1, + 0, + null + ), + n.target.dispatchEvent(o); + }, + })), + o.Browser.touch && !o.Browser.pointer && o.Map.addInitHook("addHandler", "tap", o.Map.Tap), + o.Map.mergeOptions({ boxZoom: !0 }), + (o.Map.BoxZoom = o.Handler.extend({ + initialize: function (t) { + (this._map = t), (this._container = t._container), (this._pane = t._panes.overlayPane); + }, + addHooks: function () { + o.DomEvent.on(this._container, "mousedown", this._onMouseDown, this); + }, + removeHooks: function () { + o.DomEvent.off(this._container, "mousedown", this._onMouseDown, this); + }, + moved: function () { + return this._moved; + }, + _resetState: function () { + this._moved = !1; + }, + _onMouseDown: function (t) { + return !t.shiftKey || (1 !== t.which && 1 !== t.button) + ? !1 + : (this._resetState(), + o.DomUtil.disableTextSelection(), + o.DomUtil.disableImageDrag(), + (this._startPoint = this._map.mouseEventToContainerPoint(t)), + void o.DomEvent.on( + e, + { + contextmenu: o.DomEvent.stop, + mousemove: this._onMouseMove, + mouseup: this._onMouseUp, + keydown: this._onKeyDown, + }, + this + )); + }, + _onMouseMove: function (t) { + this._moved || + ((this._moved = !0), + (this._box = o.DomUtil.create("div", "leaflet-zoom-box", this._container)), + o.DomUtil.addClass(this._container, "leaflet-crosshair"), + this._map.fire("boxzoomstart")), + (this._point = this._map.mouseEventToContainerPoint(t)); + var e = new o.Bounds(this._point, this._startPoint), + i = e.getSize(); + o.DomUtil.setPosition(this._box, e.min), + (this._box.style.width = i.x + "px"), + (this._box.style.height = i.y + "px"); + }, + _finish: function () { + this._moved && + (o.DomUtil.remove(this._box), + o.DomUtil.removeClass(this._container, "leaflet-crosshair")), + o.DomUtil.enableTextSelection(), + o.DomUtil.enableImageDrag(), + o.DomEvent.off( + e, + { + contextmenu: o.DomEvent.stop, + mousemove: this._onMouseMove, + mouseup: this._onMouseUp, + keydown: this._onKeyDown, + }, + this + ); + }, + _onMouseUp: function (t) { + if ((1 === t.which || 1 === t.button) && (this._finish(), this._moved)) { + setTimeout(o.bind(this._resetState, this), 0); + var e = new o.LatLngBounds( + this._map.containerPointToLatLng(this._startPoint), + this._map.containerPointToLatLng(this._point) + ); + this._map.fitBounds(e).fire("boxzoomend", { boxZoomBounds: e }); + } + }, + _onKeyDown: function (t) { + 27 === t.keyCode && this._finish(); + }, + })), + o.Map.addInitHook("addHandler", "boxZoom", o.Map.BoxZoom), + o.Map.mergeOptions({ keyboard: !0, keyboardPanOffset: 80, keyboardZoomOffset: 1 }), + (o.Map.Keyboard = o.Handler.extend({ + keyCodes: { + left: [37], + right: [39], + down: [40], + up: [38], + zoomIn: [187, 107, 61, 171], + zoomOut: [189, 109, 54, 173], + }, + initialize: function (t) { + (this._map = t), + this._setPanOffset(t.options.keyboardPanOffset), + this._setZoomOffset(t.options.keyboardZoomOffset); + }, + addHooks: function () { + var t = this._map._container; + t.tabIndex <= 0 && (t.tabIndex = "0"), + o.DomEvent.on( + t, + { focus: this._onFocus, blur: this._onBlur, mousedown: this._onMouseDown }, + this + ), + this._map.on({ focus: this._addHooks, blur: this._removeHooks }, this); + }, + removeHooks: function () { + this._removeHooks(), + o.DomEvent.off( + this._map._container, + { focus: this._onFocus, blur: this._onBlur, mousedown: this._onMouseDown }, + this + ), + this._map.off({ focus: this._addHooks, blur: this._removeHooks }, this); + }, + _onMouseDown: function () { + if (!this._focused) { + var i = e.body, + n = e.documentElement, + o = i.scrollTop || n.scrollTop, + s = i.scrollLeft || n.scrollLeft; + this._map._container.focus(), t.scrollTo(s, o); + } + }, + _onFocus: function () { + (this._focused = !0), this._map.fire("focus"); + }, + _onBlur: function () { + (this._focused = !1), this._map.fire("blur"); + }, + _setPanOffset: function (t) { + var e, + i, + n = (this._panKeys = {}), + o = this.keyCodes; + for (e = 0, i = o.left.length; i > e; e++) n[o.left[e]] = [-1 * t, 0]; + for (e = 0, i = o.right.length; i > e; e++) n[o.right[e]] = [t, 0]; + for (e = 0, i = o.down.length; i > e; e++) n[o.down[e]] = [0, t]; + for (e = 0, i = o.up.length; i > e; e++) n[o.up[e]] = [0, -1 * t]; + }, + _setZoomOffset: function (t) { + var e, + i, + n = (this._zoomKeys = {}), + o = this.keyCodes; + for (e = 0, i = o.zoomIn.length; i > e; e++) n[o.zoomIn[e]] = t; + for (e = 0, i = o.zoomOut.length; i > e; e++) n[o.zoomOut[e]] = -t; + }, + _addHooks: function () { + o.DomEvent.on(e, "keydown", this._onKeyDown, this); + }, + _removeHooks: function () { + o.DomEvent.off(e, "keydown", this._onKeyDown, this); + }, + _onKeyDown: function (t) { + if (!(t.altKey || t.ctrlKey || t.metaKey)) { + var e, + i = t.keyCode, + n = this._map; + if (i in this._panKeys) { + if (n._panAnim && n._panAnim._inProgress) return; + (e = this._panKeys[i]), + t.shiftKey && (e = o.point(e).multiplyBy(3)), + n.panBy(e), + n.options.maxBounds && n.panInsideBounds(n.options.maxBounds); + } else if (i in this._zoomKeys) + n.setZoom(n.getZoom() + (t.shiftKey ? 3 : 1) * this._zoomKeys[i]); + else { + if (27 !== i) return; + n.closePopup(); + } + o.DomEvent.stop(t); + } + }, + })), + o.Map.addInitHook("addHandler", "keyboard", o.Map.Keyboard), + (o.Handler.MarkerDrag = o.Handler.extend({ + initialize: function (t) { + this._marker = t; + }, + addHooks: function () { + var t = this._marker._icon; + this._draggable || (this._draggable = new o.Draggable(t, t, !0)), + this._draggable + .on( + { dragstart: this._onDragStart, drag: this._onDrag, dragend: this._onDragEnd }, + this + ) + .enable(), + o.DomUtil.addClass(t, "leaflet-marker-draggable"); + }, + removeHooks: function () { + this._draggable + .off({ dragstart: this._onDragStart, drag: this._onDrag, dragend: this._onDragEnd }, this) + .disable(), + this._marker._icon && + o.DomUtil.removeClass(this._marker._icon, "leaflet-marker-draggable"); + }, + moved: function () { + return this._draggable && this._draggable._moved; + }, + _onDragStart: function () { + this._marker.closePopup().fire("movestart").fire("dragstart"); + }, + _onDrag: function (t) { + var e = this._marker, + i = e._shadow, + n = o.DomUtil.getPosition(e._icon), + s = e._map.layerPointToLatLng(n); + i && o.DomUtil.setPosition(i, n), + (e._latlng = s), + (t.latlng = s), + e.fire("move", t).fire("drag", t); + }, + _onDragEnd: function (t) { + this._marker.fire("moveend").fire("dragend", t); + }, + })), + (o.Control = o.Class.extend({ + options: { position: "topright" }, + initialize: function (t) { + o.setOptions(this, t); + }, + getPosition: function () { + return this.options.position; + }, + setPosition: function (t) { + var e = this._map; + return e && e.removeControl(this), (this.options.position = t), e && e.addControl(this), this; + }, + getContainer: function () { + return this._container; + }, + addTo: function (t) { + this.remove(), (this._map = t); + var e = (this._container = this.onAdd(t)), + i = this.getPosition(), + n = t._controlCorners[i]; + return ( + o.DomUtil.addClass(e, "leaflet-control"), + -1 !== i.indexOf("bottom") ? n.insertBefore(e, n.firstChild) : n.appendChild(e), + this + ); + }, + remove: function () { + return this._map + ? (o.DomUtil.remove(this._container), + this.onRemove && this.onRemove(this._map), + (this._map = null), + this) + : this; + }, + _refocusOnMap: function (t) { + this._map && t && t.screenX > 0 && t.screenY > 0 && this._map.getContainer().focus(); + }, + })), + (o.control = function (t) { + return new o.Control(t); + }), + o.Map.include({ + addControl: function (t) { + return t.addTo(this), this; + }, + removeControl: function (t) { + return t.remove(), this; + }, + _initControlPos: function () { + function t(t, s) { + var r = i + t + " " + i + s; + e[t + s] = o.DomUtil.create("div", r, n); + } + var e = (this._controlCorners = {}), + i = "leaflet-", + n = (this._controlContainer = o.DomUtil.create( + "div", + i + "control-container", + this._container + )); + t("top", "left"), t("top", "right"), t("bottom", "left"), t("bottom", "right"); + }, + _clearControlPos: function () { + o.DomUtil.remove(this._controlContainer); + }, + }), + (o.Control.Zoom = o.Control.extend({ + options: { + position: "topleft", + zoomInText: "+", + zoomInTitle: "Zoom in", + zoomOutText: "-", + zoomOutTitle: "Zoom out", + }, + onAdd: function (t) { + var e = "leaflet-control-zoom", + i = o.DomUtil.create("div", e + " leaflet-bar"), + n = this.options; + return ( + (this._zoomInButton = this._createButton( + n.zoomInText, + n.zoomInTitle, + e + "-in", + i, + this._zoomIn + )), + (this._zoomOutButton = this._createButton( + n.zoomOutText, + n.zoomOutTitle, + e + "-out", + i, + this._zoomOut + )), + this._updateDisabled(), + t.on("zoomend zoomlevelschange", this._updateDisabled, this), + i + ); + }, + onRemove: function (t) { + t.off("zoomend zoomlevelschange", this._updateDisabled, this); + }, + disable: function () { + return (this._disabled = !0), this._updateDisabled(), this; + }, + enable: function () { + return (this._disabled = !1), this._updateDisabled(), this; + }, + _zoomIn: function (t) { + this._disabled || this._map.zoomIn(t.shiftKey ? 3 : 1); + }, + _zoomOut: function (t) { + this._disabled || this._map.zoomOut(t.shiftKey ? 3 : 1); + }, + _createButton: function (t, e, i, n, s) { + var r = o.DomUtil.create("a", i, n); + return ( + (r.innerHTML = t), + (r.href = "#"), + (r.title = e), + o.DomEvent.on(r, "mousedown dblclick", o.DomEvent.stopPropagation) + .on(r, "click", o.DomEvent.stop) + .on(r, "click", s, this) + .on(r, "click", this._refocusOnMap, this), + r + ); + }, + _updateDisabled: function () { + var t = this._map, + e = "leaflet-disabled"; + o.DomUtil.removeClass(this._zoomInButton, e), + o.DomUtil.removeClass(this._zoomOutButton, e), + (this._disabled || t._zoom === t.getMinZoom()) && + o.DomUtil.addClass(this._zoomOutButton, e), + (this._disabled || t._zoom === t.getMaxZoom()) && + o.DomUtil.addClass(this._zoomInButton, e); + }, + })), + o.Map.mergeOptions({ zoomControl: !0 }), + o.Map.addInitHook(function () { + this.options.zoomControl && + ((this.zoomControl = new o.Control.Zoom()), this.addControl(this.zoomControl)); + }), + (o.control.zoom = function (t) { + return new o.Control.Zoom(t); + }), + (o.Control.Attribution = o.Control.extend({ + options: { + position: "bottomright", + prefix: 'Leaflet', + }, + initialize: function (t) { + o.setOptions(this, t), (this._attributions = {}); + }, + onAdd: function (t) { + (this._container = o.DomUtil.create("div", "leaflet-control-attribution")), + o.DomEvent && o.DomEvent.disableClickPropagation(this._container); + for (var e in t._layers) + t._layers[e].getAttribution && this.addAttribution(t._layers[e].getAttribution()); + return this._update(), this._container; + }, + setPrefix: function (t) { + return (this.options.prefix = t), this._update(), this; + }, + addAttribution: function (t) { + return t + ? (this._attributions[t] || (this._attributions[t] = 0), + this._attributions[t]++, + this._update(), + this) + : this; + }, + removeAttribution: function (t) { + return t ? (this._attributions[t] && (this._attributions[t]--, this._update()), this) : this; + }, + _update: function () { + if (this._map) { + var t = []; + for (var e in this._attributions) this._attributions[e] && t.push(e); + var i = []; + this.options.prefix && i.push(this.options.prefix), + t.length && i.push(t.join(", ")), + (this._container.innerHTML = i.join(" | ")); + } + }, + })), + o.Map.mergeOptions({ attributionControl: !0 }), + o.Map.addInitHook(function () { + this.options.attributionControl && + (this.attributionControl = new o.Control.Attribution().addTo(this)); + }), + (o.control.attribution = function (t) { + return new o.Control.Attribution(t); + }), + (o.Control.Scale = o.Control.extend({ + options: { position: "bottomleft", maxWidth: 100, metric: !0, imperial: !0 }, + onAdd: function (t) { + var e = "leaflet-control-scale", + i = o.DomUtil.create("div", e), + n = this.options; + return ( + this._addScales(n, e + "-line", i), + t.on(n.updateWhenIdle ? "moveend" : "move", this._update, this), + t.whenReady(this._update, this), + i + ); + }, + onRemove: function (t) { + t.off(this.options.updateWhenIdle ? "moveend" : "move", this._update, this); + }, + _addScales: function (t, e, i) { + t.metric && (this._mScale = o.DomUtil.create("div", e, i)), + t.imperial && (this._iScale = o.DomUtil.create("div", e, i)); + }, + _update: function () { + var t = this._map, + e = t.getSize().y / 2, + i = t.distance( + t.containerPointToLatLng([0, e]), + t.containerPointToLatLng([this.options.maxWidth, e]) + ); + this._updateScales(i); + }, + _updateScales: function (t) { + this.options.metric && t && this._updateMetric(t), + this.options.imperial && t && this._updateImperial(t); + }, + _updateMetric: function (t) { + var e = this._getRoundNum(t), + i = 1e3 > e ? e + " m" : e / 1e3 + " km"; + this._updateScale(this._mScale, i, e / t); + }, + _updateImperial: function (t) { + var e, + i, + n, + o = 3.2808399 * t; + o > 5280 + ? ((e = o / 5280), + (i = this._getRoundNum(e)), + this._updateScale(this._iScale, i + " mi", i / e)) + : ((n = this._getRoundNum(o)), this._updateScale(this._iScale, n + " ft", n / o)); + }, + _updateScale: function (t, e, i) { + (t.style.width = Math.round(this.options.maxWidth * i) + "px"), (t.innerHTML = e); + }, + _getRoundNum: function (t) { + var e = Math.pow(10, (Math.floor(t) + "").length - 1), + i = t / e; + return (i = i >= 10 ? 10 : i >= 5 ? 5 : i >= 3 ? 3 : i >= 2 ? 2 : 1), e * i; + }, + })), + (o.control.scale = function (t) { + return new o.Control.Scale(t); + }), + (o.Control.Layers = o.Control.extend({ + options: { collapsed: !0, position: "topright", autoZIndex: !0, hideSingleBase: !1 }, + initialize: function (t, e, i) { + o.setOptions(this, i), + (this._layers = {}), + (this._lastZIndex = 0), + (this._handlingClick = !1); + for (var n in t) this._addLayer(t[n], n); + for (n in e) this._addLayer(e[n], n, !0); + }, + onAdd: function (t) { + return ( + this._initLayout(), + this._update(), + (this._map = t), + t.on("zoomend", this._checkDisabledLayers, this), + this._container + ); + }, + onRemove: function () { + this._map.off("zoomend", this._checkDisabledLayers, this); + }, + addBaseLayer: function (t, e) { + return this._addLayer(t, e), this._update(); + }, + addOverlay: function (t, e) { + return this._addLayer(t, e, !0), this._update(); + }, + removeLayer: function (t) { + return ( + t.off("add remove", this._onLayerChange, this), + delete this._layers[o.stamp(t)], + this._update() + ); + }, + _initLayout: function () { + var t = "leaflet-control-layers", + e = (this._container = o.DomUtil.create("div", t)); + e.setAttribute("aria-haspopup", !0), + o.DomEvent.disableClickPropagation(e), + o.Browser.touch || o.DomEvent.disableScrollPropagation(e); + var i = (this._form = o.DomUtil.create("form", t + "-list")); + if (this.options.collapsed) { + o.Browser.android || + o.DomEvent.on(e, { mouseenter: this._expand, mouseleave: this._collapse }, this); + var n = (this._layersLink = o.DomUtil.create("a", t + "-toggle", e)); + (n.href = "#"), + (n.title = "Layers"), + o.Browser.touch + ? o.DomEvent.on(n, "click", o.DomEvent.stop).on(n, "click", this._expand, this) + : o.DomEvent.on(n, "focus", this._expand, this), + o.DomEvent.on( + i, + "click", + function () { + setTimeout(o.bind(this._onInputClick, this), 0); + }, + this + ), + this._map.on("click", this._collapse, this); + } else this._expand(); + (this._baseLayersList = o.DomUtil.create("div", t + "-base", i)), + (this._separator = o.DomUtil.create("div", t + "-separator", i)), + (this._overlaysList = o.DomUtil.create("div", t + "-overlays", i)), + e.appendChild(i); + }, + _addLayer: function (t, e, i) { + t.on("add remove", this._onLayerChange, this); + var n = o.stamp(t); + (this._layers[n] = { layer: t, name: e, overlay: i }), + this.options.autoZIndex && + t.setZIndex && + (this._lastZIndex++, t.setZIndex(this._lastZIndex)); + }, + _update: function () { + if (!this._container) return this; + o.DomUtil.empty(this._baseLayersList), o.DomUtil.empty(this._overlaysList); + var t, + e, + i, + n, + s = 0; + for (i in this._layers) + (n = this._layers[i]), + this._addItem(n), + (e = e || n.overlay), + (t = t || !n.overlay), + (s += n.overlay ? 0 : 1); + return ( + this.options.hideSingleBase && + ((t = t && s > 1), (this._baseLayersList.style.display = t ? "" : "none")), + (this._separator.style.display = e && t ? "" : "none"), + this + ); + }, + _onLayerChange: function (t) { + this._handlingClick || this._update(); + var e = this._layers[o.stamp(t.target)], + i = e.overlay + ? "add" === t.type + ? "overlayadd" + : "overlayremove" + : "add" === t.type + ? "baselayerchange" + : null; + i && this._map.fire(i, e); + }, + _createRadioElement: function (t, i) { + var n = + '", + o = e.createElement("div"); + return (o.innerHTML = n), o.firstChild; + }, + _addItem: function (t) { + var i, + n = e.createElement("label"), + s = this._map.hasLayer(t.layer); + t.overlay + ? ((i = e.createElement("input")), + (i.type = "checkbox"), + (i.className = "leaflet-control-layers-selector"), + (i.defaultChecked = s)) + : (i = this._createRadioElement("leaflet-base-layers", s)), + (i.layerId = o.stamp(t.layer)), + o.DomEvent.on(i, "click", this._onInputClick, this); + var r = e.createElement("span"); + r.innerHTML = " " + t.name; + var a = e.createElement("div"); + n.appendChild(a), a.appendChild(i), a.appendChild(r); + var h = t.overlay ? this._overlaysList : this._baseLayersList; + return h.appendChild(n), this._checkDisabledLayers(), n; + }, + _onInputClick: function () { + var t, + e, + i, + n = this._form.getElementsByTagName("input"), + o = [], + s = []; + this._handlingClick = !0; + for (var r = n.length - 1; r >= 0; r--) + (t = n[r]), + (e = this._layers[t.layerId].layer), + (i = this._map.hasLayer(e)), + t.checked && !i ? o.push(e) : !t.checked && i && s.push(e); + for (r = 0; r < s.length; r++) this._map.removeLayer(s[r]); + for (r = 0; r < o.length; r++) this._map.addLayer(o[r]); + (this._handlingClick = !1), this._refocusOnMap(); + }, + _expand: function () { + o.DomUtil.addClass(this._container, "leaflet-control-layers-expanded"), + (this._form.style.height = null); + var t = this._map._size.y - (this._container.offsetTop + 50); + t < this._form.clientHeight + ? (o.DomUtil.addClass(this._form, "leaflet-control-layers-scrollbar"), + (this._form.style.height = t + "px")) + : o.DomUtil.removeClass(this._form, "leaflet-control-layers-scrollbar"), + this._checkDisabledLayers(); + }, + _collapse: function () { + o.DomUtil.removeClass(this._container, "leaflet-control-layers-expanded"); + }, + _checkDisabledLayers: function () { + for ( + var t, + e, + n = this._form.getElementsByTagName("input"), + o = this._map.getZoom(), + s = n.length - 1; + s >= 0; + s-- + ) + (t = n[s]), + (e = this._layers[t.layerId].layer), + (t.disabled = + (e.options.minZoom !== i && o < e.options.minZoom) || + (e.options.maxZoom !== i && o > e.options.maxZoom)); + }, + })), + (o.control.layers = function (t, e, i) { + return new o.Control.Layers(t, e, i); + }), + (o.PosAnimation = o.Evented.extend({ + run: function (t, e, i, n) { + this.stop(), + (this._el = t), + (this._inProgress = !0), + (this._duration = i || 0.25), + (this._easeOutPower = 1 / Math.max(n || 0.5, 0.2)), + (this._startPos = o.DomUtil.getPosition(t)), + (this._offset = e.subtract(this._startPos)), + (this._startTime = +new Date()), + this.fire("start"), + this._animate(); + }, + stop: function () { + this._inProgress && (this._step(!0), this._complete()); + }, + _animate: function () { + (this._animId = o.Util.requestAnimFrame(this._animate, this)), this._step(); + }, + _step: function (t) { + var e = +new Date() - this._startTime, + i = 1e3 * this._duration; + i > e ? this._runFrame(this._easeOut(e / i), t) : (this._runFrame(1), this._complete()); + }, + _runFrame: function (t, e) { + var i = this._startPos.add(this._offset.multiplyBy(t)); + e && i._round(), o.DomUtil.setPosition(this._el, i), this.fire("step"); + }, + _complete: function () { + o.Util.cancelAnimFrame(this._animId), (this._inProgress = !1), this.fire("end"); + }, + _easeOut: function (t) { + return 1 - Math.pow(1 - t, this._easeOutPower); + }, + })), + o.Map.include({ + setView: function (t, e, n) { + if ( + ((e = e === i ? this._zoom : this._limitZoom(e)), + (t = this._limitCenter(o.latLng(t), e, this.options.maxBounds)), + (n = n || {}), + this.stop(), + this._loaded && !n.reset && n !== !0) + ) { + n.animate !== i && + ((n.zoom = o.extend({ animate: n.animate }, n.zoom)), + (n.pan = o.extend({ animate: n.animate, duration: n.duration }, n.pan))); + var s = + this._zoom !== e + ? this._tryAnimatedZoom && this._tryAnimatedZoom(t, e, n.zoom) + : this._tryAnimatedPan(t, n.pan); + if (s) return clearTimeout(this._sizeTimer), this; + } + return this._resetView(t, e), this; + }, + panBy: function (t, e) { + if (((t = o.point(t).round()), (e = e || {}), !t.x && !t.y)) return this.fire("moveend"); + if (e.animate !== !0 && !this.getSize().contains(t)) + return ( + this._resetView( + this.unproject(this.project(this.getCenter()).add(t)), + this.getZoom() + ), + this + ); + if ( + (this._panAnim || + ((this._panAnim = new o.PosAnimation()), + this._panAnim.on( + { step: this._onPanTransitionStep, end: this._onPanTransitionEnd }, + this + )), + e.noMoveStart || this.fire("movestart"), + e.animate !== !1) + ) { + o.DomUtil.addClass(this._mapPane, "leaflet-pan-anim"); + var i = this._getMapPanePos().subtract(t); + this._panAnim.run(this._mapPane, i, e.duration || 0.25, e.easeLinearity); + } else this._rawPanBy(t), this.fire("move").fire("moveend"); + return this; + }, + _onPanTransitionStep: function () { + this.fire("move"); + }, + _onPanTransitionEnd: function () { + o.DomUtil.removeClass(this._mapPane, "leaflet-pan-anim"), this.fire("moveend"); + }, + _tryAnimatedPan: function (t, e) { + var i = this._getCenterOffset(t)._floor(); + return (e && e.animate) === !0 || this.getSize().contains(i) ? (this.panBy(i, e), !0) : !1; + }, + }), + o.Map.mergeOptions({ zoomAnimation: !0, zoomAnimationThreshold: 4 }); + var h = o.DomUtil.TRANSITION && o.Browser.any3d && !o.Browser.mobileOpera; + h && + o.Map.addInitHook(function () { + (this._zoomAnimated = this.options.zoomAnimation), + this._zoomAnimated && + (this._createAnimProxy(), + o.DomEvent.on(this._proxy, o.DomUtil.TRANSITION_END, this._catchTransitionEnd, this)); + }), + o.Map.include( + h + ? { + _createAnimProxy: function () { + var t = (this._proxy = o.DomUtil.create( + "div", + "leaflet-proxy leaflet-zoom-animated" + )); + this._panes.mapPane.appendChild(t), + this.on( + "zoomanim", + function (e) { + var i = o.DomUtil.TRANSFORM, + n = t.style[i]; + o.DomUtil.setTransform( + t, + this.project(e.center, e.zoom), + this.getZoomScale(e.zoom, 1) + ), + n === t.style[i] && + this._animatingZoom && + this._onZoomTransitionEnd(); + }, + this + ), + this.on( + "load moveend", + function () { + var e = this.getCenter(), + i = this.getZoom(); + o.DomUtil.setTransform( + t, + this.project(e, i), + this.getZoomScale(i, 1) + ); + }, + this + ); + }, + _catchTransitionEnd: function (t) { + this._animatingZoom && + t.propertyName.indexOf("transform") >= 0 && + this._onZoomTransitionEnd(); + }, + _nothingToAnimate: function () { + return !this._container.getElementsByClassName("leaflet-zoom-animated").length; + }, + _tryAnimatedZoom: function (t, e, i) { + if (this._animatingZoom) return !0; + if ( + ((i = i || {}), + !this._zoomAnimated || + i.animate === !1 || + this._nothingToAnimate() || + Math.abs(e - this._zoom) > this.options.zoomAnimationThreshold) + ) + return !1; + var n = this.getZoomScale(e), + s = this._getCenterOffset(t)._divideBy(1 - 1 / n); + return i.animate === !0 || this.getSize().contains(s) + ? (o.Util.requestAnimFrame(function () { + this._moveStart(!0)._animateZoom(t, e, !0); + }, this), + !0) + : !1; + }, + _animateZoom: function (t, e, i, n) { + i && + ((this._animatingZoom = !0), + (this._animateToCenter = t), + (this._animateToZoom = e), + o.DomUtil.addClass(this._mapPane, "leaflet-zoom-anim")), + this.fire("zoomanim", { center: t, zoom: e, noUpdate: n }), + setTimeout(o.bind(this._onZoomTransitionEnd, this), 250); + }, + _onZoomTransitionEnd: function () { + this._animatingZoom && + (o.DomUtil.removeClass(this._mapPane, "leaflet-zoom-anim"), + o.Util.requestAnimFrame(function () { + (this._animatingZoom = !1), + this._move(this._animateToCenter, this._animateToZoom)._moveEnd(!0); + }, this)); + }, + } + : {} + ), + o.Map.include({ + flyTo: function (t, e, n) { + function s(t) { + var e = (v * v - g * g + (t ? -1 : 1) * L * L * y * y) / (2 * (t ? v : g) * L * y); + return Math.log(Math.sqrt(e * e + 1) - e); + } - function a(t) { return (Math.exp(t) + Math.exp(-t)) / 2 } + function r(t) { + return (Math.exp(t) - Math.exp(-t)) / 2; + } - function h(t) { return r(t) / a(t) } + function a(t) { + return (Math.exp(t) + Math.exp(-t)) / 2; + } - function l(t) { return g * (a(x) / a(x + P * t)) } + function h(t) { + return r(t) / a(t); + } - function u(t) { return g * (a(x) * h(x + P * t) - r(x)) / L } + function l(t) { + return g * (a(x) / a(x + P * t)); + } - function c(t) { return 1 - Math.pow(1 - t, 1.5) } + function u(t) { + return (g * (a(x) * h(x + P * t) - r(x))) / L; + } - function d() { var i = (Date.now() - b) / D, - n = c(i) * w; - 1 >= i ? (this._flyToFrame = o.Util.requestAnimFrame(d, this), this._move(this.unproject(_.add(m.subtract(_).multiplyBy(u(n) / y)), f), this.getScaleZoom(g / l(n), f), { flyTo: !0 })) : this._move(t, e)._moveEnd(!0) } if (n = n || {}, n.animate === !1 || !o.Browser.any3d) return this.setView(t, e, n); - this.stop(); var _ = this.project(this.getCenter()), - m = this.project(t), - p = this.getSize(), - f = this._zoom; - t = o.latLng(t), e = e === i ? f : e; var g = Math.max(p.x, p.y), - v = g * this.getZoomScale(f, e), - y = m.distanceTo(_) || 1, - P = 1.42, - L = P * P, - x = s(0), - b = Date.now(), - w = (s(1) - x) / P, - D = n.duration ? 1e3 * n.duration : 1e3 * w * .8; return this._moveStart(!0), d.call(this), this }, flyToBounds: function(t, e) { var i = this._getBoundsCenterZoom(t, e); return this.flyTo(i.center, i.zoom, e) } }), o.Map.include({ _defaultLocateOptions: { timeout: 1e4, watch: !1 }, locate: function(t) { if (t = this._locateOptions = o.extend({}, this._defaultLocateOptions, t), !("geolocation" in navigator)) return this._handleGeolocationError({ code: 0, message: "Geolocation not supported." }), this; var e = o.bind(this._handleGeolocationResponse, this), - i = o.bind(this._handleGeolocationError, this); return t.watch ? this._locationWatchId = navigator.geolocation.watchPosition(e, i, t) : navigator.geolocation.getCurrentPosition(e, i, t), this }, stopLocate: function() { return navigator.geolocation && navigator.geolocation.clearWatch && navigator.geolocation.clearWatch(this._locationWatchId), this._locateOptions && (this._locateOptions.setView = !1), this }, _handleGeolocationError: function(t) { var e = t.code, - i = t.message || (1 === e ? "permission denied" : 2 === e ? "position unavailable" : "timeout"); - this._locateOptions.setView && !this._loaded && this.fitWorld(), this.fire("locationerror", { code: e, message: "Geolocation error: " + i + "." }) }, _handleGeolocationResponse: function(t) { var e = t.coords.latitude, - i = t.coords.longitude, - n = new o.LatLng(e, i), - s = n.toBounds(t.coords.accuracy), - r = this._locateOptions; if (r.setView) { var a = this.getBoundsZoom(s); - this.setView(n, r.maxZoom ? Math.min(a, r.maxZoom) : a) } var h = { latlng: n, bounds: s, timestamp: t.timestamp }; for (var l in t.coords) "number" == typeof t.coords[l] && (h[l] = t.coords[l]); - this.fire("locationfound", h) } }) -}(window, document); + function c(t) { + return 1 - Math.pow(1 - t, 1.5); + } + + function d() { + var i = (Date.now() - b) / D, + n = c(i) * w; + 1 >= i + ? ((this._flyToFrame = o.Util.requestAnimFrame(d, this)), + this._move( + this.unproject(_.add(m.subtract(_).multiplyBy(u(n) / y)), f), + this.getScaleZoom(g / l(n), f), + { flyTo: !0 } + )) + : this._move(t, e)._moveEnd(!0); + } + if (((n = n || {}), n.animate === !1 || !o.Browser.any3d)) return this.setView(t, e, n); + this.stop(); + var _ = this.project(this.getCenter()), + m = this.project(t), + p = this.getSize(), + f = this._zoom; + (t = o.latLng(t)), (e = e === i ? f : e); + var g = Math.max(p.x, p.y), + v = g * this.getZoomScale(f, e), + y = m.distanceTo(_) || 1, + P = 1.42, + L = P * P, + x = s(0), + b = Date.now(), + w = (s(1) - x) / P, + D = n.duration ? 1e3 * n.duration : 1e3 * w * 0.8; + return this._moveStart(!0), d.call(this), this; + }, + flyToBounds: function (t, e) { + var i = this._getBoundsCenterZoom(t, e); + return this.flyTo(i.center, i.zoom, e); + }, + }), + o.Map.include({ + _defaultLocateOptions: { timeout: 1e4, watch: !1 }, + locate: function (t) { + if ( + ((t = this._locateOptions = o.extend({}, this._defaultLocateOptions, t)), + !("geolocation" in navigator)) + ) + return ( + this._handleGeolocationError({ code: 0, message: "Geolocation not supported." }), this + ); + var e = o.bind(this._handleGeolocationResponse, this), + i = o.bind(this._handleGeolocationError, this); + return ( + t.watch + ? (this._locationWatchId = navigator.geolocation.watchPosition(e, i, t)) + : navigator.geolocation.getCurrentPosition(e, i, t), + this + ); + }, + stopLocate: function () { + return ( + navigator.geolocation && + navigator.geolocation.clearWatch && + navigator.geolocation.clearWatch(this._locationWatchId), + this._locateOptions && (this._locateOptions.setView = !1), + this + ); + }, + _handleGeolocationError: function (t) { + var e = t.code, + i = + t.message || + (1 === e ? "permission denied" : 2 === e ? "position unavailable" : "timeout"); + this._locateOptions.setView && !this._loaded && this.fitWorld(), + this.fire("locationerror", { code: e, message: "Geolocation error: " + i + "." }); + }, + _handleGeolocationResponse: function (t) { + var e = t.coords.latitude, + i = t.coords.longitude, + n = new o.LatLng(e, i), + s = n.toBounds(t.coords.accuracy), + r = this._locateOptions; + if (r.setView) { + var a = this.getBoundsZoom(s); + this.setView(n, r.maxZoom ? Math.min(a, r.maxZoom) : a); + } + var h = { latlng: n, bounds: s, timestamp: t.timestamp }; + for (var l in t.coords) "number" == typeof t.coords[l] && (h[l] = t.coords[l]); + this.fire("locationfound", h); + }, + }); +})(window, document); diff --git a/erpnext/public/js/newsletter.js b/erpnext/public/js/newsletter.js index 3a4dbf89330..96c65de8d6f 100644 --- a/erpnext/public/js/newsletter.js +++ b/erpnext/public/js/newsletter.js @@ -1,8 +1,8 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.ui.form.on('Newsletter', { +frappe.ui.form.on("Newsletter", { refresh() { erpnext.toggle_naming_series(); - } + }, }); diff --git a/erpnext/public/js/payment/payments.js b/erpnext/public/js/payment/payments.js index 4c23669dbbb..0e584205396 100644 --- a/erpnext/public/js/payment/payments.js +++ b/erpnext/public/js/payment/payments.js @@ -6,7 +6,7 @@ erpnext.payments = class payments extends erpnext.stock.StockController { var me = this; this.dialog = new frappe.ui.Dialog({ - title: 'Payment' + title: "Payment", }); this.dialog.show(); @@ -17,15 +17,17 @@ erpnext.payments = class payments extends erpnext.stock.StockController { } select_text() { - $(this.$body).find('.form-control').click(function() { - $(this).select(); - }); + $(this.$body) + .find(".form-control") + .click(function () { + $(this).select(); + }); } set_payment_primary_action() { var me = this; - this.dialog.set_primary_action(__("Submit"), function() { + this.dialog.set_primary_action(__("Submit"), function () { // Allow no ZERO payment $.each(me.frm.doc.payments, function (index, data) { if (data.amount != 0) { @@ -34,131 +36,147 @@ erpnext.payments = class payments extends erpnext.stock.StockController { return; } }); - }) + }); } - make_keyboard(){ + make_keyboard() { var me = this; $(this.$body).empty(); - $(this.$body).html(frappe.render_template('pos_payment', this.frm.doc)) + $(this.$body).html(frappe.render_template("pos_payment", this.frm.doc)); this.show_payment_details(); - this.bind_keyboard_event() - this.clear_amount() + this.bind_keyboard_event(); + this.clear_amount(); } - make_multimode_payment(){ + make_multimode_payment() { var me = this; if (this.frm.doc.change_amount > 0) { me.payment_val = me.doc.outstanding_amount; } - this.payments = frappe.model.add_child(this.frm.doc, 'Multi Mode Payment', "payments"); + this.payments = frappe.model.add_child(this.frm.doc, "Multi Mode Payment", "payments"); this.payments.mode_of_payment = this.dialog.fields_dict.mode_of_payment.get_value(); this.payments.amount = flt(this.payment_val); } - show_payment_details(){ + show_payment_details() { var me = this; - var multimode_payments = $(this.$body).find('.multimode-payments').empty(); + var multimode_payments = $(this.$body).find(".multimode-payments").empty(); if (this.frm.doc.payments.length) { - $.each(this.frm.doc.payments, function(index, data) { - $(frappe.render_template('payment_details', { - mode_of_payment: data.mode_of_payment, - amount: data.amount, - idx: data.idx, - currency: me.frm.doc.currency, - type: data.type - })).appendTo(multimode_payments) + $.each(this.frm.doc.payments, function (index, data) { + $( + frappe.render_template("payment_details", { + mode_of_payment: data.mode_of_payment, + amount: data.amount, + idx: data.idx, + currency: me.frm.doc.currency, + type: data.type, + }) + ).appendTo(multimode_payments); - if (data.type == 'Cash' && data.amount == me.frm.doc.paid_amount) { + if (data.type == "Cash" && data.amount == me.frm.doc.paid_amount) { me.idx = data.idx; - me.selected_mode = $(me.$body).find(repl("input[idx='%(idx)s']",{'idx': me.idx})); + me.selected_mode = $(me.$body).find(repl("input[idx='%(idx)s']", { idx: me.idx })); me.highlight_selected_row(); me.bind_amount_change_event(); } - }) - }else{ - $("

              No payment mode selected in pos profile

              ").appendTo(multimode_payments) + }); + } else { + $("

              No payment mode selected in pos profile

              ").appendTo(multimode_payments); } } - set_outstanding_amount(){ - this.selected_mode = $(this.$body).find(repl("input[idx='%(idx)s']",{'idx': this.idx})); + set_outstanding_amount() { + this.selected_mode = $(this.$body).find(repl("input[idx='%(idx)s']", { idx: this.idx })); this.highlight_selected_row(); this.payment_val = 0.0; if (this.frm.doc.outstanding_amount > 0 && flt(this.selected_mode.val()) == 0.0) { //When user first time click on row - this.payment_val = flt(this.frm.doc.outstanding_amount / this.frm.doc.conversion_rate, precision("outstanding_amount")) + this.payment_val = flt( + this.frm.doc.outstanding_amount / this.frm.doc.conversion_rate, + precision("outstanding_amount") + ); this.selected_mode.val(format_currency(this.payment_val, this.frm.doc.currency)); this.update_payment_amount(); } else if (flt(this.selected_mode.val()) > 0) { //If user click on existing row which has value this.payment_val = flt(this.selected_mode.val()); } - this.selected_mode.select() + this.selected_mode.select(); this.bind_amount_change_event(); } - bind_keyboard_event(){ + bind_keyboard_event() { var me = this; - this.payment_val = ''; + this.payment_val = ""; this.bind_form_control_event(); this.bind_numeric_keys_event(); } bind_form_control_event() { var me = this; - $(this.$body).find('.pos-payment-row').click(function() { - me.idx = $(this).attr("idx"); - me.set_outstanding_amount(); - }); + $(this.$body) + .find(".pos-payment-row") + .click(function () { + me.idx = $(this).attr("idx"); + me.set_outstanding_amount(); + }); - $(this.$body).find('.form-control').click(function() { - me.idx = $(this).attr("idx"); - me.set_outstanding_amount(); - me.update_paid_amount(true); - }); + $(this.$body) + .find(".form-control") + .click(function () { + me.idx = $(this).attr("idx"); + me.set_outstanding_amount(); + me.update_paid_amount(true); + }); - $(this.$body).find('.write_off_amount').change(function() { - me.write_off_amount(flt($(this).val()), precision("write_off_amount")); - }); + $(this.$body) + .find(".write_off_amount") + .change(function () { + me.write_off_amount(flt($(this).val()), precision("write_off_amount")); + }); - $(this.$body).find('.change_amount').change(function() { - me.change_amount(flt($(this).val()), precision("change_amount")); - }); + $(this.$body) + .find(".change_amount") + .change(function () { + me.change_amount(flt($(this).val()), precision("change_amount")); + }); } highlight_selected_row() { - var selected_row = $(this.$body).find(repl(".pos-payment-row[idx='%(idx)s']", {'idx': this.idx})); - $(this.$body).find('.pos-payment-row').removeClass('selected-payment-mode'); - selected_row.addClass('selected-payment-mode'); - $(this.$body).find('.amount').attr('disabled', true); - this.selected_mode.attr('disabled', false); + var selected_row = $(this.$body).find(repl(".pos-payment-row[idx='%(idx)s']", { idx: this.idx })); + $(this.$body).find(".pos-payment-row").removeClass("selected-payment-mode"); + selected_row.addClass("selected-payment-mode"); + $(this.$body).find(".amount").attr("disabled", true); + this.selected_mode.attr("disabled", false); } bind_numeric_keys_event() { var me = this; - $(this.$body).find('.pos-keyboard-key').click(function(){ - me.payment_val += $(this).text(); - me.selected_mode.val(format_currency(me.payment_val, me.frm.doc.currency)); - me.idx = me.selected_mode.attr("idx"); - me.update_paid_amount(); - }); - - $(this.$body).find('.delete-btn').click(function() { - me.payment_val = cstr(flt(me.selected_mode.val())).slice(0, -1); - me.selected_mode.val(format_currency(me.payment_val, me.frm.doc.currency)); - me.idx = me.selected_mode.attr("idx"); - me.update_paid_amount(); - }) + $(this.$body) + .find(".pos-keyboard-key") + .click(function () { + me.payment_val += $(this).text(); + me.selected_mode.val(format_currency(me.payment_val, me.frm.doc.currency)); + me.idx = me.selected_mode.attr("idx"); + me.update_paid_amount(); + }); + $(this.$body) + .find(".delete-btn") + .click(function () { + me.payment_val = cstr(flt(me.selected_mode.val())).slice(0, -1); + me.selected_mode.val(format_currency(me.payment_val, me.frm.doc.currency)); + me.idx = me.selected_mode.attr("idx"); + me.update_paid_amount(); + }); } bind_amount_change_event() { var me = this; - this.selected_mode.change(function() { - me.payment_val = flt($(this).val()) || 0.0; + this.selected_mode.change(function () { + me.payment_val = flt($(this).val()) || 0.0; me.selected_mode.val(format_currency(me.payment_val, me.frm.doc.currency)); me.idx = me.selected_mode.attr("idx"); me.update_payment_amount(); @@ -167,21 +185,25 @@ erpnext.payments = class payments extends erpnext.stock.StockController { clear_amount() { var me = this; - $(this.$body).find('.clr').click(function(e) { - e.stopPropagation(); - me.idx = $(this).attr("idx"); - me.selected_mode = $(me.$body).find(repl("input[idx='%(idx)s']",{'idx': me.idx})); - me.payment_val = 0.0; - me.selected_mode.val(0.0); - me.highlight_selected_row(); - me.update_payment_amount(); - }); + $(this.$body) + .find(".clr") + .click(function (e) { + e.stopPropagation(); + me.idx = $(this).attr("idx"); + me.selected_mode = $(me.$body).find(repl("input[idx='%(idx)s']", { idx: me.idx })); + me.payment_val = 0.0; + me.selected_mode.val(0.0); + me.highlight_selected_row(); + me.update_payment_amount(); + }); } write_off_amount(write_off_amount) { this.frm.doc.write_off_amount = flt(write_off_amount, precision("write_off_amount")); - this.frm.doc.base_write_off_amount = flt(this.frm.doc.write_off_amount * this.frm.doc.conversion_rate, - precision("base_write_off_amount")); + this.frm.doc.base_write_off_amount = flt( + this.frm.doc.write_off_amount * this.frm.doc.conversion_rate, + precision("base_write_off_amount") + ); this.calculate_outstanding_amount(false); this.show_amounts(); } @@ -196,13 +218,16 @@ erpnext.payments = class payments extends erpnext.stock.StockController { update_paid_amount(update_write_off) { var me = this; - if (in_list(['change_amount', 'write_off_amount'], this.idx)) { + if (in_list(["change_amount", "write_off_amount"], this.idx)) { var value = me.selected_mode.val(); - if (me.idx == 'change_amount') { + if (me.idx == "change_amount") { me.change_amount(value); } else { - if(flt(value) == 0 && update_write_off && me.frm.doc.outstanding_amount > 0) { - value = flt(me.frm.doc.outstanding_amount / me.frm.doc.conversion_rate, precision(me.idx)); + if (flt(value) == 0 && update_write_off && me.frm.doc.outstanding_amount > 0) { + value = flt( + me.frm.doc.outstanding_amount / me.frm.doc.conversion_rate, + precision(me.idx) + ); } me.write_off_amount(value); } @@ -211,25 +236,38 @@ erpnext.payments = class payments extends erpnext.stock.StockController { } } - update_payment_amount(){ + update_payment_amount() { var me = this; - $.each(this.frm.doc.payments, function(index, data) { + $.each(this.frm.doc.payments, function (index, data) { if (cint(me.idx) == cint(data.idx)) { data.amount = flt(me.selected_mode.val(), 2); } - }) + }); this.calculate_outstanding_amount(false); this.show_amounts(); } - show_amounts(){ + show_amounts() { var me = this; - $(this.$body).find(".write_off_amount").val(format_currency(this.frm.doc.write_off_amount, this.frm.doc.currency)); - $(this.$body).find('.paid_amount').text(format_currency(this.frm.doc.paid_amount, this.frm.doc.currency)); - $(this.$body).find('.change_amount').val(format_currency(this.frm.doc.change_amount, this.frm.doc.currency)); - $(this.$body).find('.outstanding_amount').text(format_currency(this.frm.doc.outstanding_amount, frappe.get_doc(":Company", this.frm.doc.company).default_currency)); + $(this.$body) + .find(".write_off_amount") + .val(format_currency(this.frm.doc.write_off_amount, this.frm.doc.currency)); + $(this.$body) + .find(".paid_amount") + .text(format_currency(this.frm.doc.paid_amount, this.frm.doc.currency)); + $(this.$body) + .find(".change_amount") + .val(format_currency(this.frm.doc.change_amount, this.frm.doc.currency)); + $(this.$body) + .find(".outstanding_amount") + .text( + format_currency( + this.frm.doc.outstanding_amount, + frappe.get_doc(":Company", this.frm.doc.company).default_currency + ) + ); this.update_invoice(); } -} +}; diff --git a/erpnext/public/js/projects/timer.js b/erpnext/public/js/projects/timer.js index 0209f4c2322..8370cc6ffff 100644 --- a/erpnext/public/js/projects/timer.js +++ b/erpnext/public/js/projects/timer.js @@ -1,26 +1,30 @@ frappe.provide("erpnext.timesheet"); -erpnext.timesheet.timer = function(frm, row, timestamp=0) { +erpnext.timesheet.timer = function (frm, row, timestamp = 0) { let dialog = new frappe.ui.Dialog({ title: __("Timer"), - fields: - [ - {"fieldtype": "Link", "label": __("Activity Type"), "fieldname": "activity_type", - "reqd": 1, "options": "Activity Type"}, - {"fieldtype": "Link", "label": __("Project"), "fieldname": "project", "options": "Project"}, - {"fieldtype": "Link", "label": __("Task"), "fieldname": "task", "options": "Task"}, - {"fieldtype": "Float", "label": __("Expected Hrs"), "fieldname": "expected_hours"}, - {"fieldtype": "Section Break"}, - {"fieldtype": "HTML", "fieldname": "timer_html"} - ] + fields: [ + { + fieldtype: "Link", + label: __("Activity Type"), + fieldname: "activity_type", + reqd: 1, + options: "Activity Type", + }, + { fieldtype: "Link", label: __("Project"), fieldname: "project", options: "Project" }, + { fieldtype: "Link", label: __("Task"), fieldname: "task", options: "Task" }, + { fieldtype: "Float", label: __("Expected Hrs"), fieldname: "expected_hours" }, + { fieldtype: "Section Break" }, + { fieldtype: "HTML", fieldname: "timer_html" }, + ], }); if (row) { dialog.set_values({ - 'activity_type': row.activity_type, - 'project': row.project, - 'task': row.task, - 'expected_hours': row.expected_hours + activity_type: row.activity_type, + project: row.project, + task: row.task, + expected_hours: row.expected_hours, }); } dialog.get_field("timer_html").$wrapper.append(get_timer_html()); @@ -34,8 +38,8 @@ erpnext.timesheet.timer = function(frm, row, timestamp=0) { 00
              - - + +
              `; } @@ -43,7 +47,7 @@ erpnext.timesheet.timer = function(frm, row, timestamp=0) { dialog.show(); }; -erpnext.timesheet.control_timer = function(frm, dialog, row, timestamp=0) { +erpnext.timesheet.control_timer = function (frm, dialog, row, timestamp = 0) { var $btn_start = dialog.$wrapper.find(".playpause .btn-start"); var $btn_complete = dialog.$wrapper.find(".playpause .btn-complete"); var interval = null; @@ -63,12 +67,16 @@ erpnext.timesheet.control_timer = function(frm, dialog, row, timestamp=0) { $btn_complete.hide(); } - $btn_start.click(function(e) { + $btn_start.click(function (e) { if (!initialized) { // New activity if no activities found var args = dialog.get_values(); - if(!args) return; - if (frm.doc.time_logs.length == 1 && !frm.doc.time_logs[0].activity_type && !frm.doc.time_logs[0].from_time) { + if (!args) return; + if ( + frm.doc.time_logs.length == 1 && + !frm.doc.time_logs[0].activity_type && + !frm.doc.time_logs[0].from_time + ) { frm.doc.time_logs = []; } row = frappe.model.add_child(frm.doc, "Timesheet Detail", "time_logs"); @@ -79,7 +87,7 @@ erpnext.timesheet.control_timer = function(frm, dialog, row, timestamp=0) { row.expected_hours = args.expected_hours; row.completed = 0; let d = moment(row.from_time); - if(row.expected_hours) { + if (row.expected_hours) { d.add(row.expected_hours, "hours"); row.to_time = d.format(frappe.defaultDatetimeFormat); } @@ -101,8 +109,8 @@ erpnext.timesheet.control_timer = function(frm, dialog, row, timestamp=0) { }); // Stop the timer and update the time logged by the timer on click of 'Complete' button - $btn_complete.click(function() { - var grid_row = cur_frm.fields_dict['time_logs'].grid.get_row(row.idx - 1); + $btn_complete.click(function () { + var grid_row = cur_frm.fields_dict["time_logs"].grid.get_row(row.idx - 1); var args = dialog.get_values(); grid_row.doc.completed = 1; grid_row.doc.activity_type = args.activity_type; @@ -119,7 +127,7 @@ erpnext.timesheet.control_timer = function(frm, dialog, row, timestamp=0) { }); function initializeTimer() { - interval = setInterval(function() { + interval = setInterval(function () { var current = setCurrentIncrement(); updateStopwatch(current); }, 1000); @@ -127,25 +135,24 @@ erpnext.timesheet.control_timer = function(frm, dialog, row, timestamp=0) { function updateStopwatch(increment) { var hours = Math.floor(increment / 3600); - var minutes = Math.floor((increment - (hours * 3600)) / 60); - var seconds = increment - (hours * 3600) - (minutes * 60); + var minutes = Math.floor((increment - hours * 3600) / 60); + var seconds = increment - hours * 3600 - minutes * 60; // If modal is closed by clicking anywhere outside, reset the timer - if (!$('.modal-dialog').is(':visible')) { + if (!$(".modal-dialog").is(":visible")) { reset(); } - if(hours > 99999) - reset(); - if(cur_dialog && cur_dialog.get_value('expected_hours') > 0) { - if(flag && (currentIncrement >= (cur_dialog.get_value('expected_hours') * 3600))) { + if (hours > 99999) reset(); + if (cur_dialog && cur_dialog.get_value("expected_hours") > 0) { + if (flag && currentIncrement >= cur_dialog.get_value("expected_hours") * 3600) { frappe.utils.play_sound("alert"); frappe.msgprint(__("Timer exceeded the given hours.")); flag = false; } } - $(".hours").text(hours < 10 ? ("0" + hours.toString()) : hours.toString()); - $(".minutes").text(minutes < 10 ? ("0" + minutes.toString()) : minutes.toString()); - $(".seconds").text(seconds < 10 ? ("0" + seconds.toString()) : seconds.toString()); + $(".hours").text(hours < 10 ? "0" + hours.toString() : hours.toString()); + $(".minutes").text(minutes < 10 ? "0" + minutes.toString() : minutes.toString()); + $(".seconds").text(seconds < 10 ? "0" + seconds.toString() : seconds.toString()); } function setCurrentIncrement() { diff --git a/erpnext/public/js/purchase_trends_filters.js b/erpnext/public/js/purchase_trends_filters.js index 77f1d2b496a..14ffaf82162 100644 --- a/erpnext/public/js/purchase_trends_filters.js +++ b/erpnext/public/js/purchase_trends_filters.js @@ -1,71 +1,67 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -erpnext.get_purchase_trends_filters = function() { +erpnext.get_purchase_trends_filters = function () { return [ { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "reqd": 1, - "default": frappe.defaults.get_user_default("Company") + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + reqd: 1, + default: frappe.defaults.get_user_default("Company"), }, { - "fieldname":"period", - "label": __("Period"), - "fieldtype": "Select", - "options": [ - { "value": "Monthly", "label": __("Monthly") }, - { "value": "Quarterly", "label": __("Quarterly") }, - { "value": "Half-Yearly", "label": __("Half-Yearly") }, - { "value": "Yearly", "label": __("Yearly") } + fieldname: "period", + label: __("Period"), + fieldtype: "Select", + options: [ + { value: "Monthly", label: __("Monthly") }, + { value: "Quarterly", label: __("Quarterly") }, + { value: "Half-Yearly", label: __("Half-Yearly") }, + { value: "Yearly", label: __("Yearly") }, ], - "default": "Monthly" + default: "Monthly", }, { - "fieldname":"fiscal_year", - "label": __("Fiscal Year"), - "fieldtype": "Link", - "options":'Fiscal Year', - "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()) + fieldname: "fiscal_year", + label: __("Fiscal Year"), + fieldtype: "Link", + options: "Fiscal Year", + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), }, { - "fieldname":"period_based_on", - "label": __("Period based On"), - "fieldtype": "Select", - "options": [ - { "value": "posting_date", "label": __("Posting Date") }, - { "value": "bill_date", "label": __("Billing Date") }, + fieldname: "period_based_on", + label: __("Period based On"), + fieldtype: "Select", + options: [ + { value: "posting_date", label: __("Posting Date") }, + { value: "bill_date", label: __("Billing Date") }, ], - "default": "posting_date" + default: "posting_date", }, { - "fieldname":"based_on", - "label": __("Based On"), - "fieldtype": "Select", - "options": [ - { "value": "Item", "label": __("Item") }, - { "value": "Item Group", "label": __("Item Group") }, - { "value": "Supplier", "label": __("Supplier") }, - { "value": "Supplier Group", "label": __("Supplier Group") }, - { "value": "Project", "label": __("Project") } + fieldname: "based_on", + label: __("Based On"), + fieldtype: "Select", + options: [ + { value: "Item", label: __("Item") }, + { value: "Item Group", label: __("Item Group") }, + { value: "Supplier", label: __("Supplier") }, + { value: "Supplier Group", label: __("Supplier Group") }, + { value: "Project", label: __("Project") }, ], - "default": "Item", - "dashboard_config": { - "read_only": 1 - } + default: "Item", + dashboard_config: { + read_only: 1, + }, }, { - "fieldname":"group_by", - "label": __("Group By"), - "fieldtype": "Select", - "options": [ - "", - { "value": "Item", "label": __("Item") }, - { "value": "Supplier", "label": __("Supplier") } - ], - "default": "" + fieldname: "group_by", + label: __("Group By"), + fieldtype: "Select", + options: ["", { value: "Item", label: __("Item") }, { value: "Supplier", label: __("Supplier") }], + default: "", }, ]; -} +}; diff --git a/erpnext/public/js/queries.js b/erpnext/public/js/queries.js index b7d880ae408..b7e685cd6fb 100644 --- a/erpnext/public/js/queries.js +++ b/erpnext/public/js/queries.js @@ -4,129 +4,139 @@ // searches for enabled users frappe.provide("erpnext.queries"); $.extend(erpnext.queries, { - user: function() { + user: function () { return { query: "frappe.core.doctype.user.user.user_query" }; }, - lead: function() { + lead: function () { return { query: "erpnext.controllers.queries.lead_query" }; }, - customer: function() { + customer: function () { return { query: "erpnext.controllers.queries.customer_query" }; }, - supplier: function() { + supplier: function () { return { query: "erpnext.controllers.queries.supplier_query" }; }, - item: function(filters) { + item: function (filters) { var args = { query: "erpnext.controllers.queries.item_query" }; - if(filters) args["filters"] = filters; + if (filters) args["filters"] = filters; return args; }, - bom: function() { + bom: function () { return { query: "erpnext.controllers.queries.bom" }; }, - task: function() { + task: function () { return { query: "erpnext.projects.utils.query_task" }; }, - customer_filter: function(doc) { - if(!doc.customer) { - frappe.throw(__("Please set {0}", [__(frappe.meta.get_label(doc.doctype, "customer", doc.name))])); + customer_filter: function (doc) { + if (!doc.customer) { + frappe.throw( + __("Please set {0}", [__(frappe.meta.get_label(doc.doctype, "customer", doc.name))]) + ); } return { filters: { customer: doc.customer } }; }, - contact_query: function(doc) { - if(frappe.dynamic_link) { - if(!doc[frappe.dynamic_link.fieldname]) { - frappe.throw(__("Please set {0}", - [__(frappe.meta.get_label(doc.doctype, frappe.dynamic_link.fieldname, doc.name))])); + contact_query: function (doc) { + if (frappe.dynamic_link) { + if (!doc[frappe.dynamic_link.fieldname]) { + frappe.throw( + __("Please set {0}", [ + __(frappe.meta.get_label(doc.doctype, frappe.dynamic_link.fieldname, doc.name)), + ]) + ); } return { - query: 'frappe.contacts.doctype.contact.contact.contact_query', + query: "frappe.contacts.doctype.contact.contact.contact_query", filters: { link_doctype: frappe.dynamic_link.doctype, - link_name: doc[frappe.dynamic_link.fieldname] - } + link_name: doc[frappe.dynamic_link.fieldname], + }, }; } }, - address_query: function(doc) { - if(frappe.dynamic_link) { - if(!doc[frappe.dynamic_link.fieldname]) { - frappe.throw(__("Please set {0}", - [__(frappe.meta.get_label(doc.doctype, frappe.dynamic_link.fieldname, doc.name))])); + address_query: function (doc) { + if (frappe.dynamic_link) { + if (!doc[frappe.dynamic_link.fieldname]) { + frappe.throw( + __("Please set {0}", [ + __(frappe.meta.get_label(doc.doctype, frappe.dynamic_link.fieldname, doc.name)), + ]) + ); } return { - query: 'frappe.contacts.doctype.address.address.address_query', + query: "frappe.contacts.doctype.address.address.address_query", filters: { link_doctype: frappe.dynamic_link.doctype, - link_name: doc[frappe.dynamic_link.fieldname] - } + link_name: doc[frappe.dynamic_link.fieldname], + }, }; } }, - company_address_query: function(doc) { + company_address_query: function (doc) { return { - query: 'frappe.contacts.doctype.address.address.address_query', - filters: { is_your_company_address: 1, link_doctype: 'Company', link_name: doc.company || '' } + query: "frappe.contacts.doctype.address.address.address_query", + filters: { is_your_company_address: 1, link_doctype: "Company", link_name: doc.company || "" }, }; }, - dispatch_address_query: function(doc) { + dispatch_address_query: function (doc) { return { - query: 'frappe.contacts.doctype.address.address.address_query', - filters: { link_doctype: 'Company', link_name: doc.company || '' } + query: "frappe.contacts.doctype.address.address.address_query", + filters: { link_doctype: "Company", link_name: doc.company || "" }, }; }, - supplier_filter: function(doc) { - if(!doc.supplier) { - frappe.throw(__("Please set {0}", [__(frappe.meta.get_label(doc.doctype, "supplier", doc.name))])); + supplier_filter: function (doc) { + if (!doc.supplier) { + frappe.throw( + __("Please set {0}", [__(frappe.meta.get_label(doc.doctype, "supplier", doc.name))]) + ); } return { filters: { supplier: doc.supplier } }; }, - lead_filter: function(doc) { - if(!doc.lead) { - frappe.throw(__("Please specify a {0}", - [__(frappe.meta.get_label(doc.doctype, "lead", doc.name))])); + lead_filter: function (doc) { + if (!doc.lead) { + frappe.throw( + __("Please specify a {0}", [__(frappe.meta.get_label(doc.doctype, "lead", doc.name))]) + ); } return { filters: { lead: doc.lead } }; }, - not_a_group_filter: function() { + not_a_group_filter: function () { return { filters: { is_group: 0 } }; }, - employee: function() { - return { query: "erpnext.controllers.queries.employee_query" } + employee: function () { + return { query: "erpnext.controllers.queries.employee_query" }; }, - warehouse: function(doc) { + warehouse: function (doc) { return { filters: [ ["Warehouse", "company", "in", ["", cstr(doc.company)]], - ["Warehouse", "is_group", "=",0] - - ] + ["Warehouse", "is_group", "=", 0], + ], }; }, - get_filtered_dimensions: function(doc, child_fields, dimension, company) { - let account = ''; + get_filtered_dimensions: function (doc, child_fields, dimension, company) { + let account = ""; child_fields.forEach((field) => { if (!account) { @@ -137,21 +147,23 @@ $.extend(erpnext.queries, { return { query: "erpnext.controllers.queries.get_filtered_dimensions", filters: { - 'dimension': dimension, - 'account': account, - 'company': company - } + dimension: dimension, + account: account, + company: company, + }, }; - } + }, }); -erpnext.queries.setup_queries = function(frm, options, query_fn) { +erpnext.queries.setup_queries = function (frm, options, query_fn) { var me = this; - var set_query = function(doctype, parentfield) { - var link_fields = frappe.meta.get_docfields(doctype, frm.doc.name, - {"fieldtype": "Link", "options": options}); - $.each(link_fields, function(i, df) { - if(parentfield) { + var set_query = function (doctype, parentfield) { + var link_fields = frappe.meta.get_docfields(doctype, frm.doc.name, { + fieldtype: "Link", + options: options, + }); + $.each(link_fields, function (i, df) { + if (parentfield) { frm.set_query(df.fieldname, parentfield, query_fn); } else { frm.set_query(df.fieldname, query_fn); @@ -162,24 +174,26 @@ erpnext.queries.setup_queries = function(frm, options, query_fn) { set_query(frm.doc.doctype); // warehouse field in tables - $.each(frappe.meta.get_docfields(frm.doc.doctype, frm.doc.name, {"fieldtype": "Table"}), - function(i, df) { + $.each( + frappe.meta.get_docfields(frm.doc.doctype, frm.doc.name, { fieldtype: "Table" }), + function (i, df) { set_query(df.options, df.fieldname); - }); -} + } + ); +}; /* if item code is selected in child table then list down warehouses with its quantity else apply default filters. */ -erpnext.queries.setup_warehouse_query = function(frm){ - frm.set_query('warehouse', 'items', function(doc, cdt, cdn) { - var row = locals[cdt][cdn]; +erpnext.queries.setup_warehouse_query = function (frm) { + frm.set_query("warehouse", "items", function (doc, cdt, cdn) { + var row = locals[cdt][cdn]; var filters = erpnext.queries.warehouse(frm.doc); - if(row.item_code){ - $.extend(filters, {"query":"erpnext.controllers.queries.warehouse_query"}); + if (row.item_code) { + $.extend(filters, { query: "erpnext.controllers.queries.warehouse_query" }); filters["filters"].push(["Bin", "item_code", "=", row.item_code]); } - return filters + return filters; }); -} +}; diff --git a/erpnext/public/js/sales_trends_filters.js b/erpnext/public/js/sales_trends_filters.js index 9a70a3da4c6..85daa01ff67 100644 --- a/erpnext/public/js/sales_trends_filters.js +++ b/erpnext/public/js/sales_trends_filters.js @@ -1,61 +1,57 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -erpnext.get_sales_trends_filters = function() { - return[ +erpnext.get_sales_trends_filters = function () { + return [ { - "fieldname":"period", - "label": __("Period"), - "fieldtype": "Select", - "options": [ - { "value": "Monthly", "label": __("Monthly") }, - { "value": "Quarterly", "label": __("Quarterly") }, - { "value": "Half-Yearly", "label": __("Half-Yearly") }, - { "value": "Yearly", "label": __("Yearly") } + fieldname: "period", + label: __("Period"), + fieldtype: "Select", + options: [ + { value: "Monthly", label: __("Monthly") }, + { value: "Quarterly", label: __("Quarterly") }, + { value: "Half-Yearly", label: __("Half-Yearly") }, + { value: "Yearly", label: __("Yearly") }, ], - "default": "Monthly" + default: "Monthly", }, { - "fieldname":"based_on", - "label": __("Based On"), - "fieldtype": "Select", - "options": [ - { "value": "Item", "label": __("Item") }, - { "value": "Item Group", "label": __("Item Group") }, - { "value": "Customer", "label": __("Customer") }, - { "value": "Customer Group", "label": __("Customer Group") }, - { "value": "Territory", "label": __("Territory") }, - { "value": "Project", "label": __("Project") } + fieldname: "based_on", + label: __("Based On"), + fieldtype: "Select", + options: [ + { value: "Item", label: __("Item") }, + { value: "Item Group", label: __("Item Group") }, + { value: "Customer", label: __("Customer") }, + { value: "Customer Group", label: __("Customer Group") }, + { value: "Territory", label: __("Territory") }, + { value: "Project", label: __("Project") }, ], - "default": "Item", - "dashboard_config": { - "read_only": 1, - } + default: "Item", + dashboard_config: { + read_only: 1, + }, }, { - "fieldname":"group_by", - "label": __("Group By"), - "fieldtype": "Select", - "options": [ - "", - { "value": "Item", "label": __("Item") }, - { "value": "Customer", "label": __("Customer") } - ], - "default": "" + fieldname: "group_by", + label: __("Group By"), + fieldtype: "Select", + options: ["", { value: "Item", label: __("Item") }, { value: "Customer", label: __("Customer") }], + default: "", }, { - "fieldname":"fiscal_year", - "label": __("Fiscal Year"), - "fieldtype": "Link", - "options":'Fiscal Year', - "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()) + fieldname: "fiscal_year", + label: __("Fiscal Year"), + fieldtype: "Link", + options: "Fiscal Year", + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), }, { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company") + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), }, ]; -} +}; diff --git a/erpnext/public/js/setup_wizard.js b/erpnext/public/js/setup_wizard.js index 934fd1f88ae..b146cd19a36 100644 --- a/erpnext/public/js/setup_wizard.js +++ b/erpnext/public/js/setup_wizard.js @@ -1,7 +1,7 @@ frappe.provide("erpnext.setup"); -frappe.pages['setup-wizard'].on_page_load = function(wrapper) { - if(frappe.sys_defaults.company) { +frappe.pages["setup-wizard"].on_page_load = function (wrapper) { + if (frappe.sys_defaults.company) { frappe.set_route("desk"); return; } @@ -14,32 +14,34 @@ frappe.setup.on("before_load", function () { erpnext.setup.slides_settings = [ { // Organization - name: 'organization', + name: "organization", title: __("Setup your organization"), icon: "fa fa-building", fields: [ { - fieldname: 'company_name', - label: __('Company Name'), - fieldtype: 'Data', - reqd: 1 + fieldname: "company_name", + label: __("Company Name"), + fieldtype: "Data", + reqd: 1, }, { fieldtype: "Column Break" }, { - fieldname: 'company_abbr', - label: __('Company Abbreviation'), - fieldtype: 'Data', - reqd: 1 + fieldname: "company_abbr", + label: __("Company Abbreviation"), + fieldtype: "Data", + reqd: 1, }, { fieldtype: "Section Break" }, { - fieldname: 'chart_of_accounts', label: __('Chart of Accounts'), - options: "", fieldtype: 'Select' + fieldname: "chart_of_accounts", + label: __("Chart of Accounts"), + options: "", + fieldtype: "Select", }, - { fieldname: 'view_coa', label: __('View Chart of Accounts'), fieldtype: 'Button' }, - { fieldname: 'fy_start_date', label: __('Financial Year Begins On'), fieldtype: 'Date', reqd: 1 }, + { fieldname: "view_coa", label: __("View Chart of Accounts"), fieldtype: "Button" }, + { fieldname: "fy_start_date", label: __("Financial Year Begins On"), fieldtype: "Date", reqd: 1 }, // end date should be hidden (auto calculated) - { fieldname: 'fy_end_date', label: __('End Date'), fieldtype: 'Date', reqd: 1, hidden: 1 }, + { fieldname: "fy_end_date", label: __("End Date"), fieldtype: "Date", reqd: 1, hidden: 1 }, ], onload: function (slide) { @@ -66,10 +68,10 @@ erpnext.setup.slides_settings = [ return true; }, - validate_fy_dates: function() { + validate_fy_dates: function () { // validate fiscal year start and end dates - const invalid = this.values.fy_start_date == 'Invalid date' || - this.values.fy_end_date == 'Invalid date'; + const invalid = + this.values.fy_start_date == "Invalid date" || this.values.fy_end_date == "Invalid date"; const start_greater_than_end = this.values.fy_start_date > this.values.fy_end_date; if (invalid || start_greater_than_end) { @@ -97,26 +99,24 @@ erpnext.setup.slides_settings = [ next_year = current_year; current_year -= 1; } - slide.get_field("fy_start_date").set_value(current_year + '-' + fy[0]); - slide.get_field("fy_end_date").set_value(next_year + '-' + fy[1]); + slide.get_field("fy_start_date").set_value(current_year + "-" + fy[0]); + slide.get_field("fy_end_date").set_value(next_year + "-" + fy[1]); } }, - load_chart_of_accounts: function (slide) { let country = frappe.wizard.values.country; if (country) { frappe.call({ method: "erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts.get_charts_for_country", - args: { "country": country, with_standard: true }, + args: { country: country, with_standard: true }, callback: function (r) { if (r.message) { - slide.get_input("chart_of_accounts").empty() - .add_options(r.message); + slide.get_input("chart_of_accounts").empty().add_options(r.message); } - } - }) + }, + }); } }, @@ -124,56 +124,74 @@ erpnext.setup.slides_settings = [ let me = this; slide.get_input("fy_start_date").on("change", function () { var start_date = slide.form.fields_dict.fy_start_date.get_value(); - var year_end_date = - frappe.datetime.add_days(frappe.datetime.add_months(start_date, 12), -1); + var year_end_date = frappe.datetime.add_days(frappe.datetime.add_months(start_date, 12), -1); slide.form.fields_dict.fy_end_date.set_value(year_end_date); }); - slide.get_input("view_coa").on("click", function() { + slide.get_input("view_coa").on("click", function () { let chart_template = slide.form.fields_dict.chart_of_accounts.get_value(); - if(!chart_template) return; + if (!chart_template) return; me.charts_modal(slide, chart_template); }); - slide.get_input("company_name").on("input", function () { - let parts = slide.get_input("company_name").val().split(" "); - let abbr = $.map(parts, function (p) { return p ? p.substr(0, 1) : null }).join(""); - slide.get_field("company_abbr").set_value(abbr.slice(0, 10).toUpperCase()); - }).val(frappe.boot.sysdefaults.company_name || "").trigger("change"); + slide + .get_input("company_name") + .on("input", function () { + let parts = slide.get_input("company_name").val().split(" "); + let abbr = $.map(parts, function (p) { + return p ? p.substr(0, 1) : null; + }).join(""); + slide.get_field("company_abbr").set_value(abbr.slice(0, 10).toUpperCase()); + }) + .val(frappe.boot.sysdefaults.company_name || "") + .trigger("change"); - slide.get_input("company_abbr").on("change", function () { - let abbr = slide.get_input("company_abbr").val(); - if (abbr.length > 10) { - frappe.msgprint(__("Company Abbreviation cannot have more than 5 characters")); - abbr = abbr.slice(0, 10); - } - slide.get_field("company_abbr").set_value(abbr); - }).val(frappe.boot.sysdefaults.company_abbr || "").trigger("change"); + slide + .get_input("company_abbr") + .on("change", function () { + let abbr = slide.get_input("company_abbr").val(); + if (abbr.length > 10) { + frappe.msgprint(__("Company Abbreviation cannot have more than 5 characters")); + abbr = abbr.slice(0, 10); + } + slide.get_field("company_abbr").set_value(abbr); + }) + .val(frappe.boot.sysdefaults.company_abbr || "") + .trigger("change"); }, - charts_modal: function(slide, chart_template) { - let parent = __('All Accounts'); + charts_modal: function (slide, chart_template) { + let parent = __("All Accounts"); let dialog = new frappe.ui.Dialog({ title: chart_template, fields: [ - {'fieldname': 'expand_all', 'label': __('Expand All'), 'fieldtype': 'Button', - click: function() { + { + fieldname: "expand_all", + label: __("Expand All"), + fieldtype: "Button", + click: function () { // expand all nodes on button click coa_tree.load_children(coa_tree.root_node, true); - } + }, }, - {'fieldname': 'collapse_all', 'label': __('Collapse All'), 'fieldtype': 'Button', - click: function() { + { + fieldname: "collapse_all", + label: __("Collapse All"), + fieldtype: "Button", + click: function () { // collapse all nodes - coa_tree.get_all_nodes(coa_tree.root_node.data.value, coa_tree.root_node.is_root) - .then(data_list => { - data_list.map(d => { coa_tree.toggle_node(coa_tree.nodes[d.parent]); }); + coa_tree + .get_all_nodes(coa_tree.root_node.data.value, coa_tree.root_node.is_root) + .then((data_list) => { + data_list.map((d) => { + coa_tree.toggle_node(coa_tree.nodes[d.parent]); + }); }); - } - } - ] + }, + }, + ], }); // render tree structure in the dialog modal @@ -181,49 +199,49 @@ erpnext.setup.slides_settings = [ parent: $(dialog.body), label: parent, expandable: true, - method: 'erpnext.accounts.utils.get_coa', + method: "erpnext.accounts.utils.get_coa", args: { chart: chart_template, parent: parent, - doctype: 'Account' + doctype: "Account", }, - onclick: function(node) { + onclick: function (node) { parent = node.value; - } + }, }); // add class to show buttons side by side - const form_container = $(dialog.body).find('form'); - const buttons = $(form_container).find('.frappe-control'); - form_container.addClass('flex'); + const form_container = $(dialog.body).find("form"); + const buttons = $(form_container).find(".frappe-control"); + form_container.addClass("flex"); buttons.map((index, button) => { - $(button).css({"margin-right": "1em"}); - }) + $(button).css({ "margin-right": "1em" }); + }); dialog.show(); coa_tree.load_children(coa_tree.root_node, true); // expand all node trigger - } - } + }, + }, ]; // Source: https://en.wikipedia.org/wiki/Fiscal_year // default 1st Jan - 31st Dec erpnext.setup.fiscal_years = { - "Afghanistan": ["12-21", "12-20"], - "Australia": ["07-01", "06-30"], - "Bangladesh": ["07-01", "06-30"], - "Canada": ["04-01", "03-31"], + Afghanistan: ["12-21", "12-20"], + Australia: ["07-01", "06-30"], + Bangladesh: ["07-01", "06-30"], + Canada: ["04-01", "03-31"], "Costa Rica": ["10-01", "09-30"], - "Egypt": ["07-01", "06-30"], + Egypt: ["07-01", "06-30"], "Hong Kong": ["04-01", "03-31"], - "India": ["04-01", "03-31"], - "Iran": ["06-23", "06-22"], - "Myanmar": ["04-01", "03-31"], + India: ["04-01", "03-31"], + Iran: ["06-23", "06-22"], + Myanmar: ["04-01", "03-31"], "New Zealand": ["04-01", "03-31"], - "Pakistan": ["07-01", "06-30"], - "Singapore": ["04-01", "03-31"], + Pakistan: ["07-01", "06-30"], + Singapore: ["04-01", "03-31"], "South Africa": ["03-01", "02-28"], - "Thailand": ["10-01", "09-30"], + Thailand: ["10-01", "09-30"], "United Kingdom": ["04-01", "03-31"], }; diff --git a/erpnext/public/js/shopping_cart.js b/erpnext/public/js/shopping_cart.js index d14740c1060..45ac9ad7ac4 100644 --- a/erpnext/public/js/shopping_cart.js +++ b/erpnext/public/js/shopping_cart.js @@ -7,45 +7,46 @@ var shopping_cart = erpnext.e_commerce.shopping_cart; var getParams = function (url) { var params = []; - var parser = document.createElement('a'); + var parser = document.createElement("a"); parser.href = url; var query = parser.search.substring(1); - var vars = query.split('&'); + var vars = query.split("&"); for (var i = 0; i < vars.length; i++) { - var pair = vars[i].split('='); + var pair = vars[i].split("="); params[pair[0]] = decodeURIComponent(pair[1]); } return params; }; -frappe.ready(function() { +frappe.ready(function () { var full_name = frappe.session && frappe.session.user_fullname; // update user - if(full_name) { - $('.navbar li[data-label="User"] a') - .html(' ' + full_name); + if (full_name) { + $('.navbar li[data-label="User"] a').html( + ' ' + full_name + ); } // set coupon code and sales partner code var url_args = getParams(window.location.href); - var referral_coupon_code = url_args['cc']; - var referral_sales_partner = url_args['sp']; + var referral_coupon_code = url_args["cc"]; + var referral_sales_partner = url_args["sp"]; var d = new Date(); // expires within 30 minutes - d.setTime(d.getTime() + (0.02 * 24 * 60 * 60 * 1000)); - var expires = "expires="+d.toUTCString(); + d.setTime(d.getTime() + 0.02 * 24 * 60 * 60 * 1000); + var expires = "expires=" + d.toUTCString(); if (referral_coupon_code) { document.cookie = "referral_coupon_code=" + referral_coupon_code + ";" + expires + ";path=/"; } if (referral_sales_partner) { document.cookie = "referral_sales_partner=" + referral_sales_partner + ";" + expires + ";path=/"; } - referral_coupon_code=frappe.get_cookie("referral_coupon_code"); - referral_sales_partner=frappe.get_cookie("referral_sales_partner"); + referral_coupon_code = frappe.get_cookie("referral_coupon_code"); + referral_sales_partner = frappe.get_cookie("referral_sales_partner"); - if (referral_coupon_code && $(".tot_quotation_discount").val()==undefined ) { + if (referral_coupon_code && $(".tot_quotation_discount").val() == undefined) { $(".txtcoupon").val(referral_coupon_code); } if (referral_sales_partner) { @@ -59,27 +60,27 @@ frappe.ready(function() { }); $.extend(shopping_cart, { - show_shoppingcart_dropdown: function() { - $(".shopping-cart").on('shown.bs.dropdown', function() { - if (!$('.shopping-cart-menu .cart-container').length) { + show_shoppingcart_dropdown: function () { + $(".shopping-cart").on("shown.bs.dropdown", function () { + if (!$(".shopping-cart-menu .cart-container").length) { return frappe.call({ - method: 'erpnext.e_commerce.shopping_cart.cart.get_shopping_cart_menu', - callback: function(r) { + method: "erpnext.e_commerce.shopping_cart.cart.get_shopping_cart_menu", + callback: function (r) { if (r.message) { - $('.shopping-cart-menu').html(r.message); + $(".shopping-cart-menu").html(r.message); } - } + }, }); } }); }, - update_cart: function(opts) { - if (frappe.session.user==="Guest") { + update_cart: function (opts) { + if (frappe.session.user === "Guest") { if (localStorage) { localStorage.setItem("last_visited", window.location.pathname); } - frappe.call('erpnext.e_commerce.api.get_guest_redirect_on_action').then((res) => { + frappe.call("erpnext.e_commerce.api.get_guest_redirect_on_action").then((res) => { window.location.href = res.message || "/login"; }); } else { @@ -91,35 +92,34 @@ $.extend(shopping_cart, { item_code: opts.item_code, qty: opts.qty, additional_notes: opts.additional_notes !== undefined ? opts.additional_notes : undefined, - with_items: opts.with_items || 0 + with_items: opts.with_items || 0, }, btn: opts.btn, - callback: function(r) { + callback: function (r) { shopping_cart.unfreeze(); shopping_cart.set_cart_count(true); - if(opts.callback) - opts.callback(r); - } + if (opts.callback) opts.callback(r); + }, }); } }, - set_cart_count: function(animate=false) { + set_cart_count: function (animate = false) { $(".intermediate-empty-cart").remove(); var cart_count = frappe.get_cookie("cart_count"); - if(frappe.session.user==="Guest") { + if (frappe.session.user === "Guest") { cart_count = 0; } - if(cart_count) { - $(".shopping-cart").toggleClass('hidden', false); + if (cart_count) { + $(".shopping-cart").toggleClass("hidden", false); } - var $cart = $('.cart-icon'); + var $cart = $(".cart-icon"); var $badge = $cart.find("#cart-count"); - if(parseInt(cart_count) === 0 || cart_count === undefined) { + if (parseInt(cart_count) === 0 || cart_count === undefined) { $cart.css("display", "none"); $(".cart-tax-items").hide(); $(".btn-place-order").hide(); @@ -127,17 +127,16 @@ $.extend(shopping_cart, { let intermediate_empty_cart_msg = `
              - ${ __("Cart is Empty") } + ${__("Cart is Empty")}
              `; $(".cart-table").after(intermediate_empty_cart_msg); - } - else { + } else { $cart.css("display", "inline"); $("#cart-count").text(cart_count); } - if(cart_count) { + if (cart_count) { $badge.html(cart_count); if (animate) { @@ -151,15 +150,15 @@ $.extend(shopping_cart, { } }, - shopping_cart_update: function({item_code, qty, cart_dropdown, additional_notes}) { + shopping_cart_update: function ({ item_code, qty, cart_dropdown, additional_notes }) { shopping_cart.update_cart({ item_code, qty, additional_notes, with_items: 1, btn: this, - callback: function(r) { - if(!r.exc) { + callback: function (r) { + if (!r.exc) { $(".cart-items").html(r.message.items); $(".cart-tax-items").html(r.message.total); $(".payment-summary").html(r.message.taxes_and_totals); @@ -176,9 +175,9 @@ $.extend(shopping_cart, { show_cart_navbar: function () { frappe.call({ method: "erpnext.e_commerce.doctype.e_commerce_settings.e_commerce_settings.is_cart_enabled", - callback: function(r) { - $(".shopping-cart").toggleClass('hidden', r.message ? false : true); - } + callback: function (r) { + $(".shopping-cart").toggleClass("hidden", r.message ? false : true); + }, }); }, @@ -188,43 +187,41 @@ $.extend(shopping_cart, { }, bind_add_to_cart_action() { - $('.page_content').on('click', '.btn-add-to-cart-list', (e) => { + $(".page_content").on("click", ".btn-add-to-cart-list", (e) => { const $btn = $(e.currentTarget); - $btn.prop('disabled', true); + $btn.prop("disabled", true); - if (frappe.session.user==="Guest") { + if (frappe.session.user === "Guest") { if (localStorage) { localStorage.setItem("last_visited", window.location.pathname); } - frappe.call('erpnext.e_commerce.api.get_guest_redirect_on_action').then((res) => { + frappe.call("erpnext.e_commerce.api.get_guest_redirect_on_action").then((res) => { window.location.href = res.message || "/login"; }); return; } - $btn.addClass('hidden'); - $btn.closest('.cart-action-container').addClass('d-flex'); - $btn.parent().find('.go-to-cart').removeClass('hidden'); - $btn.parent().find('.go-to-cart-grid').removeClass('hidden'); - $btn.parent().find('.cart-indicator').removeClass('hidden'); + $btn.addClass("hidden"); + $btn.closest(".cart-action-container").addClass("d-flex"); + $btn.parent().find(".go-to-cart").removeClass("hidden"); + $btn.parent().find(".go-to-cart-grid").removeClass("hidden"); + $btn.parent().find(".cart-indicator").removeClass("hidden"); - const item_code = $btn.data('item-code'); + const item_code = $btn.data("item-code"); erpnext.e_commerce.shopping_cart.update_cart({ item_code, - qty: 1 + qty: 1, }); - }); }, freeze() { if (window.location.pathname !== "/cart") return; - if (!$('#freeze').length) { - let freeze = $('') - .appendTo("body"); + if (!$("#freeze").length) { + let freeze = $('').appendTo("body"); - setTimeout(function() { + setTimeout(function () { freeze.addClass("show"); }, 1); } else { @@ -233,11 +230,11 @@ $.extend(shopping_cart, { }, unfreeze() { - if ($('#freeze').length) { - let freeze = $('#freeze').removeClass("show"); - setTimeout(function() { + if ($("#freeze").length) { + let freeze = $("#freeze").removeClass("show"); + setTimeout(function () { freeze.remove(); }, 1); } - } + }, }); diff --git a/erpnext/public/js/sms_manager.js b/erpnext/public/js/sms_manager.js index a058da23ac4..d3147bb4600 100644 --- a/erpnext/public/js/sms_manager.js +++ b/erpnext/public/js/sms_manager.js @@ -3,104 +3,114 @@ erpnext.SMSManager = function SMSManager(doc) { var me = this; - this.setup = function() { + this.setup = function () { var default_msg = { - 'Lead' : '', - 'Opportunity' : 'Your enquiry has been logged into the system. Ref No: ' + doc.name, - 'Quotation' : 'Quotation ' + doc.name + ' has been sent via email. Thanks!', - 'Sales Order' : 'Sales Order ' + doc.name + ' has been created against ' - + (doc.quotation_no ? ('Quote No:' + doc.quotation_no) : '') - + (doc.po_no ? (' for your PO: ' + doc.po_no) : ''), - 'Delivery Note' : 'Items has been delivered against delivery note: ' + doc.name - + (doc.po_no ? (' for your PO: ' + doc.po_no) : ''), - 'Sales Invoice': 'Invoice ' + doc.name + ' has been sent via email ' - + (doc.po_no ? (' for your PO: ' + doc.po_no) : ''), - 'Material Request' : 'Material Request ' + doc.name + ' has been raised in the system', - 'Purchase Order' : 'Purchase Order ' + doc.name + ' has been sent via email', - 'Purchase Receipt' : 'Items has been received against purchase receipt: ' + doc.name - } - - if (in_list(['Sales Order', 'Delivery Note', 'Sales Invoice'], doc.doctype)) - this.show(doc.contact_person, 'Customer', doc.customer, '', default_msg[doc.doctype]); - else if (doc.doctype === 'Quotation') - this.show(doc.contact_person, 'Customer', doc.party_name, '', default_msg[doc.doctype]); - else if (in_list(['Purchase Order', 'Purchase Receipt'], doc.doctype)) - this.show(doc.contact_person, 'Supplier', doc.supplier, '', default_msg[doc.doctype]); - else if (doc.doctype == 'Lead') - this.show('', '', '', doc.mobile_no, default_msg[doc.doctype]); - else if (doc.doctype == 'Opportunity') - this.show('', '', '', doc.contact_no, default_msg[doc.doctype]); - else if (doc.doctype == 'Material Request') - this.show('', '', '', '', default_msg[doc.doctype]); + Lead: "", + Opportunity: "Your enquiry has been logged into the system. Ref No: " + doc.name, + Quotation: "Quotation " + doc.name + " has been sent via email. Thanks!", + "Sales Order": + "Sales Order " + + doc.name + + " has been created against " + + (doc.quotation_no ? "Quote No:" + doc.quotation_no : "") + + (doc.po_no ? " for your PO: " + doc.po_no : ""), + "Delivery Note": + "Items has been delivered against delivery note: " + + doc.name + + (doc.po_no ? " for your PO: " + doc.po_no : ""), + "Sales Invoice": + "Invoice " + + doc.name + + " has been sent via email " + + (doc.po_no ? " for your PO: " + doc.po_no : ""), + "Material Request": "Material Request " + doc.name + " has been raised in the system", + "Purchase Order": "Purchase Order " + doc.name + " has been sent via email", + "Purchase Receipt": "Items has been received against purchase receipt: " + doc.name, + }; + if (in_list(["Sales Order", "Delivery Note", "Sales Invoice"], doc.doctype)) + this.show(doc.contact_person, "Customer", doc.customer, "", default_msg[doc.doctype]); + else if (doc.doctype === "Quotation") + this.show(doc.contact_person, "Customer", doc.party_name, "", default_msg[doc.doctype]); + else if (in_list(["Purchase Order", "Purchase Receipt"], doc.doctype)) + this.show(doc.contact_person, "Supplier", doc.supplier, "", default_msg[doc.doctype]); + else if (doc.doctype == "Lead") this.show("", "", "", doc.mobile_no, default_msg[doc.doctype]); + else if (doc.doctype == "Opportunity") + this.show("", "", "", doc.contact_no, default_msg[doc.doctype]); + else if (doc.doctype == "Material Request") this.show("", "", "", "", default_msg[doc.doctype]); }; - this.get_contact_number = function(contact, ref_doctype, ref_name) { + this.get_contact_number = function (contact, ref_doctype, ref_name) { frappe.call({ method: "frappe.core.doctype.sms_settings.sms_settings.get_contact_number", args: { contact_name: contact, ref_doctype: ref_doctype, - ref_name: ref_name + ref_name: ref_name, }, - callback: function(r) { - if(r.exc) { frappe.msgprint(r.exc); return; } + callback: function (r) { + if (r.exc) { + frappe.msgprint(r.exc); + return; + } me.number = r.message; me.show_dialog(); - } + }, }); }; - this.show = function(contact, ref_doctype, ref_name, mobile_nos, message) { + this.show = function (contact, ref_doctype, ref_name, mobile_nos, message) { this.message = message; if (mobile_nos) { me.number = mobile_nos; me.show_dialog(); - } else if (contact){ - this.get_contact_number(contact, ref_doctype, ref_name) + } else if (contact) { + this.get_contact_number(contact, ref_doctype, ref_name); } else { me.show_dialog(); } - } - this.show_dialog = function() { - if(!me.dialog) - me.make_dialog(); + }; + this.show_dialog = function () { + if (!me.dialog) me.make_dialog(); me.dialog.set_values({ - 'message': me.message, - 'number': me.number - }) + message: me.message, + number: me.number, + }); me.dialog.show(); - } - this.make_dialog = function() { + }; + this.make_dialog = function () { var d = new frappe.ui.Dialog({ - title: 'Send SMS', + title: "Send SMS", width: 400, fields: [ - {fieldname:'number', fieldtype:'Data', label:'Mobile Number', reqd:1}, - {fieldname:'message', fieldtype:'Text', label:'Message', reqd:1}, - {fieldname:'send', fieldtype:'Button', label:'Send'} - ] - }) - d.fields_dict.send.input.onclick = function() { + { fieldname: "number", fieldtype: "Data", label: "Mobile Number", reqd: 1 }, + { fieldname: "message", fieldtype: "Text", label: "Message", reqd: 1 }, + { fieldname: "send", fieldtype: "Button", label: "Send" }, + ], + }); + d.fields_dict.send.input.onclick = function () { var btn = d.fields_dict.send.input; var v = me.dialog.get_values(); - if(v) { + if (v) { $(btn).set_working(); frappe.call({ method: "frappe.core.doctype.sms_settings.sms_settings.send_sms", args: { receiver_list: [v.number], - msg: v.message + msg: v.message, }, - callback: function(r) { + callback: function (r) { $(btn).done_working(); - if(r.exc) {frappe.msgprint(r.exc); return; } + if (r.exc) { + frappe.msgprint(r.exc); + return; + } me.dialog.hide(); - } + }, }); } - } + }; this.dialog = d; - } + }; this.setup(); -} +}; diff --git a/erpnext/public/js/stock_analytics.js b/erpnext/public/js/stock_analytics.js index a343c3402af..1c75d161e64 100644 --- a/erpnext/public/js/stock_analytics.js +++ b/erpnext/public/js/stock_analytics.js @@ -1,68 +1,104 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt - erpnext.StockAnalytics = class StockAnalytics extends erpnext.StockGridReport { constructor(wrapper, opts) { var args = { title: __("Stock Analytics"), - parent: $(wrapper).find('.layout-main'), + parent: $(wrapper).find(".layout-main"), page: wrapper.page, - doctypes: ["Item", "Item Group", "Warehouse", "Stock Ledger Entry", "Brand", - "Fiscal Year", "Serial No"], + doctypes: [ + "Item", + "Item Group", + "Warehouse", + "Stock Ledger Entry", + "Brand", + "Fiscal Year", + "Serial No", + ], tree_grid: { show: true, parent_field: "parent_item_group", - formatter: function(item) { - if(!item.is_group) { - return repl("\ - %(value)s", { + %(value)s", + { value: item.name, - }); + } + ); } else { return item.name; } - - } + }, }, - } + }; - if(opts) $.extend(args, opts); + if (opts) $.extend(args, opts); super(args); this.filters = [ - {fieldtype:"Select", label: __("Value or Qty"), fieldname: "value_or_qty", - options:[{label:__("Value"), value:"Value"}, {label:__("Quantity"), value:"Quantity"}], - filter: function(val, item, opts, me) { + { + fieldtype: "Select", + label: __("Value or Qty"), + fieldname: "value_or_qty", + options: [ + { label: __("Value"), value: "Value" }, + { label: __("Quantity"), value: "Quantity" }, + ], + filter: function (val, item, opts, me) { return me.apply_zero_filter(val, item, opts, me); - }}, - {fieldtype:"Select", label: __("Brand"), link:"Brand", fieldname: "brand", - default_value: __("Select Brand..."), filter: function(val, item, opts) { + }, + }, + { + fieldtype: "Select", + label: __("Brand"), + link: "Brand", + fieldname: "brand", + default_value: __("Select Brand..."), + filter: function (val, item, opts) { return val == opts.default_value || item.brand == val || item._show; - }, link_formatter: {filter_input: "brand"}}, - {fieldtype:"Select", label: __("Warehouse"), link:"Warehouse", fieldname: "warehouse", - default_value: __("Select Warehouse...")}, - {fieldtype:"Date", label: __("From Date"), fieldname: "from_date"}, - {fieldtype:"Date", label: __("To Date"), fieldname: "to_date"}, - {fieldtype:"Select", label: __("Range"), fieldname: "range", - options:[ - {label:__("Daily"), value:"Daily"}, - {label:__("Weekly"), value:"Weekly"}, - {label:__("Monthly"), value:"Monthly"}, - {label:__("Quarterly"), value:"Quarterly"}, - {label:__("Yearly"), value:"Yearly"}, - ]} + }, + link_formatter: { filter_input: "brand" }, + }, + { + fieldtype: "Select", + label: __("Warehouse"), + link: "Warehouse", + fieldname: "warehouse", + default_value: __("Select Warehouse..."), + }, + { fieldtype: "Date", label: __("From Date"), fieldname: "from_date" }, + { fieldtype: "Date", label: __("To Date"), fieldname: "to_date" }, + { + fieldtype: "Select", + label: __("Range"), + fieldname: "range", + options: [ + { label: __("Daily"), value: "Daily" }, + { label: __("Weekly"), value: "Weekly" }, + { label: __("Monthly"), value: "Monthly" }, + { label: __("Quarterly"), value: "Quarterly" }, + { label: __("Yearly"), value: "Yearly" }, + ], + }, ]; } setup_columns() { var std_columns = [ - {id: "name", name: __("Item"), field: "name", width: 300}, - {id: "brand", name: __("Brand"), field: "brand", width: 100}, - {id: "stock_uom", name: __("UOM"), field: "stock_uom", width: 100}, - {id: "opening", name: __("Opening"), field: "opening", hidden: true, - formatter: this.currency_formatter} + { id: "name", name: __("Item"), field: "name", width: 300 }, + { id: "brand", name: __("Brand"), field: "brand", width: 100 }, + { id: "stock_uom", name: __("UOM"), field: "stock_uom", width: 100 }, + { + id: "opening", + name: __("Opening"), + field: "opening", + hidden: true, + formatter: this.currency_formatter, + }, ]; this.make_date_range_columns(); @@ -79,24 +115,24 @@ erpnext.StockAnalytics = class StockAnalytics extends erpnext.StockGridReport { } init_filter_values() { super.init_filter_values(); - this.filter_inputs.range && this.filter_inputs.range.val('Monthly'); + this.filter_inputs.range && this.filter_inputs.range.val("Monthly"); } prepare_data() { var me = this; - if(!this.data) { + if (!this.data) { var items = this.prepare_tree("Item", "Item Group"); me.parent_map = {}; me.item_by_name = {}; me.data = []; - $.each(items, function(i, v) { + $.each(items, function (i, v) { var d = copy_dict(v); me.data.push(d); me.item_by_name[d.name] = d; - if(d.parent_item_group) { + if (d.parent_item_group) { me.parent_map[d.name] = d.parent_item_group; } me.reset_item_values(d); @@ -105,7 +141,7 @@ erpnext.StockAnalytics = class StockAnalytics extends erpnext.StockGridReport { this.data[0].checked = true; } else { // otherwise, only reset values - $.each(this.data, function(i, d) { + $.each(this.data, function (i, d) { me.reset_item_values(d); d["closing_qty_value"] = 0; }); @@ -113,7 +149,6 @@ erpnext.StockAnalytics = class StockAnalytics extends erpnext.StockGridReport { this.prepare_balances(); this.update_groups(); - } prepare_balances() { var me = this; @@ -124,23 +159,24 @@ erpnext.StockAnalytics = class StockAnalytics extends erpnext.StockGridReport { this.item_warehouse = {}; this.serialized_buying_rates = this.get_serialized_buying_rates(); - for(var i=0, j=data.length; i 0) { + if (sl.qty > 0) { // incoming - rate is given var rate = sl.incoming_rate; var add_qty = sl.qty; - if(wh.balance_qty < 0) { + if (wh.balance_qty < 0) { // negative valuation // only add value of quantity if // the balance goes above 0 add_qty = wh.balance_qty + sl.qty; - if(add_qty < 0) { + if (add_qty < 0) { add_qty = 0; } } - if(sl.serial_no) { + if (sl.serial_no) { var value_diff = this.get_serialized_value_diff(sl); } else { - var value_diff = (rate * add_qty); + var value_diff = rate * add_qty; } - if(add_qty) - wh.fifo_stack.push([add_qty, sl.incoming_rate, sl.posting_date]); + if (add_qty) wh.fifo_stack.push([add_qty, sl.incoming_rate, sl.posting_date]); } else { // called everytime for maintaining fifo stack var fifo_value_diff = this.get_fifo_value_diff(wh, sl); // outgoing - if(sl.serial_no) { + if (sl.serial_no) { var value_diff = -1 * this.get_serialized_value_diff(sl); - } else if(is_fifo) { + } else if (is_fifo) { var value_diff = fifo_value_diff; } else { // average rate for weighted average - var rate = (wh.balance_qty.toFixed(2) == 0.00 ? 0 : - flt(wh.balance_value) / flt(wh.balance_qty)); + var rate = wh.balance_qty.toFixed(2) == 0.0 ? 0 : flt(wh.balance_value) / flt(wh.balance_qty); // no change in value if negative qty - if((wh.balance_qty + sl.qty).toFixed(2) >= 0.00) - var value_diff = (rate * sl.qty); - else - var value_diff = -wh.balance_value; + if ((wh.balance_qty + sl.qty).toFixed(2) >= 0.0) var value_diff = rate * sl.qty; + else var value_diff = -wh.balance_value; } } @@ -66,14 +65,14 @@ erpnext.StockGridReport = class StockGridReport extends frappe.views.TreeGridRep var fifo_value_diff = 0.0; var qty = -sl.qty; - for(var i=0, j=fifo_stack.length; i= qty) { + if (batch[0] >= qty) { batch[0] = batch[0] - qty; - fifo_value_diff += (qty * batch[1]); + fifo_value_diff += qty * batch[1]; qty = 0.0; - if(batch[0]) { + if (batch[0]) { // batch still has qty put it back fifo_stack.push(batch); } @@ -82,7 +81,7 @@ erpnext.StockGridReport = class StockGridReport extends frappe.views.TreeGridRep break; } else { // consume this batch fully - fifo_value_diff += (batch[0] * batch[1]); + fifo_value_diff += batch[0] * batch[1]; qty = qty - batch[0]; } } @@ -96,8 +95,8 @@ erpnext.StockGridReport = class StockGridReport extends frappe.views.TreeGridRep var value_diff = 0.0; - $.each(sl.serial_no.trim().split("\n"), function(i, sr) { - if(sr) { + $.each(sl.serial_no.trim().split("\n"), function (i, sr) { + if (sr) { value_diff += flt(me.serialized_buying_rates[sr.trim().toLowerCase()]); } }); @@ -109,7 +108,7 @@ erpnext.StockGridReport = class StockGridReport extends frappe.views.TreeGridRep var serialized_buying_rates = {}; if (frappe.report_dump.data["Serial No"]) { - $.each(frappe.report_dump.data["Serial No"], function(i, sn) { + $.each(frappe.report_dump.data["Serial No"], function (i, sn) { serialized_buying_rates[sn.name.toLowerCase()] = flt(sn.incoming_rate); }); } diff --git a/erpnext/public/js/telephony.js b/erpnext/public/js/telephony.js index 1c3e3147976..7caf87d3f67 100644 --- a/erpnext/public/js/telephony.js +++ b/erpnext/public/js/telephony.js @@ -1,13 +1,17 @@ -frappe.ui.form.ControlData = class ControlData extends frappe.ui.form.ControlData { +frappe.ui.form.ControlData = class ControlData extends frappe.ui.form.ControlData { make_input() { super.make_input(); - if (this.df.options == 'Phone') { + if (this.df.options == "Phone") { this.setup_phone(); } if (this.frm && this.frm.fields_dict) { - Object.values(this.frm.fields_dict).forEach(function(field) { - if (field.df.read_only === 1 && field.df.options === 'Phone' - && field.disp_area.style[0] != 'display' && !field.has_icon) { + Object.values(this.frm.fields_dict).forEach(function (field) { + if ( + field.df.read_only === 1 && + field.df.options === "Phone" && + field.disp_area.style[0] != "display" && + !field.has_icon + ) { field.setup_phone(); field.has_icon = true; } @@ -16,15 +20,18 @@ frappe.ui.form.ControlData = class ControlData extends frappe.ui.form.ControlDat } setup_phone() { if (frappe.phone_call.handler) { - let control = this.df.read_only ? '.control-value' : '.control-input'; - this.$wrapper.find(control) - .append(` + let control = this.df.read_only ? ".control-value" : ".control-input"; + this.$wrapper + .find(control) + .append( + ` - - ${frappe.utils.icon('call')} + + ${frappe.utils.icon("call")} - `) - .find('.phone-btn') + ` + ) + .find(".phone-btn") .click(() => { frappe.phone_call.handler(this.get_value(), this.frm); }); diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index 27c7444daf4..731ebeae230 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -4,44 +4,42 @@ frappe.provide("erpnext"); frappe.provide("erpnext.utils"); $.extend(erpnext, { - get_currency: function(company) { - if(!company && cur_frm) - company = cur_frm.doc.company; - if(company) + get_currency: function (company) { + if (!company && cur_frm) company = cur_frm.doc.company; + if (company) return frappe.get_doc(":Company", company).default_currency || frappe.boot.sysdefaults.currency; - else - return frappe.boot.sysdefaults.currency; + else return frappe.boot.sysdefaults.currency; }, get_presentation_currency_list: () => { const docs = frappe.boot.docs; - let currency_list = docs.filter(d => d.doctype === ":Currency").map(d => d.name); + let currency_list = docs.filter((d) => d.doctype === ":Currency").map((d) => d.name); currency_list.unshift(""); return currency_list; }, - toggle_naming_series: function() { - if(cur_frm && cur_frm.fields_dict.naming_series) { - cur_frm.toggle_display("naming_series", cur_frm.doc.__islocal?true:false); + toggle_naming_series: function () { + if (cur_frm && cur_frm.fields_dict.naming_series) { + cur_frm.toggle_display("naming_series", cur_frm.doc.__islocal ? true : false); } }, - hide_company: function() { - if(cur_frm.fields_dict.company) { + hide_company: function () { + if (cur_frm.fields_dict.company) { var companies = Object.keys(locals[":Company"] || {}); - if(companies.length === 1) { - if(!cur_frm.doc.company) cur_frm.set_value("company", companies[0]); + if (companies.length === 1) { + if (!cur_frm.doc.company) cur_frm.set_value("company", companies[0]); cur_frm.toggle_display("company", false); - } else if(erpnext.last_selected_company) { - if(!cur_frm.doc.company) cur_frm.set_value("company", erpnext.last_selected_company); + } else if (erpnext.last_selected_company) { + if (!cur_frm.doc.company) cur_frm.set_value("company", erpnext.last_selected_company); } } }, - is_perpetual_inventory_enabled: function(company) { - if(company) { + is_perpetual_inventory_enabled: function (company) { + if (company) { let company_local = locals[":Company"] && locals[":Company"][company]; - if(company_local) { + if (company_local) { return cint(company_local.enable_perpetual_inventory); } } @@ -51,280 +49,321 @@ $.extend(erpnext, { return cint(frappe.boot.sysdefaults.allow_stale); }, - setup_serial_or_batch_no: function() { + setup_serial_or_batch_no: function () { let grid_row = cur_frm.open_grid_row(); - if (!grid_row || !grid_row.grid_form.fields_dict.serial_no || - grid_row.grid_form.fields_dict.serial_no.get_status() !== "Write") return; + if ( + !grid_row || + !grid_row.grid_form.fields_dict.serial_no || + grid_row.grid_form.fields_dict.serial_no.get_status() !== "Write" + ) + return; - frappe.model.get_value('Item', {'name': grid_row.doc.item_code}, - ['has_serial_no', 'has_batch_no'], ({has_serial_no, has_batch_no}) => { - Object.assign(grid_row.doc, {has_serial_no, has_batch_no}); + frappe.model.get_value( + "Item", + { name: grid_row.doc.item_code }, + ["has_serial_no", "has_batch_no"], + ({ has_serial_no, has_batch_no }) => { + Object.assign(grid_row.doc, { has_serial_no, has_batch_no }); if (has_serial_no) { - attach_selector_button(__("Add Serial No"), - grid_row.grid_form.fields_dict.serial_no.$wrapper, this, grid_row); + attach_selector_button( + __("Add Serial No"), + grid_row.grid_form.fields_dict.serial_no.$wrapper, + this, + grid_row + ); } else if (has_batch_no) { - attach_selector_button(__("Pick Batch No"), - grid_row.grid_form.fields_dict.batch_no.$wrapper, this, grid_row); + attach_selector_button( + __("Pick Batch No"), + grid_row.grid_form.fields_dict.batch_no.$wrapper, + this, + grid_row + ); } } ); }, route_to_adjustment_jv: (args) => { - frappe.model.with_doctype('Journal Entry', () => { + frappe.model.with_doctype("Journal Entry", () => { // route to adjustment Journal Entry to handle Account Balance and Stock Value mismatch - let journal_entry = frappe.model.get_new_doc('Journal Entry'); + let journal_entry = frappe.model.get_new_doc("Journal Entry"); args.accounts.forEach((je_account) => { let child_row = frappe.model.add_child(journal_entry, "accounts"); child_row.account = je_account.account; child_row.debit_in_account_currency = je_account.debit_in_account_currency; child_row.credit_in_account_currency = je_account.credit_in_account_currency; - child_row.party_type = "" ; + child_row.party_type = ""; }); - frappe.set_route('Form','Journal Entry', journal_entry.name); + frappe.set_route("Form", "Journal Entry", journal_entry.name); }); }, route_to_pending_reposts: (args) => { - frappe.set_route('List', 'Repost Item Valuation', args); + frappe.set_route("List", "Repost Item Valuation", args); }, }); - $.extend(erpnext.utils, { - set_party_dashboard_indicators: function(frm) { - if(frm.doc.__onload && frm.doc.__onload.dashboard_info) { + set_party_dashboard_indicators: function (frm) { + if (frm.doc.__onload && frm.doc.__onload.dashboard_info) { var company_wise_info = frm.doc.__onload.dashboard_info; - if(company_wise_info.length > 1) { - company_wise_info.forEach(function(info) { + if (company_wise_info.length > 1) { + company_wise_info.forEach(function (info) { erpnext.utils.add_indicator_for_multicompany(frm, info); }); } else if (company_wise_info.length === 1) { - frm.dashboard.add_indicator(__('Annual Billing: {0}', - [format_currency(company_wise_info[0].billing_this_year, company_wise_info[0].currency)]), 'blue'); - frm.dashboard.add_indicator(__('Total Unpaid: {0}', - [format_currency(company_wise_info[0].total_unpaid, company_wise_info[0].currency)]), - company_wise_info[0].total_unpaid ? 'orange' : 'green'); + frm.dashboard.add_indicator( + __("Annual Billing: {0}", [ + format_currency( + company_wise_info[0].billing_this_year, + company_wise_info[0].currency + ), + ]), + "blue" + ); + frm.dashboard.add_indicator( + __("Total Unpaid: {0}", [ + format_currency(company_wise_info[0].total_unpaid, company_wise_info[0].currency), + ]), + company_wise_info[0].total_unpaid ? "orange" : "green" + ); - if(company_wise_info[0].loyalty_points) { - frm.dashboard.add_indicator(__('Loyalty Points: {0}', - [company_wise_info[0].loyalty_points]), 'blue'); + if (company_wise_info[0].loyalty_points) { + frm.dashboard.add_indicator( + __("Loyalty Points: {0}", [company_wise_info[0].loyalty_points]), + "blue" + ); } } } }, - add_indicator_for_multicompany: function(frm, info) { + add_indicator_for_multicompany: function (frm, info) { frm.dashboard.stats_area.show(); - frm.dashboard.stats_area_row.addClass('flex'); - frm.dashboard.stats_area_row.css('flex-wrap', 'wrap'); + frm.dashboard.stats_area_row.addClass("flex"); + frm.dashboard.stats_area_row.css("flex-wrap", "wrap"); - var color = info.total_unpaid ? 'orange' : 'green'; + var color = info.total_unpaid ? "orange" : "green"; - var indicator = $('
              '+ - '
              '+info.company+'
              '+ + var indicator = $( + '
              ' + + '
              ' + + info.company + + "
              " + + '" + + '" + + "
              " + ).appendTo(frm.dashboard.stats_area_row); - ''+ - - ''+ - - - '
              ').appendTo(frm.dashboard.stats_area_row); - - if(info.loyalty_points){ - $('').appendTo(indicator); + if (info.loyalty_points) { + $( + '" + ).appendTo(indicator); } return indicator; }, - get_party_name: function(party_type) { - var dict = {'Customer': 'customer_name', 'Supplier': 'supplier_name', 'Employee': 'employee_name', - 'Member': 'member_name'}; + get_party_name: function (party_type) { + var dict = { + Customer: "customer_name", + Supplier: "supplier_name", + Employee: "employee_name", + Member: "member_name", + }; return dict[party_type]; }, - copy_value_in_all_rows: function(doc, dt, dn, table_fieldname, fieldname) { + copy_value_in_all_rows: function (doc, dt, dn, table_fieldname, fieldname) { var d = locals[dt][dn]; - if(d[fieldname]){ + if (d[fieldname]) { var cl = doc[table_fieldname] || []; - for(var i = 0; i < cl.length; i++) { - if(!cl[i][fieldname]) cl[i][fieldname] = d[fieldname]; + for (var i = 0; i < cl.length; i++) { + if (!cl[i][fieldname]) cl[i][fieldname] = d[fieldname]; } } refresh_field(table_fieldname); }, - get_terms: function(tc_name, doc, callback) { - if(tc_name) { + get_terms: function (tc_name, doc, callback) { + if (tc_name) { return frappe.call({ - method: 'erpnext.setup.doctype.terms_and_conditions.terms_and_conditions.get_terms_and_conditions', + method: "erpnext.setup.doctype.terms_and_conditions.terms_and_conditions.get_terms_and_conditions", args: { template_name: tc_name, - doc: doc + doc: doc, + }, + callback: function (r) { + callback(r); }, - callback: function(r) { - callback(r) - } }); } }, - make_bank_account: function(doctype, docname) { + make_bank_account: function (doctype, docname) { frappe.call({ method: "erpnext.accounts.doctype.bank_account.bank_account.make_bank_account", args: { doctype: doctype, - docname: docname + docname: docname, }, freeze: true, - callback: function(r) { + callback: function (r) { var doclist = frappe.model.sync(r.message); frappe.set_route("Form", doclist[0].doctype, doclist[0].name); - } - }) + }, + }); }, - add_dimensions: function(report_name, index) { + add_dimensions: function (report_name, index) { let filters = frappe.query_reports[report_name].filters; frappe.call({ method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.get_dimensions", - callback: function(r) { + callback: function (r) { let accounting_dimensions = r.message[0]; accounting_dimensions.forEach((dimension) => { - let found = filters.some(el => el.fieldname === dimension['fieldname']); + let found = filters.some((el) => el.fieldname === dimension["fieldname"]); if (!found) { filters.splice(index, 0, { - "fieldname": dimension["fieldname"], - "label": __(dimension["label"]), - "fieldtype": "MultiSelectList", - get_data: function(txt) { + fieldname: dimension["fieldname"], + label: __(dimension["label"]), + fieldtype: "MultiSelectList", + get_data: function (txt) { return frappe.db.get_link_options(dimension["document_type"], txt); }, }); } }); - } + }, }); }, - add_inventory_dimensions: function(report_name, index) { + add_inventory_dimensions: function (report_name, index) { let filters = frappe.query_reports[report_name].filters; frappe.call({ method: "erpnext.stock.doctype.inventory_dimension.inventory_dimension.get_inventory_dimensions", - callback: function(r) { + callback: function (r) { if (r.message && r.message.length) { r.message.forEach((dimension) => { - let existing_filter = filters.filter(el => el.fieldname === dimension['fieldname']); + let existing_filter = filters.filter((el) => el.fieldname === dimension["fieldname"]); if (!existing_filter.length) { filters.splice(index, 0, { - "fieldname": dimension["fieldname"], - "label": __(dimension["doctype"]), - "fieldtype": "MultiSelectList", - get_data: function(txt) { + fieldname: dimension["fieldname"], + label: __(dimension["doctype"]), + fieldtype: "MultiSelectList", + get_data: function (txt) { return frappe.db.get_link_options(dimension["doctype"], txt); }, }); } else { - existing_filter[0]['fieldtype'] = "MultiSelectList"; - existing_filter[0]['get_data'] = function(txt) { + existing_filter[0]["fieldtype"] = "MultiSelectList"; + existing_filter[0]["get_data"] = function (txt) { return frappe.db.get_link_options(dimension["doctype"], txt); - } + }; } }); } - } + }, }); }, - make_subscription: function(doctype, docname) { + make_subscription: function (doctype, docname) { frappe.call({ method: "frappe.automation.doctype.auto_repeat.auto_repeat.make_auto_repeat", args: { doctype: doctype, - docname: docname + docname: docname, }, - callback: function(r) { + callback: function (r) { var doclist = frappe.model.sync(r.message); frappe.set_route("Form", doclist[0].doctype, doclist[0].name); - } - }) + }, + }); }, - make_pricing_rule: function(doctype, docname) { + make_pricing_rule: function (doctype, docname) { frappe.call({ method: "erpnext.accounts.doctype.pricing_rule.pricing_rule.make_pricing_rule", args: { doctype: doctype, - docname: docname + docname: docname, }, - callback: function(r) { + callback: function (r) { var doclist = frappe.model.sync(r.message); frappe.set_route("Form", doclist[0].doctype, doclist[0].name); - } - }) + }, + }); }, /** - * Checks if the first row of a given child table is empty - * @param child_table - Child table Doctype - * @return {Boolean} - **/ - first_row_is_empty: function(child_table){ - if($.isArray(child_table) && child_table.length > 0) { + * Checks if the first row of a given child table is empty + * @param child_table - Child table Doctype + * @return {Boolean} + **/ + first_row_is_empty: function (child_table) { + if ($.isArray(child_table) && child_table.length > 0) { return !child_table[0].item_code; } return false; }, /** - * Removes the first row of a child table if it is empty - * @param {_Frm} frm - The current form - * @param {String} child_table_name - The child table field name - * @return {Boolean} - **/ - remove_empty_first_row: function(frm, child_table_name){ - const rows = frm['doc'][child_table_name]; - if (this.first_row_is_empty(rows)){ - frm['doc'][child_table_name] = rows.splice(1); + * Removes the first row of a child table if it is empty + * @param {_Frm} frm - The current form + * @param {String} child_table_name - The child table field name + * @return {Boolean} + **/ + remove_empty_first_row: function (frm, child_table_name) { + const rows = frm["doc"][child_table_name]; + if (this.first_row_is_empty(rows)) { + frm["doc"][child_table_name] = rows.splice(1); } return rows; }, - get_tree_options: function(option) { + get_tree_options: function (option) { // get valid options for tree based on user permission & locals dict let unscrub_option = frappe.model.unscrub(option); let user_permission = frappe.defaults.get_user_permissions(); let options; - if(user_permission && user_permission[unscrub_option]) { - options = user_permission[unscrub_option].map(perm => perm.doc); + if (user_permission && user_permission[unscrub_option]) { + options = user_permission[unscrub_option].map((perm) => perm.doc); } else { - options = $.map(locals[`:${unscrub_option}`], function(c) { return c.name; }).sort(); + options = $.map(locals[`:${unscrub_option}`], function (c) { + return c.name; + }).sort(); } // filter unique values, as there may be multiple user permissions for any value return options.filter((value, index, self) => self.indexOf(value) === index); }, - get_tree_default: function(option) { + get_tree_default: function (option) { // set default for a field based on user permission let options = this.get_tree_options(option); - if(options.includes(frappe.defaults.get_default(option))) { + if (options.includes(frappe.defaults.get_default(option))) { return frappe.defaults.get_default(option); } else { return options[0]; } }, - overrides_parent_value_in_all_rows: function(doc, dt, dn, table_fieldname, fieldname, parent_fieldname) { + overrides_parent_value_in_all_rows: function (doc, dt, dn, table_fieldname, fieldname, parent_fieldname) { if (doc[parent_fieldname]) { let cl = doc[table_fieldname] || []; for (let i = 0; i < cl.length; i++) { @@ -334,7 +373,7 @@ $.extend(erpnext.utils, { } }, create_new_doc: function (doctype, update_fields) { - frappe.model.with_doctype(doctype, function() { + frappe.model.with_doctype(doctype, function () { var new_doc = frappe.model.get_new_doc(doctype); for (let [key, value] of Object.entries(update_fields)) { new_doc[key] = value; @@ -346,147 +385,161 @@ $.extend(erpnext.utils, { // check if payments app is installed on site, if not warn user. check_payments_app: () => { if (frappe.boot.versions && !frappe.boot.versions.payments) { - const marketplace_link = '
              Marketplace' - const github_link = 'GitHub' - const msg = __("payments app is not installed. Please install it from {0} or {1}", [marketplace_link, github_link]) + const marketplace_link = + 'Marketplace'; + const github_link = 'GitHub'; + const msg = __("payments app is not installed. Please install it from {0} or {1}", [ + marketplace_link, + github_link, + ]); frappe.msgprint(msg); } - }, - get_fiscal_year: function(date) { - if(!date) { + get_fiscal_year: function (date) { + if (!date) { date = frappe.datetime.get_today(); } - let fiscal_year = ''; + let fiscal_year = ""; frappe.call({ method: "erpnext.accounts.utils.get_fiscal_year", args: { - date: date + date: date, }, async: false, - callback: function(r) { + callback: function (r) { if (r.message) { fiscal_year = r.message[0]; } - } + }, }); return fiscal_year; - } + }, }); -erpnext.utils.select_alternate_items = function(opts) { +erpnext.utils.select_alternate_items = function (opts) { const frm = opts.frm; - const warehouse_field = opts.warehouse_field || 'warehouse'; - const item_field = opts.item_field || 'item_code'; + const warehouse_field = opts.warehouse_field || "warehouse"; + const item_field = opts.item_field || "item_code"; this.data = []; const dialog = new frappe.ui.Dialog({ title: __("Select Alternate Item"), fields: [ - {fieldtype:'Section Break', label: __('Items')}, + { fieldtype: "Section Break", label: __("Items") }, { - fieldname: "alternative_items", fieldtype: "Table", cannot_add_rows: true, - in_place_edit: true, data: this.data, + fieldname: "alternative_items", + fieldtype: "Table", + cannot_add_rows: true, + in_place_edit: true, + data: this.data, get_data: () => { return this.data; }, - fields: [{ - fieldtype:'Data', - fieldname:"docname", - hidden: 1 - }, { - fieldtype:'Link', - fieldname:"item_code", - options: 'Item', - in_list_view: 1, - read_only: 1, - label: __('Item Code') - }, { - fieldtype:'Link', - fieldname:"alternate_item", - options: 'Item', - default: "", - in_list_view: 1, - label: __('Alternate Item'), - onchange: function() { - const item_code = this.get_value(); - const warehouse = this.grid_row.on_grid_fields_dict.warehouse.get_value(); - if (item_code && warehouse) { - frappe.call({ - method: "erpnext.stock.utils.get_latest_stock_qty", - args: { - item_code: item_code, - warehouse: warehouse - }, - callback: (r) => { - this.grid_row.on_grid_fields_dict - .actual_qty.set_value(r.message || 0); - } - }) - } + fields: [ + { + fieldtype: "Data", + fieldname: "docname", + hidden: 1, }, - get_query: (e) => { - return { - query: "erpnext.stock.doctype.item_alternative.item_alternative.get_alternative_items", - filters: { - item_code: e.item_code + { + fieldtype: "Link", + fieldname: "item_code", + options: "Item", + in_list_view: 1, + read_only: 1, + label: __("Item Code"), + }, + { + fieldtype: "Link", + fieldname: "alternate_item", + options: "Item", + default: "", + in_list_view: 1, + label: __("Alternate Item"), + onchange: function () { + const item_code = this.get_value(); + const warehouse = this.grid_row.on_grid_fields_dict.warehouse.get_value(); + if (item_code && warehouse) { + frappe.call({ + method: "erpnext.stock.utils.get_latest_stock_qty", + args: { + item_code: item_code, + warehouse: warehouse, + }, + callback: (r) => { + this.grid_row.on_grid_fields_dict.actual_qty.set_value( + r.message || 0 + ); + }, + }); } - }; - } - }, { - fieldtype:'Link', - fieldname:"warehouse", - options: 'Warehouse', - default: "", - in_list_view: 1, - label: __('Warehouse'), - onchange: function() { - const warehouse = this.get_value(); - const item_code = this.grid_row.on_grid_fields_dict.item_code.get_value(); - if (item_code && warehouse) { - frappe.call({ - method: "erpnext.stock.utils.get_latest_stock_qty", - args: { - item_code: item_code, - warehouse: warehouse + }, + get_query: (e) => { + return { + query: "erpnext.stock.doctype.item_alternative.item_alternative.get_alternative_items", + filters: { + item_code: e.item_code, }, - callback: (r) => { - this.grid_row.on_grid_fields_dict - .actual_qty.set_value(r.message || 0); - } - }) - } + }; + }, }, - }, { - fieldtype:'Float', - fieldname:"actual_qty", - default: 0, - read_only: 1, - in_list_view: 1, - label: __('Available Qty') - }] + { + fieldtype: "Link", + fieldname: "warehouse", + options: "Warehouse", + default: "", + in_list_view: 1, + label: __("Warehouse"), + onchange: function () { + const warehouse = this.get_value(); + const item_code = this.grid_row.on_grid_fields_dict.item_code.get_value(); + if (item_code && warehouse) { + frappe.call({ + method: "erpnext.stock.utils.get_latest_stock_qty", + args: { + item_code: item_code, + warehouse: warehouse, + }, + callback: (r) => { + this.grid_row.on_grid_fields_dict.actual_qty.set_value( + r.message || 0 + ); + }, + }); + } + }, + }, + { + fieldtype: "Float", + fieldname: "actual_qty", + default: 0, + read_only: 1, + in_list_view: 1, + label: __("Available Qty"), + }, + ], }, ], - primary_action: function() { + primary_action: function () { const args = this.get_values()["alternative_items"]; - const alternative_items = args.filter(d => { + const alternative_items = args.filter((d) => { if (d.alternate_item && d.item_code != d.alternate_item) { return true; } }); - alternative_items.forEach(d => { + alternative_items.forEach((d) => { let row = frappe.get_doc(opts.child_doctype, d.docname); let qty = null; - if (row.doctype === 'Work Order Item') { + if (row.doctype === "Work Order Item") { qty = row.required_qty; } else { qty = row.qty; } row[item_field] = d.alternate_item; - frappe.model.set_value(row.doctype, row.name, 'qty', qty); + frappe.model.set_value(row.doctype, row.name, "qty", qty); frappe.model.set_value(row.doctype, row.name, opts.original_item_field, d.item_code); frm.trigger(item_field, row.doctype, row.name); }); @@ -494,139 +547,145 @@ erpnext.utils.select_alternate_items = function(opts) { refresh_field(opts.child_docname); this.hide(); }, - primary_action_label: __('Update') + primary_action_label: __("Update"), }); - frm.doc[opts.child_docname].forEach(d => { + frm.doc[opts.child_docname].forEach((d) => { if (!opts.condition || opts.condition(d)) { dialog.fields_dict.alternative_items.df.data.push({ - "docname": d.name, - "item_code": d[item_field], - "warehouse": d[warehouse_field], - "actual_qty": d.actual_qty + docname: d.name, + item_code: d[item_field], + warehouse: d[warehouse_field], + actual_qty: d.actual_qty, }); } - }) + }); this.data = dialog.fields_dict.alternative_items.df.data; dialog.fields_dict.alternative_items.grid.refresh(); dialog.show(); -} +}; -erpnext.utils.update_child_items = function(opts) { +erpnext.utils.update_child_items = function (opts) { const frm = opts.frm; - const cannot_add_row = (typeof opts.cannot_add_row === 'undefined') ? true : opts.cannot_add_row; - const child_docname = (typeof opts.cannot_add_row === 'undefined') ? "items" : opts.child_docname; + const cannot_add_row = typeof opts.cannot_add_row === "undefined" ? true : opts.cannot_add_row; + const child_docname = typeof opts.cannot_add_row === "undefined" ? "items" : opts.child_docname; const child_meta = frappe.get_meta(`${frm.doc.doctype} Item`); - const get_precision = (fieldname) => child_meta.fields.find(f => f.fieldname == fieldname).precision; + const get_precision = (fieldname) => child_meta.fields.find((f) => f.fieldname == fieldname).precision; this.data = frm.doc[opts.child_docname].map((d) => { return { - "docname": d.name, - "name": d.name, - "item_code": d.item_code, - "delivery_date": d.delivery_date, - "schedule_date": d.schedule_date, - "conversion_factor": d.conversion_factor, - "qty": d.qty, - "rate": d.rate, - "uom": d.uom - } + docname: d.name, + name: d.name, + item_code: d.item_code, + delivery_date: d.delivery_date, + schedule_date: d.schedule_date, + conversion_factor: d.conversion_factor, + qty: d.qty, + rate: d.rate, + uom: d.uom, + }; }); - const fields = [{ - fieldtype:'Data', - fieldname:"docname", - read_only: 1, - hidden: 1, - }, { - fieldtype:'Link', - fieldname:"item_code", - options: 'Item', - in_list_view: 1, - read_only: 0, - disabled: 0, - label: __('Item Code'), - get_query: function() { - let filters; - if (frm.doc.doctype == 'Sales Order') { - filters = {"is_sales_item": 1}; - } else if (frm.doc.doctype == 'Purchase Order') { - if (frm.doc.is_subcontracted) { - if (frm.doc.is_old_subcontracting_flow) { - filters = {"is_sub_contracted_item": 1}; - } else { - filters = {"is_stock_item": 0}; - } - } else { - filters = {"is_purchase_item": 1}; - } - } - return { - query: "erpnext.controllers.queries.item_query", - filters: filters - }; - } - }, { - fieldtype:'Link', - fieldname:'uom', - options: 'UOM', - read_only: 0, - label: __('UOM'), - reqd: 1, - onchange: function () { - frappe.call({ - method: "erpnext.stock.get_item_details.get_conversion_factor", - args: { item_code: this.doc.item_code, uom: this.value }, - callback: r => { - if(!r.exc) { - if (this.doc.conversion_factor == r.message.conversion_factor) return; - - const docname = this.doc.docname; - dialog.fields_dict.trans_items.df.data.some(doc => { - if (doc.docname == docname) { - doc.conversion_factor = r.message.conversion_factor; - dialog.fields_dict.trans_items.grid.refresh(); - return true; - } - }) - } - } - }); - } - }, { - fieldtype:'Float', - fieldname:"qty", - default: 0, - read_only: 0, - in_list_view: 1, - label: __('Qty'), - precision: get_precision("qty") - }, { - fieldtype:'Currency', - fieldname:"rate", - options: "currency", - default: 0, - read_only: 0, - in_list_view: 1, - label: __('Rate'), - precision: get_precision("rate") - }]; - - if (frm.doc.doctype == 'Sales Order' || frm.doc.doctype == 'Purchase Order' ) { - fields.splice(2, 0, { - fieldtype: 'Date', - fieldname: frm.doc.doctype == 'Sales Order' ? "delivery_date" : "schedule_date", + const fields = [ + { + fieldtype: "Data", + fieldname: "docname", + read_only: 1, + hidden: 1, + }, + { + fieldtype: "Link", + fieldname: "item_code", + options: "Item", in_list_view: 1, - label: frm.doc.doctype == 'Sales Order' ? __("Delivery Date") : __("Reqd by date"), - reqd: 1 - }) + read_only: 0, + disabled: 0, + label: __("Item Code"), + get_query: function () { + let filters; + if (frm.doc.doctype == "Sales Order") { + filters = { is_sales_item: 1 }; + } else if (frm.doc.doctype == "Purchase Order") { + if (frm.doc.is_subcontracted) { + if (frm.doc.is_old_subcontracting_flow) { + filters = { is_sub_contracted_item: 1 }; + } else { + filters = { is_stock_item: 0 }; + } + } else { + filters = { is_purchase_item: 1 }; + } + } + return { + query: "erpnext.controllers.queries.item_query", + filters: filters, + }; + }, + }, + { + fieldtype: "Link", + fieldname: "uom", + options: "UOM", + read_only: 0, + label: __("UOM"), + reqd: 1, + onchange: function () { + frappe.call({ + method: "erpnext.stock.get_item_details.get_conversion_factor", + args: { item_code: this.doc.item_code, uom: this.value }, + callback: (r) => { + if (!r.exc) { + if (this.doc.conversion_factor == r.message.conversion_factor) return; + + const docname = this.doc.docname; + dialog.fields_dict.trans_items.df.data.some((doc) => { + if (doc.docname == docname) { + doc.conversion_factor = r.message.conversion_factor; + dialog.fields_dict.trans_items.grid.refresh(); + return true; + } + }); + } + }, + }); + }, + }, + { + fieldtype: "Float", + fieldname: "qty", + default: 0, + read_only: 0, + in_list_view: 1, + label: __("Qty"), + precision: get_precision("qty"), + }, + { + fieldtype: "Currency", + fieldname: "rate", + options: "currency", + default: 0, + read_only: 0, + in_list_view: 1, + label: __("Rate"), + precision: get_precision("rate"), + }, + ]; + + if (frm.doc.doctype == "Sales Order" || frm.doc.doctype == "Purchase Order") { + fields.splice(2, 0, { + fieldtype: "Date", + fieldname: frm.doc.doctype == "Sales Order" ? "delivery_date" : "schedule_date", + in_list_view: 1, + label: frm.doc.doctype == "Sales Order" ? __("Delivery Date") : __("Reqd by date"), + reqd: 1, + }); fields.splice(3, 0, { - fieldtype: 'Float', + fieldtype: "Float", fieldname: "conversion_factor", label: __("Conversion Factor"), - precision: get_precision('conversion_factor') - }) + precision: get_precision("conversion_factor"), + }); } new frappe.ui.Dialog({ @@ -644,86 +703,82 @@ erpnext.utils.update_child_items = function(opts) { get_data: () => { return this.data; }, - fields: fields + fields: fields, }, ], - primary_action: function() { + primary_action: function () { const trans_items = this.get_values()["trans_items"].filter((item) => !!item.item_code); frappe.call({ - method: 'erpnext.controllers.accounts_controller.update_child_qty_rate', + method: "erpnext.controllers.accounts_controller.update_child_qty_rate", freeze: true, args: { - 'parent_doctype': frm.doc.doctype, - 'trans_items': trans_items, - 'parent_doctype_name': frm.doc.name, - 'child_docname': child_docname + parent_doctype: frm.doc.doctype, + trans_items: trans_items, + parent_doctype_name: frm.doc.name, + child_docname: child_docname, }, - callback: function() { + callback: function () { frm.reload_doc(); - } + }, }); this.hide(); refresh_field("items"); }, - primary_action_label: __('Update') + primary_action_label: __("Update"), }).show(); -} +}; - - - -erpnext.utils.map_current_doc = function(opts) { +erpnext.utils.map_current_doc = function (opts) { function _map() { - if($.isArray(cur_frm.doc.items) && cur_frm.doc.items.length > 0) { + if ($.isArray(cur_frm.doc.items) && cur_frm.doc.items.length > 0) { // remove first item row if empty - if(!cur_frm.doc.items[0].item_code) { + if (!cur_frm.doc.items[0].item_code) { cur_frm.doc.items = cur_frm.doc.items.splice(1); } // find the doctype of the items table - var items_doctype = frappe.meta.get_docfield(cur_frm.doctype, 'items').options; + var items_doctype = frappe.meta.get_docfield(cur_frm.doctype, "items").options; // find the link fieldname from items table for the given // source_doctype var link_fieldname = null; - frappe.get_meta(items_doctype).fields.forEach(function(d) { - if(d.options===opts.source_doctype) link_fieldname = d.fieldname; }); + frappe.get_meta(items_doctype).fields.forEach(function (d) { + if (d.options === opts.source_doctype) link_fieldname = d.fieldname; + }); // search in existing items if the source_name is already set and full qty fetched var already_set = false; var item_qty_map = {}; - $.each(cur_frm.doc.items, function(i, d) { - opts.source_name.forEach(function(src) { - if(d[link_fieldname]==src) { + $.each(cur_frm.doc.items, function (i, d) { + opts.source_name.forEach(function (src) { + if (d[link_fieldname] == src) { already_set = true; - if (item_qty_map[d.item_code]) - item_qty_map[d.item_code] += flt(d.qty); - else - item_qty_map[d.item_code] = flt(d.qty); + if (item_qty_map[d.item_code]) item_qty_map[d.item_code] += flt(d.qty); + else item_qty_map[d.item_code] = flt(d.qty); } }); }); - if(already_set) { - opts.source_name.forEach(function(src) { - frappe.model.with_doc(opts.source_doctype, src, function(r) { + if (already_set) { + opts.source_name.forEach(function (src) { + frappe.model.with_doc(opts.source_doctype, src, function (r) { var source_doc = frappe.model.get_doc(opts.source_doctype, src); - $.each(source_doc.items || [], function(i, row) { - if(row.qty > flt(item_qty_map[row.item_code])) { + $.each(source_doc.items || [], function (i, row) { + if (row.qty > flt(item_qty_map[row.item_code])) { already_set = false; return false; } - }) - }) + }); + }); - if(already_set) { - frappe.msgprint(__("You have already selected items from {0} {1}", - [opts.source_doctype, src])); + if (already_set) { + frappe.msgprint( + __("You have already selected items from {0} {1}", [opts.source_doctype, src]) + ); return; } - - }) + }); } } @@ -731,20 +786,20 @@ erpnext.utils.map_current_doc = function(opts) { // Sometimes we hit the limit for URL length of a GET request // as we send the full target_doc. Hence this is a POST request. type: "POST", - method: 'frappe.model.mapper.map_docs', + method: "frappe.model.mapper.map_docs", args: { - "method": opts.method, - "source_names": opts.source_name, - "target_doc": cur_frm.doc, - "args": opts.args + method: opts.method, + source_names: opts.source_name, + target_doc: cur_frm.doc, + args: opts.args, }, - callback: function(r) { - if(!r.exc) { + callback: function (r) { + if (!r.exc) { frappe.model.sync(r.message); cur_frm.dirty(); cur_frm.refresh(); } - } + }, }); } @@ -763,11 +818,11 @@ erpnext.utils.map_current_doc = function(opts) { if (opts.source_doctype) { let data_fields = []; - if(opts.source_doctype == "Purchase Receipt") { + if (opts.source_doctype == "Purchase Receipt") { data_fields.push({ - fieldname: 'merge_taxes', - fieldtype: 'Check', - label: __('Merge taxes from multiple documents'), + fieldname: "merge_taxes", + fieldtype: "Check", + label: __("Merge taxes from multiple documents"), }); } const d = new frappe.ui.form.MultiSelectDialog({ @@ -782,10 +837,10 @@ erpnext.utils.map_current_doc = function(opts) { child_fieldname: opts.child_fieldname, child_columns: opts.child_columns, size: opts.size, - action: function(selections, args) { + action: function (selections, args) { let values = selections; if (values.length === 0) { - frappe.msgprint(__("Please select {0}", [opts.source_doctype])) + frappe.msgprint(__("Please select {0}", [opts.source_doctype])); return; } opts.source_name = values; @@ -805,11 +860,11 @@ erpnext.utils.map_current_doc = function(opts) { opts.source_name = [opts.source_name]; _map(); } -} +}; -frappe.form.link_formatters['Item'] = function(value, doc) { +frappe.form.link_formatters["Item"] = function (value, doc) { if (doc && value && doc.item_name && doc.item_name !== value && doc.item_code === value) { - return value + ': ' + doc.item_name; + return value + ": " + doc.item_name; } else if (!value && doc.doctype && doc.item_name) { // format blank value in child table return doc.item_name; @@ -817,11 +872,11 @@ frappe.form.link_formatters['Item'] = function(value, doc) { // if value is blank in report view or item code and name are the same, return as is return value; } -} +}; -frappe.form.link_formatters['Employee'] = function(value, doc) { +frappe.form.link_formatters["Employee"] = function (value, doc) { if (doc && value && doc.employee_name && doc.employee_name !== value && doc.employee === value) { - return value + ': ' + doc.employee_name; + return value + ": " + doc.employee_name; } else if (!value && doc.doctype && doc.employee_name) { // format blank value in child table return doc.employee; @@ -829,11 +884,11 @@ frappe.form.link_formatters['Employee'] = function(value, doc) { // if value is blank in report view or project name and name are the same, return as is return value; } -} +}; -frappe.form.link_formatters['Project'] = function(value, doc) { +frappe.form.link_formatters["Project"] = function (value, doc) { if (doc && value && doc.project_name && doc.project_name !== value && doc.project === value) { - return value + ': ' + doc.project_name; + return value + ": " + doc.project_name; } else if (!value && doc.doctype && doc.project_name) { // format blank value in child table return doc.project; @@ -844,64 +899,74 @@ frappe.form.link_formatters['Project'] = function(value, doc) { }; // add description on posting time -$(document).on('app_ready', function() { - if(!frappe.datetime.is_timezone_same()) { - $.each(["Stock Reconciliation", "Stock Entry", "Stock Ledger Entry", - "Delivery Note", "Purchase Receipt", "Sales Invoice"], function(i, d) { - frappe.ui.form.on(d, "onload", function(frm) { - cur_frm.set_df_property("posting_time", "description", - frappe.sys_defaults.time_zone); - }); - }); +$(document).on("app_ready", function () { + if (!frappe.datetime.is_timezone_same()) { + $.each( + [ + "Stock Reconciliation", + "Stock Entry", + "Stock Ledger Entry", + "Delivery Note", + "Purchase Receipt", + "Sales Invoice", + ], + function (i, d) { + frappe.ui.form.on(d, "onload", function (frm) { + cur_frm.set_df_property("posting_time", "description", frappe.sys_defaults.time_zone); + }); + } + ); } }); // Show SLA dashboard -$(document).on('app_ready', function() { - $.each(frappe.boot.service_level_agreement_doctypes, function(_i, d) { +$(document).on("app_ready", function () { + $.each(frappe.boot.service_level_agreement_doctypes, function (_i, d) { frappe.ui.form.on(d, { - onload: function(frm) { - if (!frm.doc.service_level_agreement) - return; + onload: function (frm) { + if (!frm.doc.service_level_agreement) return; frappe.call({ - method: 'erpnext.support.doctype.service_level_agreement.service_level_agreement.get_service_level_agreement_filters', + method: "erpnext.support.doctype.service_level_agreement.service_level_agreement.get_service_level_agreement_filters", args: { doctype: frm.doc.doctype, name: frm.doc.service_level_agreement, - customer: frm.doc.customer + customer: frm.doc.customer, }, callback: function (r) { if (r && r.message) { - frm.set_query('priority', function() { + frm.set_query("priority", function () { return { filters: { - 'name': ['in', r.message.priority], - } + name: ["in", r.message.priority], + }, }; }); - frm.set_query('service_level_agreement', function() { + frm.set_query("service_level_agreement", function () { return { filters: { - 'name': ['in', r.message.service_level_agreements], - } + name: ["in", r.message.service_level_agreements], + }, }; }); } - } + }, }); }, - refresh: function(frm) { - if (frm.doc.status !== 'Closed' && frm.doc.service_level_agreement - && ['First Response Due', 'Resolution Due'].includes(frm.doc.agreement_status)) { + refresh: function (frm) { + if ( + frm.doc.status !== "Closed" && + frm.doc.service_level_agreement && + ["First Response Due", "Resolution Due"].includes(frm.doc.agreement_status) + ) { frappe.call({ - 'method': 'frappe.client.get', + method: "frappe.client.get", args: { - doctype: 'Service Level Agreement', - name: frm.doc.service_level_agreement + doctype: "Service Level Agreement", + name: frm.doc.service_level_agreement, }, - callback: function(data) { + callback: function (data) { let statuses = data.message.pause_sla_on; const hold_statuses = []; $.each(statuses, (_i, entry) => { @@ -909,32 +974,46 @@ $(document).on('app_ready', function() { }); if (hold_statuses.includes(frm.doc.status)) { frm.dashboard.clear_headline(); - let message = {'indicator': 'orange', 'msg': __('SLA is on hold since {0}', [moment(frm.doc.on_hold_since).fromNow(true)])}; + let message = { + indicator: "orange", + msg: __("SLA is on hold since {0}", [ + moment(frm.doc.on_hold_since).fromNow(true), + ]), + }; frm.dashboard.set_headline_alert( '
              ' + '
              ' + - ''+ message.msg +' ' + - '
              ' + - '
              ' + '' + + message.msg + + " " + + "
              " + + "
              " ); } else { set_time_to_resolve_and_response(frm, data.message.apply_sla_for_resolution); } - } + }, }); } else if (frm.doc.service_level_agreement) { frm.dashboard.clear_headline(); - let agreement_status = (frm.doc.agreement_status == 'Fulfilled') ? - {'indicator': 'green', 'msg': 'Service Level Agreement has been fulfilled'} : - {'indicator': 'red', 'msg': 'Service Level Agreement Failed'}; + let agreement_status = + frm.doc.agreement_status == "Fulfilled" + ? { indicator: "green", msg: "Service Level Agreement has been fulfilled" } + : { indicator: "red", msg: "Service Level Agreement Failed" }; frm.dashboard.set_headline_alert( '
              ' + '
              ' + - ' ' + - '
              ' + - '
              ' + ' " + + "
              " + + "" ); } }, @@ -960,7 +1039,6 @@ function set_time_to_resolve_and_response(frm, apply_sla_for_resolution) { `; - if (apply_sla_for_resolution) { let time_to_resolve; if (!frm.doc.resolution_date) { @@ -977,34 +1055,32 @@ function set_time_to_resolve_and_response(frm, apply_sla_for_resolution) { `; } - alert += ''; + alert += ""; frm.dashboard.set_headline_alert(alert); } function get_time_left(timestamp, agreement_status) { const diff = moment(timestamp).diff(frappe.datetime.system_datetime(true)); - const diff_display = diff >= 44500 ? moment.duration(diff).humanize() : 'Failed'; - let indicator = (diff_display == 'Failed' && agreement_status != 'Fulfilled') ? 'red' : 'green'; - return {'diff_display': diff_display, 'indicator': indicator}; + const diff_display = diff >= 44500 ? moment.duration(diff).humanize() : "Failed"; + let indicator = diff_display == "Failed" && agreement_status != "Fulfilled" ? "red" : "green"; + return { diff_display: diff_display, indicator: indicator }; } function get_status(expected, actual) { const time_left = moment(expected).diff(moment(actual)); if (time_left >= 0) { - return {'diff_display': 'Fulfilled', 'indicator': 'green'}; + return { diff_display: "Fulfilled", indicator: "green" }; } else { - return {'diff_display': 'Failed', 'indicator': 'red'}; + return { diff_display: "Failed", indicator: "red" }; } } function attach_selector_button(inner_text, append_loction, context, grid_row) { - let $btn_div = $("
              ").css({"margin-bottom": "10px", "margin-top": "10px"}) - .appendTo(append_loction); - let $btn = $(``) - .appendTo($btn_div); + let $btn_div = $("
              ").css({ "margin-bottom": "10px", "margin-top": "10px" }).appendTo(append_loction); + let $btn = $(``).appendTo($btn_div); - $btn.on("click", function() { + $btn.on("click", function () { context.show_serial_batch_selector(grid_row.frm, grid_row.doc, "", "", true); }); } diff --git a/erpnext/public/js/utils/barcode_scanner.js b/erpnext/public/js/utils/barcode_scanner.js index aadbb24cc04..15d13f9ffb1 100644 --- a/erpnext/public/js/utils/barcode_scanner.js +++ b/erpnext/public/js/utils/barcode_scanner.js @@ -58,13 +58,15 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { return; } - me.update_table(data).then(row => { - this.play_success_sound(); - resolve(row); - }).catch(() => { - this.play_fail_sound(); - reject(); - }); + me.update_table(data) + .then((row) => { + this.play_success_sound(); + resolve(row); + }) + .catch(() => { + this.play_fail_sound(); + reject(); + }); }); }); } @@ -83,11 +85,11 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { } update_table(data) { - return new Promise(resolve => { + return new Promise((resolve) => { let cur_grid = this.frm.fields_dict[this.items_table_name].grid; frappe.flags.trigger_from_barcode_scanner = true; - const {item_code, barcode, batch_no, serial_no, uom} = data; + const { item_code, barcode, batch_no, serial_no, uom } = data; let row = this.get_row_to_modify_on_scan(item_code, batch_no, uom, barcode); @@ -116,16 +118,17 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { frappe.run_serially([ () => this.set_selector_trigger_flag(data), - () => this.set_item(row, item_code, barcode, batch_no, serial_no).then(qty => { - this.show_scan_message(row.idx, row.item_code, qty); - }), + () => + this.set_item(row, item_code, barcode, batch_no, serial_no).then((qty) => { + this.show_scan_message(row.idx, row.item_code, qty); + }), () => this.set_barcode_uom(row, uom), () => this.set_serial_no(row, serial_no), () => this.set_batch_no(row, batch_no), () => this.set_barcode(row, barcode), () => this.clean_up(), () => this.revert_selector_flag(), - () => resolve(row) + () => resolve(row), ]); }); } @@ -133,7 +136,7 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { // batch and serial selector is reduandant when all info can be added by scan // this flag on item row is used by transaction.js to avoid triggering selector set_selector_trigger_flag(data) { - const {batch_no, serial_no, has_batch_no, has_serial_no} = data; + const { batch_no, serial_no, has_batch_no, has_serial_no } = data; const require_selecting_batch = has_batch_no && !batch_no; const require_selecting_serial = has_serial_no && !serial_no; @@ -149,17 +152,17 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { } set_item(row, item_code, barcode, batch_no, serial_no) { - return new Promise(resolve => { + return new Promise((resolve) => { const increment = async (value = 1) => { - const item_data = {item_code: item_code}; + const item_data = { item_code: item_code }; frappe.flags.trigger_from_barcode_scanner = true; - item_data[this.qty_field] = Number((row[this.qty_field] || 0)) + Number(value); + item_data[this.qty_field] = Number(row[this.qty_field] || 0) + Number(value); await frappe.model.set_value(row.doctype, row.name, item_data); return value; }; if (this.prompt_qty) { - frappe.prompt(__("Please enter quantity for item {0}", [item_code]), ({value}) => { + frappe.prompt(__("Please enter quantity for item {0}", [item_code]), ({ value }) => { increment(value).then((value) => resolve(value)); }); } else if (this.frm.has_items) { @@ -175,14 +178,15 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { this.dialog = new frappe.ui.Dialog({ title: __("Scan barcode for item {0}", [item_code]), fields: me.get_fields_for_dialog(row, item_code, barcode, batch_no, serial_no), - }) + }); this.dialog.set_primary_action(__("Update"), () => { - const item_data = {item_code: item_code}; + const item_data = { item_code: item_code }; item_data[this.qty_field] = this.dialog.get_value("scanned_qty"); item_data["has_item_scanned"] = 1; - this.remaining_qty = flt(this.dialog.get_value("qty")) - flt(this.dialog.get_value("scanned_qty")); + this.remaining_qty = + flt(this.dialog.get_value("qty")) - flt(this.dialog.get_value("scanned_qty")); frappe.model.set_value(row.doctype, row.name, item_data); frappe.run_serially([ @@ -190,7 +194,7 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { () => this.set_barcode(row, this.dialog.get_value("barcode")), () => this.set_serial_no(row, this.dialog.get_value("serial_no")), () => this.add_child_for_remaining_qty(row), - () => this.clean_up() + () => this.clean_up(), ]); this.dialog.hide(); @@ -219,9 +223,9 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { if (r.message) { this.update_dialog_values(item_code, r); } - }) + }); } - } + }, }, { fieldtype: "Section Break", @@ -245,8 +249,8 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { }, { fieldtype: "Section Break", - } - ] + }, + ]; if (batch_no) { fields.push({ @@ -256,7 +260,7 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { label: __("Batch No"), default: batch_no, read_only: 1, - hidden: 1 + hidden: 1, }); } @@ -278,7 +282,7 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { label: __("Barcode"), default: barcode, read_only: 1, - hidden: 1 + hidden: 1, }); } @@ -286,18 +290,18 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { } update_dialog_values(scanned_item, r) { - const {item_code, barcode, batch_no, serial_no} = r.message; + const { item_code, barcode, batch_no, serial_no } = r.message; this.dialog.set_value("barcode_scanner", ""); - if (item_code === scanned_item && - (this.dialog.get_value("barcode") === barcode || batch_no || serial_no)) { - + if ( + item_code === scanned_item && + (this.dialog.get_value("barcode") === barcode || batch_no || serial_no) + ) { if (batch_no) { this.dialog.set_value("batch_no", batch_no); } if (serial_no) { - this.validate_duplicate_serial_no(serial_no); let serial_nos = this.dialog.get_value("serial_no") + "\n" + serial_no; this.dialog.set_value("serial_no", serial_nos); @@ -309,8 +313,9 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { } validate_duplicate_serial_no(serial_no) { - let serial_nos = this.dialog.get_value("serial_no") ? - this.dialog.get_value("serial_no").split("\n") : []; + let serial_nos = this.dialog.get_value("serial_no") + ? this.dialog.get_value("serial_no").split("\n") + : []; if (in_list(serial_nos, serial_no)) { frappe.throw(__("Serial No {0} already scanned", [serial_no])); @@ -318,12 +323,19 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { } add_child_for_remaining_qty(prev_row) { - if (this.remaining_qty && this.remaining_qty >0) { + if (this.remaining_qty && this.remaining_qty > 0) { let cur_grid = this.frm.fields_dict[this.items_table_name].grid; let row = frappe.model.add_child(this.frm.doc, cur_grid.doctype, this.items_table_name); - let ignore_fields = ["name", "idx", "batch_no", "barcode", - "received_qty", "serial_no", "has_item_scanned"]; + let ignore_fields = [ + "name", + "idx", + "batch_no", + "barcode", + "received_qty", + "serial_no", + "has_item_scanned", + ]; for (let key in prev_row) { if (in_list(ignore_fields, key)) { @@ -379,7 +391,7 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { if (exist) { this.show_alert(__("Row #{0}: Qty increased by {1}", [idx, qty]), "green"); } else { - this.show_alert(__("Row #{0}: Item added", [idx]), "green") + this.show_alert(__("Row #{0}: Item added", [idx]), "green"); } } @@ -401,17 +413,19 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { const matching_row = (row) => { const item_match = row.item_code == item_code; - const batch_match = (!row[this.batch_no_field] || row[this.batch_no_field] == batch_no); + const batch_match = !row[this.batch_no_field] || row[this.batch_no_field] == batch_no; const uom_match = !uom || row[this.uom_field] == uom; const qty_in_limit = flt(row[this.qty_field]) < flt(row[this.max_qty_field]); const item_scanned = row.has_item_scanned; - return item_match - && uom_match - && !item_scanned - && (!is_batch_no_scan || batch_match) - && (!check_max_qty || qty_in_limit) - } + return ( + item_match && + uom_match && + !item_scanned && + (!is_batch_no_scan || batch_match) && + (!check_max_qty || qty_in_limit) + ); + }; return this.items_table.find(matching_row) || this.get_existing_blank_row(); } @@ -432,7 +446,7 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner { this.scan_barcode_field.set_value(""); refresh_field(this.items_table_name); } - show_alert(msg, indicator, duration=3) { - frappe.show_alert({message: msg, indicator: indicator}, duration); + show_alert(msg, indicator, duration = 3) { + frappe.show_alert({ message: msg, indicator: indicator }, duration); } }; diff --git a/erpnext/public/js/utils/contact_address_quick_entry.js b/erpnext/public/js/utils/contact_address_quick_entry.js index adabf08c203..2f61dee1994 100644 --- a/erpnext/public/js/utils/contact_address_quick_entry.js +++ b/erpnext/public/js/utils/contact_address_quick_entry.js @@ -1,6 +1,8 @@ -frappe.provide('frappe.ui.form'); +frappe.provide("frappe.ui.form"); -frappe.ui.form.ContactAddressQuickEntryForm = class ContactAddressQuickEntryForm extends frappe.ui.form.QuickEntryForm { +frappe.ui.form.ContactAddressQuickEntryForm = class ContactAddressQuickEntryForm extends ( + frappe.ui.form.QuickEntryForm +) { constructor(doctype, after_insert, init_callback, doc, force) { super(doctype, after_insert, init_callback, doc, force); this.skip_redirect_on_error = true; @@ -17,8 +19,8 @@ frappe.ui.form.ContactAddressQuickEntryForm = class ContactAddressQuickEntryForm * Therefor, resulting in the fields being "hidden". */ const map_field_names = { - "email_address": "email_id", - "mobile_number": "mobile_no", + email_address: "email_id", + mobile_number: "mobile_no", }; Object.entries(map_field_names).forEach(([fieldname, new_fieldname]) => { @@ -30,71 +32,73 @@ frappe.ui.form.ContactAddressQuickEntryForm = class ContactAddressQuickEntryForm } get_variant_fields() { - var variant_fields = [{ - fieldtype: "Section Break", - label: __("Primary Contact Details"), - collapsible: 1 - }, - { - label: __("Email Id"), - fieldname: "email_address", - fieldtype: "Data", - options: "Email", - }, - { - fieldtype: "Column Break" - }, - { - label: __("Mobile Number"), - fieldname: "mobile_number", - fieldtype: "Data" - }, - { - fieldtype: "Section Break", - label: __("Primary Address Details"), - collapsible: 1 - }, - { - label: __("Address Line 1"), - fieldname: "address_line1", - fieldtype: "Data" - }, - { - label: __("Address Line 2"), - fieldname: "address_line2", - fieldtype: "Data" - }, - { - label: __("ZIP Code"), - fieldname: "pincode", - fieldtype: "Data" - }, - { - fieldtype: "Column Break" - }, - { - label: __("City"), - fieldname: "city", - fieldtype: "Data" - }, - { - label: __("State"), - fieldname: "state", - fieldtype: "Data" - }, - { - label: __("Country"), - fieldname: "country", - fieldtype: "Link", - options: "Country" - }, - { - label: __("Customer POS Id"), - fieldname: "customer_pos_id", - fieldtype: "Data", - hidden: 1 - }]; + var variant_fields = [ + { + fieldtype: "Section Break", + label: __("Primary Contact Details"), + collapsible: 1, + }, + { + label: __("Email Id"), + fieldname: "email_address", + fieldtype: "Data", + options: "Email", + }, + { + fieldtype: "Column Break", + }, + { + label: __("Mobile Number"), + fieldname: "mobile_number", + fieldtype: "Data", + }, + { + fieldtype: "Section Break", + label: __("Primary Address Details"), + collapsible: 1, + }, + { + label: __("Address Line 1"), + fieldname: "address_line1", + fieldtype: "Data", + }, + { + label: __("Address Line 2"), + fieldname: "address_line2", + fieldtype: "Data", + }, + { + label: __("ZIP Code"), + fieldname: "pincode", + fieldtype: "Data", + }, + { + fieldtype: "Column Break", + }, + { + label: __("City"), + fieldname: "city", + fieldtype: "Data", + }, + { + label: __("State"), + fieldname: "state", + fieldtype: "Data", + }, + { + label: __("Country"), + fieldname: "country", + fieldtype: "Link", + options: "Country", + }, + { + label: __("Customer POS Id"), + fieldname: "customer_pos_id", + fieldtype: "Data", + hidden: 1, + }, + ]; return variant_fields; } -} +}; diff --git a/erpnext/public/js/utils/crm_activities.js b/erpnext/public/js/utils/crm_activities.js index ec79a10dfac..a5a225458c0 100644 --- a/erpnext/public/js/utils/crm_activities.js +++ b/erpnext/public/js/utils/crm_activities.js @@ -6,21 +6,21 @@ erpnext.utils.CRMActivities = class CRMActivities { refresh() { var me = this; $(this.open_activities_wrapper).empty(); - let cur_form_footer = this.form_wrapper.find('.form-footer'); + let cur_form_footer = this.form_wrapper.find(".form-footer"); // all activities - if (!$(this.all_activities_wrapper).find('.form-footer').length) { + if (!$(this.all_activities_wrapper).find(".form-footer").length) { this.all_activities_wrapper.empty(); $(cur_form_footer).appendTo(this.all_activities_wrapper); // remove frappe-control class to avoid absolute position for action-btn - $(this.all_activities_wrapper).removeClass('frappe-control'); + $(this.all_activities_wrapper).removeClass("frappe-control"); // hide new event button - $('.timeline-actions').find('.btn-default').hide(); + $(".timeline-actions").find(".btn-default").hide(); // hide new comment box $(".comment-box").hide(); // show only communications by default - $($('.timeline-content').find('.nav-link')[0]).tab('show'); + $($(".timeline-content").find(".nav-link")[0]).tab("show"); } // open activities @@ -28,66 +28,70 @@ erpnext.utils.CRMActivities = class CRMActivities { method: "erpnext.crm.utils.get_open_activities", args: { ref_doctype: this.frm.doc.doctype, - ref_docname: this.frm.doc.name + ref_docname: this.frm.doc.name, }, callback: (r) => { if (!r.exc) { - var activities_html = frappe.render_template('crm_activities', { + var activities_html = frappe.render_template("crm_activities", { tasks: r.message.tasks, - events: r.message.events + events: r.message.events, }); $(activities_html).appendTo(me.open_activities_wrapper); - $(".open-tasks").find(".completion-checkbox").on("click", function() { - me.update_status(this, "ToDo"); - }); + $(".open-tasks") + .find(".completion-checkbox") + .on("click", function () { + me.update_status(this, "ToDo"); + }); - $(".open-events").find(".completion-checkbox").on("click", function() { - me.update_status(this, "Event"); - }); + $(".open-events") + .find(".completion-checkbox") + .on("click", function () { + me.update_status(this, "Event"); + }); me.create_task(); me.create_event(); } - } + }, }); } - create_task () { + create_task() { let me = this; let _create_task = () => { const args = { doc: me.frm.doc, frm: me.frm, - title: __("New Task") + title: __("New Task"), }; let composer = new frappe.views.InteractionComposer(args); - composer.dialog.get_field('interaction_type').set_value("ToDo"); + composer.dialog.get_field("interaction_type").set_value("ToDo"); // hide column having interaction type field - $(composer.dialog.get_field('interaction_type').wrapper).closest('.form-column').hide(); + $(composer.dialog.get_field("interaction_type").wrapper).closest(".form-column").hide(); // hide summary field - $(composer.dialog.get_field('summary').wrapper).closest('.form-section').hide(); + $(composer.dialog.get_field("summary").wrapper).closest(".form-section").hide(); }; $(".new-task-btn").click(_create_task); } - create_event () { + create_event() { let me = this; let _create_event = () => { const args = { doc: me.frm.doc, frm: me.frm, - title: __("New Event") + title: __("New Event"), }; let composer = new frappe.views.InteractionComposer(args); - composer.dialog.get_field('interaction_type').set_value("Event"); - $(composer.dialog.get_field('interaction_type').wrapper).hide(); + composer.dialog.get_field("interaction_type").set_value("Event"); + $(composer.dialog.get_field("interaction_type").wrapper).hide(); }; $(".new-event-btn").click(_create_event); } - async update_status (input_field, doctype) { + async update_status(input_field, doctype) { let completed = $(input_field).prop("checked") ? 1 : 0; let docname = $(input_field).attr("name"); if (completed) { @@ -104,132 +108,129 @@ erpnext.utils.CRMNotes = class CRMNotes { refresh() { var me = this; - this.notes_wrapper.find('.notes-section').remove(); + this.notes_wrapper.find(".notes-section").remove(); let notes = this.frm.doc.notes || []; - notes.sort( - function(a, b) { - return new Date(b.added_on) - new Date(a.added_on); - } - ); + notes.sort(function (a, b) { + return new Date(b.added_on) - new Date(a.added_on); + }); - let notes_html = frappe.render_template( - 'crm_notes', - { - notes: notes - } - ); + let notes_html = frappe.render_template("crm_notes", { + notes: notes, + }); $(notes_html).appendTo(this.notes_wrapper); this.add_note(); - $(".notes-section").find(".edit-note-btn").on("click", function() { - me.edit_note(this); - }); + $(".notes-section") + .find(".edit-note-btn") + .on("click", function () { + me.edit_note(this); + }); - $(".notes-section").find(".delete-note-btn").on("click", function() { - me.delete_note(this); - }); + $(".notes-section") + .find(".delete-note-btn") + .on("click", function () { + me.delete_note(this); + }); } - - add_note () { + add_note() { let me = this; let _add_note = () => { var d = new frappe.ui.Dialog({ - title: __('Add a Note'), + title: __("Add a Note"), fields: [ { - "label": "Note", - "fieldname": "note", - "fieldtype": "Text Editor", - "reqd": 1, - "enable_mentions": true, - } + label: "Note", + fieldname: "note", + fieldtype: "Text Editor", + reqd: 1, + enable_mentions: true, + }, ], - primary_action: function() { + primary_action: function () { var data = d.get_values(); frappe.call({ method: "add_note", doc: me.frm.doc, args: { - note: data.note + note: data.note, }, freeze: true, - callback: function(r) { + callback: function (r) { if (!r.exc) { me.frm.refresh_field("notes"); me.refresh(); } d.hide(); - } + }, }); }, - primary_action_label: __('Add') + primary_action_label: __("Add"), }); d.show(); }; $(".new-note-btn").click(_add_note); } - edit_note (edit_btn) { + edit_note(edit_btn) { var me = this; - let row = $(edit_btn).closest('.comment-content'); + let row = $(edit_btn).closest(".comment-content"); let row_id = row.attr("name"); let row_content = $(row).find(".content").html(); if (row_content) { var d = new frappe.ui.Dialog({ - title: __('Edit Note'), + title: __("Edit Note"), fields: [ { - "label": "Note", - "fieldname": "note", - "fieldtype": "Text Editor", - "default": row_content - } + label: "Note", + fieldname: "note", + fieldtype: "Text Editor", + default: row_content, + }, ], - primary_action: function() { + primary_action: function () { var data = d.get_values(); frappe.call({ method: "edit_note", doc: me.frm.doc, args: { note: data.note, - row_id: row_id + row_id: row_id, }, freeze: true, - callback: function(r) { + callback: function (r) { if (!r.exc) { me.frm.refresh_field("notes"); me.refresh(); d.hide(); } - - } + }, }); }, - primary_action_label: __('Done') + primary_action_label: __("Done"), }); d.show(); } } - delete_note (delete_btn) { + delete_note(delete_btn) { var me = this; - let row_id = $(delete_btn).closest('.comment-content').attr("name"); + let row_id = $(delete_btn).closest(".comment-content").attr("name"); frappe.call({ method: "delete_note", doc: me.frm.doc, args: { - row_id: row_id + row_id: row_id, }, freeze: true, - callback: function(r) { + callback: function (r) { if (!r.exc) { me.frm.refresh_field("notes"); me.refresh(); } - } + }, }); } }; diff --git a/erpnext/public/js/utils/customer_quick_entry.js b/erpnext/public/js/utils/customer_quick_entry.js index b2532085f65..e9b77a356b2 100644 --- a/erpnext/public/js/utils/customer_quick_entry.js +++ b/erpnext/public/js/utils/customer_quick_entry.js @@ -1,3 +1,3 @@ -frappe.provide('frappe.ui.form'); +frappe.provide("frappe.ui.form"); frappe.ui.form.CustomerQuickEntryForm = frappe.ui.form.ContactAddressQuickEntryForm; diff --git a/erpnext/public/js/utils/dimension_tree_filter.js b/erpnext/public/js/utils/dimension_tree_filter.js index 27d00bacb88..36c0f1b51ae 100644 --- a/erpnext/public/js/utils/dimension_tree_filter.js +++ b/erpnext/public/js/utils/dimension_tree_filter.js @@ -1,4 +1,4 @@ -frappe.provide('erpnext.accounts'); +frappe.provide("erpnext.accounts"); erpnext.accounts.dimensions = { setup_dimension_filters(frm, doctype) { @@ -12,36 +12,38 @@ erpnext.accounts.dimensions = { frappe.call({ method: "erpnext.accounts.doctype.accounting_dimension.accounting_dimension.get_dimensions", args: { - 'with_cost_center_and_project': true + with_cost_center_and_project: true, }, - callback: function(r) { + callback: function (r) { me.accounting_dimensions = r.message[0]; // Ignoring "Project" as it is already handled specifically in Sales Order and Delivery Note - me.accounting_dimensions = me.accounting_dimensions.filter(x=>{return x.document_type != "Project"}); + me.accounting_dimensions = me.accounting_dimensions.filter((x) => { + return x.document_type != "Project"; + }); me.default_dimensions = r.message[1]; me.setup_filters(frm, doctype); - } + }, }); }, setup_filters(frm, doctype) { - if (doctype == 'Payment Entry' && this.accounting_dimensions) { - frm.dimension_filters = this.accounting_dimensions + if (doctype == "Payment Entry" && this.accounting_dimensions) { + frm.dimension_filters = this.accounting_dimensions; } if (this.accounting_dimensions) { this.accounting_dimensions.forEach((dimension) => { - frappe.model.with_doctype(dimension['document_type'], () => { + frappe.model.with_doctype(dimension["document_type"], () => { let parent_fields = []; frappe.meta.get_docfields(doctype).forEach((df) => { - if (df.fieldtype === 'Link' && df.options === 'Account') { + if (df.fieldtype === "Link" && df.options === "Account") { parent_fields.push(df.fieldname); - } else if (df.fieldtype === 'Table') { - this.setup_child_filters(frm, df.options, df.fieldname, dimension['fieldname']); + } else if (df.fieldtype === "Table") { + this.setup_child_filters(frm, df.options, df.fieldname, dimension["fieldname"]); } - if (frappe.meta.has_field(doctype, dimension['fieldname'])) { - this.setup_account_filters(frm, dimension['fieldname'], parent_fields); + if (frappe.meta.has_field(doctype, dimension["fieldname"])) { + this.setup_account_filters(frm, dimension["fieldname"], parent_fields); } }); }); @@ -55,12 +57,12 @@ erpnext.accounts.dimensions = { if (frappe.meta.has_field(doctype, dimension)) { frappe.model.with_doctype(doctype, () => { frappe.meta.get_docfields(doctype).forEach((df) => { - if (df.fieldtype === 'Link' && df.options === 'Account') { + if (df.fieldtype === "Link" && df.options === "Account") { fields.push(df.fieldname); } }); - frm.set_query(dimension, parentfield, function(doc, cdt, cdn) { + frm.set_query(dimension, parentfield, function (doc, cdt, cdn) { let row = locals[cdt][cdn]; return erpnext.queries.get_filtered_dimensions(row, fields, dimension, doc.company); }); @@ -69,7 +71,7 @@ erpnext.accounts.dimensions = { }, setup_account_filters(frm, dimension, fields) { - frm.set_query(dimension, function(doc) { + frm.set_query(dimension, function (doc) { return erpnext.queries.get_filtered_dimensions(doc, fields, dimension, doc.company); }); }, @@ -78,18 +80,26 @@ erpnext.accounts.dimensions = { if (this.accounting_dimensions) { this.accounting_dimensions.forEach((dimension) => { if (frm.is_new()) { - if (frm.doc.company && Object.keys(this.default_dimensions || {}).length > 0 - && this.default_dimensions[frm.doc.company]) { - - let default_dimension = this.default_dimensions[frm.doc.company][dimension['fieldname']]; + if ( + frm.doc.company && + Object.keys(this.default_dimensions || {}).length > 0 && + this.default_dimensions[frm.doc.company] + ) { + let default_dimension = + this.default_dimensions[frm.doc.company][dimension["fieldname"]]; if (default_dimension) { - if (frappe.meta.has_field(doctype, dimension['fieldname'])) { - frm.set_value(dimension['fieldname'], default_dimension); + if (frappe.meta.has_field(doctype, dimension["fieldname"])) { + frm.set_value(dimension["fieldname"], default_dimension); } - $.each(frm.doc.items || frm.doc.accounts || [], function(i, row) { - frappe.model.set_value(row.doctype, row.name, dimension['fieldname'], default_dimension); + $.each(frm.doc.items || frm.doc.accounts || [], function (i, row) { + frappe.model.set_value( + row.doctype, + row.name, + dimension["fieldname"], + default_dimension + ); }); } } @@ -102,8 +112,8 @@ erpnext.accounts.dimensions = { if (frappe.meta.has_field(frm.doctype, fieldname) && this.accounting_dimensions) { this.accounting_dimensions.forEach((dimension) => { let row = frappe.get_doc(cdt, cdn); - frm.script_manager.copy_from_first_row(fieldname, row, [dimension['fieldname']]); + frm.script_manager.copy_from_first_row(fieldname, row, [dimension["fieldname"]]); }); } - } + }, }; diff --git a/erpnext/public/js/utils/item_quick_entry.js b/erpnext/public/js/utils/item_quick_entry.js index 7e0198d33b3..42c60870e0d 100644 --- a/erpnext/public/js/utils/item_quick_entry.js +++ b/erpnext/public/js/utils/item_quick_entry.js @@ -1,4 +1,4 @@ -frappe.provide('frappe.ui.form'); +frappe.provide("frappe.ui.form"); frappe.ui.form.ItemQuickEntryForm = class ItemQuickEntryForm extends frappe.ui.form.QuickEntryForm { constructor(doctype, after_insert) { @@ -12,12 +12,14 @@ frappe.ui.form.ItemQuickEntryForm = class ItemQuickEntryForm extends frappe.ui.f super.render_dialog(); this.init_post_render_dialog_operations(); this.preset_fields_for_template(); - this.dialog.$wrapper.find('.edit-full').text(__('Edit in full page for more options like assets, serial nos, batches etc.')) + this.dialog.$wrapper + .find(".edit-full") + .text(__("Edit in full page for more options like assets, serial nos, batches etc.")); } check_naming_series_based_on() { if (frappe.defaults.get_default("item_naming_by") === "Naming Series") { - this.mandatory = this.mandatory.filter(d => d.fieldname !== "item_code"); + this.mandatory = this.mandatory.filter((d) => d.fieldname !== "item_code"); } } @@ -33,7 +35,7 @@ frappe.ui.form.ItemQuickEntryForm = class ItemQuickEntryForm extends frappe.ui.f register_primary_action() { var me = this; - this.dialog.set_primary_action(__('Save'), function() { + this.dialog.set_primary_action(__("Save"), function () { if (me.dialog.working) return; var data = me.dialog.get_values(); @@ -52,8 +54,8 @@ frappe.ui.form.ItemQuickEntryForm = class ItemQuickEntryForm extends frappe.ui.f me.dialog.working = true; var values = me.update_doc(); //patch for manufacturer type variants as extend is overwriting it. - if (variant_values['variant_based_on'] == "Manufacturer") { - values['variant_based_on'] = "Manufacturer"; + if (variant_values["variant_based_on"] == "Manufacturer") { + values["variant_based_on"] = "Manufacturer"; } $.extend(variant_values, values); me.insert(variant_values); @@ -63,13 +65,13 @@ frappe.ui.form.ItemQuickEntryForm = class ItemQuickEntryForm extends frappe.ui.f insert(variant_values) { let me = this; - return new Promise(resolve => { + return new Promise((resolve) => { frappe.call({ method: "frappe.client.insert", args: { - doc: variant_values + doc: variant_values, }, - callback: function(r) { + callback: function (r) { me.dialog.hide(); // delete the old doc frappe.model.clear_doc(me.dialog.doc.doctype, me.dialog.doc.name); @@ -84,14 +86,14 @@ frappe.ui.form.ItemQuickEntryForm = class ItemQuickEntryForm extends frappe.ui.f } } }, - error: function() { + error: function () { me.open_doc(); }, - always: function() { + always: function () { me.dialog.working = false; resolve(me.dialog.doc); }, - freeze: true + freeze: true, }); }); } @@ -101,60 +103,66 @@ frappe.ui.form.ItemQuickEntryForm = class ItemQuickEntryForm extends frappe.ui.f this.update_doc(); if (this.dialog.fields_dict.create_variant.$input.prop("checked")) { var template = this.dialog.fields_dict.item_template.input.value; - if (template) - frappe.set_route("Form", this.doctype, template); + if (template) frappe.set_route("Form", this.doctype, template); } else { - frappe.set_route('Form', this.doctype, this.doc.name); + frappe.set_route("Form", this.doctype, this.doc.name); } } get_variant_fields() { - var variant_fields = [{ - fieldname: "create_variant", - fieldtype: "Check", - label: __("Create Variant") - }, - { - fieldname: 'item_template', - label: __('Item Template'), - reqd: 0, - fieldtype: 'Link', - options: "Item", - get_query: function() { - return { - filters: { - "has_variants": 1 - } - }; - } - }]; + var variant_fields = [ + { + fieldname: "create_variant", + fieldtype: "Check", + label: __("Create Variant"), + }, + { + fieldname: "item_template", + label: __("Item Template"), + reqd: 0, + fieldtype: "Link", + options: "Item", + get_query: function () { + return { + filters: { + has_variants: 1, + }, + }; + }, + }, + ]; return variant_fields; } get_manufacturing_fields() { - this.manufacturer_fields = [{ - fieldtype: 'Link', - options: 'Manufacturer', - label: 'Manufacturer', - fieldname: "manufacturer", - hidden: 1, - reqd: 0 - }, { - fieldtype: 'Data', - label: 'Manufacturer Part Number', - fieldname: 'manufacturer_part_no', - hidden: 1, - reqd: 0 - }]; + this.manufacturer_fields = [ + { + fieldtype: "Link", + options: "Manufacturer", + label: "Manufacturer", + fieldname: "manufacturer", + hidden: 1, + reqd: 0, + }, + { + fieldtype: "Data", + label: "Manufacturer Part Number", + fieldname: "manufacturer_part_no", + hidden: 1, + reqd: 0, + }, + ]; return this.manufacturer_fields; } get_attributes_fields() { - var attribute_fields = [{ - fieldname: 'attribute_html', - fieldtype: 'HTML' - }] + var attribute_fields = [ + { + fieldname: "attribute_html", + fieldtype: "HTML", + }, + ]; attribute_fields = attribute_fields.concat(this.get_manufacturing_fields()); return attribute_fields; @@ -163,38 +171,37 @@ frappe.ui.form.ItemQuickEntryForm = class ItemQuickEntryForm extends frappe.ui.f init_for_create_variant_trigger() { var me = this; - this.dialog.fields_dict.create_variant.$input.on("click", function() { + this.dialog.fields_dict.create_variant.$input.on("click", function () { me.preset_fields_for_template(); me.init_post_template_trigger_operations(false, [], true); }); } preset_fields_for_template() { - var for_variant = this.dialog.get_value('create_variant'); + var for_variant = this.dialog.get_value("create_variant"); // setup template field, seen and mandatory if variant let template_field = this.dialog.get_field("item_template"); template_field.df.reqd = for_variant; - template_field.set_value(''); + template_field.set_value(""); template_field.df.hidden = !for_variant; template_field.refresh(); // hide properties for variant - ['item_code', 'item_name', 'item_group', 'stock_uom'].forEach((d) => { + ["item_code", "item_name", "item_group", "stock_uom"].forEach((d) => { let f = this.dialog.get_field(d); f.df.hidden = for_variant; f.refresh(); }); - this.dialog.get_field('attribute_html').toggle(false); + this.dialog.get_field("attribute_html").toggle(false); // non mandatory for variants - ['item_code', 'stock_uom', 'item_group'].forEach((d) => { + ["item_code", "stock_uom", "item_group"].forEach((d) => { let f = this.dialog.get_field(d); f.df.reqd = !for_variant; f.refresh(); }); - } init_for_item_template_trigger() { @@ -208,26 +215,29 @@ frappe.ui.form.ItemQuickEntryForm = class ItemQuickEntryForm extends frappe.ui.f method: "frappe.client.get", args: { doctype: "Item", - name: template + name: template, }, - callback: function(r) { + callback: function (r) { me.template_doc = r.message; me.is_manufacturer = false; if (me.template_doc.variant_based_on === "Manufacturer") { me.init_post_template_trigger_operations(true, [], true); } else { - - me.init_post_template_trigger_operations(false, me.template_doc.attributes, false); + me.init_post_template_trigger_operations( + false, + me.template_doc.attributes, + false + ); me.render_attributes(me.template_doc.attributes); } - } + }, }); } else { - me.dialog.get_field('attribute_html').toggle(false); + me.dialog.get_field("attribute_html").toggle(false); me.init_post_template_trigger_operations(false, [], true); } - } + }; } init_post_template_trigger_operations(is_manufacturer, attributes, attributes_flag) { @@ -238,15 +248,20 @@ frappe.ui.form.ItemQuickEntryForm = class ItemQuickEntryForm extends frappe.ui.f this.dialog.fields_dict.attribute_html.$wrapper.find(".attributes").empty(); this.is_manufacturer = is_manufacturer; this.toggle_manufacturer_fields(); - this.dialog.fields_dict.attribute_html.$wrapper.find(".attributes").toggleClass("hide-control", attributes_flag); - this.dialog.fields_dict.attribute_html.$wrapper.find(".attributes-header").toggleClass("hide-control", attributes_flag); + this.dialog.fields_dict.attribute_html.$wrapper + .find(".attributes") + .toggleClass("hide-control", attributes_flag); + this.dialog.fields_dict.attribute_html.$wrapper + .find(".attributes-header") + .toggleClass("hide-control", attributes_flag); } toggle_manufacturer_fields() { var me = this; - $.each(this.manufacturer_fields, function(i, dialog_field) { + $.each(this.manufacturer_fields, function (i, dialog_field) { me.dialog.get_field(dialog_field.fieldname).df.hidden = !me.is_manufacturer; - me.dialog.get_field(dialog_field.fieldname).df.reqd = dialog_field.fieldname == 'manufacturer' ? me.is_manufacturer : false; + me.dialog.get_field(dialog_field.fieldname).df.reqd = + dialog_field.fieldname == "manufacturer" ? me.is_manufacturer : false; me.dialog.get_field(dialog_field.fieldname).refresh(); }); } @@ -259,35 +274,48 @@ frappe.ui.form.ItemQuickEntryForm = class ItemQuickEntryForm extends frappe.ui.f render_attributes(attributes) { var me = this; - this.dialog.get_field('attribute_html').toggle(true); + this.dialog.get_field("attribute_html").toggle(true); - $.each(attributes, function(index, row) { + $.each(attributes, function (index, row) { var desc = ""; var fieldtype = "Data"; if (row.numeric_values) { fieldtype = "Float"; - desc = "Min Value: " + row.from_range + " , Max Value: " + row.to_range + ", in Increments of: " + row.increment; + desc = + "Min Value: " + + row.from_range + + " , Max Value: " + + row.to_range + + ", in Increments of: " + + row.increment; } me.init_make_control(fieldtype, row); me[row.attribute].set_value(me.attribute_values[row.attribute] || ""); - me[row.attribute].$wrapper.toggleClass("has-error", me.attribute_values[row.attribute] ? false : true); + me[row.attribute].$wrapper.toggleClass( + "has-error", + me.attribute_values[row.attribute] ? false : true + ); // Set Label explicitly as make_control is not displaying label $(me[row.attribute].label_area).text(__(row.attribute)); if (desc) { - $(repl(``, { - "desc": desc - })).insertAfter(me[row.attribute].input_area); + $( + repl(``, { + desc: desc, + }) + ).insertAfter(me[row.attribute].input_area); } if (!row.numeric_values) { me.init_awesomplete_for_attribute(row); } else { - me[row.attribute].$input.on("change", function() { + me[row.attribute].$input.on("change", function () { me.attribute_values[row.attribute] = $(this).val(); - $(this).closest(".frappe-control").toggleClass("has-error", $(this).val() ? false : true); + $(this) + .closest(".frappe-control") + .toggleClass("has-error", $(this).val() ? false : true); }); } }); @@ -296,13 +324,13 @@ frappe.ui.form.ItemQuickEntryForm = class ItemQuickEntryForm extends frappe.ui.f init_make_control(fieldtype, row) { this[row.attribute] = frappe.ui.form.make_control({ df: { - "fieldtype": fieldtype, - "label": row.attribute, - "fieldname": row.attribute, - "options": row.options || "" + fieldtype: fieldtype, + label: row.attribute, + fieldname: row.attribute, + options: row.options || "", }, parent: $(this.dialog.fields_dict.attribute_html.wrapper).find(".attributes"), - only_input: false + only_input: false, }); this[row.attribute].make_input(); } @@ -317,32 +345,37 @@ frappe.ui.form.ItemQuickEntryForm = class ItemQuickEntryForm extends frappe.ui.f list: [], }); - this[row.attribute].$input.on('input', function(e) { - frappe.call({ - method: "frappe.client.get_list", - args: { - doctype: "Item Attribute Value", - filters: [ - ["parent", "=", $(e.target).attr("data-fieldname")], - ["attribute_value", "like", e.target.value + "%"] - ], - fields: ["attribute_value"], - parent: "Item Attribute" - }, - callback: function(r) { - if (r.message) { - e.target.awesomplete.list = r.message.map(function(d) { - return d.attribute_value; - }); - } - } + this[row.attribute].$input + .on("input", function (e) { + frappe.call({ + method: "frappe.client.get_list", + args: { + doctype: "Item Attribute Value", + filters: [ + ["parent", "=", $(e.target).attr("data-fieldname")], + ["attribute_value", "like", e.target.value + "%"], + ], + fields: ["attribute_value"], + parent: "Item Attribute", + }, + callback: function (r) { + if (r.message) { + e.target.awesomplete.list = r.message.map(function (d) { + return d.attribute_value; + }); + } + }, + }); + }) + .on("focus", function (e) { + $(e.target).val("").trigger("input"); + }) + .on("awesomplete-close", function (e) { + me.attribute_values[$(e.target).attr("data-fieldname")] = e.target.value; + $(e.target) + .closest(".frappe-control") + .toggleClass("has-error", e.target.value ? false : true); }); - }).on('focus', function(e) { - $(e.target).val('').trigger('input'); - }).on("awesomplete-close", function (e) { - me.attribute_values[$(e.target).attr("data-fieldname")] = e.target.value; - $(e.target).closest(".frappe-control").toggleClass("has-error", e.target.value ? false : true); - }); } get_variant_doc() { @@ -354,31 +387,38 @@ frappe.ui.form.ItemQuickEntryForm = class ItemQuickEntryForm extends frappe.ui.f frappe.call({ method: "erpnext.controllers.item_variant.create_variant_doc_for_quick_entry", args: { - "template": me.dialog.fields_dict.item_template.$input.val(), - args: attribute + template: me.dialog.fields_dict.item_template.$input.val(), + args: attribute, }, async: false, - callback: function(r) { + callback: function (r) { if (Object.prototype.toString.call(r.message) == "[object Object]") { variant_doc = r.message; } else { - var msgprint_dialog = frappe.msgprint(__("Item Variant {0} already exists with same attributes", [repl('%(item)s', { - item: r.message - })])); + var msgprint_dialog = frappe.msgprint( + __("Item Variant {0} already exists with same attributes", [ + repl( + '%(item)s', + { + item: r.message, + } + ), + ]) + ); - msgprint_dialog.$wrapper.find(".variant-click").on("click", function() { + msgprint_dialog.$wrapper.find(".variant-click").on("click", function () { msgprint_dialog.hide(); me.dialog.hide(); if (frappe._from_link) { frappe._from_link.set_value($(this).attr("data-item-code")); } else { - frappe.set_route('Form', "Item", $(this).attr("data-item-code")); + frappe.set_route("Form", "Item", $(this).attr("data-item-code")); } }); } - } - }) + }, + }); } return variant_doc; } @@ -388,17 +428,17 @@ frappe.ui.form.ItemQuickEntryForm = class ItemQuickEntryForm extends frappe.ui.f var attribute = {}; var mandatory = []; - $.each(this.attributes, function(index, attr) { + $.each(this.attributes, function (index, attr) { var value = me.attribute_values[attr.attribute] || ""; if (value) { attribute[attr.attribute] = attr.numeric_values ? flt(value) : value; } else { mandatory.push(attr.attribute); } - }) + }); if (this.is_manufacturer) { - $.each(this.manufacturer_fields, function(index, field) { + $.each(this.manufacturer_fields, function (index, field) { attribute[field.fieldname] = me.dialog.fields_dict[field.fieldname].input.value; }); } diff --git a/erpnext/public/js/utils/item_selector.js b/erpnext/public/js/utils/item_selector.js index 9fc264086a3..5d2e915022e 100644 --- a/erpnext/public/js/utils/item_selector.js +++ b/erpnext/public/js/utils/item_selector.js @@ -3,7 +3,7 @@ erpnext.ItemSelector = class ItemSelector { $.extend(this, opts); if (!this.item_field) { - this.item_field = 'item_code'; + this.item_field = "item_code"; } if (!this.item_query) { @@ -16,39 +16,43 @@ erpnext.ItemSelector = class ItemSelector { setup() { var me = this; - if(!this.grid.add_items_button) { - this.grid.add_items_button = this.grid.add_custom_button(__('Add Items'), function() { - if(!me.dialog) { + if (!this.grid.add_items_button) { + this.grid.add_items_button = this.grid.add_custom_button(__("Add Items"), function () { + if (!me.dialog) { me.make_dialog(); } me.dialog.show(); me.render_items(); - setTimeout(function() { me.dialog.input.focus(); }, 1000); + setTimeout(function () { + me.dialog.input.focus(); + }, 1000); }); } } make_dialog() { this.dialog = new frappe.ui.Dialog({ - title: __('Add Items') + title: __("Add Items"), }); var body = $(this.dialog.body); - body.html('

              \ -
              '); + body.html( + '

              \ +
              ' + ); - this.dialog.input = body.find('.form-control'); - this.dialog.results = body.find('.results'); + this.dialog.input = body.find(".form-control"); + this.dialog.results = body.find(".results"); var me = this; - this.dialog.results.on('click', '.image-view-item', function() { - me.add_item($(this).attr('data-name')); + this.dialog.results.on("click", ".image-view-item", function () { + me.add_item($(this).attr("data-name")); }); - this.dialog.input.on('keyup', function() { - if(me.timeout_id) { + this.dialog.input.on("keyup", function () { + if (me.timeout_id) { clearTimeout(me.timeout_id); } - me.timeout_id = setTimeout(function() { + me.timeout_id = setTimeout(function () { me.render_items(); me.timeout_id = undefined; }, 500); @@ -61,33 +65,34 @@ erpnext.ItemSelector = class ItemSelector { // find row with item if exists $.each(this.frm.doc.items || [], (i, d) => { - if(d[this.item_field]===item_code) { - frappe.model.set_value(d.doctype, d.name, 'qty', d.qty + 1); - frappe.show_alert({message: __("Added {0} ({1})", [item_code, d.qty]), indicator: 'green'}); + if (d[this.item_field] === item_code) { + frappe.model.set_value(d.doctype, d.name, "qty", d.qty + 1); + frappe.show_alert({ message: __("Added {0} ({1})", [item_code, d.qty]), indicator: "green" }); added = true; return false; } }); - if(!added) { + if (!added) { var d = null; frappe.run_serially([ - () => { d = this.grid.add_new_row(); }, + () => { + d = this.grid.add_new_row(); + }, () => frappe.model.set_value(d.doctype, d.name, this.item_field, item_code), () => frappe.timeout(0.1), () => { - frappe.model.set_value(d.doctype, d.name, 'qty', 1); - frappe.show_alert({message: __("Added {0} ({1})", [item_code, 1]), indicator: 'green'}); - } + frappe.model.set_value(d.doctype, d.name, "qty", 1); + frappe.show_alert({ message: __("Added {0} ({1})", [item_code, 1]), indicator: "green" }); + }, ]); } - } render_items() { let args = { query: this.item_query, - filters: {} + filters: {}, }; args.txt = this.dialog.input.val(); args.as_dict = 1; @@ -97,14 +102,14 @@ erpnext.ItemSelector = class ItemSelector { } var me = this; - frappe.link_search("Item", args, function(r) { - $.each(r.values, function(i, d) { - if(!d.image) { + frappe.link_search("Item", args, function (r) { + $.each(r.values, function (i, d) { + if (!d.image) { d.abbr = frappe.get_abbr(d.item_name); d.color = frappe.get_palette(d.item_name); } }); - me.dialog.results.html(frappe.render_template('item_selector', {'data':r.values})); + me.dialog.results.html(frappe.render_template("item_selector", { data: r.values })); }); } }; diff --git a/erpnext/public/js/utils/party.js b/erpnext/public/js/utils/party.js index cba615c0d22..801376b2ed7 100644 --- a/erpnext/public/js/utils/party.js +++ b/erpnext/public/js/utils/party.js @@ -3,18 +3,19 @@ frappe.provide("erpnext.utils"); -const SALES_DOCTYPES = ['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice']; -const PURCHASE_DOCTYPES = ['Supplier Quotation','Purchase Order', 'Purchase Receipt', 'Purchase Invoice']; +const SALES_DOCTYPES = ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice"]; +const PURCHASE_DOCTYPES = ["Supplier Quotation", "Purchase Order", "Purchase Receipt", "Purchase Invoice"]; -erpnext.utils.get_party_details = function(frm, method, args, callback) { +erpnext.utils.get_party_details = function (frm, method, args, callback) { if (!method) { method = "erpnext.accounts.party.get_party_details"; } if (!args) { - if ((frm.doctype != "Purchase Order" && frm.doc.customer) - || (frm.doc.party_name && in_list(['Quotation', 'Opportunity'], frm.doc.doctype))) { - + if ( + (frm.doctype != "Purchase Order" && frm.doc.customer) || + (frm.doc.party_name && in_list(["Quotation", "Opportunity"], frm.doc.doctype)) + ) { let party_type = "Customer"; if (frm.doc.quotation_to && in_list(["Lead", "Prospect"], frm.doc.quotation_to)) { party_type = frm.doc.quotation_to; @@ -23,14 +24,14 @@ erpnext.utils.get_party_details = function(frm, method, args, callback) { args = { party: frm.doc.customer || frm.doc.party_name, party_type: party_type, - price_list: frm.doc.selling_price_list + price_list: frm.doc.selling_price_list, }; } else if (frm.doc.supplier) { args = { party: frm.doc.supplier, party_type: "Supplier", bill_date: frm.doc.bill_date, - price_list: frm.doc.buying_price_list + price_list: frm.doc.buying_price_list, }; } @@ -38,14 +39,14 @@ erpnext.utils.get_party_details = function(frm, method, args, callback) { if (in_list(SALES_DOCTYPES, frm.doc.doctype)) { args = { party: frm.doc.customer || frm.doc.party_name, - party_type: 'Customer' + party_type: "Customer", }; } if (in_list(PURCHASE_DOCTYPES, frm.doc.doctype)) { args = { party: frm.doc.supplier, - party_type: 'Supplier' + party_type: "Supplier", }; } } @@ -72,13 +73,26 @@ erpnext.utils.get_party_details = function(frm, method, args, callback) { } } - if (frappe.meta.get_docfield(frm.doc.doctype, "taxes")) { - if (!erpnext.utils.validate_mandatory(frm, "Posting / Transaction Date", - args.posting_date, args.party_type=="Customer" ? "customer": "supplier")) return; + if ( + !erpnext.utils.validate_mandatory( + frm, + "Posting / Transaction Date", + args.posting_date, + args.party_type == "Customer" ? "customer" : "supplier" + ) + ) + return; } - if (!erpnext.utils.validate_mandatory(frm, "Company", frm.doc.company, args.party_type=="Customer" ? "customer": "supplier")) { + if ( + !erpnext.utils.validate_mandatory( + frm, + "Company", + frm.doc.company, + args.party_type == "Customer" ? "customer" : "supplier" + ) + ) { return; } @@ -88,7 +102,7 @@ erpnext.utils.get_party_details = function(frm, method, args, callback) { frappe.call({ method: method, args: args, - callback: function(r) { + callback: function (r) { if (r.message) { frm.supplier_tds = r.message.supplier_tds; frm.updating_party_details = true; @@ -99,28 +113,28 @@ erpnext.utils.get_party_details = function(frm, method, args, callback) { if (callback) callback(); frm.refresh(); erpnext.utils.add_item(frm); - } + }, ]); } - } + }, }); -} +}; -erpnext.utils.add_item = function(frm) { +erpnext.utils.add_item = function (frm) { if (frm.is_new()) { var prev_route = frappe.get_prev_route(); - if (prev_route[1]==='Item' && !(frm.doc.items && frm.doc.items.length)) { + if (prev_route[1] === "Item" && !(frm.doc.items && frm.doc.items.length)) { // add row - var item = frm.add_child('items'); - frm.refresh_field('items'); + var item = frm.add_child("items"); + frm.refresh_field("items"); // set item - frappe.model.set_value(item.doctype, item.name, 'item_code', prev_route[2]); + frappe.model.set_value(item.doctype, item.name, "item_code", prev_route[2]); } } -} +}; -erpnext.utils.get_address_display = function(frm, address_field, display_field, is_your_company_address) { +erpnext.utils.get_address_display = function (frm, address_field, display_field, is_your_company_address) { if (frm.updating_party_details) return; if (!address_field) { @@ -135,29 +149,46 @@ erpnext.utils.get_address_display = function(frm, address_field, display_field, if (frm.doc[address_field]) { frappe.call({ method: "frappe.contacts.doctype.address.address.get_address_display", - args: {"address_dict": frm.doc[address_field] }, - callback: function(r) { + args: { address_dict: frm.doc[address_field] }, + callback: function (r) { if (r.message) { - frm.set_value(display_field, r.message) + frm.set_value(display_field, r.message); } - } - }) + }, + }); } else { - frm.set_value(display_field, ''); + frm.set_value(display_field, ""); } }; -erpnext.utils.set_taxes_from_address = function(frm, triggered_from_field, billing_address_field, shipping_address_field) { +erpnext.utils.set_taxes_from_address = function ( + frm, + triggered_from_field, + billing_address_field, + shipping_address_field +) { if (frm.updating_party_details) return; if (frappe.meta.get_docfield(frm.doc.doctype, "taxes")) { - if (!erpnext.utils.validate_mandatory(frm, "Lead / Customer / Supplier", - frm.doc.customer || frm.doc.supplier || frm.doc.lead || frm.doc.party_name, triggered_from_field)) { + if ( + !erpnext.utils.validate_mandatory( + frm, + "Lead / Customer / Supplier", + frm.doc.customer || frm.doc.supplier || frm.doc.lead || frm.doc.party_name, + triggered_from_field + ) + ) { return; } - if (!erpnext.utils.validate_mandatory(frm, "Posting / Transaction Date", - frm.doc.posting_date || frm.doc.transaction_date, triggered_from_field)) { + if ( + !erpnext.utils.validate_mandatory( + frm, + "Posting / Transaction Date", + frm.doc.posting_date || frm.doc.transaction_date, + triggered_from_field + ) + ) { return; } } else { @@ -167,35 +198,47 @@ erpnext.utils.set_taxes_from_address = function(frm, triggered_from_field, billi frappe.call({ method: "erpnext.accounts.party.get_address_tax_category", args: { - "tax_category": frm.doc.tax_category, - "billing_address": frm.doc[billing_address_field], - "shipping_address": frm.doc[shipping_address_field] + tax_category: frm.doc.tax_category, + billing_address: frm.doc[billing_address_field], + shipping_address: frm.doc[shipping_address_field], }, - callback: function(r) { - if (!r.exc){ + callback: function (r) { + if (!r.exc) { if (frm.doc.tax_category != r.message) { frm.set_value("tax_category", r.message); } else { erpnext.utils.set_taxes(frm, triggered_from_field); } } - } + }, }); }; -erpnext.utils.set_taxes = function(frm, triggered_from_field) { +erpnext.utils.set_taxes = function (frm, triggered_from_field) { if (frappe.meta.get_docfield(frm.doc.doctype, "taxes")) { if (!erpnext.utils.validate_mandatory(frm, "Company", frm.doc.company, triggered_from_field)) { return; } - if (!erpnext.utils.validate_mandatory(frm, "Lead / Customer / Supplier", - frm.doc.customer || frm.doc.supplier || frm.doc.lead || frm.doc.party_name, triggered_from_field)) { + if ( + !erpnext.utils.validate_mandatory( + frm, + "Lead / Customer / Supplier", + frm.doc.customer || frm.doc.supplier || frm.doc.lead || frm.doc.party_name, + triggered_from_field + ) + ) { return; } - if (!erpnext.utils.validate_mandatory(frm, "Posting / Transaction Date", - frm.doc.posting_date || frm.doc.transaction_date, triggered_from_field)) { + if ( + !erpnext.utils.validate_mandatory( + frm, + "Posting / Transaction Date", + frm.doc.posting_date || frm.doc.transaction_date, + triggered_from_field + ) + ) { return; } } else { @@ -204,15 +247,15 @@ erpnext.utils.set_taxes = function(frm, triggered_from_field) { var party_type, party; if (frm.doc.lead) { - party_type = 'Lead'; + party_type = "Lead"; party = frm.doc.lead; } else if (frm.doc.customer) { - party_type = 'Customer'; + party_type = "Customer"; party = frm.doc.customer; } else if (frm.doc.supplier) { - party_type = 'Supplier'; + party_type = "Supplier"; party = frm.doc.supplier; - } else if (frm.doc.quotation_to){ + } else if (frm.doc.quotation_to) { party_type = frm.doc.quotation_to; party = frm.doc.party_name; } @@ -224,21 +267,22 @@ erpnext.utils.set_taxes = function(frm, triggered_from_field) { frappe.call({ method: "erpnext.accounts.party.set_taxes", args: { - "party": party, - "party_type": party_type, - "posting_date": frm.doc.posting_date || frm.doc.transaction_date, - "company": frm.doc.company, - "customer_group": frm.doc.customer_group, - "supplier_group": frm.doc.supplier_group, - "tax_category": frm.doc.tax_category, - "billing_address": ((frm.doc.customer || frm.doc.lead) ? (frm.doc.customer_address) : (frm.doc.supplier_address)), - "shipping_address": frm.doc.shipping_address_name + party: party, + party_type: party_type, + posting_date: frm.doc.posting_date || frm.doc.transaction_date, + company: frm.doc.company, + customer_group: frm.doc.customer_group, + supplier_group: frm.doc.supplier_group, + tax_category: frm.doc.tax_category, + billing_address: + frm.doc.customer || frm.doc.lead ? frm.doc.customer_address : frm.doc.supplier_address, + shipping_address: frm.doc.shipping_address_name, }, - callback: function(r) { - if (r.message){ - frm.set_value("taxes_and_charges", r.message) + callback: function (r) { + if (r.message) { + frm.set_value("taxes_and_charges", r.message); } - } + }, }); }; @@ -266,20 +310,23 @@ erpnext.utils.get_contact_details = function (frm) { } }; -erpnext.utils.validate_mandatory = function(frm, label, value, trigger_on) { +erpnext.utils.validate_mandatory = function (frm, label, value, trigger_on) { if (!value) { frm.doc[trigger_on] = ""; refresh_field(trigger_on); - frappe.throw({message:__("Please enter {0} first", [label]), title:__("Mandatory")}); + frappe.throw({ message: __("Please enter {0} first", [label]), title: __("Mandatory") }); return false; } return true; -} +}; -erpnext.utils.get_shipping_address = function(frm, callback) { +erpnext.utils.get_shipping_address = function (frm, callback) { if (frm.doc.company) { - if ((frm.doc.inter_company_order_reference || frm.doc.internal_invoice_reference || - frm.doc.internal_order_reference)) { + if ( + frm.doc.inter_company_order_reference || + frm.doc.internal_invoice_reference || + frm.doc.internal_order_reference + ) { if (callback) { return callback(); } @@ -288,20 +335,20 @@ erpnext.utils.get_shipping_address = function(frm, callback) { method: "erpnext.accounts.custom.address.get_shipping_address", args: { company: frm.doc.company, - address: frm.doc.shipping_address + address: frm.doc.shipping_address, }, - callback: function(r) { + callback: function (r) { if (r.message) { - frm.set_value("shipping_address", r.message[0]) //Address title or name - frm.set_value("shipping_address_display", r.message[1]) //Address to be displayed on the page + frm.set_value("shipping_address", r.message[0]); //Address title or name + frm.set_value("shipping_address_display", r.message[1]); //Address to be displayed on the page } - if (callback){ + if (callback) { return callback(); } - } + }, }); } else { frappe.msgprint(__("Select company first")); } -} +}; diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js index 22120988ad0..6fd7f7f4588 100644 --- a/erpnext/public/js/utils/serial_no_batch_selector.js +++ b/erpnext/public/js/utils/serial_no_batch_selector.js @@ -1,15 +1,15 @@ - erpnext.SerialNoBatchSelector = class SerialNoBatchSelector { constructor(opts, show_dialog) { $.extend(this, opts); this.show_dialog = show_dialog; // frm, item, warehouse_details, has_batch, oldest let d = this.item; - this.has_batch = 0; this.has_serial_no = 0; + this.has_batch = 0; + this.has_serial_no = 0; if (d && d.has_batch_no && (!d.batch_no || this.show_dialog)) this.has_batch = 1; // !(this.show_dialog == false) ensures that show_dialog is implictly true, even when undefined - if(d && d.has_serial_no && !(this.show_dialog == false)) this.has_serial_no = 1; + if (d && d.has_serial_no && !(this.show_dialog == false)) this.has_serial_no = 1; this.setup(); } @@ -28,70 +28,70 @@ erpnext.SerialNoBatchSelector = class SerialNoBatchSelector { let title = ""; let fields = [ { - fieldname: 'item_code', + fieldname: "item_code", read_only: 1, - fieldtype:'Link', - options: 'Item', - label: __('Item Code'), - default: me.item_code + fieldtype: "Link", + options: "Item", + label: __("Item Code"), + default: me.item_code, }, { - fieldname: 'warehouse', - fieldtype:'Link', - options: 'Warehouse', + fieldname: "warehouse", + fieldtype: "Link", + options: "Warehouse", reqd: me.has_batch && !me.has_serial_no ? 0 : 1, label: __(me.warehouse_details.type), - default: typeof me.warehouse_details.name == "string" ? me.warehouse_details.name : '', - onchange: function(e) { + default: typeof me.warehouse_details.name == "string" ? me.warehouse_details.name : "", + onchange: function (e) { me.warehouse_details.name = this.get_value(); - if(me.has_batch && !me.has_serial_no) { + if (me.has_batch && !me.has_serial_no) { fields = fields.concat(me.get_batch_fields()); } else { fields = fields.concat(me.get_serial_no_fields()); } var batches = this.layout.fields_dict.batches; - if(batches) { + if (batches) { batches.grid.df.data = []; batches.grid.refresh(); batches.grid.add_new_row(null, null, null); } }, - get_query: function() { + get_query: function () { return { query: "erpnext.controllers.queries.warehouse_query", filters: [ ["Bin", "item_code", "=", me.item_code], ["Warehouse", "is_group", "=", 0], - ["Warehouse", "company", "=", me.frm.doc.company] - ] - } - } + ["Warehouse", "company", "=", me.frm.doc.company], + ], + }; + }, }, - {fieldtype:'Column Break'}, + { fieldtype: "Column Break" }, { - fieldname: 'qty', - fieldtype:'Float', + fieldname: "qty", + fieldtype: "Float", read_only: me.has_batch && !me.has_serial_no, - label: __(me.has_batch && !me.has_serial_no ? 'Selected Qty' : 'Qty'), + label: __(me.has_batch && !me.has_serial_no ? "Selected Qty" : "Qty"), default: flt(me.item.stock_qty) || flt(me.item.transfer_qty), }, ...get_pending_qty_fields(me), { - fieldname: 'uom', + fieldname: "uom", read_only: 1, - fieldtype: 'Link', - options: 'UOM', - label: __('UOM'), - default: me.item.uom + fieldtype: "Link", + options: "UOM", + label: __("UOM"), + default: me.item.uom, }, { - fieldname: 'auto_fetch_button', - fieldtype:'Button', + fieldname: "auto_fetch_button", + fieldtype: "Button", hidden: me.has_batch && !me.has_serial_no, - label: __('Auto Fetch'), - description: __('Fetch Serial Numbers based on FIFO'), + label: __("Auto Fetch"), + description: __("Fetch Serial Numbers based on FIFO"), click: () => { let qty = this.dialog.fields_dict.qty.get_value(); let already_selected_serial_nos = get_selected_serial_nos(me); @@ -100,11 +100,12 @@ erpnext.SerialNoBatchSelector = class SerialNoBatchSelector { args: { qty: qty, item_code: me.item_code, - warehouse: typeof me.warehouse_details.name == "string" ? me.warehouse_details.name : '', + warehouse: + typeof me.warehouse_details.name == "string" ? me.warehouse_details.name : "", batch_nos: me.item.batch_no || null, posting_date: me.frm.doc.posting_date || me.frm.doc.transaction_date, - exclude_sr_nos: already_selected_serial_nos - } + exclude_sr_nos: already_selected_serial_nos, + }, }); numbers.then((data) => { @@ -113,18 +114,23 @@ erpnext.SerialNoBatchSelector = class SerialNoBatchSelector { if (!records_length) { const warehouse = me.dialog.fields_dict.warehouse.get_value().bold(); frappe.msgprint( - __('Serial numbers unavailable for Item {0} under warehouse {1}. Please try changing warehouse.', [me.item.item_code.bold(), warehouse]) + __( + "Serial numbers unavailable for Item {0} under warehouse {1}. Please try changing warehouse.", + [me.item.item_code.bold(), warehouse] + ) ); } if (records_length < qty) { - frappe.msgprint(__('Fetched only {0} available serial numbers.', [records_length])); + frappe.msgprint( + __("Fetched only {0} available serial numbers.", [records_length]) + ); } let serial_no_list_field = this.dialog.fields_dict.serial_no; - numbers = auto_fetched_serial_numbers.join('\n'); + numbers = auto_fetched_serial_numbers.join("\n"); serial_no_list_field.set_value(numbers); }); - } - } + }, + }, ]; if (this.has_batch && !this.has_serial_no) { @@ -139,12 +145,12 @@ erpnext.SerialNoBatchSelector = class SerialNoBatchSelector { this.dialog = new frappe.ui.Dialog({ title: title, - fields: fields + fields: fields, }); - this.dialog.set_primary_action(__('Insert'), function() { + this.dialog.set_primary_action(__("Insert"), function () { me.values = me.dialog.get_values(); - if(me.validate()) { + if (me.validate()) { frappe.run_serially([ () => me.update_batch_items(), () => me.update_serial_no_item(), @@ -156,25 +162,25 @@ erpnext.SerialNoBatchSelector = class SerialNoBatchSelector { return me.callback(me.item); } }, - () => me.dialog.hide() - ]) + () => me.dialog.hide(), + ]); } }); - if(this.show_dialog) { + if (this.show_dialog) { let d = this.item; if (this.item.serial_no) { this.dialog.fields_dict.serial_no.set_value(this.item.serial_no); } if (this.has_batch && !this.has_serial_no && d.batch_no) { - this.frm.doc.items.forEach(data => { - if(data.item_code == d.item_code) { + this.frm.doc.items.forEach((data) => { + if (data.item_code == d.item_code) { this.dialog.fields_dict.batches.df.data.push({ - 'batch_no': data.batch_no, - 'actual_qty': data.actual_qty, - 'selected_qty': data.qty, - 'available_qty': data.actual_batch_qty + batch_no: data.batch_no, + actual_qty: data.actual_qty, + selected_qty: data.qty, + available_qty: data.actual_batch_qty, }); } }); @@ -191,33 +197,32 @@ erpnext.SerialNoBatchSelector = class SerialNoBatchSelector { } on_close_dialog() { - this.dialog.get_close_btn().on('click', () => { + this.dialog.get_close_btn().on("click", () => { this.on_close && this.on_close(this.item); }); } validate() { let values = this.values; - if(!values.warehouse) { + if (!values.warehouse) { frappe.throw(__("Please select a warehouse")); return false; } - if(this.has_batch && !this.has_serial_no) { - if(values.batches.length === 0 || !values.batches) { + if (this.has_batch && !this.has_serial_no) { + if (values.batches.length === 0 || !values.batches) { frappe.throw(__("Please select batches for batched item {0}", [values.item_code])); } values.batches.map((batch, i) => { - if(!batch.selected_qty || batch.selected_qty === 0 ) { + if (!batch.selected_qty || batch.selected_qty === 0) { if (!this.show_dialog) { - frappe.throw(__("Please select quantity on row {0}", [i+1])); + frappe.throw(__("Please select quantity on row {0}", [i + 1])); } } }); return true; - } else { - let serial_nos = values.serial_no || ''; - if (!serial_nos || !serial_nos.replace(/\s/g, '').length) { + let serial_nos = values.serial_no || ""; + if (!serial_nos || !serial_nos.replace(/\s/g, "").length) { frappe.throw(__("Please enter serial numbers for serialized item {0}", [values.item_code])); } return true; @@ -226,87 +231,92 @@ erpnext.SerialNoBatchSelector = class SerialNoBatchSelector { update_batch_items() { // clones an items if muliple batches are selected. - if(this.has_batch && !this.has_serial_no) { + if (this.has_batch && !this.has_serial_no) { this.values.batches.map((batch, i) => { let batch_no = batch.batch_no; - let row = ''; + let row = ""; if (i !== 0 && !this.batch_exists(batch_no)) { row = this.frm.add_child("items", { ...this.item }); } else { - row = this.frm.doc.items.find(i => i.batch_no === batch_no); + row = this.frm.doc.items.find((i) => i.batch_no === batch_no); } if (!row) { row = this.item; } // this ensures that qty & batch no is set - this.map_row_values(row, batch, 'batch_no', - 'selected_qty', this.values.warehouse); + this.map_row_values(row, batch, "batch_no", "selected_qty", this.values.warehouse); }); } } update_serial_no_item() { // just updates serial no for the item - if(this.has_serial_no && !this.has_batch) { - this.map_row_values(this.item, this.values, 'serial_no', 'qty'); + if (this.has_serial_no && !this.has_batch) { + this.map_row_values(this.item, this.values, "serial_no", "qty"); } } update_batch_serial_no_items() { // if serial no selected is from different batches, adds new rows for each batch. - if(this.has_batch && this.has_serial_no) { - const selected_serial_nos = this.values.serial_no.split(/\n/g).filter(s => s); + if (this.has_batch && this.has_serial_no) { + const selected_serial_nos = this.values.serial_no.split(/\n/g).filter((s) => s); - return frappe.db.get_list("Serial No", { - filters: { 'name': ["in", selected_serial_nos]}, - fields: ["batch_no", "name"] - }).then((data) => { - // data = [{batch_no: 'batch-1', name: "SR-001"}, - // {batch_no: 'batch-2', name: "SR-003"}, {batch_no: 'batch-2', name: "SR-004"}] - const batch_serial_map = data.reduce((acc, d) => { - if (!acc[d['batch_no']]) acc[d['batch_no']] = []; - acc[d['batch_no']].push(d['name']) - return acc - }, {}) - // batch_serial_map = { "batch-1": ['SR-001'], "batch-2": ["SR-003", "SR-004"]} - Object.keys(batch_serial_map).map((batch_no, i) => { - let row = ''; - const serial_no = batch_serial_map[batch_no]; - if (i == 0) { - row = this.item; - this.map_row_values(row, {qty: serial_no.length, batch_no: batch_no}, 'batch_no', - 'qty', this.values.warehouse); - } else if (!this.batch_exists(batch_no)) { - row = this.frm.add_child("items", { ...this.item }); - row.batch_no = batch_no; - } else { - row = this.frm.doc.items.find(i => i.batch_no === batch_no); - } - const values = { - 'qty': serial_no.length, - 'serial_no': serial_no.join('\n') - } - this.map_row_values(row, values, 'serial_no', - 'qty', this.values.warehouse); + return frappe.db + .get_list("Serial No", { + filters: { name: ["in", selected_serial_nos] }, + fields: ["batch_no", "name"], + }) + .then((data) => { + // data = [{batch_no: 'batch-1', name: "SR-001"}, + // {batch_no: 'batch-2', name: "SR-003"}, {batch_no: 'batch-2', name: "SR-004"}] + const batch_serial_map = data.reduce((acc, d) => { + if (!acc[d["batch_no"]]) acc[d["batch_no"]] = []; + acc[d["batch_no"]].push(d["name"]); + return acc; + }, {}); + // batch_serial_map = { "batch-1": ['SR-001'], "batch-2": ["SR-003", "SR-004"]} + Object.keys(batch_serial_map).map((batch_no, i) => { + let row = ""; + const serial_no = batch_serial_map[batch_no]; + if (i == 0) { + row = this.item; + this.map_row_values( + row, + { qty: serial_no.length, batch_no: batch_no }, + "batch_no", + "qty", + this.values.warehouse + ); + } else if (!this.batch_exists(batch_no)) { + row = this.frm.add_child("items", { ...this.item }); + row.batch_no = batch_no; + } else { + row = this.frm.doc.items.find((i) => i.batch_no === batch_no); + } + const values = { + qty: serial_no.length, + serial_no: serial_no.join("\n"), + }; + this.map_row_values(row, values, "serial_no", "qty", this.values.warehouse); + }); }); - }) } } batch_exists(batch) { - const batches = this.frm.doc.items.map(data => data.batch_no); - return (batches && in_list(batches, batch)) ? true : false; + const batches = this.frm.doc.items.map((data) => data.batch_no); + return batches && in_list(batches, batch) ? true : false; } map_row_values(row, values, number, qty_field, warehouse) { row.qty = values[qty_field]; row.transfer_qty = flt(values[qty_field]) * flt(row.conversion_factor); row[number] = values[number]; - if(this.warehouse_details.type === 'Source Warehouse') { + if (this.warehouse_details.type === "Source Warehouse") { row.s_warehouse = values.warehouse || warehouse; - } else if(this.warehouse_details.type === 'Target Warehouse') { + } else if (this.warehouse_details.type === "Target Warehouse") { row.t_warehouse = values.warehouse || warehouse; } else { row.warehouse = values.warehouse || warehouse; @@ -319,7 +329,7 @@ erpnext.SerialNoBatchSelector = class SerialNoBatchSelector { let qty_field = this.dialog.fields_dict.qty; let total_qty = 0; - this.dialog.fields_dict.batches.df.data.forEach(data => { + this.dialog.fields_dict.batches.df.data.forEach((data) => { total_qty += flt(data.selected_qty); }); @@ -346,30 +356,35 @@ erpnext.SerialNoBatchSelector = class SerialNoBatchSelector { var me = this; return [ - {fieldtype:'Section Break', label: __('Batches')}, - {fieldname: 'batches', fieldtype: 'Table', label: __('Batch Entries'), + { fieldtype: "Section Break", label: __("Batches") }, + { + fieldname: "batches", + fieldtype: "Table", + label: __("Batch Entries"), fields: [ { - 'fieldtype': 'Link', - 'read_only': 0, - 'fieldname': 'batch_no', - 'options': 'Batch', - 'label': __('Select Batch'), - 'in_list_view': 1, + fieldtype: "Link", + read_only: 0, + fieldname: "batch_no", + options: "Batch", + label: __("Select Batch"), + in_list_view: 1, get_query: function () { return { filters: { item_code: me.item_code, - warehouse: me.warehouse || typeof me.warehouse_details.name == "string" ? me.warehouse_details.name : '' + warehouse: + me.warehouse || typeof me.warehouse_details.name == "string" + ? me.warehouse_details.name + : "", }, - query: 'erpnext.controllers.queries.get_batch_no' + query: "erpnext.controllers.queries.get_batch_no", }; }, change: function () { const batch_no = this.get_value(); if (!batch_no) { - this.grid_row.on_grid_fields_dict - .available_qty.set_value(0); + this.grid_row.on_grid_fields_dict.available_qty.set_value(0); return; } let selected_batches = this.grid.grid_rows.map((row) => { @@ -383,48 +398,48 @@ erpnext.SerialNoBatchSelector = class SerialNoBatchSelector { }); if (selected_batches.includes(batch_no)) { this.set_value(""); - frappe.throw(__('Batch {0} already selected.', [batch_no])); + frappe.throw(__("Batch {0} already selected.", [batch_no])); } if (me.warehouse_details.name) { frappe.call({ - method: 'erpnext.stock.doctype.batch.batch.get_batch_qty', + method: "erpnext.stock.doctype.batch.batch.get_batch_qty", args: { batch_no, warehouse: me.warehouse_details.name, - item_code: me.item_code + item_code: me.item_code, }, callback: (r) => { - this.grid_row.on_grid_fields_dict - .available_qty.set_value(r.message || 0); - } + this.grid_row.on_grid_fields_dict.available_qty.set_value( + r.message || 0 + ); + }, }); - } else { this.set_value(""); - frappe.throw(__('Please select a warehouse to get available quantities')); + frappe.throw(__("Please select a warehouse to get available quantities")); } // e.stopImmediatePropagation(); - } + }, }, { - 'fieldtype': 'Float', - 'read_only': 1, - 'fieldname': 'available_qty', - 'label': __('Available'), - 'in_list_view': 1, - 'default': 0, + fieldtype: "Float", + read_only: 1, + fieldname: "available_qty", + label: __("Available"), + in_list_view: 1, + default: 0, change: function () { - this.grid_row.on_grid_fields_dict.selected_qty.set_value('0'); - } + this.grid_row.on_grid_fields_dict.selected_qty.set_value("0"); + }, }, { - 'fieldtype': 'Float', - 'read_only': 0, - 'fieldname': 'selected_qty', - 'label': __('Qty'), - 'in_list_view': 1, - 'default': 0, + fieldtype: "Float", + read_only: 0, + fieldname: "selected_qty", + label: __("Qty"), + in_list_view: 1, + default: 0, change: function () { var batch_no = this.grid_row.on_grid_fields_dict.batch_no.get_value(); var available_qty = this.grid_row.on_grid_fields_dict.available_qty.get_value(); @@ -433,18 +448,23 @@ erpnext.SerialNoBatchSelector = class SerialNoBatchSelector { if (batch_no.length === 0 && parseInt(selected_qty) !== 0) { frappe.throw(__("Please select a batch")); } - if (me.warehouse_details.type === 'Source Warehouse' && - parseFloat(available_qty) < parseFloat(selected_qty)) { - - this.set_value('0'); - frappe.throw(__('For transfer from source, selected quantity cannot be greater than available quantity')); + if ( + me.warehouse_details.type === "Source Warehouse" && + parseFloat(available_qty) < parseFloat(selected_qty) + ) { + this.set_value("0"); + frappe.throw( + __( + "For transfer from source, selected quantity cannot be greater than available quantity" + ) + ); } else { this.grid.refresh(); } me.update_total_qty(); me.update_pending_qtys(); - } + }, }, ], in_place_edit: true, @@ -452,7 +472,7 @@ erpnext.SerialNoBatchSelector = class SerialNoBatchSelector { get_data: function () { return this.data; }, - } + }, ]; } @@ -462,43 +482,48 @@ erpnext.SerialNoBatchSelector = class SerialNoBatchSelector { let serial_no_filters = { item_code: me.item_code, - delivery_document_no: "" - } + delivery_document_no: "", + }; if (this.item.batch_no) { serial_no_filters["batch_no"] = this.item.batch_no; } if (me.warehouse_details.name) { - serial_no_filters['warehouse'] = me.warehouse_details.name; + serial_no_filters["warehouse"] = me.warehouse_details.name; } - if (me.frm.doc.doctype === 'POS Invoice' && !this.showing_reserved_serial_nos_error) { - frappe.call({ - method: "erpnext.stock.doctype.serial_no.serial_no.get_pos_reserved_serial_nos", - args: { - filters: { - item_code: me.item_code, - warehouse: typeof me.warehouse_details.name == "string" ? me.warehouse_details.name : '', - } - } - }).then((data) => { - serial_no_filters['name'] = ["not in", data.message[0]] - }) + if (me.frm.doc.doctype === "POS Invoice" && !this.showing_reserved_serial_nos_error) { + frappe + .call({ + method: "erpnext.stock.doctype.serial_no.serial_no.get_pos_reserved_serial_nos", + args: { + filters: { + item_code: me.item_code, + warehouse: + typeof me.warehouse_details.name == "string" ? me.warehouse_details.name : "", + }, + }, + }) + .then((data) => { + serial_no_filters["name"] = ["not in", data.message[0]]; + }); } return [ - {fieldtype: 'Section Break', label: __('Serial Numbers')}, + { fieldtype: "Section Break", label: __("Serial Numbers") }, { - fieldtype: 'Link', fieldname: 'serial_no_select', options: 'Serial No', - label: __('Select to add Serial Number.'), - get_query: function() { + fieldtype: "Link", + fieldname: "serial_no_select", + options: "Serial No", + label: __("Select to add Serial Number."), + get_query: function () { return { - filters: serial_no_filters + filters: serial_no_filters, }; }, - onchange: function(e) { - if(this.in_local_change) return; + onchange: function (e) { + if (this.in_local_change) return; this.in_local_change = 1; let serial_no_list_field = this.layout.fields_dict.serial_no; @@ -506,22 +531,22 @@ erpnext.SerialNoBatchSelector = class SerialNoBatchSelector { let new_number = this.get_value(); let list_value = serial_no_list_field.get_value(); - let new_line = '\n'; - if(!list_value) { - new_line = ''; + let new_line = "\n"; + if (!list_value) { + new_line = ""; } else { me.serial_list = list_value.split(/\n/g) || []; } - if(!me.serial_list.includes(new_number)) { - this.set_new_description(''); - serial_no_list_field.set_value(me.serial_list.join('\n') + new_line + new_number); + if (!me.serial_list.includes(new_number)) { + this.set_new_description(""); + serial_no_list_field.set_value(me.serial_list.join("\n") + new_line + new_number); me.serial_list = serial_no_list_field.get_value().split(/\n/g) || []; } else { - this.set_new_description(new_number + ' is already selected.'); + this.set_new_description(new_number + " is already selected."); } - me.serial_list = me.serial_list.filter(serial => { + me.serial_list = me.serial_list.filter((serial) => { if (serial) { return true; } @@ -530,61 +555,68 @@ erpnext.SerialNoBatchSelector = class SerialNoBatchSelector { qty_field.set_input(me.serial_list.length); this.$input.val(""); this.in_local_change = 0; - } + }, }, - {fieldtype: 'Section Break'}, + { fieldtype: "Section Break" }, { - fieldname: 'serial_no', - fieldtype: 'Text', - label: __(me.has_batch && !me.has_serial_no ? 'Selected Batch Numbers' : 'Selected Serial Numbers'), - onchange: function() { + fieldname: "serial_no", + fieldtype: "Text", + label: __( + me.has_batch && !me.has_serial_no ? "Selected Batch Numbers" : "Selected Serial Numbers" + ), + onchange: function () { me.serial_list = this.get_value().split(/\n/g); - me.serial_list = me.serial_list.filter(serial => { + me.serial_list = me.serial_list.filter((serial) => { if (serial) { return true; } }); this.layout.fields_dict.qty.set_input(me.serial_list.length); - } - } + }, + }, ]; } }; function get_pending_qty_fields(me) { if (!check_can_calculate_pending_qty(me)) return []; - const { frm: { doc: { fg_completed_qty }}, item: { item_code, stock_qty }} = me; + const { + frm: { + doc: { fg_completed_qty }, + }, + item: { item_code, stock_qty }, + } = me; const { qty_consumed_per_unit } = erpnext.stock.bom.items[item_code]; const total_selected_qty = calc_total_selected_qty(me); const required_qty = flt(fg_completed_qty) * flt(qty_consumed_per_unit); const pending_qty = required_qty - (flt(stock_qty) + total_selected_qty); - const pending_qty_fields = [ - { fieldtype: 'Section Break', label: __('Pending Quantity') }, + const pending_qty_fields = [ + { fieldtype: "Section Break", label: __("Pending Quantity") }, { - fieldname: 'required_qty', + fieldname: "required_qty", read_only: 1, - fieldtype: 'Float', - label: __('Required Qty'), - default: required_qty + fieldtype: "Float", + label: __("Required Qty"), + default: required_qty, }, - { fieldtype: 'Column Break' }, + { fieldtype: "Column Break" }, { - fieldname: 'total_selected_qty', + fieldname: "total_selected_qty", read_only: 1, - fieldtype: 'Float', - label: __('Total Selected Qty'), - default: total_selected_qty + fieldtype: "Float", + label: __("Total Selected Qty"), + default: total_selected_qty, }, - { fieldtype: 'Column Break' }, + { fieldtype: "Column Break" }, { - fieldname: 'pending_qty', + fieldname: "pending_qty", read_only: 1, - fieldtype: 'Float', - label: __('Pending Qty'), - default: pending_qty + fieldtype: "Float", + label: __("Pending Qty"), + default: pending_qty, }, ]; return pending_qty_fields; @@ -592,37 +624,45 @@ function get_pending_qty_fields(me) { // get all items with same item code except row for which selector is open. function get_rows_with_same_item_code(me) { - const { frm: { doc: { items }}, item: { name, item_code }} = me; - return items.filter(item => (item.name !== name) && (item.item_code === item_code)) + const { + frm: { + doc: { items }, + }, + item: { name, item_code }, + } = me; + return items.filter((item) => item.name !== name && item.item_code === item_code); } function calc_total_selected_qty(me) { const totalSelectedQty = get_rows_with_same_item_code(me) - .map(item => flt(item.qty)) + .map((item) => flt(item.qty)) .reduce((i, j) => i + j, 0); return totalSelectedQty; } function get_selected_serial_nos(me) { const selected_serial_nos = get_rows_with_same_item_code(me) - .map(item => item.serial_no) - .filter(serial => serial) - .map(sr_no_string => sr_no_string.split('\n')) + .map((item) => item.serial_no) + .filter((serial) => serial) + .map((sr_no_string) => sr_no_string.split("\n")) .reduce((acc, arr) => acc.concat(arr), []) - .filter(serial => serial); + .filter((serial) => serial); return selected_serial_nos; -}; +} function check_can_calculate_pending_qty(me) { - const { frm: { doc }, item } = me; - const docChecks = doc.bom_no - && doc.fg_completed_qty - && erpnext.stock.bom - && erpnext.stock.bom.name === doc.bom_no; - const itemChecks = !!item - && !item.original_item - && erpnext.stock.bom && erpnext.stock.bom.items - && (item.item_code in erpnext.stock.bom.items); + const { + frm: { doc }, + item, + } = me; + const docChecks = + doc.bom_no && doc.fg_completed_qty && erpnext.stock.bom && erpnext.stock.bom.name === doc.bom_no; + const itemChecks = + !!item && + !item.original_item && + erpnext.stock.bom && + erpnext.stock.bom.items && + item.item_code in erpnext.stock.bom.items; return docChecks && itemChecks; } diff --git a/erpnext/public/js/utils/supplier_quick_entry.js b/erpnext/public/js/utils/supplier_quick_entry.js index 687b01454a2..968ef74c3a0 100644 --- a/erpnext/public/js/utils/supplier_quick_entry.js +++ b/erpnext/public/js/utils/supplier_quick_entry.js @@ -1,3 +1,3 @@ -frappe.provide('frappe.ui.form'); +frappe.provide("frappe.ui.form"); frappe.ui.form.SupplierQuickEntryForm = frappe.ui.form.ContactAddressQuickEntryForm; diff --git a/erpnext/public/js/utils/unreconcile.js b/erpnext/public/js/utils/unreconcile.js index 79490a162d3..6864e2865d3 100644 --- a/erpnext/public/js/utils/unreconcile.js +++ b/erpnext/public/js/utils/unreconcile.js @@ -1,27 +1,34 @@ -frappe.provide('erpnext.accounts'); +frappe.provide("erpnext.accounts"); 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")) - || !["Purchase Invoice", "Sales Invoice", "Journal Entry", "Payment Entry"].includes(frm.doc.doctype) - ) { + if ( + (frm.doc.doctype == "Journal Entry" && frm.doc.voucher_type != "Journal Entry") || + !["Purchase Invoice", "Sales Invoice", "Journal Entry", "Payment Entry"].includes( + frm.doc.doctype + ) + ) { return; } frappe.call({ - "method": "erpnext.accounts.doctype.unreconcile_payment.unreconcile_payment.doc_has_references", - "args": { - "doctype": frm.doc.doctype, - "docname": frm.doc.name + method: "erpnext.accounts.doctype.unreconcile_payment.unreconcile_payment.doc_has_references", + args: { + doctype: frm.doc.doctype, + docname: frm.doc.name, }, - callback: function(r) { + callback: function (r) { if (r.message) { - frm.add_custom_button(__("UnReconcile"), function() { - erpnext.accounts.unreconcile_payment.build_unreconcile_dialog(frm); - }, __('Actions')); + frm.add_custom_button( + __("UnReconcile"), + function () { + erpnext.accounts.unreconcile_payment.build_unreconcile_dialog(frm); + }, + __("Actions") + ); } - } + }, }); } }, @@ -30,18 +37,18 @@ erpnext.accounts.unreconcile_payment = { // assuming each row is an individual voucher // pass this to server side method that creates unreconcile doc for each row let selection_map = []; - if (['Sales Invoice', 'Purchase Invoice'].includes(frm.doc.doctype)) { - selection_map = selections.map(function(elem) { + if (["Sales Invoice", "Purchase Invoice"].includes(frm.doc.doctype)) { + selection_map = selections.map(function (elem) { return { company: elem.company, voucher_type: elem.voucher_type, voucher_no: elem.voucher_no, against_voucher_type: frm.doc.doctype, - against_voucher_no: frm.doc.name + against_voucher_no: frm.doc.name, }; }); - } else if (['Payment Entry', 'Journal Entry'].includes(frm.doc.doctype)) { - selection_map = selections.map(function(elem) { + } else if (["Payment Entry", "Journal Entry"].includes(frm.doc.doctype)) { + selection_map = selections.map(function (elem) { return { company: elem.company, voucher_type: frm.doc.doctype, @@ -55,18 +62,41 @@ erpnext.accounts.unreconcile_payment = { }, build_unreconcile_dialog(frm) { - if (['Sales Invoice', 'Purchase Invoice', 'Payment Entry', 'Journal Entry'].includes(frm.doc.doctype)) { + if ( + ["Sales Invoice", "Purchase Invoice", "Payment Entry", "Journal Entry"].includes(frm.doc.doctype) + ) { let child_table_fields = [ - { label: __("Voucher Type"), fieldname: "voucher_type", fieldtype: "Dynamic Link", options: "DocType", in_list_view: 1, read_only: 1}, - { label: __("Voucher No"), fieldname: "voucher_no", fieldtype: "Link", options: "voucher_type", in_list_view: 1, read_only: 1 }, - { label: __("Allocated Amount"), fieldname: "allocated_amount", fieldtype: "Currency", in_list_view: 1, read_only: 1 , options: "account_currency"}, - { label: __("Currency"), fieldname: "account_currency", fieldtype: "Currency", read_only: 1}, - ] + { + label: __("Voucher Type"), + fieldname: "voucher_type", + fieldtype: "Dynamic Link", + options: "DocType", + in_list_view: 1, + read_only: 1, + }, + { + label: __("Voucher No"), + fieldname: "voucher_no", + fieldtype: "Link", + options: "voucher_type", + in_list_view: 1, + read_only: 1, + }, + { + label: __("Allocated Amount"), + fieldname: "allocated_amount", + fieldtype: "Currency", + in_list_view: 1, + read_only: 1, + options: "account_currency", + }, + { label: __("Currency"), fieldname: "account_currency", fieldtype: "Currency", read_only: 1 }, + ]; let unreconcile_dialog_fields = [ { - label: __('Allocations'), - fieldname: 'allocations', - fieldtype: 'Table', + label: __("Allocations"), + fieldname: "allocations", + fieldtype: "Table", read_only: 1, fields: child_table_fields, }, @@ -74,54 +104,57 @@ erpnext.accounts.unreconcile_payment = { // get linked payments frappe.call({ - "method": "erpnext.accounts.doctype.unreconcile_payment.unreconcile_payment.get_linked_payments_for_doc", - "args": { - "company": frm.doc.company, - "doctype": frm.doc.doctype, - "docname": frm.doc.name + method: "erpnext.accounts.doctype.unreconcile_payment.unreconcile_payment.get_linked_payments_for_doc", + args: { + company: frm.doc.company, + doctype: frm.doc.doctype, + docname: frm.doc.name, }, - callback: function(r) { + callback: function (r) { if (r.message) { // populate child table with allocations unreconcile_dialog_fields[0].data = r.message; - unreconcile_dialog_fields[0].get_data = function(){ return r.message}; + unreconcile_dialog_fields[0].get_data = function () { + return r.message; + }; let d = new frappe.ui.Dialog({ - title: 'UnReconcile Allocations', + title: "UnReconcile Allocations", fields: unreconcile_dialog_fields, - size: 'large', + size: "large", cannot_add_rows: true, - primary_action_label: 'UnReconcile', + primary_action_label: "UnReconcile", primary_action(values) { - - let selected_allocations = values.allocations.filter(x=>x.__checked); + let selected_allocations = values.allocations.filter((x) => x.__checked); if (selected_allocations.length > 0) { - let selection_map = erpnext.accounts.unreconcile_payment.build_selection_map(frm, selected_allocations); - erpnext.accounts.unreconcile_payment.create_unreconcile_docs(selection_map); + let selection_map = + erpnext.accounts.unreconcile_payment.build_selection_map( + frm, + selected_allocations + ); + erpnext.accounts.unreconcile_payment.create_unreconcile_docs( + selection_map + ); d.hide(); - } else { frappe.msgprint("No Selection"); } - } + }, }); d.show(); } - } + }, }); } }, create_unreconcile_docs(selection_map) { frappe.call({ - "method": "erpnext.accounts.doctype.unreconcile_payment.unreconcile_payment.create_unreconcile_doc_for_selection", - "args": { - "selections": selection_map + method: "erpnext.accounts.doctype.unreconcile_payment.unreconcile_payment.create_unreconcile_doc_for_selection", + args: { + selections: selection_map, }, }); - } - - - -} + }, +}; diff --git a/erpnext/public/js/website_theme.js b/erpnext/public/js/website_theme.js index 0009cacf61e..9c2b8cd11b9 100644 --- a/erpnext/public/js/website_theme.js +++ b/erpnext/public/js/website_theme.js @@ -1,14 +1,15 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors // MIT License. See license.txt -frappe.ui.form.on('Website Theme', { +frappe.ui.form.on("Website Theme", { validate(frm) { let theme_scss = frm.doc.theme_scss; - if (theme_scss && theme_scss.includes('frappe/public/scss/website') - && !theme_scss.includes('erpnext/public/scss/website') + if ( + theme_scss && + theme_scss.includes("frappe/public/scss/website") && + !theme_scss.includes("erpnext/public/scss/website") ) { - frm.set_value('theme_scss', - `${frm.doc.theme_scss}\n@import "erpnext/public/scss/website";`); + frm.set_value("theme_scss", `${frm.doc.theme_scss}\n@import "erpnext/public/scss/website";`); } - } + }, }); diff --git a/erpnext/public/js/website_utils.js b/erpnext/public/js/website_utils.js index b5416065d79..f196d7c7ade 100644 --- a/erpnext/public/js/website_utils.js +++ b/erpnext/public/js/website_utils.js @@ -1,29 +1,29 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -if(!window.erpnext) window.erpnext = {}; +if (!window.erpnext) window.erpnext = {}; // Add / update a new Lead / Communication // subject, sender, description -frappe.send_message = function(opts, btn) { +frappe.send_message = function (opts, btn) { return frappe.call({ type: "POST", method: "erpnext.templates.utils.send_message", btn: btn, args: opts, - callback: opts.callback + callback: opts.callback, }); }; -erpnext.subscribe_to_newsletter = function(opts, btn) { +erpnext.subscribe_to_newsletter = function (opts, btn) { return frappe.call({ type: "POST", method: "frappe.email.doctype.newsletter.newsletter.subscribe", btn: btn, - args: {"email": opts.email}, - callback: opts.callback + args: { email: opts.email }, + callback: opts.callback, }); -} +}; // for backward compatibility erpnext.send_message = frappe.send_message; diff --git a/erpnext/public/js/wishlist.js b/erpnext/public/js/wishlist.js index f6599e9f6d1..04e28bf75fa 100644 --- a/erpnext/public/js/wishlist.js +++ b/erpnext/public/js/wishlist.js @@ -5,18 +5,18 @@ frappe.provide("erpnext.e_commerce.shopping_cart"); var shopping_cart = erpnext.e_commerce.shopping_cart; $.extend(wishlist, { - set_wishlist_count: function(animate=false) { + set_wishlist_count: function (animate = false) { // set badge count for wishlist icon var wish_count = frappe.get_cookie("wish_count"); - if (frappe.session.user==="Guest") { + if (frappe.session.user === "Guest") { wish_count = 0; } if (wish_count) { - $(".wishlist").toggleClass('hidden', false); + $(".wishlist").toggleClass("hidden", false); } - var $wishlist = $('.wishlist-icon'); + var $wishlist = $(".wishlist-icon"); var $badge = $wishlist.find("#wish-count"); if (parseInt(wish_count) === 0 || wish_count === undefined) { @@ -27,9 +27,9 @@ $.extend(wishlist, { if (wish_count) { $badge.html(wish_count); if (animate) { - $wishlist.addClass('cart-animate'); + $wishlist.addClass("cart-animate"); setTimeout(() => { - $wishlist.removeClass('cart-animate'); + $wishlist.removeClass("cart-animate"); }, 500); } } else { @@ -37,19 +37,19 @@ $.extend(wishlist, { } }, - bind_move_to_cart_action: function() { + bind_move_to_cart_action: function () { // move item to cart from wishlist - $('.page_content').on("click", ".btn-add-to-cart", (e) => { + $(".page_content").on("click", ".btn-add-to-cart", (e) => { const $move_to_cart_btn = $(e.currentTarget); let item_code = $move_to_cart_btn.data("item-code"); shopping_cart.shopping_cart_update({ item_code, qty: 1, - cart_dropdown: true + cart_dropdown: true, }); - let success_action = function() { + let success_action = function () { const $card_wrapper = $move_to_cart_btn.closest(".wishlist-card"); $card_wrapper.addClass("wish-removed"); }; @@ -58,15 +58,15 @@ $.extend(wishlist, { }); }, - bind_remove_action: function() { + bind_remove_action: function () { // remove item from wishlist let me = this; - $('.page_content').on("click", ".remove-wish", (e) => { + $(".page_content").on("click", ".remove-wish", (e) => { const $remove_wish_btn = $(e.currentTarget); let item_code = $remove_wish_btn.data("item-code"); - let success_action = function() { + let success_action = function () { const $card_wrapper = $remove_wish_btn.closest(".wishlist-card"); $card_wrapper.addClass("wish-removed"); if (frappe.get_cookie("wish_count") == 0) { @@ -81,17 +81,17 @@ $.extend(wishlist, { bind_wishlist_action() { // 'wish'('like') or 'unwish' item in product listing - $('.page_content').on('click', '.like-action, .like-action-list', (e) => { + $(".page_content").on("click", ".like-action, .like-action-list", (e) => { const $btn = $(e.currentTarget); this.wishlist_action($btn); }); }, wishlist_action(btn) { - const $wish_icon = btn.find('.wish-icon'); + const $wish_icon = btn.find(".wish-icon"); let me = this; - if (frappe.session.user==="Guest") { + if (frappe.session.user === "Guest") { if (localStorage) { localStorage.setItem("last_visited", window.location.pathname); } @@ -99,30 +99,30 @@ $.extend(wishlist, { return; } - let success_action = function() { + let success_action = function () { erpnext.e_commerce.wishlist.set_wishlist_count(true); }; - if ($wish_icon.hasClass('wished')) { + if ($wish_icon.hasClass("wished")) { // un-wish item btn.removeClass("like-animate"); btn.addClass("like-action-wished"); - this.toggle_button_class($wish_icon, 'wished', 'not-wished'); + this.toggle_button_class($wish_icon, "wished", "not-wished"); - let args = { item_code: btn.data('item-code') }; - let failure_action = function() { - me.toggle_button_class($wish_icon, 'not-wished', 'wished'); + let args = { item_code: btn.data("item-code") }; + let failure_action = function () { + me.toggle_button_class($wish_icon, "not-wished", "wished"); }; this.add_remove_from_wishlist("remove", args, success_action, failure_action); } else { // wish item btn.addClass("like-animate"); btn.addClass("like-action-wished"); - this.toggle_button_class($wish_icon, 'not-wished', 'wished'); + this.toggle_button_class($wish_icon, "not-wished", "wished"); - let args = {item_code: btn.data('item-code')}; - let failure_action = function() { - me.toggle_button_class($wish_icon, 'wished', 'not-wished'); + let args = { item_code: btn.data("item-code") }; + let failure_action = function () { + me.toggle_button_class($wish_icon, "wished", "not-wished"); }; this.add_remove_from_wishlist("add", args, success_action, failure_action); } @@ -133,14 +133,14 @@ $.extend(wishlist, { button.addClass(add); }, - add_remove_from_wishlist(action, args, success_action, failure_action, async=false) { + add_remove_from_wishlist(action, args, success_action, failure_action, async = false) { /* AJAX call to add or remove Item from Wishlist action: "add" or "remove" args: args for method (item_code, price, formatted_price), success_action: method to execute on successs, failure_action: method to execute on failure, async: make call asynchronously (true/false). */ - if (frappe.session.user==="Guest") { + if (frappe.session.user === "Guest") { if (localStorage) { localStorage.setItem("last_visited", window.location.pathname); } @@ -158,23 +158,24 @@ $.extend(wishlist, { args: args, callback: function (r) { if (r.exc) { - if (failure_action && (typeof failure_action === 'function')) { + if (failure_action && typeof failure_action === "function") { failure_action(); } frappe.msgprint({ message: __("Sorry, something went wrong. Please refresh."), - indicator: "red", title: __("Note") + indicator: "red", + title: __("Note"), }); - } else if (success_action && (typeof success_action === 'function')) { + } else if (success_action && typeof success_action === "function") { success_action(); } - } + }, }); } }, redirect_guest() { - frappe.call('erpnext.e_commerce.api.get_guest_redirect_on_action').then((res) => { + frappe.call("erpnext.e_commerce.api.get_guest_redirect_on_action").then((res) => { window.location.href = res.message || "/login"; }); }, @@ -185,20 +186,18 @@ $.extend(wishlist, {
              Empty Cart
              -
              ${ __('Wishlist is empty !') }

              +
              ${__("Wishlist is empty !")}

              `); - } - + }, }); -frappe.ready(function() { +frappe.ready(function () { if (window.location.pathname !== "/wishlist") { - $(".wishlist").toggleClass('hidden', true); + $(".wishlist").toggleClass("hidden", true); wishlist.set_wishlist_count(); } else { wishlist.bind_move_to_cart_action(); wishlist.bind_remove_action(); } - -}); \ No newline at end of file +}); diff --git a/erpnext/public/scss/erpnext.scss b/erpnext/public/scss/erpnext.scss index 8ab5973debd..6da8f24cf9b 100644 --- a/erpnext/public/scss/erpnext.scss +++ b/erpnext/public/scss/erpnext.scss @@ -51,7 +51,7 @@ } // assessment tool -.frappe-control[data-fieldname='result_html'] { +.frappe-control[data-fieldname="result_html"] { overflow: scroll; } .assessment-result-tool { @@ -70,7 +70,9 @@ text-overflow: ellipsis; } - .total-score, .grade, .score { + .total-score, + .grade, + .score { text-align: right; } } @@ -78,13 +80,13 @@ /* pos */ body[data-route="pos"] { - .pos-bill-toolbar { padding: 10px 0px; height: 51px; } - .pos-bill-item:hover, .list-customers-table > .pos-list-row:hover { + .pos-bill-item:hover, + .list-customers-table > .pos-list-row:hover { background-color: #f5f7fa; cursor: pointer; } @@ -135,50 +137,52 @@ body[data-route="pos"] { } .pos-payment-row .col-xs-6 { - padding :15px; + padding: 15px; } .pos-payment-row { - border-bottom:1px solid var(--border-color); + border-bottom: 1px solid var(--border-color); margin: 2px 0px 5px 0px; height: 60px; margin-top: 0px; margin-bottom: 0px; } - .pos-payment-row:hover, .pos-keyboard-key:hover{ + .pos-payment-row:hover, + .pos-keyboard-key:hover { background-color: var(--bg-color); cursor: pointer; } - .pos-keyboard-key, .delete-btn { + .pos-keyboard-key, + .delete-btn { border: 1px solid var(--border-color); - height:85px; - width:85px; - margin:10px 10px; - font-size:24px; - font-weight:200; - background-color: #FDFDFD; + height: 85px; + width: 85px; + margin: 10px 10px; + font-size: 24px; + font-weight: 200; + background-color: #fdfdfd; border-color: #e8e8e8; } .numeric-keypad { border: 1px solid var(--border-color); - height:69px; - width:69px; - font-size:20px; - font-weight:200; - background-color: #FDFDFD; + height: 69px; + width: 69px; + font-size: 20px; + font-weight: 200; + background-color: #fdfdfd; border-color: #e8e8e8; - margin-left:-4px; + margin-left: -4px; } .pos-pay { - height:69px; - width:69px; - font-size:17px; - font-weight:200; - margin-left:-4px; + height: 69px; + width: 69px; + font-size: 17px; + font-weight: 200; + margin-left: -4px; } .numeric-keypad { @@ -188,7 +192,7 @@ body[data-route="pos"] { font-weight: 200; border-radius: 0; background-color: #fff; - margin-left:-4px; + margin-left: -4px; @media (max-width: var(--xl-width)) { height: 45px; @@ -253,7 +257,8 @@ body[data-route="pos"] { .amount-row h3 { font-size: 15px; } - .pos-keyboard-key, .delete-btn { + .pos-keyboard-key, + .delete-btn { height: 50px; } .multimode-payments { @@ -277,7 +282,8 @@ body[data-route="pos"] { padding: 15px 10px; } - .write_off_amount, .change_amount { + .write_off_amount, + .change_amount { margin: 15px; width: 130px; } @@ -301,10 +307,11 @@ body[data-route="pos"] { } .subject { - width: 40% + width: 40%; } - .list-row-checkbox, .list-select-all { + .list-row-checkbox, + .list-select-all { margin-right: 7px; } } @@ -397,7 +404,7 @@ body[data-route="pos"] { padding-top: 0; } - &> .pos-list-row { + & > .pos-list-row { border: none; @media (max-width: var(--xl-width)) { @@ -442,13 +449,12 @@ body[data-route="pos"] { padding: 5px 9px; border-radius: 3px; color: #fff; - } // Healthcare .exercise-card { - box-shadow: 0 1px 3px rgba(0,0,0,0.30); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); border-radius: 2px; padding: 6px 6px 6px 8px; margin-top: 10px; diff --git a/erpnext/public/scss/hierarchy_chart.scss b/erpnext/public/scss/hierarchy_chart.scss index 57d5e8414ae..834d5ccb194 100644 --- a/erpnext/public/scss/hierarchy_chart.scss +++ b/erpnext/public/scss/hierarchy_chart.scss @@ -22,12 +22,12 @@ } .node-card.exported { - box-shadow: none + box-shadow: none; } .node-image { - width: 3.0rem; - height: 3.0rem; + width: 3rem; + height: 3rem; } .node-name { @@ -61,8 +61,8 @@ display: flex; background: var(--blue-100); color: var(--blue-500); - padding: .25rem .5rem; - font-size: .75rem; + padding: 0.25rem 0.5rem; + font-size: 0.75rem; justify-content: center; box-shadow: var(--shadow-sm); margin-left: auto; @@ -77,7 +77,7 @@ display: block; } - .node-edit-icon > .icon{ + .node-edit-icon > .icon { stroke: var(--blue-500); } @@ -96,7 +96,7 @@ border-radius: 0.5rem; padding: 0.75rem; width: 15rem; - height: 3.0rem; + height: 3rem; .btn-edit-node { display: none !important; @@ -144,7 +144,7 @@ border-radius: 0.5rem; padding: 0.75rem; width: 15rem; - height: 3.0rem; + height: 3rem; .btn-edit-node { display: none !important; @@ -206,7 +206,8 @@ margin: 0px 0px 16px 0px; } -.hierarchy, .hierarchy-mobile { +.hierarchy, +.hierarchy-mobile { .level { margin-right: 8px; align-items: flex-start; diff --git a/erpnext/public/scss/order-page.scss b/erpnext/public/scss/order-page.scss index 6f5fe5d4d7a..2b1dae282a3 100644 --- a/erpnext/public/scss/order-page.scss +++ b/erpnext/public/scss/order-page.scss @@ -1,115 +1,113 @@ #page-order { - .main-column { - .page-content-wrapper { + .main-column { + .page-content-wrapper { + .breadcrumb-container { + @media screen and (min-width: 567px) { + padding-left: var(--padding-sm); + } + } - .breadcrumb-container { - @media screen and (min-width: 567px) { - padding-left: var(--padding-sm); - } - } + .container.my-4 { + background-color: var(--fg-color); - .container.my-4 { - background-color: var(--fg-color); - - @media screen and (min-width: 567px) { - padding: 1.25rem 1.5rem; - border-radius: var(--border-radius-md); - box-shadow: var(--card-shadow); - } - } - } - } + @media screen and (min-width: 567px) { + padding: 1.25rem 1.5rem; + border-radius: var(--border-radius-md); + box-shadow: var(--card-shadow); + } + } + } + } } .indicator-container { - @media screen and (max-width: 567px) { - padding-bottom: 0.8rem; - } + @media screen and (max-width: 567px) { + padding-bottom: 0.8rem; + } } .order-items { - padding: 1.5rem 0; - border-bottom: 1px solid var(--border-color); - color: var(--gray-700); + padding: 1.5rem 0; + border-bottom: 1px solid var(--border-color); + color: var(--gray-700); - @media screen and (max-width: 567px) { - align-items: flex-start !important; - } - .col-2 { - @media screen and (max-width: 567px) { - flex: auto; - max-width: 28%; - } - } + @media screen and (max-width: 567px) { + align-items: flex-start !important; + } + .col-2 { + @media screen and (max-width: 567px) { + flex: auto; + max-width: 28%; + } + } - .order-item-name { - font-size: var(--text-base); - font-weight: 500; - } + .order-item-name { + font-size: var(--text-base); + font-weight: 500; + } - .btn:focus, - .btn:hover { - background-color: var(--control-bg); - } + .btn:focus, + .btn:hover { + background-color: var(--control-bg); + } + .col-6 { + @media screen and (max-width: 567px) { + max-width: 100%; + } - .col-6 { - @media screen and (max-width: 567px) { - max-width: 100%; - } - - &.order-item-name { - font-size: var(--text-base); - } - } + &.order-item-name { + font-size: var(--text-base); + } + } } .item-grand-total { - font-size: var(--text-base); + font-size: var(--text-base); } .list-item-name, .item-total, .order-container, .order-qty { - font-size: var(--text-md); + font-size: var(--text-md); } .d-s-n { - @media screen and (max-width: 567px) { - display: none; - } + @media screen and (max-width: 567px) { + display: none; + } } .d-l-n { - @media screen and (min-width: 567px) { - display: none; - } + @media screen and (min-width: 567px) { + display: none; + } } .border-btm { - border-bottom: 1px solid var(--border-color); + border-bottom: 1px solid var(--border-color); } .order-taxes { - display: flex; + display: flex; - @media screen and (min-width: 567px) { - justify-content: flex-end; - } + @media screen and (min-width: 567px) { + justify-content: flex-end; + } - .col-4 { - padding-right: 0; + .col-4 { + padding-right: 0; - .col-8 { - padding-left: 0; - padding-right: 0; - } + .col-8 { + padding-left: 0; + padding-right: 0; + } - @media screen and (max-width: 567px) { - padding-left: 0; - flex: auto; - max-width: 100%; - } - } -} \ No newline at end of file + @media screen and (max-width: 567px) { + padding-left: 0; + flex: auto; + max-width: 100%; + } + } +} diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss index 7b7530b1501..f50e2a52117 100644 --- a/erpnext/public/scss/point-of-sale.scss +++ b/erpnext/public/scss/point-of-sale.scss @@ -185,7 +185,6 @@ font-weight: 700; } } - } } } @@ -262,12 +261,12 @@ flex-direction: column; margin-right: auto; - >.customer-name { + > .customer-name { font-weight: 700; font-size: var(--text-lg); } - >.customer-desc { + > .customer-desc { color: var(--gray-600); font-weight: 500; font-size: var(--text-sm); @@ -279,7 +278,6 @@ align-items: center; cursor: pointer; } - } > .customer-fields-container { @@ -408,7 +406,6 @@ font-size: var(--text-lg); } - > .item-name-desc { @extend .nowrap; display: flex; @@ -456,7 +453,6 @@ } } } - } } @@ -855,7 +851,7 @@ display: flex; flex: 1; height: 100%; - position: relative; + position: relative; justify-content: flex-end; > .fields-section { @@ -1136,7 +1132,6 @@ } } - > .summary-btns { display: flex; justify-content: space-between; @@ -1148,7 +1143,7 @@ > .new-btn { background-color: var(--blue-500); - color:white; + color: white; font-weight: 500; } } diff --git a/erpnext/public/scss/shopping_cart.scss b/erpnext/public/scss/shopping_cart.scss index 6ae464d2c21..35c1fb64e36 100644 --- a/erpnext/public/scss/shopping_cart.scss +++ b/erpnext/public/scss/shopping_cart.scss @@ -1,9 +1,9 @@ @import "frappe/public/scss/common/mixins"; :root { - --green-info: #38A160; + --green-info: #38a160; --product-bg-color: white; - --body-bg-color: var(--gray-50); + --body-bg-color: var(--gray-50); } body.product-page { @@ -96,7 +96,8 @@ body.product-page { } } - .card:hover, .card:focus-within { + .card:hover, + .card:focus-within { .btn-add-to-cart-list { visibility: visible; } @@ -108,7 +109,6 @@ body.product-page { } } - .card-img-container { height: 210px; width: 100%; @@ -205,7 +205,8 @@ body.product-page { } } -#products-list-area, #products-grid-area { +#products-list-area, +#products-grid-area { padding: 0 5px; } @@ -216,7 +217,8 @@ body.product-page { border-radius: 8px; border-bottom: 1px solid var(--gray-50); - &:hover, &:focus-within { + &:hover, + &:focus-within { box-shadow: 0px 16px 60px rgba(0, 0, 0, 0.08), 0px 8px 30px -20px rgba(0, 0, 0, 0.04); transition: box-shadow 400ms; @@ -347,13 +349,13 @@ body.product-page { .btn-add-to-wishlist { svg use { - --icon-stroke: #F47A7A; + --icon-stroke: #f47a7a; } } .btn-view-in-wishlist { svg use { - fill: #F47A7A; + fill: #f47a7a; --icon-stroke: none; } } @@ -384,13 +386,12 @@ body.product-page { width: 350px; } - img { + img { object-fit: contain; } } .item-slideshow { - @media (max-width: var(--md-width)) { max-height: 320px; } @@ -411,7 +412,8 @@ body.product-page { border-radius: 4px; cursor: pointer; - &:hover, &.active { + &:hover, + &.active { border-color: var(--primary); } } @@ -460,11 +462,11 @@ body.product-page { .recommendation-header { font-size: 16px; - font-weight: 500 + font-weight: 500; } .recommendation-container { - padding: .5rem; + padding: 0.5rem; min-height: 0px; .r-item-image { @@ -476,11 +478,12 @@ body.product-page { } .no-image-r-item { - display: flex; justify-content: center; + display: flex; + justify-content: center; background-color: var(--gray-200); align-items: center; color: var(--gray-400); - margin-top: .15rem; + margin-top: 0.15rem; border-radius: 6px; height: 100%; font-size: 24px; @@ -513,16 +516,16 @@ body.product-page { } .product-code { - padding: .5rem 0; + padding: 0.5rem 0; color: var(--text-muted); font-size: 14px; .product-item-group { - padding-right: .25rem; + padding-right: 0.25rem; border-right: solid 1px var(--text-muted); } .product-item-code { - padding-left: .5rem; + padding-left: 0.5rem; } } @@ -561,14 +564,13 @@ body.product-page { } .item-group-slideshow { - .carousel-inner.rounded-carousel { border-radius: var(--card-border-radius); } } .sub-category-container { - padding-bottom: .5rem; + padding-bottom: 0.5rem; margin-bottom: 1.25rem; border-bottom: 1px solid var(--table-border-color); @@ -590,7 +592,6 @@ body.product-page { } } - .shopping-badge { position: relative; top: -10px; @@ -602,7 +603,6 @@ body.product-page { border-radius: 50%; } - .cart-animate { animation: wiggle 0.5s linear; } @@ -656,7 +656,9 @@ body.product-page { margin-bottom: 1rem; } - th, tr, td { + th, + tr, + td { border-color: var(--border-color); border-width: 1px; } @@ -725,7 +727,6 @@ body.product-page { height: 60px; font-size: 14px; } - } .cart-tax-items { @@ -759,7 +760,7 @@ body.product-page { background-color: var(--gray-100); float: right; cursor: pointer; - margin-top: .25rem; + margin-top: 0.25rem; justify-content: center; } @@ -859,11 +860,12 @@ body.product-page { .no-image-cart-item { max-height: 112px; - display: flex; justify-content: center; + display: flex; + justify-content: center; background-color: var(--gray-200); align-items: center; color: var(--gray-400); - margin-top: .15rem; + margin-top: 0.15rem; border-radius: 6px; height: 100%; font-size: 24px; @@ -911,7 +913,8 @@ body.product-page { } .address-header { - margin-top: .15rem;padding: 0; + margin-top: 0.15rem; + padding: 0; } .btn-new-address { @@ -920,7 +923,8 @@ body.product-page { color: var(--primary-color) !important; } -.btn-new-address:hover, .btn-change-address:hover { +.btn-new-address:hover, +.btn-change-address:hover { color: var(--primary-color) !important; } @@ -952,7 +956,6 @@ body.product-page { } } - .like-action { visibility: hidden; text-align: center; @@ -1013,31 +1016,31 @@ body.product-page { @keyframes expand { 30% { - transform: scale(1.3); + transform: scale(1.3); } 50% { - transform: scale(0.8); + transform: scale(0.8); } 70% { transform: scale(1.1); } 100% { - transform: scale(1); + transform: scale(1); } - } +} .not-wished { cursor: pointer; - --icon-stroke: #F47A7A !important; + --icon-stroke: #f47a7a !important; &:hover { - fill: #F47A7A; + fill: #f47a7a; } } .wished { --icon-stroke: none; - fill: #F47A7A !important; + fill: #f47a7a !important; } .list-row-checkbox { @@ -1052,7 +1055,7 @@ body.product-page { } #pay-for-order { - padding: .5rem 1rem; // Pay button in SO + padding: 0.5rem 1rem; // Pay button in SO } .btn-explore-variants { @@ -1074,7 +1077,7 @@ body.product-page { } } -.btn-add-to-cart-list{ +.btn-add-to-cart-list { visibility: hidden; box-shadow: none; margin: var(--margin-sm) 0; @@ -1108,7 +1111,7 @@ body.product-page { background-color: white; position: absolute; cursor: pointer; - top:10px; + top: 10px; right: 20px; width: 32px; height: 32px; @@ -1123,7 +1126,7 @@ body.product-page { } .item-website-specification { - font-size: .875rem; + font-size: 0.875rem; .product-title { font-size: 18px; } @@ -1150,8 +1153,8 @@ body.product-page { } .ratings-reviews-section { - border-top: 1px solid #E2E6E9; - padding: .5rem 1rem; + border-top: 1px solid #e2e6e9; + padding: 0.5rem 1rem; } .reviews-header { @@ -1165,7 +1168,7 @@ body.product-page { .btn-write-review { float: right; - padding: .5rem 1rem; + padding: 0.5rem 1rem; font-size: 14px; font-weight: 400; border: none !important; @@ -1218,7 +1221,7 @@ body.product-page { .ratings-pill { background-color: var(--gray-100); - padding: .5rem 1rem; + padding: 0.5rem 1rem; border-radius: 66px; } @@ -1226,7 +1229,7 @@ body.product-page { max-width: 80%; line-height: 1.6; padding-bottom: 0.5rem; - border-bottom: 1px solid #E2E6E9; + border-bottom: 1px solid #e2e6e9; } .review-signature { @@ -1266,7 +1269,7 @@ body.product-page { #search-results-container { border: 1px solid var(--gray-200); - padding: .25rem 1rem; + padding: 0.25rem 1rem; .category-chip { background-color: var(--gray-100); @@ -1275,7 +1278,7 @@ body.product-page { } .recent-search { - padding: .5rem .5rem; + padding: 0.5rem 0.5rem; border-radius: var(--border-radius); &:hover { @@ -1313,11 +1316,11 @@ body.product-page { } .placeholder-div { - height:80%; + height: 80%; width: -webkit-fill-available; padding: 50px; text-align: center; - background-color: #F9FAFA; + background-color: #f9fafa; border-top-left-radius: calc(0.75rem - 1px); border-top-right-radius: calc(0.75rem - 1px); } @@ -1342,7 +1345,8 @@ body.product-page { color: gray; } -.btn-next, .btn-prev { +.btn-next, +.btn-prev { font-size: 14px; } @@ -1369,7 +1373,7 @@ body.product-page { font-weight: 400; font-size: 14px; line-height: 20px; - color: #F47A7A; + color: #f47a7a; } .mt-minus-2 { @@ -1378,4 +1382,4 @@ body.product-page { .mt-minus-1 { margin-top: -1rem; -} \ No newline at end of file +} diff --git a/erpnext/public/scss/website.scss b/erpnext/public/scss/website.scss index b5e97f1c34b..dd2ae210c76 100644 --- a/erpnext/public/scss/website.scss +++ b/erpnext/public/scss/website.scss @@ -1,4 +1,4 @@ -@import './order-page'; +@import "./order-page"; .filter-options { max-height: 300px; @@ -52,7 +52,8 @@ border-bottom: 1px solid var(--border-color); position: relative; - &:only-child, &:last-child { + &:only-child, + &:last-child { border: 0; } @@ -80,7 +81,8 @@ } } -.list-item-name, .item-total { +.list-item-name, +.item-total { font-size: var(--font-size-sm); } @@ -88,4 +90,4 @@ @media screen and (max-width: 567px) { margin-top: 1rem; } -} \ No newline at end of file +} diff --git a/erpnext/quality_management/doctype/non_conformance/non_conformance.js b/erpnext/quality_management/doctype/non_conformance/non_conformance.js index e7f5eee623e..5524d7472b2 100644 --- a/erpnext/quality_management/doctype/non_conformance/non_conformance.js +++ b/erpnext/quality_management/doctype/non_conformance/non_conformance.js @@ -1,8 +1,7 @@ // Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Non Conformance', { +frappe.ui.form.on("Non Conformance", { // refresh: function(frm) { - // } }); diff --git a/erpnext/quality_management/doctype/quality_action/quality_action.js b/erpnext/quality_management/doctype/quality_action/quality_action.js index b44f2a20344..8261fab1ab5 100644 --- a/erpnext/quality_management/doctype/quality_action/quality_action.js +++ b/erpnext/quality_management/doctype/quality_action/quality_action.js @@ -1,6 +1,4 @@ // Copyright (c) 2018, Frappe and contributors // For license information, please see license.txt -frappe.ui.form.on('Quality Action', { - -}); +frappe.ui.form.on("Quality Action", {}); diff --git a/erpnext/quality_management/doctype/quality_feedback/quality_feedback.js b/erpnext/quality_management/doctype/quality_feedback/quality_feedback.js index 6fb326776ed..8166c257d46 100644 --- a/erpnext/quality_management/doctype/quality_feedback/quality_feedback.js +++ b/erpnext/quality_management/doctype/quality_feedback/quality_feedback.js @@ -1,10 +1,10 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Quality Feedback', { - template: function(frm) { +frappe.ui.form.on("Quality Feedback", { + template: function (frm) { if (frm.doc.template) { - frm.call('set_parameters'); + frm.call("set_parameters"); } - } + }, }); diff --git a/erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.js b/erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.js index 490eed97065..054572a80eb 100644 --- a/erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.js +++ b/erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.js @@ -1,8 +1,7 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Quality Feedback Template', { +frappe.ui.form.on("Quality Feedback Template", { // refresh: function(frm) { - // } }); diff --git a/erpnext/quality_management/doctype/quality_goal/quality_goal.js b/erpnext/quality_management/doctype/quality_goal/quality_goal.js index 40cb4d92464..5b7cae5d22a 100644 --- a/erpnext/quality_management/doctype/quality_goal/quality_goal.js +++ b/erpnext/quality_management/doctype/quality_goal/quality_goal.js @@ -1,7 +1,7 @@ // Copyright (c) 2018, Frappe and contributors // For license information, please see license.txt -frappe.ui.form.on('Quality Goal', { +frappe.ui.form.on("Quality Goal", { // refresh: function(frm) { // } }); diff --git a/erpnext/quality_management/doctype/quality_meeting/quality_meeting.js b/erpnext/quality_management/doctype/quality_meeting/quality_meeting.js index eb7a8c32d73..00ab1e4c37a 100644 --- a/erpnext/quality_management/doctype/quality_meeting/quality_meeting.js +++ b/erpnext/quality_management/doctype/quality_meeting/quality_meeting.js @@ -1,6 +1,4 @@ // Copyright (c) 2018, Frappe and contributors // For license information, please see license.txt -frappe.ui.form.on('Quality Meeting', { - -}); +frappe.ui.form.on("Quality Meeting", {}); diff --git a/erpnext/quality_management/doctype/quality_meeting/quality_meeting_list.js b/erpnext/quality_management/doctype/quality_meeting/quality_meeting_list.js index 5fd1b30eb45..ae3112f3d09 100644 --- a/erpnext/quality_management/doctype/quality_meeting/quality_meeting_list.js +++ b/erpnext/quality_management/doctype/quality_meeting/quality_meeting_list.js @@ -1,11 +1,10 @@ -frappe.listview_settings['Quality Meeting'] = { +frappe.listview_settings["Quality Meeting"] = { add_fields: ["status"], - get_indicator: function(doc) { - if(doc.status == "Open") { + get_indicator: function (doc) { + if (doc.status == "Open") { return [__("Open"), "red", "status=,Open"]; - } - else if(doc.status == "Close") { + } else if (doc.status == "Close") { return [__("Close"), "green", ",status=,Close"]; } - } + }, }; diff --git a/erpnext/quality_management/doctype/quality_meeting_agenda/quality_meeting_agenda.js b/erpnext/quality_management/doctype/quality_meeting_agenda/quality_meeting_agenda.js index 09989dc643f..ad037c25651 100644 --- a/erpnext/quality_management/doctype/quality_meeting_agenda/quality_meeting_agenda.js +++ b/erpnext/quality_management/doctype/quality_meeting_agenda/quality_meeting_agenda.js @@ -1,8 +1,7 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Quality Meeting Agenda', { +frappe.ui.form.on("Quality Meeting Agenda", { // refresh: function(frm) { - // } }); diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js index 79fd2ebdbe9..a2c16dddadb 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js @@ -1,23 +1,23 @@ // Copyright (c) 2018, Frappe and contributors // For license information, please see license.txt -frappe.ui.form.on('Quality Procedure', { - refresh: function(frm) { - frm.set_query('procedure', 'processes', (frm) =>{ +frappe.ui.form.on("Quality Procedure", { + refresh: function (frm) { + frm.set_query("procedure", "processes", (frm) => { return { filters: { - name: ['not in', [frm.parent_quality_procedure, frm.name]] - } + name: ["not in", [frm.parent_quality_procedure, frm.name]], + }, }; }); - frm.set_query('parent_quality_procedure', function(){ + frm.set_query("parent_quality_procedure", function () { return { filters: { is_group: 1, - name: ['!=', frm.doc.name] - } + name: ["!=", frm.doc.name], + }, }; }); - } + }, }); diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js b/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js index 2851fcc5969..25f9a5906a1 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure_tree.js @@ -1,18 +1,18 @@ frappe.treeview_settings["Quality Procedure"] = { - ignore_fields:["parent_quality_procedure"], - get_tree_nodes: 'erpnext.quality_management.doctype.quality_procedure.quality_procedure.get_children', - add_tree_node: 'erpnext.quality_management.doctype.quality_procedure.quality_procedure.add_node', + ignore_fields: ["parent_quality_procedure"], + get_tree_nodes: "erpnext.quality_management.doctype.quality_procedure.quality_procedure.get_children", + add_tree_node: "erpnext.quality_management.doctype.quality_procedure.quality_procedure.add_node", filters: [ { fieldname: "parent_quality_procedure", fieldtype: "Link", options: "Quality Procedure", label: __("Quality Procedure"), - get_query: function() { + get_query: function () { return { - filters: [["Quality Procedure", 'is_group', '=', 1]] + filters: [["Quality Procedure", "is_group", "=", 1]], }; - } + }, }, ], breadcrumb: "Quality Management", @@ -22,13 +22,13 @@ frappe.treeview_settings["Quality Procedure"] = { menu_items: [ { label: __("New Quality Procedure"), - action: function() { + action: function () { frappe.new_doc("Quality Procedure", true); }, - condition: 'frappe.boot.user.can_create.indexOf("Quality Procedure") !== -1' - } + condition: 'frappe.boot.user.can_create.indexOf("Quality Procedure") !== -1', + }, ], - onload: function(treeview) { + onload: function (treeview) { treeview.make_tree(); }, }; diff --git a/erpnext/quality_management/doctype/quality_review/quality_review.js b/erpnext/quality_management/doctype/quality_review/quality_review.js index 0e6b7034101..504f8fceb2e 100644 --- a/erpnext/quality_management/doctype/quality_review/quality_review.js +++ b/erpnext/quality_management/doctype/quality_review/quality_review.js @@ -1,15 +1,15 @@ // Copyright (c) 2018, Frappe and contributors // For license information, please see license.txt -frappe.ui.form.on('Quality Review', { - goal: function(frm) { +frappe.ui.form.on("Quality Review", { + goal: function (frm) { frappe.call({ - "method": "frappe.client.get", + method: "frappe.client.get", args: { doctype: "Quality Goal", - name: frm.doc.goal + name: frm.doc.goal, }, - callback: function(data){ + callback: function (data) { frm.fields_dict.reviews.grid.remove_all(); let objectives = data.message.objectives; for (var i in objectives) { @@ -19,7 +19,7 @@ frappe.ui.form.on('Quality Review', { frm.fields_dict.reviews.get_value()[i].uom = objectives[i].uom; } frm.refresh(); - } + }, }); }, }); diff --git a/erpnext/quality_management/doctype/quality_review/quality_review_list.js b/erpnext/quality_management/doctype/quality_review/quality_review_list.js index b0be783de56..3be6c10a89d 100644 --- a/erpnext/quality_management/doctype/quality_review/quality_review_list.js +++ b/erpnext/quality_management/doctype/quality_review/quality_review_list.js @@ -1,12 +1,10 @@ -frappe.listview_settings['Quality Review'] = { +frappe.listview_settings["Quality Review"] = { add_fields: ["action"], - get_indicator: function(doc) - { - if(doc.action == "No Action") { + get_indicator: function (doc) { + if (doc.action == "No Action") { return [__("No Action"), "green", "action,=,No Action"]; - } - else if(doc.action == "Action Initialised") { + } else if (doc.action == "Action Initialised") { return [__("Action Initialised"), "red", "action,=,Action Initialised"]; } - } + }, }; diff --git a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.js b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.js index 5918ec8b316..5fbb5cb7e01 100644 --- a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.js +++ b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.js @@ -1,39 +1,39 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.ui.form.on('Import Supplier Invoice', { - onload: function(frm) { +frappe.ui.form.on("Import Supplier Invoice", { + onload: function (frm) { frappe.realtime.on("import_invoice_update", function (data) { frm.dashboard.show_progress(data.title, (data.count / data.total) * 100, data.message); if (data.count == data.total) { - window.setTimeout(title => frm.dashboard.hide_progress(title), 1500, data.title); + window.setTimeout((title) => frm.dashboard.hide_progress(title), 1500, data.title); } }); }, - setup: function(frm) { - frm.set_query("tax_account", function(doc) { + setup: function (frm) { + frm.set_query("tax_account", function (doc) { return { filters: { - account_type: 'Tax', - company: doc.company - } + account_type: "Tax", + company: doc.company, + }, }; }); - frm.set_query("default_buying_price_list", function(doc) { + frm.set_query("default_buying_price_list", function (doc) { return { filters: { - currency: frappe.get_doc(":Company", doc.company).default_currency - } + currency: frappe.get_doc(":Company", doc.company).default_currency, + }, }; }); }, - refresh: function(frm) { + refresh: function (frm) { frm.trigger("toggle_read_only_fields"); }, - toggle_read_only_fields: function(frm) { + toggle_read_only_fields: function (frm) { if (in_list(["File Import Completed", "Processing File Data"], frm.doc.status)) { cur_frm.set_read_only(); cur_frm.refresh_fields(); @@ -41,6 +41,5 @@ frappe.ui.form.on('Import Supplier Invoice', { } else { frm.set_df_property("import_invoices", "hidden", 0); } - } - + }, }); diff --git a/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.js b/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.js index 72613f4064f..c70973dcc35 100644 --- a/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.js +++ b/erpnext/regional/doctype/ksa_vat_sales_account/ksa_vat_sales_account.js @@ -1,8 +1,7 @@ // Copyright (c) 2021, Havenir Solutions and contributors // For license information, please see license.txt -frappe.ui.form.on('KSA VAT Sales Account', { +frappe.ui.form.on("KSA VAT Sales Account", { // refresh: function(frm) { - // } }); diff --git a/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.js b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.js index 00b62b9adfb..5db1641f3d7 100644 --- a/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.js +++ b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting.js @@ -1,8 +1,8 @@ // Copyright (c) 2021, Havenir Solutions and contributors // For license information, please see license.txt -frappe.ui.form.on('KSA VAT Setting', { +frappe.ui.form.on("KSA VAT Setting", { onload: function () { - frappe.breadcrumbs.add('Accounts', 'KSA VAT Setting'); - } + frappe.breadcrumbs.add("Accounts", "KSA VAT Setting"); + }, }); diff --git a/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting_list.js b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting_list.js index 269cbec5fb4..39f8584cb72 100644 --- a/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting_list.js +++ b/erpnext/regional/doctype/ksa_vat_setting/ksa_vat_setting_list.js @@ -1,5 +1,5 @@ -frappe.listview_settings['KSA VAT Setting'] = { - onload () { - frappe.breadcrumbs.add('Accounts'); - } -} \ No newline at end of file +frappe.listview_settings["KSA VAT Setting"] = { + onload() { + frappe.breadcrumbs.add("Accounts"); + }, +}; diff --git a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.js b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.js index 8257bf8a969..02eaff53bb9 100644 --- a/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.js +++ b/erpnext/regional/doctype/lower_deduction_certificate/lower_deduction_certificate.js @@ -1,8 +1,7 @@ // Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Lower Deduction Certificate', { +frappe.ui.form.on("Lower Deduction Certificate", { // refresh: function(frm) { - // } }); diff --git a/erpnext/regional/doctype/product_tax_category/product_tax_category.js b/erpnext/regional/doctype/product_tax_category/product_tax_category.js index 9f8e7950156..ffbdc5d149c 100644 --- a/erpnext/regional/doctype/product_tax_category/product_tax_category.js +++ b/erpnext/regional/doctype/product_tax_category/product_tax_category.js @@ -1,8 +1,7 @@ // Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Product Tax Category', { +frappe.ui.form.on("Product Tax Category", { // refresh: function(frm) { - // } }); diff --git a/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js b/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js index e37a61ac853..86384e41b34 100644 --- a/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js +++ b/erpnext/regional/doctype/south_africa_vat_settings/south_africa_vat_settings.js @@ -1,23 +1,23 @@ // Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('South Africa VAT Settings', { - refresh: function(frm) { - frm.set_query("company", function() { +frappe.ui.form.on("South Africa VAT Settings", { + refresh: function (frm) { + frm.set_query("company", function () { return { filters: { country: "South Africa", - } + }, }; }); - frm.set_query("account", "vat_accounts", function() { + frm.set_query("account", "vat_accounts", function () { return { filters: { company: frm.doc.company, account_type: "Tax", - is_group: 0 - } + is_group: 0, + }, }; }); - } + }, }); diff --git a/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.js b/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.js index 66531412faf..c7d0f054b51 100644 --- a/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.js +++ b/erpnext/regional/doctype/uae_vat_settings/uae_vat_settings.js @@ -1,14 +1,14 @@ // Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('UAE VAT Settings', { - onload: function(frm) { - frm.set_query('account', 'uae_vat_accounts', function() { +frappe.ui.form.on("UAE VAT Settings", { + onload: function (frm) { + frm.set_query("account", "uae_vat_accounts", function () { return { filters: { - 'company': frm.doc.company - } + company: frm.doc.company, + }, }; }); - } + }, }); diff --git a/erpnext/regional/report/electronic_invoice_register/electronic_invoice_register.js b/erpnext/regional/report/electronic_invoice_register/electronic_invoice_register.js index d7e3ac9a5d3..1512d95a7eb 100644 --- a/erpnext/regional/report/electronic_invoice_register/electronic_invoice_register.js +++ b/erpnext/regional/report/electronic_invoice_register/electronic_invoice_register.js @@ -3,51 +3,53 @@ /* eslint-disable */ frappe.query_reports["Electronic Invoice Register"] = { - "filters": [ + filters: [ { - "fieldname":"from_date", - "label": __("From Date"), - "fieldtype": "Date", - "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), - "width": "80" + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), + width: "80", }, { - "fieldname":"to_date", - "label": __("To Date"), - "fieldtype": "Date", - "default": frappe.datetime.get_today() + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.datetime.get_today(), }, { - "fieldname":"customer", - "label": __("Customer"), - "fieldtype": "Link", - "options": "Customer" + fieldname: "customer", + label: __("Customer"), + fieldtype: "Link", + options: "Customer", }, { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company") + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), }, ], - "onload": function(reportview) { - reportview.page.add_inner_button(__("Export E-Invoices"), function() { + onload: function (reportview) { + reportview.page.add_inner_button(__("Export E-Invoices"), function () { //TODO: refactor condition to disallow export if report has no data. if (!reportview.data.length) { frappe.msgprint(__("No data to export")); - return + return; } var w = window.open( frappe.urllib.get_full_url( - "/api/method/erpnext.regional.italy.utils.export_invoices?" - + "filters=" + JSON.stringify(reportview.get_filter_values()) + "/api/method/erpnext.regional.italy.utils.export_invoices?" + + "filters=" + + JSON.stringify(reportview.get_filter_values()) ) ); if (!w) { - frappe.msgprint(__("Please enable pop-ups")); return; + frappe.msgprint(__("Please enable pop-ups")); + return; } - }) - } -} + }); + }, +}; diff --git a/erpnext/regional/report/fichier_des_ecritures_comptables_[fec]/fichier_des_ecritures_comptables_[fec].js b/erpnext/regional/report/fichier_des_ecritures_comptables_[fec]/fichier_des_ecritures_comptables_[fec].js index b85b58f636a..9a373affa29 100644 --- a/erpnext/regional/report/fichier_des_ecritures_comptables_[fec]/fichier_des_ecritures_comptables_[fec].js +++ b/erpnext/regional/report/fichier_des_ecritures_comptables_[fec]/fichier_des_ecritures_comptables_[fec].js @@ -2,40 +2,40 @@ // For license information, please see license.txt frappe.query_reports["Fichier des Ecritures Comptables [FEC]"] = { - "filters": [ + filters: [ { - "fieldname": "company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company"), - "reqd": 1 + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, }, { - "fieldname": "fiscal_year", - "label": __("Fiscal Year"), - "fieldtype": "Link", - "options": "Fiscal Year", - "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), - "reqd": 1 - } + fieldname: "fiscal_year", + label: __("Fiscal Year"), + fieldtype: "Link", + options: "Fiscal Year", + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), + reqd: 1, + }, ], - onload: function(query_report) { - query_report.page.add_inner_button(__("Export"), function() { + onload: function (query_report) { + query_report.page.add_inner_button(__("Export"), function () { fec_export(query_report); }); - query_report.add_make_chart_button = function() { + query_report.add_make_chart_button = function () { // }; - query_report.export_report = function() { + query_report.export_report = function () { fec_export(query_report); }; - } + }, }; -let fec_export = function(query_report) { +let fec_export = function (query_report) { const fiscal_year = query_report.get_values().fiscal_year; const company = query_report.get_values().company; @@ -46,38 +46,38 @@ let fec_export = function(query_report) { } else { frappe.db.get_value("Fiscal Year", fiscal_year, "year_end_date", (r) => { const fy = r.year_end_date; - const title = company_data + "FEC" + moment(fy).format('YYYYMMDD'); - const column_row = query_report.columns.map(col => col.label); + const title = company_data + "FEC" + moment(fy).format("YYYYMMDD"); + const column_row = query_report.columns.map((col) => col.label); const column_data = query_report.get_data_for_csv(false); const result = [column_row].concat(column_data); downloadify(result, null, title); }); - } }); }; -let downloadify = function(data, roles, title) { +let downloadify = function (data, roles, title) { if (roles && roles.length && !has_common(roles, roles)) { - frappe.msgprint(__("Export not allowed. You need {0} role to export.", [frappe.utils.comma_or(roles)])); + frappe.msgprint( + __("Export not allowed. You need {0} role to export.", [frappe.utils.comma_or(roles)]) + ); return; } const filename = title + ".txt"; let csv_data = to_tab_csv(data); - const a = document.createElement('a'); + const a = document.createElement("a"); if ("download" in a) { // Used Blob object, because it can handle large files let blob_object = new Blob([csv_data], { - type: 'text/csv;charset=UTF-8' + type: "text/csv;charset=UTF-8", }); a.href = URL.createObjectURL(blob_object); a.download = filename; - } else { // use old method - a.href = 'data:attachment/csv,' + encodeURIComponent(csv_data); + a.href = "data:attachment/csv," + encodeURIComponent(csv_data); a.download = filename; a.target = "_blank"; } @@ -88,9 +88,9 @@ let downloadify = function(data, roles, title) { document.body.removeChild(a); }; -let to_tab_csv = function(data) { +let to_tab_csv = function (data) { let res = []; - $.each(data, function(i, row) { + $.each(data, function (i, row) { res.push(row.join("\t")); }); return res.join("\n"); diff --git a/erpnext/regional/report/irs_1099/irs_1099.js b/erpnext/regional/report/irs_1099/irs_1099.js index b3508e40a9f..385468b58aa 100644 --- a/erpnext/regional/report/irs_1099/irs_1099.js +++ b/erpnext/regional/report/irs_1099/irs_1099.js @@ -2,33 +2,33 @@ // License: GNU General Public License v3. See license.txt frappe.query_reports["IRS 1099"] = { - "filters": [ + filters: [ { - "fieldname": "company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company"), - "reqd": 1, - "width": 80, + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, + width: 80, }, { - "fieldname": "fiscal_year", - "label": __("Fiscal Year"), - "fieldtype": "Link", - "options": "Fiscal Year", - "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), - "reqd": 1, - "width": 80, + fieldname: "fiscal_year", + label: __("Fiscal Year"), + fieldtype: "Link", + options: "Fiscal Year", + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), + reqd: 1, + width: 80, }, { - "fieldname": "supplier_group", - "label": __("Supplier Group"), - "fieldtype": "Link", - "options": "Supplier Group", - "default": "", - "reqd": 0, - "width": 80 + fieldname: "supplier_group", + label: __("Supplier Group"), + fieldtype: "Link", + options: "Supplier Group", + default: "", + reqd: 0, + width: 80, }, ], @@ -36,12 +36,15 @@ frappe.query_reports["IRS 1099"] = { query_report.page.add_inner_button(__("Print IRS 1099 Forms"), () => { build_1099_print(query_report); }); - } + }, }; function build_1099_print(query_report) { let filters = JSON.stringify(query_report.get_values()); - let w = window.open('/api/method/erpnext.regional.report.irs_1099.irs_1099.irs_1099_print?' + - '&filters=' + encodeURIComponent(filters)); + let w = window.open( + "/api/method/erpnext.regional.report.irs_1099.irs_1099.irs_1099_print?" + + "&filters=" + + encodeURIComponent(filters) + ); // w.print(); } diff --git a/erpnext/regional/report/ksa_vat/ksa_vat.js b/erpnext/regional/report/ksa_vat/ksa_vat.js index 59e72c3e638..b21ce6b7f70 100644 --- a/erpnext/regional/report/ksa_vat/ksa_vat.js +++ b/erpnext/regional/report/ksa_vat/ksa_vat.js @@ -4,54 +4,56 @@ frappe.query_reports["KSA VAT"] = { onload() { - frappe.breadcrumbs.add('Accounts'); + frappe.breadcrumbs.add("Accounts"); }, - "filters": [ + filters: [ { - "fieldname": "company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "reqd": 1, - "default": frappe.defaults.get_user_default("Company") + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + reqd: 1, + default: frappe.defaults.get_user_default("Company"), }, { - "fieldname": "from_date", - "label": __("From Date"), - "fieldtype": "Date", - "reqd": 1, - "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + reqd: 1, + default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), }, { - "fieldname": "to_date", - "label": __("To Date"), - "fieldtype": "Date", - "reqd": 1, - "default": frappe.datetime.get_today() - } + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + reqd: 1, + default: frappe.datetime.get_today(), + }, ], - "formatter": function(value, row, column, data, default_formatter) { - if (data - && (data.title=='VAT on Sales' || data.title=='VAT on Purchases') - && data.title==value) { + formatter: function (value, row, column, data, default_formatter) { + if ( + data && + (data.title == "VAT on Sales" || data.title == "VAT on Purchases") && + data.title == value + ) { value = $(`${value}`); var $value = $(value).css("font-weight", "bold"); value = $value.wrap("

              ").parent().html(); - return value - }else if (data.title=='Grand Total'){ - if (data.title==value) { + return value; + } else if (data.title == "Grand Total") { + if (data.title == value) { value = $(`${value}`); var $value = $(value).css("font-weight", "bold"); value = $value.wrap("

              ").parent().html(); - return value - }else{ + return value; + } else { value = default_formatter(value, row, column, data); value = $(`${value}`); var $value = $(value).css("font-weight", "bold"); value = $value.wrap("

              ").parent().html(); - return value + return value; } - }else{ + } else { value = default_formatter(value, row, column, data); return value; } diff --git a/erpnext/regional/report/uae_vat_201/uae_vat_201.js b/erpnext/regional/report/uae_vat_201/uae_vat_201.js index 59574247701..ddfa5d42689 100644 --- a/erpnext/regional/report/uae_vat_201/uae_vat_201.js +++ b/erpnext/regional/report/uae_vat_201/uae_vat_201.js @@ -3,34 +3,37 @@ /* eslint-disable */ frappe.query_reports["UAE VAT 201"] = { - "filters": [ + filters: [ { - "fieldname": "company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "reqd": 1, - "default": frappe.defaults.get_user_default("Company") + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + reqd: 1, + default: frappe.defaults.get_user_default("Company"), }, { - "fieldname": "from_date", - "label": __("From Date"), - "fieldtype": "Date", - "reqd": 1, - "default": frappe.datetime.add_months(frappe.datetime.get_today(), -3), + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + reqd: 1, + default: frappe.datetime.add_months(frappe.datetime.get_today(), -3), }, { - "fieldname": "to_date", - "label": __("To Date"), - "fieldtype": "Date", - "reqd": 1, - "default": frappe.datetime.get_today() + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + reqd: 1, + default: frappe.datetime.get_today(), }, ], - "formatter": function(value, row, column, data, default_formatter) { - if (data - && (data.legend=='VAT on Sales and All Other Outputs' || data.legend=='VAT on Expenses and All Other Inputs') - && data.legend==value) { + formatter: function (value, row, column, data, default_formatter) { + if ( + data && + (data.legend == "VAT on Sales and All Other Outputs" || + data.legend == "VAT on Expenses and All Other Inputs") && + data.legend == value + ) { value = $(`${value}`); var $value = $(value).css("font-weight", "bold"); value = $value.wrap("

              ").parent().html(); diff --git a/erpnext/regional/report/vat_audit_report/vat_audit_report.js b/erpnext/regional/report/vat_audit_report/vat_audit_report.js index 39ef9b563ac..fa8c8a99e07 100644 --- a/erpnext/regional/report/vat_audit_report/vat_audit_report.js +++ b/erpnext/regional/report/vat_audit_report/vat_audit_report.js @@ -3,29 +3,29 @@ /* eslint-disable */ frappe.query_reports["VAT Audit Report"] = { - "filters": [ + filters: [ { - "fieldname": "company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "reqd": 1, - "default": frappe.defaults.get_user_default("Company") + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + reqd: 1, + default: frappe.defaults.get_user_default("Company"), }, { - "fieldname": "from_date", - "label": __("From Date"), - "fieldtype": "Date", - "reqd": 1, - "default": frappe.datetime.add_months(frappe.datetime.get_today(), -2), - "width": "80" + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + reqd: 1, + default: frappe.datetime.add_months(frappe.datetime.get_today(), -2), + width: "80", }, { - "fieldname": "to_date", - "label": __("To Date"), - "fieldtype": "Date", - "reqd": 1, - "default": frappe.datetime.get_today() - } - ] + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + reqd: 1, + default: frappe.datetime.get_today(), + }, + ], }; diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js index 432abfce73b..1507d8e923f 100644 --- a/erpnext/selling/doctype/customer/customer.js +++ b/erpnext/selling/doctype/customer/customer.js @@ -2,147 +2,172 @@ // License: GNU General Public License v3. See license.txt frappe.ui.form.on("Customer", { - setup: function(frm) { - + setup: function (frm) { frm.make_methods = { - 'Quotation': () => frappe.model.open_mapped_doc({ - method: "erpnext.selling.doctype.customer.customer.make_quotation", - frm: cur_frm - }), - 'Opportunity': () => frappe.model.open_mapped_doc({ - method: "erpnext.selling.doctype.customer.customer.make_opportunity", - frm: cur_frm - }) - } + Quotation: () => + frappe.model.open_mapped_doc({ + method: "erpnext.selling.doctype.customer.customer.make_quotation", + frm: cur_frm, + }), + Opportunity: () => + frappe.model.open_mapped_doc({ + method: "erpnext.selling.doctype.customer.customer.make_opportunity", + frm: cur_frm, + }), + }; - frm.add_fetch('lead_name', 'company_name', 'customer_name'); - frm.add_fetch('default_sales_partner','commission_rate','default_commission_rate'); - frm.set_query('customer_group', {'is_group': 0}); - frm.set_query('default_price_list', { 'selling': 1}); - frm.set_query('account', 'accounts', function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; + frm.add_fetch("lead_name", "company_name", "customer_name"); + frm.add_fetch("default_sales_partner", "commission_rate", "default_commission_rate"); + frm.set_query("customer_group", { is_group: 0 }); + frm.set_query("default_price_list", { selling: 1 }); + frm.set_query("account", "accounts", function (doc, cdt, cdn) { + var d = locals[cdt][cdn]; var filters = { - 'account_type': 'Receivable', - 'company': d.company, - "is_group": 0 + account_type: "Receivable", + company: d.company, + is_group: 0, }; - if(doc.party_account_currency) { - $.extend(filters, {"account_currency": doc.party_account_currency}); + if (doc.party_account_currency) { + $.extend(filters, { account_currency: doc.party_account_currency }); } return { - filters: filters - } + filters: filters, + }; }); if (frm.doc.__islocal == 1) { frm.set_value("represents_company", ""); } - frm.set_query('customer_primary_contact', function(doc) { + frm.set_query("customer_primary_contact", function (doc) { return { query: "erpnext.selling.doctype.customer.customer.get_customer_primary_contact", filters: { - 'customer': doc.name - } - } - }) - frm.set_query('customer_primary_address', function(doc) { + customer: doc.name, + }, + }; + }); + frm.set_query("customer_primary_address", function (doc) { return { filters: { - 'link_doctype': 'Customer', - 'link_name': doc.name - } - } - }) + link_doctype: "Customer", + link_name: doc.name, + }, + }; + }); - frm.set_query('default_bank_account', function() { + frm.set_query("default_bank_account", function () { return { filters: { - 'is_company_account': 1 - } - } + is_company_account: 1, + }, + }; }); }, - customer_primary_address: function(frm){ - if(frm.doc.customer_primary_address){ + customer_primary_address: function (frm) { + if (frm.doc.customer_primary_address) { frappe.call({ - method: 'frappe.contacts.doctype.address.address.get_address_display', + method: "frappe.contacts.doctype.address.address.get_address_display", args: { - "address_dict": frm.doc.customer_primary_address + address_dict: frm.doc.customer_primary_address, }, - callback: function(r) { + callback: function (r) { frm.set_value("primary_address", r.message); - } + }, }); } - if(!frm.doc.customer_primary_address){ + if (!frm.doc.customer_primary_address) { frm.set_value("primary_address", ""); } }, - is_internal_customer: function(frm) { + is_internal_customer: function (frm) { if (frm.doc.is_internal_customer == 1) { frm.toggle_reqd("represents_company", true); - } - else { + } else { frm.toggle_reqd("represents_company", false); } }, - customer_primary_contact: function(frm){ - if(!frm.doc.customer_primary_contact){ + customer_primary_contact: function (frm) { + if (!frm.doc.customer_primary_contact) { frm.set_value("mobile_no", ""); frm.set_value("email_id", ""); } }, - loyalty_program: function(frm) { - if(frm.doc.loyalty_program) { - frm.set_value('loyalty_program_tier', null); + loyalty_program: function (frm) { + if (frm.doc.loyalty_program) { + frm.set_value("loyalty_program_tier", null); } }, - refresh: function(frm) { - if(frappe.defaults.get_default("cust_master_name")!="Naming Series") { + refresh: function (frm) { + if (frappe.defaults.get_default("cust_master_name") != "Naming Series") { frm.toggle_display("naming_series", false); } else { erpnext.toggle_naming_series(); } - frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Customer'} + frappe.dynamic_link = { doc: frm.doc, fieldname: "name", doctype: "Customer" }; - if(!frm.doc.__islocal) { + if (!frm.doc.__islocal) { frappe.contacts.render_address_and_contact(frm); // custom buttons - frm.add_custom_button(__('Accounts Receivable'), function () { - frappe.set_route('query-report', 'Accounts Receivable', { party_type: "Customer", party: frm.doc.name }); - }, __('View')); + frm.add_custom_button( + __("Accounts Receivable"), + function () { + frappe.set_route("query-report", "Accounts Receivable", { + party_type: "Customer", + party: frm.doc.name, + }); + }, + __("View") + ); - frm.add_custom_button(__('Accounting Ledger'), function () { - frappe.set_route('query-report', 'General Ledger', - {party_type: 'Customer', party: frm.doc.name, party_name: frm.doc.customer_name}); - }, __('View')); + frm.add_custom_button( + __("Accounting Ledger"), + function () { + frappe.set_route("query-report", "General Ledger", { + party_type: "Customer", + party: frm.doc.name, + party_name: frm.doc.customer_name, + }); + }, + __("View") + ); - frm.add_custom_button(__('Pricing Rule'), function () { - erpnext.utils.make_pricing_rule(frm.doc.doctype, frm.doc.name); - }, __('Create')); + frm.add_custom_button( + __("Pricing Rule"), + function () { + erpnext.utils.make_pricing_rule(frm.doc.doctype, frm.doc.name); + }, + __("Create") + ); - frm.add_custom_button(__('Get Customer Group Details'), function () { - frm.trigger("get_customer_group_details"); - }, __('Actions')); + frm.add_custom_button( + __("Get Customer Group Details"), + function () { + frm.trigger("get_customer_group_details"); + }, + __("Actions") + ); if (cint(frappe.defaults.get_default("enable_common_party_accounting"))) { - frm.add_custom_button(__('Link with Supplier'), function () { - frm.trigger('show_party_link_dialog'); - }, __('Actions')); + frm.add_custom_button( + __("Link with Supplier"), + function () { + frm.trigger("show_party_link_dialog"); + }, + __("Actions") + ); } // indicator erpnext.utils.set_party_dashboard_indicators(frm); - } else { frappe.contacts.clear_address_and_contact(frm); } @@ -151,55 +176,58 @@ frappe.ui.form.on("Customer", { grid.set_column_disp("allocated_amount", false); grid.set_column_disp("incentives", false); }, - validate: function(frm) { - if(frm.doc.lead_name) frappe.model.clear_doc("Lead", frm.doc.lead_name); - + validate: function (frm) { + if (frm.doc.lead_name) frappe.model.clear_doc("Lead", frm.doc.lead_name); }, - get_customer_group_details: function(frm) { + get_customer_group_details: function (frm) { frappe.call({ method: "get_customer_group_details", doc: frm.doc, - callback: function() { + callback: function () { frm.refresh(); - } + }, }); - }, - show_party_link_dialog: function(frm) { + show_party_link_dialog: function (frm) { const dialog = new frappe.ui.Dialog({ - title: __('Select a Supplier'), - fields: [{ - fieldtype: 'Link', label: __('Supplier'), - options: 'Supplier', fieldname: 'supplier', reqd: 1 - }], - primary_action: function({ supplier }) { + title: __("Select a Supplier"), + fields: [ + { + fieldtype: "Link", + label: __("Supplier"), + options: "Supplier", + fieldname: "supplier", + reqd: 1, + }, + ], + primary_action: function ({ supplier }) { frappe.call({ - method: 'erpnext.accounts.doctype.party_link.party_link.create_party_link', + method: "erpnext.accounts.doctype.party_link.party_link.create_party_link", args: { - primary_role: 'Customer', + primary_role: "Customer", primary_party: frm.doc.name, - secondary_party: supplier + secondary_party: supplier, }, freeze: true, - callback: function() { + callback: function () { dialog.hide(); frappe.msgprint({ - message: __('Successfully linked to Supplier'), - alert: true + message: __("Successfully linked to Supplier"), + alert: true, }); }, - error: function() { + error: function () { dialog.hide(); frappe.msgprint({ - message: __('Linking to Supplier Failed. Please try again.'), - title: __('Linking Failed'), - indicator: 'red' + message: __("Linking to Supplier Failed. Please try again."), + title: __("Linking Failed"), + indicator: "red", }); - } + }, }); }, - primary_action_label: __('Create Link') + primary_action_label: __("Create Link"), }); dialog.show(); - } + }, }); diff --git a/erpnext/selling/doctype/customer/customer_list.js b/erpnext/selling/doctype/customer/customer_list.js index 38fc9ad1f13..6149c6f873d 100644 --- a/erpnext/selling/doctype/customer/customer_list.js +++ b/erpnext/selling/doctype/customer/customer_list.js @@ -1,3 +1,3 @@ -frappe.listview_settings['Customer'] = { +frappe.listview_settings["Customer"] = { add_fields: ["customer_name", "territory", "customer_group", "customer_type", "image"], }; diff --git a/erpnext/selling/doctype/industry_type/industry_type.js b/erpnext/selling/doctype/industry_type/industry_type.js index 3680906057f..273e30fd197 100644 --- a/erpnext/selling/doctype/industry_type/industry_type.js +++ b/erpnext/selling/doctype/industry_type/industry_type.js @@ -1,13 +1,7 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt - - //--------- ONLOAD ------------- -cur_frm.cscript.onload = function(doc, cdt, cdn) { +cur_frm.cscript.onload = function (doc, cdt, cdn) {}; -} - -cur_frm.cscript.refresh = function(doc, cdt, cdn) { - -} +cur_frm.cscript.refresh = function (doc, cdt, cdn) {}; diff --git a/erpnext/selling/doctype/installation_note/installation_note.js b/erpnext/selling/doctype/installation_note/installation_note.js index 27a3b35ccfb..f26b40bdb04 100644 --- a/erpnext/selling/doctype/installation_note/installation_note.js +++ b/erpnext/selling/doctype/installation_note/installation_note.js @@ -1,30 +1,30 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.ui.form.on('Installation Note', { - setup: function(frm) { - frappe.dynamic_link = {doc: frm.doc, fieldname: 'customer', doctype: 'Customer'} - frm.set_query('customer_address', erpnext.queries.address_query); - frm.set_query('contact_person', erpnext.queries.contact_query); - frm.set_query('customer', erpnext.queries.customer); +frappe.ui.form.on("Installation Note", { + setup: function (frm) { + frappe.dynamic_link = { doc: frm.doc, fieldname: "customer", doctype: "Customer" }; + frm.set_query("customer_address", erpnext.queries.address_query); + frm.set_query("contact_person", erpnext.queries.contact_query); + frm.set_query("customer", erpnext.queries.customer); }, - onload: function(frm) { - if(!frm.doc.status) { - frm.set_value({ status:'Draft'}); + onload: function (frm) { + if (!frm.doc.status) { + frm.set_value({ status: "Draft" }); } - if(frm.doc.__islocal) { - frm.set_value({inst_date: frappe.datetime.get_today()}); + if (frm.doc.__islocal) { + frm.set_value({ inst_date: frappe.datetime.get_today() }); } }, - customer: function(frm) { + customer: function (frm) { erpnext.utils.get_party_details(frm); }, - customer_address: function(frm) { + customer_address: function (frm) { erpnext.utils.get_address_display(frm); }, - contact_person: function(frm) { + contact_person: function (frm) { erpnext.utils.get_contact_details(frm); - } + }, }); frappe.provide("erpnext.selling"); @@ -33,9 +33,10 @@ frappe.provide("erpnext.selling"); erpnext.selling.InstallationNote = class InstallationNote extends frappe.ui.form.Controller { refresh() { var me = this; - if (this.frm.doc.docstatus===0) { - this.frm.add_custom_button(__('From Delivery Note'), - function() { + if (this.frm.doc.docstatus === 0) { + this.frm.add_custom_button( + __("From Delivery Note"), + function () { erpnext.utils.map_current_doc({ method: "erpnext.stock.doctype.delivery_note.delivery_note.make_installation_note", source_doctype: "Delivery Note", @@ -48,13 +49,15 @@ erpnext.selling.InstallationNote = class InstallationNote extends frappe.ui.form docstatus: 1, status: ["not in", ["Stopped", "Closed"]], per_installed: ["<", 99.99], - company: me.frm.doc.company - } - }) - }, "fa fa-download", "btn-default" + company: me.frm.doc.company, + }, + }); + }, + "fa fa-download", + "btn-default" ); } } }; -extend_cscript(cur_frm.cscript, new erpnext.selling.InstallationNote({frm: cur_frm})); +extend_cscript(cur_frm.cscript, new erpnext.selling.InstallationNote({ frm: cur_frm })); diff --git a/erpnext/selling/doctype/party_specific_item/party_specific_item.js b/erpnext/selling/doctype/party_specific_item/party_specific_item.js index 077b93631ec..0decc70d7ac 100644 --- a/erpnext/selling/doctype/party_specific_item/party_specific_item.js +++ b/erpnext/selling/doctype/party_specific_item/party_specific_item.js @@ -1,8 +1,7 @@ // Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Party Specific Item', { +frappe.ui.form.on("Party Specific Item", { // refresh: function(frm) { - // } }); diff --git a/erpnext/selling/doctype/quotation/quotation_list.js b/erpnext/selling/doctype/quotation/quotation_list.js index 32fce1f2ad8..ae744b9cba3 100644 --- a/erpnext/selling/doctype/quotation/quotation_list.js +++ b/erpnext/selling/doctype/quotation/quotation_list.js @@ -1,38 +1,37 @@ -frappe.listview_settings['Quotation'] = { - add_fields: ["customer_name", "base_grand_total", "status", - "company", "currency", 'valid_till'], +frappe.listview_settings["Quotation"] = { + add_fields: ["customer_name", "base_grand_total", "status", "company", "currency", "valid_till"], - onload: function(listview) { + onload: function (listview) { if (listview.page.fields_dict.quotation_to) { - listview.page.fields_dict.quotation_to.get_query = function() { + listview.page.fields_dict.quotation_to.get_query = function () { return { - "filters": { - "name": ["in", ["Customer", "Lead"]], - } + filters: { + name: ["in", ["Customer", "Lead"]], + }, }; }; } - listview.page.add_action_item(__("Sales Order"), ()=>{ + listview.page.add_action_item(__("Sales Order"), () => { erpnext.bulk_transaction_processing.create(listview, "Quotation", "Sales Order"); }); - listview.page.add_action_item(__("Sales Invoice"), ()=>{ + listview.page.add_action_item(__("Sales Invoice"), () => { erpnext.bulk_transaction_processing.create(listview, "Quotation", "Sales Invoice"); }); }, - get_indicator: function(doc) { - if(doc.status==="Open") { + get_indicator: function (doc) { + if (doc.status === "Open") { return [__("Open"), "orange", "status,=,Open"]; - } else if (doc.status==="Partially Ordered") { + } else if (doc.status === "Partially Ordered") { return [__("Partially Ordered"), "yellow", "status,=,Partially Ordered"]; - } else if(doc.status==="Ordered") { + } else if (doc.status === "Ordered") { return [__("Ordered"), "green", "status,=,Ordered"]; - } else if(doc.status==="Lost") { + } else if (doc.status === "Lost") { return [__("Lost"), "gray", "status,=,Lost"]; - } else if(doc.status==="Expired") { + } else if (doc.status === "Expired") { return [__("Expired"), "gray", "status,=,Expired"]; } - } + }, }; diff --git a/erpnext/selling/doctype/sales_order/sales_order_calendar.js b/erpnext/selling/doctype/sales_order/sales_order_calendar.js index cb412cfb4a7..f4c0e2ba72a 100644 --- a/erpnext/selling/doctype/sales_order/sales_order_calendar.js +++ b/erpnext/selling/doctype/sales_order/sales_order_calendar.js @@ -3,43 +3,44 @@ frappe.views.calendar["Sales Order"] = { field_map: { - "start": "delivery_date", - "end": "delivery_date", - "id": "name", - "title": "customer_name", - "allDay": "allDay" + start: "delivery_date", + end: "delivery_date", + id: "name", + title: "customer_name", + allDay: "allDay", }, gantt: true, filters: [ { - "fieldtype": "Link", - "fieldname": "customer", - "options": "Customer", - "label": __("Customer") + fieldtype: "Link", + fieldname: "customer", + options: "Customer", + label: __("Customer"), }, { - "fieldtype": "Select", - "fieldname": "delivery_status", - "options": "Not Delivered\nFully Delivered\nPartly Delivered\nClosed\nNot Applicable", - "label": __("Delivery Status") + fieldtype: "Select", + fieldname: "delivery_status", + options: "Not Delivered\nFully Delivered\nPartly Delivered\nClosed\nNot Applicable", + label: __("Delivery Status"), }, { - "fieldtype": "Select", - "fieldname": "billing_status", - "options": "Not Billed\nFully Billed\nPartly Billed\nClosed", - "label": __("Billing Status") + fieldtype: "Select", + fieldname: "billing_status", + options: "Not Billed\nFully Billed\nPartly Billed\nClosed", + label: __("Billing Status"), }, ], get_events_method: "erpnext.selling.doctype.sales_order.sales_order.get_events", - get_css_class: function(data) { - if(data.status=="Closed") { - return "success"; - } if(data.delivery_status=="Not Delivered") { - return "danger"; - } else if(data.delivery_status=="Partly Delivered") { - return "warning"; - } else if(data.delivery_status=="Fully Delivered") { + get_css_class: function (data) { + if (data.status == "Closed") { return "success"; } - } -} + if (data.delivery_status == "Not Delivered") { + return "danger"; + } else if (data.delivery_status == "Partly Delivered") { + return "warning"; + } else if (data.delivery_status == "Fully Delivered") { + return "success"; + } + }, +}; diff --git a/erpnext/selling/doctype/sales_order/sales_order_list.js b/erpnext/selling/doctype/sales_order/sales_order_list.js index 64c58ef5d7b..61a29c9bcd7 100644 --- a/erpnext/selling/doctype/sales_order/sales_order_list.js +++ b/erpnext/selling/doctype/sales_order/sales_order_list.js @@ -1,6 +1,16 @@ -frappe.listview_settings['Sales Order'] = { - add_fields: ["base_grand_total", "customer_name", "currency", "delivery_date", - "per_delivered", "per_billed", "status", "order_type", "name", "skip_delivery_note"], +frappe.listview_settings["Sales Order"] = { + add_fields: [ + "base_grand_total", + "customer_name", + "currency", + "delivery_date", + "per_delivered", + "per_billed", + "status", + "order_type", + "name", + "skip_delivery_note", + ], get_indicator: function (doc) { if (doc.status === "Closed") { // Closed @@ -12,53 +22,54 @@ frappe.listview_settings['Sales Order'] = { return [__("Completed"), "green", "status,=,Completed"]; } else if (!doc.skip_delivery_note && flt(doc.per_delivered, 6) < 100) { if (frappe.datetime.get_diff(doc.delivery_date) < 0) { - // not delivered & overdue - return [__("Overdue"), "red", - "per_delivered,<,100|delivery_date,<,Today|status,!=,Closed"]; + // 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"]; + return [__("To Deliver"), "orange", "per_delivered,<,100|grand_total,=,0|status,!=,Closed"]; } else if (flt(doc.per_billed, 6) < 100) { // not delivered & not billed - return [__("To Deliver and Bill"), "orange", - "per_delivered,<,100|per_billed,<,100|status,!=,Closed"]; + return [ + __("To Deliver and Bill"), + "orange", + "per_delivered,<,100|per_billed,<,100|status,!=,Closed", + ]; } else { // not billed - return [__("To Deliver"), "orange", - "per_delivered,<,100|per_billed,=,100|status,!=,Closed"]; + return [__("To Deliver"), "orange", "per_delivered,<,100|per_billed,=,100|status,!=,Closed"]; } - } else if ((flt(doc.per_delivered, 6) === 100) && flt(doc.grand_total) !== 0 - && flt(doc.per_billed, 6) < 100) { + } else if ( + flt(doc.per_delivered, 6) === 100 && + flt(doc.grand_total) !== 0 && + flt(doc.per_billed, 6) < 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, 6) < 100){ + return [__("To Bill"), "orange", "per_delivered,=,100|per_billed,<,100|status,!=,Closed"]; + } else if (doc.skip_delivery_note && flt(doc.per_billed, 6) < 100) { return [__("To Bill"), "orange", "per_billed,<,100|status,!=,Closed"]; } }, - onload: function(listview) { + onload: function (listview) { var method = "erpnext.selling.doctype.sales_order.sales_order.close_or_unclose_sales_orders"; - listview.page.add_menu_item(__("Close"), function() { - listview.call_for_selected_items(method, {"status": "Closed"}); + listview.page.add_menu_item(__("Close"), function () { + listview.call_for_selected_items(method, { status: "Closed" }); }); - listview.page.add_menu_item(__("Re-open"), function() { - listview.call_for_selected_items(method, {"status": "Submitted"}); + listview.page.add_menu_item(__("Re-open"), function () { + listview.call_for_selected_items(method, { status: "Submitted" }); }); - listview.page.add_action_item(__("Sales Invoice"), ()=>{ + listview.page.add_action_item(__("Sales Invoice"), () => { erpnext.bulk_transaction_processing.create(listview, "Sales Order", "Sales Invoice"); }); - listview.page.add_action_item(__("Delivery Note"), ()=>{ + listview.page.add_action_item(__("Delivery Note"), () => { erpnext.bulk_transaction_processing.create(listview, "Sales Order", "Delivery Note"); }); - listview.page.add_action_item(__("Advance Payment"), ()=>{ + listview.page.add_action_item(__("Advance Payment"), () => { erpnext.bulk_transaction_processing.create(listview, "Sales Order", "Payment Entry"); }); - - } + }, }; diff --git a/erpnext/selling/doctype/sales_partner_type/sales_partner_type.js b/erpnext/selling/doctype/sales_partner_type/sales_partner_type.js index 3e183f6e6fc..ac5a8134f3c 100644 --- a/erpnext/selling/doctype/sales_partner_type/sales_partner_type.js +++ b/erpnext/selling/doctype/sales_partner_type/sales_partner_type.js @@ -1,8 +1,6 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Sales Partner Type', { - refresh: function() { - - } +frappe.ui.form.on("Sales Partner Type", { + refresh: function () {}, }); diff --git a/erpnext/selling/doctype/selling_settings/selling_settings.js b/erpnext/selling/doctype/selling_settings/selling_settings.js index cf6fb2806ee..4471458fb10 100644 --- a/erpnext/selling/doctype/selling_settings/selling_settings.js +++ b/erpnext/selling/doctype/selling_settings/selling_settings.js @@ -1,8 +1,6 @@ // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Selling Settings', { - refresh: function(frm) { - - } +frappe.ui.form.on("Selling Settings", { + refresh: function (frm) {}, }); diff --git a/erpnext/selling/doctype/sms_center/sms_center.js b/erpnext/selling/doctype/sms_center/sms_center.js index 974cfc79181..f513213e71c 100644 --- a/erpnext/selling/doctype/sms_center/sms_center.js +++ b/erpnext/selling/doctype/sms_center/sms_center.js @@ -8,10 +8,10 @@ extend_cscript(cur_frm.cscript, { if (total_characters > 160) { total_msg = cint(total_characters / 160); - total_msg = (total_characters % 160 == 0 ? total_msg : total_msg + 1); + total_msg = total_characters % 160 == 0 ? total_msg : total_msg + 1; } this.frm.set_value("total_characters", total_characters); this.frm.set_value("total_messages", this.frm.doc.message ? total_msg : 0); - } + }, }); diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.js b/erpnext/selling/page/point_of_sale/point_of_sale.js index 6db4150be94..6e4d1d0a373 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.js +++ b/erpnext/selling/page/point_of_sale/point_of_sale.js @@ -1,19 +1,19 @@ -frappe.provide('erpnext.PointOfSale'); +frappe.provide("erpnext.PointOfSale"); -frappe.pages['point-of-sale'].on_page_load = function(wrapper) { +frappe.pages["point-of-sale"].on_page_load = function (wrapper) { frappe.ui.make_app_page({ parent: wrapper, - title: __('Point of Sale'), - single_column: true + title: __("Point of Sale"), + single_column: true, }); - frappe.require('point-of-sale.bundle.js', function() { + frappe.require("point-of-sale.bundle.js", function () { wrapper.pos = new erpnext.PointOfSale.Controller(wrapper); window.cur_pos = wrapper.pos; }); }; -frappe.pages['point-of-sale'].refresh = function(wrapper) { +frappe.pages["point-of-sale"].refresh = function (wrapper) { if (document.scannerDetectionData) { onScan.detachFrom(document); wrapper.pos.wrapper.html(""); diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js index 07b1c2eb7ee..3d42f3ea23f 100644 --- a/erpnext/selling/page/point_of_sale/pos_controller.js +++ b/erpnext/selling/page/point_of_sale/pos_controller.js @@ -1,13 +1,15 @@ erpnext.PointOfSale.Controller = class { constructor(wrapper) { - this.wrapper = $(wrapper).find('.layout-main-section'); + this.wrapper = $(wrapper).find(".layout-main-section"); this.page = wrapper.page; this.check_opening_entry(); } fetch_opening_entry() { - return frappe.call("erpnext.selling.page.point_of_sale.point_of_sale.check_opening_entry", { "user": frappe.session.user }); + return frappe.call("erpnext.selling.page.point_of_sale.point_of_sale.check_opening_entry", { + user: frappe.session.user, + }); } check_opening_entry() { @@ -25,50 +27,62 @@ erpnext.PointOfSale.Controller = class { const me = this; const table_fields = [ { - fieldname: "mode_of_payment", fieldtype: "Link", - in_list_view: 1, label: "Mode of Payment", - options: "Mode of Payment", reqd: 1 + fieldname: "mode_of_payment", + fieldtype: "Link", + in_list_view: 1, + label: "Mode of Payment", + options: "Mode of Payment", + reqd: 1, }, { - fieldname: "opening_amount", fieldtype: "Currency", - in_list_view: 1, label: "Opening Amount", + fieldname: "opening_amount", + fieldtype: "Currency", + in_list_view: 1, + label: "Opening Amount", options: "company:company_currency", change: function () { - dialog.fields_dict.balance_details.df.data.some(d => { + dialog.fields_dict.balance_details.df.data.some((d) => { if (d.idx == this.doc.idx) { d.opening_amount = this.value; dialog.fields_dict.balance_details.grid.refresh(); return true; } }); - } - } + }, + }, ]; const fetch_pos_payment_methods = () => { const pos_profile = dialog.fields_dict.pos_profile.get_value(); if (!pos_profile) return; frappe.db.get_doc("POS Profile", pos_profile).then(({ payments }) => { dialog.fields_dict.balance_details.df.data = []; - payments.forEach(pay => { + payments.forEach((pay) => { const { mode_of_payment } = pay; - dialog.fields_dict.balance_details.df.data.push({ mode_of_payment, opening_amount: '0' }); + dialog.fields_dict.balance_details.df.data.push({ mode_of_payment, opening_amount: "0" }); }); dialog.fields_dict.balance_details.grid.refresh(); }); - } + }; const dialog = new frappe.ui.Dialog({ - title: __('Create POS Opening Entry'), + title: __("Create POS Opening Entry"), static: true, fields: [ { - fieldtype: 'Link', label: __('Company'), default: frappe.defaults.get_default('company'), - options: 'Company', fieldname: 'company', reqd: 1 + fieldtype: "Link", + label: __("Company"), + default: frappe.defaults.get_default("company"), + options: "Company", + fieldname: "company", + reqd: 1, }, { - fieldtype: 'Link', label: __('POS Profile'), - options: 'POS Profile', fieldname: 'pos_profile', reqd: 1, + fieldtype: "Link", + label: __("POS Profile"), + options: "POS Profile", + fieldname: "pos_profile", + reqd: 1, get_query: () => pos_profile_query(), - onchange: () => fetch_pos_payment_methods() + onchange: () => fetch_pos_payment_methods(), }, { fieldname: "balance_details", @@ -78,34 +92,38 @@ erpnext.PointOfSale.Controller = class { in_place_edit: true, reqd: 1, data: [], - fields: table_fields - } + fields: table_fields, + }, ], - primary_action: async function({ company, pos_profile, balance_details }) { + primary_action: async function ({ company, pos_profile, balance_details }) { if (!balance_details.length) { frappe.show_alert({ message: __("Please add Mode of payments and opening balance details."), - indicator: 'red' - }) + indicator: "red", + }); return frappe.utils.play_sound("error"); } // filter balance details for empty rows - balance_details = balance_details.filter(d => d.mode_of_payment); + balance_details = balance_details.filter((d) => d.mode_of_payment); const method = "erpnext.selling.page.point_of_sale.point_of_sale.create_opening_voucher"; - const res = await frappe.call({ method, args: { pos_profile, company, balance_details }, freeze:true }); + const res = await frappe.call({ + method, + args: { pos_profile, company, balance_details }, + freeze: true, + }); !res.exc && me.prepare_app_defaults(res.message); dialog.hide(); }, - primary_action_label: __('Submit') + primary_action_label: __("Submit"), }); dialog.show(); const pos_profile_query = () => { return { - query: 'erpnext.accounts.doctype.pos_profile.pos_profile.pos_profile_query', - filters: { company: dialog.fields_dict.company.get_value() } - } + query: "erpnext.accounts.doctype.pos_profile.pos_profile.pos_profile_query", + filters: { company: dialog.fields_dict.company.get_value() }, + }; }; } @@ -117,19 +135,19 @@ erpnext.PointOfSale.Controller = class { this.item_stock_map = {}; this.settings = {}; - frappe.db.get_value('Stock Settings', undefined, 'allow_negative_stock').then(({ message }) => { + frappe.db.get_value("Stock Settings", undefined, "allow_negative_stock").then(({ message }) => { this.allow_negative_stock = flt(message.allow_negative_stock) || false; }); frappe.call({ method: "erpnext.selling.page.point_of_sale.point_of_sale.get_pos_profile_data", - args: { "pos_profile": this.pos_profile }, + args: { pos_profile: this.pos_profile }, callback: (res) => { const profile = res.message; Object.assign(this.settings, profile); - this.settings.customer_groups = profile.customer_groups.map(group => group.name); + this.settings.customer_groups = profile.customer_groups.map((group) => group.name); this.make_app(); - } + }, }); } @@ -139,7 +157,8 @@ erpnext.PointOfSale.Controller = class { Opened at ${moment(this.pos_opening_time).format("Do MMMM, h:mma")} - `); + ` + ); } make_app() { @@ -150,11 +169,9 @@ erpnext.PointOfSale.Controller = class { } prepare_dom() { - this.wrapper.append( - `
              ` - ); + this.wrapper.append(`
              `); - this.$components_wrapper = this.wrapper.find('.point-of-sale-app'); + this.$components_wrapper = this.wrapper.find(".point-of-sale-app"); } prepare_components() { @@ -169,13 +186,18 @@ erpnext.PointOfSale.Controller = class { prepare_menu() { this.page.clear_menu(); - this.page.add_menu_item(__("Open Form View"), this.open_form_view.bind(this), false, 'Ctrl+F'); + this.page.add_menu_item(__("Open Form View"), this.open_form_view.bind(this), false, "Ctrl+F"); - this.page.add_menu_item(__("Toggle Recent Orders"), this.toggle_recent_order.bind(this), false, 'Ctrl+O'); + this.page.add_menu_item( + __("Toggle Recent Orders"), + this.toggle_recent_order.bind(this), + false, + "Ctrl+O" + ); - this.page.add_menu_item(__("Save as Draft"), this.save_draft_invoice.bind(this), false, 'Ctrl+S'); + this.page.add_menu_item(__("Save as Draft"), this.save_draft_invoice.bind(this), false, "Ctrl+S"); - this.page.add_menu_item(__('Close the POS'), this.close_pos.bind(this), false, 'Shift+Ctrl+C'); + this.page.add_menu_item(__("Close the POS"), this.close_pos.bind(this), false, "Shift+Ctrl+C"); } open_form_view() { @@ -184,7 +206,7 @@ erpnext.PointOfSale.Controller = class { } toggle_recent_order() { - const show = this.recent_order_list.$component.is(':hidden'); + const show = this.recent_order_list.$component.is(":hidden"); this.toggle_recent_order_list(show); } @@ -194,38 +216,40 @@ erpnext.PointOfSale.Controller = class { if (this.frm.doc.items.length == 0) { frappe.show_alert({ message: __("You must add atleast one item to save it as draft."), - indicator:'red' + indicator: "red", }); frappe.utils.play_sound("error"); return; } - this.frm.save(undefined, undefined, undefined, () => { - frappe.show_alert({ - message: __("There was an error saving the document."), - indicator: 'red' + this.frm + .save(undefined, undefined, undefined, () => { + frappe.show_alert({ + message: __("There was an error saving the document."), + indicator: "red", + }); + frappe.utils.play_sound("error"); + }) + .then(() => { + frappe.run_serially([ + () => frappe.dom.freeze(), + () => this.make_new_invoice(), + () => frappe.dom.unfreeze(), + ]); }); - frappe.utils.play_sound("error"); - }).then(() => { - frappe.run_serially([ - () => frappe.dom.freeze(), - () => this.make_new_invoice(), - () => frappe.dom.unfreeze(), - ]); - }); } close_pos() { if (!this.$components_wrapper.is(":visible")) return; - let voucher = frappe.model.get_new_doc('POS Closing Entry'); + let voucher = frappe.model.get_new_doc("POS Closing Entry"); voucher.pos_profile = this.frm.doc.pos_profile; voucher.user = frappe.session.user; voucher.company = this.frm.doc.company; voucher.pos_opening_entry = this.pos_opening; voucher.period_end_date = frappe.datetime.now_datetime(); voucher.posting_date = frappe.datetime.now_date(); - frappe.set_route('Form', 'POS Closing Entry', voucher.name); + frappe.set_route("Form", "POS Closing Entry", voucher.name); } init_item_selector() { @@ -234,11 +258,11 @@ erpnext.PointOfSale.Controller = class { pos_profile: this.pos_profile, settings: this.settings, events: { - item_selected: args => this.on_cart_update(args), + item_selected: (args) => this.on_cart_update(args), - get_frm: () => this.frm || {} - } - }) + get_frm: () => this.frm || {}, + }, + }); } init_item_cart() { @@ -263,9 +287,9 @@ erpnext.PointOfSale.Controller = class { this.customer_details = details; // will add/remove LP payment method this.payment.render_loyalty_points_payment_mode(); - } - } - }) + }, + }, + }); } init_item_details() { @@ -286,7 +310,7 @@ erpnext.PointOfSale.Controller = class { const args = { field, value, - item: this.item_details.current_item + item: this.item_details.current_item, }; return this.on_cart_update(args); } @@ -303,24 +327,28 @@ erpnext.PointOfSale.Controller = class { this.cart.toggle_numpad_field_edit(fieldname); }, set_value_in_current_cart_item: (selector, value) => { - this.cart.update_selector_value_in_cart_item(selector, value, this.item_details.current_item); + this.cart.update_selector_value_in_cart_item( + selector, + value, + this.item_details.current_item + ); }, clone_new_batch_item_in_frm: (batch_serial_map, item) => { // called if serial nos are 'auto_selected' and if those serial nos belongs to multiple batches // for each unique batch new item row is added in the form & cart - Object.keys(batch_serial_map).forEach(batch => { - const item_to_clone = this.frm.doc.items.find(i => i.name == item.name); + Object.keys(batch_serial_map).forEach((batch) => { + const item_to_clone = this.frm.doc.items.find((i) => i.name == item.name); const new_row = this.frm.add_child("items", { ...item_to_clone }); // update new serialno and batch new_row.batch_no = batch; new_row.serial_no = batch_serial_map[batch].join(`\n`); new_row.qty = batch_serial_map[batch].length; - this.frm.doc.items.forEach(row => { + this.frm.doc.items.forEach((row) => { if (item.item_code === row.item_code) { this.update_cart_html(row); } }); - }) + }); }, remove_item_from_cart: () => this.remove_item_from_cart(), get_item_stock_map: () => this.item_stock_map, @@ -329,8 +357,8 @@ erpnext.PointOfSale.Controller = class { this.cart.prev_action = null; this.cart.toggle_item_highlight(); }, - get_available_stock: (item_code, warehouse) => this.get_available_stock(item_code, warehouse) - } + get_available_stock: (item_code, warehouse) => this.get_available_stock(item_code, warehouse), + }, }); } @@ -344,7 +372,9 @@ erpnext.PointOfSale.Controller = class { toggle_other_sections: (show) => { if (show) { - this.item_details.$component.is(':visible') ? this.item_details.$component.css('display', 'none') : ''; + this.item_details.$component.is(":visible") + ? this.item_details.$component.css("display", "none") + : ""; this.item_selector.toggle_component(false); } else { this.item_selector.toggle_component(true); @@ -352,18 +382,17 @@ erpnext.PointOfSale.Controller = class { }, submit_invoice: () => { - this.frm.savesubmit() - .then((r) => { - this.toggle_components(false); - this.order_summary.toggle_component(true); - this.order_summary.load_summary_of(this.frm.doc, true); - frappe.show_alert({ - indicator: 'green', - message: __('POS invoice {0} created succesfully', [r.doc.name]) - }); + this.frm.savesubmit().then((r) => { + this.toggle_components(false); + this.order_summary.toggle_component(true); + this.order_summary.load_summary_of(this.frm.doc, true); + frappe.show_alert({ + indicator: "green", + message: __("POS invoice {0} created succesfully", [r.doc.name]), }); - } - } + }); + }, + }, }); } @@ -372,13 +401,13 @@ erpnext.PointOfSale.Controller = class { wrapper: this.$components_wrapper, events: { open_invoice_data: (name) => { - frappe.db.get_doc('POS Invoice', name).then((doc) => { + frappe.db.get_doc("POS Invoice", name).then((doc) => { this.order_summary.load_summary_of(doc); }); }, - reset_summary: () => this.order_summary.toggle_summary_placeholder(true) - } - }) + reset_summary: () => this.order_summary.toggle_summary_placeholder(true), + }, + }); } init_order_summary() { @@ -389,11 +418,11 @@ erpnext.PointOfSale.Controller = class { process_return: (name) => { this.recent_order_list.toggle_component(false); - frappe.db.get_doc('POS Invoice', name).then((doc) => { + frappe.db.get_doc("POS Invoice", name).then((doc) => { frappe.run_serially([ () => this.make_return_invoice(doc), () => this.cart.load_invoice(), - () => this.item_selector.toggle_component(true) + () => this.item_selector.toggle_component(true), ]); }); }, @@ -401,9 +430,9 @@ erpnext.PointOfSale.Controller = class { this.recent_order_list.toggle_component(false); frappe.run_serially([ () => this.frm.refresh(name), - () => this.frm.call('reset_mode_of_payments'), + () => this.frm.call("reset_mode_of_payments"), () => this.cart.load_invoice(), - () => this.item_selector.toggle_component(true) + () => this.item_selector.toggle_component(true), ]); }, delete_order: (name) => { @@ -418,9 +447,9 @@ erpnext.PointOfSale.Controller = class { () => this.item_selector.toggle_component(true), () => frappe.dom.unfreeze(), ]); - } - } - }) + }, + }, + }); } toggle_recent_order_list(show) { @@ -434,7 +463,7 @@ erpnext.PointOfSale.Controller = class { this.item_selector.toggle_component(show); // do not show item details or payment if recent order is toggled off - !show ? (this.item_details.toggle_component(false) || this.payment.toggle_component(false)) : ''; + !show ? this.item_details.toggle_component(false) || this.payment.toggle_component(false) : ""; } make_new_invoice() { @@ -444,23 +473,23 @@ erpnext.PointOfSale.Controller = class { () => this.set_pos_profile_data(), () => this.set_pos_profile_status(), () => this.cart.load_invoice(), - () => frappe.dom.unfreeze() + () => frappe.dom.unfreeze(), ]); } make_sales_invoice_frm() { - const doctype = 'POS Invoice'; - return new Promise(resolve => { + const doctype = "POS Invoice"; + return new Promise((resolve) => { if (this.frm) { this.frm = this.get_new_frm(this.frm); this.frm.doc.items = []; - this.frm.doc.is_pos = 1 + this.frm.doc.is_pos = 1; resolve(); } else { frappe.model.with_doctype(doctype, () => { this.frm = this.get_new_frm(); this.frm.doc.items = []; - this.frm.doc.is_pos = 1 + this.frm.doc.is_pos = 1; resolve(); }); } @@ -468,8 +497,8 @@ erpnext.PointOfSale.Controller = class { } get_new_frm(_frm) { - const doctype = 'POS Invoice'; - const page = $('
              '); + const doctype = "POS Invoice"; + const page = $("
              "); const frm = _frm || new frappe.ui.form.Form(doctype, page, false); const name = frappe.model.make_new_doc_and_get_name(doctype, true); frm.refresh(name); @@ -484,8 +513,8 @@ erpnext.PointOfSale.Controller = class { return frappe.call({ method: "erpnext.accounts.doctype.pos_invoice.pos_invoice.make_sales_return", args: { - 'source_name': doc.name, - 'target_doc': this.frm.doc + source_name: doc.name, + target_doc: this.frm.doc, }, callback: (r) => { frappe.model.sync(r.message); @@ -493,13 +522,16 @@ erpnext.PointOfSale.Controller = class { this.set_pos_profile_data().then(() => { frappe.dom.unfreeze(); }); - } + }, }); } set_pos_profile_data() { if (this.company && !this.frm.doc.company) this.frm.doc.company = this.company; - if ((this.pos_profile && !this.frm.doc.pos_profile) | (this.frm.doc.is_return && this.pos_profile != this.frm.doc.pos_profile)) { + if ( + (this.pos_profile && !this.frm.doc.pos_profile) | + (this.frm.doc.is_return && this.pos_profile != this.frm.doc.pos_profile) + ) { this.frm.doc.pos_profile = this.pos_profile; } @@ -520,16 +552,15 @@ erpnext.PointOfSale.Controller = class { item_row = this.get_item_from_frm(item); 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); + const from_selector = field === "qty" && value === "+1"; + if (from_selector) value = flt(item_row.stock_qty) + flt(value); if (item_row_exists) { - if (field === 'qty') - value = flt(value); + if (field === "qty") value = flt(value); - if (['qty', 'conversion_factor'].includes(field) && value > 0 && !this.allow_negative_stock) { - const qty_needed = field === 'qty' ? value * item_row.conversion_factor : item_row.qty * value; + if (["qty", "conversion_factor"].includes(field) && value > 0 && !this.allow_negative_stock) { + const qty_needed = + field === "qty" ? value * item_row.conversion_factor : item_row.qty * value; await this.check_stock_availability(item_row, qty_needed, this.frm.doc.set_warehouse); } @@ -537,29 +568,25 @@ erpnext.PointOfSale.Controller = class { await frappe.model.set_value(item_row.doctype, item_row.name, field, value); this.update_cart_html(item_row); } - } else { - if (!this.frm.doc.customer) - return this.raise_customer_selection_alert(); + if (!this.frm.doc.customer) return this.raise_customer_selection_alert(); const { item_code, batch_no, serial_no, rate } = item; - if (!item_code) - return; + if (!item_code) return; const new_item = { item_code, batch_no, rate, [field]: value }; if (serial_no) { await this.check_serial_no_availablilty(item_code, this.frm.doc.set_warehouse, serial_no); - new_item['serial_no'] = serial_no; + new_item["serial_no"] = serial_no; } - if (field === 'serial_no') - new_item['qty'] = value.split(`\n`).length || 0; + if (field === "serial_no") new_item["qty"] = value.split(`\n`).length || 0; - item_row = this.frm.add_child('items', new_item); + item_row = this.frm.add_child("items", new_item); - if (field === 'qty' && value !== 0 && !this.allow_negative_stock) { + if (field === "qty" && value !== 0 && !this.allow_negative_stock) { const qty_needed = value * item_row.conversion_factor; await this.check_stock_availability(item_row, qty_needed, this.frm.doc.set_warehouse); } @@ -568,13 +595,14 @@ erpnext.PointOfSale.Controller = class { this.update_cart_html(item_row); - if (this.item_details.$component.is(':visible')) - this.edit_item_details_of(item_row); + if (this.item_details.$component.is(":visible")) this.edit_item_details_of(item_row); - if (this.check_serial_batch_selection_needed(item_row) && !this.item_details.$component.is(':visible')) + if ( + this.check_serial_batch_selection_needed(item_row) && + !this.item_details.$component.is(":visible") + ) this.edit_item_details_of(item_row); } - } catch (error) { console.log(error); } finally { @@ -586,8 +614,8 @@ erpnext.PointOfSale.Controller = class { raise_customer_selection_alert() { frappe.dom.unfreeze(); frappe.show_alert({ - message: __('You must select a customer before adding an item.'), - indicator: 'orange' + message: __("You must select a customer before adding an item."), + indicator: "orange", }); frappe.utils.play_sound("error"); } @@ -595,17 +623,18 @@ erpnext.PointOfSale.Controller = class { get_item_from_frm({ name, item_code, batch_no, uom, rate }) { let item_row = null; if (name) { - item_row = this.frm.doc.items.find(i => i.name == name); + item_row = this.frm.doc.items.find((i) => i.name == name); } else { // if item is clicked twice from item selector // then "item_code, batch_no, uom, rate" will help in getting the exact item // to increase the qty by one const has_batch_no = batch_no; item_row = this.frm.doc.items.find( - i => i.item_code === item_code - && (!has_batch_no || (has_batch_no && i.batch_no === batch_no)) - && (i.uom === uom) - && (i.rate == rate) + (i) => + i.item_code === item_code && + (!has_batch_no || (has_batch_no && i.batch_no === batch_no)) && + i.uom === uom && + i.rate == rate ); } @@ -633,16 +662,19 @@ erpnext.PointOfSale.Controller = class { const no_serial_selected = !item_row.serial_no; const no_batch_selected = !item_row.batch_no; - if ((serialized && no_serial_selected) || (batched && no_batch_selected) || - (serialized && batched && (no_batch_selected || no_serial_selected))) { + if ( + (serialized && no_serial_selected) || + (batched && no_batch_selected) || + (serialized && batched && (no_batch_selected || no_serial_selected)) + ) { return true; } return false; } async trigger_new_item_events(item_row) { - await this.frm.script_manager.trigger('item_code', item_row.doctype, item_row.name); - await this.frm.script_manager.trigger('qty', item_row.doctype, item_row.name); + await this.frm.script_manager.trigger("item_code", item_row.doctype, item_row.name); + await this.frm.script_manager.trigger("qty", item_row.doctype, item_row.name); } async check_stock_availability(item_row, qty_needed, warehouse) { @@ -653,21 +685,27 @@ erpnext.PointOfSale.Controller = class { frappe.dom.unfreeze(); const bold_item_code = item_row.item_code.bold(); const bold_warehouse = warehouse.bold(); - const bold_available_qty = available_qty.toString().bold() + const bold_available_qty = available_qty.toString().bold(); if (!(available_qty > 0)) { if (is_stock_item) { frappe.model.clear_doc(item_row.doctype, item_row.name); frappe.throw({ title: __("Not Available"), - message: __('Item Code: {0} is not available under warehouse {1}.', [bold_item_code, bold_warehouse]) + message: __("Item Code: {0} is not available under warehouse {1}.", [ + bold_item_code, + bold_warehouse, + ]), }); } else { return; } } else if (is_stock_item && available_qty < qty_needed) { frappe.throw({ - message: __('Stock quantity not enough for Item Code: {0} under warehouse {1}. Available quantity {2}.', [bold_item_code, bold_warehouse, bold_available_qty]), - indicator: 'orange' + message: __( + "Stock quantity not enough for Item Code: {0} under warehouse {1}. Available quantity {2}.", + [bold_item_code, bold_warehouse, bold_available_qty] + ), + indicator: "orange", }); frappe.utils.play_sound("error"); } @@ -676,13 +714,15 @@ erpnext.PointOfSale.Controller = class { async check_serial_no_availablilty(item_code, warehouse, serial_no) { const method = "erpnext.stock.doctype.serial_no.serial_no.get_pos_reserved_serial_nos"; - const args = {filters: { item_code, warehouse }} + const args = { filters: { item_code, warehouse } }; const res = await frappe.call({ method, args }); if (res.message.includes(serial_no)) { frappe.throw({ title: __("Not Available"), - message: __('Serial No: {0} has already been transacted into another POS Invoice.', [serial_no.bold()]) + message: __("Serial No: {0} has already been transacted into another POS Invoice.", [ + serial_no.bold(), + ]), }); } } @@ -692,21 +732,20 @@ erpnext.PointOfSale.Controller = class { return frappe.call({ method: "erpnext.accounts.doctype.pos_invoice.pos_invoice.get_stock_availability", args: { - 'item_code': item_code, - 'warehouse': warehouse, + item_code: item_code, + warehouse: warehouse, }, callback(res) { - if (!me.item_stock_map[item_code]) - me.item_stock_map[item_code] = {}; + if (!me.item_stock_map[item_code]) me.item_stock_map[item_code] = {}; me.item_stock_map[item_code][warehouse] = res.message; - } + }, }); } update_item_field(value, field_or_action) { - if (field_or_action === 'checkout') { + if (field_or_action === "checkout") { this.item_details.toggle_item_details_section(null); - } else if (field_or_action === 'remove') { + } else if (field_or_action === "remove") { this.remove_item_from_cart(); } else { const field_control = this.item_details[`${field_or_action}_control`]; @@ -720,26 +759,28 @@ erpnext.PointOfSale.Controller = class { frappe.dom.freeze(); const { doctype, name, current_item } = this.item_details; - return frappe.model.set_value(doctype, name, 'qty', 0) + return frappe.model + .set_value(doctype, name, "qty", 0) .then(() => { frappe.model.clear_doc(doctype, name); this.update_cart_html(current_item, true); this.item_details.toggle_item_details_section(null); frappe.dom.unfreeze(); }) - .catch(e => console.log(e)); + .catch((e) => console.log(e)); } async save_and_checkout() { if (this.frm.is_dirty()) { let save_error = false; - await this.frm.save(null, null, null, () => save_error = true); + await this.frm.save(null, null, null, () => (save_error = true)); // only move to payment section if save is successful !save_error && this.payment.checkout(); // show checkout button on error - save_error && setTimeout(() => { - this.cart.toggle_checkout_btn(true); - }, 300); // wait for save to finish + save_error && + setTimeout(() => { + this.cart.toggle_checkout_btn(true); + }, 300); // wait for save to finish } else { this.payment.checkout(); } 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 eacf480ef8f..08bebd11252 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_cart.js +++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js @@ -18,11 +18,9 @@ erpnext.PointOfSale.ItemCart = class { } prepare_dom() { - this.wrapper.append( - `
              ` - ) + this.wrapper.append(`
              `); - this.$component = this.wrapper.find('.customer-cart-container'); + this.$component = this.wrapper.find(".customer-cart-container"); } init_child_components() { @@ -31,16 +29,14 @@ erpnext.PointOfSale.ItemCart = class { } init_customer_selector() { - this.$component.append( - `
              ` - ) - this.$customer_section = this.$component.find('.customer-section'); + this.$component.append(`
              `); + this.$customer_section = this.$component.find(".customer-section"); this.make_customer_selector(); } reset_customer_selector() { const frm = this.events.get_frm(); - frm.set_value('customer', ''); + frm.set_value("customer", ""); this.make_customer_selector(); this.customer_field.set_focus(); } @@ -49,11 +45,11 @@ erpnext.PointOfSale.ItemCart = class { this.$component.append( `
              -
              ${__('Item Cart')}
              +
              ${__("Item Cart")}
              -
              ${__('Item')}
              -
              ${__('Quantity')}
              -
              ${__('Amount')}
              +
              ${__("Item")}
              +
              ${__("Quantity")}
              +
              ${__("Amount")}
              @@ -61,7 +57,7 @@ erpnext.PointOfSale.ItemCart = class {
              ` ); - this.$cart_container = this.$component.find('.cart-container'); + this.$cart_container = this.$component.find(".cart-container"); this.make_cart_totals_section(); this.make_cart_items_section(); @@ -69,39 +65,35 @@ erpnext.PointOfSale.ItemCart = class { } make_cart_items_section() { - this.$cart_header = this.$component.find('.cart-header'); - this.$cart_items_wrapper = this.$component.find('.cart-items-section'); + this.$cart_header = this.$component.find(".cart-header"); + this.$cart_items_wrapper = this.$component.find(".cart-items-section"); this.make_no_items_placeholder(); } make_no_items_placeholder() { - this.$cart_header.css('display', 'none'); - this.$cart_items_wrapper.html( - `
              ${__('No items in cart')}
              ` - ); + this.$cart_header.css("display", "none"); + this.$cart_items_wrapper.html(`
              ${__("No items in cart")}
              `); } get_discount_icon() { - return ( - ` + return ` - ` - ); + `; } make_cart_totals_section() { - this.$totals_section = this.$component.find('.cart-totals-section'); + this.$totals_section = this.$component.find(".cart-totals-section"); this.$totals_section.append( `
              - ${this.get_discount_icon()} ${__('Add Discount')} + ${this.get_discount_icon()} ${__("Add Discount")}
              -
              ${__('Total Items')}
              +
              ${__("Total Items")}
              0.00
              @@ -110,39 +102,39 @@ erpnext.PointOfSale.ItemCart = class {
              -
              ${__('Grand Total')}
              +
              ${__("Grand Total")}
              0.00
              -
              ${__('Checkout')}
              -
              ${__('Edit Cart')}
              ` - ) +
              ${__("Checkout")}
              +
              ${__("Edit Cart")}
              ` + ); this.$add_discount_elem = this.$component.find(".add-discount-wrapper"); } make_cart_numpad() { - this.$numpad_section = this.$component.find('.numpad-section'); + this.$numpad_section = this.$component.find(".numpad-section"); this.number_pad = new erpnext.PointOfSale.NumberPad({ wrapper: this.$numpad_section, events: { - numpad_event: this.on_numpad_event.bind(this) + numpad_event: this.on_numpad_event.bind(this), }, cols: 5, keys: [ - [ 1, 2, 3, 'Quantity' ], - [ 4, 5, 6, 'Discount' ], - [ 7, 8, 9, 'Rate' ], - [ '.', 0, 'Delete', 'Remove' ] + [1, 2, 3, "Quantity"], + [4, 5, 6, "Discount"], + [7, 8, 9, "Rate"], + [".", 0, "Delete", "Remove"], ], css_classes: [ - [ '', '', '', 'col-span-2' ], - [ '', '', '', 'col-span-2' ], - [ '', '', '', 'col-span-2' ], - [ '', '', '', 'col-span-2 remove-btn' ] + ["", "", "", "col-span-2"], + ["", "", "", "col-span-2"], + ["", "", "", "col-span-2"], + ["", "", "", "col-span-2 remove-btn"], ], - fieldnames_map: { 'Quantity': 'qty', 'Discount': 'discount_percentage' } - }) + fieldnames_map: { Quantity: "qty", Discount: "discount_percentage" }, + }); this.$numpad_section.prepend( `
              @@ -150,49 +142,49 @@ erpnext.PointOfSale.ItemCart = class {
              ` - ) + ); this.$numpad_section.append( - `
              ${__('Checkout')}
              ` - ) + `
              ${__("Checkout")}
              ` + ); } bind_events() { const me = this; - this.$customer_section.on('click', '.reset-customer-btn', function () { + this.$customer_section.on("click", ".reset-customer-btn", function () { me.reset_customer_selector(); }); - this.$customer_section.on('click', '.close-details-btn', function () { + this.$customer_section.on("click", ".close-details-btn", function () { me.toggle_customer_info(false); }); - this.$customer_section.on('click', '.customer-display', function(e) { - if ($(e.target).closest('.reset-customer-btn').length) return; + this.$customer_section.on("click", ".customer-display", function (e) { + if ($(e.target).closest(".reset-customer-btn").length) return; - const show = me.$cart_container.is(':visible'); + const show = me.$cart_container.is(":visible"); me.toggle_customer_info(show); }); - this.$cart_items_wrapper.on('click', '.cart-item-wrapper', function() { + this.$cart_items_wrapper.on("click", ".cart-item-wrapper", function () { const $cart_item = $(this); me.toggle_item_highlight(this); - const payment_section_hidden = !me.$totals_section.find('.edit-cart-btn').is(':visible'); + const payment_section_hidden = !me.$totals_section.find(".edit-cart-btn").is(":visible"); if (!payment_section_hidden) { // payment section is visible // edit cart first and then open item details section me.$totals_section.find(".edit-cart-btn").click(); } - const item_row_name = unescape($cart_item.attr('data-row-name')); + const item_row_name = unescape($cart_item.attr("data-row-name")); me.events.cart_item_clicked({ name: item_row_name }); - this.numpad_value = ''; + this.numpad_value = ""; }); - this.$component.on('click', '.checkout-btn', async function() { - if ($(this).attr('style').indexOf('--blue-500') == -1) return; + this.$component.on("click", ".checkout-btn", async function () { + if ($(this).attr("style").indexOf("--blue-500") == -1) return; await me.events.checkout(); me.toggle_checkout_btn(false); @@ -200,18 +192,18 @@ erpnext.PointOfSale.ItemCart = class { me.allow_discount_change && me.$add_discount_elem.removeClass("d-none"); }); - this.$totals_section.on('click', '.edit-cart-btn', () => { + this.$totals_section.on("click", ".edit-cart-btn", () => { this.events.edit_cart(); this.toggle_checkout_btn(true); }); - this.$component.on('click', '.add-discount-wrapper', () => { - const can_edit_discount = this.$add_discount_elem.find('.edit-discount-btn').length; + this.$component.on("click", ".add-discount-wrapper", () => { + const can_edit_discount = this.$add_discount_elem.find(".edit-discount-btn").length; - if(!this.discount_field || can_edit_discount) this.show_discount_control(); + if (!this.discount_field || can_edit_discount) this.show_discount_control(); }); - frappe.ui.form.on("POS Invoice", "paid_amount", frm => { + frappe.ui.form.on("POS Invoice", "paid_amount", (frm) => { // called when discount is applied this.update_totals_section(frm); }); @@ -220,43 +212,49 @@ erpnext.PointOfSale.ItemCart = class { attach_shortcuts() { for (let row of this.number_pad.keys) { for (let btn of row) { - if (typeof btn !== 'string') continue; // do not make shortcuts for numbers + if (typeof btn !== "string") continue; // do not make shortcuts for numbers let shortcut_key = `ctrl+${frappe.scrub(String(btn))[0]}`; - if (btn === 'Delete') shortcut_key = 'ctrl+backspace'; - if (btn === 'Remove') shortcut_key = 'shift+ctrl+backspace' - if (btn === '.') shortcut_key = 'ctrl+>'; + if (btn === "Delete") shortcut_key = "ctrl+backspace"; + if (btn === "Remove") shortcut_key = "shift+ctrl+backspace"; + if (btn === ".") shortcut_key = "ctrl+>"; // to account for fieldname map - const fieldname = this.number_pad.fieldnames[btn] ? this.number_pad.fieldnames[btn] : - typeof btn === 'string' ? frappe.scrub(btn) : btn; + const fieldname = this.number_pad.fieldnames[btn] + ? this.number_pad.fieldnames[btn] + : typeof btn === "string" + ? frappe.scrub(btn) + : btn; - let shortcut_label = shortcut_key.split('+').map(frappe.utils.to_title_case).join('+'); - shortcut_label = frappe.utils.is_mac() ? shortcut_label.replace('Ctrl', '⌘') : shortcut_label; - this.$numpad_section.find(`.numpad-btn[data-button-value="${fieldname}"]`).attr("title", shortcut_label); + let shortcut_label = shortcut_key.split("+").map(frappe.utils.to_title_case).join("+"); + shortcut_label = frappe.utils.is_mac() ? shortcut_label.replace("Ctrl", "⌘") : shortcut_label; + this.$numpad_section + .find(`.numpad-btn[data-button-value="${fieldname}"]`) + .attr("title", shortcut_label); frappe.ui.keys.on(`${shortcut_key}`, () => { const cart_is_visible = this.$component.is(":visible"); if (cart_is_visible && this.item_is_selected && this.$numpad_section.is(":visible")) { this.$numpad_section.find(`.numpad-btn[data-button-value="${fieldname}"]`).click(); } - }) + }); } } - const ctrl_label = frappe.utils.is_mac() ? '⌘' : 'Ctrl'; + const ctrl_label = frappe.utils.is_mac() ? "⌘" : "Ctrl"; this.$component.find(".checkout-btn").attr("title", `${ctrl_label}+Enter`); frappe.ui.keys.add_shortcut({ shortcut: "ctrl+enter", action: () => this.$component.find(".checkout-btn").click(), - condition: () => this.$component.is(":visible") && !this.$totals_section.find('.edit-cart-btn').is(':visible'), + condition: () => + this.$component.is(":visible") && !this.$totals_section.find(".edit-cart-btn").is(":visible"), description: __("Checkout Order / Submit Order / New Order"), ignore_inputs: true, - page: cur_page.page.page + page: cur_page.page.page, }); this.$component.find(".edit-cart-btn").attr("title", `${ctrl_label}+E`); frappe.ui.keys.on("ctrl+e", () => { const item_cart_visible = this.$component.is(":visible"); - const checkout_btn_invisible = !this.$totals_section.find('.checkout-btn').is('visible'); + const checkout_btn_invisible = !this.$totals_section.find(".checkout-btn").is("visible"); if (item_cart_visible && checkout_btn_invisible) { this.$component.find(".edit-cart-btn").click(); } @@ -268,7 +266,7 @@ erpnext.PointOfSale.ItemCart = class { condition: () => this.$add_discount_elem.is(":visible"), description: __("Add Order Discount"), ignore_inputs: true, - page: cur_page.page.page + page: cur_page.page.page, }); frappe.ui.keys.on("escape", () => { const item_cart_visible = this.$component.is(":visible"); @@ -284,11 +282,11 @@ erpnext.PointOfSale.ItemCart = class { if (!item || item_is_highlighted) { this.item_is_selected = false; - this.$cart_container.find('.cart-item-wrapper').css("background-color", ""); + this.$cart_container.find(".cart-item-wrapper").css("background-color", ""); } else { $cart_item.css("background-color", "var(--gray-50)"); this.item_is_selected = true; - this.$cart_container.find('.cart-item-wrapper').not(item).css("background-color", ""); + this.$cart_container.find(".cart-item-wrapper").not(item).css("background-color", ""); } } @@ -297,38 +295,38 @@ erpnext.PointOfSale.ItemCart = class {
              `); const me = this; - const query = { query: 'erpnext.controllers.queries.customer_query' }; + const query = { query: "erpnext.controllers.queries.customer_query" }; const allowed_customer_group = this.allowed_customer_groups || []; if (allowed_customer_group.length) { query.filters = { - customer_group: ['in', allowed_customer_group] - } + customer_group: ["in", allowed_customer_group], + }; } this.customer_field = frappe.ui.form.make_control({ df: { - label: __('Customer'), - fieldtype: 'Link', - options: 'Customer', - placeholder: __('Search by customer name, phone, email.'), + label: __("Customer"), + fieldtype: "Link", + options: "Customer", + placeholder: __("Search by customer name, phone, email."), get_query: () => query, - onchange: function() { + onchange: function () { if (this.value) { const frm = me.events.get_frm(); frappe.dom.freeze(); - frappe.model.set_value(frm.doc.doctype, frm.doc.name, 'customer', this.value); - frm.script_manager.trigger('customer', frm.doc.doctype, frm.doc.name).then(() => { + frappe.model.set_value(frm.doc.doctype, frm.doc.name, "customer", this.value); + frm.script_manager.trigger("customer", frm.doc.doctype, frm.doc.name).then(() => { frappe.run_serially([ () => me.fetch_customer_details(this.value), () => me.events.customer_details_updated(me.customer_info), () => me.update_customer_section(), () => me.update_totals_section(), - () => frappe.dom.unfreeze() + () => frappe.dom.unfreeze(), ]); - }) + }); } }, }, - parent: this.$customer_section.find('.customer-field'), + parent: this.$customer_section.find(".customer-field"), render_input: true, }); this.customer_field.toggle_label(false); @@ -337,66 +335,81 @@ erpnext.PointOfSale.ItemCart = class { fetch_customer_details(customer) { if (customer) { return new Promise((resolve) => { - frappe.db.get_value('Customer', customer, ["email_id", "mobile_no", "image", "loyalty_program"]).then(({ message }) => { - const { loyalty_program } = message; - // if loyalty program then fetch loyalty points too - if (loyalty_program) { - frappe.call({ - method: "erpnext.accounts.doctype.loyalty_program.loyalty_program.get_loyalty_program_details_with_points", - args: { customer, loyalty_program, "silent": true }, - callback: (r) => { - const { loyalty_points, conversion_factor } = r.message; - if (!r.exc) { - this.customer_info = { ...message, customer, loyalty_points, conversion_factor }; - resolve(); - } - } - }); - } else { - this.customer_info = { ...message, customer }; - resolve(); - } - }); + frappe.db + .get_value("Customer", customer, ["email_id", "mobile_no", "image", "loyalty_program"]) + .then(({ message }) => { + const { loyalty_program } = message; + // if loyalty program then fetch loyalty points too + if (loyalty_program) { + frappe.call({ + method: "erpnext.accounts.doctype.loyalty_program.loyalty_program.get_loyalty_program_details_with_points", + args: { customer, loyalty_program, silent: true }, + callback: (r) => { + const { loyalty_points, conversion_factor } = r.message; + if (!r.exc) { + this.customer_info = { + ...message, + customer, + loyalty_points, + conversion_factor, + }; + resolve(); + } + }, + }); + } else { + this.customer_info = { ...message, customer }; + resolve(); + } + }); }); } else { return new Promise((resolve) => { - this.customer_info = {} + this.customer_info = {}; resolve(); }); } } show_discount_control() { - this.$add_discount_elem.css({ 'padding': '0px', 'border': 'none' }); - this.$add_discount_elem.html( - `
              ` - ); + this.$add_discount_elem.css({ padding: "0px", border: "none" }); + this.$add_discount_elem.html(`
              `); const me = this; const frm = me.events.get_frm(); let discount = frm.doc.additional_discount_percentage; this.discount_field = frappe.ui.form.make_control({ df: { - label: __('Discount'), - fieldtype: 'Data', - placeholder: ( discount ? discount + '%' : __('Enter discount percentage.') ), - input_class: 'input-xs', - onchange: function() { + label: __("Discount"), + fieldtype: "Data", + 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)); + 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); + 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)' + 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.$add_discount_elem.html(`${me.get_discount_icon()} ${__("Add Discount")}`); me.discount_field = undefined; } }, }, - parent: this.$add_discount_elem.find('.add-discount-field'), + parent: this.$add_discount_elem.find(".add-discount-field"), render_input: true, }); this.discount_field.toggle_label(false); @@ -405,14 +418,12 @@ erpnext.PointOfSale.ItemCart = class { hide_discount_control(discount) { if (!discount) { - this.$add_discount_elem.css({ 'padding': '0px', 'border': 'none' }); - this.$add_discount_elem.html( - `
              ` - ); + this.$add_discount_elem.css({ padding: "0px", border: "none" }); + this.$add_discount_elem.html(`
              `); } else { this.$add_discount_elem.css({ - 'border': '1px dashed var(--dark-green-500)', - 'padding': 'var(--padding-sm) var(--padding-md)' + border: "1px dashed var(--dark-green-500)", + padding: "var(--padding-sm) var(--padding-md)", }); this.$add_discount_elem.html( `
              @@ -424,7 +435,7 @@ erpnext.PointOfSale.ItemCart = class { update_customer_section() { const me = this; - const { customer, email_id='', mobile_no='', image } = this.customer_info || {}; + const { customer, email_id = "", mobile_no = "", image } = this.customer_info || {}; if (customer) { this.$customer_section.html( @@ -450,7 +461,7 @@ erpnext.PointOfSale.ItemCart = class { function get_customer_description() { if (!email_id && !mobile_no) { - return `
              ${__('Click to add email / phone')}
              `; + return `
              ${__("Click to add email / phone")}
              `; } else if (email_id && !mobile_no) { return `
              ${email_id}
              `; } else if (mobile_no && !email_id) { @@ -459,7 +470,6 @@ erpnext.PointOfSale.ItemCart = class { return `
              ${email_id} - ${mobile_no}
              `; } } - } get_customer_image() { @@ -476,7 +486,9 @@ erpnext.PointOfSale.ItemCart = class { this.render_net_total(frm.doc.net_total); this.render_total_item_qty(frm.doc.items); - const grand_total = cint(frappe.sys_defaults.disable_rounded_total) ? frm.doc.grand_total : frm.doc.rounded_total; + const grand_total = cint(frappe.sys_defaults.disable_rounded_total) + ? frm.doc.grand_total + : frm.doc.rounded_total; this.render_grand_total(grand_total); this.render_taxes(frm.doc.taxes); @@ -484,13 +496,13 @@ erpnext.PointOfSale.ItemCart = class { render_net_total(value) { const currency = this.events.get_frm().doc.currency; - this.$totals_section.find('.net-total-container').html( - `
              ${__('Net Total')}
              ${format_currency(value, currency)}
              ` - ) + this.$totals_section + .find(".net-total-container") + .html(`
              ${__("Net Total")}
              ${format_currency(value, currency)}
              `); - this.$numpad_section.find('.numpad-net-total').html( - `
              ${__('Net Total')}: ${format_currency(value, currency)}
              ` - ); + this.$numpad_section + .find(".numpad-net-total") + .html(`
              ${__("Net Total")}: ${format_currency(value, currency)}
              `); } render_total_item_qty(items) { @@ -499,40 +511,44 @@ erpnext.PointOfSale.ItemCart = class { total_item_qty = total_item_qty + item.qty; }); - this.$totals_section.find('.item-qty-total-container').html( - `
              ${__('Total Quantity')}
              ${total_item_qty}
              ` - ); + this.$totals_section + .find(".item-qty-total-container") + .html(`
              ${__("Total Quantity")}
              ${total_item_qty}
              `); - this.$numpad_section.find('.numpad-item-qty-total').html( - `
              ${__('Total Quantity')}: ${total_item_qty}
              ` - ); + this.$numpad_section + .find(".numpad-item-qty-total") + .html(`
              ${__("Total Quantity")}: ${total_item_qty}
              `); } render_grand_total(value) { const currency = this.events.get_frm().doc.currency; - this.$totals_section.find('.grand-total-container').html( - `
              ${__('Grand Total')}
              ${format_currency(value, currency)}
              ` - ) + this.$totals_section + .find(".grand-total-container") + .html(`
              ${__("Grand Total")}
              ${format_currency(value, currency)}
              `); - this.$numpad_section.find('.numpad-grand-total').html( - `
              ${__('Grand Total')}: ${format_currency(value, currency)}
              ` - ); + this.$numpad_section + .find(".numpad-grand-total") + .html(`
              ${__("Grand Total")}: ${format_currency(value, currency)}
              `); } render_taxes(taxes) { if (taxes.length) { const currency = this.events.get_frm().doc.currency; - const taxes_html = taxes.map(t => { - if (t.tax_amount_after_discount_amount == 0.0) return; - const description = /[0-9]+/.test(t.description) ? t.description : `${t.description} @ ${t.rate}%`; - return `
              + const taxes_html = taxes + .map((t) => { + if (t.tax_amount_after_discount_amount == 0.0) return; + const description = /[0-9]+/.test(t.description) + ? t.description + : `${t.description} @ ${t.rate}%`; + return `
              ${description}
              ${format_currency(t.tax_amount_after_discount_amount, currency)}
              `; - }).join(''); - this.$totals_section.find('.taxes-container').css('display', 'flex').html(taxes_html); + }) + .join(""); + this.$totals_section.find(".taxes-container").css("display", "flex").html(taxes_html); } else { - this.$totals_section.find('.taxes-container').css('display', 'none').html(''); + this.$totals_section.find(".taxes-container").css("display", "none").html(""); } } @@ -543,7 +559,7 @@ erpnext.PointOfSale.ItemCart = class { get_item_from_frm(item) { const doc = this.events.get_frm().doc; - return doc.items.find(i => i.name == item.name); + return doc.items.find((i) => i.name == item.name); } update_item_html(item, remove_item) { @@ -556,7 +572,7 @@ erpnext.PointOfSale.ItemCart = class { this.render_cart_item(item_row, $item); } - const no_of_cart_items = this.$cart_items_wrapper.find('.cart-item-wrapper').length; + const no_of_cart_items = this.$cart_items_wrapper.find(".cart-item-wrapper").length; this.highlight_checkout_btn(no_of_cart_items > 0); this.update_empty_cart_section(no_of_cart_items); @@ -570,7 +586,7 @@ erpnext.PointOfSale.ItemCart = class { this.$cart_items_wrapper.append( `
              ` - ) + ); $item_to_update = this.get_cart_item(item_data); } @@ -583,7 +599,7 @@ erpnext.PointOfSale.ItemCart = class { ${get_description_html()}
              ${get_rate_discount_html()}` - ) + ); set_dynamic_rate_header_width(); @@ -592,8 +608,7 @@ erpnext.PointOfSale.ItemCart = class { me.$cart_header.find(".rate-amount-header").css("width", ""); me.$cart_items_wrapper.find(".item-rate-amount").css("width", ""); let max_width = rate_cols.reduce((max_width, elm) => { - if ($(elm).width() > max_width) - max_width = $(elm).width(); + if ($(elm).width() > max_width) max_width = $(elm).width(); return max_width; }, 0); @@ -613,7 +628,7 @@ erpnext.PointOfSale.ItemCart = class {
              ${format_currency(item_data.amount, currency)}
              ${format_currency(item_data.rate, currency)}
              -
              ` +
              `; } else { return `
              @@ -621,17 +636,20 @@ erpnext.PointOfSale.ItemCart = class {
              ${format_currency(item_data.rate, currency)}
              -
              ` +
              `; } } function get_description_html() { if (item_data.description) { - if (item_data.description.indexOf('
              ') != -1) { + if (item_data.description.indexOf("
              ") != -1) { try { item_data.description = $(item_data.description).text(); } catch (error) { - item_data.description = item_data.description.replace(/
              /g, ' ').replace(/<\/div>/g, ' ').replace(/ +/g, ' '); + item_data.description = item_data.description + .replace(/
              /g, " ") + .replace(/<\/div>/g, " ") + .replace(/ +/g, " "); } } item_data.description = frappe.ellipsis(item_data.description, 45); @@ -656,7 +674,7 @@ erpnext.PointOfSale.ItemCart = class { } handle_broken_image($img) { - const item_abbr = $($img).attr('alt'); + const item_abbr = $($img).attr("alt"); $($img).parent().replaceWith(`
              ${item_abbr}
              `); } @@ -667,44 +685,48 @@ erpnext.PointOfSale.ItemCart = class { toggle_checkout_btn(show_checkout) { if (show_checkout) { - this.$totals_section.find('.checkout-btn').css('display', 'flex'); - this.$totals_section.find('.edit-cart-btn').css('display', 'none'); + this.$totals_section.find(".checkout-btn").css("display", "flex"); + this.$totals_section.find(".edit-cart-btn").css("display", "none"); } else { - this.$totals_section.find('.checkout-btn').css('display', 'none'); - this.$totals_section.find('.edit-cart-btn').css('display', 'flex'); + this.$totals_section.find(".checkout-btn").css("display", "none"); + this.$totals_section.find(".edit-cart-btn").css("display", "flex"); } } highlight_checkout_btn(toggle) { if (toggle) { - this.$add_discount_elem.css('display', 'flex'); - this.$cart_container.find('.checkout-btn').css({ - 'background-color': 'var(--blue-500)' + this.$add_discount_elem.css("display", "flex"); + this.$cart_container.find(".checkout-btn").css({ + "background-color": "var(--blue-500)", }); } else { - this.$add_discount_elem.css('display', 'none'); - this.$cart_container.find('.checkout-btn').css({ - 'background-color': 'var(--blue-200)' + this.$add_discount_elem.css("display", "none"); + this.$cart_container.find(".checkout-btn").css({ + "background-color": "var(--blue-200)", }); } } update_empty_cart_section(no_of_cart_items) { - const $no_item_element = this.$cart_items_wrapper.find('.no-item-wrapper'); + const $no_item_element = this.$cart_items_wrapper.find(".no-item-wrapper"); // if cart has items and no item is present - no_of_cart_items > 0 && $no_item_element && $no_item_element.remove() && this.$cart_header.css('display', 'flex'); + no_of_cart_items > 0 && + $no_item_element && + $no_item_element.remove() && + this.$cart_header.css("display", "flex"); no_of_cart_items === 0 && !$no_item_element.length && this.make_no_items_placeholder(); } on_numpad_event($btn) { - const current_action = $btn.attr('data-button-value'); - const action_is_field_edit = ['qty', 'discount_percentage', 'rate'].includes(current_action); - const action_is_allowed = action_is_field_edit ? ( - (current_action == 'rate' && this.allow_rate_change) || - (current_action == 'discount_percentage' && this.allow_discount_change) || - (current_action == 'qty')) : true; + const current_action = $btn.attr("data-button-value"); + const action_is_field_edit = ["qty", "discount_percentage", "rate"].includes(current_action); + const action_is_allowed = action_is_field_edit + ? (current_action == "rate" && this.allow_rate_change) || + (current_action == "discount_percentage" && this.allow_discount_change) || + current_action == "qty" + : true; const action_is_pressed_twice = this.prev_action === current_action; const first_click_event = !this.prev_action; @@ -712,11 +734,11 @@ erpnext.PointOfSale.ItemCart = class { if (action_is_field_edit) { if (!action_is_allowed) { - const label = current_action == 'rate' ? 'Rate'.bold() : 'Discount'.bold(); - const message = __('Editing {0} is not allowed as per POS Profile settings', [label]); + const label = current_action == "rate" ? "Rate".bold() : "Discount".bold(); + const message = __("Editing {0} is not allowed as per POS Profile settings", [label]); frappe.show_alert({ - indicator: 'red', - message: message + indicator: "red", + message: message, }); frappe.utils.play_sound("error"); return; @@ -727,20 +749,22 @@ erpnext.PointOfSale.ItemCart = class { } else if (action_is_pressed_twice) { this.prev_action = undefined; } - this.numpad_value = ''; - - } else if (current_action === 'checkout') { + this.numpad_value = ""; + } else if (current_action === "checkout") { this.prev_action = undefined; this.toggle_item_highlight(); this.events.numpad_event(undefined, current_action); return; - } else if (current_action === 'remove') { + } else if (current_action === "remove") { this.prev_action = undefined; this.toggle_item_highlight(); this.events.numpad_event(undefined, current_action); return; } else { - this.numpad_value = current_action === 'delete' ? this.numpad_value.slice(0, -1) : this.numpad_value + current_action; + this.numpad_value = + current_action === "delete" + ? this.numpad_value.slice(0, -1) + : this.numpad_value + current_action; this.numpad_value = this.numpad_value || 0; } @@ -748,17 +772,17 @@ erpnext.PointOfSale.ItemCart = class { if (first_click_event_is_not_field_edit) { frappe.show_alert({ - indicator: 'red', - message: __('Please select a field to edit from numpad') + indicator: "red", + message: __("Please select a field to edit from numpad"), }); frappe.utils.play_sound("error"); return; } - if (flt(this.numpad_value) > 100 && this.prev_action === 'discount_percentage') { + if (flt(this.numpad_value) > 100 && this.prev_action === "discount_percentage") { frappe.show_alert({ - message: __('Discount cannot be greater than 100%'), - indicator: 'orange' + message: __("Discount cannot be greater than 100%"), + indicator: "orange", }); frappe.utils.play_sound("error"); this.numpad_value = current_action; @@ -769,48 +793,48 @@ erpnext.PointOfSale.ItemCart = class { } highlight_numpad_btn($btn, curr_action) { - const curr_action_is_highlighted = $btn.hasClass('highlighted-numpad-btn'); - const curr_action_is_action = ['qty', 'discount_percentage', 'rate', 'done'].includes(curr_action); + const curr_action_is_highlighted = $btn.hasClass("highlighted-numpad-btn"); + const curr_action_is_action = ["qty", "discount_percentage", "rate", "done"].includes(curr_action); if (!curr_action_is_highlighted) { - $btn.addClass('highlighted-numpad-btn'); + $btn.addClass("highlighted-numpad-btn"); } if (this.prev_action === curr_action && curr_action_is_highlighted) { // if Qty is pressed twice - $btn.removeClass('highlighted-numpad-btn'); + $btn.removeClass("highlighted-numpad-btn"); } if (this.prev_action && this.prev_action !== curr_action && curr_action_is_action) { // Order: Qty -> Rate then remove Qty highlight const prev_btn = $(`[data-button-value='${this.prev_action}']`); - prev_btn.removeClass('highlighted-numpad-btn'); + prev_btn.removeClass("highlighted-numpad-btn"); } - if (!curr_action_is_action || curr_action === 'done') { + if (!curr_action_is_action || curr_action === "done") { // if numbers are clicked setTimeout(() => { - $btn.removeClass('highlighted-numpad-btn'); + $btn.removeClass("highlighted-numpad-btn"); }, 200); } } toggle_numpad(show) { if (show) { - this.$totals_section.css('display', 'none'); - this.$numpad_section.css('display', 'flex'); + this.$totals_section.css("display", "none"); + this.$numpad_section.css("display", "flex"); } else { - this.$totals_section.css('display', 'flex'); - this.$numpad_section.css('display', 'none'); + this.$totals_section.css("display", "flex"); + this.$numpad_section.css("display", "none"); } this.reset_numpad(); } reset_numpad() { - this.numpad_value = ''; + this.numpad_value = ""; this.prev_action = undefined; - this.$numpad_section.find('.highlighted-numpad-btn').removeClass('highlighted-numpad-btn'); + this.$numpad_section.find(".highlighted-numpad-btn").removeClass("highlighted-numpad-btn"); } toggle_numpad_field_edit(fieldname) { - if (['qty', 'discount_percentage', 'rate'].includes(fieldname)) { + if (["qty", "discount_percentage", "rate"].includes(fieldname)) { this.$numpad_section.find(`[data-button-value="${fieldname}"]`).click(); } } @@ -819,12 +843,12 @@ erpnext.PointOfSale.ItemCart = class { if (show) { const { customer } = this.customer_info || {}; - this.$cart_container.css('display', 'none'); + this.$cart_container.css("display", "none"); this.$customer_section.css({ - 'height': '100%', - 'padding-top': '0px' + height: "100%", + "padding-top": "0px", }); - this.$customer_section.find('.customer-details').html( + this.$customer_section.find(".customer-details").html( `
              Contact Details
              @@ -853,12 +877,11 @@ erpnext.PointOfSale.ItemCart = class { this.render_customer_fields(); this.fetch_customer_transactions(); - } else { - this.$cart_container.css('display', 'flex'); + this.$cart_container.css("display", "flex"); this.$customer_section.css({ - 'height': '', - 'padding-top': '' + height: "", + "padding-top": "", }); this.update_customer_section(); @@ -866,100 +889,107 @@ erpnext.PointOfSale.ItemCart = class { } render_customer_fields() { - const $customer_form = this.$customer_section.find('.customer-fields-container'); + const $customer_form = this.$customer_section.find(".customer-fields-container"); - const dfs = [{ - fieldname: 'email_id', - label: __('Email'), - fieldtype: 'Data', - options: 'email', - placeholder: __("Enter customer's email") - },{ - fieldname: 'mobile_no', - label: __('Phone Number'), - fieldtype: 'Data', - placeholder: __("Enter customer's phone number") - },{ - fieldname: 'loyalty_program', - label: __('Loyalty Program'), - fieldtype: 'Link', - options: 'Loyalty Program', - placeholder: __("Select Loyalty Program") - },{ - fieldname: 'loyalty_points', - label: __('Loyalty Points'), - fieldtype: 'Data', - read_only: 1 - }]; + const dfs = [ + { + fieldname: "email_id", + label: __("Email"), + fieldtype: "Data", + options: "email", + placeholder: __("Enter customer's email"), + }, + { + fieldname: "mobile_no", + label: __("Phone Number"), + fieldtype: "Data", + placeholder: __("Enter customer's phone number"), + }, + { + fieldname: "loyalty_program", + label: __("Loyalty Program"), + fieldtype: "Link", + options: "Loyalty Program", + placeholder: __("Select Loyalty Program"), + }, + { + fieldname: "loyalty_points", + label: __("Loyalty Points"), + fieldtype: "Data", + read_only: 1, + }, + ]; const me = this; - dfs.forEach(df => { + dfs.forEach((df) => { this[`customer_${df.fieldname}_field`] = frappe.ui.form.make_control({ - df: { ...df, - onchange: handle_customer_field_change, - }, + df: { ...df, onchange: handle_customer_field_change }, parent: $customer_form.find(`.${df.fieldname}-field`), render_input: true, }); this[`customer_${df.fieldname}_field`].set_value(this.customer_info[df.fieldname]); - }) + }); function handle_customer_field_change() { const current_value = me.customer_info[this.df.fieldname]; const current_customer = me.customer_info.customer; - if (this.value && current_value != this.value && this.df.fieldname != 'loyalty_points') { + if (this.value && current_value != this.value && this.df.fieldname != "loyalty_points") { frappe.call({ - method: 'erpnext.selling.page.point_of_sale.point_of_sale.set_customer_info', + method: "erpnext.selling.page.point_of_sale.point_of_sale.set_customer_info", args: { fieldname: this.df.fieldname, customer: current_customer, - value: this.value + value: this.value, }, callback: (r) => { - if(!r.exc) { + if (!r.exc) { me.customer_info[this.df.fieldname] = this.value; frappe.show_alert({ message: __("Customer contact updated successfully."), - indicator: 'green' + indicator: "green", }); frappe.utils.play_sound("submit"); } - } + }, }); } } } fetch_customer_transactions() { - frappe.db.get_list('POS Invoice', { - filters: { customer: this.customer_info.customer, docstatus: 1 }, - fields: ['name', 'grand_total', 'status', 'posting_date', 'posting_time', 'currency'], - limit: 20 - }).then((res) => { - const transaction_container = this.$customer_section.find('.customer-transactions'); + frappe.db + .get_list("POS Invoice", { + filters: { customer: this.customer_info.customer, docstatus: 1 }, + fields: ["name", "grand_total", "status", "posting_date", "posting_time", "currency"], + limit: 20, + }) + .then((res) => { + const transaction_container = this.$customer_section.find(".customer-transactions"); - if (!res.length) { - transaction_container.html( - `
              No recent transactions found
              ` - ) - return; - }; + if (!res.length) { + transaction_container.html( + `
              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}`); + const elapsed_time = moment(res[0].posting_date + " " + res[0].posting_time).fromNow(); + 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("Do MMMM, h:mma"); - let indicator_color = { - 'Paid': 'green', - 'Draft': 'red', - 'Return': 'gray', - 'Consolidated': 'blue' - }; + res.forEach((invoice) => { + const posting_datetime = moment(invoice.posting_date + " " + invoice.posting_time).format( + "Do MMMM, h:mma" + ); + let indicator_color = { + Paid: "green", + Draft: "red", + Return: "gray", + Consolidated: "blue", + }; - transaction_container.append( - `
              + transaction_container.append( + `
              ${invoice.name}
              ${posting_datetime}
              @@ -976,17 +1006,17 @@ erpnext.PointOfSale.ItemCart = class {
              ` - ) + ); + }); }); - }); } attach_refresh_field_event(frm) { - $(frm.wrapper).off('refresh-fields'); - $(frm.wrapper).on('refresh-fields', () => { + $(frm.wrapper).off("refresh-fields"); + $(frm.wrapper).on("refresh-fields", () => { if (frm.doc.items.length) { - this.$cart_items_wrapper.html(''); - frm.doc.items.forEach(item => { + this.$cart_items_wrapper.html(""); + frm.doc.items.forEach((item) => { this.update_item_html(item); }); } @@ -1004,9 +1034,9 @@ erpnext.PointOfSale.ItemCart = class { this.update_customer_section(); }); - this.$cart_items_wrapper.html(''); + this.$cart_items_wrapper.html(""); if (frm.doc.items.length) { - frm.doc.items.forEach(item => { + frm.doc.items.forEach((item) => { this.update_item_html(item); }); } else { @@ -1016,19 +1046,18 @@ erpnext.PointOfSale.ItemCart = class { this.update_totals_section(frm); - if(frm.doc.docstatus === 1) { - this.$totals_section.find('.checkout-btn').css('display', 'none'); - this.$totals_section.find('.edit-cart-btn').css('display', 'none'); + if (frm.doc.docstatus === 1) { + this.$totals_section.find(".checkout-btn").css("display", "none"); + this.$totals_section.find(".edit-cart-btn").css("display", "none"); } else { - this.$totals_section.find('.checkout-btn').css('display', 'flex'); - this.$totals_section.find('.edit-cart-btn').css('display', 'none'); + this.$totals_section.find(".checkout-btn").css("display", "flex"); + this.$totals_section.find(".edit-cart-btn").css("display", "none"); } this.toggle_component(true); } toggle_component(show) { - show ? this.$component.css('display', 'flex') : this.$component.css('display', 'none'); + show ? this.$component.css("display", "flex") : this.$component.css("display", "none"); } - -} +}; 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 f9b5bb2e452..9a6d2cc36ae 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_details.js +++ b/erpnext/selling/page/point_of_sale/pos_item_details.js @@ -18,17 +18,15 @@ erpnext.PointOfSale.ItemDetails = class { } prepare_dom() { - this.wrapper.append( - `
              ` - ) + this.wrapper.append(`
              `); - this.$component = this.wrapper.find('.item-details-container'); + this.$component = this.wrapper.find(".item-details-container"); } init_child_components() { this.$component.html( `
              -
              ${__('Item Details')}
              +
              ${__("Item Details")}
              @@ -45,14 +43,14 @@ erpnext.PointOfSale.ItemDetails = class {
              ` - ) + ); - this.$item_name = this.$component.find('.item-name'); - this.$item_description = this.$component.find('.item-desc'); - this.$item_price = this.$component.find('.item-price'); - this.$item_image = this.$component.find('.item-image'); - this.$form_container = this.$component.find('.form-container'); - this.$dicount_section = this.$component.find('.discount-section'); + this.$item_name = this.$component.find(".item-name"); + this.$item_description = this.$component.find(".item-desc"); + this.$item_price = this.$component.find(".item-price"); + this.$item_image = this.$component.find(".item-image"); + this.$form_container = this.$component.find(".form-container"); + this.$dicount_section = this.$component.find(".discount-section"); } compare_with_current_item(item) { @@ -95,7 +93,7 @@ erpnext.PointOfSale.ItemDetails = class { validate_serial_batch_item() { const doc = this.events.get_frm().doc; - const item_row = doc.items.find(item => item.name === this.name); + const item_row = doc.items.find((item) => item.name === this.name); if (!item_row) return; @@ -104,12 +102,14 @@ erpnext.PointOfSale.ItemDetails = class { const no_serial_selected = !item_row.serial_no; const no_batch_selected = !item_row.batch_no; - if ((serialized && no_serial_selected) || (batched && no_batch_selected) || - (serialized && batched && (no_batch_selected || no_serial_selected))) { - + if ( + (serialized && no_serial_selected) || + (batched && no_batch_selected) || + (serialized && batched && (no_batch_selected || no_serial_selected)) + ) { frappe.show_alert({ message: __("Item is removed since no serial / batch no selected."), - indicator: 'orange' + indicator: "orange", }); frappe.utils.play_sound("cancel"); return this.events.remove_item_from_cart(); @@ -121,7 +121,10 @@ erpnext.PointOfSale.ItemDetails = class { function get_description_html() { if (description) { - description = description.indexOf('...') === -1 && description.length > 140 ? description.substr(0, 139) + '...' : description; + description = + description.indexOf("...") === -1 && description.length > 140 + ? description.substr(0, 139) + "..." + : description; return description; } return ``; @@ -141,11 +144,10 @@ erpnext.PointOfSale.ItemDetails = class { } else { this.$item_image.html(`
              ${frappe.get_abbr(item_name)}
              `); } - } handle_broken_image($img) { - const item_abbr = $($img).attr('alt'); + const item_abbr = $($img).attr("alt"); $($img).replaceWith(`
              ${item_abbr}
              `); } @@ -154,36 +156,36 @@ erpnext.PointOfSale.ItemDetails = class { this.$dicount_section.html( `
              ${format_currency(item.price_list_rate, this.currency)}
              ${item.discount_percentage}% off
              ` - ) + ); this.$item_price.html(format_currency(item.rate, this.currency)); } else { - this.$dicount_section.html(``) + this.$dicount_section.html(``); } } render_form(item) { const fields_to_display = this.get_form_fields(item); - this.$form_container.html(''); + this.$form_container.html(""); fields_to_display.forEach((fieldname, idx) => { this.$form_container.append( `
              ` - ) + ); - const field_meta = this.item_meta.fields.find(df => df.fieldname === fieldname); - fieldname === 'discount_percentage' ? (field_meta.label = __('Discount (%)')) : ''; + const field_meta = this.item_meta.fields.find((df) => df.fieldname === fieldname); + fieldname === "discount_percentage" ? (field_meta.label = __("Discount (%)")) : ""; const me = this; this[`${fieldname}_control`] = frappe.ui.form.make_control({ df: { ...field_meta, - onchange: function() { + onchange: function () { me.events.form_updated(me.current_item, fieldname, this.value); - } + }, }, parent: this.$form_container.find(`.${fieldname}-control`), render_input: true, - }) + }); this[`${fieldname}_control`].set_value(item[fieldname]); }); @@ -193,33 +195,40 @@ erpnext.PointOfSale.ItemDetails = class { } get_form_fields(item) { - const fields = ['qty', 'uom', 'rate', 'conversion_factor', 'discount_percentage', 'warehouse', 'actual_qty', 'price_list_rate']; - if (item.has_serial_no) fields.push('serial_no'); - if (item.has_batch_no) fields.push('batch_no'); + const fields = [ + "qty", + "uom", + "rate", + "conversion_factor", + "discount_percentage", + "warehouse", + "actual_qty", + "price_list_rate", + ]; + if (item.has_serial_no) fields.push("serial_no"); + if (item.has_batch_no) fields.push("batch_no"); return fields; } make_auto_serial_selection_btn(item) { if (item.has_serial_no) { if (!item.has_batch_no) { - this.$form_container.append( - `
              ` - ); + this.$form_container.append(`
              `); } - const label = __('Auto Fetch Serial Numbers'); + const label = __("Auto Fetch Serial Numbers"); this.$form_container.append( `
              ${label}
              ` ); - this.$form_container.find('.serial_no-control').find('textarea').css('height', '6rem'); + this.$form_container.find(".serial_no-control").find("textarea").css("height", "6rem"); } } bind_custom_control_change_event() { const me = this; if (this.rate_control) { - this.rate_control.df.onchange = function() { + this.rate_control.df.onchange = function () { if (this.value || flt(this.value) === 0) { - me.events.form_updated(me.current_item, 'rate', this.value).then(() => { + me.events.form_updated(me.current_item, "rate", this.value).then(() => { const item_row = frappe.get_doc(me.doctype, me.name); const doc = me.events.get_frm().doc; me.$item_price.html(format_currency(item_row.rate, doc.currency)); @@ -238,43 +247,48 @@ erpnext.PointOfSale.ItemDetails = class { if (this.warehouse_control) { this.warehouse_control.df.reqd = 1; - this.warehouse_control.df.onchange = function() { + this.warehouse_control.df.onchange = function () { if (this.value) { - me.events.form_updated(me.current_item, 'warehouse', this.value).then(() => { + me.events.form_updated(me.current_item, "warehouse", this.value).then(() => { me.item_stock_map = me.events.get_item_stock_map(); const available_qty = me.item_stock_map[me.item_row.item_code][this.value][0]; - const is_stock_item = Boolean(me.item_stock_map[me.item_row.item_code][this.value][1]); + const is_stock_item = Boolean( + me.item_stock_map[me.item_row.item_code][this.value][1] + ); if (available_qty === undefined) { me.events.get_available_stock(me.item_row.item_code, this.value).then(() => { // item stock map is updated now reset warehouse me.warehouse_control.set_value(this.value); - }) + }); } else if (available_qty === 0 && is_stock_item) { - me.warehouse_control.set_value(''); + me.warehouse_control.set_value(""); const bold_item_code = me.item_row.item_code.bold(); const bold_warehouse = this.value.bold(); frappe.throw( - __('Item Code: {0} is not available under warehouse {1}.', [bold_item_code, bold_warehouse]) + __("Item Code: {0} is not available under warehouse {1}.", [ + bold_item_code, + bold_warehouse, + ]) ); } me.actual_qty_control.set_value(available_qty); }); } - } + }; this.warehouse_control.df.get_query = () => { return { - filters: { company: this.events.get_frm().doc.company } - } + filters: { company: this.events.get_frm().doc.company }, + }; }; this.warehouse_control.refresh(); } if (this.serial_no_control) { this.serial_no_control.df.reqd = 1; - this.serial_no_control.df.onchange = async function() { - !me.current_item.batch_no && await me.auto_update_batch_no(); - me.events.form_updated(me.current_item, 'serial_no', this.value); - } + this.serial_no_control.df.onchange = async function () { + !me.current_item.batch_no && (await me.auto_update_batch_no()); + me.events.form_updated(me.current_item, "serial_no", this.value); + }; this.serial_no_control.refresh(); } @@ -282,25 +296,25 @@ erpnext.PointOfSale.ItemDetails = class { this.batch_no_control.df.reqd = 1; this.batch_no_control.df.get_query = () => { return { - query: 'erpnext.controllers.queries.get_batch_no', + query: "erpnext.controllers.queries.get_batch_no", filters: { item_code: me.item_row.item_code, warehouse: me.item_row.warehouse, - posting_date: me.events.get_frm().doc.posting_date - } - } + posting_date: me.events.get_frm().doc.posting_date, + }, + }; }; this.batch_no_control.refresh(); } if (this.uom_control) { - this.uom_control.df.onchange = function() { - me.events.form_updated(me.current_item, 'uom', this.value); + this.uom_control.df.onchange = function () { + me.events.form_updated(me.current_item, "uom", this.value); const item_row = frappe.get_doc(me.doctype, me.name); - me.conversion_factor_control.df.read_only = (item_row.stock_uom == this.value); + me.conversion_factor_control.df.read_only = item_row.stock_uom == this.value; me.conversion_factor_control.refresh(); - } + }; } frappe.model.on("POS Invoice Item", "*", (fieldname, value, item_row) => { @@ -316,13 +330,16 @@ erpnext.PointOfSale.ItemDetails = class { async auto_update_batch_no() { if (this.serial_no_control && this.batch_no_control) { - const selected_serial_nos = this.serial_no_control.get_value().split(`\n`).filter(s => s); + const selected_serial_nos = this.serial_no_control + .get_value() + .split(`\n`) + .filter((s) => s); if (!selected_serial_nos.length) return; // find batch nos of the selected serial no const serials_with_batch_no = await frappe.db.get_list("Serial No", { - filters: { 'name': ["in", selected_serial_nos]}, - fields: ["batch_no", "name"] + filters: { name: ["in", selected_serial_nos] }, + fields: ["batch_no", "name"], }); const batch_serial_map = serials_with_batch_no.reduce((acc, r) => { if (!acc[r.batch_no]) { @@ -335,10 +352,11 @@ erpnext.PointOfSale.ItemDetails = class { const batch_no = Object.keys(batch_serial_map)[0]; const batch_serial_nos = batch_serial_map[batch_no].join(`\n`); // eg. 10 selected serial no. -> 5 belongs to first batch other 5 belongs to second batch - const serial_nos_belongs_to_other_batch = selected_serial_nos.length !== batch_serial_map[batch_no].length; + const serial_nos_belongs_to_other_batch = + selected_serial_nos.length !== batch_serial_map[batch_no].length; const current_batch_no = this.batch_no_control.get_value(); - current_batch_no != batch_no && await this.batch_no_control.set_value(batch_no); + current_batch_no != batch_no && (await this.batch_no_control.set_value(batch_no)); if (serial_nos_belongs_to_other_batch) { this.serial_no_control.set_value(batch_serial_nos); @@ -354,13 +372,13 @@ erpnext.PointOfSale.ItemDetails = class { this.bind_auto_serial_fetch_event(); this.bind_fields_to_numpad_fields(); - this.$component.on('click', '.close-btn', () => { + this.$component.on("click", ".close-btn", () => { this.events.close_item_details(); }); } attach_shortcuts() { - this.wrapper.find('.close-btn').attr("title", "Esc"); + this.wrapper.find(".close-btn").attr("title", "Esc"); frappe.ui.keys.on("escape", () => { const item_details_visible = this.$component.is(":visible"); if (item_details_visible) { @@ -371,8 +389,8 @@ erpnext.PointOfSale.ItemDetails = class { bind_fields_to_numpad_fields() { const me = this; - this.$form_container.on('click', '.input-with-feedback', function() { - const fieldname = $(this).attr('data-fieldname'); + this.$form_container.on("click", ".input-with-feedback", function () { + const fieldname = $(this).attr("data-fieldname"); if (this.last_field_focused != fieldname) { me.events.item_field_focused(fieldname); this.last_field_focused = fieldname; @@ -381,8 +399,8 @@ erpnext.PointOfSale.ItemDetails = class { } bind_auto_serial_fetch_event() { - this.$form_container.on('click', '.auto-fetch-btn', () => { - this.batch_no_control && this.batch_no_control.set_value(''); + this.$form_container.on("click", ".auto-fetch-btn", () => { + this.batch_no_control && this.batch_no_control.set_value(""); let qty = this.qty_control.get_value(); let conversion_factor = this.conversion_factor_control.get_value(); let expiry_date = this.item_row.has_batch_no ? this.events.get_frm().doc.posting_date : ""; @@ -392,11 +410,11 @@ erpnext.PointOfSale.ItemDetails = class { args: { qty: qty * conversion_factor, item_code: this.current_item.item_code, - warehouse: this.warehouse_control.get_value() || '', - batch_nos: this.current_item.batch_no || '', + warehouse: this.warehouse_control.get_value() || "", + batch_nos: this.current_item.batch_no || "", posting_date: expiry_date, - for_doctype: 'POS Invoice' - } + for_doctype: "POS Invoice", + }, }); numbers.then((data) => { @@ -406,21 +424,22 @@ erpnext.PointOfSale.ItemDetails = class { const warehouse = this.warehouse_control.get_value().bold(); const item_code = this.current_item.item_code.bold(); frappe.msgprint( - __('Serial numbers unavailable for Item {0} under warehouse {1}. Please try changing warehouse.', [item_code, warehouse]) + __( + "Serial numbers unavailable for Item {0} under warehouse {1}. Please try changing warehouse.", + [item_code, warehouse] + ) ); } else if (records_length < qty) { - frappe.msgprint( - __('Fetched only {0} available serial numbers.', [records_length]) - ); + frappe.msgprint(__("Fetched only {0} available serial numbers.", [records_length])); this.qty_control.set_value(records_length); } numbers = auto_fetched_serial_numbers.join(`\n`); this.serial_no_control.set_value(numbers); }); - }) + }); } toggle_component(show) { - show ? this.$component.css('display', 'flex') : this.$component.css('display', 'none'); + show ? this.$component.css("display", "flex") : this.$component.css("display", "none"); } -} +}; 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 b5eb0489f9d..dba36bd2429 100644 --- a/erpnext/selling/page/point_of_sale/pos_item_selector.js +++ b/erpnext/selling/page/point_of_sale/pos_item_selector.js @@ -1,4 +1,4 @@ -import onScan from 'onscan.js'; +import onScan from "onscan.js"; erpnext.PointOfSale.ItemSelector = class { // eslint-disable-next-line no-unused-vars @@ -24,7 +24,7 @@ erpnext.PointOfSale.ItemSelector = class { this.wrapper.append( `
              -
              ${__('All Items')}
              +
              ${__("All Items")}
              @@ -32,13 +32,13 @@ erpnext.PointOfSale.ItemSelector = class {
              ` ); - this.$component = this.wrapper.find('.items-selector'); - this.$items_container = this.$component.find('.items-container'); + this.$component = this.wrapper.find(".items-selector"); + this.$items_container = this.$component.find(".items-container"); } async load_items_data() { if (!this.item_group) { - const res = await frappe.db.get_value("Item Group", {lft: 1, is_group: 1}, "name"); + const res = await frappe.db.get_value("Item Group", { lft: 1, is_group: 1 }, "name"); this.parent_item_group = res.message.name; } if (!this.price_list) { @@ -46,12 +46,12 @@ erpnext.PointOfSale.ItemSelector = class { this.price_list = res.message.selling_price_list; } - this.get_items({}).then(({message}) => { + this.get_items({}).then(({ message }) => { this.render_item_list(message.items); }); } - get_items({start = 0, page_length = 40, search_term=''}) { + get_items({ start = 0, page_length = 40, search_term = "" }) { const doc = this.events.get_frm().doc; const price_list = (doc && doc.selling_price_list) || this.price_list; let { item_group, pos_profile } = this; @@ -65,11 +65,10 @@ erpnext.PointOfSale.ItemSelector = class { }); } - render_item_list(items) { - this.$items_container.html(''); + this.$items_container.html(""); - items.forEach(item => { + items.forEach((item) => { const item_html = this.get_item_html(item); this.$items_container.append(item_html); }); @@ -84,15 +83,15 @@ erpnext.PointOfSale.ItemSelector = class { let qty_to_display = actual_qty; if (item.is_stock_item) { - indicator_color = (actual_qty > 10 ? "green" : actual_qty <= 0 ? "red" : "orange"); + indicator_color = actual_qty > 10 ? "green" : actual_qty <= 0 ? "red" : "orange"; if (Math.round(qty_to_display) > 999) { - qty_to_display = Math.round(qty_to_display)/1000; - qty_to_display = qty_to_display.toFixed(1) + 'K'; + qty_to_display = Math.round(qty_to_display) / 1000; + qty_to_display = qty_to_display.toFixed(1) + "K"; } } else { - indicator_color = ''; - qty_to_display = ''; + indicator_color = ""; + qty_to_display = ""; } function get_item_image_html() { @@ -115,8 +114,7 @@ erpnext.PointOfSale.ItemSelector = class { } } - return ( - `
              ${format_currency(price_list_rate, item.currency, precision) || 0}
              -
              ` - ); +
              `; } handle_broken_image($img) { - const item_abbr = $($img).attr('alt'); + const item_abbr = $($img).attr("alt"); $($img).parent().replaceWith(`
              ${item_abbr}
              `); } 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(''); + this.$component.find(".search-field").html(""); + this.$component.find(".item-group-field").html(""); this.search_field = frappe.ui.form.make_control({ df: { - label: __('Search'), - fieldtype: 'Data', - placeholder: __('Search by item code, serial number or barcode') + label: __("Search"), + fieldtype: "Data", + placeholder: __("Search by item code, serial number or barcode"), }, - parent: this.$component.find('.search-field'), + parent: this.$component.find(".search-field"), render_input: true, }); this.item_group_field = frappe.ui.form.make_control({ df: { - label: __('Item Group'), - fieldtype: 'Link', - options: 'Item Group', - placeholder: __('Select item group'), - onchange: function() { + label: __("Item Group"), + fieldtype: "Link", + options: "Item Group", + placeholder: __("Select item group"), + onchange: function () { me.item_group = this.value; !me.item_group && (me.item_group = me.parent_item_group); me.filter_items(); }, get_query: function () { return { - query: 'erpnext.selling.page.point_of_sale.point_of_sale.item_group_query', + query: "erpnext.selling.page.point_of_sale.point_of_sale.item_group_query", filters: { - pos_profile: doc ? doc.pos_profile : '' - } + pos_profile: doc ? doc.pos_profile : "", + }, }; }, }, - parent: this.$component.find('.item-group-field'), + parent: this.$component.find(".item-group-field"), render_input: true, }); this.search_field.toggle_label(false); @@ -184,18 +181,18 @@ erpnext.PointOfSale.ItemSelector = class { } attach_clear_btn() { - this.search_field.$wrapper.find('.control-input').append( + this.search_field.$wrapper.find(".control-input").append( ` - ${frappe.utils.icon('close', 'sm')} + ${frappe.utils.icon("close", "sm")} ` ); - this.$clear_search_btn = this.search_field.$wrapper.find('.link-btn'); + this.$clear_search_btn = this.search_field.$wrapper.find(".link-btn"); - this.$clear_search_btn.on('click', 'a', () => { - this.set_search_value(''); + this.$clear_search_btn.on("click", "a", () => { + this.set_search_value(""); this.search_field.set_focus(); }); } @@ -217,39 +214,43 @@ erpnext.PointOfSale.ItemSelector = class { case iCode >= 186 && iCode <= 194: // (; = , - . / `) case iCode >= 219 && iCode <= 222: // ([ \ ] ') case iCode == 32: // spacebar - if (oEvent.key !== undefined && oEvent.key !== '') { + if (oEvent.key !== undefined && oEvent.key !== "") { return oEvent.key; } var sDecoded = String.fromCharCode(iCode); switch (oEvent.shiftKey) { - case false: sDecoded = sDecoded.toLowerCase(); break; - case true: sDecoded = sDecoded.toUpperCase(); break; + case false: + sDecoded = sDecoded.toLowerCase(); + break; + case true: + sDecoded = sDecoded.toUpperCase(); + break; } return sDecoded; case iCode >= 96 && iCode <= 105: // numbers on numeric keypad return 0 + (iCode - 96); } - return ''; + return ""; }; onScan.attachTo(document, { onScan: (sScancode) => { - if (this.search_field && this.$component.is(':visible')) { + if (this.search_field && this.$component.is(":visible")) { this.search_field.set_focus(); this.set_search_value(sScancode); this.barcode_scanned = true; } - } + }, }); - this.$component.on('click', '.item-wrapper', function() { + this.$component.on("click", ".item-wrapper", function () { const $item = $(this); - const item_code = unescape($item.attr('data-item-code')); - let batch_no = unescape($item.attr('data-batch-no')); - let serial_no = unescape($item.attr('data-serial-no')); - let uom = unescape($item.attr('data-uom')); - let rate = unescape($item.attr('data-rate')); + const item_code = unescape($item.attr("data-item-code")); + let batch_no = unescape($item.attr("data-batch-no")); + let serial_no = unescape($item.attr("data-serial-no")); + let uom = unescape($item.attr("data-uom")); + let rate = unescape($item.attr("data-rate")); // escape(undefined) returns "undefined" then unescape returns "undefined" batch_no = batch_no === "undefined" ? undefined : batch_no; @@ -258,76 +259,72 @@ erpnext.PointOfSale.ItemSelector = class { rate = rate === "undefined" ? undefined : rate; me.events.item_selected({ - field: 'qty', + field: "qty", value: "+1", - item: { item_code, batch_no, serial_no, uom, rate } + item: { item_code, batch_no, serial_no, uom, rate }, }); me.search_field.set_focus(); }); - this.search_field.$input.on('input', (e) => { + this.search_field.$input.on("input", (e) => { clearTimeout(this.last_search); this.last_search = setTimeout(() => { const search_term = e.target.value; this.filter_items({ search_term }); }, 300); - this.$clear_search_btn.toggle( - Boolean(this.search_field.$input.val()) - ); + this.$clear_search_btn.toggle(Boolean(this.search_field.$input.val())); }); - this.search_field.$input.on('focus', () => { - this.$clear_search_btn.toggle( - Boolean(this.search_field.$input.val()) - ); + this.search_field.$input.on("focus", () => { + this.$clear_search_btn.toggle(Boolean(this.search_field.$input.val())); }); } attach_shortcuts() { - const ctrl_label = frappe.utils.is_mac() ? '⌘' : 'Ctrl'; + const ctrl_label = frappe.utils.is_mac() ? "⌘" : "Ctrl"; this.search_field.parent.attr("title", `${ctrl_label}+I`); frappe.ui.keys.add_shortcut({ shortcut: "ctrl+i", action: () => this.search_field.set_focus(), - condition: () => this.$component.is(':visible'), + condition: () => this.$component.is(":visible"), description: __("Focus on search input"), ignore_inputs: true, - page: cur_page.page.page + page: cur_page.page.page, }); this.item_group_field.parent.attr("title", `${ctrl_label}+G`); frappe.ui.keys.add_shortcut({ shortcut: "ctrl+g", action: () => this.item_group_field.set_focus(), - condition: () => this.$component.is(':visible'), + condition: () => this.$component.is(":visible"), description: __("Focus on Item Group filter"), ignore_inputs: true, - page: cur_page.page.page + page: cur_page.page.page, }); // for selecting the last filtered item on search frappe.ui.keys.on("enter", () => { - const selector_is_visible = this.$component.is(':visible'); + const selector_is_visible = this.$component.is(":visible"); if (!selector_is_visible || this.search_field.get_value() === "") return; if (this.items.length == 1) { this.$items_container.find(".item-wrapper").click(); frappe.utils.play_sound("submit"); - this.set_search_value(''); + this.set_search_value(""); } else if (this.items.length == 0 && this.barcode_scanned) { // only show alert of barcode is scanned and enter is pressed frappe.show_alert({ message: __("No items found. Scan barcode again."), - indicator: 'orange' + indicator: "orange", }); frappe.utils.play_sound("error"); this.barcode_scanned = false; - this.set_search_value(''); + this.set_search_value(""); } }); } - filter_items({ search_term='' }={}) { + filter_items({ search_term = "" } = {}) { if (search_term) { search_term = search_term.toLowerCase(); @@ -342,44 +339,47 @@ erpnext.PointOfSale.ItemSelector = class { } } - this.get_items({ search_term }) - .then(({ message }) => { - // eslint-disable-next-line no-unused-vars - const { items, serial_no, batch_no, barcode } = message; - if (search_term && !barcode) { - this.search_index[search_term] = items; - } - this.items = items; - this.render_item_list(items); - this.auto_add_item && this.items.length == 1 && this.add_filtered_item_to_cart(); - }); + this.get_items({ search_term }).then(({ message }) => { + // eslint-disable-next-line no-unused-vars + const { items, serial_no, batch_no, barcode } = message; + if (search_term && !barcode) { + this.search_index[search_term] = items; + } + this.items = items; + this.render_item_list(items); + this.auto_add_item && this.items.length == 1 && this.add_filtered_item_to_cart(); + }); } add_filtered_item_to_cart() { this.$items_container.find(".item-wrapper").click(); - this.set_search_value(''); + this.set_search_value(""); } resize_selector(minimize) { - minimize ? - this.$component.find('.filter-section').css('grid-template-columns', 'repeat(1, minmax(0, 1fr))') : - this.$component.find('.filter-section').css('grid-template-columns', 'repeat(12, minmax(0, 1fr))'); + minimize + ? this.$component + .find(".filter-section") + .css("grid-template-columns", "repeat(1, minmax(0, 1fr))") + : this.$component + .find(".filter-section") + .css("grid-template-columns", "repeat(12, minmax(0, 1fr))"); - minimize ? - this.$component.find('.search-field').css('margin', 'var(--margin-sm) 0px') : - this.$component.find('.search-field').css('margin', '0px var(--margin-sm)'); + minimize + ? this.$component.find(".search-field").css("margin", "var(--margin-sm) 0px") + : this.$component.find(".search-field").css("margin", "0px var(--margin-sm)"); - minimize ? - this.$component.css('grid-column', 'span 2 / span 2') : - this.$component.css('grid-column', 'span 6 / span 6'); + minimize + ? this.$component.css("grid-column", "span 2 / span 2") + : this.$component.css("grid-column", "span 6 / span 6"); - minimize ? - this.$items_container.css('grid-template-columns', 'repeat(1, minmax(0, 1fr))') : - this.$items_container.css('grid-template-columns', 'repeat(4, minmax(0, 1fr))'); + minimize + ? this.$items_container.css("grid-template-columns", "repeat(1, minmax(0, 1fr))") + : this.$items_container.css("grid-template-columns", "repeat(4, minmax(0, 1fr))"); } toggle_component(show) { - this.set_search_value(''); - this.$component.css('display', show ? 'flex': 'none'); + this.set_search_value(""); + this.$component.css("display", show ? "flex" : "none"); } }; diff --git a/erpnext/selling/page/point_of_sale/pos_number_pad.js b/erpnext/selling/page/point_of_sale/pos_number_pad.js index f27b0d55ef6..c77f206308d 100644 --- a/erpnext/selling/page/point_of_sale/pos_number_pad.js +++ b/erpnext/selling/page/point_of_sale/pos_number_pad.js @@ -20,28 +20,40 @@ erpnext.PointOfSale.NumberPad = class { function get_keys() { return keys.reduce((a, row, i) => { - return a + row.reduce((a2, number, j) => { - const class_to_append = css_classes && css_classes[i] ? css_classes[i][j] : ''; - const fieldname = fieldnames && fieldnames[number] ? - fieldnames[number] : typeof number === 'string' ? frappe.scrub(number) : number; + return ( + a + + row.reduce((a2, number, j) => { + const class_to_append = css_classes && css_classes[i] ? css_classes[i][j] : ""; + const fieldname = + fieldnames && fieldnames[number] + ? fieldnames[number] + : typeof number === "string" + ? frappe.scrub(number) + : number; - return a2 + `
              ${__(number)}
              `; - }, ''); - }, ''); + return ( + a2 + + `
              ${__( + number + )}
              ` + ); + }, "") + ); + }, ""); } this.wrapper.html( `
              ${get_keys()}
              ` - ) + ); } bind_events() { const me = this; - this.wrapper.on('click', '.numpad-btn', function() { + this.wrapper.on("click", ".numpad-btn", function () { const $btn = $(this); me.events.numpad_event($btn); }); } -} +}; diff --git a/erpnext/selling/page/point_of_sale/pos_past_order_list.js b/erpnext/selling/page/point_of_sale/pos_past_order_list.js index a0475c70d0d..c450d8a109a 100644 --- a/erpnext/selling/page/point_of_sale/pos_past_order_list.js +++ b/erpnext/selling/page/point_of_sale/pos_past_order_list.js @@ -16,7 +16,7 @@ erpnext.PointOfSale.PastOrderList = class { this.wrapper.append( `
              -
              ${__('Recent Orders')}
              +
              ${__("Recent Orders")}
              @@ -24,12 +24,12 @@ erpnext.PointOfSale.PastOrderList = class {
              ` ); - this.$component = this.wrapper.find('.past-order-list'); - this.$invoices_container = this.$component.find('.invoices-container'); + this.$component = this.wrapper.find(".past-order-list"); + this.$invoices_container = this.$component.find(".invoices-container"); } bind_events() { - this.search_field.$input.on('input', (e) => { + this.search_field.$input.on("input", (e) => { clearTimeout(this.last_search); this.last_search = setTimeout(() => { const search_term = e.target.value; @@ -37,8 +37,8 @@ erpnext.PointOfSale.PastOrderList = class { }, 300); }); const me = this; - this.$invoices_container.on('click', '.invoice-wrapper', function() { - const invoice_name = unescape($(this).attr('data-invoice-name')); + this.$invoices_container.on("click", ".invoice-wrapper", function () { + const invoice_name = unescape($(this).attr("data-invoice-name")); me.events.open_invoice_data(invoice_name); }); @@ -48,29 +48,29 @@ erpnext.PointOfSale.PastOrderList = class { const me = this; this.search_field = frappe.ui.form.make_control({ df: { - label: __('Search'), - fieldtype: 'Data', - placeholder: __('Search by invoice id or customer name') + label: __("Search"), + fieldtype: "Data", + placeholder: __("Search by invoice id or customer name"), }, - parent: this.$component.find('.search-field'), + parent: this.$component.find(".search-field"), render_input: true, }); this.status_field = frappe.ui.form.make_control({ df: { - label: __('Invoice Status'), - fieldtype: 'Select', + label: __("Invoice Status"), + fieldtype: "Select", options: `Draft\nPaid\nConsolidated\nReturn`, - placeholder: __('Filter by invoice status'), - onchange: function() { - if (me.$component.is(':visible')) me.refresh_list(); - } + placeholder: __("Filter by invoice status"), + onchange: function () { + if (me.$component.is(":visible")) me.refresh_list(); + }, }, - parent: this.$component.find('.status-field'), + parent: this.$component.find(".status-field"), render_input: true, }); this.search_field.toggle_label(false); this.status_field.toggle_label(false); - this.status_field.set_value('Draft'); + this.status_field.set_value("Draft"); } refresh_list() { @@ -79,7 +79,7 @@ erpnext.PointOfSale.PastOrderList = class { const search_term = this.search_field.get_value(); const status = this.status_field.get_value(); - this.$invoices_container.html(''); + this.$invoices_container.html(""); return frappe.call({ method: "erpnext.selling.page.point_of_sale.point_of_sale.get_past_order_list", @@ -87,18 +87,19 @@ erpnext.PointOfSale.PastOrderList = class { args: { search_term, status }, callback: (response) => { frappe.dom.unfreeze(); - response.message.forEach(invoice => { + response.message.forEach((invoice) => { const invoice_html = this.get_invoice_html(invoice); this.$invoices_container.append(invoice_html); }); - } + }, }); } get_invoice_html(invoice) { - const posting_datetime = moment(invoice.posting_date+" "+invoice.posting_time).format("Do MMMM, h:mma"); - return ( - `
              + const posting_datetime = moment(invoice.posting_date + " " + invoice.posting_time).format( + "Do MMMM, h:mma" + ); + return `
              ${invoice.name}
              @@ -113,11 +114,12 @@ erpnext.PointOfSale.PastOrderList = class {
              ${posting_datetime}
              -
              ` - ); +
              `; } toggle_component(show) { - show ? this.$component.css('display', 'flex') && this.refresh_list() : this.$component.css('display', 'none'); + show + ? this.$component.css("display", "flex") && this.refresh_list() + : this.$component.css("display", "none"); } }; 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 eeb8523f19c..53fedbf56cf 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 @@ -17,16 +17,16 @@ erpnext.PointOfSale.PastOrderSummary = class { this.wrapper.append( `
              - ${__('Select an invoice to load summary data')} + ${__("Select an invoice to load summary data")}
              -
              ${__('Items')}
              +
              ${__("Items")}
              -
              ${__('Totals')}
              +
              ${__("Totals")}
              -
              ${__('Payments')}
              +
              ${__("Payments")}
              @@ -34,55 +34,53 @@ erpnext.PointOfSale.PastOrderSummary = class {
              ` ); - this.$component = this.wrapper.find('.past-order-summary'); - this.$summary_wrapper = this.$component.find('.invoice-summary-wrapper'); - this.$summary_container = this.$component.find('.abs-container'); - this.$upper_section = this.$summary_container.find('.upper-section'); - this.$items_container = this.$summary_container.find('.items-container'); - this.$totals_container = this.$summary_container.find('.totals-container'); - this.$payment_container = this.$summary_container.find('.payments-container'); - this.$summary_btns = this.$summary_container.find('.summary-btns'); + this.$component = this.wrapper.find(".past-order-summary"); + this.$summary_wrapper = this.$component.find(".invoice-summary-wrapper"); + this.$summary_container = this.$component.find(".abs-container"); + this.$upper_section = this.$summary_container.find(".upper-section"); + this.$items_container = this.$summary_container.find(".items-container"); + this.$totals_container = this.$summary_container.find(".totals-container"); + this.$payment_container = this.$summary_container.find(".payments-container"); + this.$summary_btns = this.$summary_container.find(".summary-btns"); } 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'}, + { fieldname: "email_id", fieldtype: "Data", options: "Email", label: "Email ID" }, // {fieldname:'remarks', fieldtype:'Text', label:'Remarks (if any)'} ], primary_action: () => { this.send_email(); }, - primary_action_label: __('Send'), + primary_action_label: __("Send"), }); this.email_dialog = email_dialog; const print_dialog = new frappe.ui.Dialog({ - title: 'Print Receipt', - fields: [ - {fieldname: 'print', fieldtype: 'Data', label: 'Print Preview'} - ], + title: "Print Receipt", + fields: [{ fieldname: "print", fieldtype: "Data", label: "Print Preview" }], primary_action: () => { this.print_receipt(); }, - primary_action_label: __('Print'), + primary_action_label: __("Print"), }); this.print_dialog = print_dialog; } get_upper_section_html(doc) { const { status } = doc; - let indicator_color = ''; + let indicator_color = ""; - in_list(['Paid', 'Consolidated'], status) && (indicator_color = 'green'); - status === 'Draft' && (indicator_color = 'red'); - status === 'Return' && (indicator_color = 'grey'); + in_list(["Paid", "Consolidated"], status) && (indicator_color = "green"); + status === "Draft" && (indicator_color = "red"); + status === "Return" && (indicator_color = "grey"); return `
              ${doc.customer}
              ${this.customer_email}
              -
              ${__('Sold by')}: ${doc.owner}
              +
              ${__("Sold by")}: ${doc.owner}
              @@ -103,7 +101,10 @@ erpnext.PointOfSale.PastOrderSummary = class { return `(${item_data.discount_percentage}% off)
              ${format_currency(item_data.rate, doc.currency)}
              `; } else { - return `
              ${format_currency(item_data.price_list_rate || item_data.rate, doc.currency)}
              `; + return `
              ${format_currency( + item_data.price_list_rate || item_data.rate, + doc.currency + )}
              `; } } } @@ -121,30 +122,34 @@ erpnext.PointOfSale.PastOrderSummary = class { get_net_total_html(doc) { return `
              -
              ${__('Net Total')}
              +
              ${__("Net Total")}
              ${format_currency(doc.net_total, doc.currency)}
              `; } get_taxes_html(doc) { - if (!doc.taxes.length) return ''; + if (!doc.taxes.length) return ""; - let taxes_html = doc.taxes.map(t => { - const description = /[0-9]+/.test(t.description) ? t.description : `${t.description} @ ${t.rate}%`; - return ` + let taxes_html = doc.taxes + .map((t) => { + const description = /[0-9]+/.test(t.description) + ? t.description + : `${t.description} @ ${t.rate}%`; + return `
              ${description}
              ${format_currency(t.tax_amount_after_discount_amount, doc.currency)}
              `; - }).join(''); + }) + .join(""); return `
              ${taxes_html}
              `; } get_grand_total_html(doc) { return `
              -
              ${__('Grand Total')}
              +
              ${__("Grand Total")}
              ${format_currency(doc.grand_total, doc.currency)}
              `; } @@ -157,26 +162,26 @@ erpnext.PointOfSale.PastOrderSummary = class { } bind_events() { - this.$summary_container.on('click', '.return-btn', () => { + this.$summary_container.on("click", ".return-btn", () => { this.events.process_return(this.doc.name); this.toggle_component(false); - this.$component.find('.no-summary-placeholder').css('display', 'flex'); - this.$summary_wrapper.css('display', 'none'); + this.$component.find(".no-summary-placeholder").css("display", "flex"); + this.$summary_wrapper.css("display", "none"); }); - this.$summary_container.on('click', '.edit-btn', () => { + this.$summary_container.on("click", ".edit-btn", () => { this.events.edit_order(this.doc.name); this.toggle_component(false); - this.$component.find('.no-summary-placeholder').css('display', 'flex'); - this.$summary_wrapper.css('display', 'none'); + this.$component.find(".no-summary-placeholder").css("display", "flex"); + this.$summary_wrapper.css("display", "none"); }); - this.$summary_container.on('click', '.delete-btn', () => { + this.$summary_container.on("click", ".delete-btn", () => { this.events.delete_order(this.doc.name); this.show_summary_placeholder(); }); - this.$summary_container.on('click', '.delete-btn', () => { + this.$summary_container.on("click", ".delete-btn", () => { this.events.delete_order(this.doc.name); this.show_summary_placeholder(); // this.toggle_component(false); @@ -184,19 +189,19 @@ erpnext.PointOfSale.PastOrderSummary = class { // this.$summary_wrapper.addClass('d-none'); }); - this.$summary_container.on('click', '.new-btn', () => { + this.$summary_container.on("click", ".new-btn", () => { this.events.new_order(); this.toggle_component(false); - this.$component.find('.no-summary-placeholder').css('display', 'flex'); - this.$summary_wrapper.css('display', 'none'); + this.$component.find(".no-summary-placeholder").css("display", "flex"); + this.$summary_wrapper.css("display", "none"); }); - this.$summary_container.on('click', '.email-btn', () => { + this.$summary_container.on("click", ".email-btn", () => { this.email_dialog.fields_dict.email_id.set_value(this.customer_email); this.email_dialog.show(); }); - this.$summary_container.on('click', '.print-btn', () => { + this.$summary_container.on("click", ".print-btn", () => { this.print_receipt(); }); } @@ -213,29 +218,31 @@ erpnext.PointOfSale.PastOrderSummary = class { } attach_shortcuts() { - const ctrl_label = frappe.utils.is_mac() ? '⌘' : 'Ctrl'; - this.$summary_container.find('.print-btn').attr("title", `${ctrl_label}+P`); + const ctrl_label = frappe.utils.is_mac() ? "⌘" : "Ctrl"; + this.$summary_container.find(".print-btn").attr("title", `${ctrl_label}+P`); frappe.ui.keys.add_shortcut({ shortcut: "ctrl+p", - action: () => this.$summary_container.find('.print-btn').click(), - condition: () => this.$component.is(':visible') && this.$summary_container.find('.print-btn').is(":visible"), + action: () => this.$summary_container.find(".print-btn").click(), + condition: () => + this.$component.is(":visible") && this.$summary_container.find(".print-btn").is(":visible"), description: __("Print Receipt"), - page: cur_page.page.page + page: cur_page.page.page, }); - this.$summary_container.find('.new-btn').attr("title", `${ctrl_label}+Enter`); + this.$summary_container.find(".new-btn").attr("title", `${ctrl_label}+Enter`); frappe.ui.keys.on("ctrl+enter", () => { const summary_is_visible = this.$component.is(":visible"); - if (summary_is_visible && this.$summary_container.find('.new-btn').is(":visible")) { - this.$summary_container.find('.new-btn').click(); + if (summary_is_visible && this.$summary_container.find(".new-btn").is(":visible")) { + this.$summary_container.find(".new-btn").click(); } }); - this.$summary_container.find('.edit-btn').attr("title", `${ctrl_label}+E`); + this.$summary_container.find(".edit-btn").attr("title", `${ctrl_label}+E`); frappe.ui.keys.add_shortcut({ shortcut: "ctrl+e", - action: () => this.$summary_container.find('.edit-btn').click(), - condition: () => this.$component.is(':visible') && this.$summary_container.find('.edit-btn').is(":visible"), + action: () => this.$summary_container.find(".edit-btn").click(), + condition: () => + this.$component.is(":visible") && this.$summary_container.find(".edit-btn").is(":visible"), description: __("Edit Receipt"), - page: cur_page.page.page + page: cur_page.page.page, }); } @@ -249,42 +256,43 @@ erpnext.PointOfSale.PastOrderSummary = class { method: "frappe.core.doctype.communication.email.make", args: { recipients: recipients, - subject: __(frm.meta.name) + ': ' + doc.name, + subject: __(frm.meta.name) + ": " + doc.name, doctype: doc.doctype, name: doc.name, send_email: 1, print_format, sender_full_name: frappe.user.full_name(), - _lang: doc.language + _lang: doc.language, }, - callback: r => { + callback: (r) => { if (!r.exc) { frappe.utils.play_sound("email"); if (r.message["emails_not_sent_to"]) { - frappe.msgprint(__( - "Email not sent to {0} (unsubscribed / disabled)", - [ frappe.utils.escape_html(r.message["emails_not_sent_to"]) ] - )); + frappe.msgprint( + __("Email not sent to {0} (unsubscribed / disabled)", [ + frappe.utils.escape_html(r.message["emails_not_sent_to"]), + ]) + ); } else { frappe.show_alert({ - message: __('Email sent successfully.'), - indicator: 'green' + message: __("Email sent successfully."), + indicator: "green", }); } this.email_dialog.hide(); } else { frappe.msgprint(__("There were errors while sending email. Please try again.")); } - } + }, }); } add_summary_btns(map) { - this.$summary_btns.html(''); - map.forEach(m => { + this.$summary_btns.html(""); + map.forEach((m) => { if (m.condition) { - m.visible_btns.forEach(b => { - const class_name = b.split(' ')[0].toLowerCase(); + m.visible_btns.forEach((b) => { + const class_name = b.split(" ")[0].toLowerCase(); const btn = __(b); this.$summary_btns.append( `
              ${btn}
              ` @@ -292,34 +300,40 @@ erpnext.PointOfSale.PastOrderSummary = class { }); } }); - this.$summary_btns.children().last().removeClass('mr-4'); + this.$summary_btns.children().last().removeClass("mr-4"); } toggle_summary_placeholder(show) { if (show) { - this.$summary_wrapper.css('display', 'none'); - this.$component.find('.no-summary-placeholder').css('display', 'flex'); + this.$summary_wrapper.css("display", "none"); + this.$component.find(".no-summary-placeholder").css("display", "flex"); } else { - this.$summary_wrapper.css('display', 'flex'); - this.$component.find('.no-summary-placeholder').css('display', 'none'); + this.$summary_wrapper.css("display", "flex"); + this.$component.find(".no-summary-placeholder").css("display", "none"); } } get_condition_btn_map(after_submission) { if (after_submission) - return [{ condition: true, visible_btns: ['Print Receipt', 'Email Receipt', 'New Order'] }]; + return [{ condition: true, visible_btns: ["Print Receipt", "Email Receipt", "New Order"] }]; return [ - { condition: this.doc.docstatus === 0, visible_btns: ['Edit Order', 'Delete Order'] }, - { condition: !this.doc.is_return && this.doc.docstatus === 1, visible_btns: ['Print Receipt', 'Email Receipt', 'Return']}, - { condition: this.doc.is_return && this.doc.docstatus === 1, visible_btns: ['Print Receipt', 'Email Receipt']} + { condition: this.doc.docstatus === 0, visible_btns: ["Edit Order", "Delete Order"] }, + { + condition: !this.doc.is_return && this.doc.docstatus === 1, + visible_btns: ["Print Receipt", "Email Receipt", "Return"], + }, + { + condition: this.doc.is_return && this.doc.docstatus === 1, + visible_btns: ["Print Receipt", "Email Receipt"], + }, ]; } - load_summary_of(doc, after_submission=false) { - after_submission ? - this.$component.css('grid-column', 'span 10 / span 10') : - this.$component.css('grid-column', 'span 6 / span 6'); + load_summary_of(doc, after_submission = false) { + after_submission + ? this.$component.css("grid-column", "span 10 / span 10") + : this.$component.css("grid-column", "span 6 / span 6"); this.toggle_summary_placeholder(false); @@ -339,16 +353,16 @@ erpnext.PointOfSale.PastOrderSummary = class { } attach_document_info(doc) { - frappe.db.get_value('Customer', this.doc.customer, 'email_id').then(({ message }) => { - this.customer_email = message.email_id || ''; + frappe.db.get_value("Customer", this.doc.customer, "email_id").then(({ message }) => { + this.customer_email = message.email_id || ""; const upper_section_dom = this.get_upper_section_html(doc); this.$upper_section.html(upper_section_dom); }); } attach_items_info(doc) { - this.$items_container.html(''); - doc.items.forEach(item => { + this.$items_container.html(""); + doc.items.forEach((item) => { const item_dom = this.get_item_html(doc, item); this.$items_container.append(item_dom); this.set_dynamic_rate_header_width(); @@ -359,8 +373,7 @@ erpnext.PointOfSale.PastOrderSummary = class { const rate_cols = Array.from(this.$items_container.find(".item-rate-disc")); this.$items_container.find(".item-rate-disc").css("width", ""); let max_width = rate_cols.reduce((max_width, elm) => { - if ($(elm).width() > max_width) - max_width = $(elm).width(); + if ($(elm).width() > max_width) max_width = $(elm).width(); return max_width; }, 0); @@ -371,8 +384,8 @@ erpnext.PointOfSale.PastOrderSummary = class { } attach_payments_info(doc) { - this.$payment_container.html(''); - doc.payments.forEach(p => { + this.$payment_container.html(""); + doc.payments.forEach((p) => { if (p.amount) { const payment_dom = this.get_payment_html(doc, p); this.$payment_container.append(payment_dom); @@ -380,7 +393,7 @@ erpnext.PointOfSale.PastOrderSummary = class { }); if (doc.redeem_loyalty_points && doc.loyalty_amount) { const payment_dom = this.get_payment_html(doc, { - mode_of_payment: 'Loyalty Points', + mode_of_payment: "Loyalty Points", amount: doc.loyalty_amount, }); this.$payment_container.append(payment_dom); @@ -388,7 +401,7 @@ erpnext.PointOfSale.PastOrderSummary = class { } attach_totals_info(doc) { - this.$totals_container.html(''); + this.$totals_container.html(""); const net_total_dom = this.get_net_total_html(doc); const taxes_dom = this.get_taxes_html(doc); @@ -401,6 +414,6 @@ erpnext.PointOfSale.PastOrderSummary = class { } toggle_component(show) { - show ? this.$component.css('display', 'flex') : this.$component.css('display', 'none'); + show ? this.$component.css("display", "flex") : this.$component.css("display", "none"); } }; diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js index 89ce61ab168..232b6a02123 100644 --- a/erpnext/selling/page/point_of_sale/pos_payment.js +++ b/erpnext/selling/page/point_of_sale/pos_payment.js @@ -12,17 +12,16 @@ erpnext.PointOfSale.Payment = class { this.initialize_numpad(); this.bind_events(); this.attach_shortcuts(); - } prepare_dom() { this.wrapper.append( `
              - +
              - +
              @@ -33,12 +32,12 @@ erpnext.PointOfSale.Payment = class {
              ${__("Complete Order")}
              ` ); - this.$component = this.wrapper.find('.payment-container'); - this.$payment_modes = this.$component.find('.payment-modes'); - this.$totals_section = this.$component.find('.totals-section'); - this.$totals = this.$component.find('.totals'); - this.$numpad = this.$component.find('.number-pad'); - this.$invoice_fields_section = this.$component.find('.fields-section'); + this.$component = this.wrapper.find(".payment-container"); + this.$payment_modes = this.$component.find(".payment-modes"); + this.$totals_section = this.$component.find(".totals-section"); + this.$totals = this.$component.find(".totals"); + this.$numpad = this.$component.find(".number-pad"); + this.$invoice_fields_section = this.$component.find(".fields-section"); } make_invoice_fields_control() { @@ -46,33 +45,33 @@ erpnext.PointOfSale.Payment = class { const fields = doc.invoice_fields; if (!fields.length) return; - this.$invoice_fields = this.$invoice_fields_section.find('.invoice-fields'); - this.$invoice_fields.html(''); + this.$invoice_fields = this.$invoice_fields_section.find(".invoice-fields"); + this.$invoice_fields.html(""); const frm = this.events.get_frm(); - fields.forEach(df => { + fields.forEach((df) => { this.$invoice_fields.append( `
              ` ); let df_events = { - onchange: function() { + onchange: function () { frm.set_value(this.df.fieldname, this.get_value()); - } + }, }; if (df.fieldtype == "Button") { df_events = { - click: function() { + click: function () { if (frm.script_manager.has_handlers(df.fieldname, frm.doc.doctype)) { frm.script_manager.trigger(df.fieldname, frm.doc.doctype, frm.doc.docname); } - } + }, }; } this[`${df.fieldname}_field`] = frappe.ui.form.make_control({ df: { ...df, - ...df_events + ...df_events, }, parent: this.$invoice_fields.find(`.${df.fieldname}-field`), render_input: true, @@ -87,34 +86,35 @@ erpnext.PointOfSale.Payment = class { this.number_pad = new erpnext.PointOfSale.NumberPad({ wrapper: this.$numpad, events: { - numpad_event: function($btn) { + numpad_event: function ($btn) { me.on_numpad_clicked($btn); - } + }, }, cols: 3, keys: [ - [ 1, 2, 3 ], - [ 4, 5, 6 ], - [ 7, 8, 9 ], - [ '.', 0, 'Delete' ] + [1, 2, 3], + [4, 5, 6], + [7, 8, 9], + [".", 0, "Delete"], ], }); - this.numpad_value = ''; + this.numpad_value = ""; } on_numpad_clicked($btn) { - const button_value = $btn.attr('data-button-value'); + const button_value = $btn.attr("data-button-value"); highlight_numpad_btn($btn); - this.numpad_value = button_value === 'delete' ? this.numpad_value.slice(0, -1) : this.numpad_value + button_value; + this.numpad_value = + button_value === "delete" ? this.numpad_value.slice(0, -1) : this.numpad_value + button_value; this.selected_mode.$input.get(0).focus(); this.selected_mode.set_value(this.numpad_value); function highlight_numpad_btn($btn) { - $btn.addClass('shadow-base-inner bg-selected'); + $btn.addClass("shadow-base-inner bg-selected"); setTimeout(() => { - $btn.removeClass('shadow-base-inner bg-selected'); + $btn.removeClass("shadow-base-inner bg-selected"); }, 100); } } @@ -122,36 +122,37 @@ erpnext.PointOfSale.Payment = class { bind_events() { const me = this; - this.$payment_modes.on('click', '.mode-of-payment', function(e) { + this.$payment_modes.on("click", ".mode-of-payment", function (e) { const mode_clicked = $(this); // if clicked element doesn't have .mode-of-payment class then return if (!$(e.target).is(mode_clicked)) return; - const scrollLeft = mode_clicked.offset().left - me.$payment_modes.offset().left + me.$payment_modes.scrollLeft(); + const scrollLeft = + mode_clicked.offset().left - me.$payment_modes.offset().left + me.$payment_modes.scrollLeft(); me.$payment_modes.animate({ scrollLeft }); - const mode = mode_clicked.attr('data-mode'); + const mode = mode_clicked.attr("data-mode"); // hide all control fields and shortcuts - $(`.mode-of-payment-control`).css('display', 'none'); - $(`.cash-shortcuts`).css('display', 'none'); - me.$payment_modes.find(`.pay-amount`).css('display', 'inline'); - me.$payment_modes.find(`.loyalty-amount-name`).css('display', 'none'); + $(`.mode-of-payment-control`).css("display", "none"); + $(`.cash-shortcuts`).css("display", "none"); + me.$payment_modes.find(`.pay-amount`).css("display", "inline"); + me.$payment_modes.find(`.loyalty-amount-name`).css("display", "none"); // remove highlight from all mode-of-payments - $('.mode-of-payment').removeClass('border-primary'); + $(".mode-of-payment").removeClass("border-primary"); - if (mode_clicked.hasClass('border-primary')) { + if (mode_clicked.hasClass("border-primary")) { // clicked one is selected then unselect it - mode_clicked.removeClass('border-primary'); - me.selected_mode = ''; + mode_clicked.removeClass("border-primary"); + me.selected_mode = ""; } else { // clicked one is not selected then select it - mode_clicked.addClass('border-primary'); - mode_clicked.find('.mode-of-payment-control').css('display', 'flex'); - mode_clicked.find('.cash-shortcuts').css('display', 'grid'); - me.$payment_modes.find(`.${mode}-amount`).css('display', 'none'); - me.$payment_modes.find(`.${mode}-name`).css('display', 'inline'); + mode_clicked.addClass("border-primary"); + mode_clicked.find(".mode-of-payment-control").css("display", "flex"); + mode_clicked.find(".cash-shortcuts").css("display", "grid"); + me.$payment_modes.find(`.${mode}-amount`).css("display", "none"); + me.$payment_modes.find(`.${mode}-name`).css("display", "inline"); me.selected_mode = me[`${mode}_control`]; me.selected_mode && me.selected_mode.$input.get(0).focus(); @@ -159,33 +160,33 @@ erpnext.PointOfSale.Payment = class { } }); - frappe.ui.form.on('POS Invoice', 'contact_mobile', (frm) => { + frappe.ui.form.on("POS Invoice", "contact_mobile", (frm) => { const contact = frm.doc.contact_mobile; const request_button = $(this.request_for_payment_field?.$input[0]); if (contact) { - request_button.removeClass('btn-default').addClass('btn-primary'); + request_button.removeClass("btn-default").addClass("btn-primary"); } else { - request_button.removeClass('btn-primary').addClass('btn-default'); + request_button.removeClass("btn-primary").addClass("btn-default"); } }); - frappe.ui.form.on('POS Invoice', 'coupon_code', (frm) => { + frappe.ui.form.on("POS Invoice", "coupon_code", (frm) => { if (frm.doc.coupon_code && !frm.applying_pos_coupon_code) { if (!frm.doc.ignore_pricing_rule) { frm.applying_pos_coupon_code = true; frappe.run_serially([ - () => frm.doc.ignore_pricing_rule=1, - () => frm.trigger('ignore_pricing_rule'), - () => frm.doc.ignore_pricing_rule=0, - () => frm.trigger('apply_pricing_rule'), + () => (frm.doc.ignore_pricing_rule = 1), + () => frm.trigger("ignore_pricing_rule"), + () => (frm.doc.ignore_pricing_rule = 0), + () => frm.trigger("apply_pricing_rule"), () => frm.save(), () => this.update_totals_section(frm.doc), - () => (frm.applying_pos_coupon_code = false) + () => (frm.applying_pos_coupon_code = false), ]); } else if (frm.doc.ignore_pricing_rule) { frappe.show_alert({ message: __("Ignore Pricing Rule is enabled. Cannot apply coupon code."), - indicator: "orange" + indicator: "orange", }); } } @@ -193,18 +194,20 @@ erpnext.PointOfSale.Payment = class { this.setup_listener_for_payments(); - this.$payment_modes.on('click', '.shortcut', function() { - const value = $(this).attr('data-value'); + this.$payment_modes.on("click", ".shortcut", function () { + const value = $(this).attr("data-value"); me.selected_mode.set_value(value); }); - this.$component.on('click', '.submit-order-btn', () => { + this.$component.on("click", ".submit-order-btn", () => { const doc = this.events.get_frm().doc; const paid_amount = doc.paid_amount; const items = doc.items; if (paid_amount == 0 || !items.length) { - const message = items.length ? __("You cannot submit the order without payment.") : __("You cannot submit empty order."); + const message = items.length + ? __("You cannot submit the order without payment.") + : __("You cannot submit empty order."); frappe.show_alert({ message, indicator: "orange" }); frappe.utils.play_sound("error"); return; @@ -213,17 +216,18 @@ erpnext.PointOfSale.Payment = class { this.events.submit_invoice(); }); - frappe.ui.form.on('POS Invoice', 'paid_amount', (frm) => { + frappe.ui.form.on("POS Invoice", "paid_amount", (frm) => { this.update_totals_section(frm.doc); // need to re calculate cash shortcuts after discount is applied - const is_cash_shortcuts_invisible = !this.$payment_modes.find('.cash-shortcuts').is(':visible'); + const is_cash_shortcuts_invisible = !this.$payment_modes.find(".cash-shortcuts").is(":visible"); this.attach_cash_shortcuts(frm.doc); - !is_cash_shortcuts_invisible && this.$payment_modes.find('.cash-shortcuts').css('display', 'grid'); + !is_cash_shortcuts_invisible && + this.$payment_modes.find(".cash-shortcuts").css("display", "grid"); this.render_payment_mode_dom(); }); - frappe.ui.form.on('POS Invoice', 'loyalty_amount', (frm) => { + frappe.ui.form.on("POS Invoice", "loyalty_amount", (frm) => { const formatted_currency = format_currency(frm.doc.loyalty_amount, frm.doc.currency); this.$payment_modes.find(`.loyalty-amount-amount`).html(formatted_currency); }); @@ -246,28 +250,36 @@ erpnext.PointOfSale.Payment = class { if (success) { title = __("Payment Received"); - const grand_total = cint(frappe.sys_defaults.disable_rounded_total) ? doc.grand_total : doc.rounded_total; + const grand_total = cint(frappe.sys_defaults.disable_rounded_total) + ? doc.grand_total + : doc.rounded_total; if (amount >= grand_total) { frappe.dom.unfreeze(); - message = __("Payment of {0} received successfully.", [format_currency(amount, doc.currency, 0)]); + message = __("Payment of {0} received successfully.", [ + format_currency(amount, doc.currency, 0), + ]); this.events.submit_invoice(); cur_frm.reload_doc(); - } else { - message = __("Payment of {0} received successfully. Waiting for other requests to complete...", [format_currency(amount, doc.currency, 0)]); + message = __( + "Payment of {0} received successfully. Waiting for other requests to complete...", + [format_currency(amount, doc.currency, 0)] + ); } } else if (failure_message) { message = failure_message; title = __("Payment Failed"); } - frappe.msgprint({ "message": message, "title": title }); + frappe.msgprint({ message: message, title: title }); }); } auto_set_remaining_amount() { const doc = this.events.get_frm().doc; - const grand_total = cint(frappe.sys_defaults.disable_rounded_total) ? doc.grand_total : doc.rounded_total; + const grand_total = cint(frappe.sys_defaults.disable_rounded_total) + ? doc.grand_total + : doc.rounded_total; const remaining_amount = grand_total - doc.paid_amount; const current_value = this.selected_mode ? this.selected_mode.get_value() : undefined; if (!current_value && remaining_amount > 0 && this.selected_mode) { @@ -276,13 +288,13 @@ erpnext.PointOfSale.Payment = class { } attach_shortcuts() { - const ctrl_label = frappe.utils.is_mac() ? '⌘' : 'Ctrl'; - this.$component.find('.submit-order-btn').attr("title", `${ctrl_label}+Enter`); + const ctrl_label = frappe.utils.is_mac() ? "⌘" : "Ctrl"; + this.$component.find(".submit-order-btn").attr("title", `${ctrl_label}+Enter`); frappe.ui.keys.on("ctrl+enter", () => { const payment_is_visible = this.$component.is(":visible"); const active_mode = this.$payment_modes.find(".border-primary"); if (payment_is_visible && active_mode.length) { - this.$component.find('.submit-order-btn').click(); + this.$component.find(".submit-order-btn").click(); } }); @@ -295,19 +307,24 @@ erpnext.PointOfSale.Payment = class { if (!active_mode) return; - const mode_of_payments = Array.from(this.$payment_modes.find(".mode-of-payment")).map(m => $(m).attr("data-mode")); + const mode_of_payments = Array.from(this.$payment_modes.find(".mode-of-payment")).map((m) => + $(m).attr("data-mode") + ); const mode_index = mode_of_payments.indexOf(active_mode); const next_mode_index = (mode_index + 1) % mode_of_payments.length; - const next_mode_to_be_clicked = this.$payment_modes.find(`.mode-of-payment[data-mode="${mode_of_payments[next_mode_index]}"]`); + const next_mode_to_be_clicked = this.$payment_modes.find( + `.mode-of-payment[data-mode="${mode_of_payments[next_mode_index]}"]` + ); if (payment_is_visible && mode_index != next_mode_index) { next_mode_to_be_clicked.click(); } }, - condition: () => this.$component.is(':visible') && this.$payment_modes.find(".border-primary").length, + condition: () => + this.$component.is(":visible") && this.$payment_modes.find(".border-primary").length, description: __("Switch Between Payment Modes"), ignore_inputs: true, - page: cur_page.page.page + page: cur_page.page.page, }); } @@ -341,20 +358,20 @@ erpnext.PointOfSale.Payment = class { } toggle_remarks_control() { - if (this.$remarks.find('.frappe-control').length) { - this.$remarks.html('+ Add Remark'); + if (this.$remarks.find(".frappe-control").length) { + this.$remarks.html("+ Add Remark"); } else { - this.$remarks.html(''); + this.$remarks.html(""); this[`remark_control`] = frappe.ui.form.make_control({ df: { - label: __('Remark'), - fieldtype: 'Data', - onchange: function() {} + label: __("Remark"), + fieldtype: "Data", + onchange: function () {}, }, parent: this.$totals_section.find(`.remarks`), render_input: true, }); - this[`remark_control`].set_value(''); + this[`remark_control`].set_value(""); } } @@ -363,14 +380,15 @@ erpnext.PointOfSale.Payment = class { const payments = doc.payments; const currency = doc.currency; - this.$payment_modes.html(`${ - payments.map((p, i) => { - const mode = p.mode_of_payment.replace(/ +/g, "_").toLowerCase(); - const payment_type = p.type; - const margin = i % 2 === 0 ? 'pr-2' : 'pl-2'; - const amount = p.amount > 0 ? format_currency(p.amount, currency) : ''; + this.$payment_modes.html( + `${payments + .map((p, i) => { + const mode = p.mode_of_payment.replace(/ +/g, "_").toLowerCase(); + const payment_type = p.type; + const margin = i % 2 === 0 ? "pr-2" : "pl-2"; + const amount = p.amount > 0 ? format_currency(p.amount, currency) : ""; - return (` + return `
              ${p.mode_of_payment} @@ -378,29 +396,30 @@ erpnext.PointOfSale.Payment = class {
              - `); - }).join('') - }`); + `; + }) + .join("")}` + ); - payments.forEach(p => { + payments.forEach((p) => { const mode = p.mode_of_payment.replace(/ +/g, "_").toLowerCase(); const me = this; this[`${mode}_control`] = frappe.ui.form.make_control({ df: { label: p.mode_of_payment, - fieldtype: 'Currency', - placeholder: __('Enter {0} amount.', [p.mode_of_payment]), - onchange: function() { - const current_value = frappe.model.get_value(p.doctype, p.name, 'amount'); + fieldtype: "Currency", + placeholder: __("Enter {0} amount.", [p.mode_of_payment]), + onchange: function () { + const current_value = frappe.model.get_value(p.doctype, p.name, "amount"); if (current_value != this.value) { frappe.model - .set_value(p.doctype, p.name, 'amount', flt(this.value)) - .then(() => me.update_totals_section()) + .set_value(p.doctype, p.name, "amount", flt(this.value)) + .then(() => me.update_totals_section()); const formatted_currency = format_currency(this.value, currency); me.$payment_modes.find(`.${mode}-amount`).html(formatted_currency); } - } + }, }, parent: this.$payment_modes.find(`.${mode}.mode-of-payment-control`), render_input: true, @@ -417,7 +436,7 @@ erpnext.PointOfSale.Payment = class { focus_on_default_mop() { const doc = this.events.get_frm().doc; const payments = doc.payments; - payments.forEach(p => { + payments.forEach((p) => { const mode = p.mode_of_payment.replace(/ +/g, "_").toLowerCase(); if (p.default) { setTimeout(() => { @@ -428,17 +447,23 @@ erpnext.PointOfSale.Payment = class { } attach_cash_shortcuts(doc) { - const grand_total = cint(frappe.sys_defaults.disable_rounded_total) ? doc.grand_total : doc.rounded_total; + const grand_total = cint(frappe.sys_defaults.disable_rounded_total) + ? doc.grand_total + : doc.rounded_total; const currency = doc.currency; const shortcuts = this.get_cash_shortcuts(flt(grand_total)); - this.$payment_modes.find('.cash-shortcuts').remove(); - let shortcuts_html = shortcuts.map(s => { - return `
              ${format_currency(s, currency, 0)}
              `; - }).join(''); + this.$payment_modes.find(".cash-shortcuts").remove(); + let shortcuts_html = shortcuts + .map((s) => { + return `
              ${format_currency(s, currency, 0)}
              `; + }) + .join(""); - this.$payment_modes.find('[data-payment-type="Cash"]').find('.mode-of-payment-control') + this.$payment_modes + .find('[data-payment-type="Cash"]') + .find(".mode-of-payment-control") .after(`
              ${shortcuts_html}
              `); } @@ -446,10 +471,10 @@ erpnext.PointOfSale.Payment = class { let steps = [1, 5, 10]; const digits = String(Math.round(grand_total)).length; - steps = steps.map(x => x * (10 ** (digits - 2))); + steps = steps.map((x) => x * 10 ** (digits - 2)); const get_nearest = (amount, x) => { - let nearest_x = Math.ceil((amount / x)) * x; + let nearest_x = Math.ceil(amount / x) * x; return nearest_x === amount ? nearest_x + x : nearest_x; }; @@ -474,13 +499,16 @@ erpnext.PointOfSale.Payment = class { description = __("You don't have enough points to redeem."); read_only = true; } else { - max_redeemable_amount = flt(flt(loyalty_points) * flt(conversion_factor), precision("loyalty_amount", doc)); + max_redeemable_amount = flt( + flt(loyalty_points) * flt(conversion_factor), + precision("loyalty_amount", doc) + ); description = __("You can redeem upto {0}.", [format_currency(max_redeemable_amount)]); read_only = false; } - const margin = this.$payment_modes.children().length % 2 === 0 ? 'pr-2' : 'pl-2'; - const amount = doc.loyalty_amount > 0 ? format_currency(doc.loyalty_amount, doc.currency) : ''; + const margin = this.$payment_modes.children().length % 2 === 0 ? "pr-2" : "pl-2"; + const amount = doc.loyalty_amount > 0 ? format_currency(doc.loyalty_amount, doc.currency) : ""; this.$payment_modes.append( `
              @@ -492,35 +520,47 @@ erpnext.PointOfSale.Payment = class {
              ` ); - this['loyalty-amount_control'] = frappe.ui.form.make_control({ + this["loyalty-amount_control"] = frappe.ui.form.make_control({ df: { label: __("Redeem Loyalty Points"), - fieldtype: 'Currency', + fieldtype: "Currency", placeholder: __("Enter amount to be redeemed."), - options: 'company:currency', + options: "company:currency", read_only, - onchange: async function() { + onchange: async function () { if (!loyalty_points) return; if (this.value > max_redeemable_amount) { frappe.show_alert({ - message: __("You cannot redeem more than {0}.", [format_currency(max_redeemable_amount)]), - indicator: "red" + message: __("You cannot redeem more than {0}.", [ + format_currency(max_redeemable_amount), + ]), + indicator: "red", }); frappe.utils.play_sound("submit"); - me['loyalty-amount_control'].set_value(0); + me["loyalty-amount_control"].set_value(0); return; } const redeem_loyalty_points = this.value > 0 ? 1 : 0; - await frappe.model.set_value(doc.doctype, doc.name, 'redeem_loyalty_points', redeem_loyalty_points); - frappe.model.set_value(doc.doctype, doc.name, 'loyalty_points', parseInt(this.value / conversion_factor)); + await frappe.model.set_value( + doc.doctype, + doc.name, + "redeem_loyalty_points", + redeem_loyalty_points + ); + frappe.model.set_value( + doc.doctype, + doc.name, + "loyalty_points", + parseInt(this.value / conversion_factor) + ); }, - description + description, }, parent: this.$payment_modes.find(`.loyalty-amount.mode-of-payment-control`), render_input: true, }); - this['loyalty-amount_control'].toggle_label(false); + this["loyalty-amount_control"].toggle_label(false); // this.render_add_payment_method_dom(); } @@ -538,20 +578,22 @@ erpnext.PointOfSale.Payment = class { update_totals_section(doc) { if (!doc) doc = this.events.get_frm().doc; const paid_amount = doc.paid_amount; - const grand_total = cint(frappe.sys_defaults.disable_rounded_total) ? doc.grand_total : doc.rounded_total; + const grand_total = cint(frappe.sys_defaults.disable_rounded_total) + ? doc.grand_total + : doc.rounded_total; 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 ? __("Change") : __("To Be Paid"); this.$totals.html( `
              -
              ${__('Grand Total')}
              +
              ${__("Grand Total")}
              ${format_currency(grand_total, currency)}
              -
              ${__('Paid Amount')}
              +
              ${__("Paid Amount")}
              ${format_currency(paid_amount, currency)}
              @@ -563,6 +605,6 @@ erpnext.PointOfSale.Payment = class { } toggle_component(show) { - show ? this.$component.css('display', 'flex') : this.$component.css('display', 'none'); + show ? this.$component.css("display", "flex") : this.$component.css("display", "none"); } }; diff --git a/erpnext/selling/page/sales_funnel/sales_funnel.js b/erpnext/selling/page/sales_funnel/sales_funnel.js index c37c7266362..d8546ba51ce 100644 --- a/erpnext/selling/page/sales_funnel/sales_funnel.js +++ b/erpnext/selling/page/sales_funnel/sales_funnel.js @@ -1,23 +1,23 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.pages['sales-funnel'].on_page_load = function(wrapper) { +frappe.pages["sales-funnel"].on_page_load = function (wrapper) { frappe.ui.make_app_page({ parent: wrapper, - title: __('Sales Funnel'), - single_column: true + title: __("Sales Funnel"), + single_column: true, }); wrapper.sales_funnel = new erpnext.SalesFunnel(wrapper); frappe.breadcrumbs.add("Selling"); -} +}; erpnext.SalesFunnel = class SalesFunnel { constructor(wrapper) { var me = this; // 0 setTimeout hack - this gives time for canvas to get width and height - setTimeout(function() { + setTimeout(function () { me.setup(wrapper); me.get_data(); }, 0); @@ -26,50 +26,65 @@ erpnext.SalesFunnel = class SalesFunnel { setup(wrapper) { var me = this; - this.company_field = wrapper.page.add_field({"fieldtype": "Link", "fieldname": "company", "options": "Company", - "label": __("Company"), "reqd": 1, "default": frappe.defaults.get_user_default('company'), - change: function() { - me.company = this.value || frappe.defaults.get_user_default('company'); + (this.company_field = wrapper.page.add_field({ + fieldtype: "Link", + fieldname: "company", + options: "Company", + label: __("Company"), + reqd: 1, + default: frappe.defaults.get_user_default("company"), + change: function () { + me.company = this.value || frappe.defaults.get_user_default("company"); me.get_data(); - } - }), + }, + })), + (this.elements = { + layout: $(wrapper).find(".layout-main"), + from_date: wrapper.page.add_date(__("From Date")), + to_date: wrapper.page.add_date(__("To Date")), + chart: wrapper.page.add_select(__("Chart"), [ + { value: "sales_funnel", label: __("Sales Funnel") }, + { value: "sales_pipeline", label: __("Sales Pipeline") }, + { value: "opp_by_lead_source", label: __("Opportunities by lead source") }, + ]), + refresh_btn: wrapper.page.set_primary_action( + __("Refresh"), + function () { + me.get_data(); + }, + "fa fa-refresh" + ), + }); - this.elements = { - layout: $(wrapper).find(".layout-main"), - from_date: wrapper.page.add_date(__("From Date")), - to_date: wrapper.page.add_date(__("To Date")), - chart: wrapper.page.add_select(__("Chart"), [{value: 'sales_funnel', label:__("Sales Funnel")}, - {value: 'sales_pipeline', label:__("Sales Pipeline")}, - {value: 'opp_by_lead_source', label:__("Opportunities by lead source")}]), - refresh_btn: wrapper.page.set_primary_action(__("Refresh"), - function() { me.get_data(); }, "fa fa-refresh"), - }; - - this.elements.no_data = $('
              ' + __("No Data") + '
              ') + this.elements.no_data = $('
              ' + __("No Data") + "
              ") .toggle(false) .appendTo(this.elements.layout); - this.elements.funnel_wrapper = $('
              ') - .appendTo(this.elements.layout); + this.elements.funnel_wrapper = $('
              ').appendTo( + this.elements.layout + ); - this.company = frappe.defaults.get_user_default('company'); + this.company = frappe.defaults.get_user_default("company"); this.options = { from_date: frappe.datetime.add_months(frappe.datetime.get_today(), -1), to_date: frappe.datetime.get_today(), - chart: 'sales_funnel' + chart: "sales_funnel", }; // set defaults and bind on change - $.each(this.options, function(k, v) { - if (['from_date', 'to_date'].includes(k)) { + $.each(this.options, function (k, v) { + if (["from_date", "to_date"].includes(k)) { me.elements[k].val(frappe.datetime.str_to_user(v)); } else { me.elements[k].val(v); } - me.elements[k].on("change", function() { - if (['from_date', 'to_date'].includes(k)) { - me.options[k] = frappe.datetime.user_to_str($(this).val()) != 'Invalid date' ? frappe.datetime.user_to_str($(this).val()) : frappe.datetime.get_today(); + me.elements[k].on("change", function () { + if (["from_date", "to_date"].includes(k)) { + me.options[k] = + frappe.datetime.user_to_str($(this).val()) != "Invalid date" + ? frappe.datetime.user_to_str($(this).val()) + : frappe.datetime.get_today(); } else { me.options.chart = $(this).val(); } @@ -78,12 +93,12 @@ erpnext.SalesFunnel = class SalesFunnel { }); // bind refresh - this.elements.refresh_btn.on("click", function() { + this.elements.refresh_btn.on("click", function () { me.get_data(this); }); // bind resize - $(window).resize(function() { + $(window).resize(function () { me.render(); }); } @@ -95,39 +110,39 @@ erpnext.SalesFunnel = class SalesFunnel { } const method_map = { - "sales_funnel": "erpnext.selling.page.sales_funnel.sales_funnel.get_funnel_data", - "opp_by_lead_source": "erpnext.selling.page.sales_funnel.sales_funnel.get_opp_by_lead_source", - "sales_pipeline": "erpnext.selling.page.sales_funnel.sales_funnel.get_pipeline_data" + sales_funnel: "erpnext.selling.page.sales_funnel.sales_funnel.get_funnel_data", + opp_by_lead_source: "erpnext.selling.page.sales_funnel.sales_funnel.get_opp_by_lead_source", + sales_pipeline: "erpnext.selling.page.sales_funnel.sales_funnel.get_pipeline_data", }; frappe.call({ method: method_map[this.options.chart], args: { from_date: this.options.from_date, to_date: this.options.to_date, - company: this.company + company: this.company, }, btn: btn, - callback: function(r) { - if(!r.exc) { + callback: function (r) { + if (!r.exc) { me.options.data = r.message; - if (me.options.data=='empty') { + if (me.options.data == "empty") { const $parent = me.elements.funnel_wrapper; - $parent.html(__('No data for this period')); + $parent.html(__("No data for this period")); } else { me.render(); } } - } + }, }); } render() { let me = this; - if (me.options.chart == 'sales_funnel'){ + if (me.options.chart == "sales_funnel") { me.render_funnel(); - } else if (me.options.chart == 'opp_by_lead_source'){ + } else if (me.options.chart == "opp_by_lead_source") { me.render_chart("Sales Opportunities by Source"); - } else if (me.options.chart == 'sales_pipeline'){ + } else if (me.options.chart == "sales_pipeline") { me.render_chart("Sales Pipeline by Stage"); } } @@ -143,12 +158,12 @@ erpnext.SalesFunnel = class SalesFunnel { y = 0, y_old = 0.0; - if(this.options.total_value === 0) { + if (this.options.total_value === 0) { this.elements.no_data.toggle(true); return; } - this.options.data.forEach(function(d) { + this.options.data.forEach(function (d) { context.fillStyle = d.color; context.strokeStyle = d.color; me.draw_triangle(x_start, x_mid, x_end, y, me.options.height); @@ -175,21 +190,22 @@ erpnext.SalesFunnel = class SalesFunnel { this.elements.no_data.toggle(false); // calculate width and height options - this.options.width = $(this.elements.funnel_wrapper).width() * 2.0 / 3.0; + this.options.width = ($(this.elements.funnel_wrapper).width() * 2.0) / 3.0; this.options.height = (Math.sqrt(3) * this.options.width) / 2.0; // calculate total weightage // as height decreases, area decreases by the square of the reduction // hence, compensating by squaring the index value - this.options.total_weightage = this.options.data.reduce( - function(prev, curr, i) { return prev + Math.pow(i+1, 2) * curr.value; }, 0.0); + this.options.total_weightage = this.options.data.reduce(function (prev, curr, i) { + return prev + Math.pow(i + 1, 2) * curr.value; + }, 0.0); // calculate height for each data - $.each(this.options.data, function(i, d) { - d.height = me.options.height * d.value * Math.pow(i+1, 2) / me.options.total_weightage; + $.each(this.options.data, function (i, d) { + d.height = (me.options.height * d.value * Math.pow(i + 1, 2)) / me.options.total_weightage; }); - this.elements.canvas = $('') + this.elements.canvas = $("") .appendTo(this.elements.funnel_wrapper.empty()) .attr("width", $(this.elements.funnel_wrapper).width()) .attr("height", this.options.height); @@ -211,7 +227,7 @@ erpnext.SalesFunnel = class SalesFunnel { draw_legend(x_mid, y_mid, width, height, title) { var context = this.elements.context; - if(y_mid == 0) { + if (y_mid == 0) { y_mid = 7; } @@ -246,13 +262,13 @@ erpnext.SalesFunnel = class SalesFunnel { title: title, height: 400, data: chart_data, - type: 'bar', + type: "bar", barOptions: { - stacked: 1 + stacked: 1, }, tooltipOptions: { - formatTooltipY: d => format_currency(d, currency), - } + formatTooltipY: (d) => format_currency(d, currency), + }, }); } }; diff --git a/erpnext/selling/report/address_and_contacts/address_and_contacts.js b/erpnext/selling/report/address_and_contacts/address_and_contacts.js index ef87586f66e..26265480416 100644 --- a/erpnext/selling/report/address_and_contacts/address_and_contacts.js +++ b/erpnext/selling/report/address_and_contacts/address_and_contacts.js @@ -3,32 +3,32 @@ /* eslint-disable */ frappe.query_reports["Address And Contacts"] = { - "filters": [ + filters: [ { - "reqd": 1, - "fieldname":"party_type", - "label": __("Party Type"), - "fieldtype": "Link", - "options": "DocType", - "get_query": function() { + reqd: 1, + fieldname: "party_type", + label: __("Party Type"), + fieldtype: "Link", + options: "DocType", + get_query: function () { return { - "filters": { - "name": ["in","Customer,Supplier,Sales Partner"], - } - } - } + filters: { + name: ["in", "Customer,Supplier,Sales Partner"], + }, + }; + }, }, { - "fieldname":"party_name", - "label": __("Party Name"), - "fieldtype": "Dynamic Link", - "get_options": function() { - let party_type = frappe.query_report.get_filter_value('party_type'); - if(!party_type) { + fieldname: "party_name", + label: __("Party Name"), + fieldtype: "Dynamic Link", + get_options: function () { + let party_type = frappe.query_report.get_filter_value("party_type"); + if (!party_type) { frappe.throw(__("Please select Party Type first")); } return party_type; - } - } - ] -} + }, + }, + ], +}; diff --git a/erpnext/selling/report/available_stock_for_packing_items/available_stock_for_packing_items.js b/erpnext/selling/report/available_stock_for_packing_items/available_stock_for_packing_items.js index 1e83acdeb18..a9ee0cbf067 100644 --- a/erpnext/selling/report/available_stock_for_packing_items/available_stock_for_packing_items.js +++ b/erpnext/selling/report/available_stock_for_packing_items/available_stock_for_packing_items.js @@ -2,7 +2,5 @@ // For license information, please see license.txt frappe.query_reports["Available Stock for Packing Items"] = { - "filters": [ - - ] -} + filters: [], +}; diff --git a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.js b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.js index 1b931e12de3..04891301d7b 100644 --- a/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.js +++ b/erpnext/selling/report/customer_acquisition_and_loyalty/customer_acquisition_and_loyalty.js @@ -2,43 +2,43 @@ // License: GNU General Public License v3. See license.txt frappe.query_reports["Customer Acquisition and Loyalty"] = { - "filters": [ + filters: [ { - "fieldname": "view_type", - "label": __("View Type"), - "fieldtype": "Select", - "options": ["Monthly", "Territory Wise"], - "default": "Monthly", - "reqd": 1 + fieldname: "view_type", + label: __("View Type"), + fieldtype: "Select", + options: ["Monthly", "Territory Wise"], + default: "Monthly", + reqd: 1, }, { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company"), - "reqd": 1 + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, }, { - "fieldname":"from_date", - "label": __("From Date"), - "fieldtype": "Date", - "default": frappe.defaults.get_user_default("year_start_date"), - "reqd": 1 + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.defaults.get_user_default("year_start_date"), + reqd: 1, }, { - "fieldname":"to_date", - "label": __("To Date"), - "fieldtype": "Date", - "default": frappe.defaults.get_user_default("year_end_date"), - "reqd": 1 - } + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.defaults.get_user_default("year_end_date"), + reqd: 1, + }, ], - 'formatter': function(value, row, column, data, default_formatter) { + formatter: function (value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); if (data && data.bold) { value = value.bold(); } return value; - } -} + }, +}; diff --git a/erpnext/selling/report/customer_credit_balance/customer_credit_balance.js b/erpnext/selling/report/customer_credit_balance/customer_credit_balance.js index 3a99eb0891d..9902abedfa3 100644 --- a/erpnext/selling/report/customer_credit_balance/customer_credit_balance.js +++ b/erpnext/selling/report/customer_credit_balance/customer_credit_balance.js @@ -2,20 +2,20 @@ // For license information, please see license.txt frappe.query_reports["Customer Credit Balance"] = { - "filters": [ + filters: [ { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "reqd": 1, - "default": frappe.defaults.get_user_default("Company") + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + reqd: 1, + default: frappe.defaults.get_user_default("Company"), }, { - "fieldname":"customer", - "label": __("Customer"), - "fieldtype": "Link", - "options": "Customer" - } - ] -} + fieldname: "customer", + label: __("Customer"), + fieldtype: "Link", + options: "Customer", + }, + ], +}; diff --git a/erpnext/selling/report/customer_wise_item_price/customer_wise_item_price.js b/erpnext/selling/report/customer_wise_item_price/customer_wise_item_price.js index d333c8be65e..6d937a8eede 100644 --- a/erpnext/selling/report/customer_wise_item_price/customer_wise_item_price.js +++ b/erpnext/selling/report/customer_wise_item_price/customer_wise_item_price.js @@ -3,25 +3,25 @@ /* eslint-disable */ frappe.query_reports["Customer-wise Item Price"] = { - "filters": [ + filters: [ { - "label": __("Customer"), - "fieldname": "customer", - "fieldtype": "Link", - "options": "Customer", - "reqd": 1 + label: __("Customer"), + fieldname: "customer", + fieldtype: "Link", + options: "Customer", + reqd: 1, }, { - "label": __("Item"), - "fieldname": "item", - "fieldtype": "Link", - "options": "Item", - "get_query": () => { + label: __("Item"), + fieldname: "item", + fieldtype: "Link", + options: "Item", + get_query: () => { return { query: "erpnext.controllers.queries.item_query", - filters: { 'is_sales_item': 1 } - } - } - } - ] -} + filters: { is_sales_item: 1 }, + }; + }, + }, + ], +}; diff --git a/erpnext/selling/report/inactive_customers/inactive_customers.js b/erpnext/selling/report/inactive_customers/inactive_customers.js index 804d17e2b0b..a191d564fee 100644 --- a/erpnext/selling/report/inactive_customers/inactive_customers.js +++ b/erpnext/selling/report/inactive_customers/inactive_customers.js @@ -2,19 +2,19 @@ // For license information, please see license.txt frappe.query_reports["Inactive Customers"] = { - "filters": [ + filters: [ { - "fieldname":"days_since_last_order", - "label": __("Days Since Last Order"), - "fieldtype": "Int", - "default": 60 + fieldname: "days_since_last_order", + label: __("Days Since Last Order"), + fieldtype: "Int", + default: 60, }, { - "fieldname":"doctype", - "label": __("Doctype"), - "fieldtype": "Select", - "default": "Sales Order", - "options": "Sales Order\nSales Invoice" - } - ] -} + fieldname: "doctype", + label: __("Doctype"), + fieldtype: "Select", + default: "Sales Order", + options: "Sales Order\nSales Invoice", + }, + ], +}; diff --git a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.js b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.js index 073be789791..1db05643b4b 100644 --- a/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.js +++ b/erpnext/selling/report/item_wise_sales_history/item_wise_sales_history.js @@ -3,55 +3,55 @@ /* eslint-disable */ frappe.query_reports["Item-wise Sales History"] = { - "filters": [ + filters: [ { - fieldname:"company", + fieldname: "company", label: __("Company"), fieldtype: "Link", options: "Company", default: frappe.defaults.get_user_default("Company"), - reqd: 1 + reqd: 1, }, { - fieldname:"from_date", + fieldname: "from_date", reqd: 1, label: __("From Date"), fieldtype: "Date", default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), }, { - fieldname:"to_date", + fieldname: "to_date", reqd: 1, default: frappe.datetime.get_today(), label: __("To Date"), fieldtype: "Date", }, { - fieldname:"item_group", + fieldname: "item_group", label: __("Item Group"), fieldtype: "Link", - options: "Item Group" + options: "Item Group", }, { - fieldname:"item_code", + fieldname: "item_code", label: __("Item"), fieldtype: "Link", options: "Item", get_query: () => { return { - query: "erpnext.controllers.queries.item_query" - } - } + query: "erpnext.controllers.queries.item_query", + }; + }, }, { - fieldname:"customer", + fieldname: "customer", label: __("Customer"), fieldtype: "Link", - options: "Customer" - } + options: "Customer", + }, ], - "formatter": function (value, row, column, data, default_formatter) { + formatter: function (value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); let format_fields = ["delivered_quantity", "billed_amount"]; @@ -59,5 +59,5 @@ frappe.query_reports["Item-wise Sales History"] = { value = "" + value + ""; } return value; - } + }, }; diff --git a/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.js b/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.js index 990d736baa4..32c84b2538b 100644 --- a/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.js +++ b/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.js @@ -5,140 +5,135 @@ function get_filters() { let filters = [ { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company"), - "reqd": 1 + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, }, { - "fieldname":"period_start_date", - "label": __("Start Date"), - "fieldtype": "Date", - "reqd": 1, - "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1) + fieldname: "period_start_date", + label: __("Start Date"), + fieldtype: "Date", + reqd: 1, + default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), }, { - "fieldname":"period_end_date", - "label": __("End Date"), - "fieldtype": "Date", - "reqd": 1, - "default": frappe.datetime.get_today() + fieldname: "period_end_date", + label: __("End Date"), + fieldtype: "Date", + reqd: 1, + default: frappe.datetime.get_today(), }, { - "fieldname":"customer_group", - "label": __("Customer Group"), - "fieldtype": "Link", - "width": 100, - "options": "Customer Group", + fieldname: "customer_group", + label: __("Customer Group"), + fieldtype: "Link", + width: 100, + options: "Customer Group", }, { - "fieldname":"customer", - "label": __("Customer"), - "fieldtype": "Link", - "width": 100, - "options": "Customer", - "get_query": () => { - var customer_group = frappe.query_report.get_filter_value('customer_group'); - return{ - "query": "erpnext.selling.report.payment_terms_status_for_sales_order.payment_terms_status_for_sales_order.get_customers_or_items", - "filters": [ - ['Customer', 'disabled', '=', '0'], - ['Customer Group','name', '=', customer_group] - ] - } - } + fieldname: "customer", + label: __("Customer"), + fieldtype: "Link", + width: 100, + options: "Customer", + get_query: () => { + var customer_group = frappe.query_report.get_filter_value("customer_group"); + return { + query: "erpnext.selling.report.payment_terms_status_for_sales_order.payment_terms_status_for_sales_order.get_customers_or_items", + filters: [ + ["Customer", "disabled", "=", "0"], + ["Customer Group", "name", "=", customer_group], + ], + }; + }, }, { - "fieldname":"item_group", - "label": __("Item Group"), - "fieldtype": "Link", - "width": 100, - "options": "Item Group", - + fieldname: "item_group", + label: __("Item Group"), + fieldtype: "Link", + width: 100, + options: "Item Group", }, { - "fieldname":"item", - "label": __("Item"), - "fieldtype": "Link", - "width": 100, - "options": "Item", - "get_query": () => { - var item_group = frappe.query_report.get_filter_value('item_group'); - return{ - "query": "erpnext.selling.report.payment_terms_status_for_sales_order.payment_terms_status_for_sales_order.get_customers_or_items", - "filters": [ - ['Item', 'disabled', '=', '0'], - ['Item Group','name', '=', item_group] - ] - } - } + fieldname: "item", + label: __("Item"), + fieldtype: "Link", + width: 100, + options: "Item", + get_query: () => { + var item_group = frappe.query_report.get_filter_value("item_group"); + return { + query: "erpnext.selling.report.payment_terms_status_for_sales_order.payment_terms_status_for_sales_order.get_customers_or_items", + filters: [ + ["Item", "disabled", "=", "0"], + ["Item Group", "name", "=", item_group], + ], + }; + }, }, { - "fieldname":"from_due_date", - "label": __("From Due Date"), - "fieldtype": "Date", + fieldname: "from_due_date", + label: __("From Due Date"), + fieldtype: "Date", }, { - "fieldname":"to_due_date", - "label": __("To Due Date"), - "fieldtype": "Date", + fieldname: "to_due_date", + label: __("To Due Date"), + fieldtype: "Date", }, { - "fieldname":"status", - "label": __("Status"), - "fieldtype": "MultiSelectList", - "width": 100, - get_data: function(txt) { - let status = ["Overdue", "Unpaid", "Completed", "Partly Paid"] - let options = [] - for (let option of status){ + fieldname: "status", + label: __("Status"), + fieldtype: "MultiSelectList", + width: 100, + get_data: function (txt) { + let status = ["Overdue", "Unpaid", "Completed", "Partly Paid"]; + let options = []; + for (let option of status) { options.push({ - "value": option, - "label": __(option), - "description": "" - }) + value: option, + label: __(option), + description: "", + }); } - return options - } + return options; + }, }, { - "fieldname":"only_immediate_upcoming_term", - "label": __("Show only the Immediate Upcoming Term"), - "fieldtype": "Check", + fieldname: "only_immediate_upcoming_term", + label: __("Show only the Immediate Upcoming Term"), + fieldtype: "Check", }, - ] + ]; return filters; } frappe.query_reports["Payment Terms Status for Sales Order"] = { - "filters": get_filters(), - "formatter": function(value, row, column, data, default_formatter){ - if(column.fieldname == 'invoices' && value) { - invoices = value.split(','); + filters: get_filters(), + formatter: function (value, row, column, data, default_formatter) { + if (column.fieldname == "invoices" && value) { + invoices = value.split(","); const invoice_formatter = (prev_value, curr_value) => { - if(prev_value != "") { + if (prev_value != "") { return prev_value + ", " + default_formatter(curr_value, row, column, data); - } - else { + } else { return default_formatter(curr_value, row, column, data); } - } - return invoices.reduce(invoice_formatter, "") - } - else if (column.fieldname == 'paid_amount' && value){ + }; + return invoices.reduce(invoice_formatter, ""); + } else if (column.fieldname == "paid_amount" && value) { formatted_value = default_formatter(value, row, column, data); - if(value > 0) { - formatted_value = "" + formatted_value + "" + if (value > 0) { + formatted_value = "" + formatted_value + ""; } return formatted_value; - } - else if (column.fieldname == 'status' && value == 'Completed'){ + } else if (column.fieldname == "status" && value == "Completed") { return "" + default_formatter(value, row, column, data) + ""; } return default_formatter(value, row, column, data); }, - }; diff --git a/erpnext/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.js b/erpnext/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.js index 37634efb6cd..484fdc508b0 100644 --- a/erpnext/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.js +++ b/erpnext/selling/report/pending_so_items_for_purchase_request/pending_so_items_for_purchase_request.js @@ -2,5 +2,4 @@ // For license information, please see license.txt /* eslint-disable */ -frappe.query_reports["Pending SO Items For Purchase Request"] = { -} +frappe.query_reports["Pending SO Items For Purchase Request"] = {}; diff --git a/erpnext/selling/report/quotation_trends/quotation_trends.js b/erpnext/selling/report/quotation_trends/quotation_trends.js index 97a19315ec3..8ffeda47b64 100644 --- a/erpnext/selling/report/quotation_trends/quotation_trends.js +++ b/erpnext/selling/report/quotation_trends/quotation_trends.js @@ -1,8 +1,8 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.require("assets/erpnext/js/sales_trends_filters.js", function() { +frappe.require("assets/erpnext/js/sales_trends_filters.js", function () { frappe.query_reports["Quotation Trends"] = { - filters: erpnext.get_sales_trends_filters() - } + filters: erpnext.get_sales_trends_filters(), + }; }); diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.js b/erpnext/selling/report/sales_analytics/sales_analytics.js index 87dd02f9915..9274586c3e3 100644 --- a/erpnext/selling/report/sales_analytics/sales_analytics.js +++ b/erpnext/selling/report/sales_analytics/sales_analytics.js @@ -3,47 +3,55 @@ /* eslint-disable */ frappe.query_reports["Sales Analytics"] = { - "filters": [ + filters: [ { fieldname: "tree_type", label: __("Tree Type"), fieldtype: "Select", - options: ["Customer Group", "Customer", "Item Group", "Item", "Territory", "Order Type", "Project"], + options: [ + "Customer Group", + "Customer", + "Item Group", + "Item", + "Territory", + "Order Type", + "Project", + ], default: "Customer", - reqd: 1 + reqd: 1, }, { fieldname: "doc_type", label: __("based_on"), fieldtype: "Select", - options: ["Sales Order","Delivery Note","Sales Invoice"], + options: ["Sales Order", "Delivery Note", "Sales Invoice"], default: "Sales Invoice", - reqd: 1 + reqd: 1, }, { fieldname: "value_quantity", label: __("Value Or Qty"), fieldtype: "Select", options: [ - { "value": "Value", "label": __("Value") }, - { "value": "Quantity", "label": __("Quantity") }, + { value: "Value", label: __("Value") }, + { value: "Quantity", label: __("Quantity") }, ], default: "Value", - reqd: 1 + reqd: 1, }, { fieldname: "from_date", label: __("From Date"), fieldtype: "Date", default: frappe.defaults.get_user_default("year_start_date"), - reqd: 1 + reqd: 1, }, { - fieldname:"to_date", + fieldname: "to_date", label: __("To Date"), fieldtype: "Date", default: frappe.defaults.get_user_default("year_end_date"), - reqd: 1 + reqd: 1, }, { fieldname: "company", @@ -51,21 +59,21 @@ frappe.query_reports["Sales Analytics"] = { fieldtype: "Link", options: "Company", default: frappe.defaults.get_user_default("Company"), - reqd: 1 + reqd: 1, }, { fieldname: "range", label: __("Range"), fieldtype: "Select", options: [ - { "value": "Weekly", "label": __("Weekly") }, - { "value": "Monthly", "label": __("Monthly") }, - { "value": "Quarterly", "label": __("Quarterly") }, - { "value": "Yearly", "label": __("Yearly") } + { value: "Weekly", label: __("Weekly") }, + { value: "Monthly", label: __("Monthly") }, + { value: "Quarterly", label: __("Quarterly") }, + { value: "Yearly", label: __("Yearly") }, ], default: "Monthly", - reqd: 1 - } + reqd: 1, + }, ], get_datatable_options(options) { return Object.assign(options, { @@ -73,32 +81,26 @@ frappe.query_reports["Sales Analytics"] = { events: { onCheckRow: function (data) { if (!data) return; - const data_doctype = $( - data[2].html - )[0].attributes.getNamedItem("data-doctype").value; + const data_doctype = $(data[2].html)[0].attributes.getNamedItem("data-doctype").value; const tree_type = frappe.query_report.filters[0].value; if (data_doctype != tree_type) return; const row_name = data[2].content; const raw_data = frappe.query_report.chart.data; const new_datasets = raw_data.datasets; - const element_found = new_datasets.some( - (element, index, array) => { - if (element.name == row_name) { - array.splice(index, 1); - return true; - } - return false; + const element_found = new_datasets.some((element, index, array) => { + if (element.name == row_name) { + array.splice(index, 1); + return true; } - ); + return false; + }); const slice_at = { Customer: 4, Item: 5 }[tree_type] || 3; if (!element_found) { new_datasets.push({ name: row_name, - values: data - .slice(slice_at, data.length - 1) - .map(column => column.content), + values: data.slice(slice_at, data.length - 1).map((column) => column.content), }); } @@ -106,7 +108,9 @@ frappe.query_reports["Sales Analytics"] = { labels: raw_data.labels, datasets: new_datasets, }; - const new_options = Object.assign({}, frappe.query_report.chart_options, {data: new_data}); + const new_options = Object.assign({}, frappe.query_report.chart_options, { + data: new_data, + }); frappe.query_report.render_chart(new_options); frappe.query_report.raw_chart_data = new_data; diff --git a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js index 91748bc7be2..25089c4b870 100644 --- a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js +++ b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js @@ -3,74 +3,74 @@ /* eslint-disable */ frappe.query_reports["Sales Order Analysis"] = { - "filters": [ + filters: [ { - "fieldname": "company", - "label": __("Company"), - "fieldtype": "Link", - "width": "80", - "options": "Company", - "reqd": 1, - "default": frappe.defaults.get_default("company") + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + width: "80", + options: "Company", + reqd: 1, + default: frappe.defaults.get_default("company"), }, { - "fieldname":"from_date", - "label": __("From Date"), - "fieldtype": "Date", - "width": "80", - "reqd": 1, - "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + width: "80", + reqd: 1, + default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), }, { - "fieldname":"to_date", - "label": __("To Date"), - "fieldtype": "Date", - "width": "80", - "reqd": 1, - "default": frappe.datetime.get_today() + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + width: "80", + reqd: 1, + default: frappe.datetime.get_today(), }, { - "fieldname": "sales_order", - "label": __("Sales Order"), - "fieldtype": "MultiSelectList", - "width": "80", - "options": "Sales Order", - "get_data": function(txt) { + fieldname: "sales_order", + label: __("Sales Order"), + fieldtype: "MultiSelectList", + width: "80", + options: "Sales Order", + get_data: function (txt) { return frappe.db.get_link_options("Sales Order", txt); }, - "get_query": () =>{ + get_query: () => { return { - filters: { "docstatus": 1 } - } - } + filters: { docstatus: 1 }, + }; + }, }, { - "fieldname": "status", - "label": __("Status"), - "fieldtype": "MultiSelectList", - "width": "80", - get_data: function(txt) { - let status = ["To Bill", "To Deliver", "To Deliver and Bill", "Completed"] - let options = [] - for (let option of status){ + fieldname: "status", + label: __("Status"), + fieldtype: "MultiSelectList", + width: "80", + get_data: function (txt) { + let status = ["To Bill", "To Deliver", "To Deliver and Bill", "Completed"]; + let options = []; + for (let option of status) { options.push({ - "value": option, - "label": __(option), - "description": "" - }) + value: option, + label: __(option), + description: "", + }); } - return options - } + return options; + }, }, { - "fieldname": "group_by_so", - "label": __("Group by Sales Order"), - "fieldtype": "Check", - "default": 0 - } + fieldname: "group_by_so", + label: __("Group by Sales Order"), + fieldtype: "Check", + default: 0, + }, ], - "formatter": function (value, row, column, data, default_formatter) { + formatter: function (value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); let format_fields = ["delivered_qty", "billed_amount"]; @@ -82,5 +82,5 @@ frappe.query_reports["Sales Order Analysis"] = { value = "" + value + ""; } return value; - } + }, }; 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 b22ea8fa2ca..fe38804ed45 100644 --- a/erpnext/selling/report/sales_order_trends/sales_order_trends.js +++ b/erpnext/selling/report/sales_order_trends/sales_order_trends.js @@ -1,8 +1,8 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.require("assets/erpnext/js/sales_trends_filters.js", function() { +frappe.require("assets/erpnext/js/sales_trends_filters.js", function () { frappe.query_reports["Sales Order Trends"] = { - filters: erpnext.get_sales_trends_filters() - } + filters: erpnext.get_sales_trends_filters(), + }; }); diff --git a/erpnext/selling/report/sales_partner_commission_summary/sales_partner_commission_summary.js b/erpnext/selling/report/sales_partner_commission_summary/sales_partner_commission_summary.js index 63d930c1452..2a67fa35ddf 100644 --- a/erpnext/selling/report/sales_partner_commission_summary/sales_partner_commission_summary.js +++ b/erpnext/selling/report/sales_partner_commission_summary/sales_partner_commission_summary.js @@ -3,20 +3,19 @@ /* eslint-disable */ frappe.query_reports["Sales Partner Commission Summary"] = { - "filters": [ - + filters: [ { fieldname: "sales_partner", label: __("Sales Partner"), fieldtype: "Link", - options: "Sales Partner" + options: "Sales Partner", }, { fieldname: "doctype", label: __("Document Type"), fieldtype: "Select", options: "Sales Order\nDelivery Note\nSales Invoice", - default: "Sales Order" + default: "Sales Order", }, { fieldname: "from_date", @@ -25,30 +24,29 @@ frappe.query_reports["Sales Partner Commission Summary"] = { default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), }, { - fieldname:"to_date", + fieldname: "to_date", label: __("To Date"), fieldtype: "Date", - default: frappe.datetime.get_today() + default: frappe.datetime.get_today(), }, { - fieldname:"company", + fieldname: "company", label: __("Company"), fieldtype: "Link", options: "Company", - default: frappe.defaults.get_user_default("Company") + default: frappe.defaults.get_user_default("Company"), }, { - fieldname:"customer", + fieldname: "customer", label: __("Customer"), fieldtype: "Link", options: "Customer", }, { - fieldname:"territory", + fieldname: "territory", label: __("Territory"), fieldtype: "Link", options: "Territory", }, - - ] -} + ], +}; diff --git a/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/sales_partner_target_variance_based_on_item_group.js b/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/sales_partner_target_variance_based_on_item_group.js index adae47b87da..f8c950763e1 100644 --- a/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/sales_partner_target_variance_based_on_item_group.js +++ b/erpnext/selling/report/sales_partner_target_variance_based_on_item_group/sales_partner_target_variance_based_on_item_group.js @@ -3,61 +3,59 @@ /* eslint-disable */ frappe.query_reports["Sales Partner Target Variance based on Item Group"] = { - "filters": [ + filters: [ { - fieldname:"company", + fieldname: "company", label: __("Company"), fieldtype: "Link", options: "Company", - default: frappe.defaults.get_user_default("Company") + default: frappe.defaults.get_user_default("Company"), }, { fieldname: "fiscal_year", label: __("Fiscal Year"), fieldtype: "Link", options: "Fiscal Year", - default: frappe.sys_defaults.fiscal_year + default: frappe.sys_defaults.fiscal_year, }, { fieldname: "doctype", label: __("Document Type"), fieldtype: "Select", options: "Sales Order\nDelivery Note\nSales Invoice", - default: "Sales Order" + default: "Sales Order", }, { fieldname: "period", label: __("Period"), fieldtype: "Select", options: [ - { "value": "Monthly", "label": __("Monthly") }, - { "value": "Quarterly", "label": __("Quarterly") }, - { "value": "Half-Yearly", "label": __("Half-Yearly") }, - { "value": "Yearly", "label": __("Yearly") } + { value: "Monthly", label: __("Monthly") }, + { value: "Quarterly", label: __("Quarterly") }, + { value: "Half-Yearly", label: __("Half-Yearly") }, + { value: "Yearly", label: __("Yearly") }, ], - default: "Monthly" + default: "Monthly", }, { fieldname: "target_on", label: __("Target On"), fieldtype: "Select", options: "Quantity\nAmount", - default: "Quantity" + default: "Quantity", }, ], - "formatter": function (value, row, column, data, default_formatter) { + formatter: function (value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); - if (column.fieldname.includes('variance')) { - + if (column.fieldname.includes("variance")) { if (data[column.fieldname] < 0) { value = "" + value + ""; - } - else if (data[column.fieldname] > 0) { + } else if (data[column.fieldname] > 0) { value = "" + value + ""; } } return value; - } -} + }, +}; diff --git a/erpnext/selling/report/sales_partner_transaction_summary/sales_partner_transaction_summary.js b/erpnext/selling/report/sales_partner_transaction_summary/sales_partner_transaction_summary.js index e404233953e..6bd8077ddd6 100644 --- a/erpnext/selling/report/sales_partner_transaction_summary/sales_partner_transaction_summary.js +++ b/erpnext/selling/report/sales_partner_transaction_summary/sales_partner_transaction_summary.js @@ -3,19 +3,19 @@ /* eslint-disable */ frappe.query_reports["Sales Partner Transaction Summary"] = { - "filters": [ + filters: [ { fieldname: "sales_partner", label: __("Sales Partner"), fieldtype: "Link", - options: "Sales Partner" + options: "Sales Partner", }, { fieldname: "doctype", label: __("Document Type"), fieldtype: "Select", options: "Sales Order\nDelivery Note\nSales Invoice", - default: "Sales Order" + default: "Sales Order", }, { fieldname: "from_date", @@ -24,48 +24,48 @@ frappe.query_reports["Sales Partner Transaction Summary"] = { default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), }, { - fieldname:"to_date", + fieldname: "to_date", label: __("To Date"), fieldtype: "Date", - default: frappe.datetime.get_today() + default: frappe.datetime.get_today(), }, { - fieldname:"company", + fieldname: "company", label: __("Company"), fieldtype: "Link", options: "Company", default: frappe.defaults.get_user_default("Company"), - reqd: 1 + reqd: 1, }, { - fieldname:"item_group", + fieldname: "item_group", label: __("Item Group"), fieldtype: "Link", options: "Item Group", }, { - fieldname:"brand", + fieldname: "brand", label: __("Brand"), fieldtype: "Link", options: "Brand", }, { - fieldname:"customer", + fieldname: "customer", label: __("Customer"), fieldtype: "Link", options: "Customer", }, { - fieldname:"territory", + fieldname: "territory", label: __("Territory"), fieldtype: "Link", options: "Territory", }, { - fieldname:"show_return_entries", + fieldname: "show_return_entries", label: __("Show Return Entries"), fieldtype: "Check", default: 0, }, - ] -} + ], +}; diff --git a/erpnext/selling/report/sales_person_commission_summary/sales_person_commission_summary.js b/erpnext/selling/report/sales_person_commission_summary/sales_person_commission_summary.js index ba6ee784b91..ebbf9cf2dd2 100644 --- a/erpnext/selling/report/sales_person_commission_summary/sales_person_commission_summary.js +++ b/erpnext/selling/report/sales_person_commission_summary/sales_person_commission_summary.js @@ -3,20 +3,19 @@ /* eslint-disable */ frappe.query_reports["Sales Person Commission Summary"] = { - "filters": [ - + filters: [ { fieldname: "sales_person", label: __("Sales Person"), fieldtype: "Link", - options: "Sales Person" + options: "Sales Person", }, { fieldname: "doc_type", label: __("Document Type"), fieldtype: "Select", options: "Sales Order\nDelivery Note\nSales Invoice", - default: "Sales Order" + default: "Sales Order", }, { fieldname: "from_date", @@ -25,30 +24,29 @@ frappe.query_reports["Sales Person Commission Summary"] = { default: frappe.defaults.get_user_default("year_start_date"), }, { - fieldname:"to_date", + fieldname: "to_date", label: __("To Date"), fieldtype: "Date", - default: frappe.datetime.get_today() + default: frappe.datetime.get_today(), }, { - fieldname:"company", + fieldname: "company", label: __("Company"), fieldtype: "Link", options: "Company", - default: frappe.defaults.get_user_default("Company") + default: frappe.defaults.get_user_default("Company"), }, { - fieldname:"customer", + fieldname: "customer", label: __("Customer"), fieldtype: "Link", options: "Customer", }, { - fieldname:"territory", + fieldname: "territory", label: __("Territory"), fieldtype: "Link", options: "Territory", }, - - ] -} + ], +}; diff --git a/erpnext/selling/report/sales_person_target_variance_based_on_item_group/sales_person_target_variance_based_on_item_group.js b/erpnext/selling/report/sales_person_target_variance_based_on_item_group/sales_person_target_variance_based_on_item_group.js index 2b8443627d5..b3497085fb7 100644 --- a/erpnext/selling/report/sales_person_target_variance_based_on_item_group/sales_person_target_variance_based_on_item_group.js +++ b/erpnext/selling/report/sales_person_target_variance_based_on_item_group/sales_person_target_variance_based_on_item_group.js @@ -3,61 +3,59 @@ /* eslint-disable */ frappe.query_reports["Sales Person Target Variance Based On Item Group"] = { - "filters": [ + filters: [ { - fieldname:"company", + fieldname: "company", label: __("Company"), fieldtype: "Link", options: "Company", - default: frappe.defaults.get_user_default("Company") + default: frappe.defaults.get_user_default("Company"), }, { fieldname: "fiscal_year", label: __("Fiscal Year"), fieldtype: "Link", options: "Fiscal Year", - default: frappe.sys_defaults.fiscal_year + default: frappe.sys_defaults.fiscal_year, }, { fieldname: "doctype", label: __("Document Type"), fieldtype: "Select", options: "Sales Order\nDelivery Note\nSales Invoice", - default: "Sales Order" + default: "Sales Order", }, { fieldname: "period", label: __("Period"), fieldtype: "Select", options: [ - { "value": "Monthly", "label": __("Monthly") }, - { "value": "Quarterly", "label": __("Quarterly") }, - { "value": "Half-Yearly", "label": __("Half-Yearly") }, - { "value": "Yearly", "label": __("Yearly") } + { value: "Monthly", label: __("Monthly") }, + { value: "Quarterly", label: __("Quarterly") }, + { value: "Half-Yearly", label: __("Half-Yearly") }, + { value: "Yearly", label: __("Yearly") }, ], - default: "Monthly" + default: "Monthly", }, { fieldname: "target_on", label: __("Target On"), fieldtype: "Select", options: "Quantity\nAmount", - default: "Quantity" + default: "Quantity", }, ], - "formatter": function (value, row, column, data, default_formatter) { + formatter: function (value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); - if (column.fieldname.includes('variance')) { - + if (column.fieldname.includes("variance")) { if (data[column.fieldname] < 0) { value = "" + value + ""; - } - else if (data[column.fieldname] > 0) { + } else if (data[column.fieldname] > 0) { value = "" + value + ""; } } return value; - } -} + }, +}; diff --git a/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.js b/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.js index e269f02d0ce..a02d0711128 100644 --- a/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.js +++ b/erpnext/selling/report/sales_person_wise_transaction_summary/sales_person_wise_transaction_summary.js @@ -2,19 +2,19 @@ // License: GNU General Public License v3. See license.txt frappe.query_reports["Sales Person-wise Transaction Summary"] = { - "filters": [ + filters: [ { fieldname: "sales_person", label: __("Sales Person"), fieldtype: "Link", - options: "Sales Person" + options: "Sales Person", }, { fieldname: "doc_type", label: __("Document Type"), fieldtype: "Select", options: "Sales Order\nDelivery Note\nSales Invoice", - default: "Sales Order" + default: "Sales Order", }, { fieldname: "from_date", @@ -23,48 +23,48 @@ frappe.query_reports["Sales Person-wise Transaction Summary"] = { default: frappe.defaults.get_user_default("year_start_date"), }, { - fieldname:"to_date", + fieldname: "to_date", label: __("To Date"), fieldtype: "Date", - default: frappe.datetime.get_today() + default: frappe.datetime.get_today(), }, { - fieldname:"company", + fieldname: "company", label: __("Company"), fieldtype: "Link", options: "Company", default: frappe.defaults.get_user_default("Company"), - reqd: 1 + reqd: 1, }, { - fieldname:"item_group", + fieldname: "item_group", label: __("Item Group"), fieldtype: "Link", options: "Item Group", }, { - fieldname:"brand", + fieldname: "brand", label: __("Brand"), fieldtype: "Link", options: "Brand", }, { - fieldname:"customer", + fieldname: "customer", label: __("Customer"), fieldtype: "Link", options: "Customer", }, { - fieldname:"territory", + fieldname: "territory", label: __("Territory"), fieldtype: "Link", options: "Territory", }, { - fieldname:"show_return_entries", + fieldname: "show_return_entries", label: __("Show Return Entries"), fieldtype: "Check", default: 0, }, - ] -} + ], +}; diff --git a/erpnext/selling/report/territory_target_variance_based_on_item_group/territory_target_variance_based_on_item_group.js b/erpnext/selling/report/territory_target_variance_based_on_item_group/territory_target_variance_based_on_item_group.js index 9f3d255e662..988fb86ff6c 100644 --- a/erpnext/selling/report/territory_target_variance_based_on_item_group/territory_target_variance_based_on_item_group.js +++ b/erpnext/selling/report/territory_target_variance_based_on_item_group/territory_target_variance_based_on_item_group.js @@ -3,61 +3,59 @@ /* eslint-disable */ frappe.query_reports["Territory Target Variance Based On Item Group"] = { - "filters": [ + filters: [ { - fieldname:"company", + fieldname: "company", label: __("Company"), fieldtype: "Link", options: "Company", - default: frappe.defaults.get_user_default("Company") + default: frappe.defaults.get_user_default("Company"), }, { fieldname: "fiscal_year", label: __("Fiscal Year"), fieldtype: "Link", options: "Fiscal Year", - default: frappe.sys_defaults.fiscal_year + default: frappe.sys_defaults.fiscal_year, }, { fieldname: "doctype", label: __("Document Type"), fieldtype: "Select", options: "Sales Order\nDelivery Note\nSales Invoice", - default: "Sales Order" + default: "Sales Order", }, { fieldname: "period", label: __("Period"), fieldtype: "Select", options: [ - { "value": "Monthly", "label": __("Monthly") }, - { "value": "Quarterly", "label": __("Quarterly") }, - { "value": "Half-Yearly", "label": __("Half-Yearly") }, - { "value": "Yearly", "label": __("Yearly") } + { value: "Monthly", label: __("Monthly") }, + { value: "Quarterly", label: __("Quarterly") }, + { value: "Half-Yearly", label: __("Half-Yearly") }, + { value: "Yearly", label: __("Yearly") }, ], - default: "Monthly" + default: "Monthly", }, { fieldname: "target_on", label: __("Target On"), fieldtype: "Select", options: "Quantity\nAmount", - default: "Quantity" + default: "Quantity", }, ], - "formatter": function (value, row, column, data, default_formatter) { + formatter: function (value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); - if (column.fieldname.includes('variance')) { - + if (column.fieldname.includes("variance")) { if (data[column.fieldname] < 0) { value = "" + value + ""; - } - else if (data[column.fieldname] > 0) { + } else if (data[column.fieldname] > 0) { value = "" + value + ""; } } return value; - } -} + }, +}; diff --git a/erpnext/selling/report/territory_wise_sales/territory_wise_sales.js b/erpnext/selling/report/territory_wise_sales/territory_wise_sales.js index bef800f1040..c13c7408428 100644 --- a/erpnext/selling/report/territory_wise_sales/territory_wise_sales.js +++ b/erpnext/selling/report/territory_wise_sales/territory_wise_sales.js @@ -2,21 +2,23 @@ // For license information, please see license.txt /* eslint-disable */ - frappe.query_reports["Territory-wise Sales"] = { - "breadcrumb":"Selling", - "filters": [ + breadcrumb: "Selling", + filters: [ { - fieldname:"transaction_date", + fieldname: "transaction_date", label: __("Transaction Date"), fieldtype: "DateRange", - default: [frappe.datetime.add_months(frappe.datetime.get_today(),-1), frappe.datetime.get_today()], + default: [ + frappe.datetime.add_months(frappe.datetime.get_today(), -1), + frappe.datetime.get_today(), + ], }, { fieldname: "company", label: __("Company"), fieldtype: "Link", options: "Company", - } - ] + }, + ], }; diff --git a/erpnext/setup/doctype/authorization_rule/authorization_rule.js b/erpnext/setup/doctype/authorization_rule/authorization_rule.js index 3f6afcae7f5..a98d6965d8f 100644 --- a/erpnext/setup/doctype/authorization_rule/authorization_rule.js +++ b/erpnext/setup/doctype/authorization_rule/authorization_rule.js @@ -2,14 +2,14 @@ // License: GNU General Public License v3. See license.txt frappe.ui.form.on("Authorization Rule", { - refresh: function(frm) { + refresh: function (frm) { frm.events.set_master_type(frm); }, - set_master_type: function(frm) { - if(frm.doc.based_on==="Customerwise Discount") { + set_master_type: function (frm) { + if (frm.doc.based_on === "Customerwise Discount") { unhide_field("master_name"); frm.set_value("customer_or_item", "Customer"); - } else if(frm.doc.based_on==="Itemwise Discount") { + } else if (frm.doc.based_on === "Itemwise Discount") { unhide_field("master_name"); frm.set_value("customer_or_item", "Item"); } else { @@ -18,77 +18,67 @@ frappe.ui.form.on("Authorization Rule", { hide_field("master_name"); } }, - based_on: function(frm) { + based_on: function (frm) { frm.events.set_master_type(frm); - if (frm.doc.based_on === 'Not Applicable') { + if (frm.doc.based_on === "Not Applicable") { frm.set_value("value", 0); - hide_field('value'); + hide_field("value"); } else { - unhide_field('value'); + unhide_field("value"); } }, - transaction: function(frm) { - unhide_field(['system_role', 'system_user', 'value', 'based_on']); - hide_field(['to_emp', 'to_designation']); - } -}) - + transaction: function (frm) { + unhide_field(["system_role", "system_user", "value", "based_on"]); + hide_field(["to_emp", "to_designation"]); + }, +}); // Settings Module -cur_frm.cscript.refresh = function(doc, cdt, cdn) { - if (doc.based_on == 'Not Applicable') - hide_field('value'); - else - unhide_field('value'); +cur_frm.cscript.refresh = function (doc, cdt, cdn) { + if (doc.based_on == "Not Applicable") hide_field("value"); + else unhide_field("value"); - unhide_field(['system_role', 'system_user', 'value']); - hide_field(['to_emp', 'to_designation']); -} + unhide_field(["system_role", "system_user", "value"]); + hide_field(["to_emp", "to_designation"]); +}; -cur_frm.fields_dict.system_user.get_query = function(doc, cdt, cdn) { - return { query:"frappe.core.doctype.user.user.user_query" } -} +cur_frm.fields_dict.system_user.get_query = function (doc, cdt, cdn) { + return { query: "frappe.core.doctype.user.user.user_query" }; +}; -cur_frm.fields_dict.approving_user.get_query = function(doc, cdt, cdn) { - return { query:"frappe.core.doctype.user.user.user_query" } -} +cur_frm.fields_dict.approving_user.get_query = function (doc, cdt, cdn) { + return { query: "frappe.core.doctype.user.user.user_query" }; +}; -cur_frm.fields_dict['approving_role'].get_query = cur_frm.fields_dict['system_role'].get_query; +cur_frm.fields_dict["approving_role"].get_query = cur_frm.fields_dict["system_role"].get_query; // System Role Trigger // ----------------------- -cur_frm.fields_dict['system_role'].get_query = function(doc) { +cur_frm.fields_dict["system_role"].get_query = function (doc) { return { - filters:[ - ['Role', 'name', 'not in', 'Administrator, Guest, All'] - ] - } -} - + filters: [["Role", "name", "not in", "Administrator, Guest, All"]], + }; +}; // Master Name Trigger // -------------------- -cur_frm.fields_dict['master_name'].get_query = function(doc) { - if (doc.based_on == 'Customerwise Discount') +cur_frm.fields_dict["master_name"].get_query = function (doc) { + if (doc.based_on == "Customerwise Discount") return { doctype: "Customer", - filters:[ - ['Customer', 'docstatus', '!=', 2] - ] - } - else if (doc.based_on == 'Itemwise Discount') + filters: [["Customer", "docstatus", "!=", 2]], + }; + else if (doc.based_on == "Itemwise Discount") return { doctype: "Item", - query: "erpnext.controllers.queries.item_query" - } + query: "erpnext.controllers.queries.item_query", + }; else return { - filters: [ - ['Item', 'name', '=', 'cheating done to avoid null'] - ] - } -} + filters: [["Item", "name", "=", "cheating done to avoid null"]], + }; +}; -cur_frm.fields_dict.to_emp.get_query = function(doc, cdt, cdn) { - return { query: "erpnext.controllers.queries.employee_query" } -} +cur_frm.fields_dict.to_emp.get_query = function (doc, cdt, cdn) { + return { query: "erpnext.controllers.queries.employee_query" }; +}; diff --git a/erpnext/setup/doctype/branch/branch.js b/erpnext/setup/doctype/branch/branch.js index c8a0afced8d..6e17c781931 100644 --- a/erpnext/setup/doctype/branch/branch.js +++ b/erpnext/setup/doctype/branch/branch.js @@ -1,8 +1,6 @@ // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Branch', { - refresh: function(frm) { - - } +frappe.ui.form.on("Branch", { + refresh: function (frm) {}, }); diff --git a/erpnext/setup/doctype/brand/brand.js b/erpnext/setup/doctype/brand/brand.js index 0abb71a3629..5d16d734e0f 100644 --- a/erpnext/setup/doctype/brand/brand.js +++ b/erpnext/setup/doctype/brand/brand.js @@ -1,71 +1,99 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.ui.form.on('Brand', { +frappe.ui.form.on("Brand", { setup: (frm) => { - frm.fields_dict["brand_defaults"].grid.get_field("default_warehouse").get_query = function(doc, cdt, cdn) { + frm.fields_dict["brand_defaults"].grid.get_field("default_warehouse").get_query = 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) { - const row = locals[cdt][cdn]; - return { - filters: { - 'report_type': 'Profit and Loss', - 'company': row.company, - "is_group": 0 - } + filters: { company: row.company }, }; - } + }; - frm.fields_dict["brand_defaults"].grid.get_field("buying_cost_center").get_query = function(doc, cdt, cdn) { + frm.fields_dict["brand_defaults"].grid.get_field("default_discount_account").get_query = function ( + doc, + cdt, + cdn + ) { const row = locals[cdt][cdn]; return { filters: { - "is_group": 0, - "company": row.company - } - } - } + report_type: "Profit and Loss", + company: row.company, + is_group: 0, + }, + }; + }; - frm.fields_dict["brand_defaults"].grid.get_field("expense_account").get_query = function(doc, cdt, cdn) { + frm.fields_dict["brand_defaults"].grid.get_field("buying_cost_center").get_query = function ( + doc, + cdt, + cdn + ) { + const row = locals[cdt][cdn]; + return { + filters: { + is_group: 0, + company: row.company, + }, + }; + }; + + frm.fields_dict["brand_defaults"].grid.get_field("expense_account").get_query = 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) { - const row = locals[cdt][cdn]; - return { - filters: { - "company": row.company, - "root_type": ["in", ["Liability", "Asset"]], - "is_group": 0 - } + filters: { company: row.company }, }; - } + }; - frm.fields_dict["brand_defaults"].grid.get_field("selling_cost_center").get_query = function(doc, cdt, cdn) { + frm.fields_dict["brand_defaults"].grid.get_field("default_provisional_account").get_query = function ( + doc, + cdt, + cdn + ) { const row = locals[cdt][cdn]; return { filters: { - "is_group": 0, - "company": row.company - } - } - } + company: row.company, + root_type: ["in", ["Liability", "Asset"]], + is_group: 0, + }, + }; + }; - frm.fields_dict["brand_defaults"].grid.get_field("income_account").get_query = function(doc, cdt, cdn) { + frm.fields_dict["brand_defaults"].grid.get_field("selling_cost_center").get_query = function ( + doc, + cdt, + cdn + ) { + const row = locals[cdt][cdn]; + return { + filters: { + is_group: 0, + company: row.company, + }, + }; + }; + + frm.fields_dict["brand_defaults"].grid.get_field("income_account").get_query = function ( + doc, + cdt, + cdn + ) { const row = locals[cdt][cdn]; return { query: "erpnext.controllers.queries.get_income_account", - filters: { company: row.company } - } - } - } -}); \ No newline at end of file + filters: { company: row.company }, + }; + }; + }, +}); diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 589b3fe8b98..23a55487adc 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -4,121 +4,148 @@ frappe.provide("erpnext.company"); frappe.ui.form.on("Company", { - onload: function(frm) { + onload: function (frm) { if (frm.doc.__islocal && frm.doc.parent_company) { - frappe.db.get_value('Company', frm.doc.parent_company, 'is_group', (r) => { + frappe.db.get_value("Company", frm.doc.parent_company, "is_group", (r) => { if (!r.is_group) { - frm.set_value('parent_company', ''); + frm.set_value("parent_company", ""); } }); } - frm.call('check_if_transactions_exist').then(r => { - frm.toggle_enable("default_currency", (!r.message)); + frm.call("check_if_transactions_exist").then((r) => { + frm.toggle_enable("default_currency", !r.message); }); }, - setup: function(frm) { + setup: function (frm) { frm.__rename_queue = "long"; erpnext.company.setup_queries(frm); - frm.set_query("parent_company", function() { + frm.set_query("parent_company", function () { return { - filters: {"is_group": 1} - } + filters: { is_group: 1 }, + }; }); - frm.set_query("default_selling_terms", function() { + frm.set_query("default_selling_terms", function () { return { filters: { selling: 1 } }; }); - frm.set_query("default_buying_terms", function() { + frm.set_query("default_buying_terms", function () { return { filters: { buying: 1 } }; }); - frm.set_query("default_in_transit_warehouse", function() { + frm.set_query("default_in_transit_warehouse", function () { return { - filters:{ - 'warehouse_type' : 'Transit', - 'is_group': 0, - 'company': frm.doc.company_name - } + filters: { + warehouse_type: "Transit", + is_group: 0, + company: frm.doc.company_name, + }, }; }); }, - company_name: function(frm) { - if(frm.doc.__islocal) { + company_name: function (frm) { + if (frm.doc.__islocal) { // add missing " " arg in split method let parts = frm.doc.company_name.split(" "); let abbr = $.map(parts, function (p) { - return p? p.substr(0, 1) : null; + return p ? p.substr(0, 1) : null; }).join(""); frm.set_value("abbr", abbr); } }, - parent_company: function(frm) { + parent_company: function (frm) { var bool = frm.doc.parent_company ? true : false; - frm.set_value('create_chart_of_accounts_based_on', bool ? "Existing Company" : ""); - frm.set_value('existing_company', bool ? frm.doc.parent_company : ""); + frm.set_value("create_chart_of_accounts_based_on", bool ? "Existing Company" : ""); + frm.set_value("existing_company", bool ? frm.doc.parent_company : ""); disbale_coa_fields(frm, bool); }, - date_of_commencement: function(frm) { - if(frm.doc.date_of_commencement { + .then((r) => { frappe.model.set_value(cdt, cdn, "address", r.name); }) - .catch(err => { + .catch((err) => { console.log(err); }); - } + }, }); diff --git a/erpnext/setup/doctype/email_digest/email_digest.js b/erpnext/setup/doctype/email_digest/email_digest.js index c2c2710b025..c0a887e4b11 100644 --- a/erpnext/setup/doctype/email_digest/email_digest.js +++ b/erpnext/setup/doctype/email_digest/email_digest.js @@ -2,30 +2,30 @@ // License: GNU General Public License v3. See license.txt frappe.ui.form.on("Email Digest", { - refresh: function(frm) { + refresh: function (frm) { if (!frm.is_new()) { - frm.add_custom_button(__('View Now'), function() { + frm.add_custom_button(__("View Now"), function () { frappe.call({ - method: 'erpnext.setup.doctype.email_digest.email_digest.get_digest_msg', + method: "erpnext.setup.doctype.email_digest.email_digest.get_digest_msg", args: { - name: frm.doc.name + name: frm.doc.name, }, - callback: function(r) { + callback: function (r) { let d = new frappe.ui.Dialog({ - title: __('Email Digest: {0}', [frm.doc.name]), - width: 800 + title: __("Email Digest: {0}", [frm.doc.name]), + width: 800, }); $(d.body).html(r.message); d.show(); - } + }, }); }); - frm.add_custom_button(__('Send Now'), function() { - return frm.call('send', null, () => { - frappe.show_alert({ message: __("Message Sent"), indicator: 'green'}); + frm.add_custom_button(__("Send Now"), function () { + return frm.call("send", null, () => { + frappe.show_alert({ message: __("Message Sent"), indicator: "green" }); }); }); } - } + }, }); diff --git a/erpnext/setup/doctype/employee/employee.js b/erpnext/setup/doctype/employee/employee.js index efc3fd1d33d..d165d429f44 100755 --- a/erpnext/setup/doctype/employee/employee.js +++ b/erpnext/setup/doctype/employee/employee.js @@ -4,14 +4,15 @@ frappe.provide("erpnext.setup"); erpnext.setup.EmployeeController = class EmployeeController extends frappe.ui.form.Controller { setup() { - this.frm.fields_dict.user_id.get_query = function(doc, cdt, cdn) { + this.frm.fields_dict.user_id.get_query = function (doc, cdt, cdn) { return { query: "frappe.core.doctype.user.user.user_query", - filters: {ignore_user_type: 1} - } - } - this.frm.fields_dict.reports_to.get_query = function(doc, cdt, cdn) { - return { query: "erpnext.controllers.queries.employee_query"} } + filters: { ignore_user_type: 1 }, + }; + }; + this.frm.fields_dict.reports_to.get_query = function (doc, cdt, cdn) { + return { query: "erpnext.controllers.queries.employee_query" }; + }; } refresh() { @@ -20,58 +21,59 @@ erpnext.setup.EmployeeController = class EmployeeController extends frappe.ui.fo salutation() { if (this.frm.doc.salutation) { - this.frm.set_value("gender", { - "Mr": "Male", - "Ms": "Female" - }[this.frm.doc.salutation]); + this.frm.set_value( + "gender", + { + Mr: "Male", + Ms: "Female", + }[this.frm.doc.salutation] + ); } } - }; frappe.ui.form.on("Employee", { onload: function (frm) { - frm.set_query("department", function() { + frm.set_query("department", function () { return { - "filters": { - "company": frm.doc.company, - } + filters: { + company: frm.doc.company, + }, }; }); }, - prefered_contact_email: function(frm) { + prefered_contact_email: function (frm) { frm.events.update_contact(frm); }, - personal_email: function(frm) { + personal_email: function (frm) { frm.events.update_contact(frm); }, - company_email: function(frm) { + company_email: function (frm) { frm.events.update_contact(frm); }, - user_id: function(frm) { + user_id: function (frm) { frm.events.update_contact(frm); }, - update_contact: function(frm) { - var prefered_email_fieldname = frappe.model.scrub(frm.doc.prefered_contact_email) || 'user_id'; - frm.set_value("prefered_email", - frm.fields_dict[prefered_email_fieldname].value); + update_contact: function (frm) { + var prefered_email_fieldname = frappe.model.scrub(frm.doc.prefered_contact_email) || "user_id"; + frm.set_value("prefered_email", frm.fields_dict[prefered_email_fieldname].value); }, - status: function(frm) { + status: function (frm) { return frm.call({ method: "deactivate_sales_person", args: { employee: frm.doc.employee, - status: frm.doc.status - } + status: frm.doc.status, + }, }); }, - create_user: function(frm) { + create_user: function (frm) { if (!frm.doc.prefered_email) { frappe.throw(__("Please enter Preferred Contact Email")); } @@ -79,46 +81,53 @@ frappe.ui.form.on("Employee", { method: "erpnext.setup.doctype.employee.employee.create_user", args: { employee: frm.doc.name, - email: frm.doc.prefered_email + email: frm.doc.prefered_email, }, freeze: true, freeze_message: __("Creating User..."), callback: function (r) { frm.reload_doc(); - } + }, }); - } + }, }); cur_frm.cscript = new erpnext.setup.EmployeeController({ - frm: cur_frm + frm: cur_frm, }); - -frappe.tour['Employee'] = [ +frappe.tour["Employee"] = [ { fieldname: "first_name", title: "First Name", - description: __("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.") + description: __( + "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." + ), }, { fieldname: "company", title: "Company", - description: __("Select a Company this Employee belongs to.") + description: __("Select a Company this Employee belongs to."), }, { fieldname: "date_of_birth", title: "Date of Birth", - description: __("Select Date of Birth. This will validate Employees age and prevent hiring of under-age staff.") + description: __( + "Select Date of Birth. This will validate Employees age and prevent hiring of under-age staff." + ), }, { fieldname: "date_of_joining", title: "Date of Joining", - description: __("Select Date of joining. It will have impact on the first salary calculation, Leave allocation on pro-rata bases.") + description: __( + "Select Date of joining. It will have impact on the first salary calculation, Leave allocation on pro-rata bases." + ), }, { fieldname: "reports_to", title: "Reports To", - description: __("Here, you can select a senior of this Employee. Based on this, Organization Chart will be populated.") + description: __( + "Here, you can select a senior of this Employee. Based on this, Organization Chart will be populated." + ), }, ]; diff --git a/erpnext/setup/doctype/employee/employee_list.js b/erpnext/setup/doctype/employee/employee_list.js index d37e1496ca3..0c97b626954 100644 --- a/erpnext/setup/doctype/employee/employee_list.js +++ b/erpnext/setup/doctype/employee/employee_list.js @@ -1,9 +1,9 @@ -frappe.listview_settings['Employee'] = { - add_fields: ["status", "branch", "department", "designation","image"], - filters: [["status","=", "Active"]], - get_indicator: function(doc) { +frappe.listview_settings["Employee"] = { + add_fields: ["status", "branch", "department", "designation", "image"], + filters: [["status", "=", "Active"]], + get_indicator: function (doc) { var indicator = [__(doc.status), frappe.utils.guess_colour(doc.status), "status,=," + doc.status]; - indicator[1] = {"Active": "green", "Inactive": "red", "Left": "gray", "Suspended": "orange"}[doc.status]; + indicator[1] = { Active: "green", Inactive: "red", Left: "gray", Suspended: "orange" }[doc.status]; return indicator; - } + }, }; diff --git a/erpnext/setup/doctype/employee/employee_tree.js b/erpnext/setup/doctype/employee/employee_tree.js index 8f52cff8c2c..26a89719d7f 100644 --- a/erpnext/setup/doctype/employee/employee_tree.js +++ b/erpnext/setup/doctype/employee/employee_tree.js @@ -1,13 +1,13 @@ -frappe.treeview_settings['Employee'] = { +frappe.treeview_settings["Employee"] = { get_tree_nodes: "erpnext.setup.doctype.employee.employee.get_children", filters: [ { fieldname: "company", - fieldtype:"Select", - options: ['All Companies'].concat(erpnext.utils.get_tree_options("company")), + fieldtype: "Select", + options: ["All Companies"].concat(erpnext.utils.get_tree_options("company")), label: __("Company"), - default: erpnext.utils.get_tree_default("company") - } + default: erpnext.utils.get_tree_default("company"), + }, ], breadcrumb: "Hr", disable_add_node: true, @@ -15,22 +15,22 @@ frappe.treeview_settings['Employee'] = { toolbar: [ { toggle_btn: true }, { - label:__("Edit"), - condition: function(node) { + label: __("Edit"), + condition: function (node) { return !node.is_root; }, - click: function(node) { + click: function (node) { frappe.set_route("Form", "Employee", node.data.value); - } - } + }, + }, ], menu_items: [ { label: __("New Employee"), - action: function() { + action: function () { frappe.new_doc("Employee", true); }, - condition: 'frappe.boot.user.can_create.indexOf("Employee") !== -1' - } + condition: 'frappe.boot.user.can_create.indexOf("Employee") !== -1', + }, ], }; diff --git a/erpnext/setup/doctype/employee_group/employee_group.js b/erpnext/setup/doctype/employee_group/employee_group.js index 3db57181f8a..0ffa877ee7e 100644 --- a/erpnext/setup/doctype/employee_group/employee_group.js +++ b/erpnext/setup/doctype/employee_group/employee_group.js @@ -1,6 +1,4 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Employee Group', { - -}); +frappe.ui.form.on("Employee Group", {}); diff --git a/erpnext/setup/doctype/global_defaults/global_defaults.js b/erpnext/setup/doctype/global_defaults/global_defaults.js index 942dd5989ea..20ac6dafde5 100644 --- a/erpnext/setup/doctype/global_defaults/global_defaults.js +++ b/erpnext/setup/doctype/global_defaults/global_defaults.js @@ -1,14 +1,14 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.ui.form.on('Global Defaults', { - onload: function(frm) { - frm.trigger('get_distance_uoms'); +frappe.ui.form.on("Global Defaults", { + onload: function (frm) { + frm.trigger("get_distance_uoms"); }, - validate: function(frm) { - frm.call('get_defaults', null, r => { + validate: function (frm) { + frm.call("get_defaults", null, (r) => { frappe.sys_defaults = r.message; - }) + }); }, get_distance_uoms: function (frm) { let units = []; @@ -17,16 +17,16 @@ frappe.ui.form.on('Global Defaults', { method: "frappe.client.get_list", args: { doctype: "UOM Conversion Factor", - filters: { "category": __("Length") }, + filters: { category: __("Length") }, fields: ["to_uom"], - limit_page_length: 500 + limit_page_length: 500, }, callback: function (r) { - r.message.forEach(row => units.push(row.to_uom)); - } + r.message.forEach((row) => units.push(row.to_uom)); + }, }); frm.set_query("default_distance_unit", function () { - return { filters: { "name": ["IN", units] } }; + return { filters: { name: ["IN", units] } }; }); - } + }, }); diff --git a/erpnext/setup/doctype/holiday_list/holiday_list.js b/erpnext/setup/doctype/holiday_list/holiday_list.js index 90d9f1b6f50..1f95a73c242 100644 --- a/erpnext/setup/doctype/holiday_list/holiday_list.js +++ b/erpnext/setup/doctype/holiday_list/holiday_list.js @@ -2,12 +2,12 @@ // For license information, please see license.txt frappe.ui.form.on("Holiday List", { - refresh: function(frm) { + refresh: function (frm) { if (frm.doc.holidays) { frm.set_value("total_holidays", frm.doc.holidays.length); } - frm.call("get_supported_countries").then(r => { + frm.call("get_supported_countries").then((r) => { frm.subdivisions_by_country = r.message.subdivisions_by_country; frm.fields_dict.country.set_data( r.message.countries.sort((a, b) => a.label.localeCompare(b.label)) @@ -18,20 +18,20 @@ frappe.ui.form.on("Holiday List", { } }); }, - from_date: function(frm) { + from_date: function (frm) { if (frm.doc.from_date && !frm.doc.to_date) { var a_year_from_start = frappe.datetime.add_months(frm.doc.from_date, 12); frm.set_value("to_date", frappe.datetime.add_days(a_year_from_start, -1)); } }, - country: function(frm) { + country: function (frm) { frm.set_value("subdivision", ""); if (frm.doc.country) { frm.trigger("set_subdivisions"); } }, - set_subdivisions: function(frm) { + set_subdivisions: function (frm) { const subdivisions = [...frm.subdivisions_by_country[frm.doc.country]]; if (subdivisions && subdivisions.length > 0) { frm.fields_dict.subdivision.set_data(subdivisions); @@ -67,11 +67,15 @@ frappe.tour["Holiday List"] = [ { fieldname: "get_weekly_off_dates", title: "Add Holidays", - description: __("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"), + description: __( + "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" + ), }, { fieldname: "holidays", title: "Holidays", - description: __("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.") + description: __( + "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." + ), }, ]; diff --git a/erpnext/setup/doctype/holiday_list/holiday_list_calendar.js b/erpnext/setup/doctype/holiday_list/holiday_list_calendar.js index bb6d8315467..ff830881c92 100644 --- a/erpnext/setup/doctype/holiday_list/holiday_list_calendar.js +++ b/erpnext/setup/doctype/holiday_list/holiday_list_calendar.js @@ -3,20 +3,20 @@ frappe.views.calendar["Holiday List"] = { field_map: { - "start": "holiday_date", - "end": "holiday_date", - "id": "name", - "title": "description", - "allDay": "allDay" + start: "holiday_date", + end: "holiday_date", + id: "name", + title: "description", + allDay: "allDay", }, order_by: `from_date`, get_events_method: "erpnext.setup.doctype.holiday_list.holiday_list.get_events", filters: [ { - 'fieldtype': 'Link', - 'fieldname': 'holiday_list', - 'options': 'Holiday List', - 'label': __('Holiday List') - } - ] -} + fieldtype: "Link", + fieldname: "holiday_list", + options: "Holiday List", + label: __("Holiday List"), + }, + ], +}; diff --git a/erpnext/setup/doctype/item_group/item_group.js b/erpnext/setup/doctype/item_group/item_group.js index 4b04ac1d5ef..ec5ba2a77e0 100644 --- a/erpnext/setup/doctype/item_group/item_group.js +++ b/erpnext/setup/doctype/item_group/item_group.js @@ -2,99 +2,116 @@ // License: GNU General Public License v3. See license.txt frappe.ui.form.on("Item Group", { - onload: function(frm) { + onload: function (frm) { frm.list_route = "Tree/Item Group"; //get query select item group - frm.fields_dict['parent_item_group'].get_query = function(doc,cdt,cdn) { - return{ - filters:[ - ['Item Group', 'is_group', '=', 1], - ['Item Group', 'name', '!=', doc.item_group_name] - ] - } - } - frm.fields_dict['item_group_defaults'].grid.get_field("default_discount_account").get_query = function(doc, cdt, cdn) { - const row = locals[cdt][cdn]; + frm.fields_dict["parent_item_group"].get_query = function (doc, cdt, cdn) { return { - filters: { - 'report_type': 'Profit and Loss', - 'company': row.company, - "is_group": 0 - } + filters: [ + ["Item Group", "is_group", "=", 1], + ["Item Group", "name", "!=", doc.item_group_name], + ], }; - } - frm.fields_dict["item_group_defaults"].grid.get_field("expense_account").get_query = function(doc, cdt, cdn) { + }; + frm.fields_dict["item_group_defaults"].grid.get_field("default_discount_account").get_query = + function (doc, cdt, cdn) { + const row = locals[cdt][cdn]; + return { + filters: { + report_type: "Profit and Loss", + company: row.company, + is_group: 0, + }, + }; + }; + frm.fields_dict["item_group_defaults"].grid.get_field("expense_account").get_query = function ( + doc, + cdt, + cdn + ) { const row = locals[cdt][cdn]; return { query: "erpnext.controllers.queries.get_expense_account", - filters: { company: row.company } - } - } - frm.fields_dict["item_group_defaults"].grid.get_field("income_account").get_query = function(doc, cdt, cdn) { + filters: { company: row.company }, + }; + }; + frm.fields_dict["item_group_defaults"].grid.get_field("income_account").get_query = function ( + doc, + cdt, + cdn + ) { const row = locals[cdt][cdn]; return { query: "erpnext.controllers.queries.get_income_account", - filters: { company: row.company } - } - } + filters: { company: row.company }, + }; + }; - frm.fields_dict["item_group_defaults"].grid.get_field("buying_cost_center").get_query = function(doc, cdt, cdn) { + frm.fields_dict["item_group_defaults"].grid.get_field("buying_cost_center").get_query = function ( + doc, + cdt, + cdn + ) { const row = locals[cdt][cdn]; return { filters: { - "is_group": 0, - "company": row.company - } - } - } + is_group: 0, + company: row.company, + }, + }; + }; - frm.fields_dict["item_group_defaults"].grid.get_field("selling_cost_center").get_query = function(doc, cdt, cdn) { + frm.fields_dict["item_group_defaults"].grid.get_field("selling_cost_center").get_query = function ( + doc, + cdt, + cdn + ) { const row = locals[cdt][cdn]; return { filters: { - "is_group": 0, - "company": row.company - } - } - } + is_group: 0, + company: row.company, + }, + }; + }; }, - refresh: function(frm) { + refresh: function (frm) { frm.trigger("set_root_readonly"); - frm.add_custom_button(__("Item Group Tree"), function() { + frm.add_custom_button(__("Item Group Tree"), function () { frappe.set_route("Tree", "Item Group"); }); - if(!frm.is_new()) { - frm.add_custom_button(__("Items"), function() { - frappe.set_route("List", "Item", {"item_group": frm.doc.name}); + if (!frm.is_new()) { + frm.add_custom_button(__("Items"), function () { + frappe.set_route("List", "Item", { item_group: frm.doc.name }); }); } - frappe.model.with_doctype('Website Item', () => { - const web_item_meta = frappe.get_meta('Website Item'); + frappe.model.with_doctype("Website Item", () => { + const web_item_meta = frappe.get_meta("Website Item"); - const valid_fields = web_item_meta.fields.filter(df => - ['Link', 'Table MultiSelect'].includes(df.fieldtype) && !df.hidden - ).map(df => - ({ label: df.label, value: df.fieldname }) - ); + const valid_fields = web_item_meta.fields + .filter((df) => ["Link", "Table MultiSelect"].includes(df.fieldtype) && !df.hidden) + .map((df) => ({ label: df.label, value: df.fieldname })); frm.get_field("filter_fields").grid.update_docfield_property( - 'fieldname', 'options', valid_fields + "fieldname", + "options", + valid_fields ); }); }, - set_root_readonly: function(frm) { + set_root_readonly: function (frm) { // read-only for root item group frm.set_intro(""); - if(!frm.doc.parent_item_group && !frm.doc.__islocal) { + if (!frm.doc.parent_item_group && !frm.doc.__islocal) { frm.set_read_only(); frm.set_intro(__("This is a root item group and cannot be edited."), true); } }, - page_name: frappe.utils.warn_page_name_change + page_name: frappe.utils.warn_page_name_change, }); diff --git a/erpnext/setup/doctype/item_group/item_group_tree.js b/erpnext/setup/doctype/item_group/item_group_tree.js index b2628f4f4f8..a7c2b6360cf 100644 --- a/erpnext/setup/doctype/item_group/item_group_tree.js +++ b/erpnext/setup/doctype/item_group/item_group_tree.js @@ -1,3 +1,3 @@ frappe.treeview_settings["Item Group"] = { - ignore_fields:["parent_item_group"] -} + ignore_fields: ["parent_item_group"], +}; diff --git a/erpnext/setup/doctype/party_type/party_type.js b/erpnext/setup/doctype/party_type/party_type.js index 7a96dc55ee9..9bbfd7d8832 100644 --- a/erpnext/setup/doctype/party_type/party_type.js +++ b/erpnext/setup/doctype/party_type/party_type.js @@ -1,15 +1,15 @@ // Copyright (c) 2016, Frappe Technologies and contributors // For license information, please see license.txt -frappe.ui.form.on('Party Type', { - setup: function(frm) { - frm.fields_dict["party_type"].get_query = function(frm) { +frappe.ui.form.on("Party Type", { + setup: function (frm) { + frm.fields_dict["party_type"].get_query = function (frm) { return { filters: { - "istable": 0, - "is_submittable": 0 - } - } - } - } + istable: 0, + is_submittable: 0, + }, + }; + }; + }, }); diff --git a/erpnext/setup/doctype/print_heading/print_heading.js b/erpnext/setup/doctype/print_heading/print_heading.js index 3680906057f..273e30fd197 100644 --- a/erpnext/setup/doctype/print_heading/print_heading.js +++ b/erpnext/setup/doctype/print_heading/print_heading.js @@ -1,13 +1,7 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt - - //--------- ONLOAD ------------- -cur_frm.cscript.onload = function(doc, cdt, cdn) { +cur_frm.cscript.onload = function (doc, cdt, cdn) {}; -} - -cur_frm.cscript.refresh = function(doc, cdt, cdn) { - -} +cur_frm.cscript.refresh = function (doc, cdt, cdn) {}; diff --git a/erpnext/setup/doctype/quotation_lost_reason/quotation_lost_reason.js b/erpnext/setup/doctype/quotation_lost_reason/quotation_lost_reason.js index 3680906057f..273e30fd197 100644 --- a/erpnext/setup/doctype/quotation_lost_reason/quotation_lost_reason.js +++ b/erpnext/setup/doctype/quotation_lost_reason/quotation_lost_reason.js @@ -1,13 +1,7 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt - - //--------- ONLOAD ------------- -cur_frm.cscript.onload = function(doc, cdt, cdn) { +cur_frm.cscript.onload = function (doc, cdt, cdn) {}; -} - -cur_frm.cscript.refresh = function(doc, cdt, cdn) { - -} +cur_frm.cscript.refresh = function (doc, cdt, cdn) {}; diff --git a/erpnext/setup/doctype/sales_partner/sales_partner.js b/erpnext/setup/doctype/sales_partner/sales_partner.js index 5656d43e852..cb957941dc8 100644 --- a/erpnext/setup/doctype/sales_partner/sales_partner.js +++ b/erpnext/setup/doctype/sales_partner/sales_partner.js @@ -1,34 +1,33 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.ui.form.on('Sales Partner', { - refresh: function(frm) { - frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Sales Partner'} +frappe.ui.form.on("Sales Partner", { + refresh: function (frm) { + frappe.dynamic_link = { doc: frm.doc, fieldname: "name", doctype: "Sales Partner" }; - if(frm.doc.__islocal){ - hide_field(['address_html', 'contact_html', 'address_contacts']); + if (frm.doc.__islocal) { + hide_field(["address_html", "contact_html", "address_contacts"]); frappe.contacts.clear_address_and_contact(frm); - } - else{ - unhide_field(['address_html', 'contact_html', 'address_contacts']); + } else { + unhide_field(["address_html", "contact_html", "address_contacts"]); frappe.contacts.render_address_and_contact(frm); } }, - setup: function(frm) { - frm.fields_dict["targets"].grid.get_field("distribution_id").get_query = function(doc, cdt, cdn){ + setup: function (frm) { + frm.fields_dict["targets"].grid.get_field("distribution_id").get_query = function (doc, cdt, cdn) { var row = locals[cdt][cdn]; return { filters: { - 'fiscal_year': row.fiscal_year - } - } + fiscal_year: row.fiscal_year, + }, + }; }; }, - referral_code:function(frm){ + referral_code: function (frm) { if (frm.doc.referral_code) { - frm.doc.referral_code=frm.doc.referral_code.toUpperCase(); - frm.refresh_field('referral_code'); + frm.doc.referral_code = frm.doc.referral_code.toUpperCase(); + frm.refresh_field("referral_code"); } - } + }, }); diff --git a/erpnext/setup/doctype/sales_person/sales_person.js b/erpnext/setup/doctype/sales_person/sales_person.js index d86a8f3d984..89f794e0955 100644 --- a/erpnext/setup/doctype/sales_person/sales_person.js +++ b/erpnext/setup/doctype/sales_person/sales_person.js @@ -1,59 +1,69 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.ui.form.on('Sales Person', { - refresh: function(frm) { - if(frm.doc.__onload && frm.doc.__onload.dashboard_info) { +frappe.ui.form.on("Sales Person", { + refresh: function (frm) { + if (frm.doc.__onload && frm.doc.__onload.dashboard_info) { let info = frm.doc.__onload.dashboard_info; - frm.dashboard.add_indicator(__('Total Contribution Amount Against Orders: {0}', - [format_currency(info.allocated_amount_against_order, info.currency)]), 'blue'); + frm.dashboard.add_indicator( + __("Total Contribution Amount Against Orders: {0}", [ + format_currency(info.allocated_amount_against_order, info.currency), + ]), + "blue" + ); - frm.dashboard.add_indicator(__('Total Contribution Amount Against Invoices: {0}', - [format_currency(info.allocated_amount_against_invoice, info.currency)]), 'blue'); + frm.dashboard.add_indicator( + __("Total Contribution Amount Against Invoices: {0}", [ + format_currency(info.allocated_amount_against_invoice, info.currency), + ]), + "blue" + ); } }, - setup: function(frm) { - frm.fields_dict["targets"].grid.get_field("distribution_id").get_query = function(doc, cdt, cdn){ + setup: function (frm) { + frm.fields_dict["targets"].grid.get_field("distribution_id").get_query = function (doc, cdt, cdn) { var row = locals[cdt][cdn]; return { filters: { - 'fiscal_year': row.fiscal_year - } - } + fiscal_year: row.fiscal_year, + }, + }; }; frm.make_methods = { - 'Sales Order': () => frappe.new_doc("Sales Order") - .then(() => frm.add_child("sales_team", {"sales_person": frm.doc.name})) - } - } + "Sales Order": () => + frappe + .new_doc("Sales Order") + .then(() => frm.add_child("sales_team", { sales_person: frm.doc.name })), + }; + }, }); -cur_frm.cscript.refresh = function(doc, cdt, cdn) { +cur_frm.cscript.refresh = function (doc, cdt, cdn) { cur_frm.cscript.set_root_readonly(doc); -} +}; -cur_frm.cscript.set_root_readonly = function(doc) { +cur_frm.cscript.set_root_readonly = function (doc) { // read-only for root - if(!doc.parent_sales_person && !doc.__islocal) { + if (!doc.parent_sales_person && !doc.__islocal) { cur_frm.set_read_only(); cur_frm.set_intro(__("This is a root sales person and cannot be edited.")); } else { cur_frm.set_intro(null); } -} +}; //get query select sales person -cur_frm.fields_dict['parent_sales_person'].get_query = function(doc, cdt, cdn) { - return{ +cur_frm.fields_dict["parent_sales_person"].get_query = function (doc, cdt, cdn) { + return { filters: [ - ['Sales Person', 'is_group', '=', 1], - ['Sales Person', 'name', '!=', doc.sales_person_name] - ] - } -} + ["Sales Person", "is_group", "=", 1], + ["Sales Person", "name", "!=", doc.sales_person_name], + ], + }; +}; -cur_frm.fields_dict.employee.get_query = function(doc, cdt, cdn) { - return { query: "erpnext.controllers.queries.employee_query" } -} +cur_frm.fields_dict.employee.get_query = function (doc, cdt, cdn) { + return { query: "erpnext.controllers.queries.employee_query" }; +}; diff --git a/erpnext/setup/doctype/sales_person/sales_person_tree.js b/erpnext/setup/doctype/sales_person/sales_person_tree.js index 00056fde869..0fcd592aea3 100644 --- a/erpnext/setup/doctype/sales_person/sales_person_tree.js +++ b/erpnext/setup/doctype/sales_person/sales_person_tree.js @@ -1,12 +1,18 @@ - frappe.treeview_settings["Sales Person"] = { fields: [ - {fieldtype:'Data', fieldname: 'sales_person_name', - label:__('New Sales Person Name'), reqd:true}, - {fieldtype:'Link', fieldname:'employee', - label:__('Employee'), options:'Employee', - description: __("Please enter Employee Id of this sales person")}, - {fieldtype:'Check', fieldname:'is_group', label:__('Group Node'), - description: __("Further nodes can be only created under 'Group' type nodes")} + { fieldtype: "Data", fieldname: "sales_person_name", label: __("New Sales Person Name"), reqd: true }, + { + fieldtype: "Link", + fieldname: "employee", + label: __("Employee"), + options: "Employee", + description: __("Please enter Employee Id of this sales person"), + }, + { + fieldtype: "Check", + fieldname: "is_group", + label: __("Group Node"), + description: __("Further nodes can be only created under 'Group' type nodes"), + }, ], -} +}; diff --git a/erpnext/setup/doctype/supplier_group/supplier_group.js b/erpnext/setup/doctype/supplier_group/supplier_group.js index e75030d4414..cd58d23889d 100644 --- a/erpnext/setup/doctype/supplier_group/supplier_group.js +++ b/erpnext/setup/doctype/supplier_group/supplier_group.js @@ -1,14 +1,14 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -cur_frm.cscript.refresh = function(doc) { +cur_frm.cscript.refresh = function (doc) { cur_frm.set_intro(doc.__islocal ? "" : __("There is nothing to edit.")); cur_frm.cscript.set_root_readonly(doc); }; -cur_frm.cscript.set_root_readonly = function(doc) { +cur_frm.cscript.set_root_readonly = function (doc) { // read-only for root customer group - if(!doc.parent_supplier_group && !doc.__islocal) { + if (!doc.parent_supplier_group && !doc.__islocal) { cur_frm.set_read_only(); cur_frm.set_intro(__("This is a root supplier group and cannot be edited.")); } else { @@ -17,22 +17,22 @@ cur_frm.cscript.set_root_readonly = function(doc) { }; // get query select Customer Group -cur_frm.fields_dict['parent_supplier_group'].get_query = function() { +cur_frm.fields_dict["parent_supplier_group"].get_query = function () { return { filters: { - 'is_group': 1, - 'name': ['!=', cur_frm.doc.supplier_group_name] - } + is_group: 1, + name: ["!=", cur_frm.doc.supplier_group_name], + }, }; }; -cur_frm.fields_dict['accounts'].grid.get_field('account').get_query = function(doc, cdt, cdn) { - var d = locals[cdt][cdn]; +cur_frm.fields_dict["accounts"].grid.get_field("account").get_query = function (doc, cdt, cdn) { + var d = locals[cdt][cdn]; return { filters: { - 'account_type': 'Payable', - 'company': d.company, - "is_group": 0 - } + account_type: "Payable", + company: d.company, + is_group: 0, + }, }; }; diff --git a/erpnext/setup/doctype/supplier_group/supplier_group_tree.js b/erpnext/setup/doctype/supplier_group/supplier_group_tree.js index 728793eb25f..9210dc1f002 100644 --- a/erpnext/setup/doctype/supplier_group/supplier_group_tree.js +++ b/erpnext/setup/doctype/supplier_group/supplier_group_tree.js @@ -1,4 +1,4 @@ frappe.treeview_settings["Supplier Group"] = { breadcrumbs: "Buying", - ignore_fields:["parent_supplier_group"] + ignore_fields: ["parent_supplier_group"], }; diff --git a/erpnext/setup/doctype/territory/territory.js b/erpnext/setup/doctype/territory/territory.js index 3caf814c90b..744c7fe01e2 100644 --- a/erpnext/setup/doctype/territory/territory.js +++ b/erpnext/setup/doctype/territory/territory.js @@ -2,38 +2,38 @@ // License: GNU General Public License v3. See license.txt frappe.ui.form.on("Territory", { - setup: function(frm) { - frm.fields_dict["targets"].grid.get_field("distribution_id").get_query = function(doc, cdt, cdn){ + setup: function (frm) { + frm.fields_dict["targets"].grid.get_field("distribution_id").get_query = function (doc, cdt, cdn) { var row = locals[cdt][cdn]; return { filters: { - 'fiscal_year': row.fiscal_year - } - } + fiscal_year: row.fiscal_year, + }, + }; }; - } + }, }); -cur_frm.cscript.refresh = function(doc, cdt, cdn) { +cur_frm.cscript.refresh = function (doc, cdt, cdn) { cur_frm.cscript.set_root_readonly(doc); -} +}; -cur_frm.cscript.set_root_readonly = function(doc) { +cur_frm.cscript.set_root_readonly = function (doc) { // read-only for root territory - if(!doc.parent_territory && !doc.__islocal) { + if (!doc.parent_territory && !doc.__islocal) { cur_frm.set_read_only(); cur_frm.set_intro(__("This is a root territory and cannot be edited.")); } else { cur_frm.set_intro(null); } -} +}; //get query select territory -cur_frm.fields_dict['parent_territory'].get_query = function(doc,cdt,cdn) { - return{ - filters:[ - ['Territory', 'is_group', '=', 1], - ['Territory', 'name', '!=', doc.territory_name] - ] - } -} +cur_frm.fields_dict["parent_territory"].get_query = function (doc, cdt, cdn) { + return { + filters: [ + ["Territory", "is_group", "=", 1], + ["Territory", "name", "!=", doc.territory_name], + ], + }; +}; diff --git a/erpnext/setup/doctype/territory/territory_tree.js b/erpnext/setup/doctype/territory/territory_tree.js index dadeeef09e9..4892b8c45fc 100644 --- a/erpnext/setup/doctype/territory/territory_tree.js +++ b/erpnext/setup/doctype/territory/territory_tree.js @@ -1,3 +1,3 @@ frappe.treeview_settings["Territory"] = { - ignore_fields:["parent_territory"] -} + ignore_fields: ["parent_territory"], +}; diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js index a7b0709b3da..527c753d6a9 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record.js @@ -1,39 +1,38 @@ // Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Transaction Deletion Record', { - onload: function(frm) { +frappe.ui.form.on("Transaction Deletion Record", { + onload: function (frm) { if (frm.doc.docstatus == 0) { let doctypes_to_be_ignored_array; frappe.call({ - method: 'erpnext.setup.doctype.transaction_deletion_record.transaction_deletion_record.get_doctypes_to_be_ignored', - callback: function(r) { + method: "erpnext.setup.doctype.transaction_deletion_record.transaction_deletion_record.get_doctypes_to_be_ignored", + callback: function (r) { doctypes_to_be_ignored_array = r.message; populate_doctypes_to_be_ignored(doctypes_to_be_ignored_array, frm); - frm.fields_dict['doctypes_to_be_ignored'].grid.set_column_disp('no_of_docs', false); - frm.refresh_field('doctypes_to_be_ignored'); - } + frm.fields_dict["doctypes_to_be_ignored"].grid.set_column_disp("no_of_docs", false); + frm.refresh_field("doctypes_to_be_ignored"); + }, }); } - frm.get_field('doctypes_to_be_ignored').grid.cannot_add_rows = true; - frm.fields_dict['doctypes_to_be_ignored'].grid.set_column_disp('no_of_docs', false); - frm.refresh_field('doctypes_to_be_ignored'); + frm.get_field("doctypes_to_be_ignored").grid.cannot_add_rows = true; + frm.fields_dict["doctypes_to_be_ignored"].grid.set_column_disp("no_of_docs", false); + frm.refresh_field("doctypes_to_be_ignored"); }, - refresh: function(frm) { - frm.fields_dict['doctypes_to_be_ignored'].grid.set_column_disp('no_of_docs', false); - frm.refresh_field('doctypes_to_be_ignored'); - } - + refresh: function (frm) { + frm.fields_dict["doctypes_to_be_ignored"].grid.set_column_disp("no_of_docs", false); + frm.refresh_field("doctypes_to_be_ignored"); + }, }); function populate_doctypes_to_be_ignored(doctypes_to_be_ignored_array, frm) { if (frm.doc.doctypes_to_be_ignored.length === 0) { var i; for (i = 0; i < doctypes_to_be_ignored_array.length; i++) { - frm.add_child('doctypes_to_be_ignored', { - doctype_name: doctypes_to_be_ignored_array[i] + frm.add_child("doctypes_to_be_ignored", { + doctype_name: doctypes_to_be_ignored_array[i], }); } } diff --git a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record_list.js b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record_list.js index c238f18abad..08a35df2c17 100644 --- a/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record_list.js +++ b/erpnext/setup/doctype/transaction_deletion_record/transaction_deletion_record_list.js @@ -1,12 +1,12 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.listview_settings['Transaction Deletion Record'] = { - get_indicator: function(doc) { +frappe.listview_settings["Transaction Deletion Record"] = { + get_indicator: function (doc) { if (doc.docstatus == 0) { return [__("Draft"), "red"]; } else { return [__("Completed"), "green"]; } - } + }, }; diff --git a/erpnext/setup/doctype/uom/uom.js b/erpnext/setup/doctype/uom/uom.js index 3680906057f..273e30fd197 100644 --- a/erpnext/setup/doctype/uom/uom.js +++ b/erpnext/setup/doctype/uom/uom.js @@ -1,13 +1,7 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt - - //--------- ONLOAD ------------- -cur_frm.cscript.onload = function(doc, cdt, cdn) { +cur_frm.cscript.onload = function (doc, cdt, cdn) {}; -} - -cur_frm.cscript.refresh = function(doc, cdt, cdn) { - -} +cur_frm.cscript.refresh = function (doc, cdt, cdn) {}; diff --git a/erpnext/setup/doctype/uom_conversion_factor/uom_conversion_factor.js b/erpnext/setup/doctype/uom_conversion_factor/uom_conversion_factor.js index e734d83500f..e975729dca7 100644 --- a/erpnext/setup/doctype/uom_conversion_factor/uom_conversion_factor.js +++ b/erpnext/setup/doctype/uom_conversion_factor/uom_conversion_factor.js @@ -1,8 +1,6 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('UOM Conversion Factor', { - refresh: function() { - - } +frappe.ui.form.on("UOM Conversion Factor", { + refresh: function () {}, }); diff --git a/erpnext/setup/doctype/vehicle/vehicle.js b/erpnext/setup/doctype/vehicle/vehicle.js index f12c4346852..9d598a9883b 100644 --- a/erpnext/setup/doctype/vehicle/vehicle.js +++ b/erpnext/setup/doctype/vehicle/vehicle.js @@ -1,8 +1,6 @@ // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Vehicle', { - refresh: function(frm) { - - } +frappe.ui.form.on("Vehicle", { + refresh: function (frm) {}, }); diff --git a/erpnext/setup/page/welcome_to_erpnext/welcome_to_erpnext.js b/erpnext/setup/page/welcome_to_erpnext/welcome_to_erpnext.js index f072b8d8c57..0b4d8235106 100644 --- a/erpnext/setup/page/welcome_to_erpnext/welcome_to_erpnext.js +++ b/erpnext/setup/page/welcome_to_erpnext/welcome_to_erpnext.js @@ -1,20 +1,27 @@ -frappe.pages['welcome-to-erpnext'].on_page_load = function(wrapper) { +frappe.pages["welcome-to-erpnext"].on_page_load = function (wrapper) { var parent = $('
              ').appendTo(wrapper); parent.html(frappe.render_template("welcome_to_erpnext", {})); - parent.find(".video-placeholder").on("click", function() { + parent.find(".video-placeholder").on("click", function () { window.erpnext_welcome_video_started = true; parent.find(".video-placeholder").addClass("hidden"); - parent.find(".embed-responsive").append('') + parent + .find(".embed-responsive") + .append( + '' + ); }); // pause video on page change - $(document).on("page-change", function() { + $(document).on("page-change", function () { if (window.erpnext_welcome_video_started && parent) { - parent.find(".video-playlist").each(function() { - this.contentWindow.postMessage('{"event":"command","func":"' + 'pauseVideo' + '","args":""}', '*'); + parent.find(".video-playlist").each(function () { + this.contentWindow.postMessage( + '{"event":"command","func":"' + "pauseVideo" + '","args":""}', + "*" + ); }); } }); -} +}; diff --git a/erpnext/stock/dashboard/item_dashboard.js b/erpnext/stock/dashboard/item_dashboard.js index bef438f9fd7..29db6e3c01d 100644 --- a/erpnext/stock/dashboard/item_dashboard.js +++ b/erpnext/stock/dashboard/item_dashboard.js @@ -1,4 +1,4 @@ -frappe.provide('erpnext.stock'); +frappe.provide("erpnext.stock"); erpnext.stock.ItemDashboard = class ItemDashboard { constructor(opts) { @@ -9,46 +9,51 @@ erpnext.stock.ItemDashboard = class ItemDashboard { var me = this; this.start = 0; if (!this.sort_by) { - this.sort_by = 'projected_qty'; - this.sort_order = 'asc'; + this.sort_by = "projected_qty"; + this.sort_order = "asc"; } - this.content = $(frappe.render_template('item_dashboard')).appendTo(this.parent); - this.result = this.content.find('.result'); + this.content = $(frappe.render_template("item_dashboard")).appendTo(this.parent); + this.result = this.content.find(".result"); - this.content.on('click', '.btn-move', function () { + this.content.on("click", ".btn-move", function () { handle_move_add($(this), "Move"); }); - this.content.on('click', '.btn-add', function () { + this.content.on("click", ".btn-add", function () { handle_move_add($(this), "Add"); }); - this.content.on('click', '.btn-edit', function () { - let item = unescape($(this).attr('data-item')); - let warehouse = unescape($(this).attr('data-warehouse')); - let company = unescape($(this).attr('data-company')); - frappe.db.get_value('Putaway Rule', { - 'item_code': item, - 'warehouse': warehouse, - 'company': company - }, 'name', (r) => { - frappe.set_route("Form", "Putaway Rule", r.name); - }); + this.content.on("click", ".btn-edit", function () { + let item = unescape($(this).attr("data-item")); + let warehouse = unescape($(this).attr("data-warehouse")); + let company = unescape($(this).attr("data-company")); + frappe.db.get_value( + "Putaway Rule", + { + item_code: item, + warehouse: warehouse, + company: company, + }, + "name", + (r) => { + frappe.set_route("Form", "Putaway Rule", r.name); + } + ); }); function handle_move_add(element, action) { - let item = unescape(element.attr('data-item')); - let warehouse = unescape(element.attr('data-warehouse')); - let actual_qty = unescape(element.attr('data-actual_qty')); - let disable_quick_entry = Number(unescape(element.attr('data-disable_quick_entry'))); + let item = unescape(element.attr("data-item")); + let warehouse = unescape(element.attr("data-warehouse")); + 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"; if (disable_quick_entry) { open_stock_entry(item, warehouse, entry_type); } else { if (action === "Add") { - let rate = unescape($(this).attr('data-rate')); + let rate = unescape($(this).attr("data-rate")); erpnext.stock.move_item(item, null, warehouse, actual_qty, rate, function () { me.refresh(); }); @@ -61,35 +66,33 @@ erpnext.stock.ItemDashboard = class ItemDashboard { } function open_stock_entry(item, warehouse, entry_type) { - frappe.model.with_doctype('Stock Entry', function () { - var doc = frappe.model.get_new_doc('Stock Entry'); + frappe.model.with_doctype("Stock Entry", function () { + var doc = frappe.model.get_new_doc("Stock Entry"); if (entry_type) { doc.stock_entry_type = entry_type; } - var row = frappe.model.add_child(doc, 'items'); + var row = frappe.model.add_child(doc, "items"); row.item_code = item; if (entry_type === "Material Transfer") { row.s_warehouse = warehouse; - } - else { + } else { row.t_warehouse = warehouse; } - frappe.set_route('Form', doc.doctype, doc.name); + frappe.set_route("Form", doc.doctype, doc.name); }); } // more - this.content.find('.btn-more').on('click', function () { + this.content.find(".btn-more").on("click", function () { me.start += me.page_length; me.refresh(); }); - } refresh() { - if(this.before_refresh) { + if (this.before_refresh) { this.before_refresh(); } @@ -101,7 +104,7 @@ erpnext.stock.ItemDashboard = class ItemDashboard { company: this.company, start: this.start, sort_by: this.sort_by, - sort_order: this.sort_order + sort_order: this.sort_order, }; var me = this; @@ -110,11 +113,11 @@ erpnext.stock.ItemDashboard = class ItemDashboard { args: args, callback: function (r) { me.render(r.message); - } + }, }); } render(data) { - if (this.start===0) { + if (this.start === 0) { this.max_count = 0; this.result.empty(); } @@ -129,22 +132,22 @@ erpnext.stock.ItemDashboard = class ItemDashboard { this.max_count = this.max_count; // show more button - if (data && data.length === (this.page_length + 1)) { - this.content.find('.more').removeClass('hidden'); + if (data && data.length === this.page_length + 1) { + this.content.find(".more").removeClass("hidden"); // remove the last element data.splice(-1); } else { - this.content.find('.more').addClass('hidden'); + this.content.find(".more").addClass("hidden"); } // If not any stock in any warehouses provide a message to end user if (context.data.length > 0) { - this.content.find('.result').css('text-align', 'unset'); + this.content.find(".result").css("text-align", "unset"); $(frappe.render_template(this.template, context)).appendTo(this.result); } else { var message = __("No Stock Available Currently"); - this.content.find('.result').css('text-align', 'center'); + this.content.find(".result").css("text-align", "center"); $(`
              ${message}
              `).appendTo(this.result); @@ -152,19 +155,23 @@ erpnext.stock.ItemDashboard = class ItemDashboard { } get_item_dashboard_data(data, max_count, show_item) { - if(!max_count) max_count = 0; - if(!data) data = []; + if (!max_count) max_count = 0; + if (!data) data = []; data.forEach(function (d) { - d.actual_or_pending = d.projected_qty + d.reserved_qty + d.reserved_qty_for_production + d.reserved_qty_for_sub_contract; + d.actual_or_pending = + d.projected_qty + + d.reserved_qty + + d.reserved_qty_for_production + + d.reserved_qty_for_sub_contract; d.pending_qty = 0; - d.total_reserved = d.reserved_qty + d.reserved_qty_for_production + d.reserved_qty_for_sub_contract; + d.total_reserved = + d.reserved_qty + d.reserved_qty_for_production + d.reserved_qty_for_sub_contract; if (d.actual_or_pending > d.actual_qty) { d.pending_qty = d.actual_or_pending - d.actual_qty; } - max_count = Math.max(d.actual_or_pending, d.actual_qty, - d.total_reserved, max_count); + max_count = Math.max(d.actual_or_pending, d.actual_qty, d.total_reserved, max_count); }); let can_write = 0; @@ -176,7 +183,7 @@ erpnext.stock.ItemDashboard = class ItemDashboard { data: data, max_count: max_count, can_write: can_write, - show_item: show_item || false + show_item: show_item || false, }; } @@ -201,73 +208,74 @@ erpnext.stock.ItemDashboard = class ItemDashboard { erpnext.stock.move_item = function (item, source, target, actual_qty, rate, callback) { var dialog = new frappe.ui.Dialog({ - title: target ? __('Add Item') : __('Move Item'), - fields: [{ - fieldname: 'item_code', - label: __('Item'), - fieldtype: 'Link', - options: 'Item', - read_only: 1 - }, - { - fieldname: 'source', - label: __('Source Warehouse'), - fieldtype: 'Link', - options: 'Warehouse', - read_only: 1 - }, - { - fieldname: 'target', - label: __('Target Warehouse'), - fieldtype: 'Link', - options: 'Warehouse', - reqd: 1, - get_query() { - return { - filters: { - is_group: 0 - } - } - } - }, - { - fieldname: 'qty', - label: __('Quantity'), - reqd: 1, - fieldtype: 'Float', - description: __('Available {0}', [actual_qty]) - }, - { - fieldname: 'rate', - label: __('Rate'), - fieldtype: 'Currency', - hidden: 1 - }, + title: target ? __("Add Item") : __("Move Item"), + fields: [ + { + fieldname: "item_code", + label: __("Item"), + fieldtype: "Link", + options: "Item", + read_only: 1, + }, + { + fieldname: "source", + label: __("Source Warehouse"), + fieldtype: "Link", + options: "Warehouse", + read_only: 1, + }, + { + fieldname: "target", + label: __("Target Warehouse"), + fieldtype: "Link", + options: "Warehouse", + reqd: 1, + get_query() { + return { + filters: { + is_group: 0, + }, + }; + }, + }, + { + fieldname: "qty", + label: __("Quantity"), + reqd: 1, + fieldtype: "Float", + description: __("Available {0}", [actual_qty]), + }, + { + fieldname: "rate", + label: __("Rate"), + fieldtype: "Currency", + hidden: 1, + }, ], }); dialog.show(); - dialog.get_field('item_code').set_input(item); + dialog.get_field("item_code").set_input(item); if (source) { - dialog.get_field('source').set_input(source); + dialog.get_field("source").set_input(source); } else { - dialog.get_field('source').df.hidden = 1; - dialog.get_field('source').refresh(); + dialog.get_field("source").df.hidden = 1; + dialog.get_field("source").refresh(); } if (rate) { - dialog.get_field('rate').set_value(rate); - dialog.get_field('rate').df.hidden = 0; - dialog.get_field('rate').refresh(); + dialog.get_field("rate").set_value(rate); + dialog.get_field("rate").df.hidden = 0; + dialog.get_field("rate").refresh(); } if (target) { - dialog.get_field('target').df.read_only = 1; - dialog.get_field('target').value = target; - dialog.get_field('target').refresh(); + dialog.get_field("target").df.read_only = 1; + dialog.get_field("target").value = target; + dialog.get_field("target").refresh(); } - dialog.set_primary_action(__('Create Stock Entry'), function () { + dialog.set_primary_action(__("Create Stock Entry"), function () { if (source && (dialog.get_value("qty") == 0 || dialog.get_value("qty") > actual_qty)) { frappe.msgprint(__("Quantity must be greater than zero, and less or equal to {0}", [actual_qty])); return; @@ -278,20 +286,20 @@ erpnext.stock.move_item = function (item, source, target, actual_qty, rate, call return; } - frappe.model.with_doctype('Stock Entry', function () { - let doc = frappe.model.get_new_doc('Stock Entry'); - doc.from_warehouse = dialog.get_value('source'); - doc.to_warehouse = dialog.get_value('target'); + frappe.model.with_doctype("Stock Entry", function () { + let doc = frappe.model.get_new_doc("Stock Entry"); + doc.from_warehouse = dialog.get_value("source"); + doc.to_warehouse = dialog.get_value("target"); doc.stock_entry_type = doc.from_warehouse ? "Material Transfer" : "Material Receipt"; - let row = frappe.model.add_child(doc, 'items'); - row.item_code = dialog.get_value('item_code'); - row.s_warehouse = dialog.get_value('source'); - row.t_warehouse = dialog.get_value('target'); - row.qty = dialog.get_value('qty'); + let row = frappe.model.add_child(doc, "items"); + row.item_code = dialog.get_value("item_code"); + row.s_warehouse = dialog.get_value("source"); + row.t_warehouse = dialog.get_value("target"); + row.qty = dialog.get_value("qty"); row.conversion_factor = 1; - row.transfer_qty = dialog.get_value('qty'); - row.basic_rate = dialog.get_value('rate'); - frappe.set_route('Form', doc.doctype, doc.name); + row.transfer_qty = dialog.get_value("qty"); + row.basic_rate = dialog.get_value("rate"); + frappe.set_route("Form", doc.doctype, doc.name); }); }); }; diff --git a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.js b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.js index 2b9d46e4ab0..a550174d656 100644 --- a/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.js +++ b/erpnext/stock/dashboard_chart_source/warehouse_wise_stock_value/warehouse_wise_stock_value.js @@ -1,4 +1,4 @@ -frappe.provide('frappe.dashboards.chart_sources'); +frappe.provide("frappe.dashboards.chart_sources"); frappe.dashboards.chart_sources["Warehouse wise Stock Value"] = { method: "erpnext.stock.dashboard_chart_source.warehouse_wise_stock_value.warehouse_wise_stock_value.get", @@ -8,7 +8,7 @@ frappe.dashboards.chart_sources["Warehouse wise Stock Value"] = { label: __("Company"), fieldtype: "Link", options: "Company", - default: frappe.defaults.get_user_default("Company") - } - ] + default: frappe.defaults.get_user_default("Company"), + }, + ], }; diff --git a/erpnext/stock/doctype/batch/batch.js b/erpnext/stock/doctype/batch/batch.js index 38d3d1cc376..fc160fa3b65 100644 --- a/erpnext/stock/doctype/batch/batch.js +++ b/erpnext/stock/doctype/batch/batch.js @@ -1,62 +1,72 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.ui.form.on('Batch', { +frappe.ui.form.on("Batch", { setup: (frm) => { - frm.fields_dict['item'].get_query = function(doc, cdt, cdn) { + frm.fields_dict["item"].get_query = function (doc, cdt, cdn) { return { query: "erpnext.controllers.queries.item_query", - filters:{ - 'is_stock_item': 1, - 'has_batch_no': 1 - } - } - } + filters: { + is_stock_item: 1, + has_batch_no: 1, + }, + }; + }; }, refresh: (frm) => { - if(!frm.is_new()) { + if (!frm.is_new()) { frm.add_custom_button(__("View Ledger"), () => { frappe.route_options = { - batch_no: frm.doc.name + batch_no: frm.doc.name, }; frappe.set_route("query-report", "Stock Ledger"); }); - frm.trigger('make_dashboard'); + frm.trigger("make_dashboard"); } }, item: (frm) => { // frappe.db.get_value('Item', {name: frm.doc.item}, 'has_expiry_date', (r) => { // frm.toggle_reqd('expiry_date', r.has_expiry_date); // }); - frappe.db.get_value('Item', {name: frm.doc.item}, ['shelf_life_in_days', 'has_expiry_date'], (r) => { - if (r.has_expiry_date && r.shelf_life_in_days) { - // Calculate expiry date based on shelf_life_in_days - frm.set_value('expiry_date', frappe.datetime.add_days(frm.doc.manufacturing_date, r.shelf_life_in_days)); - }else if(r.has_expiry_date){ - frm.toggle_reqd('expiry_date', r.has_expiry_date); + frappe.db.get_value( + "Item", + { name: frm.doc.item }, + ["shelf_life_in_days", "has_expiry_date"], + (r) => { + if (r.has_expiry_date && r.shelf_life_in_days) { + // Calculate expiry date based on shelf_life_in_days + frm.set_value( + "expiry_date", + frappe.datetime.add_days(frm.doc.manufacturing_date, r.shelf_life_in_days) + ); + } else if (r.has_expiry_date) { + frm.toggle_reqd("expiry_date", r.has_expiry_date); + } } - }) + ); }, make_dashboard: (frm) => { - if(!frm.is_new()) { + if (!frm.is_new()) { frappe.call({ - method: 'erpnext.stock.doctype.batch.batch.get_batch_qty', - args: {batch_no: frm.doc.name}, + method: "erpnext.stock.doctype.batch.batch.get_batch_qty", + args: { batch_no: frm.doc.name }, callback: (r) => { - if(!r.message) { + if (!r.message) { return; } - const section = frm.dashboard.add_section('', __("Stock Levels")); + const section = frm.dashboard.add_section("", __("Stock Levels")); // sort by qty - r.message.sort(function(a, b) { a.qty > b.qty ? 1 : -1 }); + r.message.sort(function (a, b) { + a.qty > b.qty ? 1 : -1; + }); - const rows = $('
              ').appendTo(section); + const rows = $("
              ").appendTo(section); // show - (r.message || []).forEach(function(d) { - if(d.qty > 0) { + (r.message || []).forEach(function (d) { + if (d.qty > 0) { $(`
              ${d.warehouse}
              ${d.qty}
              @@ -64,98 +74,110 @@ frappe.ui.form.on('Batch', { + ${__("Move")} + ${__("Split")}
              `).appendTo(rows); } }); // move - ask for target warehouse and make stock entry - rows.find('.btn-move').on('click', function() { + rows.find(".btn-move").on("click", function () { const $btn = $(this); const fields = [ { - fieldname: 'to_warehouse', - label: __('To Warehouse'), - fieldtype: 'Link', - options: 'Warehouse' - } + fieldname: "to_warehouse", + label: __("To Warehouse"), + fieldtype: "Link", + options: "Warehouse", + }, ]; frappe.prompt( fields, (data) => { frappe.call({ - method: 'erpnext.stock.doctype.stock_entry.stock_entry_utils.make_stock_entry', + method: "erpnext.stock.doctype.stock_entry.stock_entry_utils.make_stock_entry", args: { item_code: frm.doc.item, batch_no: frm.doc.name, - qty: $btn.attr('data-qty'), - from_warehouse: $btn.attr('data-warehouse'), + qty: $btn.attr("data-qty"), + from_warehouse: $btn.attr("data-warehouse"), to_warehouse: data.to_warehouse, source_document: frm.doc.reference_name, - reference_doctype: frm.doc.reference_doctype + reference_doctype: frm.doc.reference_doctype, }, callback: (r) => { - frappe.show_alert(__('Stock Entry {0} created', - ['' + r.message.name+ ''])); + frappe.show_alert( + __("Stock Entry {0} created", [ + '' + + r.message.name + + "", + ]) + ); frm.refresh(); }, }); }, - __('Select Target Warehouse'), - __('Move') + __("Select Target Warehouse"), + __("Move") ); }); // split - ask for new qty and batch ID (optional) // and make stock entry via batch.batch_split - rows.find('.btn-split').on('click', function() { + rows.find(".btn-split").on("click", function () { const $btn = $(this); - frappe.prompt([{ - fieldname: 'qty', - label: __('New Batch Qty'), - fieldtype: 'Float', - 'default': $btn.attr('data-qty') - }, - { - fieldname: 'new_batch_id', - label: __('New Batch ID (Optional)'), - fieldtype: 'Data', - }], - (data) => { - frappe.xcall( - 'erpnext.stock.doctype.batch.batch.split_batch', + frappe.prompt( + [ { - item_code: frm.doc.item, - batch_no: frm.doc.name, - qty: data.qty, - warehouse: $btn.attr('data-warehouse'), - new_batch_id: data.new_batch_id - } - ).then(() => frm.reload_doc()); - }, - __('Split Batch'), - __('Split') + fieldname: "qty", + label: __("New Batch Qty"), + fieldtype: "Float", + default: $btn.attr("data-qty"), + }, + { + fieldname: "new_batch_id", + label: __("New Batch ID (Optional)"), + fieldtype: "Data", + }, + ], + (data) => { + frappe + .xcall("erpnext.stock.doctype.batch.batch.split_batch", { + item_code: frm.doc.item, + batch_no: frm.doc.name, + qty: data.qty, + warehouse: $btn.attr("data-warehouse"), + new_batch_id: data.new_batch_id, + }) + .then(() => frm.reload_doc()); + }, + __("Split Batch"), + __("Split") ); - }) + }); frm.dashboard.show(); - } + }, }); } - } -}) + }, +}); -frappe.ui.form.on('Batch', 'manufacturing_date', function (frm){ - frappe.db.get_value('Item', {name: frm.doc.item}, ['shelf_life_in_days', 'has_expiry_date'], (r) => { +frappe.ui.form.on("Batch", "manufacturing_date", function (frm) { + frappe.db.get_value("Item", { name: frm.doc.item }, ["shelf_life_in_days", "has_expiry_date"], (r) => { if (r.has_expiry_date && r.shelf_life_in_days) { // Calculate expiry date based on shelf_life_in_days - frm.set_value('expiry_date', frappe.datetime.add_days(frm.doc.manufacturing_date, r.shelf_life_in_days)); + frm.set_value( + "expiry_date", + frappe.datetime.add_days(frm.doc.manufacturing_date, r.shelf_life_in_days) + ); } - }) -}) + }); +}); diff --git a/erpnext/stock/doctype/batch/batch_list.js b/erpnext/stock/doctype/batch/batch_list.js index 0de9fd01503..2060d6e8763 100644 --- a/erpnext/stock/doctype/batch/batch_list.js +++ b/erpnext/stock/doctype/batch/batch_list.js @@ -1,14 +1,21 @@ -frappe.listview_settings['Batch'] = { +frappe.listview_settings["Batch"] = { add_fields: ["item", "expiry_date", "batch_qty", "disabled"], get_indicator: (doc) => { if (doc.disabled) { return [__("Disabled"), "gray", "disabled,=,1"]; } else if (!doc.batch_qty) { return [__("Empty"), "gray", "batch_qty,=,0|disabled,=,0"]; - } else if (doc.expiry_date && frappe.datetime.get_diff(doc.expiry_date, frappe.datetime.nowdate()) <= 0) { - return [__("Expired"), "red", "expiry_date,not in,|expiry_date,<=,Today|batch_qty,>,0|disabled,=,0"] + } else if ( + doc.expiry_date && + frappe.datetime.get_diff(doc.expiry_date, frappe.datetime.nowdate()) <= 0 + ) { + return [ + __("Expired"), + "red", + "expiry_date,not in,|expiry_date,<=,Today|batch_qty,>,0|disabled,=,0", + ]; } else { return [__("Active"), "green", "batch_qty,>,0|disabled,=,0"]; - }; - } + } + }, }; diff --git a/erpnext/stock/doctype/bin/bin.js b/erpnext/stock/doctype/bin/bin.js index 40411b68b48..02ff8b62396 100644 --- a/erpnext/stock/doctype/bin/bin.js +++ b/erpnext/stock/doctype/bin/bin.js @@ -1,8 +1,6 @@ // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Bin', { - refresh: function(frm) { - - } +frappe.ui.form.on("Bin", { + refresh: function (frm) {}, }); diff --git a/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.js b/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.js index 5c807a80a04..0f0221fa562 100644 --- a/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.js +++ b/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.js @@ -16,9 +16,9 @@ frappe.ui.form.on("Closing Stock Balance", { freeze: true, callback: () => { frm.reload_doc(); - } - }) - }) + }, + }); + }); } }, @@ -31,9 +31,9 @@ frappe.ui.form.on("Closing Stock Balance", { freeze: true, callback: () => { frm.reload_doc(); - } - }) - }) + }, + }); + }); } - } + }, }); diff --git a/erpnext/stock/doctype/customs_tariff_number/customs_tariff_number.js b/erpnext/stock/doctype/customs_tariff_number/customs_tariff_number.js index a39414c8363..bb60ee7e368 100644 --- a/erpnext/stock/doctype/customs_tariff_number/customs_tariff_number.js +++ b/erpnext/stock/doctype/customs_tariff_number/customs_tariff_number.js @@ -1,8 +1,6 @@ // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Customs Tariff Number', { - refresh: function(frm) { - - } +frappe.ui.form.on("Customs Tariff Number", { + refresh: function (frm) {}, }); diff --git a/erpnext/stock/doctype/delivery_note/delivery_note_list.js b/erpnext/stock/doctype/delivery_note/delivery_note_list.js index 6ff3ed3e8e5..c6b98c4134c 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note_list.js +++ b/erpnext/stock/doctype/delivery_note/delivery_note_list.js @@ -1,8 +1,18 @@ -frappe.listview_settings['Delivery Note'] = { - add_fields: ["customer", "customer_name", "base_grand_total", "per_installed", "per_billed", - "transporter_name", "grand_total", "is_return", "status", "currency"], - get_indicator: function(doc) { - if(cint(doc.is_return)==1) { +frappe.listview_settings["Delivery Note"] = { + add_fields: [ + "customer", + "customer_name", + "base_grand_total", + "per_installed", + "per_billed", + "transporter_name", + "grand_total", + "is_return", + "status", + "currency", + ], + get_indicator: function (doc) { + if (cint(doc.is_return) == 1) { return [__("Return"), "gray", "is_return,=,Yes"]; } else if (doc.status === "Closed") { return [__("Closed"), "green", "status,=,Closed"]; @@ -24,46 +34,45 @@ frappe.listview_settings['Delivery Note'] = { if (!doc.docstatus) { frappe.throw(__("Cannot create a Delivery Trip from Draft documents.")); } - }; + } - frappe.new_doc("Delivery Trip") - .then(() => { - // Empty out the child table before inserting new ones - cur_frm.set_value("delivery_stops", []); + frappe.new_doc("Delivery Trip").then(() => { + // Empty out the child table before inserting new ones + cur_frm.set_value("delivery_stops", []); - // We don't want to use `map_current_doc` since it brings up - // the dialog to select more items. We just want the mapper - // function to be called. - frappe.call({ - type: "POST", - method: "frappe.model.mapper.map_docs", - args: { - "method": "erpnext.stock.doctype.delivery_note.delivery_note.make_delivery_trip", - "source_names": docnames, - "target_doc": cur_frm.doc - }, - callback: function (r) { - if (!r.exc) { - frappe.model.sync(r.message); - cur_frm.dirty(); - cur_frm.refresh(); - } + // We don't want to use `map_current_doc` since it brings up + // the dialog to select more items. We just want the mapper + // function to be called. + frappe.call({ + type: "POST", + method: "frappe.model.mapper.map_docs", + args: { + method: "erpnext.stock.doctype.delivery_note.delivery_note.make_delivery_trip", + source_names: docnames, + target_doc: cur_frm.doc, + }, + callback: function (r) { + if (!r.exc) { + frappe.model.sync(r.message); + cur_frm.dirty(); + cur_frm.refresh(); } - }); - }) - }; + }, + }); + }); + } }; // doclist.page.add_actions_menu_item(__('Create Delivery Trip'), action, false); - doclist.page.add_action_item(__('Create Delivery Trip'), action); + doclist.page.add_action_item(__("Create Delivery Trip"), action); - doclist.page.add_action_item(__("Sales Invoice"), ()=>{ + doclist.page.add_action_item(__("Sales Invoice"), () => { erpnext.bulk_transaction_processing.create(doclist, "Delivery Note", "Sales Invoice"); }); - doclist.page.add_action_item(__("Packaging Slip From Delivery Note"), ()=>{ + doclist.page.add_action_item(__("Packaging Slip From Delivery Note"), () => { erpnext.bulk_transaction_processing.create(doclist, "Delivery Note", "Packing Slip"); }); - } + }, }; diff --git a/erpnext/stock/doctype/delivery_settings/delivery_settings.js b/erpnext/stock/doctype/delivery_settings/delivery_settings.js index 03aa1922ff5..4328158e6d9 100644 --- a/erpnext/stock/doctype/delivery_settings/delivery_settings.js +++ b/erpnext/stock/doctype/delivery_settings/delivery_settings.js @@ -1,8 +1,6 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Delivery Settings', { - refresh: function(frm) { - - } +frappe.ui.form.on("Delivery Settings", { + refresh: function (frm) {}, }); diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip.js b/erpnext/stock/doctype/delivery_trip/delivery_trip.js index 4ba5b32ca62..4f8649c0bfa 100755 --- a/erpnext/stock/doctype/delivery_trip/delivery_trip.js +++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.js @@ -1,15 +1,15 @@ // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Delivery Trip', { +frappe.ui.form.on("Delivery Trip", { setup: function (frm) { - frm.set_indicator_formatter('customer', (stop) => (stop.visited) ? "green" : "orange"); + frm.set_indicator_formatter("customer", (stop) => (stop.visited ? "green" : "orange")); frm.set_query("driver", function () { return { filters: { - "status": "Active" - } + status: "Active", + }, }; }); @@ -17,58 +17,71 @@ frappe.ui.form.on('Delivery Trip', { var row = locals[cdt][cdn]; if (row.customer) { return { - query: 'frappe.contacts.doctype.address.address.address_query', + query: "frappe.contacts.doctype.address.address.address_query", filters: { link_doctype: "Customer", - link_name: row.customer - } + link_name: row.customer, + }, }; } - }) + }); frm.set_query("contact", "delivery_stops", function (doc, cdt, cdn) { var row = locals[cdt][cdn]; if (row.customer) { return { - query: 'frappe.contacts.doctype.contact.contact.contact_query', + query: "frappe.contacts.doctype.contact.contact.contact_query", filters: { link_doctype: "Customer", - link_name: row.customer - } + link_name: row.customer, + }, }; } - }) + }); }, refresh: function (frm) { if (frm.doc.docstatus == 1 && frm.doc.delivery_stops.length > 0) { frm.add_custom_button(__("Notify Customers via Email"), function () { - frm.trigger('notify_customers'); + frm.trigger("notify_customers"); }); } if (frm.doc.docstatus === 0) { - frm.add_custom_button(__('Delivery Note'), () => { - erpnext.utils.map_current_doc({ - method: "erpnext.stock.doctype.delivery_note.delivery_note.make_delivery_trip", - source_doctype: "Delivery Note", - target: frm, - date_field: "posting_date", - setters: { - company: frm.doc.company, - }, - get_query_filters: { - docstatus: 1, - company: frm.doc.company, - } - }) - }, __("Get stops from")); - } - frm.add_custom_button(__("Delivery Notes"), function () { - frappe.set_route("List", "Delivery Note", - {'name': ["in", frm.doc.delivery_stops.map((stop) => {return stop.delivery_note;})]} + frm.add_custom_button( + __("Delivery Note"), + () => { + erpnext.utils.map_current_doc({ + method: "erpnext.stock.doctype.delivery_note.delivery_note.make_delivery_trip", + source_doctype: "Delivery Note", + target: frm, + date_field: "posting_date", + setters: { + company: frm.doc.company, + }, + get_query_filters: { + docstatus: 1, + company: frm.doc.company, + }, + }); + }, + __("Get stops from") ); - }, __("View")); + } + frm.add_custom_button( + __("Delivery Notes"), + function () { + frappe.set_route("List", "Delivery Note", { + name: [ + "in", + frm.doc.delivery_stops.map((stop) => { + return stop.delivery_note; + }), + ], + }); + }, + __("View") + ); }, calculate_arrival_time: function (frm) { @@ -77,13 +90,17 @@ frappe.ui.form.on('Delivery Trip', { } frappe.show_alert({ message: "Calculating Arrival Times", - indicator: 'orange' - }); - frm.call("process_route", { - optimize: false, - }, () => { - frm.reload_doc(); + indicator: "orange", }); + frm.call( + "process_route", + { + optimize: false, + }, + () => { + frm.reload_doc(); + } + ); }, driver: function (frm) { @@ -91,13 +108,13 @@ frappe.ui.form.on('Delivery Trip', { frappe.call({ method: "erpnext.stock.doctype.delivery_trip.delivery_trip.get_driver_email", args: { - driver: frm.doc.driver + driver: frm.doc.driver, }, callback: (data) => { frm.set_value("driver_email", data.message.email); - } + }, }); - }; + } }, optimize_route: function (frm) { @@ -106,23 +123,27 @@ frappe.ui.form.on('Delivery Trip', { } frappe.show_alert({ message: "Optimizing Route", - indicator: 'orange' - }); - frm.call("process_route", { - optimize: true, - }, () => { - frm.reload_doc(); + indicator: "orange", }); + frm.call( + "process_route", + { + optimize: true, + }, + () => { + frm.reload_doc(); + } + ); }, notify_customers: function (frm) { $.each(frm.doc.delivery_stops || [], function (i, delivery_stop) { if (!delivery_stop.delivery_note) { frappe.msgprint({ - "message": __("No Delivery Note selected for Customer {}", [delivery_stop.customer]), - "title": __("Warning"), - "indicator": "orange", - "alert": 1 + message: __("No Delivery Note selected for Customer {}", [delivery_stop.customer]), + title: __("Warning"), + indicator: "orange", + alert: 1, }); } }); @@ -135,48 +156,45 @@ frappe.ui.form.on('Delivery Trip', { frappe.call({ method: "erpnext.stock.doctype.delivery_trip.delivery_trip.notify_customers", args: { - "delivery_trip": frm.doc.name + delivery_trip: frm.doc.name, }, callback: function (r) { if (!r.exc) { frm.doc.email_notification_sent = true; - frm.refresh_field('email_notification_sent'); + frm.refresh_field("email_notification_sent"); } - } + }, }); }); } }); - } + }, }); -frappe.ui.form.on('Delivery Stop', { +frappe.ui.form.on("Delivery Stop", { customer: function (frm, cdt, cdn) { var row = locals[cdt][cdn]; if (row.customer) { frappe.call({ method: "erpnext.stock.doctype.delivery_trip.delivery_trip.get_contact_and_address", - args: { "name": row.customer }, + args: { name: row.customer }, callback: function (r) { if (r.message) { if (r.message["shipping_address"]) { frappe.model.set_value(cdt, cdn, "address", r.message["shipping_address"].parent); - } - else { - frappe.model.set_value(cdt, cdn, "address", ''); + } else { + frappe.model.set_value(cdt, cdn, "address", ""); } if (r.message["contact_person"]) { frappe.model.set_value(cdt, cdn, "contact", r.message["contact_person"].parent); + } else { + frappe.model.set_value(cdt, cdn, "contact", ""); } - else { - frappe.model.set_value(cdt, cdn, "contact", ''); - } + } else { + frappe.model.set_value(cdt, cdn, "address", ""); + frappe.model.set_value(cdt, cdn, "contact", ""); } - else { - frappe.model.set_value(cdt, cdn, "address", ''); - frappe.model.set_value(cdt, cdn, "contact", ''); - } - } + }, }); } }, @@ -186,12 +204,12 @@ frappe.ui.form.on('Delivery Stop', { if (row.address) { frappe.call({ method: "frappe.contacts.doctype.address.address.get_address_display", - args: { "address_dict": row.address }, + args: { address_dict: row.address }, callback: function (r) { if (r.message) { frappe.model.set_value(cdt, cdn, "customer_address", r.message); } - } + }, }); } else { frappe.model.set_value(cdt, cdn, "customer_address", ""); @@ -203,15 +221,15 @@ frappe.ui.form.on('Delivery Stop', { if (row.contact) { frappe.call({ method: "erpnext.stock.doctype.delivery_trip.delivery_trip.get_contact_display", - args: { "contact": row.contact }, + args: { contact: row.contact }, callback: function (r) { if (r.message) { frappe.model.set_value(cdt, cdn, "customer_contact", r.message); } - } + }, }); } else { frappe.model.set_value(cdt, cdn, "customer_contact", ""); } - } + }, }); diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip_list.js b/erpnext/stock/doctype/delivery_trip/delivery_trip_list.js index 1d198b733fb..230107caadb 100644 --- a/erpnext/stock/doctype/delivery_trip/delivery_trip_list.js +++ b/erpnext/stock/doctype/delivery_trip/delivery_trip_list.js @@ -1,4 +1,4 @@ -frappe.listview_settings['Delivery Trip'] = { +frappe.listview_settings["Delivery Trip"] = { add_fields: ["status"], get_indicator: function (doc) { if (in_list(["Cancelled", "Draft"], doc.status)) { @@ -8,5 +8,5 @@ frappe.listview_settings['Delivery Trip'] = { } else if (doc.status === "Completed") { return [__(doc.status), "green", "status,=," + doc.status]; } - } + }, }; diff --git a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.js b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.js index 35d1c02719c..c819d17b1e7 100644 --- a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.js +++ b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.js @@ -1,43 +1,59 @@ // Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Inventory Dimension', { +frappe.ui.form.on("Inventory Dimension", { setup(frm) { - frm.trigger('set_query_on_fields'); + frm.trigger("set_query_on_fields"); }, set_query_on_fields(frm) { - frm.set_query('reference_document', () => { + frm.set_query("reference_document", () => { let invalid_doctypes = frappe.model.core_doctypes_list; - invalid_doctypes.push('Batch', 'Serial No', 'Warehouse', 'Item', 'Inventory Dimension', - 'Accounting Dimension', 'Accounting Dimension Filter'); + invalid_doctypes.push( + "Batch", + "Serial No", + "Warehouse", + "Item", + "Inventory Dimension", + "Accounting Dimension", + "Accounting Dimension Filter" + ); return { filters: { - 'istable': 0, - 'issingle': 0, - 'name': ['not in', invalid_doctypes] - } + istable: 0, + issingle: 0, + name: ["not in", invalid_doctypes], + }, }; }); - frm.set_query('document_type', () => { + frm.set_query("document_type", () => { return { - query: 'erpnext.stock.doctype.inventory_dimension.inventory_dimension.get_inventory_documents', + query: "erpnext.stock.doctype.inventory_dimension.inventory_dimension.get_inventory_documents", }; }); }, onload(frm) { - frm.trigger('render_traget_field'); + frm.trigger("render_traget_field"); frm.trigger("set_parent_fields"); }, refresh(frm) { - if (frm.doc.__onload && frm.doc.__onload.has_stock_ledger - && frm.doc.__onload.has_stock_ledger.length) { - let allow_to_edit_fields = ['disabled', 'fetch_from_parent', - 'type_of_transaction', 'condition', 'mandatory_depends_on', 'validate_negative_stock']; + if ( + frm.doc.__onload && + frm.doc.__onload.has_stock_ledger && + frm.doc.__onload.has_stock_ledger.length + ) { + let allow_to_edit_fields = [ + "disabled", + "fetch_from_parent", + "type_of_transaction", + "condition", + "mandatory_depends_on", + "validate_negative_stock", + ]; frm.fields.forEach((field) => { if (!in_list(allow_to_edit_fields, field.df.fieldname)) { @@ -47,8 +63,8 @@ frappe.ui.form.on('Inventory Dimension', { } if (!frm.is_new()) { - frm.add_custom_button(__('Delete Dimension'), () => { - frm.trigger('delete_dimension'); + frm.add_custom_button(__("Delete Dimension"), () => { + frm.trigger("delete_dimension"); }); } }, @@ -62,39 +78,38 @@ frappe.ui.form.on('Inventory Dimension', { frm.set_df_property("fetch_from_parent", "options", frm.doc.reference_document); } else if (frm.doc.document_type && frm.doc.istable) { frappe.call({ - method: 'erpnext.stock.doctype.inventory_dimension.inventory_dimension.get_parent_fields', + method: "erpnext.stock.doctype.inventory_dimension.inventory_dimension.get_parent_fields", args: { child_doctype: frm.doc.document_type, - dimension_name: frm.doc.reference_document + dimension_name: frm.doc.reference_document, }, callback: (r) => { if (r.message && r.message.length) { - frm.set_df_property("fetch_from_parent", "options", - [""].concat(r.message)); + frm.set_df_property("fetch_from_parent", "options", [""].concat(r.message)); } else { frm.set_df_property("fetch_from_parent", "hidden", 1); } - } + }, }); } }, delete_dimension(frm) { - let msg = (` + let msg = ` Custom fields related to this dimension will be deleted on deletion of dimension.
              Do you want to delete {0} dimension? - `); + `; frappe.confirm(__(msg, [frm.doc.name.bold()]), () => { frappe.call({ - method: 'erpnext.stock.doctype.inventory_dimension.inventory_dimension.delete_dimension', + method: "erpnext.stock.doctype.inventory_dimension.inventory_dimension.delete_dimension", args: { - dimension: frm.doc.name + dimension: frm.doc.name, + }, + callback: function () { + frappe.set_route("List", "Inventory Dimension"); }, - callback: function() { - frappe.set_route('List', 'Inventory Dimension'); - } }); }); - } + }, }); diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js index 1b71017bd4d..29d41fe7625 100644 --- a/erpnext/stock/doctype/item/item.js +++ b/erpnext/stock/doctype/item/item.js @@ -3,48 +3,47 @@ frappe.provide("erpnext.item"); -const SALES_DOCTYPES = ['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice']; -const PURCHASE_DOCTYPES = ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice']; +const SALES_DOCTYPES = ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice"]; +const PURCHASE_DOCTYPES = ["Purchase Order", "Purchase Receipt", "Purchase Invoice"]; frappe.ui.form.on("Item", { - setup: function(frm) { - frm.add_fetch('attribute', 'numeric_values', 'numeric_values'); - frm.add_fetch('attribute', 'from_range', 'from_range'); - frm.add_fetch('attribute', 'to_range', 'to_range'); - frm.add_fetch('attribute', 'increment', 'increment'); - frm.add_fetch('tax_type', 'tax_rate', 'tax_rate'); + setup: function (frm) { + frm.add_fetch("attribute", "numeric_values", "numeric_values"); + frm.add_fetch("attribute", "from_range", "from_range"); + frm.add_fetch("attribute", "to_range", "to_range"); + frm.add_fetch("attribute", "increment", "increment"); + frm.add_fetch("tax_type", "tax_rate", "tax_rate"); frm.make_methods = { - 'Sales Order': () => { + "Sales Order": () => { open_form(frm, "Sales Order", "Sales Order Item", "items"); }, - 'Delivery Note': () => { + "Delivery Note": () => { open_form(frm, "Delivery Note", "Delivery Note Item", "items"); }, - 'Sales Invoice': () => { + "Sales Invoice": () => { open_form(frm, "Sales Invoice", "Sales Invoice Item", "items"); }, - 'Purchase Order': () => { + "Purchase Order": () => { open_form(frm, "Purchase Order", "Purchase Order Item", "items"); }, - 'Purchase Receipt': () => { + "Purchase Receipt": () => { open_form(frm, "Purchase Receipt", "Purchase Receipt Item", "items"); }, - 'Purchase Invoice': () => { + "Purchase Invoice": () => { open_form(frm, "Purchase Invoice", "Purchase Invoice Item", "items"); }, - 'Material Request': () => { + "Material Request": () => { open_form(frm, "Material Request", "Material Request Item", "items"); }, - 'Stock Entry': () => { + "Stock Entry": () => { open_form(frm, "Stock Entry", "Stock Entry Detail", "items"); }, }; - }, - onload: function(frm) { + onload: function (frm) { erpnext.item.setup_queries(frm); - if (frm.doc.variant_of){ + if (frm.doc.variant_of) { frm.fields_dict["attributes"].grid.set_column_disp("attribute_value", true); } @@ -53,106 +52,161 @@ frappe.ui.form.on("Item", { } }, - refresh: function(frm) { + refresh: function (frm) { if (frm.doc.is_stock_item) { - frm.add_custom_button(__("Stock Balance"), function() { - frappe.route_options = { - "item_code": frm.doc.name - } - frappe.set_route("query-report", "Stock Balance"); - }, __("View")); - frm.add_custom_button(__("Stock Ledger"), function() { - frappe.route_options = { - "item_code": frm.doc.name - } - frappe.set_route("query-report", "Stock Ledger"); - }, __("View")); - frm.add_custom_button(__("Stock Projected Qty"), function() { - frappe.route_options = { - "item_code": frm.doc.name - } - frappe.set_route("query-report", "Stock Projected Qty"); - }, __("View")); + frm.add_custom_button( + __("Stock Balance"), + function () { + frappe.route_options = { + item_code: frm.doc.name, + }; + frappe.set_route("query-report", "Stock Balance"); + }, + __("View") + ); + frm.add_custom_button( + __("Stock Ledger"), + function () { + frappe.route_options = { + item_code: frm.doc.name, + }; + frappe.set_route("query-report", "Stock Ledger"); + }, + __("View") + ); + frm.add_custom_button( + __("Stock Projected Qty"), + function () { + frappe.route_options = { + item_code: frm.doc.name, + }; + frappe.set_route("query-report", "Stock Projected Qty"); + }, + __("View") + ); } - if (frm.doc.is_fixed_asset) { - frm.trigger('is_fixed_asset'); - frm.trigger('auto_create_assets'); + frm.trigger("is_fixed_asset"); + frm.trigger("auto_create_assets"); } // clear intro frm.set_intro(); if (frm.doc.has_variants) { - frm.set_intro(__("This Item is a Template and cannot be used in transactions. Item attributes will be copied over into the variants unless 'No Copy' is set"), true); + frm.set_intro( + __( + "This Item is a Template and cannot be used in transactions. Item attributes will be copied over into the variants unless 'No Copy' is set" + ), + true + ); - frm.add_custom_button(__("Show Variants"), function() { - frappe.set_route("List", "Item", {"variant_of": frm.doc.name}); - }, __("View")); + frm.add_custom_button( + __("Show Variants"), + function () { + frappe.set_route("List", "Item", { variant_of: frm.doc.name }); + }, + __("View") + ); - frm.add_custom_button(__("Item Variant Settings"), function() { - frappe.set_route("Form", "Item Variant Settings"); - }, __("View")); + frm.add_custom_button( + __("Item Variant Settings"), + function () { + frappe.set_route("Form", "Item Variant Settings"); + }, + __("View") + ); - frm.add_custom_button(__("Variant Details Report"), function() { - frappe.set_route("query-report", "Item Variant Details", {"item": frm.doc.name}); - }, __("View")); + frm.add_custom_button( + __("Variant Details Report"), + function () { + frappe.set_route("query-report", "Item Variant Details", { item: frm.doc.name }); + }, + __("View") + ); - if(frm.doc.variant_based_on==="Item Attribute") { - frm.add_custom_button(__("Single Variant"), function() { - erpnext.item.show_single_variant_dialog(frm); - }, __('Create')); - frm.add_custom_button(__("Multiple Variants"), function() { - erpnext.item.show_multiple_variants_dialog(frm); - }, __('Create')); + if (frm.doc.variant_based_on === "Item Attribute") { + frm.add_custom_button( + __("Single Variant"), + function () { + erpnext.item.show_single_variant_dialog(frm); + }, + __("Create") + ); + frm.add_custom_button( + __("Multiple Variants"), + function () { + erpnext.item.show_multiple_variants_dialog(frm); + }, + __("Create") + ); } else { - frm.add_custom_button(__("Variant"), function() { - erpnext.item.show_modal_for_manufacturers(frm); - }, __('Create')); + frm.add_custom_button( + __("Variant"), + function () { + erpnext.item.show_modal_for_manufacturers(frm); + }, + __("Create") + ); } // frm.page.set_inner_btn_group_as_primary(__('Create')); } if (frm.doc.variant_of) { - frm.set_intro(__('This Item is a Variant of {0} (Template).', - [`${frm.doc.variant_of}`]), true); + frm.set_intro( + __("This Item is a Variant of {0} (Template).", [ + `${frm.doc.variant_of}`, + ]), + true + ); } - if (frappe.defaults.get_default("item_naming_by")!="Naming Series" || frm.doc.variant_of) { + if (frappe.defaults.get_default("item_naming_by") != "Naming Series" || frm.doc.variant_of) { frm.toggle_display("naming_series", false); } else { erpnext.toggle_naming_series(); } if (!frm.doc.published_in_website) { - frm.add_custom_button(__("Publish in Website"), function() { - frappe.call({ - method: "erpnext.e_commerce.doctype.website_item.website_item.make_website_item", - args: {doc: frm.doc}, - freeze: true, - freeze_message: __("Publishing Item ..."), - callback: function(result) { - frappe.msgprint({ - message: __("Website Item {0} has been created.", - [repl('%(item)s', { - item_encoded: encodeURIComponent(result.message[0]), - item: result.message[1] - })] - ), - title: __("Published"), - indicator: "green" - }); - } - }); - }, __('Actions')); + frm.add_custom_button( + __("Publish in Website"), + function () { + frappe.call({ + method: "erpnext.e_commerce.doctype.website_item.website_item.make_website_item", + args: { doc: frm.doc }, + freeze: true, + freeze_message: __("Publishing Item ..."), + callback: function (result) { + frappe.msgprint({ + message: __("Website Item {0} has been created.", [ + repl( + '%(item)s', + { + item_encoded: encodeURIComponent(result.message[0]), + item: result.message[1], + } + ), + ]), + title: __("Published"), + indicator: "green", + }); + }, + }); + }, + __("Actions") + ); } else { - frm.add_custom_button(__("Website Item"), function() { - frappe.db.get_value("Website Item", {item_code: frm.doc.name}, "name", (d) => { - if (!d.name) frappe.throw(__("Website Item not found")); - frappe.set_route("Form", "Website Item", d.name); - }); - }, __("View")); + frm.add_custom_button( + __("Website Item"), + function () { + frappe.db.get_value("Website Item", { item_code: frm.doc.name }, "name", (d) => { + if (!d.name) frappe.throw(__("Website Item not found")); + frappe.set_route("Form", "Website Item", d.name); + }); + }, + __("View") + ); } erpnext.item.edit_prices_button(frm); @@ -162,341 +216,374 @@ frappe.ui.form.on("Item", { erpnext.item.make_dashboard(frm); } - frm.add_custom_button(__('Duplicate'), function() { + frm.add_custom_button(__("Duplicate"), function () { var new_item = frappe.model.copy_doc(frm.doc); // Duplicate item could have different name, causing "copy paste" error. - if (new_item.item_name===new_item.item_code) { + if (new_item.item_name === new_item.item_code) { new_item.item_name = null; } - if (new_item.item_code===new_item.description || new_item.item_code===new_item.description) { + if (new_item.item_code === new_item.description || new_item.item_code === new_item.description) { new_item.description = null; } - frappe.set_route('Form', 'Item', new_item.name); + frappe.set_route("Form", "Item", new_item.name); }); - const stock_exists = (frm.doc.__onload - && frm.doc.__onload.stock_exists) ? 1 : 0; + const stock_exists = frm.doc.__onload && frm.doc.__onload.stock_exists ? 1 : 0; - ['is_stock_item', 'has_serial_no', 'has_batch_no', 'has_variants'].forEach((fieldname) => { - frm.set_df_property(fieldname, 'read_only', stock_exists); + ["is_stock_item", "has_serial_no", "has_batch_no", "has_variants"].forEach((fieldname) => { + frm.set_df_property(fieldname, "read_only", stock_exists); }); - frm.toggle_reqd('customer', frm.doc.is_customer_provided_item ? 1:0); + frm.toggle_reqd("customer", frm.doc.is_customer_provided_item ? 1 : 0); }, - validate: function(frm){ + validate: function (frm) { erpnext.item.weight_to_validate(frm); }, - image: function() { + image: function () { refresh_field("image_view"); }, - is_customer_provided_item: function(frm) { - frm.toggle_reqd('customer', frm.doc.is_customer_provided_item ? 1:0); + is_customer_provided_item: function (frm) { + frm.toggle_reqd("customer", frm.doc.is_customer_provided_item ? 1 : 0); }, - is_fixed_asset: function(frm) { + is_fixed_asset: function (frm) { // set serial no to false & toggles its visibility - frm.set_value('has_serial_no', 0); - frm.set_value('has_batch_no', 0); - frm.toggle_enable(['has_serial_no', 'serial_no_series'], !frm.doc.is_fixed_asset); + frm.set_value("has_serial_no", 0); + frm.set_value("has_batch_no", 0); + frm.toggle_enable(["has_serial_no", "serial_no_series"], !frm.doc.is_fixed_asset); frappe.call({ method: "erpnext.stock.doctype.item.item.get_asset_naming_series", - callback: function(r) { + callback: function (r) { frm.set_value("is_stock_item", frm.doc.is_fixed_asset ? 0 : 1); frm.events.set_asset_naming_series(frm, r.message); - } + }, }); - frm.trigger('auto_create_assets'); + frm.trigger("auto_create_assets"); }, - set_asset_naming_series: function(frm, asset_naming_series) { + set_asset_naming_series: function (frm, asset_naming_series) { if ((frm.doc.__onload && frm.doc.__onload.asset_naming_series) || asset_naming_series) { - let naming_series = (frm.doc.__onload && frm.doc.__onload.asset_naming_series) || asset_naming_series; + let naming_series = + (frm.doc.__onload && frm.doc.__onload.asset_naming_series) || asset_naming_series; frm.set_df_property("asset_naming_series", "options", naming_series); } }, - auto_create_assets: function(frm) { - frm.toggle_reqd(['asset_naming_series'], frm.doc.auto_create_assets); - frm.toggle_display(['asset_naming_series'], frm.doc.auto_create_assets); + auto_create_assets: function (frm) { + frm.toggle_reqd(["asset_naming_series"], frm.doc.auto_create_assets); + frm.toggle_display(["asset_naming_series"], frm.doc.auto_create_assets); }, page_name: frappe.utils.warn_page_name_change, - item_code: function(frm) { - if(!frm.doc.item_name) - frm.set_value("item_name", frm.doc.item_code); + item_code: function (frm) { + if (!frm.doc.item_name) frm.set_value("item_name", frm.doc.item_code); }, - is_stock_item: function(frm) { - if(!frm.doc.is_stock_item) { + is_stock_item: function (frm) { + if (!frm.doc.is_stock_item) { frm.set_value("has_batch_no", 0); frm.set_value("create_new_batch", 0); frm.set_value("has_serial_no", 0); } }, - has_variants: function(frm) { + has_variants: function (frm) { erpnext.item.toggle_attributes(frm); - } + }, }); -frappe.ui.form.on('Item Reorder', { - reorder_levels_add: function(frm, cdt, cdn) { +frappe.ui.form.on("Item Reorder", { + reorder_levels_add: function (frm, cdt, cdn) { var row = frappe.get_doc(cdt, cdn); - var type = frm.doc.default_material_request_type - row.material_request_type = (type == 'Material Transfer')? 'Transfer' : type; - } -}) - -frappe.ui.form.on('Item Customer Detail', { - customer_items_add: function(frm, cdt, cdn) { - frappe.model.set_value(cdt, cdn, 'customer_group', ""); + var type = frm.doc.default_material_request_type; + row.material_request_type = type == "Material Transfer" ? "Transfer" : type; }, - customer_name: function(frm, cdt, cdn) { +}); + +frappe.ui.form.on("Item Customer Detail", { + customer_items_add: function (frm, cdt, cdn) { + frappe.model.set_value(cdt, cdn, "customer_group", ""); + }, + customer_name: function (frm, cdt, cdn) { set_customer_group(frm, cdt, cdn); }, - customer_group: function(frm, cdt, cdn) { - if(set_customer_group(frm, cdt, cdn)){ + customer_group: function (frm, cdt, cdn) { + if (set_customer_group(frm, cdt, cdn)) { frappe.msgprint(__("Changing Customer Group for the selected Customer is not allowed.")); } - } + }, }); -var set_customer_group = function(frm, cdt, cdn) { +var set_customer_group = function (frm, cdt, cdn) { var row = frappe.get_doc(cdt, cdn); if (!row.customer_name) { return false; } - frappe.model.with_doc("Customer", row.customer_name, function() { + frappe.model.with_doc("Customer", row.customer_name, function () { var customer = frappe.model.get_doc("Customer", row.customer_name); row.customer_group = customer.customer_group; refresh_field("customer_group", cdn, "customer_items"); }); return true; -} +}; $.extend(erpnext.item, { - setup_queries: function(frm) { - frm.fields_dict["item_defaults"].grid.get_field("expense_account").get_query = function(doc, cdt, cdn) { + setup_queries: function (frm) { + frm.fields_dict["item_defaults"].grid.get_field("expense_account").get_query = function ( + doc, + cdt, + cdn + ) { const row = locals[cdt][cdn]; return { query: "erpnext.controllers.queries.get_expense_account", - filters: { company: row.company } - } - } - - frm.fields_dict["item_defaults"].grid.get_field("income_account").get_query = function(doc, cdt, cdn) { - const row = locals[cdt][cdn]; - return { - query: "erpnext.controllers.queries.get_income_account", - filters: { company: row.company } - } - } - - frm.fields_dict["item_defaults"].grid.get_field("default_discount_account").get_query = function(doc, cdt, cdn) { - const row = locals[cdt][cdn]; - return { - filters: { - 'report_type': 'Profit and Loss', - 'company': row.company, - "is_group": 0 - } + filters: { company: row.company }, }; }; - frm.fields_dict["item_defaults"].grid.get_field("buying_cost_center").get_query = function(doc, cdt, cdn) { + frm.fields_dict["item_defaults"].grid.get_field("income_account").get_query = function ( + doc, + cdt, + cdn + ) { + const row = locals[cdt][cdn]; + return { + query: "erpnext.controllers.queries.get_income_account", + filters: { company: row.company }, + }; + }; + + frm.fields_dict["item_defaults"].grid.get_field("default_discount_account").get_query = function ( + doc, + cdt, + cdn + ) { const row = locals[cdt][cdn]; return { filters: { - "is_group": 0, - "company": row.company - } - } - } + report_type: "Profit and Loss", + company: row.company, + is_group: 0, + }, + }; + }; - frm.fields_dict["item_defaults"].grid.get_field("selling_cost_center").get_query = function(doc, cdt, cdn) { + frm.fields_dict["item_defaults"].grid.get_field("buying_cost_center").get_query = function ( + doc, + cdt, + cdn + ) { const row = locals[cdt][cdn]; return { filters: { - "is_group": 0, - "company": row.company - } - } - } + is_group: 0, + company: row.company, + }, + }; + }; + frm.fields_dict["item_defaults"].grid.get_field("selling_cost_center").get_query = function ( + doc, + cdt, + cdn + ) { + const row = locals[cdt][cdn]; + return { + filters: { + is_group: 0, + company: row.company, + }, + }; + }; - frm.fields_dict['taxes'].grid.get_field("tax_type").get_query = function(doc, cdt, cdn) { + frm.fields_dict["taxes"].grid.get_field("tax_type").get_query = function (doc, cdt, cdn) { return { filters: [ - ['Account', 'account_type', 'in', - 'Tax, Chargeable, Income Account, Expense Account'], - ['Account', 'docstatus', '!=', 2] - ] - } - } + ["Account", "account_type", "in", "Tax, Chargeable, Income Account, Expense Account"], + ["Account", "docstatus", "!=", 2], + ], + }; + }; - frm.fields_dict['item_group'].get_query = function(doc, cdt, cdn) { + frm.fields_dict["item_group"].get_query = function (doc, cdt, cdn) { return { - filters: [ - ['Item Group', 'docstatus', '!=', 2] - ] - } - } + filters: [["Item Group", "docstatus", "!=", 2]], + }; + }; - frm.fields_dict["item_defaults"].grid.get_field("deferred_revenue_account").get_query = function(doc, cdt, cdn) { + frm.fields_dict["item_defaults"].grid.get_field("deferred_revenue_account").get_query = function ( + doc, + cdt, + cdn + ) { return { filters: { - "company": locals[cdt][cdn].company, - 'root_type': 'Liability', - "is_group": 0 - } - } - } + company: locals[cdt][cdn].company, + root_type: "Liability", + is_group: 0, + }, + }; + }; - frm.fields_dict["item_defaults"].grid.get_field("deferred_expense_account").get_query = function(doc, cdt, cdn) { + frm.fields_dict["item_defaults"].grid.get_field("deferred_expense_account").get_query = function ( + doc, + cdt, + cdn + ) { return { filters: { - "company": locals[cdt][cdn].company, - 'root_type': 'Asset', - "is_group": 0 - } - } - } + company: locals[cdt][cdn].company, + root_type: "Asset", + is_group: 0, + }, + }; + }; - frm.fields_dict.customer_items.grid.get_field("customer_name").get_query = function(doc, cdt, cdn) { - return { query: "erpnext.controllers.queries.customer_query" } - } + frm.fields_dict.customer_items.grid.get_field("customer_name").get_query = function (doc, cdt, cdn) { + return { query: "erpnext.controllers.queries.customer_query" }; + }; - frm.fields_dict.supplier_items.grid.get_field("supplier").get_query = function(doc, cdt, cdn) { - return { query: "erpnext.controllers.queries.supplier_query" } - } + frm.fields_dict.supplier_items.grid.get_field("supplier").get_query = function (doc, cdt, cdn) { + return { query: "erpnext.controllers.queries.supplier_query" }; + }; - frm.fields_dict["item_defaults"].grid.get_field("default_warehouse").get_query = function(doc, cdt, cdn) { + frm.fields_dict["item_defaults"].grid.get_field("default_warehouse").get_query = function ( + doc, + cdt, + cdn + ) { const row = locals[cdt][cdn]; return { filters: { - "is_group": 0, - "company": row.company - } - } - } + is_group: 0, + company: row.company, + }, + }; + }; - frm.fields_dict.reorder_levels.grid.get_field("warehouse_group").get_query = function(doc, cdt, cdn) { + frm.fields_dict.reorder_levels.grid.get_field("warehouse_group").get_query = function ( + doc, + cdt, + cdn + ) { return { - filters: { "is_group": 1 } - } - } + filters: { is_group: 1 }, + }; + }; - frm.fields_dict.reorder_levels.grid.get_field("warehouse").get_query = function(doc, cdt, cdn) { + frm.fields_dict.reorder_levels.grid.get_field("warehouse").get_query = function (doc, cdt, cdn) { var d = locals[cdt][cdn]; var filters = { - "is_group": 0 - } + is_group: 0, + }; if (d.parent_warehouse) { - filters.extend({"parent_warehouse": d.warehouse_group}) + filters.extend({ parent_warehouse: d.warehouse_group }); } return { - filters: filters - } - } + filters: filters, + }; + }; - frm.set_query('default_provisional_account', 'item_defaults', (doc, cdt, cdn) => { + frm.set_query("default_provisional_account", "item_defaults", (doc, cdt, cdn) => { let row = locals[cdt][cdn]; return { filters: { - "company": row.company, - "root_type": ["in", ["Liability", "Asset"]], - "is_group": 0 - } + company: row.company, + root_type: ["in", ["Liability", "Asset"]], + is_group: 0, + }, }; }); - }, - make_dashboard: function(frm) { - if(frm.doc.__islocal) - return; + make_dashboard: function (frm) { + if (frm.doc.__islocal) return; // Show Stock Levels only if is_stock_item if (frm.doc.is_stock_item) { - frappe.require('item-dashboard.bundle.js', function() { - const section = frm.dashboard.add_section('', __("Stock Levels")); + frappe.require("item-dashboard.bundle.js", function () { + const section = frm.dashboard.add_section("", __("Stock Levels")); erpnext.item.item_dashboard = new erpnext.stock.ItemDashboard({ parent: section, item_code: frm.doc.name, page_length: 20, - method: 'erpnext.stock.dashboard.item_dashboard.get_data', - template: 'item_dashboard_list' + method: "erpnext.stock.dashboard.item_dashboard.get_data", + template: "item_dashboard_list", }); erpnext.item.item_dashboard.refresh(); }); } }, - edit_prices_button: function(frm) { - frm.add_custom_button(__("Add / Edit Prices"), function() { - frappe.set_route("List", "Item Price", {"item_code": frm.doc.name}); - }, __("Actions")); + edit_prices_button: function (frm) { + frm.add_custom_button( + __("Add / Edit Prices"), + function () { + frappe.set_route("List", "Item Price", { item_code: frm.doc.name }); + }, + __("Actions") + ); }, - weight_to_validate: function(frm) { + weight_to_validate: function (frm) { if (frm.doc.weight_per_unit && !frm.doc.weight_uom) { frappe.msgprint({ message: __("Please mention 'Weight UOM' along with Weight."), - title: __("Note") + title: __("Note"), }); } }, - show_modal_for_manufacturers: function(frm) { + show_modal_for_manufacturers: function (frm) { var dialog = new frappe.ui.Dialog({ fields: [ { - fieldtype: 'Link', - fieldname: 'manufacturer', - options: 'Manufacturer', - label: 'Manufacturer', + fieldtype: "Link", + fieldname: "manufacturer", + options: "Manufacturer", + label: "Manufacturer", reqd: 1, }, { - fieldtype: 'Data', - label: 'Manufacturer Part Number', - fieldname: 'manufacturer_part_no' + fieldtype: "Data", + label: "Manufacturer Part Number", + fieldname: "manufacturer_part_no", }, - ] + ], }); - dialog.set_primary_action(__('Create'), function() { + dialog.set_primary_action(__("Create"), function () { var data = dialog.get_values(); - if(!data) return; + if (!data) return; // call the server to make the variant data.template = frm.doc.name; frappe.call({ method: "erpnext.controllers.item_variant.get_variant", args: data, - callback: function(r) { + callback: function (r) { var doclist = frappe.model.sync(r.message); dialog.hide(); frappe.set_route("Form", doclist[0].doctype, doclist[0].name); - } + }, }); - }) + }); dialog.show(); }, - show_multiple_variants_dialog: function(frm) { + show_multiple_variants_dialog: function (frm) { var me = this; let promises = []; @@ -505,27 +592,26 @@ $.extend(erpnext.item, { function make_fields_from_attribute_values(attr_dict) { let fields = []; Object.keys(attr_dict).forEach((name, i) => { - if(i % 3 === 0){ - fields.push({fieldtype: 'Section Break'}); + if (i % 3 === 0) { + fields.push({ fieldtype: "Section Break" }); } - fields.push({fieldtype: 'Column Break', label: name}); - attr_dict[name].forEach(value => { + fields.push({ fieldtype: "Column Break", label: name }); + attr_dict[name].forEach((value) => { fields.push({ - fieldtype: 'Check', + fieldtype: "Check", label: value, fieldname: value, default: 0, - onchange: function() { + onchange: function () { let selected_attributes = get_selected_attributes(); let lengths = []; - Object.keys(selected_attributes).map(key => { + Object.keys(selected_attributes).map((key) => { lengths.push(selected_attributes[key].length); }); - if(lengths.includes(0)) { - me.multiple_variant_dialog.get_primary_btn().html(__('Create Variants')); + if (lengths.includes(0)) { + me.multiple_variant_dialog.get_primary_btn().html(__("Create Variants")); me.multiple_variant_dialog.disable_primary_action(); } else { - let no_of_combinations = lengths.reduce((a, b) => a * b, 1); let msg; if (no_of_combinations === 1) { @@ -536,7 +622,7 @@ $.extend(erpnext.item, { me.multiple_variant_dialog.get_primary_btn().html(msg); me.multiple_variant_dialog.enable_primary_action(); } - } + }, }); }); }); @@ -553,38 +639,40 @@ $.extend(erpnext.item, { options: ``, - } - ].concat(fields) + }, + ].concat(fields), }); - me.multiple_variant_dialog.set_primary_action(__('Create Variants'), () => { + me.multiple_variant_dialog.set_primary_action(__("Create Variants"), () => { let selected_attributes = get_selected_attributes(); me.multiple_variant_dialog.hide(); frappe.call({ method: "erpnext.controllers.item_variant.enqueue_multiple_variant_creation", args: { - "item": frm.doc.name, - "args": selected_attributes + item: frm.doc.name, + args: selected_attributes, }, - callback: function(r) { - if (r.message==='queued') { + callback: function (r) { + if (r.message === "queued") { frappe.show_alert({ message: __("Variant creation has been queued."), - indicator: 'orange' + indicator: "orange", }); } else { frappe.show_alert({ message: __("{0} variants created.", [r.message]), - indicator: 'green' + indicator: "green", }); } - } + }, }); }); - $($(me.multiple_variant_dialog.$wrapper.find('.form-column')) - .find('.frappe-control')).css('margin-bottom', '0px'); + $($(me.multiple_variant_dialog.$wrapper.find(".form-column")).find(".frappe-control")).css( + "margin-bottom", + "0px" + ); me.multiple_variant_dialog.disable_primary_action(); me.multiple_variant_dialog.clear(); @@ -593,14 +681,14 @@ $.extend(erpnext.item, { function get_selected_attributes() { let selected_attributes = {}; - me.multiple_variant_dialog.$wrapper.find('.form-column').each((i, col) => { - if(i===0) return; - let attribute_name = $(col).find('.control-label').html().trim(); + me.multiple_variant_dialog.$wrapper.find(".form-column").each((i, col) => { + if (i === 0) return; + let attribute_name = $(col).find(".control-label").html().trim(); selected_attributes[attribute_name] = []; - let checked_opts = $(col).find('.checkbox input'); + let checked_opts = $(col).find(".checkbox input"); checked_opts.each((i, opt) => { - if($(opt).is(':checked')) { - selected_attributes[attribute_name].push($(opt).attr('data-fieldname')); + if ($(opt).is(":checked")) { + selected_attributes[attribute_name].push($(opt).attr("data-fieldname")); } }); }); @@ -608,30 +696,32 @@ $.extend(erpnext.item, { return selected_attributes; } - 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(); - } - }); + 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)) { + 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; @@ -640,66 +730,74 @@ $.extend(erpnext.item, { }); promises.push(p); - }, this); Promise.all(promises).then(() => { let fields = make_fields_from_attribute_values(attr_val_fields); make_and_show_dialog(fields); - }) - + }); }, - show_single_variant_dialog: function(frm) { - var fields = [] + show_single_variant_dialog: function (frm) { + var fields = []; - for(var i=0;i< frm.doc.attributes.length;i++){ + for (var i = 0; i < frm.doc.attributes.length; i++) { var fieldtype, desc; var row = frm.doc.attributes[i]; - if (row.numeric_values){ + if (row.numeric_values) { fieldtype = "Float"; - desc = "Min Value: "+ row.from_range +" , Max Value: "+ row.to_range +", in Increments of: "+ row.increment - } - else { + desc = + "Min Value: " + + row.from_range + + " , Max Value: " + + row.to_range + + ", in Increments of: " + + row.increment; + } else { fieldtype = "Data"; - desc = "" + desc = ""; } fields = fields.concat({ - "label": row.attribute, - "fieldname": row.attribute, - "fieldtype": fieldtype, - "reqd": 0, - "description": desc - }) + label: row.attribute, + fieldname: row.attribute, + fieldtype: fieldtype, + reqd: 0, + description: desc, + }); } var d = new frappe.ui.Dialog({ - title: __('Create Variant'), - fields: fields + title: __("Create Variant"), + fields: fields, }); - d.set_primary_action(__('Create'), function() { + d.set_primary_action(__("Create"), function () { var args = d.get_values(); - if(!args) return; + if (!args) return; frappe.call({ method: "erpnext.controllers.item_variant.get_variant", btn: d.get_primary_btn(), args: { - "template": frm.doc.name, - "args": d.get_values() + template: frm.doc.name, + args: d.get_values(), }, - callback: function(r) { + callback: function (r) { // returns variant item if (r.message) { var variant = r.message; - frappe.msgprint_dialog = frappe.msgprint(__("Item Variant {0} already exists with same attributes", - [repl('%(item)s', { - item_encoded: encodeURIComponent(variant), - item: variant - })] - )); + frappe.msgprint_dialog = frappe.msgprint( + __("Item Variant {0} already exists with same attributes", [ + repl( + '%(item)s', + { + item_encoded: encodeURIComponent(variant), + item: variant, + } + ), + ]) + ); frappe.msgprint_dialog.hide_on_page_refresh = true; - frappe.msgprint_dialog.$wrapper.find(".variant-click").on("click", function() { + frappe.msgprint_dialog.$wrapper.find(".variant-click").on("click", function () { d.hide(); }); } else { @@ -707,24 +805,23 @@ $.extend(erpnext.item, { frappe.call({ method: "erpnext.controllers.item_variant.create_variant", args: { - "item": frm.doc.name, - "args": d.get_values() + item: frm.doc.name, + args: d.get_values(), }, - callback: function(r) { + callback: function (r) { var doclist = frappe.model.sync(r.message); frappe.set_route("Form", doclist[0].doctype, doclist[0].name); - } + }, }); } - } + }, }); }); d.show(); - $.each(d.fields_dict, function(i, field) { - - if(field.df.fieldtype !== "Data") { + $.each(d.fields_dict, function (i, field) { + if (field.df.fieldtype !== "Data") { return; } @@ -740,32 +837,34 @@ $.extend(erpnext.item, { input.field = field; field.$input - .on('input', function(e) { + .on("input", function (e) { var term = e.target.value; frappe.call({ method: "erpnext.stock.doctype.item.item.get_item_attribute", args: { parent: i, - attribute_value: term + attribute_value: term, }, - callback: function(r) { + callback: function (r) { if (r.message) { - e.target.awesomplete.list = r.message.map(function(d) { return d.attribute_value; }); + e.target.awesomplete.list = r.message.map(function (d) { + return d.attribute_value; + }); } - } + }, }); }) - .on('focus', function(e) { - $(e.target).val('').trigger('input'); + .on("focus", function (e) { + $(e.target).val("").trigger("input"); }) .on("awesomplete-open", () => { - let modal = field.$input.parents('.modal-dialog')[0]; + let modal = field.$input.parents(".modal-dialog")[0]; if (modal) { $(modal).removeClass("modal-dialog-scrollable"); } }) .on("awesomplete-close", () => { - let modal = field.$input.parents('.modal-dialog')[0]; + let modal = field.$input.parents(".modal-dialog")[0]; if (modal) { $(modal).addClass("modal-dialog-scrollable"); } @@ -773,14 +872,13 @@ $.extend(erpnext.item, { }); }, - toggle_attributes: function(frm) { - if((frm.doc.has_variants || frm.doc.variant_of) - && frm.doc.variant_based_on==='Item Attribute') { + toggle_attributes: function (frm) { + if ((frm.doc.has_variants || frm.doc.variant_of) && frm.doc.variant_based_on === "Item Attribute") { frm.toggle_display("attributes", true); var grid = frm.fields_dict.attributes.grid; - if(frm.doc.variant_of) { + if (frm.doc.variant_of) { // variant // value column is displayed but not editable @@ -804,78 +902,87 @@ $.extend(erpnext.item, { // enable the grid so you can add more attributes grid.toggle_enable("attribute", true); } - } else { // nothing to do with attributes, hide it frm.toggle_display("attributes", false); } frm.layout.refresh_sections(); - } + }, }); frappe.ui.form.on("UOM Conversion Detail", { - uom: function(frm, cdt, cdn) { + uom: function (frm, cdt, cdn) { var row = locals[cdt][cdn]; if (row.uom) { frappe.call({ method: "erpnext.stock.doctype.item.item.get_uom_conv_factor", args: { - "uom": row.uom, - "stock_uom": frm.doc.stock_uom + uom: row.uom, + stock_uom: frm.doc.stock_uom, }, - callback: function(r) { + callback: function (r) { if (!r.exc && r.message) { frappe.model.set_value(cdt, cdn, "conversion_factor", r.message); } - } + }, }); } - } + }, }); -frappe.tour['Item'] = [ +frappe.tour["Item"] = [ { fieldname: "item_code", title: "Item Code", - description: __("Enter an Item Code, the name will be auto-filled the same as Item Code on clicking inside the Item Name field.") + description: __( + "Enter an Item Code, the name will be auto-filled the same as Item Code on clicking inside the Item Name field." + ), }, { fieldname: "item_group", title: "Item Group", - description: __("Select an Item Group.") + description: __("Select an Item Group."), }, { fieldname: "is_stock_item", title: "Maintain Stock", - description: __("If you are maintaining stock of this Item in your Inventory, ERPNext will make a stock ledger entry for each transaction of this item.") + description: __( + "If you are maintaining stock of this Item in your Inventory, ERPNext will make a stock ledger entry for each transaction of this item." + ), }, { fieldname: "include_item_in_manufacturing", title: "Include Item in Manufacturing", - description: __("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.") + description: __( + "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." + ), }, { fieldname: "opening_stock", title: "Opening Stock", - description: __("Enter the opening stock units.") + description: __("Enter the opening stock units."), }, { fieldname: "valuation_rate", title: "Valuation Rate", - description: __("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.") + description: __( + "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." + ), }, { fieldname: "standard_rate", title: "Standard Selling Rate", - description: __("When creating an Item, entering a value for this field will automatically create an Item Price at the backend.") + description: __( + "When creating an Item, entering a value for this field will automatically create an Item Price at the backend." + ), }, { fieldname: "item_defaults", title: "Item Defaults", - description: __("In this section, you can define Company-wide transaction-related defaults for this Item. Eg. Default Warehouse, Default Price List, Supplier, etc.") - } - - + description: __( + "In this section, you can define Company-wide transaction-related defaults for this Item. Eg. Default Warehouse, Default Price List, Supplier, etc." + ), + }, ]; function open_form(frm, doctype, child_doctype, parentfield) { @@ -902,7 +1009,7 @@ function open_form(frm, doctype, child_doctype, parentfield) { () => { frappe.flags.ignore_company_party_validation = true; frappe.model.trigger("item_code", frm.doc.name, new_child_doc); - } - ]) + }, + ]); }); } diff --git a/erpnext/stock/doctype/item/item_list.js b/erpnext/stock/doctype/item/item_list.js index 22d38e88935..aef0b2a648b 100644 --- a/erpnext/stock/doctype/item/item_list.js +++ b/erpnext/stock/doctype/item/item_list.js @@ -1,9 +1,8 @@ -frappe.listview_settings['Item'] = { - add_fields: ["item_name", "stock_uom", "item_group", "image", - "has_variants", "end_of_life", "disabled"], +frappe.listview_settings["Item"] = { + add_fields: ["item_name", "stock_uom", "item_group", "image", "has_variants", "end_of_life", "disabled"], filters: [["disabled", "=", "0"]], - get_indicator: function(doc) { + get_indicator: function (doc) { if (doc.disabled) { return [__("Disabled"), "grey", "disabled,=,Yes"]; } else if (doc.end_of_life && doc.end_of_life < frappe.datetime.get_today()) { @@ -17,24 +16,23 @@ frappe.listview_settings['Item'] = { reports: [ { - name: 'Stock Summary', - report_type: 'Page', - route: 'stock-balance' + name: "Stock Summary", + report_type: "Page", + route: "stock-balance", }, { - name: 'Stock Ledger', - report_type: 'Script Report' + name: "Stock Ledger", + report_type: "Script Report", }, { - name: 'Stock Balance', - report_type: 'Script Report' + name: "Stock Balance", + report_type: "Script Report", }, { - name: 'Stock Projected Qty', - report_type: 'Script Report' - } - - ] + name: "Stock Projected Qty", + report_type: "Script Report", + }, + ], }; frappe.help.youtube_id["Item"] = "qXaEwld4_Ps"; diff --git a/erpnext/stock/doctype/item_alternative/item_alternative.js b/erpnext/stock/doctype/item_alternative/item_alternative.js index ef0a88b9c81..e384e576bd2 100644 --- a/erpnext/stock/doctype/item_alternative/item_alternative.js +++ b/erpnext/stock/doctype/item_alternative/item_alternative.js @@ -1,14 +1,14 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Item Alternative', { - setup: function(frm) { +frappe.ui.form.on("Item Alternative", { + setup: function (frm) { frm.fields_dict.item_code.get_query = () => { return { filters: { - 'allow_alternative_item': 1 - } + allow_alternative_item: 1, + }, }; }; - } + }, }); diff --git a/erpnext/stock/doctype/item_attribute/item_attribute.js b/erpnext/stock/doctype/item_attribute/item_attribute.js index f253e22327f..22c7978ac3c 100644 --- a/erpnext/stock/doctype/item_attribute/item_attribute.js +++ b/erpnext/stock/doctype/item_attribute/item_attribute.js @@ -1,6 +1,4 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Item Attribute', { - -}); +frappe.ui.form.on("Item Attribute", {}); diff --git a/erpnext/stock/doctype/item_manufacturer/item_manufacturer.js b/erpnext/stock/doctype/item_manufacturer/item_manufacturer.js index a4df923f039..21803720f1f 100644 --- a/erpnext/stock/doctype/item_manufacturer/item_manufacturer.js +++ b/erpnext/stock/doctype/item_manufacturer/item_manufacturer.js @@ -1,8 +1,7 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Item Manufacturer', { +frappe.ui.form.on("Item Manufacturer", { // refresh: function(frm) { - // } }); diff --git a/erpnext/stock/doctype/item_price/item_price.js b/erpnext/stock/doctype/item_price/item_price.js index 8a4b4eef0ae..a5599e340a6 100644 --- a/erpnext/stock/doctype/item_price/item_price.js +++ b/erpnext/stock/doctype/item_price/item_price.js @@ -3,11 +3,11 @@ frappe.ui.form.on("Item Price", { setup(frm) { - frm.set_query("item_code", function() { + frm.set_query("item_code", function () { return { filters: { - "has_variants": 0 - } + has_variants: 0, + }, }; }); }, @@ -23,15 +23,18 @@ frappe.ui.form.on("Item Price", { frm.add_fetch("item_code", "description", "item_description"); frm.add_fetch("item_code", "stock_uom", "uom"); - frm.set_df_property("bulk_import_help", "options", - '' + __("Import in Bulk") + ''); + frm.set_df_property( + "bulk_import_help", + "options", + '' + __("Import in Bulk") + "" + ); - frm.set_query('batch_no', function() { + frm.set_query("batch_no", function () { return { filters: { - 'item': frm.doc.item_code - } + item: frm.doc.item_code, + }, }; }); - } + }, }); diff --git a/erpnext/stock/doctype/item_price/item_price_list.js b/erpnext/stock/doctype/item_price/item_price_list.js index 48158393f67..94c232e2e65 100644 --- a/erpnext/stock/doctype/item_price/item_price_list.js +++ b/erpnext/stock/doctype/item_price/item_price_list.js @@ -1,3 +1,3 @@ -frappe.listview_settings['Item Price'] = { +frappe.listview_settings["Item Price"] = { hide_name_column: true, }; diff --git a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.js b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.js index 058783c4ae3..4c49ce99510 100644 --- a/erpnext/stock/doctype/item_variant_settings/item_variant_settings.js +++ b/erpnext/stock/doctype/item_variant_settings/item_variant_settings.js @@ -1,24 +1,39 @@ // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Item Variant Settings', { - refresh: function(frm) { +frappe.ui.form.on("Item Variant Settings", { + refresh: function (frm) { const allow_fields = []; - const existing_fields = frm.doc.fields.map(row => row.field_name); - const exclude_fields = [...existing_fields, "naming_series", "item_code", "item_name", - "published_in_website", "standard_rate", "opening_stock", "image", - "variant_of", "valuation_rate", "barcodes", "has_variants", "attributes"]; + const existing_fields = frm.doc.fields.map((row) => row.field_name); + const exclude_fields = [ + ...existing_fields, + "naming_series", + "item_code", + "item_name", + "published_in_website", + "standard_rate", + "opening_stock", + "image", + "variant_of", + "valuation_rate", + "barcodes", + "has_variants", + "attributes", + ]; - const exclude_field_types = ['HTML', 'Section Break', 'Column Break', 'Button', 'Read Only']; + const exclude_field_types = ["HTML", "Section Break", "Column Break", "Button", "Read Only"]; - frappe.model.with_doctype('Item', () => { + frappe.model.with_doctype("Item", () => { const field_label_map = {}; - frappe.get_meta('Item').fields.forEach(d => { + frappe.get_meta("Item").fields.forEach((d) => { field_label_map[d.fieldname] = __(d.label) + ` (${d.fieldname})`; - if (!in_list(exclude_field_types, d.fieldtype) - && !d.no_copy && !in_list(exclude_fields, d.fieldname)) { + if ( + !in_list(exclude_field_types, d.fieldtype) && + !d.no_copy && + !in_list(exclude_fields, d.fieldname) + ) { allow_fields.push({ label: field_label_map[d.fieldname], value: d.fieldname, @@ -33,9 +48,7 @@ frappe.ui.form.on('Item Variant Settings', { }); } - frm.fields_dict.fields.grid.update_docfield_property( - 'field_name', 'options', allow_fields - ); + frm.fields_dict.fields.grid.update_docfield_property("field_name", "options", allow_fields); }); - } + }, }); diff --git a/erpnext/stock/doctype/manufacturer/manufacturer.js b/erpnext/stock/doctype/manufacturer/manufacturer.js index bb7e314e14e..1af6e4cb26d 100644 --- a/erpnext/stock/doctype/manufacturer/manufacturer.js +++ b/erpnext/stock/doctype/manufacturer/manufacturer.js @@ -1,16 +1,15 @@ // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Manufacturer', { - refresh: function(frm) { - frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Manufacturer' }; +frappe.ui.form.on("Manufacturer", { + refresh: function (frm) { + frappe.dynamic_link = { doc: frm.doc, fieldname: "name", doctype: "Manufacturer" }; if (frm.doc.__islocal) { - hide_field(['address_html','contact_html']); + hide_field(["address_html", "contact_html"]); frappe.contacts.clear_address_and_contact(frm); - } - else { - unhide_field(['address_html','contact_html']); + } else { + unhide_field(["address_html", "contact_html"]); frappe.contacts.render_address_and_contact(frm); } - } + }, }); diff --git a/erpnext/stock/doctype/material_request/material_request_list.js b/erpnext/stock/doctype/material_request/material_request_list.js index de7a3d05bf5..ee5c9e7b86c 100644 --- a/erpnext/stock/doctype/material_request/material_request_list.js +++ b/erpnext/stock/doctype/material_request/material_request_list.js @@ -1,8 +1,8 @@ -frappe.listview_settings['Material Request'] = { +frappe.listview_settings["Material Request"] = { add_fields: ["material_request_type", "status", "per_ordered", "per_received", "transfer_status"], - get_indicator: function(doc) { + get_indicator: function (doc) { var precision = frappe.defaults.get_default("float_precision"); - if (doc.status=="Stopped") { + if (doc.status == "Stopped") { return [__("Stopped"), "red", "status,=,Stopped"]; } else if (doc.transfer_status && doc.docstatus != 2) { if (doc.transfer_status == "Not Started") { @@ -12,12 +12,16 @@ frappe.listview_settings['Material Request'] = { } else if (doc.transfer_status == "Completed") { return [__("Completed"), "green"]; } - } else if (doc.docstatus==1 && flt(doc.per_ordered, precision) == 0) { + } else if (doc.docstatus == 1 && flt(doc.per_ordered, precision) == 0) { return [__("Pending"), "orange", "per_ordered,=,0"]; - } else if (doc.docstatus==1 && flt(doc.per_ordered, precision) < 100) { + } else if (doc.docstatus == 1 && flt(doc.per_ordered, precision) < 100) { return [__("Partially ordered"), "yellow", "per_ordered,<,100"]; - } else if (doc.docstatus==1 && flt(doc.per_ordered, precision) == 100) { - if (doc.material_request_type == "Purchase" && flt(doc.per_received, precision) < 100 && flt(doc.per_received, precision) > 0) { + } else if (doc.docstatus == 1 && flt(doc.per_ordered, precision) == 100) { + if ( + doc.material_request_type == "Purchase" && + flt(doc.per_received, precision) < 100 && + flt(doc.per_received, precision) > 0 + ) { return [__("Partially Received"), "yellow", "per_received,<,100"]; } else if (doc.material_request_type == "Purchase" && flt(doc.per_received, precision) == 100) { return [__("Received"), "green", "per_received,=,100"]; @@ -33,5 +37,5 @@ frappe.listview_settings['Material Request'] = { return [__("Manufactured"), "green", "per_ordered,=,100"]; } } - } + }, }; diff --git a/erpnext/stock/doctype/packing_slip/packing_slip.js b/erpnext/stock/doctype/packing_slip/packing_slip.js index 95e5ea309f8..682631f1b74 100644 --- a/erpnext/stock/doctype/packing_slip/packing_slip.js +++ b/erpnext/stock/doctype/packing_slip/packing_slip.js @@ -1,45 +1,45 @@ // Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Packing Slip', { - setup: (frm) => { - frm.set_query('delivery_note', () => { - return { - filters: { - docstatus: 0, - } - } - }); +frappe.ui.form.on("Packing Slip", { + setup: (frm) => { + frm.set_query("delivery_note", () => { + return { + filters: { + docstatus: 0, + }, + }; + }); - frm.set_query('item_code', 'items', (doc, cdt, cdn) => { - if (!doc.delivery_note) { - frappe.throw(__('Please select a Delivery Note')); - } else { - let d = locals[cdt][cdn]; - return { - query: 'erpnext.stock.doctype.packing_slip.packing_slip.item_details', - filters: { - delivery_note: doc.delivery_note, - } - } - } - }); + frm.set_query("item_code", "items", (doc, cdt, cdn) => { + if (!doc.delivery_note) { + frappe.throw(__("Please select a Delivery Note")); + } else { + let d = locals[cdt][cdn]; + return { + query: "erpnext.stock.doctype.packing_slip.packing_slip.item_details", + filters: { + delivery_note: doc.delivery_note, + }, + }; + } + }); }, refresh: (frm) => { - frm.toggle_display('misc_details', frm.doc.amended_from); + frm.toggle_display("misc_details", frm.doc.amended_from); }, delivery_note: (frm) => { - frm.set_value('items', null); + frm.set_value("items", null); if (frm.doc.delivery_note) { erpnext.utils.map_current_doc({ - method: 'erpnext.stock.doctype.delivery_note.delivery_note.make_packing_slip', + method: "erpnext.stock.doctype.delivery_note.delivery_note.make_packing_slip", source_name: frm.doc.delivery_note, target_doc: frm, freeze: true, - freeze_message: __('Creating Packing Slip ...'), + freeze_message: __("Creating Packing Slip ..."), }); } }, diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js index 8213adb89bf..38b1ca7f76e 100644 --- a/erpnext/stock/doctype/pick_list/pick_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -1,61 +1,62 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Pick List', { +frappe.ui.form.on("Pick List", { setup: (frm) => { - frm.set_indicator_formatter('item_code', - function(doc) { return (doc.stock_qty === 0) ? "red" : "green"; }); + frm.set_indicator_formatter("item_code", function (doc) { + return doc.stock_qty === 0 ? "red" : "green"; + }); frm.custom_make_buttons = { - 'Delivery Note': 'Delivery Note', - 'Stock Entry': 'Stock Entry', + "Delivery Note": "Delivery Note", + "Stock Entry": "Stock Entry", }; - frm.set_query('parent_warehouse', () => { + frm.set_query("parent_warehouse", () => { return { filters: { - 'is_group': 1, - 'company': frm.doc.company - } + is_group: 1, + company: frm.doc.company, + }, }; }); - frm.set_query('work_order', () => { + frm.set_query("work_order", () => { return { - query: 'erpnext.stock.doctype.pick_list.pick_list.get_pending_work_orders', + query: "erpnext.stock.doctype.pick_list.pick_list.get_pending_work_orders", filters: { - 'company': frm.doc.company - } + company: frm.doc.company, + }, }; }); - frm.set_query('material_request', () => { + frm.set_query("material_request", () => { return { filters: { - 'material_request_type': ['=', frm.doc.purpose] - } + material_request_type: ["=", frm.doc.purpose], + }, }; }); - frm.set_query('item_code', 'locations', () => { - return erpnext.queries.item({ "is_stock_item": 1 }); + frm.set_query("item_code", "locations", () => { + return erpnext.queries.item({ is_stock_item: 1 }); }); - frm.set_query('batch_no', 'locations', (frm, cdt, cdn) => { + frm.set_query("batch_no", "locations", (frm, cdt, cdn) => { const row = locals[cdt][cdn]; return { - query: 'erpnext.controllers.queries.get_batch_no', + query: "erpnext.controllers.queries.get_batch_no", filters: { item_code: row.item_code, - warehouse: row.warehouse + warehouse: row.warehouse, }, }; }); }, - set_item_locations:(frm, save) => { + set_item_locations: (frm, save) => { if (!(frm.doc.locations && frm.doc.locations.length)) { - frappe.msgprint(__('Add items in the Item Locations table')); + frappe.msgprint(__("Add items in the Item Locations table")); } else { frappe.call({ method: "set_item_locations", doc: frm.doc, args: { - "save": save, + save: save, }, freeze: 1, freeze_message: __("Setting Item Locations..."), @@ -67,155 +68,172 @@ frappe.ui.form.on('Pick List', { frm.events.set_item_locations(frm, false); }, refresh: (frm) => { - frm.trigger('add_get_items_button'); + frm.trigger("add_get_items_button"); if (frm.doc.docstatus === 1) { - frappe.xcall('erpnext.stock.doctype.pick_list.pick_list.target_document_exists', { - 'pick_list_name': frm.doc.name, - 'purpose': frm.doc.purpose - }).then(target_document_exists => { - frm.set_df_property("locations", "allow_on_submit", target_document_exists ? 0 : 1); + frappe + .xcall("erpnext.stock.doctype.pick_list.pick_list.target_document_exists", { + pick_list_name: frm.doc.name, + purpose: frm.doc.purpose, + }) + .then((target_document_exists) => { + frm.set_df_property("locations", "allow_on_submit", target_document_exists ? 0 : 1); - if (target_document_exists) return; + if (target_document_exists) return; - frm.add_custom_button(__('Update Current Stock'), () => frm.trigger('update_pick_list_stock')); + frm.add_custom_button(__("Update Current Stock"), () => + frm.trigger("update_pick_list_stock") + ); - if (frm.doc.purpose === 'Delivery') { - frm.add_custom_button(__('Delivery Note'), () => frm.trigger('create_delivery_note'), __('Create')); - } else { - frm.add_custom_button(__('Stock Entry'), () => frm.trigger('create_stock_entry'), __('Create')); - } - }); + if (frm.doc.purpose === "Delivery") { + frm.add_custom_button( + __("Delivery Note"), + () => frm.trigger("create_delivery_note"), + __("Create") + ); + } else { + frm.add_custom_button( + __("Stock Entry"), + () => frm.trigger("create_stock_entry"), + __("Create") + ); + } + }); } }, work_order: (frm) => { - frappe.db.get_value('Work Order', - frm.doc.work_order, - ['qty', 'material_transferred_for_manufacturing'] - ).then(data => { - let qty_data = data.message; - let max = qty_data.qty - qty_data.material_transferred_for_manufacturing; - frappe.prompt({ - fieldtype: 'Float', - label: __('Qty of Finished Goods Item'), - fieldname: 'qty', - description: __('Max: {0}', [max]), - default: max - }, (data) => { - frm.set_value('for_qty', data.qty); - if (data.qty > max) { - frappe.msgprint(__('Quantity must not be more than {0}', [max])); - return; - } - frm.clear_table('locations'); - erpnext.utils.map_current_doc({ - method: 'erpnext.manufacturing.doctype.work_order.work_order.create_pick_list', - target: frm, - source_name: frm.doc.work_order - }); - }, __('Select Quantity'), __('Get Items')); - }); + frappe.db + .get_value("Work Order", frm.doc.work_order, ["qty", "material_transferred_for_manufacturing"]) + .then((data) => { + let qty_data = data.message; + let max = qty_data.qty - qty_data.material_transferred_for_manufacturing; + frappe.prompt( + { + fieldtype: "Float", + label: __("Qty of Finished Goods Item"), + fieldname: "qty", + description: __("Max: {0}", [max]), + default: max, + }, + (data) => { + frm.set_value("for_qty", data.qty); + if (data.qty > max) { + frappe.msgprint(__("Quantity must not be more than {0}", [max])); + return; + } + frm.clear_table("locations"); + erpnext.utils.map_current_doc({ + method: "erpnext.manufacturing.doctype.work_order.work_order.create_pick_list", + target: frm, + source_name: frm.doc.work_order, + }); + }, + __("Select Quantity"), + __("Get Items") + ); + }); }, material_request: (frm) => { erpnext.utils.map_current_doc({ - method: 'erpnext.stock.doctype.material_request.material_request.create_pick_list', + method: "erpnext.stock.doctype.material_request.material_request.create_pick_list", target: frm, - source_name: frm.doc.material_request + source_name: frm.doc.material_request, }); }, purpose: (frm) => { - frm.clear_table('locations'); - frm.trigger('add_get_items_button'); + frm.clear_table("locations"); + frm.trigger("add_get_items_button"); }, create_delivery_note: (frm) => { frappe.model.open_mapped_doc({ - method: 'erpnext.stock.doctype.pick_list.pick_list.create_delivery_note', - frm: frm + method: "erpnext.stock.doctype.pick_list.pick_list.create_delivery_note", + frm: frm, }); - }, create_stock_entry: (frm) => { - frappe.xcall('erpnext.stock.doctype.pick_list.pick_list.create_stock_entry', { - 'pick_list': frm.doc, - }).then(stock_entry => { - frappe.model.sync(stock_entry); - frappe.set_route("Form", 'Stock Entry', stock_entry.name); - }); + frappe + .xcall("erpnext.stock.doctype.pick_list.pick_list.create_stock_entry", { + pick_list: frm.doc, + }) + .then((stock_entry) => { + frappe.model.sync(stock_entry); + frappe.set_route("Form", "Stock Entry", stock_entry.name); + }); }, update_pick_list_stock: (frm) => { frm.events.set_item_locations(frm, true); }, add_get_items_button: (frm) => { let purpose = frm.doc.purpose; - if (purpose != 'Delivery' || frm.doc.docstatus !== 0) return; + if (purpose != "Delivery" || frm.doc.docstatus !== 0) return; let get_query_filters = { docstatus: 1, - per_delivered: ['<', 100], - status: ['!=', ''], - customer: frm.doc.customer + per_delivered: ["<", 100], + status: ["!=", ""], + customer: frm.doc.customer, }; - frm.get_items_btn = frm.add_custom_button(__('Get Items'), () => { + frm.get_items_btn = frm.add_custom_button(__("Get Items"), () => { erpnext.utils.map_current_doc({ - method: 'erpnext.selling.doctype.sales_order.sales_order.create_pick_list', - source_doctype: 'Sales Order', + method: "erpnext.selling.doctype.sales_order.sales_order.create_pick_list", + source_doctype: "Sales Order", target: frm, setters: { company: frm.doc.company, - customer: frm.doc.customer + customer: frm.doc.customer, }, - date_field: 'transaction_date', - get_query_filters: get_query_filters + date_field: "transaction_date", + get_query_filters: get_query_filters, }); }); }, scan_barcode: (frm) => { const opts = { frm, - items_table_name: 'locations', - qty_field: 'picked_qty', - max_qty_field: 'qty', + items_table_name: "locations", + qty_field: "picked_qty", + max_qty_field: "qty", dont_allow_new_row: true, prompt_qty: frm.doc.prompt_qty, - serial_no_field: "not_supported", // doesn't make sense for picklist without a separate field. + serial_no_field: "not_supported", // doesn't make sense for picklist without a separate field. }; const barcode_scanner = new erpnext.utils.BarcodeScanner(opts); barcode_scanner.process_scan(); - } + }, }); -frappe.ui.form.on('Pick List Item', { +frappe.ui.form.on("Pick List Item", { item_code: (frm, cdt, cdn) => { let row = frappe.get_doc(cdt, cdn); if (row.item_code) { - get_item_details(row.item_code).then(data => { - frappe.model.set_value(cdt, cdn, 'uom', data.stock_uom); - frappe.model.set_value(cdt, cdn, 'stock_uom', data.stock_uom); - frappe.model.set_value(cdt, cdn, 'conversion_factor', 1); + get_item_details(row.item_code).then((data) => { + frappe.model.set_value(cdt, cdn, "uom", data.stock_uom); + frappe.model.set_value(cdt, cdn, "stock_uom", data.stock_uom); + frappe.model.set_value(cdt, cdn, "conversion_factor", 1); }); } }, uom: (frm, cdt, cdn) => { let row = frappe.get_doc(cdt, cdn); if (row.uom) { - get_item_details(row.item_code, row.uom).then(data => { - frappe.model.set_value(cdt, cdn, 'conversion_factor', data.conversion_factor); + get_item_details(row.item_code, row.uom).then((data) => { + frappe.model.set_value(cdt, cdn, "conversion_factor", data.conversion_factor); }); } }, qty: (frm, cdt, cdn) => { let row = frappe.get_doc(cdt, cdn); - frappe.model.set_value(cdt, cdn, 'stock_qty', row.qty * row.conversion_factor); + frappe.model.set_value(cdt, cdn, "stock_qty", row.qty * row.conversion_factor); }, conversion_factor: (frm, cdt, cdn) => { let row = frappe.get_doc(cdt, cdn); - frappe.model.set_value(cdt, cdn, 'stock_qty', row.qty * row.conversion_factor); - } + frappe.model.set_value(cdt, cdn, "stock_qty", row.qty * row.conversion_factor); + }, }); -function get_item_details(item_code, uom=null) { +function get_item_details(item_code, uom = null) { if (item_code) { - return frappe.xcall('erpnext.stock.doctype.pick_list.pick_list.get_item_details', { + return frappe.xcall("erpnext.stock.doctype.pick_list.pick_list.get_item_details", { item_code, - uom + uom, }); } } diff --git a/erpnext/stock/doctype/pick_list/pick_list_list.js b/erpnext/stock/doctype/pick_list/pick_list_list.js index ad88b0a682f..9cdbfe18720 100644 --- a/erpnext/stock/doctype/pick_list/pick_list_list.js +++ b/erpnext/stock/doctype/pick_list/pick_list_list.js @@ -1,14 +1,14 @@ // Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.listview_settings['Pick List'] = { +frappe.listview_settings["Pick List"] = { get_indicator: function (doc) { const status_colors = { - "Draft": "grey", - "Open": "orange", - "Completed": "green", - "Cancelled": "red", + Draft: "grey", + Open: "orange", + Completed: "green", + Cancelled: "red", }; return [__(doc.status), status_colors[doc.status], "status,=," + doc.status]; }, -}; \ No newline at end of file +}; diff --git a/erpnext/stock/doctype/price_list/price_list.js b/erpnext/stock/doctype/price_list/price_list.js index 9291498e863..85a3a391187 100644 --- a/erpnext/stock/doctype/price_list/price_list.js +++ b/erpnext/stock/doctype/price_list/price_list.js @@ -2,13 +2,17 @@ // License: GNU General Public License v3. See license.txt frappe.ui.form.on("Price List", { - refresh: function(frm) { + refresh: function (frm) { let me = this; - frm.add_custom_button(__("Add / Edit Prices"), function() { - frappe.route_options = { - "price_list": frm.doc.name - }; - frappe.set_route("Report", "Item Price"); - }, "fa fa-money"); - } + frm.add_custom_button( + __("Add / Edit Prices"), + function () { + frappe.route_options = { + price_list: frm.doc.name, + }; + frappe.set_route("Report", "Item Price"); + }, + "fa fa-money" + ); + }, }); diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt_list.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_list.js index 4029f0c127b..ddc9bb026fb 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt_list.js +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt_list.js @@ -1,8 +1,17 @@ -frappe.listview_settings['Purchase Receipt'] = { - add_fields: ["supplier", "supplier_name", "base_grand_total", "is_subcontracted", - "transporter_name", "is_return", "status", "per_billed", "currency"], - get_indicator: function(doc) { - if(cint(doc.is_return)==1) { +frappe.listview_settings["Purchase Receipt"] = { + add_fields: [ + "supplier", + "supplier_name", + "base_grand_total", + "is_subcontracted", + "transporter_name", + "is_return", + "status", + "per_billed", + "currency", + ], + get_indicator: function (doc) { + if (cint(doc.is_return) == 1) { return [__("Return"), "gray", "is_return,=,Yes"]; } else if (doc.status === "Closed") { return [__("Closed"), "green", "status,=,Closed"]; @@ -15,11 +24,9 @@ frappe.listview_settings['Purchase Receipt'] = { } }, - onload: function(listview) { - - listview.page.add_action_item(__("Purchase Invoice"), ()=>{ + onload: function (listview) { + listview.page.add_action_item(__("Purchase Invoice"), () => { erpnext.bulk_transaction_processing.create(listview, "Purchase Receipt", "Purchase Invoice"); }); - } - + }, }; diff --git a/erpnext/stock/doctype/putaway_rule/putaway_rule.js b/erpnext/stock/doctype/putaway_rule/putaway_rule.js index e0569206ef9..351be16d7cb 100644 --- a/erpnext/stock/doctype/putaway_rule/putaway_rule.js +++ b/erpnext/stock/doctype/putaway_rule/putaway_rule.js @@ -1,41 +1,41 @@ // Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Putaway Rule', { - setup: function(frm) { - frm.set_query("warehouse", function() { +frappe.ui.form.on("Putaway Rule", { + setup: function (frm) { + frm.set_query("warehouse", function () { return { - "filters": { - "company": frm.doc.company, - "is_group": 0 - } + filters: { + company: frm.doc.company, + is_group: 0, + }, }; }); }, - uom: function(frm) { + uom: function (frm) { if (frm.doc.item_code && frm.doc.uom) { return frm.call({ method: "erpnext.stock.get_item_details.get_conversion_factor", args: { item_code: frm.doc.item_code, - uom: frm.doc.uom + uom: frm.doc.uom, }, - callback: function(r) { + callback: function (r) { if (!r.exc) { let stock_capacity = flt(frm.doc.capacity) * flt(r.message.conversion_factor); - frm.set_value('conversion_factor', r.message.conversion_factor); - frm.set_value('stock_capacity', stock_capacity); + frm.set_value("conversion_factor", r.message.conversion_factor); + frm.set_value("stock_capacity", stock_capacity); } - } + }, }); } }, - capacity: function(frm) { + capacity: function (frm) { let stock_capacity = flt(frm.doc.capacity) * flt(frm.doc.conversion_factor); - frm.set_value('stock_capacity', stock_capacity); - } + frm.set_value("stock_capacity", stock_capacity); + }, // refresh: function(frm) { diff --git a/erpnext/stock/doctype/putaway_rule/putaway_rule_list.js b/erpnext/stock/doctype/putaway_rule/putaway_rule_list.js index 725e91ee8d9..c5c85908b8f 100644 --- a/erpnext/stock/doctype/putaway_rule/putaway_rule_list.js +++ b/erpnext/stock/doctype/putaway_rule/putaway_rule_list.js @@ -1,4 +1,4 @@ -frappe.listview_settings['Putaway Rule'] = { +frappe.listview_settings["Putaway Rule"] = { add_fields: ["disable"], get_indicator: (doc) => { if (doc.disable) { @@ -10,9 +10,9 @@ frappe.listview_settings['Putaway Rule'] = { reports: [ { - name: 'Warehouse Capacity Summary', - report_type: 'Page', - route: 'warehouse-capacity-summary' - } - ] + name: "Warehouse Capacity Summary", + report_type: "Page", + route: "warehouse-capacity-summary", + }, + ], }; diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.js b/erpnext/stock/doctype/quality_inspection/quality_inspection.js index 05fa2324dd4..fc487514a2c 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.js +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.js @@ -4,88 +4,85 @@ cur_frm.cscript.refresh = cur_frm.cscript.inspection_type; frappe.ui.form.on("Quality Inspection", { - - setup: function(frm) { - frm.set_query("reference_name", function() { + setup: function (frm) { + frm.set_query("reference_name", function () { return { filters: { - "docstatus": ["!=", 2], - } - } + docstatus: ["!=", 2], + }, + }; }); - frm.set_query("batch_no", function() { + frm.set_query("batch_no", function () { return { filters: { - "item": frm.doc.item_code - } + item: frm.doc.item_code, + }, }; }); // Serial No based on item_code - frm.set_query("item_serial_no", function() { + frm.set_query("item_serial_no", function () { let filters = {}; if (frm.doc.item_code) { filters = { - 'item_code': frm.doc.item_code + item_code: frm.doc.item_code, }; } return { filters: filters }; }); // item code based on GRN/DN - frm.set_query("item_code", function(doc) { + frm.set_query("item_code", function (doc) { let doctype = doc.reference_type; if (doc.reference_type !== "Job Card") { - doctype = (doc.reference_type == "Stock Entry") ? - "Stock Entry Detail" : doc.reference_type + " Item"; + doctype = + doc.reference_type == "Stock Entry" ? "Stock Entry Detail" : doc.reference_type + " Item"; } if (doc.reference_type && doc.reference_name) { let filters = { - "from": doctype, - "inspection_type": doc.inspection_type + from: doctype, + inspection_type: doc.inspection_type, }; - if (doc.reference_type == doctype) - filters["reference_name"] = doc.reference_name; - else - filters["parent"] = doc.reference_name; + if (doc.reference_type == doctype) filters["reference_name"] = doc.reference_name; + else filters["parent"] = doc.reference_name; return { query: "erpnext.stock.doctype.quality_inspection.quality_inspection.item_query", - filters: filters + filters: filters, }; } }); }, - refresh: function(frm) { + refresh: function (frm) { // Ignore cancellation of reference doctype on cancel all. frm.ignore_doctypes_on_cancel_all = [frm.doc.reference_type]; }, - item_code: function(frm) { + item_code: function (frm) { if (frm.doc.item_code && !frm.doc.quality_inspection_template) { return frm.call({ method: "get_quality_inspection_template", doc: frm.doc, - callback: function() { - refresh_field(['quality_inspection_template', 'readings']); - } + callback: function () { + refresh_field(["quality_inspection_template", "readings"]); + }, }); } }, - quality_inspection_template: function(frm) { + quality_inspection_template: function (frm) { if (frm.doc.quality_inspection_template) { return frm.call({ method: "get_item_specification_details", doc: frm.doc, - callback: function() { - refresh_field('readings'); - } + callback: function () { + refresh_field("readings"); + }, }); } }, diff --git a/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.js b/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.js index 47c7e11d237..ecf84c91506 100644 --- a/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.js +++ b/erpnext/stock/doctype/quality_inspection_parameter/quality_inspection_parameter.js @@ -1,8 +1,7 @@ // Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Quality Inspection Parameter', { +frappe.ui.form.on("Quality Inspection Parameter", { // refresh: function(frm) { - // } }); diff --git a/erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.js b/erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.js index 8716a298716..796ab89d2b5 100644 --- a/erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.js +++ b/erpnext/stock/doctype/quality_inspection_parameter_group/quality_inspection_parameter_group.js @@ -1,8 +1,7 @@ // Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Quality Inspection Parameter Group', { +frappe.ui.form.on("Quality Inspection Parameter Group", { // refresh: function(frm) { - // } }); diff --git a/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.js b/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.js index fa57a3dcc34..053809d5fe0 100644 --- a/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.js +++ b/erpnext/stock/doctype/quality_inspection_template/quality_inspection_template.js @@ -1,8 +1,6 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Quality Inspection Template', { - refresh: function() { - - } +frappe.ui.form.on("Quality Inspection Template", { + refresh: function () {}, }); diff --git a/erpnext/stock/doctype/quick_stock_balance/quick_stock_balance.js b/erpnext/stock/doctype/quick_stock_balance/quick_stock_balance.js index f261fd99790..5cfd1f5843a 100644 --- a/erpnext/stock/doctype/quick_stock_balance/quick_stock_balance.js +++ b/erpnext/stock/doctype/quick_stock_balance/quick_stock_balance.js @@ -1,91 +1,90 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Quick Stock Balance', { - +frappe.ui.form.on("Quick Stock Balance", { setup: (frm) => { - frm.set_query('item', () => { + frm.set_query("item", () => { if (!(frm.doc.warehouse && frm.doc.date)) { - frm.trigger('check_warehouse_and_date'); + frm.trigger("check_warehouse_and_date"); } }); }, make_custom_stock_report_button: (frm) => { if (frm.doc.item) { - frm.add_custom_button(__('Stock Balance Report'), () => { - frappe.set_route('query-report', 'Stock Balance', - { 'item_code': frm.doc.item, 'warehouse': frm.doc.warehouse }); + frm.add_custom_button(__("Stock Balance Report"), () => { + frappe.set_route("query-report", "Stock Balance", { + item_code: frm.doc.item, + warehouse: frm.doc.warehouse, + }); }); } }, refresh: (frm) => { frm.disable_save(); - frm.trigger('make_custom_stock_report_button'); + frm.trigger("make_custom_stock_report_button"); }, check_warehouse_and_date: (frm) => { - frappe.msgprint(__('Please enter Warehouse and Date')); - frm.doc.item = ''; + frappe.msgprint(__("Please enter Warehouse and Date")); + frm.doc.item = ""; frm.refresh(); }, warehouse: (frm) => { if (frm.doc.item || frm.doc.item_barcode) { - frm.trigger('get_stock_and_item_details'); + frm.trigger("get_stock_and_item_details"); } }, date: (frm) => { if (frm.doc.item || frm.doc.item_barcode) { - frm.trigger('get_stock_and_item_details'); + frm.trigger("get_stock_and_item_details"); } }, item: (frm) => { - frappe.flags.last_updated_element = 'item'; - frm.trigger('get_stock_and_item_details'); - frm.trigger('make_custom_stock_report_button'); + frappe.flags.last_updated_element = "item"; + frm.trigger("get_stock_and_item_details"); + frm.trigger("make_custom_stock_report_button"); }, item_barcode: (frm) => { - frappe.flags.last_updated_element = 'item_barcode'; - frm.trigger('get_stock_and_item_details'); - frm.trigger('make_custom_stock_report_button'); + frappe.flags.last_updated_element = "item_barcode"; + frm.trigger("get_stock_and_item_details"); + frm.trigger("make_custom_stock_report_button"); }, get_stock_and_item_details: (frm) => { if (!(frm.doc.warehouse && frm.doc.date)) { - frm.trigger('check_warehouse_and_date'); - } - else if (frm.doc.item || frm.doc.item_barcode) { + frm.trigger("check_warehouse_and_date"); + } else if (frm.doc.item || frm.doc.item_barcode) { let filters = { warehouse: frm.doc.warehouse, date: frm.doc.date, }; - if (frappe.flags.last_updated_element === 'item') { - filters = { ...filters, ...{ item: frm.doc.item }}; - } - else { - filters = { ...filters, ...{ barcode: frm.doc.item_barcode }}; + if (frappe.flags.last_updated_element === "item") { + filters = { ...filters, ...{ item: frm.doc.item } }; + } else { + filters = { ...filters, ...{ barcode: frm.doc.item_barcode } }; } frappe.call({ - method: 'erpnext.stock.doctype.quick_stock_balance.quick_stock_balance.get_stock_item_details', + method: "erpnext.stock.doctype.quick_stock_balance.quick_stock_balance.get_stock_item_details", args: filters, callback: (r) => { if (r.message) { - let fields = ['item', 'qty', 'value', 'image']; - if (!r.message['barcodes'].includes(frm.doc.item_barcode)) { - frm.doc.item_barcode = ''; + let fields = ["item", "qty", "value", "image"]; + if (!r.message["barcodes"].includes(frm.doc.item_barcode)) { + frm.doc.item_barcode = ""; frm.refresh(); } fields.forEach(function (field) { frm.set_value(field, r.message[field]); }); } - } + }, }); } - } + }, }); diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js index 8aec5328476..38241ae0609 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js @@ -1,22 +1,32 @@ // Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Repost Item Valuation', { - setup: function(frm) { +frappe.ui.form.on("Repost Item Valuation", { + setup: function (frm) { frm.set_query("warehouse", () => { let filters = { - 'is_group': 0 + is_group: 0, }; - if (frm.doc.company) filters['company'] = frm.doc.company; - return {filters: filters}; + if (frm.doc.company) filters["company"] = frm.doc.company; + return { filters: filters }; }); frm.set_query("voucher_type", () => { return { filters: { - name: ['in', ['Purchase Receipt', 'Purchase Invoice', 'Delivery Note', - 'Sales Invoice', 'Stock Entry', 'Stock Reconciliation', 'Subcontracting Receipt']] - } + name: [ + "in", + [ + "Purchase Receipt", + "Purchase Invoice", + "Delivery Note", + "Sales Invoice", + "Stock Entry", + "Stock Reconciliation", + "Subcontracting Receipt", + ], + ], + }, }; }); @@ -25,97 +35,97 @@ frappe.ui.form.on('Repost Item Valuation', { return { filters: { company: frm.doc.company, - docstatus: 1 - } + docstatus: 1, + }, }; }); } - frm.trigger('setup_realtime_progress'); + frm.trigger("setup_realtime_progress"); }, - based_on: function(frm) { + based_on: function (frm) { var fields_to_reset = []; - if (frm.doc.based_on == 'Transaction') { - fields_to_reset = ['item_code', 'warehouse']; - } else if (frm.doc.based_on == 'Item and Warehouse') { - fields_to_reset = ['voucher_type', 'voucher_no']; + if (frm.doc.based_on == "Transaction") { + fields_to_reset = ["item_code", "warehouse"]; + } else if (frm.doc.based_on == "Item and Warehouse") { + fields_to_reset = ["voucher_type", "voucher_no"]; } if (fields_to_reset) { - fields_to_reset.forEach(field => { + fields_to_reset.forEach((field) => { frm.set_value(field, undefined); }); } }, - setup_realtime_progress: function(frm) { - frappe.realtime.on('item_reposting_progress', data => { + setup_realtime_progress: function (frm) { + frappe.realtime.on("item_reposting_progress", (data) => { if (frm.doc.name !== data.name) { return; } - if (frm.doc.status == 'In Progress') { + if (frm.doc.status == "In Progress") { frm.doc.current_index = data.current_index; frm.doc.items_to_be_repost = data.items_to_be_repost; frm.dashboard.reset(); - frm.trigger('show_reposting_progress'); + frm.trigger("show_reposting_progress"); } }); }, - refresh: function(frm) { - if (frm.doc.status == "Failed" && frm.doc.docstatus==1) { - frm.add_custom_button(__('Restart'), function () { + refresh: function (frm) { + if (frm.doc.status == "Failed" && frm.doc.docstatus == 1) { + frm.add_custom_button(__("Restart"), function () { frm.trigger("restart_reposting"); }).addClass("btn-primary"); } - frm.trigger('show_reposting_progress'); + frm.trigger("show_reposting_progress"); - if (frm.doc.status === 'Queued' && frm.doc.docstatus === 1) { - frm.trigger('execute_reposting'); + if (frm.doc.status === "Queued" && frm.doc.docstatus === 1) { + frm.trigger("execute_reposting"); } }, execute_reposting(frm) { frm.add_custom_button(__("Start Reposting"), () => { frappe.call({ - method: 'erpnext.stock.doctype.repost_item_valuation.repost_item_valuation.execute_repost_item_valuation', - callback: function() { - frappe.msgprint(__('Reposting has been started in the background.')); - } + method: "erpnext.stock.doctype.repost_item_valuation.repost_item_valuation.execute_repost_item_valuation", + callback: function () { + frappe.msgprint(__("Reposting has been started in the background.")); + }, }); }); }, - show_reposting_progress: function(frm) { + show_reposting_progress: function (frm) { var bars = []; let total_count = frm.doc.items_to_be_repost ? JSON.parse(frm.doc.items_to_be_repost).length : 0; - let progress = flt(cint(frm.doc.current_index) / total_count * 100, 2) || 0.5; - var title = __('Reposting Completed {0}%', [progress]); + let progress = flt((cint(frm.doc.current_index) / total_count) * 100, 2) || 0.5; + var title = __("Reposting Completed {0}%", [progress]); bars.push({ - 'title': title, - 'width': progress + '%', - 'progress_class': 'progress-bar-success' + title: title, + width: progress + "%", + progress_class: "progress-bar-success", }); - frm.dashboard.add_progress(__('Reposting Progress'), bars); + frm.dashboard.add_progress(__("Reposting Progress"), bars); }, - restart_reposting: function(frm) { + restart_reposting: function (frm) { frappe.call({ method: "restart_reposting", doc: frm.doc, - callback: function(r) { + callback: function (r) { if (!r.exc) { frm.refresh(); } - } + }, }); - } + }, }); diff --git a/erpnext/stock/doctype/serial_no/serial_no.js b/erpnext/stock/doctype/serial_no/serial_no.js index 9d5555ed631..88ed7abe2bd 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.js +++ b/erpnext/stock/doctype/serial_no/serial_no.js @@ -1,20 +1,20 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -cur_frm.add_fetch("customer", "customer_name", "customer_name") -cur_frm.add_fetch("supplier", "supplier_name", "supplier_name") +cur_frm.add_fetch("customer", "customer_name", "customer_name"); +cur_frm.add_fetch("supplier", "supplier_name", "supplier_name"); -cur_frm.add_fetch("item_code", "item_name", "item_name") -cur_frm.add_fetch("item_code", "description", "description") -cur_frm.add_fetch("item_code", "item_group", "item_group") -cur_frm.add_fetch("item_code", "brand", "brand") +cur_frm.add_fetch("item_code", "item_name", "item_name"); +cur_frm.add_fetch("item_code", "description", "description"); +cur_frm.add_fetch("item_code", "item_group", "item_group"); +cur_frm.add_fetch("item_code", "brand", "brand"); -cur_frm.cscript.onload = function() { - cur_frm.set_query("item_code", function() { - return erpnext.queries.item({"is_stock_item": 1, "has_serial_no": 1}) +cur_frm.cscript.onload = function () { + cur_frm.set_query("item_code", function () { + return erpnext.queries.item({ is_stock_item: 1, has_serial_no: 1 }); }); }; -frappe.ui.form.on("Serial No", "refresh", function(frm) { +frappe.ui.form.on("Serial No", "refresh", function (frm) { frm.toggle_enable("item_code", frm.doc.__islocal); }); diff --git a/erpnext/stock/doctype/serial_no/serial_no_list.js b/erpnext/stock/doctype/serial_no/serial_no_list.js index 7526d1d8a5c..e085c2a9312 100644 --- a/erpnext/stock/doctype/serial_no/serial_no_list.js +++ b/erpnext/stock/doctype/serial_no/serial_no_list.js @@ -1,14 +1,21 @@ -frappe.listview_settings['Serial No'] = { +frappe.listview_settings["Serial No"] = { add_fields: ["item_code", "warehouse", "warranty_expiry_date", "delivery_document_type"], get_indicator: (doc) => { if (doc.delivery_document_type) { return [__("Delivered"), "green", "delivery_document_type,is,set"]; - } else if (doc.warranty_expiry_date && frappe.datetime.get_diff(doc.warranty_expiry_date, frappe.datetime.nowdate()) <= 0) { - return [__("Expired"), "red", "warranty_expiry_date,not in,|warranty_expiry_date,<=,Today|delivery_document_type,is,not set"]; + } else if ( + doc.warranty_expiry_date && + frappe.datetime.get_diff(doc.warranty_expiry_date, frappe.datetime.nowdate()) <= 0 + ) { + return [ + __("Expired"), + "red", + "warranty_expiry_date,not in,|warranty_expiry_date,<=,Today|delivery_document_type,is,not set", + ]; } else if (!doc.warehouse) { return [__("Inactive"), "grey", "warehouse,is,not set"]; } else { return [__("Active"), "green", "delivery_document_type,is,not set"]; } - } + }, }; diff --git a/erpnext/stock/doctype/shipment/shipment.js b/erpnext/stock/doctype/shipment/shipment.js index 13a17a25913..8843d383531 100644 --- a/erpnext/stock/doctype/shipment/shipment.js +++ b/erpnext/stock/doctype/shipment/shipment.js @@ -1,34 +1,44 @@ // Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Shipment', { - address_query: function(frm, link_doctype, link_name, is_your_company_address) { +frappe.ui.form.on("Shipment", { + address_query: function (frm, link_doctype, link_name, is_your_company_address) { return { - query: 'frappe.contacts.doctype.address.address.address_query', + query: "frappe.contacts.doctype.address.address.address_query", filters: { link_doctype: link_doctype, link_name: link_name, - is_your_company_address: is_your_company_address - } + is_your_company_address: is_your_company_address, + }, }; }, - contact_query: function(frm, link_doctype, link_name) { + contact_query: function (frm, link_doctype, link_name) { return { - query: 'frappe.contacts.doctype.contact.contact.contact_query', + query: "frappe.contacts.doctype.contact.contact.contact_query", filters: { link_doctype: link_doctype, - link_name: link_name - } + link_name: link_name, + }, }; }, - onload: function(frm) { + onload: function (frm) { frm.set_query("delivery_address_name", () => { let delivery_to = `delivery_${frappe.model.scrub(frm.doc.delivery_to_type)}`; - return frm.events.address_query(frm, frm.doc.delivery_to_type, frm.doc[delivery_to], frm.doc.delivery_to_type === 'Company' ? 1 : 0); + return frm.events.address_query( + frm, + frm.doc.delivery_to_type, + frm.doc[delivery_to], + frm.doc.delivery_to_type === "Company" ? 1 : 0 + ); }); frm.set_query("pickup_address_name", () => { let pickup_from = `pickup_${frappe.model.scrub(frm.doc.pickup_from_type)}`; - return frm.events.address_query(frm, frm.doc.pickup_from_type, frm.doc[pickup_from], frm.doc.pickup_from_type === 'Company' ? 1 : 0); + return frm.events.address_query( + frm, + frm.doc.pickup_from_type, + frm.doc[pickup_from], + frm.doc.pickup_from_type === "Company" ? 1 : 0 + ); }); frm.set_query("delivery_contact_name", () => { let delivery_to = `delivery_${frappe.model.scrub(frm.doc.delivery_to_type)}`; @@ -38,8 +48,8 @@ frappe.ui.form.on('Shipment', { let pickup_from = `pickup_${frappe.model.scrub(frm.doc.pickup_from_type)}`; return frm.events.contact_query(frm, frm.doc.pickup_from_type, frm.doc[pickup_from]); }); - frm.set_query("delivery_note", "shipment_delivery_note", function() { - let customer = ''; + frm.set_query("delivery_note", "shipment_delivery_note", function () { + let customer = ""; if (frm.doc.delivery_to_type == "Customer") { customer = frm.doc.delivery_customer; } @@ -51,305 +61,329 @@ frappe.ui.form.on('Shipment', { filters: { customer: customer, docstatus: 1, - status: ["not in", ["Cancelled"]] - } + status: ["not in", ["Cancelled"]], + }, }; } }); }, - refresh: function() { - $('div[data-fieldname=pickup_address] > div > .clearfix').hide(); - $('div[data-fieldname=pickup_contact] > div > .clearfix').hide(); - $('div[data-fieldname=delivery_address] > div > .clearfix').hide(); - $('div[data-fieldname=delivery_contact] > div > .clearfix').hide(); + refresh: function () { + $("div[data-fieldname=pickup_address] > div > .clearfix").hide(); + $("div[data-fieldname=pickup_contact] > div > .clearfix").hide(); + $("div[data-fieldname=delivery_address] > div > .clearfix").hide(); + $("div[data-fieldname=delivery_contact] > div > .clearfix").hide(); }, - before_save: function(frm) { + before_save: function (frm) { let delivery_to = `delivery_${frappe.model.scrub(frm.doc.delivery_to_type)}`; frm.set_value("delivery_to", frm.doc[delivery_to]); let pickup_from = `pickup_${frappe.model.scrub(frm.doc.pickup_from_type)}`; frm.set_value("pickup", frm.doc[pickup_from]); }, - set_pickup_company_address: function(frm) { - frappe.db.get_value('Address', { - address_title: frm.doc.pickup_company, - is_your_company_address: 1 - }, 'name', (r) => { - frm.set_value("pickup_address_name", r.name); - }); + set_pickup_company_address: function (frm) { + frappe.db.get_value( + "Address", + { + address_title: frm.doc.pickup_company, + is_your_company_address: 1, + }, + "name", + (r) => { + frm.set_value("pickup_address_name", r.name); + } + ); }, - set_delivery_company_address: function(frm) { - frappe.db.get_value('Address', { - address_title: frm.doc.delivery_company, - is_your_company_address: 1 - }, 'name', (r) => { - frm.set_value("delivery_address_name", r.name); - }); + set_delivery_company_address: function (frm) { + frappe.db.get_value( + "Address", + { + address_title: frm.doc.delivery_company, + is_your_company_address: 1, + }, + "name", + (r) => { + frm.set_value("delivery_address_name", r.name); + } + ); }, - pickup_from_type: function(frm) { - if (frm.doc.pickup_from_type == 'Company') { - frm.set_value("pickup_company", frappe.defaults.get_default('company')); - frm.set_value("pickup_customer", ''); - frm.set_value("pickup_supplier", ''); + pickup_from_type: function (frm) { + if (frm.doc.pickup_from_type == "Company") { + frm.set_value("pickup_company", frappe.defaults.get_default("company")); + frm.set_value("pickup_customer", ""); + frm.set_value("pickup_supplier", ""); } else { - frm.trigger('clear_pickup_fields'); + frm.trigger("clear_pickup_fields"); } - if (frm.doc.pickup_from_type == 'Customer') { - frm.set_value("pickup_company", ''); - frm.set_value("pickup_supplier", ''); + if (frm.doc.pickup_from_type == "Customer") { + frm.set_value("pickup_company", ""); + frm.set_value("pickup_supplier", ""); } - if (frm.doc.pickup_from_type == 'Supplier') { - frm.set_value("pickup_customer", ''); - frm.set_value("pickup_company", ''); + if (frm.doc.pickup_from_type == "Supplier") { + frm.set_value("pickup_customer", ""); + frm.set_value("pickup_company", ""); } }, - delivery_to_type: function(frm) { - if (frm.doc.delivery_to_type == 'Company') { - frm.set_value("delivery_company", frappe.defaults.get_default('company')); - frm.set_value("delivery_customer", ''); - frm.set_value("delivery_supplier", ''); + delivery_to_type: function (frm) { + if (frm.doc.delivery_to_type == "Company") { + frm.set_value("delivery_company", frappe.defaults.get_default("company")); + frm.set_value("delivery_customer", ""); + frm.set_value("delivery_supplier", ""); } else { - frm.trigger('clear_delivery_fields'); + frm.trigger("clear_delivery_fields"); } - if (frm.doc.delivery_to_type == 'Customer') { - frm.set_value("delivery_company", ''); - frm.set_value("delivery_supplier", ''); + if (frm.doc.delivery_to_type == "Customer") { + frm.set_value("delivery_company", ""); + frm.set_value("delivery_supplier", ""); } - if (frm.doc.delivery_to_type == 'Supplier') { - frm.set_value("delivery_customer", ''); - frm.set_value("delivery_company", ''); + if (frm.doc.delivery_to_type == "Supplier") { + frm.set_value("delivery_customer", ""); + frm.set_value("delivery_company", ""); frm.toggle_display("shipment_delivery_note", false); } else { frm.toggle_display("shipment_delivery_note", true); } }, - delivery_address_name: function(frm) { - if (frm.doc.delivery_to_type == 'Company') { - erpnext.utils.get_address_display(frm, 'delivery_address_name', 'delivery_address', true); + delivery_address_name: function (frm) { + if (frm.doc.delivery_to_type == "Company") { + erpnext.utils.get_address_display(frm, "delivery_address_name", "delivery_address", true); } else { - erpnext.utils.get_address_display(frm, 'delivery_address_name', 'delivery_address', false); + erpnext.utils.get_address_display(frm, "delivery_address_name", "delivery_address", false); } }, - pickup_address_name: function(frm) { - if (frm.doc.pickup_from_type == 'Company') { - erpnext.utils.get_address_display(frm, 'pickup_address_name', 'pickup_address', true); + pickup_address_name: function (frm) { + if (frm.doc.pickup_from_type == "Company") { + erpnext.utils.get_address_display(frm, "pickup_address_name", "pickup_address", true); } else { - erpnext.utils.get_address_display(frm, 'pickup_address_name', 'pickup_address', false); + erpnext.utils.get_address_display(frm, "pickup_address_name", "pickup_address", false); } }, - get_contact_display: function(frm, contact_name, contact_type) { + get_contact_display: function (frm, contact_name, contact_type) { frappe.call({ method: "frappe.contacts.doctype.contact.contact.get_contact_details", args: { contact: contact_name }, - callback: function(r) { + callback: function (r) { if (r.message) { if (!(r.message.contact_email && (r.message.contact_phone || r.message.contact_mobile))) { - if (contact_type == 'Delivery') { - frm.set_value('delivery_contact_name', ''); - frm.set_value('delivery_contact', ''); + if (contact_type == "Delivery") { + frm.set_value("delivery_contact_name", ""); + frm.set_value("delivery_contact", ""); } else { - frm.set_value('pickup_contact_name', ''); - frm.set_value('pickup_contact', ''); + frm.set_value("pickup_contact_name", ""); + frm.set_value("pickup_contact", ""); } - frappe.throw(__("Email or Phone/Mobile of the Contact are mandatory to continue.") - + "
              " + __("Please set Email/Phone for the contact") - + ` ${contact_name}`); + frappe.throw( + __("Email or Phone/Mobile of the Contact are mandatory to continue.") + + "
              " + + __("Please set Email/Phone for the contact") + + ` ${contact_name}` + ); } let contact_display = r.message.contact_display; if (r.message.contact_email) { - contact_display += '
              ' + r.message.contact_email; + contact_display += "
              " + r.message.contact_email; } if (r.message.contact_phone) { - contact_display += '
              ' + r.message.contact_phone; + contact_display += "
              " + r.message.contact_phone; } if (r.message.contact_mobile && !r.message.contact_phone) { - contact_display += '
              ' + r.message.contact_mobile; + contact_display += "
              " + r.message.contact_mobile; } - if (contact_type == 'Delivery') { - frm.set_value('delivery_contact', contact_display); + if (contact_type == "Delivery") { + frm.set_value("delivery_contact", contact_display); if (r.message.contact_email) { - frm.set_value('delivery_contact_email', r.message.contact_email); + frm.set_value("delivery_contact_email", r.message.contact_email); } } else { - frm.set_value('pickup_contact', contact_display); + frm.set_value("pickup_contact", contact_display); if (r.message.contact_email) { - frm.set_value('pickup_contact_email', r.message.contact_email); + frm.set_value("pickup_contact_email", r.message.contact_email); } } } - } + }, }); }, - delivery_contact_name: function(frm) { + delivery_contact_name: function (frm) { if (frm.doc.delivery_contact_name) { - frm.events.get_contact_display(frm, frm.doc.delivery_contact_name, 'Delivery'); + frm.events.get_contact_display(frm, frm.doc.delivery_contact_name, "Delivery"); } }, - pickup_contact_name: function(frm) { + pickup_contact_name: function (frm) { if (frm.doc.pickup_contact_name) { - frm.events.get_contact_display(frm, frm.doc.pickup_contact_name, 'Pickup'); + frm.events.get_contact_display(frm, frm.doc.pickup_contact_name, "Pickup"); } }, - pickup_contact_person: function(frm) { + pickup_contact_person: function (frm) { if (frm.doc.pickup_contact_person) { frappe.call({ method: "erpnext.stock.doctype.shipment.shipment.get_company_contact", args: { user: frm.doc.pickup_contact_person }, - callback: function({ message }) { + callback: function ({ message }) { const r = message; let contact_display = `${r.first_name} ${r.last_name}`; if (r.email) { - contact_display += `
              ${ r.email }`; - frm.set_value('pickup_contact_email', r.email); + contact_display += `
              ${r.email}`; + frm.set_value("pickup_contact_email", r.email); } if (r.phone) { - contact_display += `
              ${ r.phone }`; + contact_display += `
              ${r.phone}`; } if (r.mobile_no && !r.phone) { - contact_display += `
              ${ r.mobile_no }`; + contact_display += `
              ${r.mobile_no}`; } - frm.set_value('pickup_contact', contact_display); - } + frm.set_value("pickup_contact", contact_display); + }, }); } else { - if (frm.doc.pickup_from_type === 'Company') { + if (frm.doc.pickup_from_type === "Company") { frappe.call({ method: "erpnext.stock.doctype.shipment.shipment.get_company_contact", args: { user: frappe.session.user }, - callback: function({ message }) { + callback: function ({ message }) { const r = message; let contact_display = `${r.first_name} ${r.last_name}`; if (r.email) { - contact_display += `
              ${ r.email }`; - frm.set_value('pickup_contact_email', r.email); + contact_display += `
              ${r.email}`; + frm.set_value("pickup_contact_email", r.email); } if (r.phone) { - contact_display += `
              ${ r.phone }`; + contact_display += `
              ${r.phone}`; } if (r.mobile_no && !r.phone) { - contact_display += `
              ${ r.mobile_no }`; + contact_display += `
              ${r.mobile_no}`; } - frm.set_value('pickup_contact', contact_display); - } + frm.set_value("pickup_contact", contact_display); + }, }); } } }, - set_company_contact: function(frm, delivery_type) { - frappe.db.get_value('User', { name: frappe.session.user }, ['full_name', 'last_name', 'email', 'phone', 'mobile_no'], (r) => { - if (!(r.last_name && r.email && (r.phone || r.mobile_no))) { - if (delivery_type == 'Delivery') { - frm.set_value('delivery_company', ''); - frm.set_value('delivery_contact', ''); + set_company_contact: function (frm, delivery_type) { + frappe.db.get_value( + "User", + { name: frappe.session.user }, + ["full_name", "last_name", "email", "phone", "mobile_no"], + (r) => { + if (!(r.last_name && r.email && (r.phone || r.mobile_no))) { + if (delivery_type == "Delivery") { + frm.set_value("delivery_company", ""); + frm.set_value("delivery_contact", ""); + } else { + frm.set_value("pickup_company", ""); + frm.set_value("pickup_contact", ""); + } + frappe.throw( + __("Last Name, Email or Phone/Mobile of the user are mandatory to continue.") + + "
              " + + __("Please first set Last Name, Email and Phone for the user") + + ` ${frappe.session.user}` + ); + } + let contact_display = r.full_name; + if (r.email) { + contact_display += "
              " + r.email; + } + if (r.phone) { + contact_display += "
              " + r.phone; + } + if (r.mobile_no && !r.phone) { + contact_display += "
              " + r.mobile_no; + } + if (delivery_type == "Delivery") { + frm.set_value("delivery_contact", contact_display); + if (r.email) { + frm.set_value("delivery_contact_email", r.email); + } } else { - frm.set_value('pickup_company', ''); - frm.set_value('pickup_contact', ''); - } - frappe.throw(__("Last Name, Email or Phone/Mobile of the user are mandatory to continue.") + "
              " - + __("Please first set Last Name, Email and Phone for the user") - + ` ${frappe.session.user}`); - } - let contact_display = r.full_name; - if (r.email) { - contact_display += '
              ' + r.email; - } - if (r.phone) { - contact_display += '
              ' + r.phone; - } - if (r.mobile_no && !r.phone) { - contact_display += '
              ' + r.mobile_no; - } - if (delivery_type == 'Delivery') { - frm.set_value('delivery_contact', contact_display); - if (r.email) { - frm.set_value('delivery_contact_email', r.email); - } - } else { - frm.set_value('pickup_contact', contact_display); - if (r.email) { - frm.set_value('pickup_contact_email', r.email); + frm.set_value("pickup_contact", contact_display); + if (r.email) { + frm.set_value("pickup_contact_email", r.email); + } } } - }); - frm.set_value('pickup_contact_person', frappe.session.user); + ); + frm.set_value("pickup_contact_person", frappe.session.user); }, - pickup_company: function(frm) { - if (frm.doc.pickup_from_type == 'Company' && frm.doc.pickup_company) { - frm.trigger('set_pickup_company_address'); - frm.events.set_company_contact(frm, 'Pickup'); + pickup_company: function (frm) { + if (frm.doc.pickup_from_type == "Company" && frm.doc.pickup_company) { + frm.trigger("set_pickup_company_address"); + frm.events.set_company_contact(frm, "Pickup"); } }, - delivery_company: function(frm) { - if (frm.doc.delivery_to_type == 'Company' && frm.doc.delivery_company) { - frm.trigger('set_delivery_company_address'); - frm.events.set_company_contact(frm, 'Delivery'); + delivery_company: function (frm) { + if (frm.doc.delivery_to_type == "Company" && frm.doc.delivery_company) { + frm.trigger("set_delivery_company_address"); + frm.events.set_company_contact(frm, "Delivery"); } }, - delivery_customer: function(frm) { - frm.trigger('clear_delivery_fields'); + delivery_customer: function (frm) { + frm.trigger("clear_delivery_fields"); if (frm.doc.delivery_customer) { - frm.events.set_address_name(frm, 'Customer', frm.doc.delivery_customer, 'Delivery'); - frm.events.set_contact_name(frm, 'Customer', frm.doc.delivery_customer, 'Delivery'); + frm.events.set_address_name(frm, "Customer", frm.doc.delivery_customer, "Delivery"); + frm.events.set_contact_name(frm, "Customer", frm.doc.delivery_customer, "Delivery"); } }, - delivery_supplier: function(frm) { - frm.trigger('clear_delivery_fields'); + delivery_supplier: function (frm) { + frm.trigger("clear_delivery_fields"); if (frm.doc.delivery_supplier) { - frm.events.set_address_name(frm, 'Supplier', frm.doc.delivery_supplier, 'Delivery'); - frm.events.set_contact_name(frm, 'Supplier', frm.doc.delivery_supplier, 'Delivery'); + frm.events.set_address_name(frm, "Supplier", frm.doc.delivery_supplier, "Delivery"); + frm.events.set_contact_name(frm, "Supplier", frm.doc.delivery_supplier, "Delivery"); } }, - pickup_customer: function(frm) { + pickup_customer: function (frm) { if (frm.doc.pickup_customer) { - frm.events.set_address_name(frm, 'Customer', frm.doc.pickup_customer, 'Pickup'); - frm.events.set_contact_name(frm, 'Customer', frm.doc.pickup_customer, 'Pickup'); + frm.events.set_address_name(frm, "Customer", frm.doc.pickup_customer, "Pickup"); + frm.events.set_contact_name(frm, "Customer", frm.doc.pickup_customer, "Pickup"); } }, - pickup_supplier: function(frm) { + pickup_supplier: function (frm) { if (frm.doc.pickup_supplier) { - frm.events.set_address_name(frm, 'Supplier', frm.doc.pickup_supplier, 'Pickup'); - frm.events.set_contact_name(frm, 'Supplier', frm.doc.pickup_supplier, 'Pickup'); + frm.events.set_address_name(frm, "Supplier", frm.doc.pickup_supplier, "Pickup"); + frm.events.set_contact_name(frm, "Supplier", frm.doc.pickup_supplier, "Pickup"); } }, - set_address_name: function(frm, ref_doctype, ref_docname, delivery_type) { + set_address_name: function (frm, ref_doctype, ref_docname, delivery_type) { frappe.call({ method: "erpnext.stock.doctype.shipment.shipment.get_address_name", args: { ref_doctype: ref_doctype, - docname: ref_docname + docname: ref_docname, }, - callback: function(r) { + callback: function (r) { if (r.message) { - if (delivery_type == 'Delivery') { - frm.set_value('delivery_address_name', r.message); + if (delivery_type == "Delivery") { + frm.set_value("delivery_address_name", r.message); } else { - frm.set_value('pickup_address_name', r.message); + frm.set_value("pickup_address_name", r.message); } } - } + }, }); }, - set_contact_name: function(frm, ref_doctype, ref_docname, delivery_type) { + set_contact_name: function (frm, ref_doctype, ref_docname, delivery_type) { frappe.call({ method: "erpnext.stock.doctype.shipment.shipment.get_contact_name", args: { ref_doctype: ref_doctype, - docname: ref_docname + docname: ref_docname, }, - callback: function(r) { + callback: function (r) { if (r.message) { - if (delivery_type == 'Delivery') { - frm.set_value('delivery_contact_name', r.message); + if (delivery_type == "Delivery") { + frm.set_value("delivery_contact_name", r.message); } else { - frm.set_value('pickup_contact_name', r.message); + frm.set_value("pickup_contact_name", r.message); } } - } + }, }); }, - add_template: function(frm) { + add_template: function (frm) { if (frm.doc.parcel_template) { frappe.model.with_doc("Shipment Parcel Template", frm.doc.parcel_template, () => { - let parcel_template = frappe.model.get_doc("Shipment Parcel Template", frm.doc.parcel_template); + let parcel_template = frappe.model.get_doc( + "Shipment Parcel Template", + frm.doc.parcel_template + ); let row = frappe.model.add_child(frm.doc, "Shipment Parcel", "shipment_parcel"); row.length = parcel_template.length; row.width = parcel_template.width; @@ -359,56 +393,71 @@ frappe.ui.form.on('Shipment', { }); } }, - pickup_date: function(frm) { + pickup_date: function (frm) { if (frm.doc.pickup_date < frappe.datetime.get_today()) { frappe.throw(__("Pickup Date cannot be before this day")); } }, - clear_pickup_fields: function(frm) { - let fields = ["pickup_address_name", "pickup_contact_name", "pickup_address", "pickup_contact", "pickup_contact_email", "pickup_contact_person"]; + clear_pickup_fields: function (frm) { + let fields = [ + "pickup_address_name", + "pickup_contact_name", + "pickup_address", + "pickup_contact", + "pickup_contact_email", + "pickup_contact_person", + ]; for (let field of fields) { - frm.set_value(field, ''); + frm.set_value(field, ""); } }, - clear_delivery_fields: function(frm) { - let fields = ["delivery_address_name", "delivery_contact_name", "delivery_address", "delivery_contact", "delivery_contact_email"]; + clear_delivery_fields: function (frm) { + let fields = [ + "delivery_address_name", + "delivery_contact_name", + "delivery_address", + "delivery_contact", + "delivery_contact_email", + ]; for (let field of fields) { - frm.set_value(field, ''); + frm.set_value(field, ""); } }, - remove_email_row: function(frm, table, fieldname) { - $.each(frm.doc[table] || [], function(i, detail) { + remove_email_row: function (frm, table, fieldname) { + $.each(frm.doc[table] || [], function (i, detail) { if (detail.email === fieldname) { cur_frm.get_field(table).grid.grid_rows[i].remove(); } }); - } + }, }); -frappe.ui.form.on('Shipment Delivery Note', { - delivery_note: function(frm, cdt, cdn) { +frappe.ui.form.on("Shipment Delivery Note", { + delivery_note: function (frm, cdt, cdn) { let row = locals[cdt][cdn]; if (row.delivery_note) { let row_index = row.idx - 1; - if (validate_duplicate(frm, 'shipment_delivery_note', row.delivery_note, row_index)) { - frappe.throw(__("You have entered a duplicate Delivery Note on Row") + ` ${row.idx}. ` + __("Please rectify and try again.")); + if (validate_duplicate(frm, "shipment_delivery_note", row.delivery_note, row_index)) { + frappe.throw( + __("You have entered a duplicate Delivery Note on Row") + + ` ${row.idx}. ` + + __("Please rectify and try again.") + ); } } }, - grand_total: function(frm, cdt, cdn) { + grand_total: function (frm, cdt, cdn) { let row = locals[cdt][cdn]; if (row.grand_total) { - var value_of_goods = parseFloat(frm.doc.value_of_goods)+parseFloat(row.grand_total); + var value_of_goods = parseFloat(frm.doc.value_of_goods) + parseFloat(row.grand_total); frm.set_value("value_of_goods", Math.round(value_of_goods)); frm.refresh_fields("value_of_goods"); } }, }); -var validate_duplicate = function(frm, table, fieldname, index) { - return ( - table === 'shipment_delivery_note' - ? frm.doc[table].some((detail, i) => detail.delivery_note === fieldname && !(index === i)) - : frm.doc[table].some((detail, i) => detail.email === fieldname && !(index === i)) - ); +var validate_duplicate = function (frm, table, fieldname, index) { + return table === "shipment_delivery_note" + ? frm.doc[table].some((detail, i) => detail.delivery_note === fieldname && !(index === i)) + : frm.doc[table].some((detail, i) => detail.email === fieldname && !(index === i)); }; diff --git a/erpnext/stock/doctype/shipment/shipment_list.js b/erpnext/stock/doctype/shipment/shipment_list.js index ae6a3c154e8..b333ccc817f 100644 --- a/erpnext/stock/doctype/shipment/shipment_list.js +++ b/erpnext/stock/doctype/shipment/shipment_list.js @@ -1,8 +1,8 @@ -frappe.listview_settings['Shipment'] = { +frappe.listview_settings["Shipment"] = { add_fields: ["status"], - get_indicator: function(doc) { - if (doc.status=='Booked') { + get_indicator: function (doc) { + if (doc.status == "Booked") { return [__("Booked"), "green"]; } - } + }, }; diff --git a/erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.js b/erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.js index 785a3b304de..3ef90fa81f3 100644 --- a/erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.js +++ b/erpnext/stock/doctype/shipment_parcel_template/shipment_parcel_template.js @@ -1,8 +1,7 @@ // Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Shipment Parcel Template', { +frappe.ui.form.on("Shipment Parcel Template", { // refresh: function(frm) { - // } }); diff --git a/erpnext/stock/doctype/stock_entry/stock_entry_list.js b/erpnext/stock/doctype/stock_entry/stock_entry_list.js index af29d495ff7..8a1c808cd91 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry_list.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry_list.js @@ -1,19 +1,25 @@ -frappe.listview_settings['Stock Entry'] = { - add_fields: ["`tabStock Entry`.`from_warehouse`", "`tabStock Entry`.`to_warehouse`", - "`tabStock Entry`.`purpose`", "`tabStock Entry`.`work_order`", "`tabStock Entry`.`bom_no`", - "`tabStock Entry`.`is_return`"], +frappe.listview_settings["Stock Entry"] = { + add_fields: [ + "`tabStock Entry`.`from_warehouse`", + "`tabStock Entry`.`to_warehouse`", + "`tabStock Entry`.`purpose`", + "`tabStock Entry`.`work_order`", + "`tabStock Entry`.`bom_no`", + "`tabStock Entry`.`is_return`", + ], get_indicator: function (doc) { - if(doc.is_return===1 && doc.purpose === "Material Transfer for Manufacture") { - return [__("Material Returned from WIP"), "orange", - "is_return,=,1|purpose,=,Material Transfer for Manufacture|docstatus,<,2"]; + if (doc.is_return === 1 && doc.purpose === "Material Transfer for Manufacture") { + return [ + __("Material Returned from WIP"), + "orange", + "is_return,=,1|purpose,=,Material Transfer for Manufacture|docstatus,<,2", + ]; } else if (doc.docstatus === 0) { return [__("Draft"), "red", "docstatus,=,0"]; - - } else if (doc.purpose === 'Send to Warehouse' && doc.per_transferred < 100) { + } else if (doc.purpose === "Send to Warehouse" && doc.per_transferred < 100) { // not delivered & overdue return [__("Goods In Transit"), "grey", "per_transferred,<,100"]; - - } else if (doc.purpose === 'Send to Warehouse' && doc.per_transferred === 100) { + } else if (doc.purpose === "Send to Warehouse" && doc.per_transferred === 100) { return [__("Goods Transferred"), "green", "per_transferred,=,100"]; } else if (doc.docstatus === 2) { return [__("Canceled"), "red", "docstatus,=,2"]; @@ -22,21 +28,30 @@ frappe.listview_settings['Stock Entry'] = { } }, column_render: { - "from_warehouse": function(doc) { + from_warehouse: function (doc) { var html = ""; - if(doc.from_warehouse) { - html += '' - +doc.from_warehouse+' '; + if (doc.from_warehouse) { + html += + '' + + doc.from_warehouse + + " "; } // if(doc.from_warehouse || doc.to_warehouse) { // html += ' '; // } - if(doc.to_warehouse) { - html += ''+doc.to_warehouse+''; + if (doc.to_warehouse) { + html += + '' + + doc.to_warehouse + + ""; } return html; - } - } + }, + }, }; diff --git a/erpnext/stock/doctype/stock_entry_type/stock_entry_type.js b/erpnext/stock/doctype/stock_entry_type/stock_entry_type.js index c554278334b..21e60fbcae4 100644 --- a/erpnext/stock/doctype/stock_entry_type/stock_entry_type.js +++ b/erpnext/stock/doctype/stock_entry_type/stock_entry_type.js @@ -1,8 +1,7 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Stock Entry Type', { +frappe.ui.form.on("Stock Entry Type", { // refresh: function(frm) { - // } }); diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.js b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.js index 23018aa615b..7196dc9f1e3 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.js +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.js @@ -1,8 +1,8 @@ // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Stock Ledger Entry', { - refresh: function(frm) { - frm.page.btn_secondary.hide() - } +frappe.ui.form.on("Stock Ledger Entry", { + refresh: function (frm) { + frm.page.btn_secondary.hide(); + }, }); diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js index 1415aa36164..0abc5264449 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js @@ -5,29 +5,29 @@ frappe.provide("erpnext.stock"); frappe.provide("erpnext.accounts.dimensions"); frappe.ui.form.on("Stock Reconciliation", { - onload: function(frm) { + onload: function (frm) { frm.add_fetch("item_code", "item_name", "item_name"); // end of life - frm.set_query("item_code", "items", function(doc, cdt, cdn) { + frm.set_query("item_code", "items", function (doc, cdt, cdn) { return { query: "erpnext.controllers.queries.item_query", - filters:{ - "is_stock_item": 1 - } - } + filters: { + is_stock_item: 1, + }, + }; }); - frm.set_query("batch_no", "items", function(doc, cdt, cdn) { + frm.set_query("batch_no", "items", function (doc, cdt, cdn) { var item = locals[cdt][cdn]; return { filters: { - 'item': item.item_code - } + item: item.item_code, + }, }; }); if (frm.doc.company) { - erpnext.queries.setup_queries(frm, "Warehouse", function() { + erpnext.queries.setup_queries(frm, "Warehouse", function () { return erpnext.queries.warehouse(frm.doc); }); } @@ -39,56 +39,56 @@ frappe.ui.form.on("Stock Reconciliation", { erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); }, - company: function(frm) { + company: function (frm) { erpnext.accounts.dimensions.update_dimension(frm, frm.doctype); }, - refresh: function(frm) { - if(frm.doc.docstatus < 1) { - frm.add_custom_button(__("Fetch Items from Warehouse"), function() { + refresh: function (frm) { + if (frm.doc.docstatus < 1) { + frm.add_custom_button(__("Fetch Items from Warehouse"), function () { frm.events.get_items(frm); }); } - if(frm.doc.company) { + if (frm.doc.company) { frm.trigger("toggle_display_account_head"); } }, - scan_barcode: function(frm) { - const barcode_scanner = new erpnext.utils.BarcodeScanner({frm:frm}); + scan_barcode: function (frm) { + const barcode_scanner = new erpnext.utils.BarcodeScanner({ frm: frm }); barcode_scanner.process_scan(); }, - scan_mode: function(frm) { + scan_mode: function (frm) { if (frm.doc.scan_mode) { frappe.show_alert({ message: __("Scan mode enabled, existing quantity will not be fetched."), - indicator: "green" + indicator: "green", }); } }, - set_warehouse: function(frm) { - let transaction_controller = new erpnext.TransactionController({frm:frm}); + set_warehouse: function (frm) { + let transaction_controller = new erpnext.TransactionController({ frm: frm }); transaction_controller.autofill_warehouse(frm.doc.items, "warehouse", frm.doc.set_warehouse); }, - get_items: function(frm) { + get_items: function (frm) { let fields = [ { - label: 'Warehouse', - fieldname: 'warehouse', - fieldtype: 'Link', - options: 'Warehouse', + label: "Warehouse", + fieldname: "warehouse", + fieldtype: "Link", + options: "Warehouse", reqd: 1, - "get_query": function() { + get_query: function () { return { - "filters": { - "company": frm.doc.company, - } + filters: { + company: frm.doc.company, + }, }; - } + }, }, { label: "Item Code", @@ -99,57 +99,62 @@ frappe.ui.form.on("Stock Reconciliation", { { label: __("Ignore Empty Stock"), fieldname: "ignore_empty_stock", - fieldtype: "Check" - } + fieldtype: "Check", + }, ]; - frappe.prompt(fields, function(data) { - frappe.call({ - method: "erpnext.stock.doctype.stock_reconciliation.stock_reconciliation.get_items", - args: { - warehouse: data.warehouse, - posting_date: frm.doc.posting_date, - posting_time: frm.doc.posting_time, - company: frm.doc.company, - item_code: data.item_code, - ignore_empty_stock: data.ignore_empty_stock - }, - callback: function(r) { - if (r.exc || !r.message || !r.message.length) return; + frappe.prompt( + fields, + function (data) { + frappe.call({ + method: "erpnext.stock.doctype.stock_reconciliation.stock_reconciliation.get_items", + args: { + warehouse: data.warehouse, + posting_date: frm.doc.posting_date, + posting_time: frm.doc.posting_time, + company: frm.doc.company, + item_code: data.item_code, + ignore_empty_stock: data.ignore_empty_stock, + }, + callback: function (r) { + if (r.exc || !r.message || !r.message.length) return; - frm.clear_table("items"); + frm.clear_table("items"); - r.message.forEach((row) => { - let item = frm.add_child("items"); - $.extend(item, row); + r.message.forEach((row) => { + let item = frm.add_child("items"); + $.extend(item, row); - item.qty = item.qty || 0; - item.valuation_rate = item.valuation_rate || 0; - }); - frm.refresh_field("items"); - } - }); - }, __("Get Items"), __("Update")); + item.qty = item.qty || 0; + item.valuation_rate = item.valuation_rate || 0; + }); + frm.refresh_field("items"); + }, + }); + }, + __("Get Items"), + __("Update") + ); }, - posting_date: function(frm) { + posting_date: function (frm) { frm.trigger("set_valuation_rate_and_qty_for_all_items"); }, - posting_time: function(frm) { + posting_time: function (frm) { frm.trigger("set_valuation_rate_and_qty_for_all_items"); }, - set_valuation_rate_and_qty_for_all_items: function(frm) { - frm.doc.items.forEach(row => { + set_valuation_rate_and_qty_for_all_items: function (frm) { + frm.doc.items.forEach((row) => { frm.events.set_valuation_rate_and_qty(frm, row.doctype, row.name); }); }, - set_valuation_rate_and_qty: function(frm, cdt, cdn) { + set_valuation_rate_and_qty: function (frm, cdt, cdn) { var d = frappe.model.get_doc(cdt, cdn); - if(d.item_code && d.warehouse) { + if (d.item_code && d.warehouse) { frappe.call({ method: "erpnext.stock.doctype.stock_reconciliation.stock_reconciliation.get_stock_balance_for", args: { @@ -157,9 +162,9 @@ frappe.ui.form.on("Stock Reconciliation", { warehouse: d.warehouse, posting_date: frm.doc.posting_date, posting_time: frm.doc.posting_time, - batch_no: d.batch_no + batch_no: d.batch_no, }, - callback: function(r) { + callback: function (r) { const row = frappe.model.get_doc(cdt, cdn); if (!frm.doc.scan_mode) { frappe.model.set_value(cdt, cdn, "qty", r.message.qty); @@ -174,12 +179,12 @@ frappe.ui.form.on("Stock Reconciliation", { if (frm.doc.purpose == "Stock Reconciliation" && !frm.doc.scan_mode) { frappe.model.set_value(cdt, cdn, "serial_no", r.message.serial_nos); } - } + }, }); } }, - set_amount_quantity: function(doc, cdt, cdn) { + set_amount_quantity: function (doc, cdt, cdn) { var d = frappe.model.get_doc(cdt, cdn); if (d.qty & d.valuation_rate) { frappe.model.set_value(cdt, cdn, "amount", flt(d.qty) * flt(d.valuation_rate)); @@ -187,37 +192,38 @@ frappe.ui.form.on("Stock Reconciliation", { frappe.model.set_value(cdt, cdn, "amount_difference", flt(d.amount) - flt(d.current_amount)); } }, - company: function(frm) { + company: function (frm) { frm.trigger("toggle_display_account_head"); }, - toggle_display_account_head: function(frm) { - frm.toggle_display(['expense_account', 'cost_center'], - erpnext.is_perpetual_inventory_enabled(frm.doc.company)); + toggle_display_account_head: function (frm) { + frm.toggle_display( + ["expense_account", "cost_center"], + erpnext.is_perpetual_inventory_enabled(frm.doc.company) + ); }, - purpose: function(frm) { + purpose: function (frm) { frm.trigger("set_expense_account"); }, - set_expense_account: function(frm) { + set_expense_account: function (frm) { if (frm.doc.company && erpnext.is_perpetual_inventory_enabled(frm.doc.company)) { return frm.call({ method: "erpnext.stock.doctype.stock_reconciliation.stock_reconciliation.get_difference_account", args: { - "purpose": frm.doc.purpose, - "company": frm.doc.company + purpose: frm.doc.purpose, + company: frm.doc.company, }, - callback: function(r) { + callback: function (r) { if (!r.exc) { frm.set_value("expense_account", r.message); } - } + }, }); } - } + }, }); frappe.ui.form.on("Stock Reconciliation Item", { - - warehouse: function(frm, cdt, cdn) { + warehouse: function (frm, cdt, cdn) { var child = locals[cdt][cdn]; if (child.batch_no && !frm.doc.scan_mode) { frappe.model.set_value(child.cdt, child.cdn, "batch_no", ""); @@ -226,7 +232,7 @@ frappe.ui.form.on("Stock Reconciliation Item", { frm.events.set_valuation_rate_and_qty(frm, cdt, cdn); }, - item_code: function(frm, cdt, cdn) { + item_code: function (frm, cdt, cdn) { var child = locals[cdt][cdn]; if (child.batch_no && !frm.doc.scan_mode) { frappe.model.set_value(cdt, cdn, "batch_no", ""); @@ -235,34 +241,33 @@ frappe.ui.form.on("Stock Reconciliation Item", { frm.events.set_valuation_rate_and_qty(frm, cdt, cdn); }, - batch_no: function(frm, cdt, cdn) { + batch_no: function (frm, cdt, cdn) { frm.events.set_valuation_rate_and_qty(frm, cdt, cdn); }, - qty: function(frm, cdt, cdn) { + qty: function (frm, cdt, cdn) { frm.events.set_amount_quantity(frm, cdt, cdn); }, - valuation_rate: function(frm, cdt, cdn) { + valuation_rate: function (frm, cdt, cdn) { frm.events.set_amount_quantity(frm, cdt, cdn); }, - serial_no: function(frm, cdt, cdn) { + serial_no: function (frm, cdt, cdn) { var child = locals[cdt][cdn]; if (child.serial_no) { - const serial_nos = child.serial_no.trim().split('\n'); + const serial_nos = child.serial_no.trim().split("\n"); frappe.model.set_value(cdt, cdn, "qty", serial_nos.length); } }, - items_add: function(frm, cdt, cdn) { + items_add: function (frm, cdt, cdn) { var item = frappe.get_doc(cdt, cdn); if (!item.warehouse && frm.doc.set_warehouse) { frappe.model.set_value(cdt, cdn, "warehouse", frm.doc.set_warehouse); } }, - }); erpnext.stock.StockReconciliation = class StockReconciliation extends erpnext.stock.StockController { @@ -274,37 +279,36 @@ erpnext.stock.StockReconciliation = class StockReconciliation extends erpnext.st if (me.frm.doc.company && erpnext.is_perpetual_inventory_enabled(me.frm.doc.company)) { this.frm.add_fetch("company", "cost_center", "cost_center"); } - this.frm.fields_dict["expense_account"].get_query = function() { - if(erpnext.is_perpetual_inventory_enabled(me.frm.doc.company)) { + this.frm.fields_dict["expense_account"].get_query = function () { + if (erpnext.is_perpetual_inventory_enabled(me.frm.doc.company)) { return { - "filters": { - 'company': me.frm.doc.company, - "is_group": 0 - } - } + filters: { + company: me.frm.doc.company, + is_group: 0, + }, + }; } - } - this.frm.fields_dict["cost_center"].get_query = function() { - if(erpnext.is_perpetual_inventory_enabled(me.frm.doc.company)) { + }; + this.frm.fields_dict["cost_center"].get_query = function () { + if (erpnext.is_perpetual_inventory_enabled(me.frm.doc.company)) { return { - "filters": { - 'company': me.frm.doc.company, - "is_group": 0 - } - } + filters: { + company: me.frm.doc.company, + is_group: 0, + }, + }; } - } + }; } refresh() { - if(this.frm.doc.docstatus > 0) { + if (this.frm.doc.docstatus > 0) { this.show_stock_ledger(); if (erpnext.is_perpetual_inventory_enabled(this.frm.doc.company)) { this.show_general_ledger(); } } } - }; -cur_frm.cscript = new erpnext.stock.StockReconciliation({frm: cur_frm}); +cur_frm.cscript = new erpnext.stock.StockReconciliation({ frm: cur_frm }); diff --git a/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.js b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.js index 5f81679bade..e8c14372416 100644 --- a/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.js +++ b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.js @@ -1,23 +1,23 @@ // Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Stock Reposting Settings', { - refresh: function(frm) { - frm.trigger('convert_to_item_based_reposting'); +frappe.ui.form.on("Stock Reposting Settings", { + refresh: function (frm) { + frm.trigger("convert_to_item_based_reposting"); }, - convert_to_item_based_reposting: function(frm) { - frm.add_custom_button(__('Convert to Item Based Reposting'), function() { + convert_to_item_based_reposting: function (frm) { + frm.add_custom_button(__("Convert to Item Based Reposting"), function () { frm.call({ - method: 'convert_to_item_wh_reposting', + method: "convert_to_item_wh_reposting", frezz: true, doc: frm.doc, - callback: function(r) { + callback: function (r) { if (!r.exc) { frm.reload_doc(); } - } - }) - }) - } + }, + }); + }); + }, }); diff --git a/erpnext/stock/doctype/stock_settings/stock_settings.js b/erpnext/stock/doctype/stock_settings/stock_settings.js index 89ac4b5fc90..1972b193732 100644 --- a/erpnext/stock/doctype/stock_settings/stock_settings.js +++ b/erpnext/stock/doctype/stock_settings/stock_settings.js @@ -1,29 +1,31 @@ // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Stock Settings', { - refresh: function(frm) { - let filters = function() { +frappe.ui.form.on("Stock Settings", { + refresh: function (frm) { + let filters = function () { return { - filters : { - is_group : 0 - } + filters: { + is_group: 0, + }, }; }; frm.set_query("default_warehouse", filters); frm.set_query("sample_retention_warehouse", filters); }, - allow_negative_stock: function(frm) { + allow_negative_stock: function (frm) { if (!frm.doc.allow_negative_stock) { return; } - let msg = __("Using negative stock disables FIFO/Moving average valuation when inventory is negative."); + let msg = __( + "Using negative stock disables FIFO/Moving average valuation when inventory is negative." + ); msg += " "; - msg += __("This is considered dangerous from accounting point of view.") + msg += __("This is considered dangerous from accounting point of view."); msg += "
              "; - msg += ("Do you still want to enable negative inventory?"); + msg += "Do you still want to enable negative inventory?"; frappe.confirm( msg, @@ -32,5 +34,5 @@ frappe.ui.form.on('Stock Settings', { frm.set_value("allow_negative_stock", 0); } ); - } + }, }); diff --git a/erpnext/stock/doctype/uom_category/uom_category.js b/erpnext/stock/doctype/uom_category/uom_category.js index 48026da1bed..b53190f7482 100644 --- a/erpnext/stock/doctype/uom_category/uom_category.js +++ b/erpnext/stock/doctype/uom_category/uom_category.js @@ -1,8 +1,6 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('UOM Category', { - refresh: function() { - - } +frappe.ui.form.on("UOM Category", { + refresh: function () {}, }); diff --git a/erpnext/stock/doctype/variant_field/variant_field.js b/erpnext/stock/doctype/variant_field/variant_field.js index 13db3f9272d..8455d912531 100644 --- a/erpnext/stock/doctype/variant_field/variant_field.js +++ b/erpnext/stock/doctype/variant_field/variant_field.js @@ -1,8 +1,6 @@ // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Variant Field', { - refresh: function() { - - } +frappe.ui.form.on("Variant Field", { + refresh: function () {}, }); diff --git a/erpnext/stock/doctype/warehouse/warehouse.js b/erpnext/stock/doctype/warehouse/warehouse.js index 746a1cbaf17..4f44db023dd 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.js +++ b/erpnext/stock/doctype/warehouse/warehouse.js @@ -35,18 +35,15 @@ frappe.ui.form.on("Warehouse", { refresh: function (frm) { frm.toggle_display("warehouse_name", frm.doc.__islocal); - frm.toggle_display( - ["address_html", "contact_html"], - !frm.doc.__islocal - ); + frm.toggle_display(["address_html", "contact_html"], !frm.doc.__islocal); 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.set_value("disabled", 1 - frm.doc.disabled); + frm.save(); }); frm.add_custom_button(__("Stock Balance"), function () { @@ -61,25 +58,20 @@ frappe.ui.form.on("Warehouse", { : __("Convert to Group", null, "Warehouse"), function () { convert_to_group_or_ledger(frm); - }, + } ); - } else { frappe.contacts.clear_address_and_contact(frm); } - if (!frm.doc.is_group && frm.doc.__onload && frm.doc.__onload.account) { - frm.add_custom_button( - __("General Ledger", null, "Warehouse"), - function () { - frappe.route_options = { - account: frm.doc.__onload.account, - company: frm.doc.company, - }; - frappe.set_route("query-report", "General Ledger"); - } - ); + frm.add_custom_button(__("General Ledger", null, "Warehouse"), function () { + frappe.route_options = { + account: frm.doc.__onload.account, + company: frm.doc.company, + }; + frappe.set_route("query-report", "General Ledger"); + }); } frm.toggle_enable(["is_group", "company"], false); diff --git a/erpnext/stock/doctype/warehouse/warehouse_tree.js b/erpnext/stock/doctype/warehouse/warehouse_tree.js index eb635e6757d..d9070b33d10 100644 --- a/erpnext/stock/doctype/warehouse/warehouse_tree.js +++ b/erpnext/stock/doctype/warehouse/warehouse_tree.js @@ -1,20 +1,25 @@ -frappe.treeview_settings['Warehouse'] = { +frappe.treeview_settings["Warehouse"] = { get_tree_nodes: "erpnext.stock.doctype.warehouse.warehouse.get_children", add_tree_node: "erpnext.stock.doctype.warehouse.warehouse.add_node", get_tree_root: false, root_label: "Warehouses", - filters: [{ - fieldname: "company", - fieldtype:"Select", - options: erpnext.utils.get_tree_options("company"), - label: __("Company"), - default: erpnext.utils.get_tree_default("company") - }], - fields:[ - {fieldtype:'Data', fieldname: 'warehouse_name', - label:__('New Warehouse Name'), reqd:true}, - {fieldtype:'Check', fieldname:'is_group', label:__('Is Group'), - description: __("Child nodes can be only created under 'Group' type nodes")} + filters: [ + { + fieldname: "company", + fieldtype: "Select", + options: erpnext.utils.get_tree_options("company"), + label: __("Company"), + default: erpnext.utils.get_tree_default("company"), + }, ], - ignore_fields:["parent_warehouse"], -} + fields: [ + { fieldtype: "Data", fieldname: "warehouse_name", label: __("New Warehouse Name"), reqd: true }, + { + fieldtype: "Check", + fieldname: "is_group", + label: __("Is Group"), + description: __("Child nodes can be only created under 'Group' type nodes"), + }, + ], + ignore_fields: ["parent_warehouse"], +}; diff --git a/erpnext/stock/doctype/warehouse_type/warehouse_type.js b/erpnext/stock/doctype/warehouse_type/warehouse_type.js index 4c4b89ce301..521b07a33d7 100644 --- a/erpnext/stock/doctype/warehouse_type/warehouse_type.js +++ b/erpnext/stock/doctype/warehouse_type/warehouse_type.js @@ -1,8 +1,7 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Warehouse Type', { +frappe.ui.form.on("Warehouse Type", { // refresh: function(frm) { - // } }); diff --git a/erpnext/stock/landed_taxes_and_charges_common.js b/erpnext/stock/landed_taxes_and_charges_common.js index 1d76a3d95a1..48cf773f627 100644 --- a/erpnext/stock/landed_taxes_and_charges_common.js +++ b/erpnext/stock/landed_taxes_and_charges_common.js @@ -1,61 +1,74 @@ -let document_list = ['Landed Cost Voucher', 'Stock Entry', 'Subcontracting Order', 'Subcontracting Receipt']; +let document_list = ["Landed Cost Voucher", "Stock Entry", "Subcontracting Order", "Subcontracting Receipt"]; document_list.forEach((doctype) => { frappe.ui.form.on(doctype, { - refresh: function(frm) { - let tax_field = frm.doc.doctype == 'Landed Cost Voucher' ? 'taxes' : 'additional_costs'; - frm.set_query("expense_account", tax_field, function() { + refresh: function (frm) { + let tax_field = frm.doc.doctype == "Landed Cost Voucher" ? "taxes" : "additional_costs"; + frm.set_query("expense_account", tax_field, function () { return { filters: { - "account_type": ['in', ["Tax", "Chargeable", "Income Account", "Expenses Included In Valuation", "Expenses Included In Asset Valuation"]], - "company": frm.doc.company - } + account_type: [ + "in", + [ + "Tax", + "Chargeable", + "Income Account", + "Expenses Included In Valuation", + "Expenses Included In Asset Valuation", + ], + ], + company: frm.doc.company, + }, }; }); }, - set_account_currency: function(frm, cdt, cdn) { + set_account_currency: function (frm, cdt, cdn) { let row = locals[cdt][cdn]; if (row.expense_account) { - frappe.db.get_value('Account', row.expense_account, 'account_currency', function(value) { + frappe.db.get_value("Account", row.expense_account, "account_currency", function (value) { frappe.model.set_value(cdt, cdn, "account_currency", value.account_currency); frm.events.set_exchange_rate(frm, cdt, cdn); }); } }, - set_exchange_rate: function(frm, cdt, cdn) { + set_exchange_rate: function (frm, cdt, cdn) { let row = locals[cdt][cdn]; let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency; if (row.account_currency == company_currency) { row.exchange_rate = 1; - frm.set_df_property('taxes', 'hidden', 1, row.name, 'exchange_rate'); + frm.set_df_property("taxes", "hidden", 1, row.name, "exchange_rate"); } else if (!row.exchange_rate || row.exchange_rate == 1) { - frm.set_df_property('taxes', 'hidden', 0, row.name, 'exchange_rate'); + frm.set_df_property("taxes", "hidden", 0, row.name, "exchange_rate"); frappe.call({ method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_exchange_rate", args: { posting_date: frm.doc.posting_date, account: row.expense_account, account_currency: row.account_currency, - company: frm.doc.company + company: frm.doc.company, }, - callback: function(r) { + callback: function (r) { if (r.message) { frappe.model.set_value(cdt, cdn, "exchange_rate", r.message); } - } + }, }); } - frm.refresh_field('taxes'); + frm.refresh_field("taxes"); }, - set_base_amount: function(frm, cdt, cdn) { + set_base_amount: function (frm, cdt, cdn) { let row = locals[cdt][cdn]; - frappe.model.set_value(cdt, cdn, "base_amount", - flt(flt(row.amount)*row.exchange_rate, precision("base_amount", row))); - } + frappe.model.set_value( + cdt, + cdn, + "base_amount", + flt(flt(row.amount) * row.exchange_rate, precision("base_amount", row)) + ); + }, }); }); diff --git a/erpnext/stock/page/stock_balance/stock_balance.js b/erpnext/stock/page/stock_balance/stock_balance.js index 90b8d453420..a5fba9f98f3 100644 --- a/erpnext/stock/page/stock_balance/stock_balance.js +++ b/erpnext/stock/page/stock_balance/stock_balance.js @@ -1,103 +1,101 @@ -frappe.pages['stock-balance'].on_page_load = function(wrapper) { +frappe.pages["stock-balance"].on_page_load = function (wrapper) { var page = frappe.ui.make_app_page({ parent: wrapper, - title: __('Stock Summary'), - single_column: true + title: __("Stock Summary"), + single_column: true, }); page.start = 0; page.warehouse_field = page.add_field({ - fieldname: 'warehouse', - label: __('Warehouse'), - fieldtype:'Link', - options:'Warehouse', + fieldname: "warehouse", + label: __("Warehouse"), + fieldtype: "Link", + options: "Warehouse", default: frappe.route_options && frappe.route_options.warehouse, - change: function() { + change: function () { page.item_dashboard.start = 0; page.item_dashboard.refresh(); - } + }, }); page.item_field = page.add_field({ - fieldname: 'item_code', - label: __('Item'), - fieldtype:'Link', - options:'Item', + fieldname: "item_code", + label: __("Item"), + fieldtype: "Link", + options: "Item", default: frappe.route_options && frappe.route_options.item_code, - change: function() { + change: function () { page.item_dashboard.start = 0; page.item_dashboard.refresh(); - } + }, }); page.item_group_field = page.add_field({ - fieldname: 'item_group', - label: __('Item Group'), - fieldtype:'Link', - options:'Item Group', + fieldname: "item_group", + label: __("Item Group"), + fieldtype: "Link", + options: "Item Group", default: frappe.route_options && frappe.route_options.item_group, - change: function() { + change: function () { page.item_dashboard.start = 0; page.item_dashboard.refresh(); - } + }, }); page.sort_selector = new frappe.ui.SortSelector({ - parent: page.wrapper.find('.page-form'), + parent: page.wrapper.find(".page-form"), args: { - sort_by: 'projected_qty', - sort_order: 'asc', + sort_by: "projected_qty", + sort_order: "asc", options: [ - {fieldname: 'projected_qty', label: __('Projected qty')}, - {fieldname: 'reserved_qty', label: __('Reserved for sale')}, - {fieldname: 'reserved_qty_for_production', label: __('Reserved for manufacturing')}, - {fieldname: 'reserved_qty_for_sub_contract', label: __('Reserved for sub contracting')}, - {fieldname: 'actual_qty', label: __('Actual qty in stock')}, - ] + { fieldname: "projected_qty", label: __("Projected qty") }, + { fieldname: "reserved_qty", label: __("Reserved for sale") }, + { fieldname: "reserved_qty_for_production", label: __("Reserved for manufacturing") }, + { fieldname: "reserved_qty_for_sub_contract", label: __("Reserved for sub contracting") }, + { fieldname: "actual_qty", label: __("Actual qty in stock") }, + ], }, - change: function(sort_by, sort_order) { + change: function (sort_by, sort_order) { page.item_dashboard.sort_by = sort_by; page.item_dashboard.sort_order = sort_order; page.item_dashboard.start = 0; page.item_dashboard.refresh(); - } + }, }); // page.sort_selector.wrapper.css({'margin-right': '15px', 'margin-top': '4px'}); - frappe.require('item-dashboard.bundle.js', function() { + frappe.require("item-dashboard.bundle.js", function () { page.item_dashboard = new erpnext.stock.ItemDashboard({ parent: page.main, page_length: 20, - method: 'erpnext.stock.dashboard.item_dashboard.get_data', - template: 'item_dashboard_list' - }) + method: "erpnext.stock.dashboard.item_dashboard.get_data", + template: "item_dashboard_list", + }); - page.item_dashboard.before_refresh = function() { + page.item_dashboard.before_refresh = function () { this.item_code = page.item_field.get_value(); this.warehouse = page.warehouse_field.get_value(); this.item_group = page.item_group_field.get_value(); - } + }; page.item_dashboard.refresh(); // item click - var setup_click = function(doctype) { - page.main.on('click', 'a[data-type="'+ doctype.toLowerCase() +'"]', function() { - var name = $(this).attr('data-name'); - var field = page[doctype.toLowerCase() + '_field']; - if(field.get_value()===name) { - frappe.set_route('Form', doctype, name) + var setup_click = function (doctype) { + page.main.on("click", 'a[data-type="' + doctype.toLowerCase() + '"]', function () { + var name = $(this).attr("data-name"); + var field = page[doctype.toLowerCase() + "_field"]; + if (field.get_value() === name) { + frappe.set_route("Form", doctype, name); } else { field.set_input(name); page.item_dashboard.refresh(); } }); - } + }; - setup_click('Item'); - setup_click('Warehouse'); + setup_click("Item"); + setup_click("Warehouse"); }); - - -} +}; diff --git a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js index 61927f51a82..f03c5ca65e8 100644 --- a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js +++ b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js @@ -1,98 +1,98 @@ -frappe.pages['warehouse-capacity-summary'].on_page_load = function(wrapper) { +frappe.pages["warehouse-capacity-summary"].on_page_load = function (wrapper) { var page = frappe.ui.make_app_page({ parent: wrapper, - title: 'Warehouse Capacity Summary', - single_column: true + title: "Warehouse Capacity Summary", + single_column: true, }); - page.set_secondary_action('Refresh', () => page.capacity_dashboard.refresh(), 'refresh'); + page.set_secondary_action("Refresh", () => page.capacity_dashboard.refresh(), "refresh"); page.start = 0; page.company_field = page.add_field({ - fieldname: 'company', - label: __('Company'), - fieldtype: 'Link', - options: 'Company', + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", reqd: 1, default: frappe.defaults.get_default("company"), - change: function() { + change: function () { page.capacity_dashboard.start = 0; page.capacity_dashboard.refresh(); - } + }, }); page.warehouse_field = page.add_field({ - fieldname: 'warehouse', - label: __('Warehouse'), - fieldtype: 'Link', - options: 'Warehouse', - change: function() { + fieldname: "warehouse", + label: __("Warehouse"), + fieldtype: "Link", + options: "Warehouse", + change: function () { page.capacity_dashboard.start = 0; page.capacity_dashboard.refresh(); - } + }, }); page.item_field = page.add_field({ - fieldname: 'item_code', - label: __('Item'), - fieldtype: 'Link', - options: 'Item', - change: function() { + fieldname: "item_code", + label: __("Item"), + fieldtype: "Link", + options: "Item", + change: function () { page.capacity_dashboard.start = 0; page.capacity_dashboard.refresh(); - } + }, }); page.parent_warehouse_field = page.add_field({ - fieldname: 'parent_warehouse', - label: __('Parent Warehouse'), - fieldtype: 'Link', - options: 'Warehouse', - get_query: function() { + fieldname: "parent_warehouse", + label: __("Parent Warehouse"), + fieldtype: "Link", + options: "Warehouse", + get_query: function () { return { filters: { - "is_group": 1 - } + is_group: 1, + }, }; }, - change: function() { + change: function () { page.capacity_dashboard.start = 0; page.capacity_dashboard.refresh(); - } + }, }); page.sort_selector = new frappe.ui.SortSelector({ - parent: page.wrapper.find('.page-form'), + parent: page.wrapper.find(".page-form"), args: { - sort_by: 'stock_capacity', - sort_order: 'desc', + sort_by: "stock_capacity", + sort_order: "desc", options: [ - {fieldname: 'stock_capacity', label: __('Capacity (Stock UOM)')}, - {fieldname: 'percent_occupied', label: __('% Occupied')}, - {fieldname: 'actual_qty', label: __('Balance Qty (Stock)')} - ] + { fieldname: "stock_capacity", label: __("Capacity (Stock UOM)") }, + { fieldname: "percent_occupied", label: __("% Occupied") }, + { fieldname: "actual_qty", label: __("Balance Qty (Stock)") }, + ], }, - change: function(sort_by, sort_order) { + change: function (sort_by, sort_order) { page.capacity_dashboard.sort_by = sort_by; page.capacity_dashboard.sort_order = sort_order; page.capacity_dashboard.start = 0; page.capacity_dashboard.refresh(); - } + }, }); - frappe.require('item-dashboard.bundle.js', function() { - $(frappe.render_template('warehouse_capacity_summary_header')).appendTo(page.main); + frappe.require("item-dashboard.bundle.js", function () { + $(frappe.render_template("warehouse_capacity_summary_header")).appendTo(page.main); page.capacity_dashboard = new erpnext.stock.ItemDashboard({ page_name: "warehouse-capacity-summary", page_length: 10, parent: page.main, - sort_by: 'stock_capacity', - sort_order: 'desc', - method: 'erpnext.stock.dashboard.warehouse_capacity_dashboard.get_data', - template: 'warehouse_capacity_summary' + sort_by: "stock_capacity", + sort_order: "desc", + method: "erpnext.stock.dashboard.warehouse_capacity_dashboard.get_data", + template: "warehouse_capacity_summary", }); - page.capacity_dashboard.before_refresh = function() { + page.capacity_dashboard.before_refresh = function () { this.item_code = page.item_field.get_value(); this.warehouse = page.warehouse_field.get_value(); this.parent_warehouse = page.parent_warehouse_field.get_value(); @@ -101,12 +101,12 @@ frappe.pages['warehouse-capacity-summary'].on_page_load = function(wrapper) { page.capacity_dashboard.refresh(); - let setup_click = function(doctype) { - page.main.on('click', 'a[data-type="'+ doctype.toLowerCase() +'"]', function() { - var name = $(this).attr('data-name'); - var field = page[doctype.toLowerCase() + '_field']; - if (field.get_value()===name) { - frappe.set_route('Form', doctype, name); + let setup_click = function (doctype) { + page.main.on("click", 'a[data-type="' + doctype.toLowerCase() + '"]', function () { + var name = $(this).attr("data-name"); + var field = page[doctype.toLowerCase() + "_field"]; + if (field.get_value() === name) { + frappe.set_route("Form", doctype, name); } else { field.set_input(name); page.capacity_dashboard.refresh(); @@ -114,7 +114,7 @@ frappe.pages['warehouse-capacity-summary'].on_page_load = function(wrapper) { }); }; - setup_click('Item'); - setup_click('Warehouse'); + setup_click("Item"); + setup_click("Warehouse"); }); }; diff --git a/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.js b/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.js index 48a72a2bfe5..a15b69b112a 100644 --- a/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.js +++ b/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.js @@ -2,34 +2,34 @@ // For license information, please see license.txt frappe.query_reports["Batch Item Expiry Status"] = { - "filters": [ + filters: [ { - "fieldname":"from_date", - "label": __("From Date"), - "fieldtype": "Date", - "width": "80", - "default": frappe.sys_defaults.year_start_date, - "reqd": 1, + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + width: "80", + default: frappe.sys_defaults.year_start_date, + reqd: 1, }, { - "fieldname":"to_date", - "label": __("To Date"), - "fieldtype": "Date", - "width": "80", - "default": frappe.datetime.get_today(), - "reqd": 1, + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + width: "80", + default: frappe.datetime.get_today(), + reqd: 1, }, { - "fieldname":"item", - "label": __("Item"), - "fieldtype": "Link", - "options": "Item", - "width": "100", - "get_query": function () { + fieldname: "item", + label: __("Item"), + fieldtype: "Link", + options: "Item", + width: "100", + get_query: function () { return { - filters: {"has_batch_no": 1} - } - } - } - ] -} + filters: { has_batch_no: 1 }, + }; + }, + }, + ], +}; diff --git a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.js b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.js index 74b5a5ae36e..1694abe7c08 100644 --- a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.js +++ b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.js @@ -2,87 +2,90 @@ // License: GNU General Public License v3. See license.txt frappe.query_reports["Batch-Wise Balance History"] = { - "filters": [ + filters: [ { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company"), - "reqd": 1 + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, }, { - "fieldname":"from_date", - "label": __("From Date"), - "fieldtype": "Date", - "width": "80", - "default": frappe.sys_defaults.year_start_date, - "reqd": 1 + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + width: "80", + default: frappe.sys_defaults.year_start_date, + reqd: 1, }, { - "fieldname":"to_date", - "label": __("To Date"), - "fieldtype": "Date", - "width": "80", - "default": frappe.datetime.get_today(), - "reqd": 1 + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + width: "80", + default: frappe.datetime.get_today(), + reqd: 1, }, { - "fieldname":"item_code", - "label": __("Item Code"), - "fieldtype": "Link", - "options": "Item", - "get_query": function() { + fieldname: "item_code", + label: __("Item Code"), + fieldtype: "Link", + options: "Item", + get_query: function () { return { filters: { - "has_batch_no": 1 - } + has_batch_no: 1, + }, }; - } + }, }, { - "fieldname":"warehouse", - "label": __("Warehouse"), - "fieldtype": "Link", - "options": "Warehouse", - "get_query": function() { - let company = frappe.query_report.get_filter_value('company'); + fieldname: "warehouse", + label: __("Warehouse"), + fieldtype: "Link", + options: "Warehouse", + get_query: function () { + let company = frappe.query_report.get_filter_value("company"); return { filters: { - "company": company - } + company: company, + }, }; - } + }, }, { - "fieldname":"batch_no", - "label": __("Batch No"), - "fieldtype": "Link", - "options": "Batch", - "get_query": function() { - let item_code = frappe.query_report.get_filter_value('item_code'); + fieldname: "batch_no", + label: __("Batch No"), + fieldtype: "Link", + options: "Batch", + get_query: function () { + let item_code = frappe.query_report.get_filter_value("item_code"); return { filters: { - "item": item_code - } + item: item_code, + }, }; - } + }, }, ], - "formatter": function (value, row, column, data, default_formatter) { + formatter: function (value, row, column, data, default_formatter) { if (column.fieldname == "Batch" && data && !!data["Batch"]) { value = data["Batch"]; - column.link_onclick = "frappe.query_reports['Batch-Wise Balance History'].set_batch_route_to_stock_ledger(" + JSON.stringify(data) + ")"; + column.link_onclick = + "frappe.query_reports['Batch-Wise Balance History'].set_batch_route_to_stock_ledger(" + + JSON.stringify(data) + + ")"; } value = default_formatter(value, row, column, data); return value; }, - "set_batch_route_to_stock_ledger": function (data) { + set_batch_route_to_stock_ledger: function (data) { frappe.route_options = { - "batch_no": data["Batch"] + batch_no: data["Batch"], }; frappe.set_route("query-report", "Stock Ledger"); - } -} + }, +}; diff --git a/erpnext/stock/report/bom_search/bom_search.js b/erpnext/stock/report/bom_search/bom_search.js index e9e763cb889..b6f3c9633e1 100644 --- a/erpnext/stock/report/bom_search/bom_search.js +++ b/erpnext/stock/report/bom_search/bom_search.js @@ -2,41 +2,41 @@ // For license information, please see license.txt frappe.query_reports["BOM Search"] = { - "filters": [ + filters: [ { fieldname: "item1", label: __("Item 1"), fieldtype: "Link", - options: "Item" + options: "Item", }, { fieldname: "item2", label: __("Item 2"), fieldtype: "Link", - options: "Item" + options: "Item", }, { fieldname: "item3", label: __("Item 3"), fieldtype: "Link", - options: "Item" + options: "Item", }, { fieldname: "item4", label: __("Item 4"), fieldtype: "Link", - options: "Item" + options: "Item", }, { fieldname: "item5", label: __("Item 5"), fieldtype: "Link", - options: "Item" + options: "Item", }, { fieldname: "search_sub_assemblies", label: __("Search Sub Assemblies"), fieldtype: "Check", }, - ] -} + ], +}; diff --git a/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.js b/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.js index d7c50a66979..e61791cc8cd 100644 --- a/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.js +++ b/erpnext/stock/report/cogs_by_item_group/cogs_by_item_group.js @@ -2,30 +2,29 @@ // For license information, please see license.txt /* eslint-disable */ - frappe.query_reports["COGS By Item Group"] = { filters: [ - { - label: __("Company"), - fieldname: "company", - fieldtype: "Link", - options: "Company", - mandatory: true, - default: frappe.defaults.get_user_default("Company"), - }, - { - label: __("From Date"), - fieldname: "from_date", - fieldtype: "Date", - mandatory: true, - default: frappe.datetime.year_start(), - }, - { - label: __("To Date"), - fieldname: "to_date", - fieldtype: "Date", - mandatory: true, - default: frappe.datetime.get_today(), - }, - ] + { + label: __("Company"), + fieldname: "company", + fieldtype: "Link", + options: "Company", + mandatory: true, + default: frappe.defaults.get_user_default("Company"), + }, + { + label: __("From Date"), + fieldname: "from_date", + fieldtype: "Date", + mandatory: true, + default: frappe.datetime.year_start(), + }, + { + label: __("To Date"), + fieldname: "to_date", + fieldtype: "Date", + mandatory: true, + default: frappe.datetime.get_today(), + }, + ], }; diff --git a/erpnext/stock/report/delayed_item_report/delayed_item_report.js b/erpnext/stock/report/delayed_item_report/delayed_item_report.js index 40e6abefeb0..317fa77827a 100644 --- a/erpnext/stock/report/delayed_item_report/delayed_item_report.js +++ b/erpnext/stock/report/delayed_item_report/delayed_item_report.js @@ -3,60 +3,60 @@ /* eslint-disable */ frappe.query_reports["Delayed Item Report"] = { - "filters": [ + filters: [ { fieldname: "company", label: __("Company"), fieldtype: "Link", options: "Company", default: frappe.defaults.get_default("company"), - reqd: 1 + reqd: 1, }, { - fieldname:"from_date", + fieldname: "from_date", label: __("From Date"), fieldtype: "Date", default: frappe.datetime.month_start(), - reqd: 1 + reqd: 1, }, { - fieldname:"to_date", + fieldname: "to_date", label: __("To Date"), fieldtype: "Date", default: frappe.datetime.now_date(), - reqd: 1 + reqd: 1, }, { - fieldname:"sales_order", + fieldname: "sales_order", label: __("Sales Order"), fieldtype: "Link", options: "Sales Order", }, { - fieldname:"customer", + fieldname: "customer", label: __("Customer"), fieldtype: "Link", options: "Customer", }, { - fieldname:"customer_group", + fieldname: "customer_group", label: __("Customer Group"), fieldtype: "Link", options: "Customer Group", }, { - fieldname:"item_group", + fieldname: "item_group", label: __("Item Group"), fieldtype: "Link", options: "Item Group", }, { - fieldname:"based_on", + fieldname: "based_on", label: __("Based On"), fieldtype: "Select", options: ["Delivery Note", "Sales Invoice"], default: "Sales Invoice", - reqd: 1 + reqd: 1, }, - ] -} + ], +}; diff --git a/erpnext/stock/report/delayed_order_report/delayed_order_report.js b/erpnext/stock/report/delayed_order_report/delayed_order_report.js index aab0f3d0d1f..34ea3240de0 100644 --- a/erpnext/stock/report/delayed_order_report/delayed_order_report.js +++ b/erpnext/stock/report/delayed_order_report/delayed_order_report.js @@ -3,60 +3,60 @@ /* eslint-disable */ frappe.query_reports["Delayed Order Report"] = { - "filters": [ + filters: [ { fieldname: "company", label: __("Company"), fieldtype: "Link", options: "Company", default: frappe.defaults.get_default("company"), - reqd: 1 + reqd: 1, }, { - fieldname:"from_date", + fieldname: "from_date", label: __("From Date"), fieldtype: "Date", default: frappe.datetime.month_start(), - reqd: 1 + reqd: 1, }, { - fieldname:"to_date", + fieldname: "to_date", label: __("To Date"), fieldtype: "Date", default: frappe.datetime.now_date(), - reqd: 1 + reqd: 1, }, { - fieldname:"sales_order", + fieldname: "sales_order", label: __("Sales Order"), fieldtype: "Link", options: "Sales Order", }, { - fieldname:"customer", + fieldname: "customer", label: __("Customer"), fieldtype: "Link", options: "Customer", }, { - fieldname:"customer_group", + fieldname: "customer_group", label: __("Customer Group"), fieldtype: "Link", options: "Customer Group", }, { - fieldname:"item_group", + fieldname: "item_group", label: __("Item Group"), fieldtype: "Link", options: "Item Group", }, { - fieldname:"based_on", + fieldname: "based_on", label: __("Based On"), fieldtype: "Select", options: ["Delivery Note", "Sales Invoice"], default: "Sales Invoice", - reqd: 1 + reqd: 1, }, - ] -} + ], +}; diff --git a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.js b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.js index 8a04565c197..5e7dc8b2a63 100644 --- a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.js +++ b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.js @@ -1,8 +1,8 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.require("assets/erpnext/js/sales_trends_filters.js", function() { +frappe.require("assets/erpnext/js/sales_trends_filters.js", function () { frappe.query_reports["Delivery Note Trends"] = { - filters: erpnext.get_sales_trends_filters() - } + filters: erpnext.get_sales_trends_filters(), + }; }); diff --git a/erpnext/stock/report/fifo_queue_vs_qty_after_transaction_comparison/fifo_queue_vs_qty_after_transaction_comparison.js b/erpnext/stock/report/fifo_queue_vs_qty_after_transaction_comparison/fifo_queue_vs_qty_after_transaction_comparison.js index 0b8f49653dd..8fc3590c4d6 100644 --- a/erpnext/stock/report/fifo_queue_vs_qty_after_transaction_comparison/fifo_queue_vs_qty_after_transaction_comparison.js +++ b/erpnext/stock/report/fifo_queue_vs_qty_after_transaction_comparison/fifo_queue_vs_qty_after_transaction_comparison.js @@ -2,48 +2,45 @@ // For license information, please see license.txt /* eslint-disable */ -const DIFFERNCE_FIELD_NAMES = [ - "fifo_qty_diff", - "fifo_value_diff", -]; +const DIFFERNCE_FIELD_NAMES = ["fifo_qty_diff", "fifo_value_diff"]; frappe.query_reports["FIFO Queue vs Qty After Transaction Comparison"] = { - "filters": [ + filters: [ { - "fieldname": "item_code", - "fieldtype": "Link", - "label": "Item", - "options": "Item", - get_query: function() { + fieldname: "item_code", + fieldtype: "Link", + label: "Item", + options: "Item", + get_query: function () { return { - filters: {is_stock_item: 1, has_serial_no: 0} - } - } + filters: { is_stock_item: 1, has_serial_no: 0 }, + }; + }, }, { - "fieldname": "item_group", - "fieldtype": "Link", - "label": "Item Group", - "options": "Item Group", + fieldname: "item_group", + fieldtype: "Link", + label: "Item Group", + options: "Item Group", }, { - "fieldname": "warehouse", - "fieldtype": "Link", - "label": "Warehouse", - "options": "Warehouse", + fieldname: "warehouse", + fieldtype: "Link", + label: "Warehouse", + options: "Warehouse", }, { - "fieldname": "from_date", - "fieldtype": "Date", - "label": "From Posting Date", + fieldname: "from_date", + fieldtype: "Date", + label: "From Posting Date", }, { - "fieldname": "to_date", - "fieldtype": "Date", - "label": "From Posting Date", - } + fieldname: "to_date", + fieldtype: "Date", + label: "From Posting Date", + }, ], - formatter (value, row, column, data, default_formatter) { + formatter(value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); if (DIFFERNCE_FIELD_NAMES.includes(column.fieldname) && Math.abs(data[column.fieldname]) > 0.001) { value = "" + value + ""; diff --git a/erpnext/stock/report/incorrect_balance_qty_after_transaction/incorrect_balance_qty_after_transaction.js b/erpnext/stock/report/incorrect_balance_qty_after_transaction/incorrect_balance_qty_after_transaction.js index bf11277d9c4..0e6f7a0d177 100644 --- a/erpnext/stock/report/incorrect_balance_qty_after_transaction/incorrect_balance_qty_after_transaction.js +++ b/erpnext/stock/report/incorrect_balance_qty_after_transaction/incorrect_balance_qty_after_transaction.js @@ -3,25 +3,25 @@ /* eslint-disable */ frappe.query_reports["Incorrect Balance Qty After Transaction"] = { - "filters": [ + filters: [ { label: __("Company"), fieldtype: "Link", fieldname: "company", options: "Company", default: frappe.defaults.get_user_default("Company"), - reqd: 1 + reqd: 1, }, { - label: __('Item Code'), - fieldtype: 'Link', - fieldname: 'item_code', - options: 'Item' + label: __("Item Code"), + fieldtype: "Link", + fieldname: "item_code", + options: "Item", }, { - label: __('Warehouse'), - fieldtype: 'Link', - fieldname: 'warehouse' - } - ] + label: __("Warehouse"), + fieldtype: "Link", + fieldname: "warehouse", + }, + ], }; diff --git a/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.js b/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.js index c62d48081c2..4d0472ceaa8 100644 --- a/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.js +++ b/erpnext/stock/report/incorrect_serial_no_valuation/incorrect_serial_no_valuation.js @@ -3,33 +3,33 @@ /* eslint-disable */ frappe.query_reports["Incorrect Serial No Valuation"] = { - "filters": [ + filters: [ { - label: __('Item Code'), - fieldtype: 'Link', - fieldname: 'item_code', - options: 'Item', - get_query: function() { + label: __("Item Code"), + fieldtype: "Link", + fieldname: "item_code", + options: "Item", + get_query: function () { return { filters: { - 'has_serial_no': 1 - } - } - } + has_serial_no: 1, + }, + }; + }, }, { - label: __('From Date'), - fieldtype: 'Date', - fieldname: 'from_date', + label: __("From Date"), + fieldtype: "Date", + fieldname: "from_date", reqd: 1, - default: frappe.defaults.get_user_default("year_start_date") + default: frappe.defaults.get_user_default("year_start_date"), }, { - label: __('To Date'), - fieldtype: 'Date', - fieldname: 'to_date', + label: __("To Date"), + fieldtype: "Date", + fieldname: "to_date", reqd: 1, - default: frappe.defaults.get_user_default("year_end_date") - } - ] + default: frappe.defaults.get_user_default("year_end_date"), + }, + ], }; diff --git a/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.js b/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.js index ff424807e3e..65e0b928bb7 100644 --- a/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.js +++ b/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.js @@ -3,34 +3,34 @@ /* eslint-disable */ frappe.query_reports["Incorrect Stock Value Report"] = { - "filters": [ + filters: [ { - "label": __("Company"), - "fieldname": "company", - "fieldtype": "Link", - "options": "Company", - "reqd": 1, - "default": frappe.defaults.get_user_default("Company") + label: __("Company"), + fieldname: "company", + fieldtype: "Link", + options: "Company", + reqd: 1, + default: frappe.defaults.get_user_default("Company"), }, { - "label": __("Account"), - "fieldname": "account", - "fieldtype": "Link", - "options": "Account", - get_query: function() { - var company = frappe.query_report.get_filter_value('company'); + label: __("Account"), + fieldname: "account", + fieldtype: "Link", + options: "Account", + get_query: function () { + var company = frappe.query_report.get_filter_value("company"); return { filters: { - "account_type": "Stock", - "company": company - } - } - } + account_type: "Stock", + company: company, + }, + }; + }, }, { - "label": __("From Date"), - "fieldname": "from_date", - "fieldtype": "Date" - } - ] + label: __("From Date"), + fieldname: "from_date", + fieldtype: "Date", + }, + ], }; diff --git a/erpnext/stock/report/item_price_stock/item_price_stock.js b/erpnext/stock/report/item_price_stock/item_price_stock.js index 7af1dab6a0b..aa2b7aaa4f3 100644 --- a/erpnext/stock/report/item_price_stock/item_price_stock.js +++ b/erpnext/stock/report/item_price_stock/item_price_stock.js @@ -3,12 +3,12 @@ /* eslint-disable */ frappe.query_reports["Item Price Stock"] = { - "filters": [ + filters: [ { - "fieldname":"item_code", - "label": __("Item"), - "fieldtype": "Link", - "options": "Item" - } - ] -} + fieldname: "item_code", + label: __("Item"), + fieldtype: "Link", + options: "Item", + }, + ], +}; diff --git a/erpnext/stock/report/item_prices/item_prices.js b/erpnext/stock/report/item_prices/item_prices.js index 77bca4466d0..868b503eb73 100644 --- a/erpnext/stock/report/item_prices/item_prices.js +++ b/erpnext/stock/report/item_prices/item_prices.js @@ -2,16 +2,16 @@ // For license information, please see license.txt frappe.query_reports["Item Prices"] = { - "filters": [ + filters: [ { - "fieldname": "items", - "label": __("Items Filter"), - "fieldtype": "Select", - "options": "Enabled Items only\nDisabled Items only\nAll Items", - "default": "Enabled Items only", - "on_change": function(query_report) { + fieldname: "items", + label: __("Items Filter"), + fieldtype: "Select", + options: "Enabled Items only\nDisabled Items only\nAll Items", + default: "Enabled Items only", + on_change: function (query_report) { query_report.trigger_refresh(); - } - } - ] -} + }, + }, + ], +}; diff --git a/erpnext/stock/report/item_shortage_report/item_shortage_report.js b/erpnext/stock/report/item_shortage_report/item_shortage_report.js index ca42a331e91..5a6a54734a4 100644 --- a/erpnext/stock/report/item_shortage_report/item_shortage_report.js +++ b/erpnext/stock/report/item_shortage_report/item_shortage_report.js @@ -3,24 +3,24 @@ /* eslint-disable */ frappe.query_reports["Item Shortage Report"] = { - "filters": [ + filters: [ { - "fieldname": "company", - "label": __("Company"), - "fieldtype": "Link", - "width": "80", - "options": "Company", - "reqd": 1, - "default": frappe.defaults.get_default("company") + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + width: "80", + options: "Company", + reqd: 1, + default: frappe.defaults.get_default("company"), }, { - "fieldname": "warehouse", - "label": __("Warehouse"), - "fieldtype": "MultiSelectList", - "width": "100", - get_data: function(txt) { - return frappe.db.get_link_options('Warehouse', txt); - } - } - ] + fieldname: "warehouse", + label: __("Warehouse"), + fieldtype: "MultiSelectList", + width: "100", + get_data: function (txt) { + return frappe.db.get_link_options("Warehouse", txt); + }, + }, + ], }; diff --git a/erpnext/stock/report/item_variant_details/item_variant_details.js b/erpnext/stock/report/item_variant_details/item_variant_details.js index 78eab4050c1..f61e7417724 100644 --- a/erpnext/stock/report/item_variant_details/item_variant_details.js +++ b/erpnext/stock/report/item_variant_details/item_variant_details.js @@ -3,7 +3,7 @@ /* eslint-disable */ frappe.query_reports["Item Variant Details"] = { - "filters": [ + filters: [ { reqd: 1, default: "", @@ -13,9 +13,9 @@ frappe.query_reports["Item Variant Details"] = { fieldtype: "Link", get_query: () => { return { - filters: { "has_variants": 1 } - } - } - } - ] -} + filters: { has_variants: 1 }, + }; + }, + }, + ], +}; diff --git a/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.js b/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.js index 173aad6d5a9..3b12dab939a 100644 --- a/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.js +++ b/erpnext/stock/report/itemwise_recommended_reorder_level/itemwise_recommended_reorder_level.js @@ -2,31 +2,31 @@ // License: GNU General Public License v3. See license.txt frappe.query_reports["Itemwise Recommended Reorder Level"] = { - "filters": [ + filters: [ { - "fieldname":"from_date", - "label": __("From Date"), - "fieldtype": "Date", - "default": frappe.sys_defaults.year_start_date + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.sys_defaults.year_start_date, }, { - "fieldname":"to_date", - "label": __("To Date"), - "fieldtype": "Date", - "default": frappe.datetime.get_today() + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.datetime.get_today(), }, { - "fieldname":"item_group", - "label": __("Item Group"), - "fieldtype": "Link", - "options": "Item Group", - "reqd": 1 + fieldname: "item_group", + label: __("Item Group"), + fieldtype: "Link", + options: "Item Group", + reqd: 1, }, { - "fieldname":"brand", - "label": __("Brand"), - "fieldtype": "Link", - "options": "Brand" - } - ] -} + fieldname: "brand", + label: __("Brand"), + fieldtype: "Link", + options: "Brand", + }, + ], +}; diff --git a/erpnext/stock/report/product_bundle_balance/product_bundle_balance.js b/erpnext/stock/report/product_bundle_balance/product_bundle_balance.js index 4458a7245f3..5cef5c70341 100644 --- a/erpnext/stock/report/product_bundle_balance/product_bundle_balance.js +++ b/erpnext/stock/report/product_bundle_balance/product_bundle_balance.js @@ -2,51 +2,51 @@ // For license information, please see license.txt frappe.query_reports["Product Bundle Balance"] = { - "filters": [ + filters: [ { - "fieldname":"date", - "label": __("Date"), - "fieldtype": "Date", - "width": "80", - "reqd": 1, - "default": frappe.datetime.get_today(), + fieldname: "date", + label: __("Date"), + fieldtype: "Date", + width: "80", + reqd: 1, + default: frappe.datetime.get_today(), }, { - "fieldname": "item_code", - "label": __("Item"), - "fieldtype": "Link", - "width": "80", - "options": "Item", - "get_query": function() { + fieldname: "item_code", + label: __("Item"), + fieldtype: "Link", + width: "80", + options: "Item", + get_query: function () { return { query: "erpnext.controllers.queries.item_query", - filters: {"is_stock_item": 0} + filters: { is_stock_item: 0 }, }; - } + }, }, { - "fieldname": "item_group", - "label": __("Item Group"), - "fieldtype": "Link", - "width": "80", - "options": "Item Group" + fieldname: "item_group", + label: __("Item Group"), + fieldtype: "Link", + width: "80", + options: "Item Group", }, { - "fieldname":"brand", - "label": __("Brand"), - "fieldtype": "Link", - "options": "Brand" + fieldname: "brand", + label: __("Brand"), + fieldtype: "Link", + options: "Brand", }, { - "fieldname": "warehouse", - "label": __("Warehouse"), - "fieldtype": "Link", - "width": "80", - "options": "Warehouse" + fieldname: "warehouse", + label: __("Warehouse"), + fieldtype: "Link", + width: "80", + options: "Warehouse", }, ], - "initial_depth": 0, - "formatter": function(value, row, column, data, default_formatter) { + initial_depth: 0, + formatter: function (value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); if (!data.parent_item) { value = $(`${value}`); @@ -54,5 +54,5 @@ frappe.query_reports["Product Bundle Balance"] = { value = $value.wrap("

              ").parent().html(); } return value; - } + }, }; diff --git a/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.js b/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.js index 695efacb694..bddfe5d7705 100644 --- a/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.js +++ b/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.js @@ -1,8 +1,8 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt -frappe.require("assets/erpnext/js/purchase_trends_filters.js", function() { +frappe.require("assets/erpnext/js/purchase_trends_filters.js", function () { frappe.query_reports["Purchase Receipt Trends"] = { - filters: erpnext.get_purchase_trends_filters() - } + filters: erpnext.get_purchase_trends_filters(), + }; }); diff --git a/erpnext/stock/report/serial_no_ledger/serial_no_ledger.js b/erpnext/stock/report/serial_no_ledger/serial_no_ledger.js index 616312e3118..6c2c17db52c 100644 --- a/erpnext/stock/report/serial_no_ledger/serial_no_ledger.js +++ b/erpnext/stock/report/serial_no_ledger/serial_no_ledger.js @@ -3,50 +3,50 @@ /* eslint-disable */ frappe.query_reports["Serial No Ledger"] = { - "filters": [ + filters: [ { - 'label': __('Item Code'), - 'fieldtype': 'Link', - 'fieldname': 'item_code', - 'reqd': 1, - 'options': 'Item', - get_query: function() { + label: __("Item Code"), + fieldtype: "Link", + fieldname: "item_code", + reqd: 1, + options: "Item", + get_query: function () { return { filters: { - 'has_serial_no': 1 - } - } - } + has_serial_no: 1, + }, + }; + }, }, { - 'label': __('Serial No'), - 'fieldtype': 'Link', - 'fieldname': 'serial_no', - 'options': 'Serial No', - 'reqd': 1 + label: __("Serial No"), + fieldtype: "Link", + fieldname: "serial_no", + options: "Serial No", + reqd: 1, }, { - 'label': __('Warehouse'), - 'fieldtype': 'Link', - 'fieldname': 'warehouse', - 'options': 'Warehouse', - get_query: function() { - let company = frappe.query_report.get_filter_value('company'); + label: __("Warehouse"), + fieldtype: "Link", + fieldname: "warehouse", + options: "Warehouse", + get_query: function () { + let company = frappe.query_report.get_filter_value("company"); if (company) { return { filters: { - 'company': company - } - } + company: company, + }, + }; } - } + }, }, { - 'label': __('As On Date'), - 'fieldtype': 'Date', - 'fieldname': 'posting_date', - 'default': frappe.datetime.get_today() + label: __("As On Date"), + fieldtype: "Date", + fieldname: "posting_date", + default: frappe.datetime.get_today(), }, - ] + ], }; diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.js b/erpnext/stock/report/stock_ageing/stock_ageing.js index db463b7ca09..641084149ab 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.js +++ b/erpnext/stock/report/stock_ageing/stock_ageing.js @@ -2,74 +2,74 @@ // License: GNU General Public License v3. See license.txt frappe.query_reports["Stock Ageing"] = { - "filters": [ + filters: [ { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company"), - "reqd": 1 + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, }, { - "fieldname":"to_date", - "label": __("As On Date"), - "fieldtype": "Date", - "default": frappe.datetime.get_today(), - "reqd": 1 + fieldname: "to_date", + label: __("As On Date"), + fieldtype: "Date", + default: frappe.datetime.get_today(), + reqd: 1, }, { - "fieldname":"warehouse", - "label": __("Warehouse"), - "fieldtype": "Link", - "options": "Warehouse", + fieldname: "warehouse", + label: __("Warehouse"), + fieldtype: "Link", + options: "Warehouse", get_query: () => { const company = frappe.query_report.get_filter_value("company"); return { filters: { - ...company && {company}, - } + ...(company && { company }), + }, }; - } + }, }, { - "fieldname":"item_code", - "label": __("Item"), - "fieldtype": "Link", - "options": "Item" + fieldname: "item_code", + label: __("Item"), + fieldtype: "Link", + options: "Item", }, { - "fieldname":"brand", - "label": __("Brand"), - "fieldtype": "Link", - "options": "Brand" + fieldname: "brand", + label: __("Brand"), + fieldtype: "Link", + options: "Brand", }, { - "fieldname":"range1", - "label": __("Ageing Range 1"), - "fieldtype": "Int", - "default": "30", - "reqd": 1 + 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: "range2", + label: __("Ageing Range 2"), + fieldtype: "Int", + default: "60", + reqd: 1, }, { - "fieldname":"range3", - "label": __("Ageing Range 3"), - "fieldtype": "Int", - "default": "90", - "reqd": 1 + fieldname: "range3", + label: __("Ageing Range 3"), + fieldtype: "Int", + default: "90", + reqd: 1, }, { - "fieldname":"show_warehouse_wise_stock", - "label": __("Show Warehouse-wise Stock"), - "fieldtype": "Check", - "default": 0 - } - ] -} + fieldname: "show_warehouse_wise_stock", + label: __("Show Warehouse-wise Stock"), + fieldtype: "Check", + default: 0, + }, + ], +}; diff --git a/erpnext/stock/report/stock_analytics/stock_analytics.js b/erpnext/stock/report/stock_analytics/stock_analytics.js index 071bfa22959..0cfed95ba6b 100644 --- a/erpnext/stock/report/stock_analytics/stock_analytics.js +++ b/erpnext/stock/report/stock_analytics/stock_analytics.js @@ -3,38 +3,38 @@ /* eslint-disable */ frappe.query_reports["Stock Analytics"] = { - "filters": [ + filters: [ { fieldname: "item_group", label: __("Item Group"), fieldtype: "Link", - options:"Item Group", + options: "Item Group", default: "", }, { fieldname: "item_code", label: __("Item"), fieldtype: "Link", - options:"Item", + options: "Item", default: "", - get_query: () => ({filters: { 'is_stock_item': 1 }}), + get_query: () => ({ filters: { is_stock_item: 1 } }), }, { fieldname: "value_quantity", label: __("Value Or Qty"), fieldtype: "Select", options: [ - { "value": "Value", "label": __("Value") }, - { "value": "Quantity", "label": __("Quantity") } + { value: "Value", label: __("Value") }, + { value: "Quantity", label: __("Quantity") }, ], default: "Value", - reqd: 1 + reqd: 1, }, { fieldname: "brand", label: __("Brand"), fieldtype: "Link", - options:"Brand", + options: "Brand", default: "", }, { @@ -51,92 +51,91 @@ frappe.query_reports["Stock Analytics"] = { fieldtype: "Link", options: "Warehouse", default: "", - get_query: function() { - const company = frappe.query_report.get_filter_value('company'); + get_query: function () { + const company = frappe.query_report.get_filter_value("company"); return { - filters: { 'company': company } - } - } + filters: { company: company }, + }; + }, }, { fieldname: "from_date", label: __("From Date"), fieldtype: "Date", default: frappe.defaults.get_global_default("year_start_date"), - reqd: 1 + reqd: 1, }, { - fieldname:"to_date", + fieldname: "to_date", label: __("To Date"), fieldtype: "Date", default: frappe.defaults.get_global_default("year_end_date"), - reqd: 1 + reqd: 1, }, { fieldname: "range", label: __("Range"), fieldtype: "Select", options: [ - { "value": "Weekly", "label": __("Weekly") }, - { "value": "Monthly", "label": __("Monthly") }, - { "value": "Quarterly", "label": __("Quarterly") }, - { "value": "Yearly", "label": __("Yearly") } + { value: "Weekly", label: __("Weekly") }, + { value: "Monthly", label: __("Monthly") }, + { value: "Quarterly", label: __("Quarterly") }, + { value: "Yearly", label: __("Yearly") }, ], default: "Monthly", - reqd: 1 - } + reqd: 1, + }, ], - after_datatable_render: function(datatable_obj) { - $(datatable_obj.wrapper).find(".dt-row-0").find('input[type=checkbox]').click(); + after_datatable_render: function (datatable_obj) { + $(datatable_obj.wrapper).find(".dt-row-0").find("input[type=checkbox]").click(); }, get_datatable_options(options) { return Object.assign(options, { checkboxColumn: true, events: { - onCheckRow: function(data) { + onCheckRow: function (data) { row_name = data[2].content; row_values = data.slice(7).map(function (column) { return column.content; - }) - entry = { - 'name':row_name, - 'values':row_values - } + }); + entry = { + name: row_name, + values: row_values, + }; let raw_data = frappe.query_report.chart.data; let new_datasets = raw_data.datasets; var found = false; - for(var i=0; i < new_datasets.length;i++){ - if(new_datasets[i].name == row_name){ + for (var i = 0; i < new_datasets.length; i++) { + if (new_datasets[i].name == row_name) { found = true; - new_datasets.splice(i,1); + new_datasets.splice(i, 1); break; } } - if(!found){ + if (!found) { new_datasets.push(entry); } let new_data = { labels: raw_data.labels, - datasets: new_datasets - } + datasets: new_datasets, + }; setTimeout(() => { - frappe.query_report.chart.update(new_data) - },500) - + frappe.query_report.chart.update(new_data); + }, 500); setTimeout(() => { frappe.query_report.chart.draw(true); - }, 1000) + }, 1000); frappe.query_report.raw_chart_data = new_data; }, - } + }, }); - } -} + }, +}; diff --git a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.js b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.js index 254f5273be2..6204c4f725f 100644 --- a/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.js +++ b/erpnext/stock/report/stock_and_account_value_comparison/stock_and_account_value_comparison.js @@ -3,35 +3,35 @@ /* eslint-disable */ frappe.query_reports["Stock and Account Value Comparison"] = { - "filters": [ + filters: [ { - "label": __("Company"), - "fieldname": "company", - "fieldtype": "Link", - "options": "Company", - "reqd": 1, - "default": frappe.defaults.get_user_default("Company") + label: __("Company"), + fieldname: "company", + fieldtype: "Link", + options: "Company", + reqd: 1, + default: frappe.defaults.get_user_default("Company"), }, { - "label": __("Account"), - "fieldname": "account", - "fieldtype": "Link", - "options": "Account", - get_query: function() { - var company = frappe.query_report.get_filter_value('company'); + label: __("Account"), + fieldname: "account", + fieldtype: "Link", + options: "Account", + get_query: function () { + var company = frappe.query_report.get_filter_value("company"); return { filters: { - "account_type": "Stock", - "company": company - } - } - } + account_type: "Stock", + company: company, + }, + }; + }, }, { - "label": __("As On Date"), - "fieldname": "as_on_date", - "fieldtype": "Date", - "default": frappe.datetime.get_today(), + label: __("As On Date"), + fieldname: "as_on_date", + fieldtype: "Date", + default: frappe.datetime.get_today(), }, ], @@ -42,7 +42,7 @@ frappe.query_reports["Stock and Account Value Comparison"] = { }, onload(report) { - report.page.add_inner_button(__("Create Reposting Entries"), function() { + report.page.add_inner_button(__("Create Reposting Entries"), function () { let message = `

              Reposting Entries will change the value of @@ -54,7 +54,7 @@ frappe.query_reports["Stock and Account Value Comparison"] = {

              `; let indexes = frappe.query_report.datatable.rowmanager.getCheckedRows(); - let selected_rows = indexes.map(i => frappe.query_report.data[i]); + let selected_rows = indexes.map((i) => frappe.query_report.data[i]); if (!selected_rows.length) { frappe.throw(__("Please select rows to create Reposting Entries")); @@ -65,11 +65,10 @@ frappe.query_reports["Stock and Account Value Comparison"] = { method: "erpnext.stock.report.stock_and_account_value_comparison.stock_and_account_value_comparison.create_reposting_entries", args: { rows: selected_rows, - company: frappe.query_report.get_filter_values().company - } + company: frappe.query_report.get_filter_values().company, + }, }); - }); }); - } + }, }; diff --git a/erpnext/stock/report/stock_balance/stock_balance.js b/erpnext/stock/report/stock_balance/stock_balance.js index fe6e83eddad..ca2c053fdb1 100644 --- a/erpnext/stock/report/stock_balance/stock_balance.js +++ b/erpnext/stock/report/stock_balance/stock_balance.js @@ -2,119 +2,118 @@ // For license information, please see license.txt frappe.query_reports["Stock Balance"] = { - "filters": [ + filters: [ { - "fieldname": "company", - "label": __("Company"), - "fieldtype": "Link", - "width": "80", - "options": "Company", - "default": frappe.defaults.get_default("company") + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + width: "80", + options: "Company", + default: frappe.defaults.get_default("company"), }, { - "fieldname":"from_date", - "label": __("From Date"), - "fieldtype": "Date", - "width": "80", - "reqd": 1, - "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + width: "80", + reqd: 1, + default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), }, { - "fieldname":"to_date", - "label": __("To Date"), - "fieldtype": "Date", - "width": "80", - "reqd": 1, - "default": frappe.datetime.get_today() + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + width: "80", + reqd: 1, + default: frappe.datetime.get_today(), }, { - "fieldname": "item_group", - "label": __("Item Group"), - "fieldtype": "Link", - "width": "80", - "options": "Item Group" + fieldname: "item_group", + label: __("Item Group"), + fieldtype: "Link", + width: "80", + options: "Item Group", }, { - "fieldname": "item_code", - "label": __("Item"), - "fieldtype": "Link", - "width": "80", - "options": "Item", - "get_query": function() { + fieldname: "item_code", + label: __("Item"), + fieldtype: "Link", + width: "80", + options: "Item", + get_query: function () { return { query: "erpnext.controllers.queries.item_query", }; - } + }, }, { - "fieldname": "warehouse", - "label": __("Warehouse"), - "fieldtype": "Link", - "width": "80", - "options": "Warehouse", + fieldname: "warehouse", + label: __("Warehouse"), + fieldtype: "Link", + width: "80", + options: "Warehouse", get_query: () => { let warehouse_type = frappe.query_report.get_filter_value("warehouse_type"); let company = frappe.query_report.get_filter_value("company"); return { filters: { - ...warehouse_type && {warehouse_type}, - ...company && {company} - } - } - } + ...(warehouse_type && { warehouse_type }), + ...(company && { company }), + }, + }; + }, }, { - "fieldname": "warehouse_type", - "label": __("Warehouse Type"), - "fieldtype": "Link", - "width": "80", - "options": "Warehouse Type" + fieldname: "warehouse_type", + label: __("Warehouse Type"), + fieldtype: "Link", + width: "80", + options: "Warehouse Type", }, { - "fieldname": "valuation_field_type", - "label": __("Valuation Field Type"), - "fieldtype": "Select", - "width": "80", - "options": "Currency\nFloat", - "default": "Currency" + fieldname: "valuation_field_type", + label: __("Valuation Field Type"), + fieldtype: "Select", + width: "80", + options: "Currency\nFloat", + default: "Currency", }, { - "fieldname":"include_uom", - "label": __("Include UOM"), - "fieldtype": "Link", - "options": "UOM" + fieldname: "include_uom", + label: __("Include UOM"), + fieldtype: "Link", + options: "UOM", }, { - "fieldname": "show_variant_attributes", - "label": __("Show Variant Attributes"), - "fieldtype": "Check" + fieldname: "show_variant_attributes", + label: __("Show Variant Attributes"), + fieldtype: "Check", }, { - "fieldname": 'show_stock_ageing_data', - "label": __('Show Stock Ageing Data'), - "fieldtype": 'Check' + fieldname: "show_stock_ageing_data", + label: __("Show Stock Ageing Data"), + fieldtype: "Check", }, { - "fieldname": 'ignore_closing_balance', - "label": __('Ignore Closing Balance'), - "fieldtype": 'Check', - "default": 0 + fieldname: "ignore_closing_balance", + label: __("Ignore Closing Balance"), + fieldtype: "Check", + default: 0, }, ], - "formatter": function (value, row, column, data, default_formatter) { + formatter: function (value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); if (column.fieldname == "out_qty" && data && data.out_qty > 0) { value = "" + value + ""; - } - else if (column.fieldname == "in_qty" && data && data.in_qty > 0) { + } else if (column.fieldname == "in_qty" && data && data.in_qty > 0) { value = "" + value + ""; } return value; - } + }, }; -erpnext.utils.add_inventory_dimensions('Stock Balance', 8); \ No newline at end of file +erpnext.utils.add_inventory_dimensions("Stock Balance", 8); diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.js b/erpnext/stock/report/stock_ledger/stock_ledger.js index b00b422a67a..2c670bee9c1 100644 --- a/erpnext/stock/report/stock_ledger/stock_ledger.js +++ b/erpnext/stock/report/stock_ledger/stock_ledger.js @@ -2,102 +2,101 @@ // License: GNU General Public License v3. See license.txt frappe.query_reports["Stock Ledger"] = { - "filters": [ + filters: [ { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company"), - "reqd": 1 + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, }, { - "fieldname":"from_date", - "label": __("From Date"), - "fieldtype": "Date", - "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), - "reqd": 1 + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), + reqd: 1, }, { - "fieldname":"to_date", - "label": __("To Date"), - "fieldtype": "Date", - "default": frappe.datetime.get_today(), - "reqd": 1 + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.datetime.get_today(), + reqd: 1, }, { - "fieldname":"warehouse", - "label": __("Warehouse"), - "fieldtype": "Link", - "options": "Warehouse", - "get_query": function() { - const company = frappe.query_report.get_filter_value('company'); + fieldname: "warehouse", + label: __("Warehouse"), + fieldtype: "Link", + options: "Warehouse", + get_query: function () { + const company = frappe.query_report.get_filter_value("company"); return { - filters: { 'company': company } - } - } + filters: { company: company }, + }; + }, }, { - "fieldname":"item_code", - "label": __("Item"), - "fieldtype": "Link", - "options": "Item", - "get_query": function() { + fieldname: "item_code", + label: __("Item"), + fieldtype: "Link", + options: "Item", + get_query: function () { return { - query: "erpnext.controllers.queries.item_query" - } - } + query: "erpnext.controllers.queries.item_query", + }; + }, }, { - "fieldname":"item_group", - "label": __("Item Group"), - "fieldtype": "Link", - "options": "Item Group" + fieldname: "item_group", + label: __("Item Group"), + fieldtype: "Link", + options: "Item Group", }, { - "fieldname":"batch_no", - "label": __("Batch No"), - "fieldtype": "Link", - "options": "Batch" + fieldname: "batch_no", + label: __("Batch No"), + fieldtype: "Link", + options: "Batch", }, { - "fieldname":"brand", - "label": __("Brand"), - "fieldtype": "Link", - "options": "Brand" + fieldname: "brand", + label: __("Brand"), + fieldtype: "Link", + options: "Brand", }, { - "fieldname":"voucher_no", - "label": __("Voucher #"), - "fieldtype": "Data" + fieldname: "voucher_no", + label: __("Voucher #"), + fieldtype: "Data", }, { - "fieldname":"project", - "label": __("Project"), - "fieldtype": "Link", - "options": "Project" + fieldname: "project", + label: __("Project"), + fieldtype: "Link", + options: "Project", }, { - "fieldname":"include_uom", - "label": __("Include UOM"), - "fieldtype": "Link", - "options": "UOM" + fieldname: "include_uom", + label: __("Include UOM"), + fieldtype: "Link", + options: "UOM", }, { - "fieldname": "valuation_field_type", - "label": __("Valuation Field Type"), - "fieldtype": "Select", - "width": "80", - "options": "Currency\nFloat", - "default": "Currency" + fieldname: "valuation_field_type", + label: __("Valuation Field Type"), + fieldtype: "Select", + width: "80", + options: "Currency\nFloat", + default: "Currency", }, ], - "formatter": function (value, row, column, data, default_formatter) { + formatter: function (value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); if (column.fieldname == "out_qty" && data && data.out_qty < 0) { value = "" + value + ""; - } - else if (column.fieldname == "in_qty" && data && data.in_qty > 0) { + } else if (column.fieldname == "in_qty" && data && data.in_qty > 0) { value = "" + value + ""; } @@ -105,4 +104,4 @@ frappe.query_reports["Stock Ledger"] = { }, }; -erpnext.utils.add_inventory_dimensions('Stock Ledger', 10); \ No newline at end of file +erpnext.utils.add_inventory_dimensions("Stock Ledger", 10); 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 94e0b2dce3b..166837dcc78 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 @@ -3,42 +3,42 @@ /* eslint-disable */ const DIFFERENCE_FIELD_NAMES = [ - 'difference_in_qty', - 'fifo_qty_diff', - 'fifo_value_diff', - 'fifo_valuation_diff', - 'valuation_diff', - 'fifo_difference_diff', - 'diff_value_diff' + "difference_in_qty", + "fifo_qty_diff", + "fifo_value_diff", + "fifo_valuation_diff", + "valuation_diff", + "fifo_difference_diff", + "diff_value_diff", ]; -frappe.query_reports['Stock Ledger Invariant Check'] = { - 'filters': [ +frappe.query_reports["Stock Ledger Invariant Check"] = { + filters: [ { - 'fieldname': 'item_code', - 'fieldtype': 'Link', - 'label': 'Item', - 'mandatory': 1, - 'options': 'Item', - get_query: function() { + fieldname: "item_code", + fieldtype: "Link", + label: "Item", + mandatory: 1, + options: "Item", + get_query: function () { return { - filters: {is_stock_item: 1, has_serial_no: 0} - } - } + filters: { is_stock_item: 1, has_serial_no: 0 }, + }; + }, }, { - 'fieldname': 'warehouse', - 'fieldtype': 'Link', - 'label': 'Warehouse', - 'mandatory': 1, - 'options': 'Warehouse', - } + fieldname: "warehouse", + fieldtype: "Link", + label: "Warehouse", + mandatory: 1, + options: "Warehouse", + }, ], - formatter (value, row, column, data, default_formatter) { + formatter(value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); if (DIFFERENCE_FIELD_NAMES.includes(column.fieldname) && Math.abs(data[column.fieldname]) > 0.001) { - value = '' + value + ''; + value = '' + value + ""; } return value; }, @@ -50,7 +50,7 @@ frappe.query_reports['Stock Ledger Invariant Check'] = { }, onload(report) { - report.page.add_inner_button(__('Create Reposting Entry'), () => { + report.page.add_inner_button(__("Create Reposting Entry"), () => { let message = `

              @@ -62,23 +62,21 @@ frappe.query_reports['Stock Ledger Invariant Check'] = {

              Are you sure you want to create a Reposting Entry?

              `; let indexes = frappe.query_report.datatable.rowmanager.getCheckedRows(); - let selected_rows = indexes.map(i => frappe.query_report.data[i]); + let selected_rows = indexes.map((i) => frappe.query_report.data[i]); if (!selected_rows.length) { - frappe.throw(__('Please select a row to create a Reposting Entry')); - } - else if (selected_rows.length > 1) { - frappe.throw(__('Please select only one row to create a Reposting Entry')); - } - else { + frappe.throw(__("Please select a row to create a Reposting Entry")); + } else if (selected_rows.length > 1) { + frappe.throw(__("Please select only one row to create a Reposting Entry")); + } else { frappe.confirm(__(message), () => { frappe.call({ - method: 'erpnext.stock.report.stock_ledger_invariant_check.stock_ledger_invariant_check.create_reposting_entries', + method: "erpnext.stock.report.stock_ledger_invariant_check.stock_ledger_invariant_check.create_reposting_entries", args: { rows: selected_rows, item_code: frappe.query_report.get_filter_values().item_code, warehouse: frappe.query_report.get_filter_values().warehouse, - } + }, }); }); } 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 bf3a397feef..07e7b59b514 100644 --- a/erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.js +++ b/erpnext/stock/report/stock_ledger_variance/stock_ledger_variance.js @@ -8,60 +8,55 @@ const DIFFERENCE_FIELD_NAMES = [ "fifo_valuation_diff", "valuation_diff", "fifo_difference_diff", - "diff_value_diff" + "diff_value_diff", ]; frappe.query_reports["Stock Ledger Variance"] = { - "filters": [ + filters: [ { - "fieldname": "company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "reqd": 1, - "default": frappe.defaults.get_user_default("Company") + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + reqd: 1, + default: frappe.defaults.get_user_default("Company"), }, { - "fieldname": "item_code", - "fieldtype": "Link", - "label": __("Item"), - "options": "Item", - get_query: function() { + fieldname: "item_code", + fieldtype: "Link", + label: __("Item"), + options: "Item", + get_query: function () { return { - filters: {is_stock_item: 1, has_serial_no: 0} - } - } + filters: { is_stock_item: 1, has_serial_no: 0 }, + }; + }, }, { - "fieldname": "warehouse", - "fieldtype": "Link", - "label": __("Warehouse"), - "options": "Warehouse", - get_query: function() { + fieldname: "warehouse", + fieldtype: "Link", + label: __("Warehouse"), + options: "Warehouse", + get_query: function () { return { - filters: {is_group: 0, disabled: 0} - } - } + filters: { is_group: 0, disabled: 0 }, + }; + }, }, { - "fieldname": "difference_in", - "fieldtype": "Select", - "label": __("Difference In"), - "options": [ - "", - "Qty", - "Value", - "Valuation", - ], + fieldname: "difference_in", + fieldtype: "Select", + label: __("Difference In"), + options: ["", "Qty", "Value", "Valuation"], }, { - "fieldname": "include_disabled", - "fieldtype": "Check", - "label": __("Include Disabled"), - } + fieldname: "include_disabled", + fieldtype: "Check", + label: __("Include Disabled"), + }, ], - formatter (value, row, column, data, default_formatter) { + formatter(value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); if (DIFFERENCE_FIELD_NAMES.includes(column.fieldname) && Math.abs(data[column.fieldname]) > 0.001) { @@ -78,7 +73,7 @@ frappe.query_reports["Stock Ledger Variance"] = { }, onload(report) { - report.page.add_inner_button(__('Create Reposting Entries'), () => { + report.page.add_inner_button(__("Create Reposting Entries"), () => { let message = `

              @@ -90,7 +85,7 @@ frappe.query_reports["Stock Ledger Variance"] = {

              Are you sure you want to create Reposting Entries?

              `; let indexes = frappe.query_report.datatable.rowmanager.getCheckedRows(); - let selected_rows = indexes.map(i => frappe.query_report.data[i]); + let selected_rows = indexes.map((i) => frappe.query_report.data[i]); if (!selected_rows.length) { frappe.throw(__("Please select rows to create Reposting Entries")); @@ -98,10 +93,10 @@ frappe.query_reports["Stock Ledger Variance"] = { frappe.confirm(__(message), () => { frappe.call({ - method: 'erpnext.stock.report.stock_ledger_invariant_check.stock_ledger_invariant_check.create_reposting_entries', + method: "erpnext.stock.report.stock_ledger_invariant_check.stock_ledger_invariant_check.create_reposting_entries", args: { rows: selected_rows, - } + }, }); }); }); diff --git a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.js b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.js index cb109f8050d..6e333aadfa5 100644 --- a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.js +++ b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.js @@ -2,55 +2,55 @@ // License: GNU General Public License v3. See license.txt frappe.query_reports["Stock Projected Qty"] = { - "filters": [ + filters: [ { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company") + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), }, { - "fieldname":"warehouse", - "label": __("Warehouse"), - "fieldtype": "Link", - "options": "Warehouse", - "get_query": () => { + fieldname: "warehouse", + label: __("Warehouse"), + fieldtype: "Link", + options: "Warehouse", + get_query: () => { return { filters: { - company: frappe.query_report.get_filter_value('company') - } - } - } + company: frappe.query_report.get_filter_value("company"), + }, + }; + }, }, { - "fieldname":"item_code", - "label": __("Item"), - "fieldtype": "Link", - "options": "Item", - "get_query": function() { + fieldname: "item_code", + label: __("Item"), + fieldtype: "Link", + options: "Item", + get_query: function () { return { - query: "erpnext.controllers.queries.item_query" - } - } + query: "erpnext.controllers.queries.item_query", + }; + }, }, { - "fieldname":"item_group", - "label": __("Item Group"), - "fieldtype": "Link", - "options": "Item Group" + fieldname: "item_group", + label: __("Item Group"), + fieldtype: "Link", + options: "Item Group", }, { - "fieldname":"brand", - "label": __("Brand"), - "fieldtype": "Link", - "options": "Brand" + fieldname: "brand", + label: __("Brand"), + fieldtype: "Link", + options: "Brand", }, { - "fieldname":"include_uom", - "label": __("Include UOM"), - "fieldtype": "Link", - "options": "UOM" - } - ] -} + fieldname: "include_uom", + label: __("Include UOM"), + fieldtype: "Link", + options: "UOM", + }, + ], +}; diff --git a/erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.js b/erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.js index 2a0fd4025cc..b0f5f5ccd5a 100644 --- a/erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.js +++ b/erpnext/stock/report/stock_qty_vs_serial_no_count/stock_qty_vs_serial_no_count.js @@ -3,40 +3,39 @@ /* eslint-disable */ frappe.query_reports["Stock Qty vs Serial No Count"] = { - "filters": [ + filters: [ { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "default": frappe.defaults.get_user_default("Company"), - "reqd": 1 + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, }, { - "fieldname":"warehouse", - "label": __("Warehouse"), - "fieldtype": "Link", - "options": "Warehouse", - "get_query": function() { - const company = frappe.query_report.get_filter_value('company'); + fieldname: "warehouse", + label: __("Warehouse"), + fieldtype: "Link", + options: "Warehouse", + get_query: function () { + const company = frappe.query_report.get_filter_value("company"); return { - filters: { 'company': company } - } + filters: { company: company }, + }; }, - "reqd": 1 + reqd: 1, }, ], - "formatter": function (value, row, column, data, default_formatter) { + formatter: function (value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); if (column.fieldname == "difference" && data) { if (data.difference > 0) { value = "" + value + ""; - } - else if (data.difference < 0) { + } else if (data.difference < 0) { value = "" + value + ""; } } return value; - } + }, }; diff --git a/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.js b/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.js index 5b006470756..92a01c4ce20 100644 --- a/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.js +++ b/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.js @@ -2,27 +2,27 @@ // License: GNU General Public License v3. See license.txt frappe.query_reports["Supplier-Wise Sales Analytics"] = { - "filters": [ + filters: [ { - "fieldname":"supplier", - "label": __("Supplier"), - "fieldtype": "Link", - "options": "Supplier", - "width": "80" + fieldname: "supplier", + label: __("Supplier"), + fieldtype: "Link", + options: "Supplier", + width: "80", }, { - "fieldname":"from_date", - "label": __("From Date"), - "fieldtype": "Date", - "width": "80", - "default": frappe.datetime.month_start() + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + width: "80", + default: frappe.datetime.month_start(), }, { - "fieldname":"to_date", - "label": __("To Date"), - "fieldtype": "Date", - "width": "80", - "default": frappe.datetime.month_end() + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + width: "80", + default: frappe.datetime.month_end(), }, - ] -} + ], +}; diff --git a/erpnext/stock/report/total_stock_summary/total_stock_summary.js b/erpnext/stock/report/total_stock_summary/total_stock_summary.js index 88054aaea73..78ab31a7df7 100644 --- a/erpnext/stock/report/total_stock_summary/total_stock_summary.js +++ b/erpnext/stock/report/total_stock_summary/total_stock_summary.js @@ -3,25 +3,25 @@ /* eslint-disable */ frappe.query_reports["Total Stock Summary"] = { - "filters": [ + filters: [ { - "fieldname":"group_by", - "label": __("Group By"), - "fieldtype": "Select", - "width": "80", - "reqd": 1, - "options": ["Warehouse", "Company"], - "default": "Warehouse", + fieldname: "group_by", + label: __("Group By"), + fieldtype: "Select", + width: "80", + reqd: 1, + options: ["Warehouse", "Company"], + default: "Warehouse", }, { - "fieldname": "company", - "label": __("Company"), - "fieldtype": "Link", - "width": "80", - "options": "Company", - "reqd": 1, - "default": frappe.defaults.get_user_default("Company"), - "depends_on": "eval: doc.group_by != 'Company'", + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + width: "80", + options: "Company", + reqd: 1, + default: frappe.defaults.get_user_default("Company"), + depends_on: "eval: doc.group_by != 'Company'", }, - ] -} + ], +}; 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 39cfd7274f2..8a0c1a7af6d 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,49 +3,49 @@ /* eslint-disable */ frappe.query_reports["Warehouse wise Item Balance Age and Value"] = { - "filters": [ -{ - "fieldname":"from_date", - "label": __("From Date"), - "fieldtype": "Date", - "width": "80", - "reqd": 1, - "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), - }, - { - "fieldname":"to_date", - "label": __("To Date"), - "fieldtype": "Date", - "width": "80", - "reqd": 1, - "default": frappe.datetime.get_today() - }, - { - "fieldname": "item_group", - "label": __("Item Group"), - "fieldtype": "Link", - "width": "80", - "options": "Item Group" - }, - { - "fieldname": "item_code", - "label": __("Item"), - "fieldtype": "Link", - "width": "80", - "options": "Item" - }, - { - "fieldname": "warehouse", - "label": __("Warehouse"), - "fieldtype": "Link", - "width": "80", - "options": "Warehouse" - }, - { - "fieldname": "filter_total_zero_qty", - "label": __("Filter Total Zero Qty"), - "fieldtype": "Check", - "default": 1 - }, - ] -} + filters: [ + { + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + width: "80", + reqd: 1, + default: frappe.datetime.add_months(frappe.datetime.get_today(), -1), + }, + { + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + width: "80", + reqd: 1, + default: frappe.datetime.get_today(), + }, + { + fieldname: "item_group", + label: __("Item Group"), + fieldtype: "Link", + width: "80", + options: "Item Group", + }, + { + fieldname: "item_code", + label: __("Item"), + fieldtype: "Link", + width: "80", + options: "Item", + }, + { + fieldname: "warehouse", + label: __("Warehouse"), + fieldtype: "Link", + width: "80", + options: "Warehouse", + }, + { + fieldname: "filter_total_zero_qty", + label: __("Filter Total Zero Qty"), + fieldtype: "Check", + default: 1, + }, + ], +}; diff --git a/erpnext/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.js b/erpnext/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.js index 752e464e27c..808fe29237f 100644 --- a/erpnext/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.js +++ b/erpnext/stock/report/warehouse_wise_stock_balance/warehouse_wise_stock_balance.js @@ -3,25 +3,24 @@ /* eslint-disable */ frappe.query_reports["Warehouse Wise Stock Balance"] = { - "filters": [ + filters: [ { - "fieldname":"company", - "label": __("Company"), - "fieldtype": "Link", - "options": "Company", - "reqd": 1, - "default": frappe.defaults.get_user_default("Company") + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + reqd: 1, + default: frappe.defaults.get_user_default("Company"), }, { - "fieldname":"show_disabled_warehouses", - "label": __("Show Disabled Warehouses"), - "fieldtype": "Check", - "default": 0 - - } + fieldname: "show_disabled_warehouses", + label: __("Show Disabled Warehouses"), + fieldtype: "Check", + default: 0, + }, ], - "initial_depth": 3, - "tree": true, - "parent_field": "parent_warehouse", - "name_field": "warehouse" + initial_depth: 3, + tree: true, + parent_field: "parent_warehouse", + name_field: "warehouse", }; diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order_list.js b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order_list.js index 7ca12642c5f..7c12abd6722 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order_list.js +++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order_list.js @@ -1,18 +1,18 @@ // Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.listview_settings['Subcontracting Order'] = { +frappe.listview_settings["Subcontracting Order"] = { get_indicator: function (doc) { const status_colors = { - "Draft": "grey", - "Open": "orange", + Draft: "grey", + Open: "orange", "Partially Received": "yellow", - "Completed": "green", + Completed: "green", "Partial Material Transferred": "purple", "Material Transferred": "blue", - "Closed": "red", - "Cancelled": "red", + Closed: "red", + Cancelled: "red", }; return [__(doc.status), status_colors[doc.status], "status,=," + doc.status]; }, -}; \ No newline at end of file +}; diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt_list.js b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt_list.js index 14a4e4ad6cb..be6c0d0b18f 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt_list.js +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt_list.js @@ -1,14 +1,14 @@ // Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.listview_settings['Subcontracting Receipt'] = { +frappe.listview_settings["Subcontracting Receipt"] = { get_indicator: function (doc) { const status_colors = { - "Draft": "grey", - "Return": "gray", + Draft: "grey", + Return: "gray", "Return Issued": "grey", - "Completed": "green", + Completed: "green", }; return [__(doc.status), status_colors[doc.status], "status,=," + doc.status]; }, -}; \ No newline at end of file +}; diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js index 9f91dc1726d..03d209e99e3 100644 --- a/erpnext/support/doctype/issue/issue.js +++ b/erpnext/support/doctype/issue/issue.js @@ -1,40 +1,47 @@ frappe.ui.form.on("Issue", { - onload: function(frm) { + onload: function (frm) { frm.email_field = "raised_by"; - frappe.db.get_value("Support Settings", {name: "Support Settings"}, - ["allow_resetting_service_level_agreement", "track_service_level_agreement"], (r) => { + frappe.db.get_value( + "Support Settings", + { name: "Support Settings" }, + ["allow_resetting_service_level_agreement", "track_service_level_agreement"], + (r) => { if (r && r.track_service_level_agreement == "0") { frm.set_df_property("service_level_section", "hidden", 1); } if (r && r.allow_resetting_service_level_agreement == "0") { frm.set_df_property("reset_service_level_agreement", "hidden", 1); } - }); + } + ); // buttons if (frm.doc.status !== "Closed") { - frm.add_custom_button(__("Close"), function() { + frm.add_custom_button(__("Close"), function () { frm.set_value("status", "Closed"); frm.save(); }); - frm.add_custom_button(__("Task"), function() { - frappe.model.open_mapped_doc({ - method: "erpnext.support.doctype.issue.issue.make_task", - frm: frm - }); - }, __("Create")); - + frm.add_custom_button( + __("Task"), + function () { + frappe.model.open_mapped_doc({ + method: "erpnext.support.doctype.issue.issue.make_task", + frm: frm, + }); + }, + __("Create") + ); } else { - frm.add_custom_button(__("Reopen"), function() { + frm.add_custom_button(__("Reopen"), function () { frm.set_value("status", "Open"); frm.save(); }); } }, - reset_service_level_agreement: function(frm) { + reset_service_level_agreement: function (frm) { let reset_sla = new frappe.ui.Dialog({ title: __("Reset Service Level Agreement"), fields: [ @@ -42,8 +49,8 @@ frappe.ui.form.on("Issue", { fieldtype: "Data", fieldname: "reason", label: __("Reason"), - reqd: 1 - } + reqd: 1, + }, ], primary_action_label: __("Reset"), primary_action: (values) => { @@ -53,39 +60,42 @@ frappe.ui.form.on("Issue", { frappe.show_alert({ indicator: "green", - message: __("Resetting Service Level Agreement.") + message: __("Resetting Service Level Agreement."), }); - frappe.call("erpnext.support.doctype.service_level_agreement.service_level_agreement.reset_service_level_agreement", { - reason: values.reason, - user: frappe.session.user_email, - doctype: frm.doc.doctype, - docname: frm.doc.name, - }, () => { - reset_sla.enable_primary_action(); - frm.refresh(); - frappe.msgprint(__("Service Level Agreement was reset.")); - }); - } + frappe.call( + "erpnext.support.doctype.service_level_agreement.service_level_agreement.reset_service_level_agreement", + { + reason: values.reason, + user: frappe.session.user_email, + doctype: frm.doc.doctype, + docname: frm.doc.name, + }, + () => { + reset_sla.enable_primary_action(); + frm.refresh(); + frappe.msgprint(__("Service Level Agreement was reset.")); + } + ); + }, }); reset_sla.show(); }, - - timeline_refresh: function(frm) { + timeline_refresh: function (frm) { if (!frm.timeline.wrapper.find(".btn-split-issue").length) { let split_issue_btn = $(` - ${frappe.utils.icon('branch', 'sm')} + ${frappe.utils.icon("branch", "sm")} `); let communication_box = frm.timeline.wrapper.find('.timeline-item[data-doctype="Communication"]'); - communication_box.find('.actions').prepend(split_issue_btn); + communication_box.find(".actions").prepend(split_issue_btn); if (!frm.timeline.wrapper.data("split-issue-event-attached")) { - frm.timeline.wrapper.on('click', '.btn-split-issue', (e) => { + frm.timeline.wrapper.on("click", ".btn-split-issue", (e) => { var dialog = new frappe.ui.Dialog({ title: __("Split Issue"), fields: [ @@ -94,20 +104,30 @@ frappe.ui.form.on("Issue", { fieldtype: "Data", reqd: 1, label: __("Subject"), - description: __("All communications including and above this shall be moved into the new Issue") - } + description: __( + "All communications including and above this shall be moved into the new Issue" + ), + }, ], primary_action_label: __("Split"), primary_action: () => { - frm.call("split_issue", { - subject: dialog.fields_dict.subject.value, - communication_id: e.currentTarget.closest(".timeline-item").getAttribute("data-name") - }, (r) => { - frappe.msgprint(`New issue created: ${r.message}`); - frm.reload_doc(); - dialog.hide(); - }); - } + frm.call( + "split_issue", + { + subject: dialog.fields_dict.subject.value, + communication_id: e.currentTarget + .closest(".timeline-item") + .getAttribute("data-name"), + }, + (r) => { + frappe.msgprint( + `New issue created: ${r.message}` + ); + frm.reload_doc(); + dialog.hide(); + } + ); + }, }); dialog.show(); }); diff --git a/erpnext/support/doctype/issue/issue_list.js b/erpnext/support/doctype/issue/issue_list.js index 5c9d4ed9890..8aa1650c599 100644 --- a/erpnext/support/doctype/issue/issue_list.js +++ b/erpnext/support/doctype/issue/issue_list.js @@ -1,30 +1,30 @@ -frappe.listview_settings['Issue'] = { - colwidths: {"subject": 6}, - add_fields: ['priority'], +frappe.listview_settings["Issue"] = { + colwidths: { subject: 6 }, + add_fields: ["priority"], filters: [["status", "=", "Open"]], - onload: function(listview) { + onload: function (listview) { var method = "erpnext.support.doctype.issue.issue.set_multiple_status"; - listview.page.add_action_item(__("Set as Open"), function() { - listview.call_for_selected_items(method, {"status": "Open"}); + listview.page.add_action_item(__("Set as Open"), function () { + listview.call_for_selected_items(method, { status: "Open" }); }); - listview.page.add_action_item(__("Set as Closed"), function() { - listview.call_for_selected_items(method, {"status": "Closed"}); + listview.page.add_action_item(__("Set as Closed"), function () { + listview.call_for_selected_items(method, { status: "Closed" }); }); }, - get_indicator: function(doc) { - if (doc.status === 'Open') { + get_indicator: function (doc) { + if (doc.status === "Open") { const color = { - 'Low': 'yellow', - 'Medium': 'orange', - 'High': 'red' + Low: "yellow", + Medium: "orange", + High: "red", }; - return [__(doc.status), color[doc.priority] || 'red', `status,=,Open`]; - } else if (doc.status === 'Closed') { + return [__(doc.status), color[doc.priority] || "red", `status,=,Open`]; + } else if (doc.status === "Closed") { return [__(doc.status), "green", "status,=," + doc.status]; } else { return [__(doc.status), "gray", "status,=," + doc.status]; } - } -} + }, +}; diff --git a/erpnext/support/doctype/issue_priority/issue_priority.js b/erpnext/support/doctype/issue_priority/issue_priority.js index 37ce6a54bf7..e777d0690ca 100644 --- a/erpnext/support/doctype/issue_priority/issue_priority.js +++ b/erpnext/support/doctype/issue_priority/issue_priority.js @@ -1,8 +1,7 @@ // Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Issue Priority', { +frappe.ui.form.on("Issue Priority", { // refresh: function(frm) { - // } }); diff --git a/erpnext/support/doctype/issue_type/issue_type.js b/erpnext/support/doctype/issue_type/issue_type.js index 2b3d14ef712..aa0c4565d6c 100644 --- a/erpnext/support/doctype/issue_type/issue_type.js +++ b/erpnext/support/doctype/issue_type/issue_type.js @@ -1,8 +1,6 @@ // Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Issue Type', { - refresh: function(frm) { - - } +frappe.ui.form.on("Issue Type", { + refresh: function (frm) {}, }); diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.js b/erpnext/support/doctype/service_level_agreement/service_level_agreement.js index 4dbb0e7e86f..a8b85de9d6a 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.js +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.js @@ -1,121 +1,145 @@ // Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Service Level Agreement', { - setup: function(frm) { +frappe.ui.form.on("Service Level Agreement", { + setup: function (frm) { if (cint(frm.doc.apply_sla_for_resolution) === 1) { - frm.get_field('priorities').grid.editable_fields = [ - {fieldname: 'default_priority', columns: 1}, - {fieldname: 'priority', columns: 2}, - {fieldname: 'response_time', columns: 2}, - {fieldname: 'resolution_time', columns: 2} + frm.get_field("priorities").grid.editable_fields = [ + { fieldname: "default_priority", columns: 1 }, + { fieldname: "priority", columns: 2 }, + { fieldname: "response_time", columns: 2 }, + { fieldname: "resolution_time", columns: 2 }, ]; } else { - frm.get_field('priorities').grid.editable_fields = [ - {fieldname: 'default_priority', columns: 1}, - {fieldname: 'priority', columns: 2}, - {fieldname: 'response_time', columns: 3}, + frm.get_field("priorities").grid.editable_fields = [ + { fieldname: "default_priority", columns: 1 }, + { fieldname: "priority", columns: 2 }, + { fieldname: "response_time", columns: 3 }, ]; } }, - refresh: function(frm) { - frm.trigger('fetch_status_fields'); - frm.trigger('toggle_resolution_fields'); - frm.trigger('default_service_level_agreement'); - frm.trigger('entity'); + refresh: function (frm) { + frm.trigger("fetch_status_fields"); + frm.trigger("toggle_resolution_fields"); + frm.trigger("default_service_level_agreement"); + frm.trigger("entity"); }, - default_service_level_agreement: function(frm) { - const field = frm.get_field('default_service_level_agreement'); + default_service_level_agreement: function (frm) { + const field = frm.get_field("default_service_level_agreement"); if (frm.doc.default_service_level_agreement) { - field.set_description(__('SLA will be applied on every {0}', [frm.doc.document_type])); + field.set_description(__("SLA will be applied on every {0}", [frm.doc.document_type])); } else { - field.set_description(__('Enable to apply SLA on every {0}', [frm.doc.document_type])); + field.set_description(__("Enable to apply SLA on every {0}", [frm.doc.document_type])); } }, - document_type: function(frm) { - frm.trigger('fetch_status_fields'); - frm.trigger('default_service_level_agreement'); + document_type: function (frm) { + frm.trigger("fetch_status_fields"); + frm.trigger("default_service_level_agreement"); }, - entity_type: function(frm) { - frm.set_value('entity', undefined); + entity_type: function (frm) { + frm.set_value("entity", undefined); }, - entity: function(frm) { - const field = frm.get_field('entity'); + entity: function (frm) { + const field = frm.get_field("entity"); if (frm.doc.entity) { - const and_descendants = frm.doc.entity_type != 'Customer' ? ' ' + __('or its descendants') : ''; + const and_descendants = frm.doc.entity_type != "Customer" ? " " + __("or its descendants") : ""; field.set_description( - __('SLA will be applied if {1} is set as {2}{3}', [ - frm.doc.document_type, frm.doc.entity_type, - frm.doc.entity, and_descendants + __("SLA will be applied if {1} is set as {2}{3}", [ + frm.doc.document_type, + frm.doc.entity_type, + frm.doc.entity, + and_descendants, ]) ); } else { - field.set_description(''); + field.set_description(""); } }, - fetch_status_fields: function(frm) { + fetch_status_fields: function (frm) { let allow_statuses = []; let exclude_statuses = []; if (frm.doc.document_type) { frappe.model.with_doctype(frm.doc.document_type, () => { - let statuses = frappe.meta.get_docfield(frm.doc.document_type, 'status', frm.doc.name).options; - statuses = statuses.split('\n'); + let statuses = frappe.meta.get_docfield( + frm.doc.document_type, + "status", + frm.doc.name + ).options; + statuses = statuses.split("\n"); - exclude_statuses = ['Open', 'Closed']; + exclude_statuses = ["Open", "Closed"]; allow_statuses = statuses.filter((status) => !exclude_statuses.includes(status)); frm.fields_dict.pause_sla_on.grid.update_docfield_property( - 'status', 'options', [''].concat(allow_statuses) + "status", + "options", + [""].concat(allow_statuses) ); - exclude_statuses = ['Open']; + exclude_statuses = ["Open"]; allow_statuses = statuses.filter((status) => !exclude_statuses.includes(status)); frm.fields_dict.sla_fulfilled_on.grid.update_docfield_property( - 'status', 'options', [''].concat(allow_statuses) + "status", + "options", + [""].concat(allow_statuses) ); }); } - frm.refresh_field('pause_sla_on'); + frm.refresh_field("pause_sla_on"); }, - apply_sla_for_resolution: function(frm) { - frm.trigger('toggle_resolution_fields'); + apply_sla_for_resolution: function (frm) { + frm.trigger("toggle_resolution_fields"); }, - toggle_resolution_fields: function(frm) { + toggle_resolution_fields: function (frm) { if (cint(frm.doc.apply_sla_for_resolution) === 1) { - frm.fields_dict.priorities.grid.update_docfield_property('resolution_time', 'hidden', 0); - frm.fields_dict.priorities.grid.update_docfield_property('resolution_time', 'reqd', 1); + frm.fields_dict.priorities.grid.update_docfield_property("resolution_time", "hidden", 0); + frm.fields_dict.priorities.grid.update_docfield_property("resolution_time", "reqd", 1); } else { - frm.fields_dict.priorities.grid.update_docfield_property('resolution_time', 'hidden', 1); - frm.fields_dict.priorities.grid.update_docfield_property('resolution_time', 'reqd', 0); + frm.fields_dict.priorities.grid.update_docfield_property("resolution_time", "hidden", 1); + frm.fields_dict.priorities.grid.update_docfield_property("resolution_time", "reqd", 0); } - frm.refresh_field('priorities'); + frm.refresh_field("priorities"); }, - onload: function(frm) { - frm.set_query("document_type", function() { + onload: function (frm) { + frm.set_query("document_type", function () { let invalid_doctypes = frappe.model.core_doctypes_list; - invalid_doctypes.push(frm.doc.doctype, 'Cost Center', 'Company'); + invalid_doctypes.push(frm.doc.doctype, "Cost Center", "Company"); return { filters: [ - ['DocType', 'issingle', '=', 0], - ['DocType', 'istable', '=', 0], - ['DocType', 'is_submittable', '=', 0], - ['DocType', 'name', 'not in', invalid_doctypes], - ['DocType', 'module', 'not in', ["Email", "Core", "Custom", "Event Streaming", "Social", "Data Migration", "Geo", "Desk"]] - ] + ["DocType", "issingle", "=", 0], + ["DocType", "istable", "=", 0], + ["DocType", "is_submittable", "=", 0], + ["DocType", "name", "not in", invalid_doctypes], + [ + "DocType", + "module", + "not in", + [ + "Email", + "Core", + "Custom", + "Event Streaming", + "Social", + "Data Migration", + "Geo", + "Desk", + ], + ], + ], }; }); - } + }, }); diff --git a/erpnext/support/doctype/support_settings/support_settings.js b/erpnext/support/doctype/support_settings/support_settings.js index 78adca81ca5..3a1cb84ba85 100644 --- a/erpnext/support/doctype/support_settings/support_settings.js +++ b/erpnext/support/doctype/support_settings/support_settings.js @@ -1,8 +1,8 @@ // Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Support Settings', { - refresh: function(frm) { +frappe.ui.form.on("Support Settings", { + refresh: function (frm) { // - } + }, }); diff --git a/erpnext/support/doctype/warranty_claim/warranty_claim.js b/erpnext/support/doctype/warranty_claim/warranty_claim.js index 10cb37f5124..d7b77f71c5e 100644 --- a/erpnext/support/doctype/warranty_claim/warranty_claim.js +++ b/erpnext/support/doctype/warranty_claim/warranty_claim.js @@ -43,10 +43,7 @@ frappe.ui.form.on("Warranty Claim", { doctype: "Customer", }; - if ( - !frm.doc.__islocal && - ["Open", "Work In Progress"].includes(frm.doc.status) - ) { + if (!frm.doc.__islocal && ["Open", "Work In Progress"].includes(frm.doc.status)) { frm.add_custom_button(__("Maintenance Visit"), () => { frappe.model.open_mapped_doc({ method: "erpnext.support.doctype.warranty_claim.warranty_claim.make_maintenance_visit", diff --git a/erpnext/support/doctype/warranty_claim/warranty_claim_list.js b/erpnext/support/doctype/warranty_claim/warranty_claim_list.js index e162e137ba6..fdafb0ec59f 100644 --- a/erpnext/support/doctype/warranty_claim/warranty_claim_list.js +++ b/erpnext/support/doctype/warranty_claim/warranty_claim_list.js @@ -1,4 +1,4 @@ -frappe.listview_settings['Warranty Claim'] = { +frappe.listview_settings["Warranty Claim"] = { add_fields: ["status", "customer", "item_code"], - filters:[["status","=", "Open"]] + filters: [["status", "=", "Open"]], }; diff --git a/erpnext/support/report/first_response_time_for_issues/first_response_time_for_issues.js b/erpnext/support/report/first_response_time_for_issues/first_response_time_for_issues.js index 18691fe264f..304515b1f0c 100644 --- a/erpnext/support/report/first_response_time_for_issues/first_response_time_for_issues.js +++ b/erpnext/support/report/first_response_time_for_issues/first_response_time_for_issues.js @@ -3,41 +3,43 @@ /* eslint-disable */ frappe.query_reports["First Response Time for Issues"] = { - "filters": [ + filters: [ { - "fieldname": "from_date", - "label": __("From Date"), - "fieldtype": "Date", - "reqd": 1, - "default": frappe.datetime.add_days(frappe.datetime.nowdate(), -30) + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + reqd: 1, + default: frappe.datetime.add_days(frappe.datetime.nowdate(), -30), }, { - "fieldname": "to_date", - "label": __("To Date"), - "fieldtype": "Date", - "reqd": 1, - "default":frappe.datetime.nowdate() - } + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + reqd: 1, + default: frappe.datetime.nowdate(), + }, ], - get_chart_data: function(_columns, result) { + get_chart_data: function (_columns, result) { return { data: { - labels: result.map(d => d.creation_date), - datasets: [{ - name: 'First Response Time', - values: result.map(d => d.first_response_time) - }] + labels: result.map((d) => d.creation_date), + datasets: [ + { + name: "First Response Time", + values: result.map((d) => d.first_response_time), + }, + ], }, type: "line", tooltipOptions: { - formatTooltipY: d => { + formatTooltipY: (d) => { let duration_options = { hide_days: 0, - hide_seconds: 0 + hide_seconds: 0, }; return frappe.utils.get_formatted_duration(d, duration_options); - } - } - } - } + }, + }, + }; + }, }; diff --git a/erpnext/support/report/issue_analytics/issue_analytics.js b/erpnext/support/report/issue_analytics/issue_analytics.js index 746eee025a5..4680321979f 100644 --- a/erpnext/support/report/issue_analytics/issue_analytics.js +++ b/erpnext/support/report/issue_analytics/issue_analytics.js @@ -3,14 +3,14 @@ /* eslint-disable */ frappe.query_reports["Issue Analytics"] = { - "filters": [ + filters: [ { fieldname: "company", label: __("Company"), fieldtype: "Link", options: "Company", default: frappe.defaults.get_user_default("Company"), - reqd: 1 + reqd: 1, }, { fieldname: "based_on", @@ -18,125 +18,124 @@ frappe.query_reports["Issue Analytics"] = { fieldtype: "Select", options: ["Customer", "Issue Type", "Issue Priority", "Assigned To"], default: "Customer", - reqd: 1 + reqd: 1, }, { fieldname: "from_date", label: __("From Date"), fieldtype: "Date", default: frappe.defaults.get_global_default("year_start_date"), - reqd: 1 + reqd: 1, }, { - fieldname:"to_date", + fieldname: "to_date", label: __("To Date"), fieldtype: "Date", default: frappe.defaults.get_global_default("year_end_date"), - reqd: 1 + reqd: 1, }, { fieldname: "range", label: __("Range"), fieldtype: "Select", options: [ - { "value": "Weekly", "label": __("Weekly") }, - { "value": "Monthly", "label": __("Monthly") }, - { "value": "Quarterly", "label": __("Quarterly") }, - { "value": "Yearly", "label": __("Yearly") } + { value: "Weekly", label: __("Weekly") }, + { value: "Monthly", label: __("Monthly") }, + { value: "Quarterly", label: __("Quarterly") }, + { value: "Yearly", label: __("Yearly") }, ], default: "Monthly", - reqd: 1 + reqd: 1, }, { fieldname: "status", label: __("Status"), fieldtype: "Select", - options:[ + options: [ "", - {label: __('Open'), value: 'Open'}, - {label: __('Replied'), value: 'Replied'}, - {label: __('Resolved'), value: 'Resolved'}, - {label: __('Closed'), value: 'Closed'} - ] + { label: __("Open"), value: "Open" }, + { label: __("Replied"), value: "Replied" }, + { label: __("Resolved"), value: "Resolved" }, + { label: __("Closed"), value: "Closed" }, + ], }, { fieldname: "priority", label: __("Issue Priority"), fieldtype: "Link", - options: "Issue Priority" + options: "Issue Priority", }, { fieldname: "customer", label: __("Customer"), fieldtype: "Link", - options: "Customer" + options: "Customer", }, { fieldname: "project", label: __("Project"), fieldtype: "Link", - options: "Project" + options: "Project", }, { fieldname: "assigned_to", label: __("Assigned To"), fieldtype: "Link", - options: "User" - } + options: "User", + }, ], - after_datatable_render: function(datatable_obj) { - $(datatable_obj.wrapper).find(".dt-row-0").find('input[type=checkbox]').click(); + after_datatable_render: function (datatable_obj) { + $(datatable_obj.wrapper).find(".dt-row-0").find("input[type=checkbox]").click(); }, get_datatable_options(options) { return Object.assign(options, { checkboxColumn: true, events: { - onCheckRow: function(data) { + onCheckRow: function (data) { if (data && data.length) { row_name = data[2].content; - row_values = data.slice(3).map(function(column) { + row_values = data.slice(3).map(function (column) { return column.content; - }) - entry = { - 'name': row_name, - 'values': row_values - } + }); + entry = { + name: row_name, + values: row_values, + }; let raw_data = frappe.query_report.chart.data; let new_datasets = raw_data.datasets; var found = false; - for(var i=0; i < new_datasets.length; i++){ - if (new_datasets[i].name == row_name){ + for (var i = 0; i < new_datasets.length; i++) { + if (new_datasets[i].name == row_name) { found = true; - new_datasets.splice(i,1); + new_datasets.splice(i, 1); break; } } - if (!found){ + if (!found) { new_datasets.push(entry); } let new_data = { labels: raw_data.labels, - datasets: new_datasets - } + datasets: new_datasets, + }; setTimeout(() => { - frappe.query_report.chart.update(new_data) - },500) - + frappe.query_report.chart.update(new_data); + }, 500); setTimeout(() => { frappe.query_report.chart.draw(true); - }, 1000) + }, 1000); frappe.query_report.raw_chart_data = new_data; } }, - } + }, }); - } + }, }; diff --git a/erpnext/support/report/issue_summary/issue_summary.js b/erpnext/support/report/issue_summary/issue_summary.js index a5122d03ad1..15d55ba6543 100644 --- a/erpnext/support/report/issue_summary/issue_summary.js +++ b/erpnext/support/report/issue_summary/issue_summary.js @@ -3,14 +3,14 @@ /* eslint-disable */ frappe.query_reports["Issue Summary"] = { - "filters": [ + filters: [ { fieldname: "company", label: __("Company"), fieldtype: "Link", options: "Company", default: frappe.defaults.get_user_default("Company"), - reqd: 1 + reqd: 1, }, { fieldname: "based_on", @@ -18,58 +18,58 @@ frappe.query_reports["Issue Summary"] = { fieldtype: "Select", options: ["Customer", "Issue Type", "Issue Priority", "Assigned To"], default: "Customer", - reqd: 1 + reqd: 1, }, { fieldname: "from_date", label: __("From Date"), fieldtype: "Date", default: frappe.defaults.get_global_default("year_start_date"), - reqd: 1 + reqd: 1, }, { - fieldname:"to_date", + fieldname: "to_date", label: __("To Date"), fieldtype: "Date", default: frappe.defaults.get_global_default("year_end_date"), - reqd: 1 + reqd: 1, }, { fieldname: "status", label: __("Status"), fieldtype: "Select", - options:[ + options: [ "", - {label: __('Open'), value: 'Open'}, - {label: __('Replied'), value: 'Replied'}, - {label: __('On Hold'), value: 'On Hold'}, - {label: __('Resolved'), value: 'Resolved'}, - {label: __('Closed'), value: 'Closed'} - ] + { label: __("Open"), value: "Open" }, + { label: __("Replied"), value: "Replied" }, + { label: __("On Hold"), value: "On Hold" }, + { label: __("Resolved"), value: "Resolved" }, + { label: __("Closed"), value: "Closed" }, + ], }, { fieldname: "priority", label: __("Issue Priority"), fieldtype: "Link", - options: "Issue Priority" + options: "Issue Priority", }, { fieldname: "customer", label: __("Customer"), fieldtype: "Link", - options: "Customer" + options: "Customer", }, { fieldname: "project", label: __("Project"), fieldtype: "Link", - options: "Project" + options: "Project", }, { fieldname: "assigned_to", label: __("Assigned To"), fieldtype: "Link", - options: "User" - } - ] + options: "User", + }, + ], }; diff --git a/erpnext/support/report/support_hour_distribution/support_hour_distribution.js b/erpnext/support/report/support_hour_distribution/support_hour_distribution.js index ae30b6a5507..8137a50d764 100644 --- a/erpnext/support/report/support_hour_distribution/support_hour_distribution.js +++ b/erpnext/support/report/support_hour_distribution/support_hour_distribution.js @@ -3,20 +3,20 @@ /* eslint-disable */ frappe.query_reports["Support Hour Distribution"] = { - "filters": [ + filters: [ { - 'lable': __("From Date"), - 'fieldname': 'from_date', - 'fieldtype': 'Date', - 'default': frappe.datetime.nowdate(), - 'reqd': 1 + lable: __("From Date"), + fieldname: "from_date", + fieldtype: "Date", + default: frappe.datetime.nowdate(), + reqd: 1, }, { - 'lable': __("To Date"), - 'fieldname': 'to_date', - 'fieldtype': 'Date', - 'default': frappe.datetime.nowdate(), - 'reqd': 1 - } - ] -} + lable: __("To Date"), + fieldname: "to_date", + fieldtype: "Date", + default: frappe.datetime.nowdate(), + reqd: 1, + }, + ], +}; diff --git a/erpnext/support/web_form/issues/issues.js b/erpnext/support/web_form/issues/issues.js index ffc5e984253..8f56ebb353d 100644 --- a/erpnext/support/web_form/issues/issues.js +++ b/erpnext/support/web_form/issues/issues.js @@ -1,3 +1,3 @@ -frappe.ready(function() { +frappe.ready(function () { // bind events here -}) +}); diff --git a/erpnext/telephony/doctype/call_log/call_log.js b/erpnext/telephony/doctype/call_log/call_log.js index e7afa0b7d09..1fd15266003 100644 --- a/erpnext/telephony/doctype/call_log/call_log.js +++ b/erpnext/telephony/doctype/call_log/call_log.js @@ -1,21 +1,21 @@ // Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors // For license information, please see license.txt -frappe.ui.form.on('Call Log', { - refresh: function(frm) { +frappe.ui.form.on("Call Log", { + refresh: function (frm) { frm.events.setup_recording_audio_control(frm); - const incoming_call = frm.doc.type == 'Incoming'; - frm.add_custom_button(incoming_call ? __('Callback'): __('Call Again'), () => { + const incoming_call = frm.doc.type == "Incoming"; + frm.add_custom_button(incoming_call ? __("Callback") : __("Call Again"), () => { const number = incoming_call ? frm.doc.from : frm.doc.to; frappe.phone_call.handler(number, frm); }); }, setup_recording_audio_control(frm) { - const recording_wrapper = frm.get_field('recording_html').$wrapper; - if (!frm.doc.recording_url || frm.doc.recording_url == 'null') { + const recording_wrapper = frm.get_field("recording_html").$wrapper; + if (!frm.doc.recording_url || frm.doc.recording_url == "null") { recording_wrapper.empty(); } else { - recording_wrapper.addClass('input-max-width'); + recording_wrapper.addClass("input-max-width"); recording_wrapper.html(`