From 0e9a1fb40e0b5310e69d85f06610f4879f3db378 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 30 Aug 2022 15:46:08 +0530 Subject: [PATCH 01/28] fix: force delete old report docs (backport #32026) (#32027) fix: force delete old report docs (#32026) (cherry picked from commit ffa3071d36e62eb721bc9a3105fb7af4b93cf8fc) Co-authored-by: Ankush Menat --- erpnext/patches/v13_0/delete_old_sales_reports.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/patches/v13_0/delete_old_sales_reports.py b/erpnext/patches/v13_0/delete_old_sales_reports.py index b31c9d17d71..1b53da755cd 100644 --- a/erpnext/patches/v13_0/delete_old_sales_reports.py +++ b/erpnext/patches/v13_0/delete_old_sales_reports.py @@ -16,18 +16,18 @@ def execute(): delete_auto_email_reports(report) check_and_delete_linked_reports(report) - frappe.delete_doc("Report", report) + frappe.delete_doc("Report", report, force=True) def delete_auto_email_reports(report): """Check for one or multiple Auto Email Reports and delete""" auto_email_reports = frappe.db.get_values("Auto Email Report", {"report": report}, ["name"]) for auto_email_report in auto_email_reports: - frappe.delete_doc("Auto Email Report", auto_email_report[0]) + frappe.delete_doc("Auto Email Report", auto_email_report[0], force=True) def delete_links_from_desktop_icons(report): """Check for one or multiple Desktop Icons and delete""" desktop_icons = frappe.db.get_values("Desktop Icon", {"_report": report}, ["name"]) for desktop_icon in desktop_icons: - frappe.delete_doc("Desktop Icon", desktop_icon[0]) + frappe.delete_doc("Desktop Icon", desktop_icon[0], force=True) From 1b9082e07b06dadb878933e1cc5cb2f865fb2710 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 30 Aug 2022 19:16:36 +0530 Subject: [PATCH 02/28] fix: Loan Interest accruals for 0 rated loans (cherry picked from commit eefc9b71725d5530e3bd259a299d3c0673385a4a) --- .../loan_interest_accrual/loan_interest_accrual.py | 1 - .../doctype/loan_repayment/loan_repayment.py | 1 + .../process_loan_interest_accrual.py | 13 ++++++++----- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py index 4978f1fcb89..730d33752ee 100644 --- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py +++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py @@ -233,7 +233,6 @@ def get_term_loans(date, term_loan=None, loan_type=None): AND l.is_term_loan =1 AND rs.payment_date <= %s AND rs.is_accrued=0 {0} - AND rs.interest_amount > 0 AND l.status = 'Disbursed' ORDER BY rs.payment_date""".format( condition diff --git a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py index 07a1d0d8506..9b7603fc3b3 100644 --- a/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py +++ b/erpnext/loan_management/doctype/loan_repayment/loan_repayment.py @@ -732,6 +732,7 @@ def get_amounts(amounts, against_loan, posting_date): ) amounts["pending_accrual_entries"] = pending_accrual_entries amounts["unaccrued_interest"] = flt(unaccrued_interest, precision) + amounts["written_off_amount"] = flt(against_loan_doc.written_off_amount, precision) if final_due_date: amounts["due_date"] = final_due_date diff --git a/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.py b/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.py index 81464a36c3d..25c72d91a7c 100644 --- a/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.py +++ b/erpnext/loan_management/doctype/process_loan_interest_accrual/process_loan_interest_accrual.py @@ -57,7 +57,7 @@ def process_loan_interest_accrual_for_demand_loans( def process_loan_interest_accrual_for_term_loans(posting_date=None, loan_type=None, loan=None): - if not term_loan_accrual_pending(posting_date or nowdate()): + if not term_loan_accrual_pending(posting_date or nowdate(), loan=loan): return loan_process = frappe.new_doc("Process Loan Interest Accrual") @@ -71,9 +71,12 @@ def process_loan_interest_accrual_for_term_loans(posting_date=None, loan_type=No return loan_process.name -def term_loan_accrual_pending(date): - pending_accrual = frappe.db.get_value( - "Repayment Schedule", {"payment_date": ("<=", date), "is_accrued": 0} - ) +def term_loan_accrual_pending(date, loan=None): + filters = {"payment_date": ("<=", date), "is_accrued": 0} + + if loan: + filters.update({"parent": loan}) + + pending_accrual = frappe.db.get_value("Repayment Schedule", filters) return pending_accrual From 52fc10d00cf4259cedf7752e2ecf72ea31398bc5 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 30 Aug 2022 19:24:57 +0530 Subject: [PATCH 03/28] chore: Add check for principal amount (cherry picked from commit a76d3827ec5355b7eecedc3e66bfd85b119d5211) --- .../doctype/loan_interest_accrual/loan_interest_accrual.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py index 730d33752ee..15e9e3f4c1a 100644 --- a/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py +++ b/erpnext/loan_management/doctype/loan_interest_accrual/loan_interest_accrual.py @@ -233,6 +233,7 @@ def get_term_loans(date, term_loan=None, loan_type=None): AND l.is_term_loan =1 AND rs.payment_date <= %s AND rs.is_accrued=0 {0} + AND rs.principal_amount > 0 AND l.status = 'Disbursed' ORDER BY rs.payment_date""".format( condition From 92f8f0ec748ab732c0edface031f4581357e6863 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Fri, 2 Sep 2022 09:10:17 +0530 Subject: [PATCH 04/28] chore: set BOM as default value for Backflush Raw Materials of Subcontract Based On (backport #32048) (#32050) chore: set BOM as default value for Backflush Raw Materials of Subcontract Based On (#32048) chore: set BOM as default value for Backflush Raw Materials of Subcontract Based On in Buying Settings (cherry picked from commit 68907ca7839cc9fcb20bf8b3b47ed9318bfedf90) Co-authored-by: Sagar Sharma --- erpnext/buying/doctype/buying_settings/buying_settings.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.json b/erpnext/buying/doctype/buying_settings/buying_settings.json index 6c18a4650b7..aad26075f2d 100644 --- a/erpnext/buying/doctype/buying_settings/buying_settings.json +++ b/erpnext/buying/doctype/buying_settings/buying_settings.json @@ -76,7 +76,7 @@ "label": "Subcontracting Settings" }, { - "default": "Material Transferred for Subcontract", + "default": "BOM", "fieldname": "backflush_raw_materials_of_subcontract_based_on", "fieldtype": "Select", "label": "Backflush Raw Materials of Subcontract Based On", @@ -148,7 +148,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2022-05-31 19:40:26.103909", + "modified": "2022-09-01 18:01:34.994657", "modified_by": "Administrator", "module": "Buying", "name": "Buying Settings", From 4bbd0ec985cf1f6b26f067412a481766ca25511a Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 24 Aug 2022 12:24:55 +0530 Subject: [PATCH 05/28] fix: include payment against PO in AR/AP report (cherry picked from commit fdd167cac123a6f2fea11ccdd73f3b12a65f2f8d) --- .../report/accounts_receivable/accounts_receivable.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 28b8d257783..3f504b13f67 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -179,6 +179,11 @@ class ReceivablePayableReport(object): key = (ple.against_voucher_type, ple.against_voucher_no, ple.party) row = self.voucher_balance.get(key) + + if not row: + # no invoice, this is an invoice / stand-alone payment / credit note + row = self.voucher_balance.get((ple.voucher_type, ple.voucher_no, ple.party)) + return row def update_voucher_balance(self, ple): From d9632e813842c5964e7e449954375e8f1282211b Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 24 Aug 2022 13:58:11 +0530 Subject: [PATCH 06/28] test: payments against so/po will show up as outstanding amount 1. Class will use FrappeTestCase fixture 2. setup and teardown methods are introduced 3. test for payments against SO (cherry picked from commit 36f5883ddaf2b2215123d0b06f7b3965b27696c5) --- .../test_accounts_receivable.py | 56 ++++++++++++++++++- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py index edddbbce219..bac8beed2e2 100644 --- a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py @@ -1,19 +1,27 @@ import unittest import frappe +from frappe.tests.utils import FrappeTestCase from frappe.utils import add_days, getdate, today from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.accounts.report.accounts_receivable.accounts_receivable import execute +from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order -class TestAccountsReceivable(unittest.TestCase): - def test_accounts_receivable(self): +class TestAccountsReceivable(FrappeTestCase): + def setUp(self): frappe.db.sql("delete from `tabSales Invoice` where company='_Test Company 2'") + frappe.db.sql("delete from `tabSales Order` where company='_Test Company 2'") + frappe.db.sql("delete from `tabPayment Entry` where company='_Test Company 2'") frappe.db.sql("delete from `tabGL Entry` where company='_Test Company 2'") frappe.db.sql("delete from `tabPayment Ledger Entry` where company='_Test Company 2'") + def tearDown(self): + frappe.db.rollback() + + def test_accounts_receivable(self): filters = { "company": "_Test Company 2", "based_on_payment_terms": 1, @@ -66,6 +74,50 @@ class TestAccountsReceivable(unittest.TestCase): ], ) + def test_payment_againt_po_in_receivable_report(self): + """ + Payments made against Purchase Order will show up as outstanding amount + """ + + so = make_sales_order( + company="_Test Company 2", + customer="_Test Customer 2", + warehouse="Finished Goods - _TC2", + currency="EUR", + debit_to="Debtors - _TC2", + income_account="Sales - _TC2", + expense_account="Cost of Goods Sold - _TC2", + cost_center="Main - _TC2", + ) + + pe = get_payment_entry(so.doctype, so.name) + pe = pe.save().submit() + + filters = { + "company": "_Test Company 2", + "based_on_payment_terms": 0, + "report_date": today(), + "range1": 30, + "range2": 60, + "range3": 90, + "range4": 120, + } + + report = execute(filters) + + expected_data_after_payment = [0, 1000, 0, -1000] + + row = report[1][0] + self.assertEqual( + expected_data_after_payment, + [ + row.invoiced, + row.paid, + row.credit_note, + row.outstanding, + ], + ) + def make_sales_invoice(): frappe.set_user("Administrator") From 4409f1128233f3dfce7209e6c5272ede21859d7c Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 2 Sep 2022 09:50:42 +0530 Subject: [PATCH 07/28] fix: key error on consolidated financial report accounts with same name but different account number will throw key error on consolidated report (cherry picked from commit 6e8395cccdb9fc3eba0a0746450660e89155c9f5) --- .../consolidated_financial_statement.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py index 98dbbf6c449..330e442a808 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py @@ -535,7 +535,11 @@ def get_accounts(root_type, companies): ): if account.account_name not in added_accounts: accounts.append(account) - added_accounts.append(account.account_name) + if account.account_number: + account_key = account.account_number + "-" + account.account_name + else: + account_key = account.account_name + added_accounts.append(account_key) return accounts From f55881aef80b0edb806ef4753e997d03bb848de9 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 22 Aug 2022 11:54:07 +0530 Subject: [PATCH 08/28] fix: incorrect import parameter for cancel PDA (cherry picked from commit 08f2e4edc365cfd5e8d0154d7aef69ad45439bee) --- .../process_deferred_accounting/process_deferred_accounting.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.py b/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.py index 8ec726b36cd..1f88849b26c 100644 --- a/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.py +++ b/erpnext/accounts/doctype/process_deferred_accounting/process_deferred_accounting.py @@ -34,4 +34,4 @@ class ProcessDeferredAccounting(Document): filters={"against_voucher_type": self.doctype, "against_voucher": self.name}, ) - make_gl_entries(gl_entries=gl_entries, cancel=1) + make_gl_entries(gl_map=gl_entries, cancel=1) From 4698dba402b532b6fda3322f32874fbd89d165e1 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 2 Sep 2022 11:22:45 +0530 Subject: [PATCH 09/28] test: pda document submission and cancellation (cherry picked from commit 1c385541fa7d8f28138996447fc38f859a71e224) --- .../test_process_deferred_accounting.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py b/erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py index 164ba6aa348..5a0aeb7284a 100644 --- a/erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py +++ b/erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py @@ -57,3 +57,16 @@ class TestProcessDeferredAccounting(unittest.TestCase): ] check_gl_entries(self, si.name, expected_gle, "2019-01-10") + + def test_pda_submission_and_cancellation(self): + pda = frappe.get_doc( + dict( + doctype="Process Deferred Accounting", + posting_date="2019-01-01", + start_date="2019-01-01", + end_date="2019-01-31", + type="Income", + ) + ) + pda.submit() + pda.cancel() From f871dd4ef6b4a6f7b04353c63ddaff7bec4e9147 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sat, 3 Sep 2022 11:42:04 +0530 Subject: [PATCH 10/28] fix: not able to make variant item (cherry picked from commit 92b0f9cd7e6e3ff7cdc18008e687a1e3a14d6ef9) --- erpnext/stock/doctype/item/item.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js index 29b001fdcb3..7e1476d240a 100644 --- a/erpnext/stock/doctype/item/item.js +++ b/erpnext/stock/doctype/item/item.js @@ -562,7 +562,7 @@ $.extend(erpnext.item, { let selected_attributes = {}; me.multiple_variant_dialog.$wrapper.find('.form-column').each((i, col) => { if(i===0) return; - let attribute_name = $(col).find('label').html().trim(); + let attribute_name = $(col).find('.control-label').html().trim(); selected_attributes[attribute_name] = []; let checked_opts = $(col).find('.checkbox input'); checked_opts.each((i, opt) => { From b96526eefdd3f16fb4e980ee02c39e866ddb6044 Mon Sep 17 00:00:00 2001 From: hamzaali15 Date: Thu, 1 Sep 2022 15:02:21 +0500 Subject: [PATCH 11/28] fix: KSA VAT report multi currency amount issue In KSA VAT report amount is not showing correctly for multi currencies because net_amount field is fetched instead of base_net_amount (cherry picked from commit 56d8962e406a192839a8bf6962d745a9c2f1ea1d) --- erpnext/regional/report/ksa_vat/ksa_vat.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/regional/report/ksa_vat/ksa_vat.py b/erpnext/regional/report/ksa_vat/ksa_vat.py index 15996d2d1f8..3571f962667 100644 --- a/erpnext/regional/report/ksa_vat/ksa_vat.py +++ b/erpnext/regional/report/ksa_vat/ksa_vat.py @@ -177,16 +177,16 @@ def get_tax_data_for_each_vat_setting(vat_setting, filters, doctype): "parent": invoice.name, "item_tax_template": vat_setting.item_tax_template, }, - fields=["item_code", "net_amount"], + fields=["item_code", "base_net_amount"], ) for item in invoice_items: # Summing up total taxable amount if invoice.is_return == 0: - total_taxable_amount += item.net_amount + total_taxable_amount += item.base_net_amount if invoice.is_return == 1: - total_taxable_adjustment_amount += item.net_amount + total_taxable_adjustment_amount += item.base_net_amount # Summing up total tax total_tax += get_tax_amount(item.item_code, vat_setting.account, doctype, invoice.name) From 319ee41403083b86325aab514cf84f4d39100340 Mon Sep 17 00:00:00 2001 From: Saqib Ansari Date: Wed, 10 Aug 2022 14:17:28 +0530 Subject: [PATCH 12/28] fix(pos): error while consolidating pos invoices (cherry picked from commit 33762dbbac55181371f7bcf052474d213312434b) --- .../accounts/doctype/pos_profile/pos_profile.json | 11 ++++++++++- erpnext/controllers/taxes_and_totals.py | 12 ++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pos_profile/pos_profile.json b/erpnext/accounts/doctype/pos_profile/pos_profile.json index d5f7ee4f217..994b6776e3c 100644 --- a/erpnext/accounts/doctype/pos_profile/pos_profile.json +++ b/erpnext/accounts/doctype/pos_profile/pos_profile.json @@ -43,6 +43,7 @@ "currency", "write_off_account", "write_off_cost_center", + "write_off_limit", "account_for_change_amount", "disable_rounded_total", "column_break_23", @@ -360,6 +361,14 @@ "fieldtype": "Check", "label": "Validate Stock on Save" }, + { + "default": "1", + "description": "Auto write off precision loss while consolidation", + "fieldname": "write_off_limit", + "fieldtype": "Currency", + "label": "Write Off Limit", + "reqd": 1 + }, { "default": "0", "description": "If enabled, the consolidated invoices will have rounded total disabled", @@ -393,7 +402,7 @@ "link_fieldname": "pos_profile" } ], - "modified": "2022-07-21 11:16:46.911173", + "modified": "2022-08-10 12:57:06.241439", "modified_by": "Administrator", "module": "Accounts", "name": "POS Profile", diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 9dbcdb04c5b..cbcccce5f71 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -770,6 +770,18 @@ class calculate_taxes_and_totals(object): self.doc.precision("outstanding_amount"), ) + if ( + self.doc.doctype == "Sales Invoice" + and self.doc.get("is_pos") + and self.doc.get("pos_profile") + and self.doc.get("is_consolidated") + ): + write_off_limit = flt( + frappe.db.get_value("POS Profile", self.doc.pos_profile, "write_off_limit") + ) + if write_off_limit and abs(self.doc.outstanding_amount) <= write_off_limit: + self.doc.write_off_outstanding_amount_automatically = 1 + if ( self.doc.doctype == "Sales Invoice" and self.doc.get("is_pos") From 60fa4214098c2d4d81db8f23fd08e4b512836f4d Mon Sep 17 00:00:00 2001 From: hrzzz Date: Mon, 15 Aug 2022 09:14:23 -0300 Subject: [PATCH 13/28] feat: two new filters for gross profit (cherry picked from commit 27891ecb77a74dea90c30133d81d92edebf59cf3) --- .../accounts/report/gross_profit/gross_profit.js | 12 ++++++++++++ .../accounts/report/gross_profit/gross_profit.py | 13 +++++++++++++ 2 files changed, 25 insertions(+) diff --git a/erpnext/accounts/report/gross_profit/gross_profit.js b/erpnext/accounts/report/gross_profit/gross_profit.js index 21205c31634..ffbe9ada6ca 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.js +++ b/erpnext/accounts/report/gross_profit/gross_profit.js @@ -38,6 +38,18 @@ frappe.query_reports["Gross Profit"] = { "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":"sales_person", + "label": __("Sales Person"), + "fieldtype": "Link", + "options": "Sales Person" + }, ], "tree": true, "name_field": "parent", diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index 54af2259154..47a72909aa5 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -8,6 +8,7 @@ from frappe.utils import cint, flt, formatdate from erpnext.controllers.queries import get_match_cond from erpnext.stock.utils import get_incoming_rate +from erpnext.stock.report.stock_ledger.stock_ledger import get_item_group_condition def execute(filters=None): @@ -676,6 +677,17 @@ class GrossProfitGenerator(object): if self.filters.to_date: conditions += " and posting_date <= %(to_date)s" + if self.filters.item_group: + conditions += " and {0}".format(get_item_group_condition(self.filters.item_group)) + + if self.filters.sales_person: + conditions += """ + and exists(select 1 + from `tabSales Team` st + where st.parent = `tabSales Invoice`.name + and st.sales_person = %(sales_person)s) + """ + if self.filters.group_by == "Sales Person": sales_person_cols = ", sales.sales_person, sales.allocated_amount, sales.incentives" sales_team_table = "left join `tabSales Team` sales on sales.parent = `tabSales Invoice`.name" @@ -723,6 +735,7 @@ class GrossProfitGenerator(object): from `tabSales Invoice` inner join `tabSales Invoice Item` on `tabSales Invoice Item`.parent = `tabSales Invoice`.name + join `tabItem` item on item.name = `tabSales Invoice Item`.item_code {sales_team_table} {payment_term_table} where From 055556b7f1327cfc62f88b0028c1649798e1aca4 Mon Sep 17 00:00:00 2001 From: hrzzz Date: Mon, 15 Aug 2022 09:23:56 -0300 Subject: [PATCH 14/28] fix: remove spaces and order import (cherry picked from commit 3ef551872a38c059e8ca09927c7bea44635c9474) --- erpnext/accounts/report/gross_profit/gross_profit.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index 47a72909aa5..f0106bea4fc 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -7,8 +7,8 @@ from frappe import _, scrub from frappe.utils import cint, flt, formatdate from erpnext.controllers.queries import get_match_cond -from erpnext.stock.utils import get_incoming_rate from erpnext.stock.report.stock_ledger.stock_ledger import get_item_group_condition +from erpnext.stock.utils import get_incoming_rate def execute(filters=None): @@ -681,9 +681,9 @@ class GrossProfitGenerator(object): conditions += " and {0}".format(get_item_group_condition(self.filters.item_group)) if self.filters.sales_person: - conditions += """ - and exists(select 1 - from `tabSales Team` st + conditions += """ + and exists(select 1 + from `tabSales Team` st where st.parent = `tabSales Invoice`.name and st.sales_person = %(sales_person)s) """ From 0438433a4ff0143079d49b522bf803b1b4fcb33e Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 4 Sep 2022 13:15:59 +0530 Subject: [PATCH 15/28] chore: Linting Issues (cherry picked from commit ad8d0efa2962a5bc5bd1286ed8f5af7183dd3644) --- .../accounts/report/gross_profit/gross_profit.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/report/gross_profit/gross_profit.js b/erpnext/accounts/report/gross_profit/gross_profit.js index ffbe9ada6ca..615804ef623 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.js +++ b/erpnext/accounts/report/gross_profit/gross_profit.js @@ -4,7 +4,7 @@ frappe.query_reports["Gross Profit"] = { "filters": [ { - "fieldname":"company", + "fieldname": "company", "label": __("Company"), "fieldtype": "Link", "options": "Company", @@ -12,40 +12,40 @@ frappe.query_reports["Gross Profit"] = { "reqd": 1 }, { - "fieldname":"from_date", + "fieldname": "from_date", "label": __("From Date"), "fieldtype": "Date", "default": frappe.defaults.get_user_default("year_start_date"), "reqd": 1 }, { - "fieldname":"to_date", + "fieldname": "to_date", "label": __("To Date"), "fieldtype": "Date", "default": frappe.defaults.get_user_default("year_end_date"), "reqd": 1 }, { - "fieldname":"sales_invoice", + "fieldname": "sales_invoice", "label": __("Sales Invoice"), "fieldtype": "Link", "options": "Sales Invoice" }, { - "fieldname":"group_by", + "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", + "fieldname": "item_group", "label": __("Item Group"), "fieldtype": "Link", "options": "Item Group" }, { - "fieldname":"sales_person", + "fieldname": "sales_person", "label": __("Sales Person"), "fieldtype": "Link", "options": "Sales Person" From 5520c6b2f3421aacdd1b0f0f42df9a6cbb3442b8 Mon Sep 17 00:00:00 2001 From: Solufyin <34390782+Solufyin@users.noreply.github.com> Date: Thu, 1 Sep 2022 11:49:13 +0530 Subject: [PATCH 16/28] fix: Naming series in Journal Entry Template (cherry picked from commit 2085626390f705d5cbd4abbf664efecbc96aa91f) --- .../doctype/journal_entry_template/journal_entry_template.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 1c19c1d2255..cf5fbe12afe 100644 --- a/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.js +++ b/erpnext/accounts/doctype/journal_entry_template/journal_entry_template.js @@ -2,7 +2,7 @@ // For license information, please see license.txt frappe.ui.form.on("Journal Entry Template", { - setup: function(frm) { + refresh: function(frm) { frappe.model.set_default_values(frm.doc); frm.set_query("account" ,"accounts", function(){ From 784fb471976f41e50476922e293c861fe6773684 Mon Sep 17 00:00:00 2001 From: HENRY Florian Date: Sun, 4 Sep 2022 12:55:03 +0200 Subject: [PATCH 17/28] feat: better Item Price list view (#31954) * feat: better Item Price list view (cherry picked from commit 86395c6adb4c577d175ed08c7615439ab9fbb224) --- .../stock/doctype/item_price/item_price.json | 113 +++++------------- .../doctype/item_price/item_price_list.js | 3 + 2 files changed, 34 insertions(+), 82 deletions(-) create mode 100644 erpnext/stock/doctype/item_price/item_price_list.js diff --git a/erpnext/stock/doctype/item_price/item_price.json b/erpnext/stock/doctype/item_price/item_price.json index 83177b372ad..8c6f6d85a46 100644 --- a/erpnext/stock/doctype/item_price/item_price.json +++ b/erpnext/stock/doctype/item_price/item_price.json @@ -48,41 +48,31 @@ "oldfieldtype": "Select", "options": "Item", "reqd": 1, - "search_index": 1, - "show_days": 1, - "show_seconds": 1 + "search_index": 1 }, { "fieldname": "uom", "fieldtype": "Link", "label": "UOM", - "options": "UOM", - "show_days": 1, - "show_seconds": 1 + "options": "UOM" }, { "default": "0", "description": "Quantity that must be bought or sold per UOM", "fieldname": "packing_unit", "fieldtype": "Int", - "label": "Packing Unit", - "show_days": 1, - "show_seconds": 1 + "label": "Packing Unit" }, { "fieldname": "column_break_17", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "item_name", "fieldtype": "Data", "in_list_view": 1, "label": "Item Name", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fetch_from": "item_code.brand", @@ -90,36 +80,29 @@ "fieldtype": "Read Only", "in_list_view": 1, "label": "Brand", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "item_description", "fieldtype": "Text", "label": "Item Description", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "price_list_details", "fieldtype": "Section Break", "label": "Price List", - "options": "fa fa-tags", - "show_days": 1, - "show_seconds": 1 + "options": "fa fa-tags" }, { "fieldname": "price_list", "fieldtype": "Link", "in_global_search": 1, + "in_list_view": 1, "in_standard_filter": 1, "label": "Price List", "options": "Price List", - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "bold": 1, @@ -127,49 +110,37 @@ "fieldname": "customer", "fieldtype": "Link", "label": "Customer", - "options": "Customer", - "show_days": 1, - "show_seconds": 1 + "options": "Customer" }, { "depends_on": "eval:doc.buying == 1", "fieldname": "supplier", "fieldtype": "Link", "label": "Supplier", - "options": "Supplier", - "show_days": 1, - "show_seconds": 1 + "options": "Supplier" }, { "fieldname": "column_break_3", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "default": "0", "fieldname": "buying", "fieldtype": "Check", "label": "Buying", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "default": "0", "fieldname": "selling", "fieldtype": "Check", "label": "Selling", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "item_details", "fieldtype": "Section Break", - "options": "fa fa-tag", - "show_days": 1, - "show_seconds": 1 + "options": "fa fa-tag" }, { "bold": 1, @@ -177,15 +148,11 @@ "fieldtype": "Link", "label": "Currency", "options": "Currency", - "read_only": 1, - "show_days": 1, - "show_seconds": 1 + "read_only": 1 }, { "fieldname": "col_br_1", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "price_list_rate", @@ -197,80 +164,61 @@ "oldfieldname": "ref_rate", "oldfieldtype": "Currency", "options": "currency", - "reqd": 1, - "show_days": 1, - "show_seconds": 1 + "reqd": 1 }, { "fieldname": "section_break_15", - "fieldtype": "Section Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Section Break" }, { "default": "Today", "fieldname": "valid_from", "fieldtype": "Date", - "label": "Valid From", - "show_days": 1, - "show_seconds": 1 + "label": "Valid From" }, { "default": "0", "fieldname": "lead_time_days", "fieldtype": "Int", - "label": "Lead Time in days", - "show_days": 1, - "show_seconds": 1 + "label": "Lead Time in days" }, { "fieldname": "column_break_18", - "fieldtype": "Column Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Column Break" }, { "fieldname": "valid_upto", "fieldtype": "Date", - "label": "Valid Upto", - "show_days": 1, - "show_seconds": 1 + "label": "Valid Upto" }, { "fieldname": "section_break_24", - "fieldtype": "Section Break", - "show_days": 1, - "show_seconds": 1 + "fieldtype": "Section Break" }, { "fieldname": "note", "fieldtype": "Text", - "label": "Note", - "show_days": 1, - "show_seconds": 1 + "label": "Note" }, { "fieldname": "reference", "fieldtype": "Data", "in_list_view": 1, - "label": "Reference", - "show_days": 1, - "show_seconds": 1 + "in_standard_filter": 1, + "label": "Reference" }, { "fieldname": "batch_no", "fieldtype": "Link", "label": "Batch No", - "options": "Batch", - "show_days": 1, - "show_seconds": 1 + "options": "Batch" } ], "icon": "fa fa-flag", "idx": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2020-12-08 18:12:15.395772", + "modified": "2022-09-02 16:33:55.612992", "modified_by": "Administrator", "module": "Stock", "name": "Item Price", @@ -307,6 +255,7 @@ "quick_entry": 1, "sort_field": "modified", "sort_order": "ASC", + "states": [], "title_field": "item_name", "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/stock/doctype/item_price/item_price_list.js b/erpnext/stock/doctype/item_price/item_price_list.js new file mode 100644 index 00000000000..48158393f67 --- /dev/null +++ b/erpnext/stock/doctype/item_price/item_price_list.js @@ -0,0 +1,3 @@ +frappe.listview_settings['Item Price'] = { + hide_name_column: true, +}; From 4cb685a326f05af6652b441eeaec2daa9fb496cd Mon Sep 17 00:00:00 2001 From: Florian HENRY Date: Thu, 25 Aug 2022 20:45:35 +0200 Subject: [PATCH 18/28] fix: upgrade process to version-14 when currency opportunity wass not set (cherry picked from commit 9d02fbadb43d729141422321996084daf31543d1) --- .../update_opportunity_currency_fields.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/erpnext/patches/v14_0/update_opportunity_currency_fields.py b/erpnext/patches/v14_0/update_opportunity_currency_fields.py index 076de526195..3a6f1b3a5de 100644 --- a/erpnext/patches/v14_0/update_opportunity_currency_fields.py +++ b/erpnext/patches/v14_0/update_opportunity_currency_fields.py @@ -4,6 +4,8 @@ from frappe.utils import flt import erpnext from erpnext.setup.utils import get_exchange_rate +import click + def execute(): frappe.reload_doctype("Opportunity") @@ -16,6 +18,19 @@ def execute(): for opportunity in opportunities: company_currency = erpnext.get_company_currency(opportunity.company) + if opportunity.currency is None or opportunity.currency == '': + opportunity.currency = company_currency + frappe.db.set_value( + "Opportunity", + opportunity.name, + {"currency": opportunity.currency}, + update_modified=False, + ) + click.secho( + f" Opportunity `{opportunity.name}` has no currency set. We for it to company currency : `{opportunity.currency}`\"\n", + fg="yellow", + ) + # base total and total will be 0 only since item table did not have amount field earlier if opportunity.currency != company_currency: conversion_rate = get_exchange_rate(opportunity.currency, company_currency) @@ -24,6 +39,10 @@ def execute(): conversion_rate = 1 base_opportunity_amount = flt(opportunity.opportunity_amount) + if conversion_rate is None: + print(opportunity.name,conversion_rate,opportunity.currency) + + frappe.db.set_value( "Opportunity", opportunity.name, From aedd0397b846193c91cf207a8c9285596198cc1a Mon Sep 17 00:00:00 2001 From: Florian HENRY Date: Thu, 25 Aug 2022 22:35:08 +0200 Subject: [PATCH 19/28] chore: remove debug (cherry picked from commit ac66538651f3b66c300678a89d7e53642af46e97) --- .../v14_0/update_opportunity_currency_fields.py | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/erpnext/patches/v14_0/update_opportunity_currency_fields.py b/erpnext/patches/v14_0/update_opportunity_currency_fields.py index 3a6f1b3a5de..7f093ce8c67 100644 --- a/erpnext/patches/v14_0/update_opportunity_currency_fields.py +++ b/erpnext/patches/v14_0/update_opportunity_currency_fields.py @@ -1,11 +1,10 @@ +import click import frappe from frappe.utils import flt import erpnext from erpnext.setup.utils import get_exchange_rate -import click - def execute(): frappe.reload_doctype("Opportunity") @@ -18,7 +17,7 @@ def execute(): for opportunity in opportunities: company_currency = erpnext.get_company_currency(opportunity.company) - if opportunity.currency is None or opportunity.currency == '': + if opportunity.currency is None or opportunity.currency == "": opportunity.currency = company_currency frappe.db.set_value( "Opportunity", @@ -27,7 +26,7 @@ def execute(): update_modified=False, ) click.secho( - f" Opportunity `{opportunity.name}` has no currency set. We for it to company currency : `{opportunity.currency}`\"\n", + f' Opportunity `{opportunity.name}` has no currency set. We for it to company currency : `{opportunity.currency}`"\n', fg="yellow", ) @@ -39,10 +38,6 @@ def execute(): conversion_rate = 1 base_opportunity_amount = flt(opportunity.opportunity_amount) - if conversion_rate is None: - print(opportunity.name,conversion_rate,opportunity.currency) - - frappe.db.set_value( "Opportunity", opportunity.name, From 357f74a5808ec0164960e375422d8225a57d55e0 Mon Sep 17 00:00:00 2001 From: Florian HENRY Date: Thu, 25 Aug 2022 22:35:44 +0200 Subject: [PATCH 20/28] chore: better text (cherry picked from commit d19b664ba950828c375f312f25356b474b53759b) --- erpnext/patches/v14_0/update_opportunity_currency_fields.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches/v14_0/update_opportunity_currency_fields.py b/erpnext/patches/v14_0/update_opportunity_currency_fields.py index 7f093ce8c67..17df853fb13 100644 --- a/erpnext/patches/v14_0/update_opportunity_currency_fields.py +++ b/erpnext/patches/v14_0/update_opportunity_currency_fields.py @@ -26,7 +26,7 @@ def execute(): update_modified=False, ) click.secho( - f' Opportunity `{opportunity.name}` has no currency set. We for it to company currency : `{opportunity.currency}`"\n', + f' Opportunity `{opportunity.name}` has no currency set. We force it to company currency : `{opportunity.currency}`"\n', fg="yellow", ) From 6976316ada2e7f7bc9062f5ce6e5d1cea55bbf89 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 4 Sep 2022 19:03:16 +0530 Subject: [PATCH 21/28] chore: fix message (cherry picked from commit 118b0c0f86fd90ae5ec27ab42cd9bfdf839f5096) --- erpnext/patches/v14_0/update_opportunity_currency_fields.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/patches/v14_0/update_opportunity_currency_fields.py b/erpnext/patches/v14_0/update_opportunity_currency_fields.py index 17df853fb13..b803e9fa2dd 100644 --- a/erpnext/patches/v14_0/update_opportunity_currency_fields.py +++ b/erpnext/patches/v14_0/update_opportunity_currency_fields.py @@ -26,7 +26,7 @@ def execute(): update_modified=False, ) click.secho( - f' Opportunity `{opportunity.name}` has no currency set. We force it to company currency : `{opportunity.currency}`"\n', + f' Opportunity `{opportunity.name}` has no currency set. Setting it to company currency as default: `{opportunity.currency}`"\n', fg="yellow", ) From 00a73c7a571d916b55ba3699afb9eeb02ded5d0b Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Fri, 2 Sep 2022 17:58:40 +0530 Subject: [PATCH 22/28] fix(Appointment): create lead notes as child table (cherry picked from commit 58e553151e8ee29a6c908e9bb9021da90f3180a1) --- erpnext/crm/doctype/appointment/appointment.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/erpnext/crm/doctype/appointment/appointment.py b/erpnext/crm/doctype/appointment/appointment.py index 5f5923dc89b..6e7ba1fd5bc 100644 --- a/erpnext/crm/doctype/appointment/appointment.py +++ b/erpnext/crm/doctype/appointment/appointment.py @@ -7,7 +7,7 @@ from collections import Counter import frappe from frappe import _ from frappe.model.document import Document -from frappe.utils import get_url, getdate +from frappe.utils import get_url, getdate, now from frappe.utils.verified_command import get_signed_params @@ -104,16 +104,28 @@ class Appointment(Document): # Return if already linked if self.party: return + lead = frappe.get_doc( { "doctype": "Lead", "lead_name": self.customer_name, "email_id": self.customer_email, - "notes": self.customer_details, "phone": self.customer_phone_number, } ) + + if self.customer_details: + lead.append( + "notes", + { + "note": self.customer_details, + "added_by": frappe.session.user, + "added_on": now(), + }, + ) + lead.insert(ignore_permissions=True) + # Link lead self.party = lead.name From b7e8fbe43fe2c4112b65751820f073ebe0c3485c Mon Sep 17 00:00:00 2001 From: Sagar Vora Date: Fri, 2 Sep 2022 18:30:39 +0530 Subject: [PATCH 23/28] test: dont create lead manually, add coverage for notes (cherry picked from commit 875ff151099af34708aefc094549d1b6988261ed) --- .../doctype/appointment/test_appointment.py | 27 +++++++------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/erpnext/crm/doctype/appointment/test_appointment.py b/erpnext/crm/doctype/appointment/test_appointment.py index 776e6043331..178b9d2de53 100644 --- a/erpnext/crm/doctype/appointment/test_appointment.py +++ b/erpnext/crm/doctype/appointment/test_appointment.py @@ -6,29 +6,20 @@ import unittest import frappe - -def create_test_lead(): - test_lead = frappe.db.get_value("Lead", {"email_id": "test@example.com"}) - if test_lead: - return frappe.get_doc("Lead", test_lead) - test_lead = frappe.get_doc( - {"doctype": "Lead", "lead_name": "Test Lead", "email_id": "test@example.com"} - ) - test_lead.insert(ignore_permissions=True) - return test_lead +LEAD_EMAIL = "test_appointment_lead@example.com" -def create_test_appointments(): +def create_test_appointment(): test_appointment = frappe.get_doc( { "doctype": "Appointment", - "email": "test@example.com", "status": "Open", "customer_name": "Test Lead", "customer_phone_number": "666", "customer_skype": "test", - "customer_email": "test@example.com", + "customer_email": LEAD_EMAIL, "scheduled_time": datetime.datetime.now(), + "customer_details": "Hello, Friend!", } ) test_appointment.insert() @@ -36,16 +27,16 @@ def create_test_appointments(): class TestAppointment(unittest.TestCase): - test_appointment = test_lead = None + def setUpClass(): + frappe.db.delete("Lead", {"email_id": LEAD_EMAIL}) def setUp(self): - self.test_lead = create_test_lead() - self.test_appointment = create_test_appointments() + self.test_appointment = create_test_appointment() + self.test_appointment.set_verified(self.test_appointment.customer_email) def test_calendar_event_created(self): cal_event = frappe.get_doc("Event", self.test_appointment.calendar_event) self.assertEqual(cal_event.starts_on, self.test_appointment.scheduled_time) def test_lead_linked(self): - lead = frappe.get_doc("Lead", self.test_lead.name) - self.assertIsNotNone(lead) + self.assertTrue(self.test_appointment.party) From 26536da74b9a67d46c0164320d481f21a18afc0b Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Sun, 4 Sep 2022 22:41:48 +0530 Subject: [PATCH 24/28] fix: validate available qty for consumption in SCR (cherry picked from commit 4a7add2169942caa8f21013695ae326c91f087c3) --- .../subcontracting_receipt.py | 44 +++++++++++++++++ .../test_subcontracting_receipt.py | 49 +++++++++++++++++++ .../subcontracting_receipt_supplied_item.json | 21 +++++--- 3 files changed, 108 insertions(+), 6 deletions(-) diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index 021d9aa8547..1da73405e8d 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -75,6 +75,7 @@ class SubcontractingReceipt(SubcontractingController): self.get_current_stock() def on_submit(self): + self.validate_available_qty_for_consumption() self.update_status_updater_args() self.update_prevdoc_status() self.set_subcontracting_order_status() @@ -107,10 +108,42 @@ class SubcontractingReceipt(SubcontractingController): self.set_missing_values_in_supplied_items() self.set_missing_values_in_items() + def set_available_qty_for_consumption(self): + supplied_items_details = {} + + sco_supplied_item = frappe.qb.DocType("Subcontracting Order Supplied Item") + for item in self.get("items"): + supplied_items = ( + frappe.qb.from_(sco_supplied_item) + .select( + sco_supplied_item.rm_item_code, + sco_supplied_item.reference_name, + (sco_supplied_item.total_supplied_qty - sco_supplied_item.consumed_qty).as_("available_qty"), + ) + .where( + (sco_supplied_item.parent == item.subcontracting_order) + & (sco_supplied_item.main_item_code == item.item_code) + & (sco_supplied_item.reference_name == item.subcontracting_order_item) + ) + ).run(as_dict=True) + + if supplied_items: + supplied_items_details[item.name] = {} + + for supplied_item in supplied_items: + supplied_items_details[item.name][supplied_item.rm_item_code] = supplied_item.available_qty + else: + for item in self.get("supplied_items"): + item.available_qty_for_consumption = supplied_items_details.get(item.reference_name, {}).get( + item.rm_item_code, 0 + ) + def set_missing_values_in_supplied_items(self): for item in self.get("supplied_items") or []: item.amount = item.rate * item.consumed_qty + self.set_available_qty_for_consumption() + def set_missing_values_in_items(self): rm_supp_cost = {} for item in self.get("supplied_items") or []: @@ -147,6 +180,17 @@ class SubcontractingReceipt(SubcontractingController): _("Rejected Warehouse is mandatory against rejected Item {0}").format(item.item_code) ) + def validate_available_qty_for_consumption(self): + for item in self.get("supplied_items"): + if ( + item.available_qty_for_consumption and item.available_qty_for_consumption < item.consumed_qty + ): + frappe.throw( + _( + "Row {0}: Consumed Qty must be less than or equal to Available Qty For Consumption in Consumed Items Table." + ).format(item.idx) + ) + def set_items_cost_center(self): if self.company: cost_center = frappe.get_cached_value("Company", self.company, "cost_center") diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py index 763e76882e0..a47af52b337 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py @@ -70,6 +70,55 @@ class TestSubcontractingReceipt(FrappeTestCase): rm_supp_cost = sum(item.amount for item in scr.get("supplied_items")) self.assertEqual(scr.get("items")[0].rm_supp_cost, flt(rm_supp_cost)) + def test_available_qty_for_consumption(self): + make_stock_entry( + item_code="_Test Item", qty=100, target="_Test Warehouse 1 - _TC", basic_rate=100 + ) + make_stock_entry( + item_code="_Test Item Home Desktop 100", + qty=100, + target="_Test Warehouse 1 - _TC", + basic_rate=100, + ) + service_items = [ + { + "warehouse": "_Test Warehouse - _TC", + "item_code": "Subcontracted Service Item 1", + "qty": 10, + "rate": 100, + "fg_item": "_Test FG Item", + "fg_item_qty": 10, + }, + ] + sco = get_subcontracting_order(service_items=service_items) + rm_items = [ + { + "main_item_code": "_Test FG Item", + "item_code": "_Test Item", + "qty": 5.0, + "rate": 100.0, + "stock_uom": "_Test UOM", + "warehouse": "_Test Warehouse - _TC", + }, + { + "main_item_code": "_Test FG Item", + "item_code": "_Test Item Home Desktop 100", + "qty": 10.0, + "rate": 100.0, + "stock_uom": "_Test UOM", + "warehouse": "_Test Warehouse - _TC", + }, + ] + itemwise_details = make_stock_in_entry(rm_items=rm_items) + make_stock_transfer_entry( + sco_no=sco.name, + rm_items=rm_items, + itemwise_details=copy.deepcopy(itemwise_details), + ) + scr = make_subcontracting_receipt(sco.name) + scr.save() + self.assertRaises(frappe.ValidationError, scr.submit) + def test_subcontracting_gle_fg_item_rate_zero(self): from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import get_gl_entries diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt_supplied_item/subcontracting_receipt_supplied_item.json b/erpnext/subcontracting/doctype/subcontracting_receipt_supplied_item/subcontracting_receipt_supplied_item.json index 100a8060e8c..ddbb80661ad 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt_supplied_item/subcontracting_receipt_supplied_item.json +++ b/erpnext/subcontracting/doctype/subcontracting_receipt_supplied_item/subcontracting_receipt_supplied_item.json @@ -19,6 +19,7 @@ "col_break2", "amount", "secbreak_2", + "available_qty_for_consumption", "required_qty", "col_break3", "consumed_qty", @@ -75,8 +76,7 @@ { "fieldname": "required_qty", "fieldtype": "Float", - "in_list_view": 1, - "label": "Available Qty For Consumption", + "label": "Required Qty", "print_hide": 1, "read_only": 1 }, @@ -85,7 +85,7 @@ "fieldname": "consumed_qty", "fieldtype": "Float", "in_list_view": 1, - "label": "Qty to be Consumed", + "label": "Consumed Qty", "reqd": 1 }, { @@ -179,12 +179,21 @@ "options": "Subcontracting Order", "print_hide": 1, "read_only": 1 + }, + { + "default": "0", + "fieldname": "available_qty_for_consumption", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Available Qty For Consumption", + "print_hide": 1, + "read_only": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2022-04-18 10:45:16.538479", + "modified": "2022-09-02 22:28:53.392381", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Receipt Supplied Item", @@ -193,6 +202,6 @@ "permissions": [], "sort_field": "modified", "sort_order": "DESC", - "track_changes": 1, - "states": [] + "states": [], + "track_changes": 1 } \ No newline at end of file From 121ec83562fc53400cd9aab383ba04783654b58b Mon Sep 17 00:00:00 2001 From: Sagar Sharma Date: Mon, 5 Sep 2022 13:55:55 +0530 Subject: [PATCH 25/28] refactor(test): test_update_reserved_qty_for_subcontracting (cherry picked from commit a349b58306d5f0723f4f13181a4c11bcc01af42e) --- .../test_subcontracting_order.py | 112 ++++++++---------- 1 file changed, 49 insertions(+), 63 deletions(-) diff --git a/erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py b/erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py index 098242aed89..f4a943f88dc 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py +++ b/erpnext/subcontracting/doctype/subcontracting_order/test_subcontracting_order.py @@ -187,22 +187,13 @@ class TestSubcontractingOrder(FrappeTestCase): self.assertEqual(len(ste.items), len(rm_items)) def test_update_reserved_qty_for_subcontracting(self): - # Make stock available for raw materials - make_stock_entry(target="_Test Warehouse - _TC", qty=10, basic_rate=100) + # Create RM Material Receipt + make_stock_entry(target="_Test Warehouse - _TC", item_code="_Test Item", qty=10, basic_rate=100) make_stock_entry( target="_Test Warehouse - _TC", item_code="_Test Item Home Desktop 100", qty=20, basic_rate=100 ) - make_stock_entry( - target="_Test Warehouse 1 - _TC", item_code="_Test Item", qty=30, basic_rate=100 - ) - make_stock_entry( - target="_Test Warehouse 1 - _TC", - item_code="_Test Item Home Desktop 100", - qty=30, - basic_rate=100, - ) - bin1 = frappe.db.get_value( + bin_before_sco = frappe.db.get_value( "Bin", filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, fieldname=["reserved_qty_for_sub_contract", "projected_qty", "modified"], @@ -222,102 +213,97 @@ class TestSubcontractingOrder(FrappeTestCase): ] sco = get_subcontracting_order(service_items=service_items) - bin2 = frappe.db.get_value( + bin_after_sco = frappe.db.get_value( "Bin", filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, fieldname=["reserved_qty_for_sub_contract", "projected_qty", "modified"], as_dict=1, ) - self.assertEqual(bin2.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract + 10) - self.assertEqual(bin2.projected_qty, bin1.projected_qty - 10) - self.assertNotEqual(bin1.modified, bin2.modified) + # reserved_qty_for_sub_contract should be increased by 10 + self.assertEqual( + bin_after_sco.reserved_qty_for_sub_contract, bin_before_sco.reserved_qty_for_sub_contract + 10 + ) - # Create stock transfer + # projected_qty should be decreased by 10 + self.assertEqual(bin_after_sco.projected_qty, bin_before_sco.projected_qty - 10) + + self.assertNotEqual(bin_before_sco.modified, bin_after_sco.modified) + + # Create Stock Entry(Send to Subcontractor) rm_items = [ { "item_code": "_Test FG Item", "rm_item_code": "_Test Item", "item_name": "_Test Item", - "qty": 6, + "qty": 10, "warehouse": "_Test Warehouse - _TC", "rate": 100, - "amount": 600, + "amount": 1000, "stock_uom": "Nos", - } + }, + { + "item_code": "_Test FG Item", + "rm_item_code": "_Test Item Home Desktop 100", + "item_name": "_Test Item Home Desktop 100", + "qty": 20, + "warehouse": "_Test Warehouse - _TC", + "rate": 100, + "amount": 2000, + "stock_uom": "Nos", + }, ] ste = frappe.get_doc(make_rm_stock_entry(sco.name, rm_items)) ste.to_warehouse = "_Test Warehouse 1 - _TC" ste.save() ste.submit() - bin3 = frappe.db.get_value( + bin_after_rm_transfer = frappe.db.get_value( "Bin", filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, fieldname="reserved_qty_for_sub_contract", as_dict=1, ) - self.assertEqual(bin3.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6) - - make_stock_entry( - target="_Test Warehouse 1 - _TC", item_code="_Test Item", qty=40, basic_rate=100 - ) - make_stock_entry( - target="_Test Warehouse 1 - _TC", - item_code="_Test Item Home Desktop 100", - qty=40, - basic_rate=100, + # reserved_qty_for_sub_contract should be decreased by 10 + self.assertEqual( + bin_after_rm_transfer.reserved_qty_for_sub_contract, + bin_after_sco.reserved_qty_for_sub_contract - 10, ) - # Make SCR against the SCO - scr = make_subcontracting_receipt(sco.name) - scr.save() - scr.submit() - - bin4 = frappe.db.get_value( - "Bin", - filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, - fieldname="reserved_qty_for_sub_contract", - as_dict=1, - ) - - self.assertEqual(bin4.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract) - - # Cancel SCR - scr.reload() - scr.cancel() - bin5 = frappe.db.get_value( - "Bin", - filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, - fieldname="reserved_qty_for_sub_contract", - as_dict=1, - ) - - self.assertEqual(bin5.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6) - - # Cancel Stock Entry + # Cancel Stock Entry(Send to Subcontractor) ste.cancel() - bin6 = frappe.db.get_value( + bin_after_cancel_ste = frappe.db.get_value( "Bin", filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, fieldname="reserved_qty_for_sub_contract", as_dict=1, ) - self.assertEqual(bin6.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract + 10) + # reserved_qty_for_sub_contract should be increased by 10 + self.assertEqual( + bin_after_cancel_ste.reserved_qty_for_sub_contract, + bin_after_rm_transfer.reserved_qty_for_sub_contract + 10, + ) - # Cancel PO + # Cancel SCO sco.reload() sco.cancel() - bin7 = frappe.db.get_value( + bin_after_cancel_sco = frappe.db.get_value( "Bin", filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"}, fieldname="reserved_qty_for_sub_contract", as_dict=1, ) - self.assertEqual(bin7.reserved_qty_for_sub_contract, bin1.reserved_qty_for_sub_contract) + # reserved_qty_for_sub_contract should be decreased by 10 + self.assertEqual( + bin_after_cancel_sco.reserved_qty_for_sub_contract, + bin_after_cancel_ste.reserved_qty_for_sub_contract - 10, + ) + self.assertEqual( + bin_after_cancel_sco.reserved_qty_for_sub_contract, bin_before_sco.reserved_qty_for_sub_contract + ) def test_exploded_items(self): item_code = "_Test Subcontracted FG Item 11" From 3c688dfa6d21dec32b2f8578a841844dd7f509f9 Mon Sep 17 00:00:00 2001 From: Rucha Mahabal Date: Tue, 6 Sep 2022 11:04:13 +0530 Subject: [PATCH 26/28] feat: tabbed view for Employee form (#31940) (#32095) --- erpnext/setup/doctype/employee/employee.json | 230 ++++++++++++------- 1 file changed, 153 insertions(+), 77 deletions(-) diff --git a/erpnext/setup/doctype/employee/employee.json b/erpnext/setup/doctype/employee/employee.json index 7a806d5906f..39e0acd02aa 100644 --- a/erpnext/setup/doctype/employee/employee.json +++ b/erpnext/setup/doctype/employee/employee.json @@ -10,79 +10,89 @@ "editable_grid": 1, "engine": "InnoDB", "field_order": [ + "basic_details_tab", "basic_information", "employee", "naming_series", "first_name", "middle_name", "last_name", - "salutation", "employee_name", - "image", - "column_break1", - "company", - "status", + "column_break_9", "gender", "date_of_birth", + "salutation", + "column_break1", "date_of_joining", - "employee_number", - "emergency_contact_details", - "person_to_be_contacted", - "relation", - "column_break_19", - "emergency_phone_number", + "image", + "status", "erpnext_user", "user_id", "create_user", "create_user_permission", - "employment_details", - "scheduled_confirmation_date", - "final_confirmation_date", - "col_break_22", - "contract_end_date", - "notice_number_of_days", - "date_of_retirement", - "job_profile", + "company_details_section", + "company", "department", + "employee_number", + "column_break_25", "designation", "reports_to", - "column_break_31", + "column_break_18", "branch", + "employment_details", + "scheduled_confirmation_date", + "column_break_32", + "final_confirmation_date", + "contract_end_date", + "col_break_22", + "notice_number_of_days", + "date_of_retirement", + "contact_details", + "cell_number", + "column_break_40", + "personal_email", + "company_email", + "column_break4", + "prefered_contact_email", + "prefered_email", + "unsubscribed", + "address_section", + "current_address", + "current_accommodation_type", + "column_break_46", + "permanent_address", + "permanent_accommodation_type", + "emergency_contact_details", + "person_to_be_contacted", + "column_break_55", + "emergency_phone_number", + "column_break_19", + "relation", "attendance_and_leave_details", "attendance_device_id", "column_break_44", "holiday_list", "salary_information", - "salary_currency", "ctc", - "payroll_cost_center", - "column_break_52", + "salary_currency", + "salary_mode", + "bank_details_section", "bank_name", "bank_ac_no", - "contact_details", - "cell_number", - "prefered_email", - "personal_email", - "unsubscribed", - "permanent_accommodation_type", - "permanent_address", - "column_break4", - "prefered_contact_email", - "company_email", - "current_accommodation_type", - "current_address", - "sb53", - "bio", "personal_details", - "passport_number", - "date_of_issue", - "valid_upto", - "place_of_issue", "marital_status", - "blood_group", - "column_break6", "family_background", + "column_break6", + "blood_group", "health_details", + "passport_details_section", + "passport_number", + "valid_upto", + "column_break_73", + "date_of_issue", + "place_of_issue", + "profile_tab", + "bio", "educational_qualification", "education", "previous_work_experience", @@ -92,16 +102,20 @@ "exit", "resignation_letter_date", "relieving_date", - "reason_for_leaving", - "leave_encashed", - "encashment_date", "exit_interview_details", "held_on", "new_workplace", + "column_break_99", + "leave_encashed", + "encashment_date", + "feedback_section", + "reason_for_leaving", + "column_break_104", "feedback", "lft", "rgt", - "old_parent" + "old_parent", + "connections_tab" ], "fields": [ { @@ -261,7 +275,7 @@ "collapsible": 1, "fieldname": "erpnext_user", "fieldtype": "Section Break", - "label": "ERPNext User" + "label": "User Details" }, { "description": "System User (login) ID. If set, it will become default for all HR forms.", @@ -289,8 +303,8 @@ "allow_in_quick_entry": 1, "collapsible": 1, "fieldname": "employment_details", - "fieldtype": "Section Break", - "label": "Joining Details" + "fieldtype": "Tab Break", + "label": "Joining" }, { "fieldname": "scheduled_confirmation_date", @@ -331,12 +345,6 @@ "oldfieldname": "date_of_retirement", "oldfieldtype": "Date" }, - { - "collapsible": 1, - "fieldname": "job_profile", - "fieldtype": "Section Break", - "label": "Department" - }, { "fieldname": "department", "fieldtype": "Link", @@ -366,10 +374,6 @@ "oldfieldtype": "Link", "options": "Employee" }, - { - "fieldname": "column_break_31", - "fieldtype": "Column Break" - }, { "fieldname": "branch", "fieldtype": "Link", @@ -391,7 +395,7 @@ { "collapsible": 1, "fieldname": "salary_information", - "fieldtype": "Section Break", + "fieldtype": "Tab Break", "label": "Salary Details", "oldfieldtype": "Section Break", "width": "50%" @@ -423,8 +427,8 @@ { "collapsible": 1, "fieldname": "contact_details", - "fieldtype": "Section Break", - "label": "Contact Details" + "fieldtype": "Tab Break", + "label": "Contact" }, { "fieldname": "cell_number", @@ -493,12 +497,6 @@ "fieldtype": "Small Text", "label": "Current Address" }, - { - "collapsible": 1, - "fieldname": "sb53", - "fieldtype": "Section Break", - "label": "Personal Bio" - }, { "description": "Short biography for website and other publications.", "fieldname": "bio", @@ -508,7 +506,7 @@ { "collapsible": 1, "fieldname": "personal_details", - "fieldtype": "Section Break", + "fieldtype": "Tab Break", "label": "Personal Details" }, { @@ -601,7 +599,7 @@ { "collapsible": 1, "fieldname": "exit", - "fieldtype": "Section Break", + "fieldtype": "Tab Break", "label": "Exit", "oldfieldtype": "Section Break" }, @@ -702,7 +700,7 @@ { "collapsible": 1, "fieldname": "attendance_and_leave_details", - "fieldtype": "Section Break", + "fieldtype": "Tab Break", "label": "Attendance and Leave Details" }, { @@ -713,10 +711,6 @@ "fieldname": "column_break_19", "fieldtype": "Column Break" }, - { - "fieldname": "column_break_52", - "fieldtype": "Column Break" - }, { "fieldname": "salary_currency", "fieldtype": "Link", @@ -728,13 +722,95 @@ "fieldtype": "Currency", "label": "Cost to Company (CTC)", "options": "salary_currency" + }, + { + "fieldname": "basic_details_tab", + "fieldtype": "Tab Break", + "label": "Basic Details" + }, + { + "fieldname": "company_details_section", + "fieldtype": "Section Break", + "label": "Company Details" + }, + { + "fieldname": "column_break_18", + "fieldtype": "Column Break" + }, + { + "collapsible": 1, + "fieldname": "address_section", + "fieldtype": "Section Break", + "label": "Address" + }, + { + "fieldname": "column_break_46", + "fieldtype": "Column Break" + }, + { + "fieldname": "profile_tab", + "fieldtype": "Tab Break", + "label": "Profile" + }, + { + "fieldname": "passport_details_section", + "fieldtype": "Section Break", + "label": "Passport Details" + }, + { + "fieldname": "column_break_73", + "fieldtype": "Column Break" + }, + { + "fieldname": "bank_details_section", + "fieldtype": "Section Break", + "label": "Bank Details" + }, + { + "fieldname": "column_break_9", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_25", + "fieldtype": "Column Break" + }, + { + "fieldname": "connections_tab", + "fieldtype": "Tab Break", + "label": "Connections", + "show_dashboard": 1 + }, + { + "fieldname": "column_break_32", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_40", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_55", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_99", + "fieldtype": "Column Break" + }, + { + "fieldname": "feedback_section", + "fieldtype": "Section Break", + "label": "Feedback" + }, + { + "fieldname": "column_break_104", + "fieldtype": "Column Break" } ], "icon": "fa fa-user", "idx": 24, "image_field": "image", "links": [], - "modified": "2022-06-27 01:29:32.952091", + "modified": "2022-08-23 13:47:46.944993", "modified_by": "Administrator", "module": "Setup", "name": "Employee", From 76ae4d87ca3f664b9d0a422aa3f63c335018181d Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Fri, 2 Sep 2022 18:43:55 +0530 Subject: [PATCH 27/28] fix: fetch from parent not working for custom field --- erpnext/controllers/selling_controller.py | 2 ++ erpnext/controllers/stock_controller.py | 12 ++++++++ .../inventory_dimension.js | 28 +++++++++++++++++++ .../inventory_dimension.json | 5 ++-- .../inventory_dimension.py | 27 ++++++++++++++++++ 5 files changed, 71 insertions(+), 3 deletions(-) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index a3d41ab29af..5e9c069b1d5 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -311,6 +311,7 @@ class SellingController(StockController): "sales_invoice_item": d.get("sales_invoice_item"), "dn_detail": d.get("dn_detail"), "incoming_rate": p.get("incoming_rate"), + "item_row": p, } ) ) @@ -334,6 +335,7 @@ class SellingController(StockController): "sales_invoice_item": d.get("sales_invoice_item"), "dn_detail": d.get("dn_detail"), "incoming_rate": d.get("incoming_rate"), + "item_row": d, } ) ) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 36bed36484e..4efe25a7a3e 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -390,6 +390,10 @@ class StockController(AccountsController): return sl_dict def update_inventory_dimensions(self, row, sl_dict) -> None: + # To handle delivery note and sales invoice + if row.get("item_row"): + row = row.get("item_row") + dimensions = get_evaluated_inventory_dimension(row, sl_dict, parent_doc=self) for dimension in dimensions: if not dimension: @@ -407,9 +411,17 @@ class StockController(AccountsController): "DocField", {"parent": self.doctype, "options": dimension.fetch_from_parent}, "fieldname" ) + if not fieldname: + fieldname = frappe.get_cached_value( + "Custom Field", {"dt": self.doctype, "options": dimension.fetch_from_parent}, "fieldname" + ) + if fieldname and self.get(fieldname): sl_dict[dimension.target_fieldname] = self.get(fieldname) + if sl_dict[dimension.target_fieldname]: + row.set(dimension.source_fieldname, sl_dict[dimension.target_fieldname]) + def make_sl_entries(self, sl_entries, allow_negative_stock=False, via_landed_cost_voucher=False): from erpnext.stock.stock_ledger import make_sl_entries diff --git a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.js b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.js index 07cb73b1d56..034f14e2a42 100644 --- a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.js +++ b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.js @@ -52,6 +52,34 @@ frappe.ui.form.on('Inventory Dimension', { } }, + onload(frm) { + frm.trigger("set_parent_fields"); + }, + + document_type(frm) { + frm.trigger("set_parent_fields"); + }, + + set_parent_fields(frm) { + if (frm.doc.apply_to_all_doctypes) { + 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', + args: { + child_doctype: frm.doc.document_type, + 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)); + } + } + }) + } + }, + delete_dimension(frm) { let msg = (` Custom fields related to this dimension will be deleted on deletion of dimension. diff --git a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.json b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.json index 03e7fda8411..09f4f63031c 100644 --- a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.json +++ b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.json @@ -144,16 +144,15 @@ "fieldtype": "Column Break" }, { - "depends_on": "istable", "description": "Set fieldname or DocType name like Supplier, Customer etc.", "fieldname": "fetch_from_parent", - "fieldtype": "Data", + "fieldtype": "Select", "label": "Fetch Value From Parent Form" } ], "index_web_pages_for_search": 1, "links": [], - "modified": "2022-08-17 11:43:24.722441", + "modified": "2022-09-02 13:29:04.098469", "modified_by": "Administrator", "module": "Stock", "name": "Inventory Dimension", diff --git a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py index 4ff8f33b409..9e8c10b394d 100644 --- a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py +++ b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py @@ -236,3 +236,30 @@ def get_inventory_dimensions(): def delete_dimension(dimension): doc = frappe.get_doc("Inventory Dimension", dimension) doc.delete() + + +@frappe.whitelist() +def get_parent_fields(child_doctype, dimension_name): + parent_doctypes = frappe.get_all( + "DocField", fields=["parent"], filters={"options": child_doctype} + ) + + fields = [] + + fields.extend( + frappe.get_all( + "DocField", + fields=["fieldname as value", "label"], + filters={"options": dimension_name, "parent": ("in", [d.parent for d in parent_doctypes])}, + ) + ) + + fields.extend( + frappe.get_all( + "Custom Field", + fields=["fieldname as value", "label"], + filters={"options": dimension_name, "dt": ("in", [d.parent for d in parent_doctypes])}, + ) + ) + + return fields From 6ab0637b0bde4f26293b9e9387eb78ff2477d66c Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Sat, 3 Sep 2022 17:09:24 +0530 Subject: [PATCH 28/28] test: test cases for PI and DN --- erpnext/controllers/stock_controller.py | 4 +- .../inventory_dimension.js | 7 +- .../test_inventory_dimension.py | 77 +++++++++++++++++++ 3 files changed, 81 insertions(+), 7 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 4efe25a7a3e..d4f9aba41d2 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -419,8 +419,8 @@ class StockController(AccountsController): if fieldname and self.get(fieldname): sl_dict[dimension.target_fieldname] = self.get(fieldname) - if sl_dict[dimension.target_fieldname]: - row.set(dimension.source_fieldname, sl_dict[dimension.target_fieldname]) + if sl_dict[dimension.target_fieldname] and self.docstatus == 1: + row.db_set(dimension.source_fieldname, sl_dict[dimension.target_fieldname]) def make_sl_entries(self, sl_entries, allow_negative_stock=False, via_landed_cost_voucher=False): from erpnext.stock.stock_ledger import make_sl_entries diff --git a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.js b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.js index 034f14e2a42..79e7895f6d0 100644 --- a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.js +++ b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.js @@ -30,6 +30,7 @@ frappe.ui.form.on('Inventory Dimension', { onload(frm) { frm.trigger('render_traget_field'); + frm.trigger("set_parent_fields"); }, refresh(frm) { @@ -52,10 +53,6 @@ frappe.ui.form.on('Inventory Dimension', { } }, - onload(frm) { - frm.trigger("set_parent_fields"); - }, - document_type(frm) { frm.trigger("set_parent_fields"); }, @@ -76,7 +73,7 @@ frappe.ui.form.on('Inventory Dimension', { [""].concat(r.message)); } } - }) + }); } }, diff --git a/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py b/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py index cc90b74ee85..19ddc449f0e 100644 --- a/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py +++ b/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py @@ -2,14 +2,17 @@ # See license.txt import frappe +from frappe.custom.doctype.custom_field.custom_field import create_custom_field from frappe.tests.utils import FrappeTestCase +from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note from erpnext.stock.doctype.inventory_dimension.inventory_dimension import ( CanNotBeChildDoc, CanNotBeDefaultDimension, DoNotChangeError, delete_dimension, ) +from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse @@ -136,6 +139,58 @@ class TestInventoryDimension(FrappeTestCase): self.assertTrue(inv_dim1.has_stock_ledger()) self.assertRaises(DoNotChangeError, inv_dim1.save) + def test_inventory_dimension_for_purchase_receipt_and_delivery_note(self): + create_inventory_dimension( + reference_document="Rack", + type_of_transaction="Both", + dimension_name="Rack", + apply_to_all_doctypes=1, + fetch_from_parent="Rack", + ) + + create_custom_field( + "Purchase Receipt", dict(fieldname="rack", label="Rack", fieldtype="Link", options="Rack") + ) + + create_custom_field( + "Delivery Note", dict(fieldname="rack", label="Rack", fieldtype="Link", options="Rack") + ) + + frappe.reload_doc("stock", "doctype", "purchase_receipt_item") + frappe.reload_doc("stock", "doctype", "delivery_note_item") + + pr_doc = make_purchase_receipt(qty=2, do_not_submit=True) + pr_doc.rack = "Rack 1" + pr_doc.save() + pr_doc.submit() + + pr_doc.load_from_db() + + self.assertEqual(pr_doc.items[0].rack, "Rack 1") + sle_rack = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_detail_no": pr_doc.items[0].name, "voucher_type": pr_doc.doctype}, + "rack", + ) + + self.assertEqual(sle_rack, "Rack 1") + + dn_doc = create_delivery_note(qty=2, do_not_submit=True) + dn_doc.rack = "Rack 1" + dn_doc.save() + dn_doc.submit() + + dn_doc.load_from_db() + + self.assertEqual(dn_doc.items[0].rack, "Rack 1") + sle_rack = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_detail_no": dn_doc.items[0].name, "voucher_type": dn_doc.doctype}, + "rack", + ) + + self.assertEqual(sle_rack, "Rack 1") + def prepare_test_data(): if not frappe.db.exists("DocType", "Shelf"): @@ -160,6 +215,28 @@ def prepare_test_data(): create_warehouse("Shelf Warehouse") + if not frappe.db.exists("DocType", "Rack"): + frappe.get_doc( + { + "doctype": "DocType", + "name": "Rack", + "module": "Stock", + "custom": 1, + "naming_rule": "By fieldname", + "autoname": "field:rack_name", + "fields": [{"label": "Rack Name", "fieldname": "rack_name", "fieldtype": "Data"}], + "permissions": [ + {"role": "System Manager", "permlevel": 0, "read": 1, "write": 1, "create": 1, "delete": 1} + ], + } + ).insert(ignore_permissions=True) + + for rack in ["Rack 1"]: + if not frappe.db.exists("Rack", rack): + frappe.get_doc({"doctype": "Rack", "rack_name": rack}).insert(ignore_permissions=True) + + create_warehouse("Rack Warehouse") + def create_inventory_dimension(**args): args = frappe._dict(args)