diff --git a/.github/workflows/server-tests-mariadb.yml b/.github/workflows/server-tests-mariadb.yml index c4ae61091ff..ba6a2835692 100644 --- a/.github/workflows/server-tests-mariadb.yml +++ b/.github/workflows/server-tests-mariadb.yml @@ -120,7 +120,7 @@ jobs: FRAPPE_BRANCH: ${{ github.event.inputs.branch }} - name: Run Tests - run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --app erpnext --use-orchestrator --with-coverage + run: 'cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --app erpnext --total-builds 3 --build-number ${{ matrix.container }}' env: TYPE: server CI_BUILD_ID: ${{ github.run_id }} diff --git a/CODEOWNERS b/CODEOWNERS index ecbae86d96b..e0a0fb75f8e 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -11,22 +11,18 @@ erpnext/selling @nextchamp-saqib @deepeshgarg007 @ruthra-kumar erpnext/support/ @nextchamp-saqib @deepeshgarg007 pos* @nextchamp-saqib -erpnext/buying/ @marination @rohitwaghchaure @s-aga-r -erpnext/e_commerce/ @marination -erpnext/maintenance/ @marination @rohitwaghchaure @s-aga-r -erpnext/manufacturing/ @marination @rohitwaghchaure @s-aga-r -erpnext/portal/ @marination -erpnext/quality_management/ @marination @rohitwaghchaure @s-aga-r -erpnext/shopping_cart/ @marination -erpnext/stock/ @marination @rohitwaghchaure @s-aga-r +erpnext/buying/ @rohitwaghchaure @s-aga-r +erpnext/maintenance/ @rohitwaghchaure @s-aga-r +erpnext/manufacturing/ @rohitwaghchaure @s-aga-r +erpnext/quality_management/ @rohitwaghchaure @s-aga-r +erpnext/stock/ @rohitwaghchaure @s-aga-r erpnext/crm/ @NagariaHussain erpnext/education/ @rutwikhdev erpnext/projects/ @ruchamahabal -erpnext/controllers/ @deepeshgarg007 @nextchamp-saqib @rohitwaghchaure @marination -erpnext/patches/ @deepeshgarg007 @nextchamp-saqib @marination -erpnext/public/ @nextchamp-saqib @marination +erpnext/controllers/ @deepeshgarg007 @nextchamp-saqib @rohitwaghchaure +erpnext/patches/ @deepeshgarg007 @nextchamp-saqib .github/ @ankush -pyproject.toml @gavindsouza @ankush +pyproject.toml @ankush diff --git a/erpnext/accounts/doctype/budget/budget.json b/erpnext/accounts/doctype/budget/budget.json index fc4dd200ea2..f0566f44368 100644 --- a/erpnext/accounts/doctype/budget/budget.json +++ b/erpnext/accounts/doctype/budget/budget.json @@ -1,6 +1,7 @@ { "actions": [], "allow_import": 1, + "autoname": "naming_series:", "creation": "2016-05-16 11:42:29.632528", "doctype": "DocType", "editable_grid": 1, @@ -9,6 +10,7 @@ "budget_against", "company", "cost_center", + "naming_series", "project", "fiscal_year", "column_break_3", @@ -190,15 +192,26 @@ "label": "Budget Accounts", "options": "Budget Account", "reqd": 1 + }, + { + "fieldname": "naming_series", + "fieldtype": "Data", + "hidden": 1, + "label": "Series", + "no_copy": 1, + "print_hide": 1, + "read_only": 1, + "set_only_once": 1 } ], "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2020-10-06 15:13:54.055854", + "modified": "2022-10-10 22:14:36.361509", "modified_by": "Administrator", "module": "Accounts", "name": "Budget", + "naming_rule": "By \"Naming Series\" field", "owner": "Administrator", "permissions": [ { @@ -220,5 +233,6 @@ ], "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/budget/budget.py b/erpnext/accounts/doctype/budget/budget.py index 5527f9fb99f..6ac3350c3b0 100644 --- a/erpnext/accounts/doctype/budget/budget.py +++ b/erpnext/accounts/doctype/budget/budget.py @@ -5,7 +5,6 @@ import frappe from frappe import _ from frappe.model.document import Document -from frappe.model.naming import make_autoname from frappe.utils import add_months, flt, fmt_money, get_last_day, getdate from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( @@ -23,11 +22,6 @@ class DuplicateBudgetError(frappe.ValidationError): class Budget(Document): - def autoname(self): - self.name = make_autoname( - self.get(frappe.scrub(self.budget_against)) + "/" + self.fiscal_year + "/.###" - ) - def validate(self): if not self.get(frappe.scrub(self.budget_against)): frappe.throw(_("{0} is mandatory").format(self.budget_against)) @@ -109,6 +103,9 @@ class Budget(Document): ): self.applicable_on_booking_actual_expenses = 1 + def before_naming(self): + self.naming_series = f"{{{frappe.scrub(self.budget_against)}}}./.{self.fiscal_year}/.###" + def validate_expense_against_budget(args): args = frappe._dict(args) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index 7af41f398ad..763e2e6992c 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -173,8 +173,8 @@ frappe.ui.form.on("Journal Entry", { var update_jv_details = function(doc, r) { $.each(r, function(i, d) { var row = frappe.model.add_child(doc, "Journal Entry Account", "accounts"); - row.account = d.account; - row.balance = d.balance; + 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"); } 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 a71b19e0922..fc6dbba7e7f 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 @@ -3,12 +3,13 @@ import frappe from frappe import qb -from frappe.tests.utils import FrappeTestCase +from frappe.tests.utils import FrappeTestCase, change_settings from frappe.utils import nowdate from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry 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.selling.doctype.sales_order.test_sales_order import make_sales_order from erpnext.stock.doctype.item.test_item import create_item @@ -127,6 +128,25 @@ class TestPaymentLedgerEntry(FrappeTestCase): payment.posting_date = posting_date return payment + def create_sales_order( + self, qty=1, rate=100, posting_date=nowdate(), do_not_save=False, do_not_submit=False + ): + so = make_sales_order( + company=self.company, + transaction_date=posting_date, + customer=self.customer, + item_code=self.item, + cost_center=self.cost_center, + warehouse=self.warehouse, + debit_to=self.debit_to, + currency="INR", + qty=qty, + rate=100, + do_not_save=do_not_save, + do_not_submit=do_not_submit, + ) + return so + def clear_old_entries(self): doctype_list = [ "GL Entry", @@ -406,3 +426,89 @@ class TestPaymentLedgerEntry(FrappeTestCase): ] self.assertEqual(pl_entries_for_crnote[0], expected_values[0]) self.assertEqual(pl_entries_for_crnote[1], expected_values[1]) + + @change_settings( + "Accounts Settings", + {"unlink_payment_on_cancellation_of_invoice": 1, "delete_linked_ledger_entries": 1}, + ) + def test_multi_payment_unlink_on_invoice_cancellation(self): + transaction_date = nowdate() + amount = 100 + si = self.create_sales_invoice(qty=1, rate=amount, posting_date=transaction_date) + + for amt in [40, 40, 20]: + # payment 1 + pe = get_payment_entry(si.doctype, si.name) + pe.paid_amount = amt + pe.get("references")[0].allocated_amount = amt + pe = pe.save().submit() + + si.reload() + si.cancel() + + entries = frappe.db.get_list( + "Payment Ledger Entry", + filters={"against_voucher_type": si.doctype, "against_voucher_no": si.name, "delinked": 0}, + ) + self.assertEqual(entries, []) + + # with references removed, deletion should be possible + si.delete() + self.assertRaises(frappe.DoesNotExistError, frappe.get_doc, si.doctype, si.name) + + @change_settings( + "Accounts Settings", + {"unlink_payment_on_cancellation_of_invoice": 1, "delete_linked_ledger_entries": 1}, + ) + def test_multi_je_unlink_on_invoice_cancellation(self): + transaction_date = nowdate() + amount = 100 + si = self.create_sales_invoice(qty=1, rate=amount, posting_date=transaction_date) + + # multiple JE's against invoice + for amt in [40, 40, 20]: + je1 = self.create_journal_entry( + self.income_account, self.debit_to, amt, posting_date=transaction_date + ) + je1.get("accounts")[1].party_type = "Customer" + je1.get("accounts")[1].party = self.customer + je1.get("accounts")[1].reference_type = si.doctype + je1.get("accounts")[1].reference_name = si.name + je1 = je1.save().submit() + + si.reload() + si.cancel() + + entries = frappe.db.get_list( + "Payment Ledger Entry", + filters={"against_voucher_type": si.doctype, "against_voucher_no": si.name, "delinked": 0}, + ) + self.assertEqual(entries, []) + + # with references removed, deletion should be possible + si.delete() + self.assertRaises(frappe.DoesNotExistError, frappe.get_doc, si.doctype, si.name) + + @change_settings( + "Accounts Settings", + {"unlink_payment_on_cancellation_of_invoice": 1, "delete_linked_ledger_entries": 1}, + ) + def test_advance_payment_unlink_on_order_cancellation(self): + transaction_date = nowdate() + amount = 100 + so = self.create_sales_order(qty=1, rate=amount, posting_date=transaction_date).save().submit() + + pe = get_payment_entry(so.doctype, so.name).save().submit() + + so.reload() + so.cancel() + + entries = frappe.db.get_list( + "Payment Ledger Entry", + filters={"against_voucher_type": so.doctype, "against_voucher_no": so.name, "delinked": 0}, + ) + self.assertEqual(entries, []) + + # with references removed, deletion should be possible + so.delete() + self.assertRaises(frappe.DoesNotExistError, frappe.get_doc, so.doctype, so.name) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json index 6f8b3822c2e..eedaaaf338b 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json @@ -343,7 +343,8 @@ "no_copy": 1, "options": "POS Invoice", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "search_index": 1 }, { "default": "0", @@ -1553,7 +1554,7 @@ "icon": "fa fa-file-text", "is_submittable": 1, "links": [], - "modified": "2022-09-27 13:00:24.166684", + "modified": "2022-09-30 03:49:50.455199", "modified_by": "Administrator", "module": "Accounts", "name": "POS Invoice", diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index fbe0ef39f83..54a3e934b2d 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -743,7 +743,3 @@ def add_return_modes(doc, pos_profile): ]: payment_mode = get_mode_of_payment_info(mode_of_payment, doc.company) append_payment(payment_mode[0]) - - -def on_doctype_update(): - frappe.db.add_index("POS Invoice", ["return_against"]) diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py index 70f128e0e39..3132fdd259a 100644 --- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py @@ -495,6 +495,67 @@ class TestPOSInvoice(unittest.TestCase): self.assertRaises(frappe.ValidationError, pos.submit) + def test_value_error_on_serial_no_validation(self): + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item + + se = make_serialized_item( + company="_Test Company", + target_warehouse="Stores - _TC", + cost_center="Main - _TC", + expense_account="Cost of Goods Sold - _TC", + ) + serial_nos = se.get("items")[0].serial_no + + # make a pos invoice + pos = create_pos_invoice( + company="_Test Company", + debit_to="Debtors - _TC", + account_for_change_amount="Cash - _TC", + warehouse="Stores - _TC", + income_account="Sales - _TC", + expense_account="Cost of Goods Sold - _TC", + cost_center="Main - _TC", + item=se.get("items")[0].item_code, + rate=1000, + qty=1, + do_not_save=1, + ) + pos.get("items")[0].has_serial_no = 1 + pos.get("items")[0].serial_no = serial_nos.split("\n")[0] + pos.set("payments", []) + pos.append( + "payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 1000, "default": 1} + ) + pos = pos.save().submit() + + # make a return + pos_return = make_sales_return(pos.name) + pos_return.paid_amount = pos_return.grand_total + pos_return.save() + pos_return.submit() + + # set docstatus to 2 for pos to trigger this issue + frappe.db.set_value("POS Invoice", pos.name, "docstatus", 2) + + pos2 = create_pos_invoice( + company="_Test Company", + debit_to="Debtors - _TC", + account_for_change_amount="Cash - _TC", + warehouse="Stores - _TC", + income_account="Sales - _TC", + expense_account="Cost of Goods Sold - _TC", + cost_center="Main - _TC", + item=se.get("items")[0].item_code, + rate=1000, + qty=1, + do_not_save=1, + ) + + pos2.get("items")[0].has_serial_no = 1 + pos2.get("items")[0].serial_no = serial_nos.split("\n")[0] + # Value error should not be triggered on validation + pos2.save() + def test_loyalty_points(self): from erpnext.accounts.doctype.loyalty_program.loyalty_program import ( get_loyalty_program_details_with_points, diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 1e477776e2d..e73d6023328 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -12,39 +12,23 @@ "supplier", "supplier_name", "tax_id", - "due_date", - "tax_withholding_category", - "column_break1", "company", + "column_break_6", "posting_date", "posting_time", "set_posting_time", + "due_date", + "column_break1", "is_paid", "is_return", + "return_against", "apply_tds", + "tax_withholding_category", "amended_from", "accounting_dimensions_section", "cost_center", "dimension_col_break", "project", - "supplier_invoice_details", - "bill_no", - "column_break_15", - "bill_date", - "returns", - "return_against", - "section_addresses", - "supplier_address", - "address_display", - "contact_person", - "contact_display", - "contact_mobile", - "contact_email", - "col_break_address", - "shipping_address", - "shipping_address_display", - "billing_address", - "billing_address_display", "currency_and_price_list", "currency", "conversion_rate", @@ -54,39 +38,33 @@ "plc_conversion_rate", "ignore_pricing_rule", "sec_warehouse", - "set_warehouse", - "rejected_warehouse", - "col_break_warehouse", - "set_from_warehouse", - "supplier_warehouse", - "is_subcontracted", - "items_section", - "update_stock", "scan_barcode", + "col_break_warehouse", + "update_stock", + "set_warehouse", + "set_from_warehouse", + "is_subcontracted", + "rejected_warehouse", + "supplier_warehouse", + "items_section", "items", - "pricing_rule_details", - "pricing_rules", - "raw_materials_supplied", - "supplied_items", "section_break_26", "total_qty", + "total_net_weight", + "column_break_50", "base_total", "base_net_total", "column_break_28", - "total_net_weight", "total", "net_total", "taxes_section", + "taxes_and_charges", + "column_break_58", "tax_category", "column_break_49", "shipping_rule", "section_break_51", - "taxes_and_charges", "taxes", - "tax_withheld_vouchers_section", - "tax_withheld_vouchers", - "sec_tax_breakup", - "other_charges_calculation", "totals", "base_taxes_and_charges_added", "base_taxes_and_charges_deducted", @@ -95,12 +73,6 @@ "taxes_and_charges_added", "taxes_and_charges_deducted", "total_taxes_and_charges", - "section_break_44", - "apply_discount_on", - "base_discount_amount", - "column_break_46", - "additional_discount_percentage", - "discount_amount", "section_break_49", "base_grand_total", "base_rounding_adjustment", @@ -114,24 +86,58 @@ "total_advance", "outstanding_amount", "disable_rounded_total", + "section_break_44", + "apply_discount_on", + "base_discount_amount", + "additional_discount_account", + "column_break_46", + "additional_discount_percentage", + "discount_amount", + "tax_withheld_vouchers_section", + "tax_withheld_vouchers", + "sec_tax_breakup", + "other_charges_calculation", + "pricing_rule_details", + "pricing_rules", + "raw_materials_supplied", + "supplied_items", + "payments_tab", "payments_section", "mode_of_payment", - "cash_bank_account", + "base_paid_amount", "clearance_date", "col_br_payments", + "cash_bank_account", "paid_amount", - "base_paid_amount", + "advances_section", + "allocate_advances_automatically", + "get_advances", + "advances", + "advance_tax", "write_off", "write_off_amount", "base_write_off_amount", "column_break_61", "write_off_account", "write_off_cost_center", - "advances_section", - "allocate_advances_automatically", - "get_advances", - "advances", - "advance_tax", + "address_and_contact_tab", + "section_addresses", + "supplier_address", + "address_display", + "col_break_address", + "contact_person", + "contact_display", + "contact_mobile", + "contact_email", + "company_shipping_address_section", + "shipping_address", + "column_break_126", + "shipping_address_display", + "company_billing_address_section", + "billing_address", + "column_break_130", + "billing_address_display", + "terms_tab", "payment_schedule_section", "payment_terms_template", "ignore_default_payment_terms_template", @@ -139,23 +145,15 @@ "terms_section_break", "tc_name", "terms", - "printing_settings", - "letter_head", - "select_print_heading", - "column_break_112", - "group_same_items", - "language", - "sb_14", - "on_hold", - "release_date", - "cb_17", - "hold_comment", - "more_info", + "more_info_tab", + "status_section", "status", - "inter_company_invoice_reference", - "represents_company", - "column_break_147", - "is_internal_supplier", + "column_break_177", + "per_received", + "supplier_invoice_details", + "bill_no", + "column_break_15", + "bill_date", "accounting_details_section", "credit_to", "party_account_currency", @@ -163,15 +161,32 @@ "against_expense_account", "column_break_63", "unrealized_profit_loss_account", - "remarks", "subscription_section", - "from_date", - "to_date", - "column_break_114", "auto_repeat", "update_auto_repeat_reference", - "per_received", - "is_old_subcontracting_flow" + "column_break_114", + "from_date", + "to_date", + "printing_settings", + "letter_head", + "group_same_items", + "column_break_112", + "select_print_heading", + "language", + "sb_14", + "on_hold", + "release_date", + "cb_17", + "hold_comment", + "additional_info_section", + "is_internal_supplier", + "represents_company", + "column_break_147", + "inter_company_invoice_reference", + "is_old_subcontracting_flow", + "remarks", + "connections_tab", + "column_break_38" ], "fields": [ { @@ -354,7 +369,7 @@ "collapsible_depends_on": "bill_no", "fieldname": "supplier_invoice_details", "fieldtype": "Section Break", - "label": "Supplier Invoice Details" + "label": "Supplier Invoice" }, { "fieldname": "bill_no", @@ -377,12 +392,6 @@ "oldfieldtype": "Date", "print_hide": 1 }, - { - "depends_on": "return_against", - "fieldname": "returns", - "fieldtype": "Section Break", - "label": "Returns" - }, { "depends_on": "return_against", "fieldname": "return_against", @@ -394,10 +403,9 @@ "read_only": 1 }, { - "collapsible": 1, "fieldname": "section_addresses", "fieldtype": "Section Break", - "label": "Address and Contact" + "label": "Supplier Address" }, { "fieldname": "supplier_address", @@ -518,11 +526,12 @@ }, { "fieldname": "sec_warehouse", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_border": 1, + "label": "Items" }, { "depends_on": "update_stock", - "description": "Sets 'Accepted Warehouse' in each row of the items table.", "fieldname": "set_warehouse", "fieldtype": "Link", "label": "Set Accepted Warehouse", @@ -531,7 +540,6 @@ }, { "depends_on": "update_stock", - "description": "Warehouse where you are maintaining stock of rejected items", "fieldname": "rejected_warehouse", "fieldtype": "Link", "label": "Rejected Warehouse", @@ -554,6 +562,7 @@ { "fieldname": "items_section", "fieldtype": "Section Break", + "hide_border": 1, "oldfieldtype": "Section Break", "options": "fa fa-shopping-cart" }, @@ -581,6 +590,7 @@ "reqd": 1 }, { + "collapsible": 1, "fieldname": "pricing_rule_details", "fieldtype": "Section Break", "label": "Pricing Rules" @@ -593,6 +603,7 @@ "read_only": 1 }, { + "collapsible": 1, "collapsible_depends_on": "supplied_items", "fieldname": "raw_materials_supplied", "fieldtype": "Section Break", @@ -656,6 +667,7 @@ "read_only": 1 }, { + "depends_on": "total_net_weight", "fieldname": "total_net_weight", "fieldtype": "Float", "label": "Total Net Weight", @@ -665,6 +677,8 @@ { "fieldname": "taxes_section", "fieldtype": "Section Break", + "hide_border": 1, + "label": "Taxes and Charges", "oldfieldtype": "Section Break", "options": "fa fa-money" }, @@ -688,7 +702,8 @@ }, { "fieldname": "section_break_51", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_border": 1 }, { "fieldname": "taxes_and_charges", @@ -792,7 +807,6 @@ }, { "collapsible": 1, - "collapsible_depends_on": "discount_amount", "fieldname": "section_break_44", "fieldtype": "Section Break", "label": "Additional Discount" @@ -832,7 +846,8 @@ }, { "fieldname": "section_break_49", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "label": "Totals" }, { "fieldname": "base_grand_total", @@ -1003,8 +1018,6 @@ }, { "collapsible": 1, - "collapsible_depends_on": "write_off_amount", - "depends_on": "grand_total", "fieldname": "write_off", "fieldtype": "Section Break", "label": "Write Off" @@ -1081,7 +1094,6 @@ "print_hide": 1 }, { - "collapsible": 1, "collapsible_depends_on": "eval:(!doc.is_return)", "fieldname": "payment_schedule_section", "fieldtype": "Section Break", @@ -1102,8 +1114,6 @@ "print_hide": 1 }, { - "collapsible": 1, - "collapsible_depends_on": "terms", "fieldname": "terms_section_break", "fieldtype": "Section Break", "label": "Terms and Conditions", @@ -1119,13 +1129,13 @@ { "fieldname": "terms", "fieldtype": "Text Editor", - "label": "Terms and Conditions1" + "label": "Terms and Conditions" }, { "collapsible": 1, "fieldname": "printing_settings", "fieldtype": "Section Break", - "label": "Printing Settings" + "label": "Print Settings" }, { "allow_on_submit": 1, @@ -1166,15 +1176,6 @@ "print_hide": 1, "read_only": 1 }, - { - "collapsible": 1, - "fieldname": "more_info", - "fieldtype": "Section Break", - "label": "More Information", - "oldfieldtype": "Section Break", - "options": "fa fa-file-text", - "print_hide": 1 - }, { "default": "0", "fetch_from": "supplier.is_internal_supplier", @@ -1260,7 +1261,7 @@ "collapsible": 1, "fieldname": "subscription_section", "fieldtype": "Section Break", - "label": "Subscription Section", + "label": "Subscription", "print_hide": 1 }, { @@ -1339,7 +1340,7 @@ }, { "depends_on": "eval:doc.is_internal_supplier", - "description": "Unrealized Profit / Loss account for intra-company transfers", + "description": "Unrealized Profit/Loss account for intra-company transfers", "fieldname": "unrealized_profit_loss_account", "fieldtype": "Link", "label": "Unrealized Profit / Loss Account", @@ -1356,7 +1357,6 @@ }, { "depends_on": "eval:doc.update_stock && doc.is_internal_supplier", - "description": "Sets 'From Warehouse' in each row of the items table.", "fieldname": "set_from_warehouse", "fieldtype": "Link", "label": "Set From Warehouse", @@ -1422,6 +1422,7 @@ "read_only": 1 }, { + "collapsible_depends_on": "tax_withheld_vouchers", "fieldname": "tax_withheld_vouchers_section", "fieldtype": "Section Break", "label": "Tax Withheld Vouchers" @@ -1433,13 +1434,92 @@ "no_copy": 1, "options": "Tax Withheld Vouchers", "read_only": 1 + }, + { + "fieldname": "payments_tab", + "fieldtype": "Tab Break", + "label": "Payments" + }, + { + "fieldname": "address_and_contact_tab", + "fieldtype": "Tab Break", + "label": "Address & Contact" + }, + { + "fieldname": "terms_tab", + "fieldtype": "Tab Break", + "label": "Terms" + }, + { + "fieldname": "more_info_tab", + "fieldtype": "Tab Break", + "label": "More Info" + }, + { + "fieldname": "connections_tab", + "fieldtype": "Tab Break", + "label": "Connections", + "show_dashboard": 1 + }, + { + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_38", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_50", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_58", + "fieldtype": "Column Break" + }, + { + "fieldname": "company_shipping_address_section", + "fieldtype": "Section Break", + "label": "Company Shipping Address" + }, + { + "fieldname": "column_break_126", + "fieldtype": "Column Break" + }, + { + "fieldname": "company_billing_address_section", + "fieldtype": "Section Break", + "label": "Company Billing Address" + }, + { + "fieldname": "column_break_130", + "fieldtype": "Column Break" + }, + { + "collapsible": 1, + "fieldname": "status_section", + "fieldtype": "Section Break", + "label": "Status" + }, + { + "fieldname": "column_break_177", + "fieldtype": "Column Break" + }, + { + "collapsible": 1, + "fieldname": "additional_info_section", + "fieldtype": "Section Break", + "label": "Additional Info", + "oldfieldtype": "Section Break", + "options": "fa fa-file-text", + "print_hide": 1 } ], "icon": "fa fa-file-text", "idx": 204, "is_submittable": 1, "links": [], - "modified": "2022-10-07 14:19:14.214157", + "modified": "2022-10-11 13:04:44.304389", "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 2b633cb8c34..5dbe7ebc863 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -705,6 +705,10 @@ class PurchaseInvoice(BuyingController): ) ) + credit_amount = item.base_net_amount + if self.is_internal_supplier and item.valuation_rate: + credit_amount = flt(item.valuation_rate * item.stock_qty) + # Intentionally passed negative debit amount to avoid incorrect GL Entry validation gl_entries.append( self.get_gl_dict( @@ -714,7 +718,7 @@ class PurchaseInvoice(BuyingController): "cost_center": item.cost_center, "project": item.project or self.project, "remarks": self.get("remarks") or _("Accounting Entry for Stock"), - "debit": -1 * flt(item.base_net_amount, item.precision("base_net_amount")), + "debit": -1 * flt(credit_amount, item.precision("base_net_amount")), }, warehouse_account[item.from_warehouse]["account_currency"], item=item, 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 fca7e3a8873..9de90368871 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -711,6 +711,7 @@ "label": "Valuation Rate", "no_copy": 1, "options": "Company:company:default_currency", + "precision": "6", "print_hide": 1, "read_only": 1 }, @@ -870,7 +871,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2022-09-27 10:54:23.980713", + "modified": "2022-10-12 03:37:29.032732", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index 2da515737a9..97e5f4017e0 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -12,44 +12,29 @@ "customer", "customer_name", "tax_id", - "pos_profile", - "is_pos", - "is_consolidated", - "is_return", - "is_debit_note", - "update_billed_amount_in_sales_order", - "column_break1", "company", "company_tax_id", + "column_break1", "posting_date", "posting_time", "set_posting_time", "due_date", + "column_break_14", + "is_pos", + "pos_profile", + "is_consolidated", + "is_return", "return_against", + "update_billed_amount_in_sales_order", + "is_debit_note", "amended_from", "accounting_dimensions_section", - "project", - "dimension_col_break", "cost_center", - "customer_po_details", - "po_no", - "column_break_23", - "po_date", - "address_and_contact", - "customer_address", - "address_display", - "contact_person", - "contact_display", - "contact_mobile", - "contact_email", - "territory", - "col_break4", - "shipping_address_name", - "shipping_address", - "company_address", - "company_address_display", - "dispatch_address_name", - "dispatch_address", + "dimension_col_break", + "project", + "column_break_27", + "campaign", + "source", "currency_and_price_list", "currency", "conversion_rate", @@ -58,60 +43,35 @@ "price_list_currency", "plc_conversion_rate", "ignore_pricing_rule", - "sec_warehouse", - "set_warehouse", - "column_break_55", - "set_target_warehouse", "items_section", - "update_stock", "scan_barcode", + "update_stock", + "column_break_39", + "set_warehouse", + "set_target_warehouse", + "section_break_42", "items", - "pricing_rule_details", - "pricing_rules", - "packing_list", - "packed_items", - "product_bundle_help", - "time_sheet_list", - "timesheets", - "total_billing_amount", - "total_billing_hours", "section_break_30", "total_qty", + "total_net_weight", + "column_break_32", "base_total", "base_net_total", - "column_break_32", - "total_net_weight", + "column_break_52", "total", "net_total", "taxes_section", "taxes_and_charges", "column_break_38", "shipping_rule", + "column_break_55", "tax_category", "section_break_40", "taxes", - "sec_tax_breakup", - "other_charges_calculation", "section_break_43", "base_total_taxes_and_charges", "column_break_47", "total_taxes_and_charges", - "loyalty_points_redemption", - "loyalty_points", - "loyalty_amount", - "redeem_loyalty_points", - "column_break_77", - "loyalty_program", - "loyalty_redemption_account", - "loyalty_redemption_cost_center", - "section_break_49", - "apply_discount_on", - "is_cash_or_non_trade_discount", - "base_discount_amount", - "additional_discount_account", - "column_break_51", - "additional_discount_percentage", - "discount_amount", "totals", "base_grand_total", "base_rounding_adjustment", @@ -125,21 +85,28 @@ "total_advance", "outstanding_amount", "disable_rounded_total", - "column_break4", - "write_off_amount", - "base_write_off_amount", - "write_off_outstanding_amount_automatically", - "column_break_74", - "write_off_account", - "write_off_cost_center", - "advances_section", - "allocate_advances_automatically", - "get_advances", - "advances", - "payment_schedule_section", - "ignore_default_payment_terms_template", - "payment_terms_template", - "payment_schedule", + "section_break_49", + "apply_discount_on", + "base_discount_amount", + "is_cash_or_non_trade_discount", + "additional_discount_account", + "column_break_51", + "additional_discount_percentage", + "discount_amount", + "sec_tax_breakup", + "other_charges_calculation", + "pricing_rule_details", + "pricing_rules", + "packing_list", + "packed_items", + "product_bundle_help", + "time_sheet_list", + "timesheets", + "section_break_104", + "total_billing_hours", + "column_break_106", + "total_billing_amount", + "payments_tab", "payments_section", "cash_bank_account", "payments", @@ -152,47 +119,95 @@ "column_break_90", "change_amount", "account_for_change_amount", + "advances_section", + "allocate_advances_automatically", + "get_advances", + "advances", + "write_off_section", + "write_off_amount", + "base_write_off_amount", + "write_off_outstanding_amount_automatically", + "column_break_74", + "write_off_account", + "write_off_cost_center", + "loyalty_points_redemption", + "redeem_loyalty_points", + "loyalty_points", + "loyalty_amount", + "column_break_77", + "loyalty_program", + "loyalty_redemption_account", + "loyalty_redemption_cost_center", + "contact_and_address_tab", + "address_and_contact", + "customer_address", + "address_display", + "col_break4", + "contact_person", + "contact_display", + "contact_mobile", + "contact_email", + "territory", + "shipping_address_section", + "shipping_address_name", + "shipping_address", + "shipping_addr_col_break", + "dispatch_address_name", + "dispatch_address", + "company_address_section", + "company_address", + "company_addr_col_break", + "company_address_display", + "terms_tab", + "payment_schedule_section", + "ignore_default_payment_terms_template", + "payment_terms_template", + "payment_schedule", "terms_section_break", "tc_name", "terms", - "edit_printing_settings", - "letter_head", - "group_same_items", - "select_print_heading", - "column_break_84", - "language", - "more_information", - "status", - "inter_company_invoice_reference", - "represents_company", - "customer_group", - "campaign", - "col_break23", - "is_internal_customer", - "is_discounted", - "source", + "more_info_tab", + "customer_po_details", + "po_no", + "column_break_23", + "po_date", "more_info", "debit_to", "party_account_currency", "is_opening", "column_break8", "unrealized_profit_loss_account", - "remarks", + "against_income_account", "sales_team_section_break", "sales_partner", - "column_break10", "amount_eligible_for_commission", + "column_break10", "commission_rate", "total_commission", "section_break2", "sales_team", + "edit_printing_settings", + "letter_head", + "group_same_items", + "column_break_84", + "select_print_heading", + "language", "subscription_section", "from_date", - "to_date", - "column_break_140", "auto_repeat", + "column_break_140", + "to_date", "update_auto_repeat_reference", - "against_income_account" + "more_information", + "status", + "inter_company_invoice_reference", + "represents_company", + "customer_group", + "col_break23", + "is_internal_customer", + "is_discounted", + "remarks", + "connections_tab" ], "fields": [ { @@ -453,12 +468,11 @@ "label": "Customer's Purchase Order Date" }, { - "collapsible": 1, "fieldname": "address_and_contact", "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Address and Contact" + "label": "Billing Address" }, { "fieldname": "customer_address", @@ -560,7 +574,6 @@ { "fieldname": "company_address_display", "fieldtype": "Small Text", - "hidden": 1, "hide_days": 1, "hide_seconds": 1, "label": "Company Address", @@ -651,13 +664,6 @@ "label": "Ignore Pricing Rule", "print_hide": 1 }, - { - "fieldname": "sec_warehouse", - "fieldtype": "Section Break", - "hide_days": 1, - "hide_seconds": 1, - "label": "Warehouse" - }, { "depends_on": "update_stock", "fieldname": "set_warehouse", @@ -671,6 +677,7 @@ { "fieldname": "items_section", "fieldtype": "Section Break", + "hide_border": 1, "hide_days": 1, "hide_seconds": 1, "label": "Items", @@ -702,7 +709,6 @@ "fieldtype": "Table", "hide_days": 1, "hide_seconds": 1, - "label": "Items", "oldfieldname": "entries", "oldfieldtype": "Table", "options": "Sales Invoice Item", @@ -755,9 +761,10 @@ { "collapsible": 1, "collapsible_depends_on": "eval:doc.total_billing_amount > 0", - "depends_on": "eval: !doc.is_return", + "depends_on": "eval:!doc.is_return", "fieldname": "time_sheet_list", "fieldtype": "Section Break", + "hide_border": 1, "hide_days": 1, "hide_seconds": 1, "label": "Time Sheet List" @@ -845,6 +852,7 @@ "read_only": 1 }, { + "depends_on": "total_net_weight", "fieldname": "total_net_weight", "fieldtype": "Float", "hide_days": 1, @@ -856,8 +864,10 @@ { "fieldname": "taxes_section", "fieldtype": "Section Break", + "hide_border": 1, "hide_days": 1, "hide_seconds": 1, + "label": "Taxes and Charges", "oldfieldtype": "Section Break", "options": "fa fa-money" }, @@ -900,6 +910,7 @@ { "fieldname": "section_break_40", "fieldtype": "Section Break", + "hide_border": 1, "hide_days": 1, "hide_seconds": 1 }, @@ -908,7 +919,6 @@ "fieldtype": "Table", "hide_days": 1, "hide_seconds": 1, - "label": "Sales Taxes and Charges", "oldfieldname": "other_charges", "oldfieldtype": "Table", "options": "Sales Taxes and Charges" @@ -1046,7 +1056,6 @@ }, { "collapsible": 1, - "collapsible_depends_on": "discount_amount", "fieldname": "section_break_49", "fieldtype": "Section Break", "hide_days": 1, @@ -1102,6 +1111,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, + "label": "Totals", "oldfieldtype": "Section Break", "options": "fa fa-money", "print_hide": 1 @@ -1283,8 +1293,6 @@ "print_hide": 1 }, { - "collapsible": 1, - "collapsible_depends_on": "eval:(!doc.is_pos && !doc.is_return)", "fieldname": "payment_schedule_section", "fieldtype": "Section Break", "hide_days": 1, @@ -1314,7 +1322,9 @@ "print_hide": 1 }, { - "depends_on": "eval:doc.is_pos===1||(doc.advances && doc.advances.length>0)", + "collapsible": 1, + "collapsible_depends_on": "eval:!doc.is_pos", + "depends_on": "eval:doc.is_pos===1", "fieldname": "payments_section", "fieldtype": "Section Break", "hide_days": 1, @@ -1352,6 +1362,7 @@ "hide_seconds": 1 }, { + "depends_on": "eval: doc.is_pos || doc.redeem_loyalty_points", "fieldname": "base_paid_amount", "fieldtype": "Currency", "hide_days": 1, @@ -1383,10 +1394,13 @@ "read_only": 1 }, { + "collapsible": 1, + "depends_on": "is_pos", "fieldname": "section_break_88", "fieldtype": "Section Break", "hide_days": 1, - "hide_seconds": 1 + "hide_seconds": 1, + "label": "Changes" }, { "depends_on": "is_pos", @@ -1427,17 +1441,6 @@ "options": "Account", "print_hide": 1 }, - { - "collapsible": 1, - "collapsible_depends_on": "write_off_amount", - "depends_on": "grand_total", - "fieldname": "column_break4", - "fieldtype": "Section Break", - "hide_days": 1, - "hide_seconds": 1, - "label": "Write Off", - "width": "50%" - }, { "fieldname": "write_off_amount", "fieldtype": "Currency", @@ -1495,8 +1498,6 @@ "print_hide": 1 }, { - "collapsible": 1, - "collapsible_depends_on": "terms", "fieldname": "terms_section_break", "fieldtype": "Section Break", "hide_days": 1, @@ -1530,7 +1531,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Printing Settings" + "label": "Print Settings" }, { "allow_on_submit": 1, @@ -1591,7 +1592,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "More Information" + "label": "Additional Info" }, { "fieldname": "inter_company_invoice_reference", @@ -1814,7 +1815,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Subscription Section" + "label": "Subscription" }, { "allow_on_submit": 1, @@ -1935,10 +1936,6 @@ "options": "Company", "read_only": 1 }, - { - "fieldname": "column_break_55", - "fieldtype": "Column Break" - }, { "depends_on": "eval: doc.is_internal_customer && doc.update_stock", "fieldname": "set_target_warehouse", @@ -2009,6 +2006,97 @@ "fieldname": "is_cash_or_non_trade_discount", "fieldtype": "Check", "label": "Is Cash or Non Trade Discount" + }, + { + "fieldname": "contact_and_address_tab", + "fieldtype": "Tab Break", + "label": "Contact & Address" + }, + { + "fieldname": "payments_tab", + "fieldtype": "Tab Break", + "label": "Payments" + }, + { + "fieldname": "terms_tab", + "fieldtype": "Tab Break", + "label": "Terms" + }, + { + "fieldname": "more_info_tab", + "fieldtype": "Tab Break", + "label": "More Info" + }, + { + "fieldname": "connections_tab", + "fieldtype": "Tab Break", + "label": "Connections", + "show_dashboard": 1 + }, + { + "fieldname": "column_break_14", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_39", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_42", + "fieldtype": "Section Break", + "hide_border": 1, + "hide_days": 1, + "hide_seconds": 1 + }, + { + "fieldname": "column_break_55", + "fieldtype": "Column Break" + }, + { + "fieldname": "shipping_address_section", + "fieldtype": "Section Break", + "label": "Shipping Address" + }, + { + "fieldname": "company_address_section", + "fieldtype": "Section Break", + "label": "Company Address" + }, + { + "fieldname": "shipping_addr_col_break", + "fieldtype": "Column Break" + }, + { + "fieldname": "company_addr_col_break", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_27", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_52", + "fieldtype": "Column Break" + }, + { + "depends_on": "eval:(!doc.is_return && doc.total_billing_amount > 0)", + "fieldname": "section_break_104", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_106", + "fieldtype": "Column Break" + }, + { + "collapsible": 1, + "collapsible_depends_on": "write_off_amount", + "depends_on": "grand_total", + "fieldname": "write_off_section", + "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, + "label": "Write Off", + "width": "50%" } ], "icon": "fa fa-file-text", @@ -2021,7 +2109,7 @@ "link_fieldname": "consolidated_invoice" } ], - "modified": "2022-09-16 17:44:22.227332", + "modified": "2022-10-11 13:07:36.488095", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index ce44ae304b3..301d3e136ec 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -32,10 +32,20 @@ from erpnext.stock.doctype.stock_entry.test_stock_entry import ( get_qty_after_transaction, make_stock_entry, ) -from erpnext.stock.utils import get_incoming_rate +from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import ( + create_stock_reconciliation, +) +from erpnext.stock.utils import get_incoming_rate, get_stock_balance class TestSalesInvoice(unittest.TestCase): + def setUp(self): + from erpnext.stock.doctype.stock_ledger_entry.test_stock_ledger_entry import create_items + + create_items(["_Test Internal Transfer Item"], uoms=[{"uom": "Box", "conversion_factor": 10}]) + create_internal_parties() + setup_accounts() + def make(self): w = frappe.copy_doc(test_records[0]) w.is_pos = 0 @@ -1705,7 +1715,7 @@ class TestSalesInvoice(unittest.TestCase): si.save() self.assertEqual(si.get("items")[0].rate, flt((price_list_rate * 25) / 100 + price_list_rate)) - def test_outstanding_amount_after_advance_jv_cancelation(self): + def test_outstanding_amount_after_advance_jv_cancellation(self): from erpnext.accounts.doctype.journal_entry.test_journal_entry import ( test_records as jv_test_records, ) @@ -1749,7 +1759,7 @@ class TestSalesInvoice(unittest.TestCase): flt(si.rounded_total + si.total_advance, si.precision("outstanding_amount")), ) - def test_outstanding_amount_after_advance_payment_entry_cancelation(self): + def test_outstanding_amount_after_advance_payment_entry_cancellation(self): pe = frappe.get_doc( { "doctype": "Payment Entry", @@ -2367,29 +2377,6 @@ class TestSalesInvoice(unittest.TestCase): acc_settings.save() def test_inter_company_transaction(self): - from erpnext.selling.doctype.customer.test_customer import create_internal_customer - - create_internal_customer( - customer_name="_Test Internal Customer", - represents_company="_Test Company 1", - allowed_to_interact_with="Wind Power LLC", - ) - - if not frappe.db.exists("Supplier", "_Test Internal Supplier"): - supplier = frappe.get_doc( - { - "supplier_group": "_Test Supplier Group", - "supplier_name": "_Test Internal Supplier", - "doctype": "Supplier", - "is_internal_supplier": 1, - "represents_company": "Wind Power LLC", - } - ) - - supplier.append("companies", {"company": "_Test Company 1"}) - - supplier.insert() - si = create_sales_invoice( company="Wind Power LLC", customer="_Test Internal Customer", @@ -2440,38 +2427,6 @@ class TestSalesInvoice(unittest.TestCase): "Expenses Included In Valuation - _TC1", ) - if not frappe.db.exists("Customer", "_Test Internal Customer"): - customer = frappe.get_doc( - { - "customer_group": "_Test Customer Group", - "customer_name": "_Test Internal Customer", - "customer_type": "Individual", - "doctype": "Customer", - "territory": "_Test Territory", - "is_internal_customer": 1, - "represents_company": "_Test Company 1", - } - ) - - customer.append("companies", {"company": "Wind Power LLC"}) - - customer.insert() - - if not frappe.db.exists("Supplier", "_Test Internal Supplier"): - supplier = frappe.get_doc( - { - "supplier_group": "_Test Supplier Group", - "supplier_name": "_Test Internal Supplier", - "doctype": "Supplier", - "is_internal_supplier": 1, - "represents_company": "Wind Power LLC", - } - ) - - supplier.append("companies", {"company": "_Test Company 1"}) - - supplier.insert() - # begin test si = create_sales_invoice( company="Wind Power LLC", @@ -2541,34 +2496,9 @@ class TestSalesInvoice(unittest.TestCase): se.cancel() def test_internal_transfer_gl_entry(self): - ## Create internal transfer account - from erpnext.selling.doctype.customer.test_customer import create_internal_customer - - account = create_account( - account_name="Unrealized Profit", - parent_account="Current Liabilities - TCP1", - company="_Test Company with perpetual inventory", - ) - - frappe.db.set_value( - "Company", "_Test Company with perpetual inventory", "unrealized_profit_loss_account", account - ) - - customer = create_internal_customer( - "_Test Internal Customer 2", - "_Test Company with perpetual inventory", - "_Test Company with perpetual inventory", - ) - - create_internal_supplier( - "_Test Internal Supplier 2", - "_Test Company with perpetual inventory", - "_Test Company with perpetual inventory", - ) - si = create_sales_invoice( company="_Test Company with perpetual inventory", - customer=customer, + customer="_Test Internal Customer 2", debit_to="Debtors - TCP1", warehouse="Stores - TCP1", income_account="Sales - TCP1", @@ -2582,7 +2512,7 @@ class TestSalesInvoice(unittest.TestCase): si.update_stock = 1 si.items[0].target_warehouse = "Work In Progress - TCP1" - # Add stock to stores for succesful stock transfer + # Add stock to stores for successful stock transfer make_stock_entry( target="Stores - TCP1", company="_Test Company with perpetual inventory", qty=1, basic_rate=100 ) @@ -2638,6 +2568,77 @@ class TestSalesInvoice(unittest.TestCase): check_gl_entries(self, target_doc.name, pi_gl_entries, add_days(nowdate(), -1)) + def test_internal_transfer_gl_precision_issues(self): + # Make a stock queue of an item with two valuations + + # Remove all existing stock for this + if get_stock_balance("_Test Internal Transfer Item", "Stores - TCP1", "2022-04-10"): + create_stock_reconciliation( + item_code="_Test Internal Transfer Item", + warehouse="Stores - TCP1", + qty=0, + rate=0, + company="_Test Company with perpetual inventory", + expense_account="Stock Adjustment - TCP1" + if frappe.get_all("Stock Ledger Entry") + else "Temporary Opening - TCP1", + posting_date="2020-04-10", + posting_time="14:00", + ) + + make_stock_entry( + item_code="_Test Internal Transfer Item", + target="Stores - TCP1", + qty=9000000, + basic_rate=52.0, + posting_date="2020-04-10", + posting_time="14:00", + ) + make_stock_entry( + item_code="_Test Internal Transfer Item", + target="Stores - TCP1", + qty=60000000, + basic_rate=52.349777, + posting_date="2020-04-10", + posting_time="14:00", + ) + + # Make an internal transfer Sales Invoice Stock in non stock uom to check + # for rounding errors while converting to stock uom + si = create_sales_invoice( + company="_Test Company with perpetual inventory", + customer="_Test Internal Customer 2", + item_code="_Test Internal Transfer Item", + qty=5000000, + uom="Box", + debit_to="Debtors - TCP1", + warehouse="Stores - TCP1", + income_account="Sales - TCP1", + expense_account="Cost of Goods Sold - TCP1", + cost_center="Main - TCP1", + currency="INR", + do_not_save=1, + ) + + # Check GL Entries with precision + si.update_stock = 1 + si.items[0].target_warehouse = "Work In Progress - TCP1" + si.items[0].conversion_factor = 10 + si.save() + si.submit() + + # Check if adjustment entry is created + self.assertTrue( + frappe.db.exists( + "GL Entry", + { + "voucher_type": "Sales Invoice", + "voucher_no": si.name, + "remarks": "Rounding gain/loss Entry for Stock Transfer", + }, + ) + ) + def test_item_tax_net_range(self): item = create_item("T Shirt") @@ -3077,7 +3078,7 @@ class TestSalesInvoice(unittest.TestCase): [deferred_account, 2022.47, 0.0, "2019-03-15"], ] - gl_entries = gl_entries = frappe.db.sql( + gl_entries = frappe.db.sql( """select account, debit, credit, posting_date from `tabGL Entry` where voucher_type='Journal Entry' and voucher_detail_no=%s and posting_date <= %s @@ -3306,6 +3307,7 @@ def create_sales_invoice(**args): "item_name": args.item_name or "_Test Item", "description": args.description or "_Test Item", "warehouse": args.warehouse or "_Test Warehouse - _TC", + "target_warehouse": args.target_warehouse, "qty": args.qty or 1, "uom": args.uom or "Nos", "stock_uom": args.uom or "Nos", @@ -3431,6 +3433,34 @@ def get_taxes_and_charges(): ] +def create_internal_parties(): + from erpnext.selling.doctype.customer.test_customer import create_internal_customer + + create_internal_customer( + customer_name="_Test Internal Customer", + represents_company="_Test Company 1", + allowed_to_interact_with="Wind Power LLC", + ) + + create_internal_customer( + customer_name="_Test Internal Customer 2", + represents_company="_Test Company with perpetual inventory", + allowed_to_interact_with="_Test Company with perpetual inventory", + ) + + create_internal_supplier( + supplier_name="_Test Internal Supplier", + represents_company="Wind Power LLC", + allowed_to_interact_with="_Test Company 1", + ) + + create_internal_supplier( + supplier_name="_Test Internal Supplier 2", + represents_company="_Test Company with perpetual inventory", + allowed_to_interact_with="_Test Company with perpetual inventory", + ) + + def create_internal_supplier(supplier_name, represents_company, allowed_to_interact_with): if not frappe.db.exists("Supplier", supplier_name): supplier = frappe.get_doc( @@ -3453,6 +3483,19 @@ def create_internal_supplier(supplier_name, represents_company, allowed_to_inter return supplier_name +def setup_accounts(): + ## Create internal transfer account + account = create_account( + account_name="Unrealized Profit", + parent_account="Current Liabilities - TCP1", + company="_Test Company with perpetual inventory", + ) + + frappe.db.set_value( + "Company", "_Test Company with perpetual inventory", "unrealized_profit_loss_account", account + ) + + def add_taxes(doc): doc.append( "taxes", diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json index 4f97b63789a..a307a6c17cd 100644 --- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json +++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json @@ -820,6 +820,7 @@ "label": "Incoming Rate (Costing)", "no_copy": 1, "options": "Company:company:default_currency", + "precision": "6", "print_hide": 1 }, { @@ -875,7 +876,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2022-09-06 14:17:43.394309", + "modified": "2022-10-10 20:57:38.340026", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Item", diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 9ede67848dd..95ba3d86cef 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -648,6 +648,16 @@ def unlink_ref_doc_from_payment_entries(ref_doc): (now(), frappe.session.user, ref_doc.doctype, ref_doc.name), ) + ple = qb.DocType("Payment Ledger Entry") + + qb.update(ple).set(ple.against_voucher_type, ple.voucher_type).set( + ple.against_voucher_no, ple.voucher_no + ).set(ple.modified, now()).set(ple.modified_by, frappe.session.user).where( + (ple.against_voucher_type == ref_doc.doctype) + & (ple.against_voucher_no == ref_doc.name) + & (ple.delinked == 0) + ).run() + if ref_doc.doctype in ("Sales Invoice", "Purchase Invoice"): ref_doc.set("advances", []) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index fb8f25a0dfc..2193985ff23 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -12,43 +12,24 @@ "title", "naming_series", "supplier", - "get_items_from_open_material_requests", "supplier_name", + "order_confirmation_no", + "order_confirmation_date", + "get_items_from_open_material_requests", + "column_break_7", + "transaction_date", + "schedule_date", + "column_break1", + "company", "apply_tds", "tax_withholding_category", "is_subcontracted", "supplier_warehouse", - "column_break1", - "company", - "transaction_date", - "schedule_date", - "order_confirmation_no", - "order_confirmation_date", "amended_from", "accounting_dimensions_section", "cost_center", "dimension_col_break", "project", - "drop_ship", - "customer", - "customer_name", - "column_break_19", - "customer_contact_person", - "customer_contact_display", - "customer_contact_mobile", - "customer_contact_email", - "section_addresses", - "supplier_address", - "address_display", - "contact_person", - "contact_display", - "contact_mobile", - "contact_email", - "col_break_address", - "shipping_address", - "shipping_address_display", - "billing_address", - "billing_address_display", "currency_and_price_list", "currency", "conversion_rate", @@ -57,7 +38,6 @@ "price_list_currency", "plc_conversion_rate", "ignore_pricing_rule", - "section_break_45", "before_items_section", "scan_barcode", "set_from_warehouse", @@ -67,10 +47,11 @@ "items", "sb_last_purchase", "total_qty", + "total_net_weight", + "column_break_40", "base_total", "base_net_total", "column_break_26", - "total_net_weight", "total", "net_total", "section_break_48", @@ -79,14 +60,13 @@ "set_reserve_warehouse", "supplied_items", "taxes_section", + "taxes_and_charges", + "column_break_53", "tax_category", "column_break_50", "shipping_rule", "section_break_52", - "taxes_and_charges", "taxes", - "sec_tax_breakup", - "other_charges_calculation", "totals", "base_taxes_and_charges_added", "base_taxes_and_charges_deducted", @@ -95,12 +75,6 @@ "taxes_and_charges_added", "taxes_and_charges_deducted", "total_taxes_and_charges", - "discount_section", - "apply_discount_on", - "base_discount_amount", - "column_break_45", - "additional_discount_percentage", - "discount_amount", "totals_section", "base_grand_total", "base_rounding_adjustment", @@ -113,37 +87,73 @@ "disable_rounded_total", "in_words", "advance_paid", + "discount_section", + "apply_discount_on", + "base_discount_amount", + "column_break_45", + "additional_discount_percentage", + "discount_amount", + "sec_tax_breakup", + "other_charges_calculation", + "address_and_contact_tab", + "section_addresses", + "supplier_address", + "address_display", + "col_break_address", + "contact_person", + "contact_display", + "contact_mobile", + "contact_email", + "company_shipping_address_section", + "shipping_address", + "column_break_99", + "shipping_address_display", + "company_billing_address_section", + "billing_address", + "column_break_103", + "billing_address_display", + "drop_ship", + "customer", + "customer_name", + "column_break_19", + "customer_contact_person", + "customer_contact_display", + "customer_contact_mobile", + "customer_contact_email", + "terms_tab", "payment_schedule_section", "payment_terms_template", "payment_schedule", + "terms_section_break", + "tc_name", + "terms", + "more_info_tab", "tracking_section", "status", "column_break_75", "per_billed", "per_received", - "terms_section_break", - "tc_name", - "terms", "column_break5", "letter_head", - "select_print_heading", - "column_break_86", - "language", "group_same_items", + "column_break_86", + "select_print_heading", + "language", "subscription_section", "from_date", "to_date", "column_break_97", "auto_repeat", "update_auto_repeat_reference", - "more_info", + "additional_info_section", + "is_internal_supplier", + "represents_company", "ref_sq", "column_break_74", "party_account_currency", - "is_internal_supplier", - "represents_company", "inter_company_order_reference", - "is_old_subcontracting_flow" + "is_old_subcontracting_flow", + "dashboard" ], "fields": [ { @@ -267,8 +277,9 @@ "read_only": 1 }, { + "depends_on": "eval:doc.customer", "fieldname": "drop_ship", - "fieldtype": "Section Break", + "fieldtype": "Tab Break", "label": "Drop Ship" }, { @@ -299,7 +310,6 @@ { "fieldname": "customer_contact_display", "fieldtype": "Small Text", - "hidden": 1, "label": "Customer Contact", "print_hide": 1 }, @@ -319,10 +329,9 @@ "print_hide": 1 }, { - "collapsible": 1, "fieldname": "section_addresses", "fieldtype": "Section Break", - "label": "Address and Contact" + "label": "Supplier Address" }, { "fieldname": "supplier_address", @@ -445,7 +454,6 @@ "print_hide": 1 }, { - "description": "Sets 'Warehouse' in each row of the Items table.", "fieldname": "set_warehouse", "fieldtype": "Link", "label": "Set Target Warehouse", @@ -483,7 +491,6 @@ "allow_bulk_edit": 1, "fieldname": "items", "fieldtype": "Table", - "label": "Items", "oldfieldname": "po_details", "oldfieldtype": "Table", "options": "Purchase Order Item", @@ -570,6 +577,7 @@ "read_only": 1 }, { + "depends_on": "total_net_weight", "fieldname": "total_net_weight", "fieldtype": "Float", "label": "Total Net Weight", @@ -579,6 +587,8 @@ { "fieldname": "taxes_section", "fieldtype": "Section Break", + "hide_border": 1, + "label": "Taxes and Charges", "oldfieldtype": "Section Break", "options": "fa fa-money" }, @@ -633,7 +643,6 @@ { "fieldname": "totals", "fieldtype": "Section Break", - "label": "Taxes and Charges", "oldfieldtype": "Section Break", "options": "fa fa-money" }, @@ -698,7 +707,6 @@ "read_only": 1 }, { - "depends_on": "total_taxes_and_charges", "fieldname": "total_taxes_and_charges", "fieldtype": "Currency", "label": "Total Taxes and Charges", @@ -708,7 +716,6 @@ }, { "collapsible": 1, - "collapsible_depends_on": "apply_discount_on", "fieldname": "discount_section", "fieldtype": "Section Break", "label": "Additional Discount" @@ -773,7 +780,6 @@ "read_only": 1 }, { - "description": "In Words will be visible once you save the Purchase Order.", "fieldname": "base_in_words", "fieldtype": "Data", "label": "In Words (Company Currency)", @@ -851,7 +857,6 @@ "read_only": 1 }, { - "collapsible": 1, "fieldname": "payment_schedule_section", "fieldtype": "Section Break", "label": "Payment Terms" @@ -871,11 +876,10 @@ "print_hide": 1 }, { - "collapsible": 1, "collapsible_depends_on": "terms", "fieldname": "terms_section_break", "fieldtype": "Section Break", - "label": "Terms and Conditions", + "label": "Terms & Conditions", "oldfieldtype": "Section Break", "options": "fa fa-legal" }, @@ -895,13 +899,6 @@ "oldfieldname": "terms", "oldfieldtype": "Text Editor" }, - { - "collapsible": 1, - "fieldname": "more_info", - "fieldtype": "Section Break", - "label": "More Information", - "oldfieldtype": "Section Break" - }, { "default": "Draft", "fieldname": "status", @@ -1023,7 +1020,7 @@ "collapsible": 1, "fieldname": "subscription_section", "fieldtype": "Section Break", - "label": "Subscription Section" + "label": "Auto Repeat" }, { "allow_on_submit": 1, @@ -1098,7 +1095,9 @@ }, { "fieldname": "before_items_section", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_border": 1, + "label": "Items" }, { "fieldname": "items_col_break", @@ -1133,10 +1132,6 @@ "label": "Tax Withholding Category", "options": "Tax Withholding Category" }, - { - "fieldname": "section_break_45", - "fieldtype": "Section Break" - }, { "collapsible": 1, "fieldname": "accounting_dimensions_section", @@ -1173,13 +1168,71 @@ "fieldtype": "Link", "label": "Set From Warehouse", "options": "Warehouse" + }, + { + "fieldname": "terms_tab", + "fieldtype": "Tab Break", + "label": "Terms" + }, + { + "fieldname": "more_info_tab", + "fieldtype": "Tab Break", + "label": "More Info" + }, + { + "fieldname": "dashboard", + "fieldtype": "Tab Break", + "label": "Dashboard", + "show_dashboard": 1 + }, + { + "fieldname": "column_break_7", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_40", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_53", + "fieldtype": "Column Break" + }, + { + "fieldname": "address_and_contact_tab", + "fieldtype": "Tab Break", + "label": "Address & Contact" + }, + { + "fieldname": "company_shipping_address_section", + "fieldtype": "Section Break", + "label": "Company Shipping Address" + }, + { + "fieldname": "company_billing_address_section", + "fieldtype": "Section Break", + "label": "Company Billing Address" + }, + { + "collapsible": 1, + "fieldname": "additional_info_section", + "fieldtype": "Section Break", + "label": "Additional Info", + "oldfieldtype": "Section Break" + }, + { + "fieldname": "column_break_99", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_103", + "fieldtype": "Column Break" } ], "icon": "fa fa-file-text", "idx": 105, "is_submittable": 1, "links": [], - "modified": "2022-09-16 17:45:04.954055", + "modified": "2022-10-11 13:01:41.674352", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index cd58d25136a..bcedd4d0a11 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -349,7 +349,7 @@ class PurchaseOrder(BuyingController): update_linked_doc(self.doctype, self.name, self.inter_company_order_reference) def on_cancel(self): - self.ignore_linked_doctypes = "Payment Ledger Entry" + self.ignore_linked_doctypes = ("GL Entry", "Payment Ledger Entry") super(PurchaseOrder, self).on_cancel() if self.is_against_so(): diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json index 8d1939a101b..16365618ca9 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json @@ -13,19 +13,13 @@ "naming_series", "supplier", "supplier_name", - "column_break1", "company", + "column_break1", + "status", "transaction_date", "valid_till", "quotation_number", "amended_from", - "address_section", - "supplier_address", - "contact_person", - "address_display", - "contact_display", - "contact_mobile", - "contact_email", "currency_and_price_list", "currency", "conversion_rate", @@ -36,25 +30,23 @@ "ignore_pricing_rule", "items_section", "items", - "pricing_rule_details", - "pricing_rules", "section_break_22", "total_qty", + "total_net_weight", + "column_break_26", "base_total", "base_net_total", "column_break_24", "total", "net_total", - "total_net_weight", "taxes_section", + "taxes_and_charges", + "column_break_34", "tax_category", "column_break_36", "shipping_rule", "section_break_38", - "taxes_and_charges", "taxes", - "tax_breakup", - "other_charges_calculation", "totals", "base_taxes_and_charges_added", "base_taxes_and_charges_deducted", @@ -80,24 +72,36 @@ "rounded_total", "in_words", "disable_rounded_total", - "terms_section_break", + "tax_breakup", + "other_charges_calculation", + "pricing_rule_details", + "pricing_rules", + "address_and_contact_tab", + "supplier_address", + "address_display", + "column_break_72", + "contact_person", + "contact_display", + "contact_mobile", + "contact_email", + "terms_tab", "tc_name", "terms", + "more_info_tab", "printing_settings", - "select_print_heading", - "group_same_items", - "column_break_72", "letter_head", + "group_same_items", + "column_break_85", + "select_print_heading", "language", "subscription_section", "auto_repeat", "update_auto_repeat_reference", "more_info", - "status", - "column_break_57", "is_subcontracted", - "reference", - "opportunity" + "column_break_57", + "opportunity", + "connections_tab" ], "fields": [ { @@ -146,7 +150,7 @@ "fieldname": "supplier_name", "fieldtype": "Data", "in_global_search": 1, - "label": "Name", + "label": "Supplier Name", "read_only": 1 }, { @@ -193,12 +197,6 @@ "reqd": 1, "search_index": 1 }, - { - "collapsible": 1, - "fieldname": "address_section", - "fieldtype": "Section Break", - "label": "Address and Contact" - }, { "fieldname": "supplier_address", "fieldtype": "Link", @@ -309,6 +307,8 @@ { "fieldname": "items_section", "fieldtype": "Section Break", + "hide_border": 1, + "label": "Items", "oldfieldtype": "Section Break", "options": "fa fa-shopping-cart" }, @@ -316,7 +316,6 @@ "allow_bulk_edit": 1, "fieldname": "items", "fieldtype": "Table", - "label": "Items", "oldfieldname": "po_details", "oldfieldtype": "Table", "options": "Supplier Quotation Item", @@ -394,6 +393,7 @@ { "fieldname": "taxes_section", "fieldtype": "Section Break", + "hide_border": 1, "label": "Taxes and Charges", "oldfieldtype": "Section Break", "options": "fa fa-money" @@ -417,7 +417,8 @@ }, { "fieldname": "section_break_38", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_border": 1 }, { "fieldname": "taxes_and_charges", @@ -523,7 +524,6 @@ }, { "collapsible": 1, - "collapsible_depends_on": "discount_amount", "fieldname": "section_break_41", "fieldtype": "Section Break", "label": "Additional Discount" @@ -563,7 +563,8 @@ }, { "fieldname": "section_break_46", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "label": "Totals" }, { "fieldname": "base_grand_total", @@ -654,19 +655,10 @@ "fieldtype": "Check", "label": "Disable Rounded Total" }, - { - "collapsible": 1, - "collapsible_depends_on": "terms", - "fieldname": "terms_section_break", - "fieldtype": "Section Break", - "label": "Terms and Conditions", - "oldfieldtype": "Section Break", - "options": "fa fa-legal" - }, { "fieldname": "tc_name", "fieldtype": "Link", - "label": "Terms", + "label": "Terms Template", "oldfieldname": "tc_name", "oldfieldtype": "Link", "options": "Terms and Conditions", @@ -729,7 +721,7 @@ { "fieldname": "subscription_section", "fieldtype": "Section Break", - "label": "Auto Repeat Section" + "label": "Auto Repeat" }, { "fieldname": "auto_repeat", @@ -751,7 +743,7 @@ "collapsible": 1, "fieldname": "more_info", "fieldtype": "Section Break", - "label": "More Information", + "label": "Additional Info", "oldfieldtype": "Section Break", "options": "fa fa-file-text" }, @@ -779,11 +771,6 @@ "label": "Is Subcontracted", "print_hide": 1 }, - { - "fieldname": "reference", - "fieldtype": "Section Break", - "label": "Reference" - }, { "fieldname": "opportunity", "fieldtype": "Link", @@ -803,6 +790,39 @@ "fieldname": "quotation_number", "fieldtype": "Data", "label": "Quotation Number" + }, + { + "fieldname": "address_and_contact_tab", + "fieldtype": "Tab Break", + "label": "Address & Contact" + }, + { + "fieldname": "terms_tab", + "fieldtype": "Tab Break", + "label": "Terms" + }, + { + "fieldname": "more_info_tab", + "fieldtype": "Tab Break", + "label": "More Info" + }, + { + "fieldname": "connections_tab", + "fieldtype": "Tab Break", + "label": "Connections", + "show_dashboard": 1 + }, + { + "fieldname": "column_break_26", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_34", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_85", + "fieldtype": "Column Break" } ], "icon": "fa fa-shopping-cart", @@ -810,7 +830,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2022-03-14 16:13:20.284572", + "modified": "2022-09-27 18:20:09.462037", "modified_by": "Administrator", "module": "Buying", "name": "Supplier Quotation", diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 5e9c069b1d5..e8e9076975e 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -442,11 +442,17 @@ class SellingController(StockController): # For internal transfers use incoming rate as the valuation rate if self.is_internal_transfer(): if d.doctype == "Packed Item": - incoming_rate = flt(d.incoming_rate * d.conversion_factor, d.precision("incoming_rate")) + incoming_rate = flt( + flt(d.incoming_rate, d.precision("incoming_rate")) * d.conversion_factor, + d.precision("incoming_rate"), + ) if d.incoming_rate != incoming_rate: d.incoming_rate = incoming_rate else: - rate = flt(d.incoming_rate * d.conversion_factor, d.precision("rate")) + rate = flt( + flt(d.incoming_rate, d.precision("incoming_rate")) * d.conversion_factor, + d.precision("rate"), + ) if d.rate != rate: d.rate = rate frappe.msgprint( diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 9149b4d8570..98dc58677b9 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -142,13 +142,15 @@ class StockController(AccountsController): warehouse_with_no_account = [] precision = self.get_debit_field_precision() for item_row in voucher_details: - sle_list = sle_map.get(item_row.name) + sle_rounding_diff = 0.0 if sle_list: for sle in sle_list: if warehouse_account.get(sle.warehouse): # from warehouse account + sle_rounding_diff += flt(sle.stock_value_difference) + self.check_expense_account(item_row) # expense account/ target_warehouse / source_warehouse @@ -191,6 +193,46 @@ class StockController(AccountsController): elif sle.warehouse not in warehouse_with_no_account: warehouse_with_no_account.append(sle.warehouse) + if abs(sle_rounding_diff) > (1.0 / (10**precision)) and self.is_internal_transfer(): + warehouse_asset_account = "" + if self.get("is_internal_customer"): + warehouse_asset_account = warehouse_account[item_row.get("target_warehouse")]["account"] + elif self.get("is_internal_supplier"): + warehouse_asset_account = warehouse_account[item_row.get("warehouse")]["account"] + + expense_account = frappe.db.get_value("Company", self.company, "default_expense_account") + + gl_list.append( + self.get_gl_dict( + { + "account": expense_account, + "against": warehouse_asset_account, + "cost_center": item_row.cost_center, + "project": item_row.project or self.get("project"), + "remarks": _("Rounding gain/loss Entry for Stock Transfer"), + "debit": sle_rounding_diff, + "is_opening": item_row.get("is_opening") or self.get("is_opening") or "No", + }, + warehouse_account[sle.warehouse]["account_currency"], + item=item_row, + ) + ) + + gl_list.append( + self.get_gl_dict( + { + "account": warehouse_asset_account, + "against": expense_account, + "cost_center": item_row.cost_center, + "remarks": _("Rounding gain/loss Entry for Stock Transfer"), + "credit": sle_rounding_diff, + "project": item_row.get("project") or self.get("project"), + "is_opening": item_row.get("is_opening") or self.get("is_opening") or "No", + }, + item=item_row, + ) + ) + if warehouse_with_no_account: for wh in warehouse_with_no_account: if frappe.db.get_value("Warehouse", wh, "company"): diff --git a/erpnext/e_commerce/doctype/website_item/website_item.json b/erpnext/e_commerce/doctype/website_item/website_item.json index c5775ee9075..6556eabf4ab 100644 --- a/erpnext/e_commerce/doctype/website_item/website_item.json +++ b/erpnext/e_commerce/doctype/website_item/website_item.json @@ -188,7 +188,8 @@ "in_list_view": 1, "label": "Item Group", "options": "Item Group", - "read_only": 1 + "read_only": 1, + "search_index": 1 }, { "default": "1", @@ -234,7 +235,8 @@ "fieldname": "brand", "fieldtype": "Link", "label": "Brand", - "options": "Brand" + "options": "Brand", + "search_index": 1 }, { "collapsible": 1, @@ -346,7 +348,7 @@ "index_web_pages_for_search": 1, "links": [], "make_attachments_public": 1, - "modified": "2022-09-13 04:05:11.614087", + "modified": "2022-09-30 04:01:52.090732", "modified_by": "Administrator", "module": "E-commerce", "name": "Website Item", diff --git a/erpnext/e_commerce/doctype/website_item/website_item.py b/erpnext/e_commerce/doctype/website_item/website_item.py index c0f8c79283d..3e5d5f768fa 100644 --- a/erpnext/e_commerce/doctype/website_item/website_item.py +++ b/erpnext/e_commerce/doctype/website_item/website_item.py @@ -403,9 +403,6 @@ def on_doctype_update(): # since route is a Text column, it needs a length for indexing frappe.db.add_index("Website Item", ["route(500)"]) - frappe.db.add_index("Website Item", ["item_group"]) - frappe.db.add_index("Website Item", ["brand"]) - def check_if_user_is_customer(user=None): from frappe.contacts.doctype.contact.contact import get_contact_name diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index a53c42c5ec8..804f03dc519 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -17,6 +17,7 @@ from erpnext.manufacturing.doctype.work_order.work_order import ( close_work_order, make_job_card, make_stock_entry, + make_stock_return_entry, stop_unstop, ) from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order @@ -1408,6 +1409,77 @@ class TestWorkOrder(FrappeTestCase): ) self.assertEqual(manufacture_ste_doc2.items[1].qty, 1) + def test_non_consumed_material_return_against_work_order(self): + frappe.db.set_value( + "Manufacturing Settings", + None, + "backflush_raw_materials_based_on", + "Material Transferred for Manufacture", + ) + + item = make_item( + "Test FG Item To Test Return Case", + { + "is_stock_item": 1, + }, + ) + + item_code = item.name + bom_doc = make_bom( + item=item_code, + source_warehouse="Stores - _TC", + raw_materials=["Test Batch MCC Keyboard", "Test Serial No BTT Headphone"], + ) + + # Create a work order + wo_doc = make_wo_order_test_record(production_item=item_code, qty=5) + wo_doc.save() + + self.assertEqual(wo_doc.bom_no, bom_doc.name) + + # Transfer material for manufacture + ste_doc = frappe.get_doc(make_stock_entry(wo_doc.name, "Material Transfer for Manufacture", 5)) + for row in ste_doc.items: + row.qty += 2 + row.transfer_qty += 2 + nste_doc = test_stock_entry.make_stock_entry( + item_code=row.item_code, target="Stores - _TC", qty=row.qty, basic_rate=100 + ) + + row.batch_no = nste_doc.items[0].batch_no + row.serial_no = nste_doc.items[0].serial_no + + ste_doc.save() + ste_doc.submit() + ste_doc.load_from_db() + + # Create a stock entry to manufacture the item + ste_doc = frappe.get_doc(make_stock_entry(wo_doc.name, "Manufacture", 5)) + for row in ste_doc.items: + if row.s_warehouse and not row.t_warehouse: + row.qty -= 2 + row.transfer_qty -= 2 + + if row.serial_no: + serial_nos = get_serial_nos(row.serial_no) + row.serial_no = "\n".join(serial_nos[0:5]) + + ste_doc.save() + ste_doc.submit() + + wo_doc.load_from_db() + for row in wo_doc.required_items: + self.assertEqual(row.transferred_qty, 7) + self.assertEqual(row.consumed_qty, 5) + + self.assertEqual(wo_doc.status, "Completed") + return_ste_doc = make_stock_return_entry(wo_doc.name) + return_ste_doc.save() + + self.assertTrue(return_ste_doc.is_return) + for row in return_ste_doc.items: + self.assertEqual(row.qty, 2) + def prepare_data_for_backflush_based_on_materials_transferred(): batch_item_doc = make_item( diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index f3640b93b22..4aab3fa3734 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -180,6 +180,37 @@ frappe.ui.form.on("Work Order", { frm.trigger("make_bom"); }); } + + frm.trigger("add_custom_button_to_return_components"); + }, + + 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) + }); + + if (non_consumed_items && non_consumed_items.length) { + frm.add_custom_button(__("Return Components"), function() { + frm.trigger("create_stock_return_entry"); + }).addClass("btn-primary"); + } + } + }, + + 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, + }, + 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) { @@ -517,7 +548,8 @@ frappe.ui.form.on("Work Order Operation", { erpnext.work_order = { set_custom_buttons: function(frm) { var doc = frm.doc; - if (doc.docstatus === 1 && doc.status != "Closed") { + + if (doc.status !== "Closed") { frm.add_custom_button(__('Close'), function() { frappe.confirm(__("Once the Work Order is Closed. It can't be resumed."), () => { @@ -525,7 +557,9 @@ erpnext.work_order = { } ); }, __("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"); diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 7b8625372a8..1e6d982fc91 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -20,6 +20,7 @@ from frappe.utils import ( nowdate, time_diff_in_hours, ) +from pypika import functions as fn from erpnext.manufacturing.doctype.bom.bom import ( get_bom_item_rate, @@ -859,6 +860,7 @@ class WorkOrder(Document): if self.docstatus == 1: # calculate transferred qty based on submitted stock entries self.update_transferred_qty_for_required_items() + self.update_returned_qty() # update in bin self.update_reserved_qty_for_production() @@ -930,23 +932,62 @@ class WorkOrder(Document): self.set_available_qty() def update_transferred_qty_for_required_items(self): - """update transferred qty from submitted stock entries for that item against - the work order""" + ste = frappe.qb.DocType("Stock Entry") + ste_child = frappe.qb.DocType("Stock Entry Detail") - for d in self.required_items: - transferred_qty = frappe.db.sql( - """select sum(qty) - from `tabStock Entry` entry, `tabStock Entry Detail` detail - where - entry.work_order = %(name)s - and entry.purpose = 'Material Transfer for Manufacture' - and entry.docstatus = 1 - and detail.parent = entry.name - and (detail.item_code = %(item)s or detail.original_item = %(item)s)""", - {"name": self.name, "item": d.item_code}, - )[0][0] + query = ( + frappe.qb.from_(ste) + .inner_join(ste_child) + .on((ste_child.parent == ste.name)) + .select( + ste_child.item_code, + ste_child.original_item, + fn.Sum(ste_child.qty).as_("qty"), + ) + .where( + (ste.docstatus == 1) + & (ste.work_order == self.name) + & (ste.purpose == "Material Transfer for Manufacture") + & (ste.is_return == 0) + ) + .groupby(ste_child.item_code) + ) - d.db_set("transferred_qty", flt(transferred_qty), update_modified=False) + data = query.run(as_dict=1) or [] + transferred_items = frappe._dict({d.original_item or d.item_code: d.qty for d in data}) + + for row in self.required_items: + row.db_set( + "transferred_qty", (transferred_items.get(row.item_code) or 0.0), update_modified=False + ) + + def update_returned_qty(self): + ste = frappe.qb.DocType("Stock Entry") + ste_child = frappe.qb.DocType("Stock Entry Detail") + + query = ( + frappe.qb.from_(ste) + .inner_join(ste_child) + .on((ste_child.parent == ste.name)) + .select( + ste_child.item_code, + ste_child.original_item, + fn.Sum(ste_child.qty).as_("qty"), + ) + .where( + (ste.docstatus == 1) + & (ste.work_order == self.name) + & (ste.purpose == "Material Transfer for Manufacture") + & (ste.is_return == 1) + ) + .groupby(ste_child.item_code) + ) + + data = query.run(as_dict=1) or [] + returned_dict = frappe._dict({d.original_item or d.item_code: d.qty for d in data}) + + for row in self.required_items: + row.db_set("returned_qty", (returned_dict.get(row.item_code) or 0.0), update_modified=False) def update_consumed_qty_for_required_items(self): """ @@ -1470,3 +1511,25 @@ def get_reserved_qty_for_production(item_code: str, warehouse: str) -> float: ) ) ).run()[0][0] or 0.0 + + +@frappe.whitelist() +def make_stock_return_entry(work_order): + from erpnext.stock.doctype.stock_entry.stock_entry import get_available_materials + + non_consumed_items = get_available_materials(work_order) + if not non_consumed_items: + return + + wo_doc = frappe.get_cached_doc("Work Order", work_order) + + stock_entry = frappe.new_doc("Stock Entry") + stock_entry.from_bom = 1 + stock_entry.is_return = 1 + stock_entry.work_order = work_order + stock_entry.purpose = "Material Transfer for Manufacture" + stock_entry.bom_no = wo_doc.bom_no + stock_entry.add_transfered_raw_materials_in_items() + stock_entry.set_stock_entry_type() + + return stock_entry 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 3acf5727d19..f354d45381c 100644 --- a/erpnext/manufacturing/doctype/work_order_item/work_order_item.json +++ b/erpnext/manufacturing/doctype/work_order_item/work_order_item.json @@ -20,6 +20,7 @@ "column_break_11", "transferred_qty", "consumed_qty", + "returned_qty", "available_qty_at_source_warehouse", "available_qty_at_wip_warehouse" ], @@ -97,6 +98,7 @@ "fieldtype": "Column Break" }, { + "columns": 1, "depends_on": "eval:!parent.skip_transfer", "fieldname": "consumed_qty", "fieldtype": "Float", @@ -127,11 +129,19 @@ "fieldtype": "Currency", "label": "Amount", "read_only": 1 + }, + { + "columns": 1, + "fieldname": "returned_qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Returned Qty ", + "read_only": 1 } ], "istable": 1, "links": [], - "modified": "2020-04-13 18:46:32.966416", + "modified": "2022-09-28 10:50:43.512562", "modified_by": "Administrator", "module": "Manufacturing", "name": "Work Order Item", @@ -140,5 +150,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/report/work_order_consumed_materials/work_order_consumed_materials.js b/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.js index b2428e85b74..2fb4ec67913 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 @@ -50,7 +50,7 @@ frappe.query_reports["Work Order Consumed Materials"] = { label: __("Status"), fieldname: "status", fieldtype: "Select", - options: ["In Process", "Completed", "Stopped"] + options: ["", "In Process", "Completed", "Stopped"] }, { label: __("Excess Materials Consumed"), diff --git a/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.py b/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.py index 8158bc9a02b..14e97d3dd78 100644 --- a/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.py +++ b/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.py @@ -1,6 +1,8 @@ # Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt +from collections import defaultdict + import frappe from frappe import _ @@ -18,7 +20,11 @@ def get_data(report_filters): filters = get_filter_condition(report_filters) wo_items = {} - for d in frappe.get_all("Work Order", filters=filters, fields=fields): + + work_orders = frappe.get_all("Work Order", filters=filters, fields=fields) + returned_materials = get_returned_materials(work_orders) + + for d in work_orders: d.extra_consumed_qty = 0.0 if d.consumed_qty and d.consumed_qty > d.required_qty: d.extra_consumed_qty = d.consumed_qty - d.required_qty @@ -39,6 +45,28 @@ def get_data(report_filters): return data +def get_returned_materials(work_orders): + raw_materials_qty = defaultdict(float) + + raw_materials = frappe.get_all( + "Stock Entry", + fields=["`tabStock Entry Detail`.`item_code`", "`tabStock Entry Detail`.`qty`"], + filters=[ + ["Stock Entry", "is_return", "=", 1], + ["Stock Entry Detail", "docstatus", "=", 1], + ["Stock Entry", "work_order", "in", [d.name for d in work_orders]], + ], + ) + + for d in raw_materials: + raw_materials_qty[d.item_code] += d.qty + + for row in work_orders: + row.returned_qty = 0.0 + if raw_materials_qty.get(row.raw_material_item_code): + row.returned_qty = raw_materials_qty.get(row.raw_material_item_code) + + def get_fields(): return [ "`tabWork Order Item`.`parent`", @@ -65,7 +93,7 @@ def get_filter_condition(report_filters): for field in ["name", "production_item", "company", "status"]: value = report_filters.get(field) if value: - key = f"`{field}`" + key = f"{field}" filters.update({key: value}) return filters @@ -112,4 +140,10 @@ def get_columns(): "fieldtype": "Float", "width": 100, }, + { + "label": _("Returned Qty"), + "fieldname": "returned_qty", + "fieldtype": "Float", + "width": 100, + }, ] diff --git a/erpnext/patches/v14_0/migrate_remarks_from_gl_to_payment_ledger.py b/erpnext/patches/v14_0/migrate_remarks_from_gl_to_payment_ledger.py index 062d24b78bd..fd2a2a39cc6 100644 --- a/erpnext/patches/v14_0/migrate_remarks_from_gl_to_payment_ledger.py +++ b/erpnext/patches/v14_0/migrate_remarks_from_gl_to_payment_ledger.py @@ -3,6 +3,29 @@ from frappe import qb from frappe.utils import create_batch +def remove_duplicate_entries(pl_entries): + unique_vouchers = set() + for x in pl_entries: + unique_vouchers.add( + (x.company, x.account, x.party_type, x.party, x.voucher_type, x.voucher_no, x.gle_remarks) + ) + + entries = [] + for x in unique_vouchers: + entries.append( + frappe._dict( + company=x[0], + account=x[1], + party_type=x[2], + party=x[3], + voucher_type=x[4], + voucher_no=x[5], + gle_remarks=x[6], + ) + ) + return entries + + def execute(): if frappe.reload_doc("accounts", "doctype", "payment_ledger_entry"): @@ -34,6 +57,8 @@ def execute(): .run(as_dict=True) ) + pl_entries = remove_duplicate_entries(pl_entries) + if pl_entries: # split into multiple batches, update and commit for each batch batch_size = 1000 diff --git a/erpnext/regional/united_arab_emirates/setup.py b/erpnext/regional/united_arab_emirates/setup.py index be621bcdd1d..36a079546e5 100644 --- a/erpnext/regional/united_arab_emirates/setup.py +++ b/erpnext/regional/united_arab_emirates/setup.py @@ -37,7 +37,7 @@ def make_custom_fields(): fieldname="vat_section", label="VAT Details", fieldtype="Section Break", - insert_after="group_same_items", + insert_after="language", print_hide=1, collapsible=1, ), diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json index c58a46ba513..fa64b1625bb 100644 --- a/erpnext/selling/doctype/quotation/quotation.json +++ b/erpnext/selling/doctype/quotation/quotation.json @@ -15,26 +15,13 @@ "quotation_to", "party_name", "customer_name", - "column_break1", - "amended_from", - "company", + "column_break_7", "transaction_date", "valid_till", + "column_break1", "order_type", - "contact_section", - "customer_address", - "address_display", - "contact_person", - "contact_display", - "contact_mobile", - "contact_email", - "col_break98", - "shipping_address_name", - "shipping_address", - "company_address", - "company_address_display", - "customer_group", - "territory", + "company", + "amended_from", "currency_and_price_list", "currency", "conversion_rate", @@ -43,79 +30,105 @@ "price_list_currency", "plc_conversion_rate", "ignore_pricing_rule", - "section_break_33", - "scan_barcode", "items_section", + "scan_barcode", "items", - "bundle_items_section", - "packed_items", - "pricing_rule_details", - "pricing_rules", "sec_break23", "total_qty", + "total_net_weight", + "column_break_28", "base_total", "base_net_total", - "column_break_28", + "column_break_31", "total", "net_total", - "total_net_weight", "taxes_section", + "taxes_and_charges", + "column_break_36", "tax_category", "column_break_34", "shipping_rule", "section_break_36", - "taxes_and_charges", "taxes", - "sec_tax_breakup", - "other_charges_calculation", "section_break_39", "base_total_taxes_and_charges", "column_break_42", "total_taxes_and_charges", - "section_break_44", - "coupon_code", - "referral_sales_partner", - "apply_discount_on", - "base_discount_amount", - "column_break_46", - "additional_discount_percentage", - "discount_amount", "totals", "base_grand_total", "base_rounding_adjustment", - "base_in_words", "base_rounded_total", + "base_in_words", "column_break3", "grand_total", "rounding_adjustment", "rounded_total", "in_words", + "section_break_44", + "apply_discount_on", + "base_discount_amount", + "coupon_code", + "column_break_46", + "additional_discount_percentage", + "discount_amount", + "referral_sales_partner", + "sec_tax_breakup", + "other_charges_calculation", + "bundle_items_section", + "packed_items", + "pricing_rule_details", + "pricing_rules", + "address_and_contact_tab", + "billing_address_section", + "customer_address", + "address_display", + "col_break98", + "contact_person", + "contact_display", + "contact_mobile", + "contact_email", + "shipping_address_section", + "shipping_address_name", + "column_break_81", + "shipping_address", + "company_address_section", + "company_address", + "column_break_87", + "company_address_display", + "terms_tab", "payment_schedule_section", "payment_terms_template", "payment_schedule", "terms_section_break", "tc_name", "terms", + "more_info_tab", + "subscription_section", + "auto_repeat", + "update_auto_repeat_reference", "print_settings", "letter_head", "group_same_items", "column_break_73", "select_print_heading", "language", - "subscription_section", - "auto_repeat", - "update_auto_repeat_reference", - "more_info", + "lost_reasons_section", + "lost_reasons", + "competitors", + "column_break_117", + "order_lost_reason", + "additional_info_section", + "status", + "customer_group", + "territory", + "column_break_108", "campaign", "source", - "order_lost_reason", "column_break4", - "status", - "enq_det", - "supplier_quotation", "opportunity", - "lost_reasons", - "competitors" + "supplier_quotation", + "enq_det", + "connections_tab" ], "fields": [ { @@ -241,14 +254,6 @@ "print_hide": 1, "reqd": 1 }, - { - "collapsible": 1, - "depends_on": "party_name", - "fieldname": "contact_section", - "fieldtype": "Section Break", - "label": "Address and Contact", - "options": "fa fa-bullhorn" - }, { "fieldname": "customer_address", "fieldtype": "Link", @@ -296,7 +301,7 @@ "read_only": 1 }, { - "depends_on": "eval:doc.quotation_to=='Customer' && doc.party_name", + "depends_on": "eval:(doc.quotation_to=='Customer' && doc.party_name)", "fieldname": "col_break98", "fieldtype": "Column Break", "width": "50%" @@ -316,7 +321,7 @@ "read_only": 1 }, { - "depends_on": "eval:doc.quotation_to=='Customer' && doc.party_name", + "depends_on": "eval:(doc.quotation_to=='Customer' && doc.party_name)", "fieldname": "customer_group", "fieldtype": "Link", "hidden": 1, @@ -408,6 +413,8 @@ { "fieldname": "items_section", "fieldtype": "Section Break", + "hide_border": 1, + "label": "Items", "oldfieldtype": "Section Break", "options": "fa fa-shopping-cart" }, @@ -415,7 +422,6 @@ "allow_bulk_edit": 1, "fieldname": "items", "fieldtype": "Table", - "label": "Items", "oldfieldname": "quotation_details", "oldfieldtype": "Table", "options": "Quotation Item", @@ -423,6 +429,7 @@ "width": "40px" }, { + "collapsible": 1, "fieldname": "pricing_rule_details", "fieldtype": "Section Break", "label": "Pricing Rules" @@ -483,6 +490,7 @@ "read_only": 1 }, { + "depends_on": "total_net_weight", "fieldname": "total_net_weight", "fieldtype": "Float", "label": "Total Net Weight", @@ -492,6 +500,7 @@ { "fieldname": "taxes_section", "fieldtype": "Section Break", + "hide_border": 1, "label": "Taxes and Charges", "oldfieldtype": "Section Break", "options": "fa fa-money" @@ -517,7 +526,8 @@ }, { "fieldname": "section_break_36", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_border": 1 }, { "fieldname": "taxes_and_charges", @@ -579,10 +589,9 @@ }, { "collapsible": 1, - "collapsible_depends_on": "discount_amount", "fieldname": "section_break_44", "fieldtype": "Section Break", - "label": "Additional Discount and Coupon Code" + "label": "Additional Discount" }, { "fieldname": "coupon_code", @@ -632,6 +641,7 @@ { "fieldname": "totals", "fieldtype": "Section Break", + "label": "Totals", "oldfieldtype": "Section Break", "options": "fa fa-money", "print_hide": 1 @@ -657,7 +667,6 @@ "read_only": 1 }, { - "description": "In Words will be visible once you save the Quotation.", "fieldname": "base_in_words", "fieldtype": "Data", "label": "In Words (Company Currency)", @@ -665,8 +674,7 @@ "oldfieldname": "in_words", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1, - "width": "200px" + "read_only": 1 }, { "fieldname": "base_rounded_total", @@ -749,7 +757,6 @@ "print_hide": 1 }, { - "collapsible": 1, "collapsible_depends_on": "terms", "fieldname": "terms_section_break", "fieldtype": "Section Break", @@ -824,7 +831,7 @@ { "fieldname": "subscription_section", "fieldtype": "Section Break", - "label": "Auto Repeat Section" + "label": "Auto Repeat" }, { "fieldname": "auto_repeat", @@ -837,20 +844,11 @@ }, { "allow_on_submit": 1, - "depends_on": "eval: doc.auto_repeat", + "depends_on": "eval:doc.auto_repeat", "fieldname": "update_auto_repeat_reference", "fieldtype": "Button", "label": "Update Auto Repeat Reference" }, - { - "collapsible": 1, - "fieldname": "more_info", - "fieldtype": "Section Break", - "label": "More Information", - "oldfieldtype": "Section Break", - "options": "fa fa-file-text", - "print_hide": 1 - }, { "fieldname": "campaign", "fieldtype": "Link", @@ -871,7 +869,7 @@ }, { "allow_on_submit": 1, - "depends_on": "eval:doc.status===\"Lost\"", + "depends_on": "eval:doc.status=='Lost'", "fieldname": "order_lost_reason", "fieldtype": "Small Text", "label": "Detailed Reason", @@ -934,7 +932,6 @@ "read_only": 1 }, { - "depends_on": "packed_items", "fieldname": "packed_items", "fieldtype": "Table", "label": "Bundle Items", @@ -943,7 +940,6 @@ }, { "collapsible": 1, - "collapsible_depends_on": "packed_items", "depends_on": "packed_items", "fieldname": "bundle_items_section", "fieldtype": "Section Break", @@ -970,22 +966,99 @@ "label": "Company Address", "read_only": 1 }, - { - "fieldname": "section_break_33", - "fieldtype": "Section Break" - }, { "fieldname": "scan_barcode", "fieldtype": "Data", "label": "Scan Barcode", "options": "Barcode" + }, + { + "fieldname": "address_and_contact_tab", + "fieldtype": "Tab Break", + "label": "Address & Contact" + }, + { + "fieldname": "terms_tab", + "fieldtype": "Tab Break", + "label": "Terms" + }, + { + "fieldname": "more_info_tab", + "fieldtype": "Tab Break", + "label": "More Info" + }, + { + "fieldname": "connections_tab", + "fieldtype": "Tab Break", + "label": "Connections", + "show_dashboard": 1 + }, + { + "fieldname": "column_break_7", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_31", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_36", + "fieldtype": "Column Break" + }, + { + "fieldname": "billing_address_section", + "fieldtype": "Section Break", + "label": "Billing Address", + "options": "fa fa-bullhorn" + }, + { + "fieldname": "shipping_address_section", + "fieldtype": "Section Break", + "label": "Shipping Address" + }, + { + "fieldname": "column_break_81", + "fieldtype": "Column Break" + }, + { + "fieldname": "company_address_section", + "fieldtype": "Section Break", + "label": "Company Address" + }, + { + "fieldname": "column_break_87", + "fieldtype": "Column Break" + }, + { + "collapsible": 1, + "depends_on": "eval:(doc.lost_reasons || doc.order_lost_reason)", + "fieldname": "lost_reasons_section", + "fieldtype": "Section Break", + "label": "Lost Reasons" + }, + { + "fieldname": "column_break_117", + "fieldtype": "Column Break" + }, + { + "collapsible": 1, + "fieldname": "additional_info_section", + "fieldtype": "Section Break", + "label": "Additional Info", + "oldfieldtype": "Section Break", + "options": "fa fa-file-text", + "print_hide": 1 + }, + { + "fieldname": "column_break_108", + "fieldtype": "Column Break" } ], "icon": "fa fa-shopping-cart", "idx": 82, "is_submittable": 1, "links": [], - "modified": "2022-09-16 17:44:43.221804", + "modified": "2022-10-11 13:06:33.479650", "modified_by": "Administrator", "module": "Selling", "name": "Quotation", @@ -1083,4 +1156,4 @@ "states": [], "timeline_field": "party_name", "title_field": "title" -} +} \ No newline at end of file diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index ff269d0e684..e6ff39d8d4a 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -15,37 +15,25 @@ "naming_series", "customer", "customer_name", + "tax_id", "order_type", - "skip_delivery_note", - "column_break1", - "amended_from", - "company", + "column_break_7", "transaction_date", "delivery_date", + "column_break1", "po_no", "po_date", - "tax_id", + "company", + "skip_delivery_note", + "amended_from", "accounting_dimensions_section", "cost_center", "dimension_col_break", "project", - "contact_info", - "customer_address", - "address_display", - "contact_person", - "contact_display", - "contact_phone", - "contact_mobile", - "contact_email", - "company_address", - "company_address_display", - "col_break46", - "shipping_address_name", - "shipping_address", - "dispatch_address_name", - "dispatch_address", - "customer_group", - "territory", + "column_break_77", + "source", + "campaign", + "custom_dimensions_section", "currency_and_price_list", "currency", "conversion_rate", @@ -55,46 +43,32 @@ "plc_conversion_rate", "ignore_pricing_rule", "sec_warehouse", + "scan_barcode", + "column_break_28", "set_warehouse", "items_section", - "scan_barcode", "items", - "packing_list", - "packed_items", - "pricing_rule_details", - "pricing_rules", "section_break_31", - "column_break_33a", "total_qty", + "total_net_weight", + "column_break_33", "base_total", "base_net_total", - "column_break_33", - "total_net_weight", + "column_break_33a", "total", "net_total", "taxes_section", - "tax_category", + "taxes_and_charges", "column_break_38", + "tax_category", + "column_break_49", "shipping_rule", "section_break_40", - "taxes_and_charges", "taxes", - "sec_tax_breakup", - "other_charges_calculation", "section_break_43", "base_total_taxes_and_charges", "column_break_46", "total_taxes_and_charges", - "loyalty_points_redemption", - "loyalty_points", - "loyalty_amount", - "section_break_48", - "coupon_code", - "apply_discount_on", - "base_discount_amount", - "column_break_50", - "additional_discount_percentage", - "discount_amount", "totals", "base_grand_total", "base_rounding_adjustment", @@ -107,26 +81,49 @@ "in_words", "advance_paid", "disable_rounded_total", + "section_break_48", + "apply_discount_on", + "base_discount_amount", + "coupon_code", + "column_break_50", + "additional_discount_percentage", + "discount_amount", + "sec_tax_breakup", + "other_charges_calculation", + "packing_list", + "packed_items", + "pricing_rule_details", + "pricing_rules", + "contact_info", + "billing_address_column", + "customer_address", + "address_display", + "customer_group", + "territory", + "column_break_84", + "contact_person", + "contact_display", + "contact_phone", + "contact_mobile", + "contact_email", + "shipping_address_column", + "shipping_address_name", + "shipping_address", + "column_break_93", + "dispatch_address_name", + "dispatch_address", + "col_break46", + "company_address", + "column_break_92", + "company_address_display", "payment_schedule_section", + "payment_terms_section", "payment_terms_template", "payment_schedule", "terms_section_break", "tc_name", "terms", "more_info", - "is_internal_customer", - "represents_company", - "inter_company_order_reference", - "party_account_currency", - "column_break_77", - "source", - "campaign", - "printing_details", - "language", - "letter_head", - "column_break4", - "select_print_heading", - "group_same_items", "section_break_78", "status", "delivery_status", @@ -143,12 +140,29 @@ "total_commission", "section_break1", "sales_team", + "loyalty_points_redemption", + "loyalty_points", + "column_break_116", + "loyalty_amount", "subscription_section", "from_date", "to_date", "column_break_108", "auto_repeat", - "update_auto_repeat_reference" + "update_auto_repeat_reference", + "printing_details", + "letter_head", + "group_same_items", + "column_break4", + "select_print_heading", + "language", + "additional_info_section", + "is_internal_customer", + "represents_company", + "column_break_152", + "inter_company_order_reference", + "party_account_currency", + "connections_tab" ], "fields": [ { @@ -334,10 +348,10 @@ "collapsible": 1, "depends_on": "customer", "fieldname": "contact_info", - "fieldtype": "Section Break", + "fieldtype": "Tab Break", "hide_days": 1, "hide_seconds": 1, - "label": "Address and Contact", + "label": "Address & Contact", "options": "fa fa-bullhorn" }, { @@ -414,9 +428,10 @@ }, { "fieldname": "col_break46", - "fieldtype": "Column Break", + "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, + "label": "Company Address", "width": "50%" }, { @@ -550,8 +565,10 @@ { "fieldname": "sec_warehouse", "fieldtype": "Section Break", + "hide_border": 1, "hide_days": 1, - "hide_seconds": 1 + "hide_seconds": 1, + "label": "Items" }, { "fieldname": "set_warehouse", @@ -565,6 +582,7 @@ { "fieldname": "items_section", "fieldtype": "Section Break", + "hide_border": 1, "hide_days": 1, "hide_seconds": 1, "oldfieldtype": "Section Break", @@ -675,6 +693,7 @@ "read_only": 1 }, { + "depends_on": "total_net_weight", "fieldname": "total_net_weight", "fieldtype": "Float", "hide_days": 1, @@ -688,7 +707,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Taxes and Charges", + "label": "Taxes", "oldfieldtype": "Section Break", "options": "fa fa-money" }, @@ -720,6 +739,7 @@ { "fieldname": "section_break_40", "fieldtype": "Section Break", + "hide_border": 1, "hide_days": 1, "hide_seconds": 1 }, @@ -804,7 +824,7 @@ "hidden": 1, "hide_days": 1, "hide_seconds": 1, - "label": "Loyalty Points Redemption", + "label": "Loyalty Points", "print_hide": 1 }, { @@ -828,12 +848,11 @@ }, { "collapsible": 1, - "collapsible_depends_on": "discount_amount", "fieldname": "section_break_48", "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Additional Discount and Coupon Code" + "label": "Additional Discount" }, { "fieldname": "coupon_code", @@ -891,6 +910,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, + "label": "Totals", "oldfieldtype": "Section Break", "options": "fa fa-money", "print_hide": 1 @@ -1045,10 +1065,10 @@ }, { "fieldname": "payment_schedule_section", - "fieldtype": "Section Break", + "fieldtype": "Tab Break", "hide_days": 1, "hide_seconds": 1, - "label": "Payment Terms" + "label": "Terms" }, { "fieldname": "payment_terms_template", @@ -1070,13 +1090,12 @@ "print_hide": 1 }, { - "collapsible": 1, "collapsible_depends_on": "terms", "fieldname": "terms_section_break", "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Terms and Conditions", + "label": "Terms & Conditions", "oldfieldtype": "Section Break", "options": "fa fa-legal" }, @@ -1104,10 +1123,10 @@ "collapsible": 1, "collapsible_depends_on": "project", "fieldname": "more_info", - "fieldtype": "Section Break", + "fieldtype": "Tab Break", "hide_days": 1, "hide_seconds": 1, - "label": "More Information", + "label": "More Info", "oldfieldtype": "Section Break", "options": "fa fa-file-text", "print_hide": 1 @@ -1122,7 +1141,6 @@ "read_only": 1 }, { - "description": "Track this Sales Order against any Project", "fieldname": "project", "fieldtype": "Link", "hide_days": 1, @@ -1240,7 +1258,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Billing and Delivery Status", + "label": "Status", "oldfieldtype": "Column Break", "print_hide": 1, "width": "50%" @@ -1410,7 +1428,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Auto Repeat Section", + "label": "Auto Repeat", "no_copy": 1, "print_hide": 1, "read_only": 1 @@ -1542,13 +1560,76 @@ { "fieldname": "dimension_col_break", "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_7", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_28", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_49", + "fieldtype": "Column Break" + }, + { + "fieldname": "connections_tab", + "fieldtype": "Tab Break", + "label": "Connections", + "show_dashboard": 1 + }, + { + "fieldname": "payment_terms_section", + "fieldtype": "Section Break", + "label": "Payment Terms" + }, + { + "fieldname": "column_break_116", + "fieldtype": "Column Break" + }, + { + "fieldname": "billing_address_column", + "fieldtype": "Section Break", + "label": "Billing Address" + }, + { + "fieldname": "shipping_address_column", + "fieldtype": "Section Break", + "label": "Shipping Address" + }, + { + "fieldname": "column_break_93", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_84", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_92", + "fieldtype": "Column Break" + }, + { + "fieldname": "custom_dimensions_section", + "fieldtype": "Section Break" + }, + { + "collapsible": 1, + "fieldname": "additional_info_section", + "fieldtype": "Section Break", + "label": "Additional Info" + }, + { + "fieldname": "column_break_152", + "fieldtype": "Column Break" } ], "icon": "fa fa-file-text", "idx": 105, "is_submittable": 1, "links": [], - "modified": "2022-09-16 17:43:57.007441", + "modified": "2022-10-11 13:06:10.469796", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order", diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py index 548df318fac..c28f45aed41 100644 --- a/erpnext/stock/doctype/bin/bin.py +++ b/erpnext/stock/doctype/bin/bin.py @@ -37,8 +37,10 @@ class Bin(Document): self.set_projected_qty() - self.db_set("reserved_qty_for_production", flt(self.reserved_qty_for_production)) - self.db_set("projected_qty", self.projected_qty) + self.db_set( + "reserved_qty_for_production", flt(self.reserved_qty_for_production), update_modified=True + ) + self.db_set("projected_qty", self.projected_qty, update_modified=True) def update_reserved_qty_for_sub_contracting(self, subcontract_doctype="Subcontracting Order"): # reserved qty @@ -118,9 +120,9 @@ class Bin(Document): else: reserved_qty_for_sub_contract = 0 - self.db_set("reserved_qty_for_sub_contract", reserved_qty_for_sub_contract) + self.db_set("reserved_qty_for_sub_contract", reserved_qty_for_sub_contract, update_modified=True) self.set_projected_qty() - self.db_set("projected_qty", self.projected_qty) + self.db_set("projected_qty", self.projected_qty, update_modified=True) def on_doctype_update(): @@ -193,4 +195,5 @@ def update_qty(bin_name, args): "planned_qty": planned_qty, "projected_qty": projected_qty, }, + update_modified=True, ) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index a8f907ed711..0ca3e69f76c 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -8,46 +8,29 @@ "document_type": "Document", "engine": "InnoDB", "field_order": [ - "delivery_to_section", - "column_break0", "title", "naming_series", "customer", + "tax_id", "customer_name", "column_break1", - "amended_from", - "company", "posting_date", "posting_time", "set_posting_time", + "column_break_10", + "company", + "amended_from", "is_return", "issue_credit_note", "return_against", "accounting_dimensions_section", "cost_center", - "dimension_col_break", + "column_break_18", "project", - "customer_po_details", - "po_no", - "column_break_17", - "po_date", - "section_break_18", - "pick_list", - "contact_info", - "shipping_address_name", - "shipping_address", - "dispatch_address_name", - "dispatch_address", - "contact_person", - "contact_display", - "contact_mobile", - "contact_email", - "col_break21", - "customer_address", - "tax_id", - "address_display", - "company_address", - "company_address_display", + "dimension_col_break", + "campaign", + "source", + "custom_dimensions_section", "currency_and_price_list", "currency", "conversion_rate", @@ -56,45 +39,35 @@ "price_list_currency", "plc_conversion_rate", "ignore_pricing_rule", - "sec_warehouse", - "set_warehouse", - "col_break_warehouse", - "set_target_warehouse", "items_section", "scan_barcode", + "pick_list", + "col_break_warehouse", + "set_warehouse", + "set_target_warehouse", + "section_break_30", "items", - "pricing_rule_details", - "pricing_rules", - "packing_list", - "packed_items", - "product_bundle_help", "section_break_31", "total_qty", + "total_net_weight", + "column_break_35", "base_total", "base_net_total", "column_break_33", - "total_net_weight", "total", "net_total", "taxes_section", + "taxes_and_charges", + "column_break_43", "tax_category", "column_break_39", "shipping_rule", "section_break_41", - "taxes_and_charges", "taxes", - "sec_tax_breakup", - "other_charges_calculation", "section_break_44", "base_total_taxes_and_charges", "column_break_47", "total_taxes_and_charges", - "section_break_49", - "apply_discount_on", - "base_discount_amount", - "column_break_51", - "additional_discount_percentage", - "discount_amount", "totals", "base_grand_total", "base_rounding_adjustment", @@ -106,9 +79,50 @@ "rounded_total", "in_words", "disable_rounded_total", - "terms_section_break", + "section_break_49", + "apply_discount_on", + "base_discount_amount", + "column_break_51", + "additional_discount_percentage", + "discount_amount", + "sec_tax_breakup", + "other_charges_calculation", + "packing_list", + "packed_items", + "product_bundle_help", + "pricing_rule_details", + "pricing_rules", + "address_and_contact_tab", + "contact_info", + "customer_address", + "address_display", + "col_break21", + "contact_person", + "contact_display", + "contact_mobile", + "contact_email", + "shipping_address_section", + "shipping_address_name", + "shipping_address", + "column_break_95", + "dispatch_address_name", + "dispatch_address", + "company_address_section", + "company_address", + "column_break_101", + "company_address_display", + "terms_tab", "tc_name", "terms", + "more_info_tab", + "section_break_83", + "per_billed", + "status", + "column_break_112", + "per_installed", + "installation_status", + "column_break_89", + "per_returned", "transporter_info", "transporter", "driver", @@ -118,56 +132,40 @@ "transporter_name", "driver_name", "lr_date", - "more_info", - "campaign", - "source", - "column_break5", - "is_internal_customer", - "represents_company", - "inter_company_reference", - "per_billed", - "customer_group", - "territory", - "printing_details", - "letter_head", - "select_print_heading", - "language", - "column_break_88", - "print_without_amount", - "group_same_items", - "section_break_83", - "status", - "per_installed", - "installation_status", - "column_break_89", - "per_returned", - "excise_page", - "instructions", - "subscription_section", - "auto_repeat", + "customer_po_details", + "po_no", + "column_break_17", + "po_date", "sales_team_section_break", "sales_partner", - "column_break7", "amount_eligible_for_commission", + "column_break7", "commission_rate", "total_commission", "section_break1", - "sales_team" + "sales_team", + "subscription_section", + "auto_repeat", + "printing_details", + "letter_head", + "print_without_amount", + "group_same_items", + "column_break_88", + "select_print_heading", + "language", + "more_info", + "is_internal_customer", + "represents_company", + "inter_company_reference", + "customer_group", + "territory", + "column_break5", + "excise_page", + "instructions", + "connections_tab", + "column_break_25" ], "fields": [ - { - "fieldname": "delivery_to_section", - "fieldtype": "Section Break", - "label": "Delivery To", - "options": "fa fa-user" - }, - { - "fieldname": "column_break0", - "fieldtype": "Column Break", - "oldfieldtype": "Column Break", - "print_width": "50%", - "width": "50%" - }, { "allow_on_submit": 1, "default": "{customer_name}", @@ -337,11 +335,10 @@ "width": "100px" }, { - "collapsible": 1, "depends_on": "customer", "fieldname": "contact_info", "fieldtype": "Section Break", - "label": "Address and Contact", + "label": "Billing Address", "options": "fa fa-bullhorn" }, { @@ -493,10 +490,6 @@ "permlevel": 1, "print_hide": 1 }, - { - "fieldname": "sec_warehouse", - "fieldtype": "Section Break" - }, { "fieldname": "set_warehouse", "fieldtype": "Link", @@ -511,6 +504,8 @@ { "fieldname": "items_section", "fieldtype": "Section Break", + "hide_border": 1, + "label": "Items", "oldfieldtype": "Section Break", "options": "fa fa-shopping-cart" }, @@ -524,7 +519,6 @@ "allow_bulk_edit": 1, "fieldname": "items", "fieldtype": "Table", - "label": "Items", "oldfieldname": "delivery_note_details", "oldfieldtype": "Table", "options": "Delivery Note Item", @@ -619,6 +613,7 @@ "read_only": 1 }, { + "depends_on": "total_net_weight", "fieldname": "total_net_weight", "fieldtype": "Float", "label": "Total Net Weight", @@ -628,6 +623,7 @@ { "fieldname": "taxes_section", "fieldtype": "Section Break", + "hide_border": 1, "label": "Taxes and Charges", "oldfieldtype": "Section Break", "options": "fa fa-money" @@ -653,10 +649,10 @@ }, { "fieldname": "section_break_41", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_border": 1 }, { - "description": "If you have created a standard template in Sales Taxes and Charges Template, select one and click on the button below.", "fieldname": "taxes_and_charges", "fieldtype": "Link", "label": "Sales Taxes and Charges Template", @@ -668,7 +664,6 @@ { "fieldname": "taxes", "fieldtype": "Table", - "label": "Sales Taxes and Charges", "oldfieldname": "other_charges", "oldfieldtype": "Table", "options": "Sales Taxes and Charges" @@ -718,7 +713,6 @@ }, { "collapsible": 1, - "collapsible_depends_on": "discount_amount", "fieldname": "section_break_49", "fieldtype": "Section Break", "label": "Additional Discount" @@ -759,6 +753,7 @@ { "fieldname": "totals", "fieldtype": "Section Break", + "label": "Totals", "oldfieldtype": "Section Break", "options": "fa fa-money" }, @@ -863,15 +858,6 @@ "read_only": 1, "width": "150px" }, - { - "collapsible": 1, - "collapsible_depends_on": "terms", - "fieldname": "terms_section_break", - "fieldtype": "Section Break", - "label": "Terms and Conditions", - "oldfieldtype": "Section Break", - "options": "fa fa-legal" - }, { "fieldname": "tc_name", "fieldtype": "Link", @@ -965,13 +951,12 @@ "collapsible": 1, "fieldname": "more_info", "fieldtype": "Section Break", - "label": "More Information", + "label": "Additional Info", "oldfieldtype": "Section Break", "options": "fa fa-file-text", "print_hide": 1 }, { - "description": "Track this Delivery Note against any Project", "fieldname": "project", "fieldtype": "Link", "label": "Project", @@ -1110,7 +1095,6 @@ }, { "depends_on": "eval:!doc.__islocal", - "description": "% of materials delivered against this Delivery Note", "fieldname": "per_installed", "fieldtype": "Percent", "in_list_view": 1, @@ -1237,10 +1221,6 @@ "options": "Pick List", "read_only": 1 }, - { - "fieldname": "section_break_18", - "fieldtype": "Section Break" - }, { "default": "0", "fetch_from": "customer.is_internal_customer", @@ -1329,13 +1309,85 @@ { "fieldname": "dimension_col_break", "fieldtype": "Column Break" + }, + { + "fieldname": "address_and_contact_tab", + "fieldtype": "Tab Break", + "label": "Address & Contact" + }, + { + "fieldname": "terms_tab", + "fieldtype": "Tab Break", + "label": "Terms" + }, + { + "fieldname": "more_info_tab", + "fieldtype": "Tab Break", + "label": "More Info" + }, + { + "fieldname": "connections_tab", + "fieldtype": "Tab Break", + "label": "Connections", + "show_dashboard": 1 + }, + { + "fieldname": "column_break_10", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_25", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_30", + "fieldtype": "Section Break", + "hide_border": 1 + }, + { + "fieldname": "column_break_35", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_43", + "fieldtype": "Column Break" + }, + { + "fieldname": "custom_dimensions_section", + "fieldtype": "Section Break" + }, + { + "fieldname": "shipping_address_section", + "fieldtype": "Section Break", + "label": "Shipping Address" + }, + { + "fieldname": "column_break_95", + "fieldtype": "Column Break" + }, + { + "fieldname": "company_address_section", + "fieldtype": "Section Break", + "label": "Company Address" + }, + { + "fieldname": "column_break_101", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_112", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_18", + "fieldtype": "Column Break" } ], "icon": "fa fa-truck", "idx": 146, "is_submittable": 1, "links": [], - "modified": "2022-09-16 17:46:17.701904", + "modified": "2022-10-11 13:06:58.655635", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note", diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json index 0911cdb476c..0a5cbabab0b 100644 --- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json +++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json @@ -753,6 +753,7 @@ "fieldtype": "Currency", "label": "Incoming Rate", "no_copy": 1, + "precision": "6", "print_hide": 1, "read_only": 1 }, @@ -813,7 +814,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2022-09-06 14:19:42.876357", + "modified": "2022-10-12 03:36:05.344847", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note Item", diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index e35c8bf335e..31c9919708f 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -32,7 +32,7 @@ test_ignore = ["BOM"] test_dependencies = ["Warehouse", "Item Group", "Item Tax Template", "Brand", "Item Attribute"] -def make_item(item_code=None, properties=None): +def make_item(item_code=None, properties=None, uoms=None): if not item_code: item_code = frappe.generate_hash(length=16) @@ -56,6 +56,11 @@ def make_item(item_code=None, properties=None): for item_default in [doc for doc in item.get("item_defaults") if not doc.default_warehouse]: item_default.default_warehouse = "_Test Warehouse - _TC" item_default.company = "_Test Company" + + if uoms: + for uom in uoms: + item.append("uoms", uom) + item.insert() return item diff --git a/erpnext/stock/doctype/material_request/material_request.json b/erpnext/stock/doctype/material_request/material_request.json index 35931307af8..413c373e0e4 100644 --- a/erpnext/stock/doctype/material_request/material_request.json +++ b/erpnext/stock/doctype/material_request/material_request.json @@ -11,34 +11,40 @@ "naming_series", "title", "material_request_type", - "transfer_status", "customer", - "status", + "company", "column_break_2", "transaction_date", "schedule_date", - "company", "amended_from", "warehouse_section", + "scan_barcode", + "column_break_13", "set_from_warehouse", "column_break5", "set_warehouse", "items_section", - "scan_barcode", "items", - "more_info", - "per_ordered", - "column_break2", - "per_received", - "printing_details", - "letter_head", - "select_print_heading", + "terms_tab", "terms_section_break", "tc_name", "terms", + "more_info_tab", + "status_section", + "status", + "per_ordered", + "column_break2", + "transfer_status", + "per_received", + "printing_details", + "letter_head", + "column_break_31", + "select_print_heading", "reference", "job_card", - "work_order" + "column_break_35", + "work_order", + "connections_tab" ], "fields": [ { @@ -147,14 +153,6 @@ "options": "Material Request Item", "reqd": 1 }, - { - "collapsible": 1, - "fieldname": "more_info", - "fieldtype": "Section Break", - "label": "More Information", - "oldfieldtype": "Section Break", - "options": "fa fa-file-text" - }, { "default": "Today", "fieldname": "transaction_date", @@ -239,7 +237,6 @@ "collapsible_depends_on": "terms", "fieldname": "terms_section_break", "fieldtype": "Section Break", - "label": "Terms and Conditions", "oldfieldtype": "Section Break", "options": "fa fa-legal" }, @@ -277,10 +274,10 @@ { "fieldname": "warehouse_section", "fieldtype": "Section Break", - "label": "Warehouse" + "hide_border": 1, + "label": "Items" }, { - "description": "Sets 'Target Warehouse' in each row of the Items table.", "fieldname": "set_warehouse", "fieldtype": "Link", "in_list_view": 1, @@ -296,7 +293,6 @@ }, { "depends_on": "eval:doc.material_request_type == 'Material Transfer'", - "description": "Sets 'Source Warehouse' in each row of the Items table.", "fieldname": "set_from_warehouse", "fieldtype": "Link", "label": "Set Source Warehouse", @@ -317,13 +313,49 @@ "label": "Work Order", "options": "Work Order", "read_only": 1 + }, + { + "fieldname": "terms_tab", + "fieldtype": "Tab Break", + "label": "Terms" + }, + { + "fieldname": "more_info_tab", + "fieldtype": "Tab Break", + "label": "More Info" + }, + { + "fieldname": "connections_tab", + "fieldtype": "Tab Break", + "label": "Connections", + "show_dashboard": 1 + }, + { + "collapsible": 1, + "fieldname": "status_section", + "fieldtype": "Section Break", + "label": "Status", + "oldfieldtype": "Section Break", + "options": "fa fa-file-text" + }, + { + "fieldname": "column_break_31", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_35", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_13", + "fieldtype": "Column Break" } ], "icon": "fa fa-ticket", "idx": 70, "is_submittable": 1, "links": [], - "modified": "2022-08-25 11:49:28.155048", + "modified": "2022-09-27 17:58:26.366469", "modified_by": "Administrator", "module": "Stock", "name": "Material Request", diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index acaac920c9e..3141212ee5d 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -17,10 +17,11 @@ "supplier_name", "supplier_delivery_note", "column_break1", - "company", "posting_date", "posting_time", "set_posting_time", + "column_break_12", + "company", "apply_putaway_rule", "is_return", "return_against", @@ -28,18 +29,6 @@ "cost_center", "dimension_col_break", "project", - "section_addresses", - "supplier_address", - "contact_person", - "address_display", - "contact_display", - "contact_mobile", - "contact_email", - "col_break_address", - "shipping_address", - "shipping_address_display", - "billing_address", - "billing_address_display", "currency_and_price_list", "currency", "conversion_rate", @@ -49,37 +38,33 @@ "plc_conversion_rate", "ignore_pricing_rule", "sec_warehouse", + "scan_barcode", + "column_break_31", "set_warehouse", - "rejected_warehouse", - "col_break_warehouse", "set_from_warehouse", + "col_break_warehouse", + "rejected_warehouse", "is_subcontracted", "supplier_warehouse", "items_section", - "scan_barcode", "items", "section_break0", "total_qty", + "total_net_weight", + "column_break_43", "base_total", "base_net_total", "column_break_27", - "total_net_weight", "total", "net_total", - "pricing_rule_details", - "pricing_rules", - "raw_material_details", - "get_current_stock", - "supplied_items", "taxes_charges_section", - "tax_category", + "taxes_and_charges", "shipping_col", + "tax_category", + "column_break_53", "shipping_rule", "taxes_section", - "taxes_and_charges", "taxes", - "sec_tax_breakup", - "other_charges_calculation", "totals", "base_taxes_and_charges_added", "base_taxes_and_charges_deducted", @@ -88,53 +73,81 @@ "taxes_and_charges_added", "taxes_and_charges_deducted", "total_taxes_and_charges", - "section_break_42", - "apply_discount_on", - "base_discount_amount", - "column_break_44", - "additional_discount_percentage", - "discount_amount", "section_break_46", "base_grand_total", "base_rounding_adjustment", - "base_in_words", "base_rounded_total", + "base_in_words", "column_break_50", "grand_total", "rounding_adjustment", "rounded_total", "in_words", "disable_rounded_total", - "terms_section_break", + "section_break_42", + "apply_discount_on", + "base_discount_amount", + "column_break_44", + "additional_discount_percentage", + "discount_amount", + "sec_tax_breakup", + "other_charges_calculation", + "pricing_rule_details", + "pricing_rules", + "raw_material_details", + "get_current_stock", + "supplied_items", + "address_and_contact_tab", + "section_addresses", + "supplier_address", + "address_display", + "col_break_address", + "contact_person", + "contact_display", + "contact_mobile", + "contact_email", + "section_break_98", + "shipping_address", + "column_break_100", + "shipping_address_display", + "billing_address_section", + "billing_address", + "column_break_104", + "billing_address_display", + "terms_tab", "tc_name", "terms", - "more_info", + "more_info_tab", + "status_section", "status", - "amended_from", - "range", "column_break4", "per_billed", "per_returned", - "is_internal_supplier", - "inter_company_reference", - "represents_company", "subscription_detail", "auto_repeat", "printing_settings", "letter_head", - "language", - "instructions", + "group_same_items", "column_break_97", "select_print_heading", - "other_details", - "remarks", - "group_same_items", + "language", "transporter_info", "transporter_name", "column_break5", "lr_no", "lr_date", - "is_old_subcontracting_flow" + "additional_info_section", + "instructions", + "is_internal_supplier", + "represents_company", + "inter_company_reference", + "column_break_131", + "remarks", + "range", + "amended_from", + "is_old_subcontracting_flow", + "other_details", + "connections_tab" ], "fields": [ { @@ -223,7 +236,6 @@ "width": "100px" }, { - "description": "Time at which materials were received", "fieldname": "posting_time", "fieldtype": "Time", "label": "Posting Time", @@ -277,15 +289,14 @@ "read_only": 1 }, { - "collapsible": 1, "fieldname": "section_addresses", "fieldtype": "Section Break", - "label": "Address and Contact" + "label": "Supplier Address" }, { "fieldname": "supplier_address", "fieldtype": "Link", - "label": "Select Supplier Address", + "label": "Supplier Address", "options": "Address", "print_hide": 1 }, @@ -330,7 +341,7 @@ { "fieldname": "shipping_address", "fieldtype": "Link", - "label": "Select Shipping Address", + "label": "Shipping Address Template", "options": "Address", "print_hide": 1 }, @@ -410,10 +421,11 @@ }, { "fieldname": "sec_warehouse", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_border": 1, + "label": "Items" }, { - "description": "Sets 'Accepted Warehouse' in each row of the items table.", "fieldname": "set_warehouse", "fieldtype": "Link", "label": "Accepted Warehouse", @@ -421,7 +433,6 @@ "print_hide": 1 }, { - "description": "Sets 'Rejected Warehouse' in each row of the items table.", "fieldname": "rejected_warehouse", "fieldtype": "Link", "label": "Rejected Warehouse", @@ -461,6 +472,7 @@ { "fieldname": "items_section", "fieldtype": "Section Break", + "hide_border": 1, "oldfieldtype": "Section Break", "options": "fa fa-shopping-cart" }, @@ -571,6 +583,7 @@ "read_only": 1 }, { + "depends_on": "total_net_weight", "fieldname": "total_net_weight", "fieldtype": "Float", "label": "Total Net Weight", @@ -578,11 +591,11 @@ "read_only": 1 }, { - "description": "Add / Edit Taxes and Charges", "fieldname": "taxes_charges_section", "fieldtype": "Section Break", - "oldfieldtype": "Section Break", - "options": "fa fa-money" + "hide_border": 1, + "label": "Taxes and Charges", + "oldfieldtype": "Section Break" }, { "fieldname": "tax_category", @@ -603,7 +616,8 @@ }, { "fieldname": "taxes_section", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_border": 1 }, { "fieldname": "taxes_and_charges", @@ -709,7 +723,6 @@ }, { "collapsible": 1, - "collapsible_depends_on": "discount_amount", "fieldname": "section_break_42", "fieldtype": "Section Break", "label": "Additional Discount" @@ -749,7 +762,8 @@ }, { "fieldname": "section_break_46", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "label": "Totals" }, { "fieldname": "base_grand_total", @@ -841,15 +855,6 @@ "fieldtype": "Check", "label": "Disable Rounded Total" }, - { - "collapsible": 1, - "collapsible_depends_on": "terms", - "fieldname": "terms_section_break", - "fieldtype": "Section Break", - "label": "Terms and Conditions", - "oldfieldtype": "Section Break", - "options": "fa fa-legal" - }, { "fieldname": "tc_name", "fieldtype": "Link", @@ -866,14 +871,6 @@ "oldfieldname": "terms", "oldfieldtype": "Text Editor" }, - { - "collapsible": 1, - "fieldname": "more_info", - "fieldtype": "Section Break", - "label": "More Information", - "oldfieldtype": "Section Break", - "options": "fa fa-file-text" - }, { "default": "Draft", "fieldname": "status", @@ -924,7 +921,6 @@ "width": "50%" }, { - "description": "Track this Purchase Receipt against any Project", "fieldname": "project", "fieldtype": "Link", "label": "Project", @@ -941,7 +937,7 @@ { "fieldname": "subscription_detail", "fieldtype": "Section Break", - "label": "Auto Repeat Detail" + "label": "Auto Repeat" }, { "fieldname": "auto_repeat", @@ -1025,7 +1021,7 @@ "collapsible_depends_on": "transporter_name", "fieldname": "transporter_info", "fieldtype": "Section Break", - "label": "Transporter Details", + "label": "Transporter", "options": "fa fa-truck" }, { @@ -1087,7 +1083,7 @@ { "fieldname": "billing_address", "fieldtype": "Link", - "label": "Select Billing Address", + "label": "Billing Address", "options": "Address" }, { @@ -1113,7 +1109,6 @@ }, { "depends_on": "eval: doc.is_internal_supplier", - "description": "Sets 'From Warehouse' in each row of the items table.", "fieldname": "set_from_warehouse", "fieldtype": "Link", "label": "Set From Warehouse", @@ -1151,13 +1146,85 @@ "hidden": 1, "label": "Is Old Subcontracting Flow", "read_only": 1 + }, + { + "fieldname": "address_and_contact_tab", + "fieldtype": "Tab Break", + "label": "Address & Contact" + }, + { + "fieldname": "terms_tab", + "fieldtype": "Tab Break", + "label": "Terms" + }, + { + "fieldname": "more_info_tab", + "fieldtype": "Tab Break", + "label": "More Info" + }, + { + "fieldname": "connections_tab", + "fieldtype": "Tab Break", + "label": "Connections", + "show_dashboard": 1 + }, + { + "fieldname": "column_break_12", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_31", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_43", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_53", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_98", + "fieldtype": "Section Break", + "label": "Company Shipping Address" + }, + { + "fieldname": "billing_address_section", + "fieldtype": "Section Break", + "label": "Company Billing Address" + }, + { + "collapsible": 1, + "fieldname": "status_section", + "fieldtype": "Section Break", + "label": "Status", + "oldfieldtype": "Section Break", + "options": "fa fa-file-text" + }, + { + "fieldname": "additional_info_section", + "fieldtype": "Section Break", + "label": "Additional Info" + }, + { + "fieldname": "column_break_131", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_100", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_104", + "fieldtype": "Column Break" } ], "icon": "fa fa-truck", "idx": 261, "is_submittable": 1, "links": [], - "modified": "2022-09-16 17:45:58.430132", + "modified": "2022-10-11 13:02:31.776256", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 62697244bab..dc9f2b21177 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt - import frappe from frappe.tests.utils import FrappeTestCase, change_settings from frappe.utils import add_days, cint, cstr, flt, today @@ -1199,6 +1198,8 @@ class TestPurchaseReceipt(FrappeTestCase): self.assertEqual(pr1.items[0].rate, 100) pr1.submit() + self.assertEqual(pr1.is_internal_supplier, 1) + # Backdated purchase receipt entry, the valuation rate should be updated for DN1 and PR1 make_purchase_receipt( item_code=item_doc.name, @@ -1241,6 +1242,234 @@ class TestPurchaseReceipt(FrappeTestCase): self.assertEqual(query[0].value, 0) + def test_backdated_transaction_for_internal_transfer_in_trasit_warehouse_for_purchase_receipt( + self, + ): + from erpnext.stock.doctype.delivery_note.delivery_note import make_inter_company_purchase_receipt + from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note + + prepare_data_for_internal_transfer() + customer = "_Test Internal Customer 2" + company = "_Test Company with perpetual inventory" + + from_warehouse = create_warehouse("_Test Internal From Warehouse New", company=company) + to_warehouse = create_warehouse("_Test Internal To Warehouse New", company=company) + item_doc = create_item("Test Internal Transfer Item") + + target_warehouse = create_warehouse("_Test Internal GIT Warehouse New", company=company) + + make_purchase_receipt( + item_code=item_doc.name, + company=company, + posting_date=add_days(today(), -1), + warehouse=from_warehouse, + qty=1, + rate=100, + ) + + # Keep stock in advance and make sure that systen won't pick this stock while reposting backdated transaction + for i in range(1, 4): + make_purchase_receipt( + item_code=item_doc.name, + company=company, + posting_date=add_days(today(), -1 * i), + warehouse=target_warehouse, + qty=1, + rate=320 * i, + ) + + dn1 = create_delivery_note( + item_code=item_doc.name, + company=company, + customer=customer, + cost_center="Main - TCP1", + expense_account="Cost of Goods Sold - TCP1", + qty=1, + rate=500, + warehouse=from_warehouse, + target_warehouse=target_warehouse, + ) + + self.assertEqual(dn1.items[0].rate, 100) + + pr1 = make_inter_company_purchase_receipt(dn1.name) + pr1.items[0].warehouse = to_warehouse + self.assertEqual(pr1.items[0].rate, 100) + pr1.submit() + + stk_ledger = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_type": "Purchase Receipt", "voucher_no": pr1.name, "warehouse": target_warehouse}, + ["stock_value_difference", "outgoing_rate"], + as_dict=True, + ) + + self.assertEqual(abs(stk_ledger.stock_value_difference), 100) + self.assertEqual(stk_ledger.outgoing_rate, 100) + + # Backdated purchase receipt entry, the valuation rate should be updated for DN1 and PR1 + make_purchase_receipt( + item_code=item_doc.name, + company=company, + posting_date=add_days(today(), -2), + warehouse=from_warehouse, + qty=1, + rate=200, + ) + + dn_value = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_type": "Delivery Note", "voucher_no": dn1.name, "warehouse": target_warehouse}, + "stock_value_difference", + ) + + self.assertEqual(abs(dn_value), 200.00) + + pr_value = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_type": "Purchase Receipt", "voucher_no": pr1.name, "warehouse": to_warehouse}, + "stock_value_difference", + ) + + self.assertEqual(abs(pr_value), 200.00) + pr1.load_from_db() + + self.assertEqual(pr1.items[0].valuation_rate, 200) + self.assertEqual(pr1.items[0].rate, 100) + + Gl = frappe.qb.DocType("GL Entry") + + query = ( + frappe.qb.from_(Gl) + .select( + (fn.Sum(Gl.debit) - fn.Sum(Gl.credit)).as_("value"), + ) + .where((Gl.voucher_type == pr1.doctype) & (Gl.voucher_no == pr1.name)) + ).run(as_dict=True) + + self.assertEqual(query[0].value, 0) + + def test_backdated_transaction_for_internal_transfer_in_trasit_warehouse_for_purchase_invoice( + self, + ): + from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import ( + make_purchase_invoice as make_purchase_invoice_for_si, + ) + from erpnext.accounts.doctype.sales_invoice.sales_invoice import ( + make_inter_company_purchase_invoice, + ) + from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice + + prepare_data_for_internal_transfer() + customer = "_Test Internal Customer 2" + company = "_Test Company with perpetual inventory" + + from_warehouse = create_warehouse("_Test Internal From Warehouse New", company=company) + to_warehouse = create_warehouse("_Test Internal To Warehouse New", company=company) + item_doc = create_item("Test Internal Transfer Item") + + target_warehouse = create_warehouse("_Test Internal GIT Warehouse New", company=company) + + make_purchase_invoice_for_si( + item_code=item_doc.name, + company=company, + posting_date=add_days(today(), -1), + warehouse=from_warehouse, + qty=1, + update_stock=1, + expense_account="Cost of Goods Sold - TCP1", + cost_center="Main - TCP1", + rate=100, + ) + + # Keep stock in advance and make sure that systen won't pick this stock while reposting backdated transaction + for i in range(1, 4): + make_purchase_invoice_for_si( + item_code=item_doc.name, + company=company, + posting_date=add_days(today(), -1 * i), + warehouse=target_warehouse, + update_stock=1, + qty=1, + expense_account="Cost of Goods Sold - TCP1", + cost_center="Main - TCP1", + rate=320 * i, + ) + + si1 = create_sales_invoice( + item_code=item_doc.name, + company=company, + customer=customer, + cost_center="Main - TCP1", + income_account="Sales - TCP1", + qty=1, + rate=500, + update_stock=1, + warehouse=from_warehouse, + target_warehouse=target_warehouse, + ) + + self.assertEqual(si1.items[0].rate, 100) + + pi1 = make_inter_company_purchase_invoice(si1.name) + pi1.items[0].warehouse = to_warehouse + self.assertEqual(pi1.items[0].rate, 100) + pi1.update_stock = 1 + pi1.save() + pi1.submit() + + stk_ledger = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_type": pi1.doctype, "voucher_no": pi1.name, "warehouse": target_warehouse}, + ["stock_value_difference", "outgoing_rate"], + as_dict=True, + ) + + self.assertEqual(abs(stk_ledger.stock_value_difference), 100) + self.assertEqual(stk_ledger.outgoing_rate, 100) + + # Backdated purchase receipt entry, the valuation rate should be updated for si1 and pi1 + make_purchase_receipt( + item_code=item_doc.name, + company=company, + posting_date=add_days(today(), -2), + warehouse=from_warehouse, + qty=1, + rate=200, + ) + + si_value = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_type": si1.doctype, "voucher_no": si1.name, "warehouse": target_warehouse}, + "stock_value_difference", + ) + + self.assertEqual(abs(si_value), 200.00) + + pi_value = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_type": pi1.doctype, "voucher_no": pi1.name, "warehouse": to_warehouse}, + "stock_value_difference", + ) + + self.assertEqual(abs(pi_value), 200.00) + pi1.load_from_db() + + self.assertEqual(pi1.items[0].valuation_rate, 200) + self.assertEqual(pi1.items[0].rate, 100) + + Gl = frappe.qb.DocType("GL Entry") + + query = ( + frappe.qb.from_(Gl) + .select( + (fn.Sum(Gl.debit) - fn.Sum(Gl.credit)).as_("value"), + ) + .where((Gl.voucher_type == pi1.doctype) & (Gl.voucher_no == pi1.name)) + ).run(as_dict=True) + + self.assertEqual(query[0].value, 0) + def test_batch_expiry_for_purchase_receipt(self): from erpnext.controllers.sales_and_purchase_return import make_return_doc 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 39833b5e910..772736e0adc 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -744,6 +744,7 @@ "oldfieldname": "valuation_rate", "oldfieldtype": "Currency", "options": "Company:company:default_currency", + "precision": "6", "print_hide": 1, "print_width": "80px", "read_only": 1, @@ -999,7 +1000,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2022-07-28 19:27:54.880781", + "modified": "2022-10-12 03:37:59.516609", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.json b/erpnext/stock/doctype/quality_inspection/quality_inspection.json index edfe7e98b2e..db9322f3263 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.json +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.json @@ -10,6 +10,7 @@ "naming_series", "report_date", "status", + "manual_inspection", "column_break_4", "inspection_type", "reference_type", @@ -231,6 +232,12 @@ "label": "Status", "options": "\nAccepted\nRejected", "reqd": 1 + }, + { + "default": "0", + "fieldname": "manual_inspection", + "fieldtype": "Check", + "label": "Manual Inspection" } ], "icon": "fa fa-search", @@ -238,10 +245,11 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2020-12-18 19:59:55.710300", + "modified": "2022-10-04 22:00:13.995221", "modified_by": "Administrator", "module": "Stock", "name": "Quality Inspection", + "naming_rule": "By \"Naming Series\" field", "owner": "Administrator", "permissions": [ { @@ -262,5 +270,6 @@ "search_fields": "item_code, report_date, reference_name", "show_name_in_global_search": 1, "sort_field": "modified", - "sort_order": "ASC" + "sort_order": "ASC", + "states": [] } \ No newline at end of file diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.py b/erpnext/stock/doctype/quality_inspection/quality_inspection.py index 13abfad4557..8ffd3f2ad10 100644 --- a/erpnext/stock/doctype/quality_inspection/quality_inspection.py +++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.py @@ -30,6 +30,9 @@ class QualityInspection(Document): if self.readings: self.inspect_and_set_status() + def before_submit(self): + self.validate_readings_status_mandatory() + @frappe.whitelist() def get_item_specification_details(self): if not self.quality_inspection_template: @@ -65,6 +68,11 @@ class QualityInspection(Document): def on_cancel(self): self.update_qc_reference() + def validate_readings_status_mandatory(self): + for reading in self.readings: + if not reading.status: + frappe.throw(_("Row #{0}: Status is mandatory").format(reading.idx)) + def update_qc_reference(self): quality_inspection = self.name if self.docstatus == 1 else "" @@ -124,6 +132,16 @@ class QualityInspection(Document): # if not formula based check acceptance values set self.set_status_based_on_acceptance_values(reading) + if not self.manual_inspection: + self.status = "Accepted" + for reading in self.readings: + if reading.status == "Rejected": + self.status = "Rejected" + frappe.msgprint( + _("Status set to rejected as there are one or more rejected readings."), alert=True + ) + break + def set_status_based_on_acceptance_values(self, reading): if not cint(reading.numeric): result = reading.get("reading_value") == reading.get("value") diff --git a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py index 144f13880b1..4f19643ad52 100644 --- a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py +++ b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py @@ -160,7 +160,7 @@ class TestQualityInspection(FrappeTestCase): ) readings = [ - {"specification": "Iron Content", "min_value": 0.1, "max_value": 0.9, "reading_1": "0.4"} + {"specification": "Iron Content", "min_value": 0.1, "max_value": 0.9, "reading_1": "1.0"} ] qa = create_quality_inspection( @@ -184,6 +184,38 @@ class TestQualityInspection(FrappeTestCase): se.cancel() frappe.db.set_value("Stock Settings", None, "action_if_quality_inspection_is_rejected", "Stop") + def test_qi_status(self): + make_stock_entry( + item_code="_Test Item with QA", target="_Test Warehouse - _TC", qty=1, basic_rate=100 + ) + dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True) + qa = create_quality_inspection( + reference_type="Delivery Note", reference_name=dn.name, status="Accepted", do_not_save=True + ) + qa.readings[0].manual_inspection = 1 + qa.save() + + # Case - 1: When there are one or more readings with rejected status and parent manual inspection is unchecked, then parent status should be set to rejected. + qa.status = "Accepted" + qa.manual_inspection = 0 + qa.readings[0].status = "Rejected" + qa.save() + self.assertEqual(qa.status, "Rejected") + + # Case - 2: When all readings have accepted status and parent manual inspection is unchecked, then parent status should be set to accepted. + qa.status = "Rejected" + qa.manual_inspection = 0 + qa.readings[0].status = "Accepted" + qa.save() + self.assertEqual(qa.status, "Accepted") + + # Case - 3: When parent manual inspection is checked, then parent status should not be changed. + qa.status = "Accepted" + qa.manual_inspection = 1 + qa.readings[0].status = "Rejected" + qa.save() + self.assertEqual(qa.status, "Accepted") + def create_quality_inspection(**args): args = frappe._dict(args) 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 c4705246b3c..d6f9bae5da2 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py @@ -128,6 +128,9 @@ def repost(doc): if not frappe.db.exists("Repost Item Valuation", doc.name): return + # This is to avoid TooManyWritesError in case of large reposts + frappe.db.MAX_WRITES_PER_TRANSACTION *= 4 + doc.set_status("In Progress") if not frappe.flags.in_test: frappe.db.commit() diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index 6042ed4ac5d..a2748d0d09a 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -846,16 +846,15 @@ def get_pos_reserved_serial_nos(filters): pos_transacted_sr_nos = query.run(as_dict=True) - reserved_sr_nos = [] - returned_sr_nos = [] + reserved_sr_nos = set() + returned_sr_nos = set() for d in pos_transacted_sr_nos: if d.is_return == 0: - reserved_sr_nos += get_serial_nos(d.serial_no) + [reserved_sr_nos.add(x) for x in get_serial_nos(d.serial_no)] elif d.is_return == 1: - returned_sr_nos += get_serial_nos(d.serial_no) + [returned_sr_nos.add(x) for x in get_serial_nos(d.serial_no)] - for sr_no in returned_sr_nos: - reserved_sr_nos.remove(sr_no) + reserved_sr_nos = list(reserved_sr_nos - returned_sr_nos) return reserved_sr_nos diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json index abe98e2933e..7e9420d5035 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.json +++ b/erpnext/stock/doctype/stock_entry/stock_entry.json @@ -148,19 +148,19 @@ "search_index": 1 }, { - "depends_on": "eval:doc.purpose==\"Send to Subcontractor\"", - "fieldname": "purchase_order", - "fieldtype": "Link", - "label": "Purchase Order", - "options": "Purchase Order" + "depends_on": "eval:doc.purpose==\"Send to Subcontractor\"", + "fieldname": "purchase_order", + "fieldtype": "Link", + "label": "Purchase Order", + "options": "Purchase Order" }, { - "depends_on": "eval:doc.purpose==\"Send to Subcontractor\"", - "fieldname": "subcontracting_order", - "fieldtype": "Link", - "label": "Subcontracting Order", - "options": "Subcontracting Order" - }, + "depends_on": "eval:doc.purpose==\"Send to Subcontractor\"", + "fieldname": "subcontracting_order", + "fieldtype": "Link", + "label": "Subcontracting Order", + "options": "Subcontracting Order" + }, { "depends_on": "eval:doc.purpose==\"Sales Return\"", "fieldname": "delivery_note_no", @@ -616,6 +616,7 @@ "fieldname": "is_return", "fieldtype": "Check", "hidden": 1, + "in_list_view": 1, "label": "Is Return", "no_copy": 1, "print_hide": 1, @@ -627,7 +628,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2022-05-02 05:21:39.060501", + "modified": "2022-10-07 14:39:51.943770", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry", diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 8bcd772d909..b1167351c48 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -1212,13 +1212,19 @@ class StockEntry(StockController): def update_work_order(self): def _validate_work_order(pro_doc): + msg, title = "", "" if flt(pro_doc.docstatus) != 1: - frappe.throw(_("Work Order {0} must be submitted").format(self.work_order)) + msg = f"Work Order {self.work_order} must be submitted" if pro_doc.status == "Stopped": - frappe.throw( - _("Transaction not allowed against stopped Work Order {0}").format(self.work_order) - ) + msg = f"Transaction not allowed against stopped Work Order {self.work_order}" + + if self.is_return and pro_doc.status not in ["Completed", "Closed"]: + title = _("Stock Return") + msg = f"Work Order {self.work_order} must be completed or closed" + + if msg: + frappe.throw(_(msg), title=title) if self.job_card: job_doc = frappe.get_doc("Job Card", self.job_card) @@ -1754,10 +1760,12 @@ class StockEntry(StockController): for key, row in available_materials.items(): remaining_qty_to_produce = flt(wo_data.trans_qty) - flt(wo_data.produced_qty) - if remaining_qty_to_produce <= 0: + if remaining_qty_to_produce <= 0 and not self.is_return: continue - qty = (flt(row.qty) * flt(self.fg_completed_qty)) / remaining_qty_to_produce + qty = flt(row.qty) + if not self.is_return: + qty = (flt(row.qty) * flt(self.fg_completed_qty)) / remaining_qty_to_produce item = row.item_details if cint(frappe.get_cached_value("UOM", item.stock_uom, "must_be_whole_number")): @@ -1781,6 +1789,9 @@ class StockEntry(StockController): self.update_item_in_stock_entry_detail(row, item, qty) def update_item_in_stock_entry_detail(self, row, item, qty) -> None: + if not qty: + return + ste_item_details = { "from_warehouse": item.warehouse, "to_warehouse": "", @@ -1794,6 +1805,9 @@ class StockEntry(StockController): "original_item": item.original_item, } + if self.is_return: + ste_item_details["to_warehouse"] = item.s_warehouse + if row.serial_nos: serial_nos = row.serial_nos if item.batch_no: diff --git a/erpnext/stock/doctype/stock_entry/stock_entry_list.js b/erpnext/stock/doctype/stock_entry/stock_entry_list.js index cbc3491eba4..4eb0da11d2f 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry_list.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry_list.js @@ -1,8 +1,13 @@ 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`.`purpose`", "`tabStock Entry`.`work_order`", "`tabStock Entry`.`bom_no`", + "`tabStock Entry`.`is_return`"], get_indicator: function (doc) { - if (doc.docstatus === 0) { + debugger + 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) { diff --git a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py index 1410da56a38..6c341d9e9ec 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py @@ -1323,13 +1323,15 @@ def create_product_bundle_item(new_item_code, packed_items): item.save() -def create_items(): - items = [ - "_Test Item for Reposting", - "_Test Finished Item for Reposting", - "_Test Subcontracted Item for Reposting", - "_Test Bundled Item for Reposting", - ] +def create_items(items=None, uoms=None): + if not items: + items = [ + "_Test Item for Reposting", + "_Test Finished Item for Reposting", + "_Test Subcontracted Item for Reposting", + "_Test Bundled Item for Reposting", + ] + for d in items: properties = {"valuation_method": "FIFO"} if d == "_Test Bundled Item for Reposting": @@ -1337,7 +1339,7 @@ def create_items(): elif d == "_Test Subcontracted Item for Reposting": properties.update({"is_sub_contracted_item": 1}) - make_item(d, properties=properties) + make_item(d, properties=properties, uoms=uoms) return items diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 23e0f1efafa..d92d0f1686e 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -132,7 +132,9 @@ class StockReconciliation(StockController): key.append(row.get(field)) if key in item_warehouse_combinations: - self.validation_messages.append(_get_msg(row_num, _("Duplicate entry"))) + self.validation_messages.append( + _get_msg(row_num, _("Same item and warehouse combination already entered.")) + ) else: item_warehouse_combinations.append(key) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 50309647de0..cdf6e89fcbb 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -542,6 +542,14 @@ class update_entries_after(object): if not self.args.get("sle_id"): self.get_dynamic_incoming_outgoing_rate(sle) + if ( + sle.voucher_type in ["Purchase Receipt", "Purchase Invoice"] + and sle.voucher_detail_no + and sle.actual_qty < 0 + and frappe.get_cached_value(sle.voucher_type, sle.voucher_no, "is_internal_supplier") + ): + sle.outgoing_rate = get_incoming_rate_for_inter_company_transfer(sle) + if get_serial_nos(sle.serial_no): self.get_serialized_values(sle) self.wh_data.qty_after_transaction += flt(sle.actual_qty) @@ -589,6 +597,7 @@ class update_entries_after(object): sle.stock_queue = json.dumps(self.wh_data.stock_queue) sle.stock_value_difference = stock_value_difference sle.doctype = "Stock Ledger Entry" + frappe.get_doc(sle).db_update() if not self.args.get("sle_id"): @@ -652,22 +661,7 @@ class update_entries_after(object): and sle.voucher_detail_no and frappe.get_cached_value(sle.voucher_type, sle.voucher_no, "is_internal_supplier") ): - field = ( - "delivery_note_item" if sle.voucher_type == "Purchase Receipt" else "sales_invoice_item" - ) - doctype = ( - "Delivery Note Item" if sle.voucher_type == "Purchase Receipt" else "Sales Invoice Item" - ) - refernce_name = frappe.get_cached_value( - sle.voucher_type + " Item", sle.voucher_detail_no, field - ) - - if refernce_name: - rate = frappe.get_cached_value( - doctype, - refernce_name, - "incoming_rate", - ) + rate = get_incoming_rate_for_inter_company_transfer(sle) else: if sle.voucher_type in ("Purchase Receipt", "Purchase Invoice"): rate_field = "valuation_rate" @@ -748,14 +742,12 @@ class update_entries_after(object): def update_rate_on_purchase_receipt(self, sle, outgoing_rate): if frappe.db.exists(sle.voucher_type + " Item", sle.voucher_detail_no): - frappe.db.set_value( - sle.voucher_type + " Item", - sle.voucher_detail_no, - { - "base_net_rate": outgoing_rate, - "valuation_rate": outgoing_rate, - }, - ) + if sle.voucher_type in ["Purchase Receipt", "Purchase Invoice"] and frappe.get_cached_value( + sle.voucher_type, sle.voucher_no, "is_internal_supplier" + ): + frappe.db.set_value( + f"{sle.voucher_type} Item", sle.voucher_detail_no, "valuation_rate", sle.outgoing_rate + ) else: frappe.db.set_value( "Purchase Receipt Item Supplied", sle.voucher_detail_no, "rate", outgoing_rate @@ -1053,7 +1045,7 @@ class update_entries_after(object): updated_values = {"actual_qty": data.qty_after_transaction, "stock_value": data.stock_value} if data.valuation_rate is not None: updated_values["valuation_rate"] = data.valuation_rate - frappe.db.set_value("Bin", bin_name, updated_values) + frappe.db.set_value("Bin", bin_name, updated_values, update_modified=True) def get_previous_sle_of_current_voucher(args, exclude_current_voucher=False): @@ -1546,3 +1538,25 @@ def is_negative_stock_allowed(*, item_code: Optional[str] = None) -> bool: if item_code and cint(frappe.db.get_value("Item", item_code, "allow_negative_stock", cache=True)): return True return False + + +def get_incoming_rate_for_inter_company_transfer(sle) -> float: + """ + For inter company transfer, incoming rate is the average of the outgoing rate + """ + rate = 0.0 + + field = "delivery_note_item" if sle.voucher_type == "Purchase Receipt" else "sales_invoice_item" + + doctype = "Delivery Note Item" if sle.voucher_type == "Purchase Receipt" else "Sales Invoice Item" + + reference_name = frappe.get_cached_value(sle.voucher_type + " Item", sle.voucher_detail_no, field) + + if reference_name: + rate = frappe.get_cached_value( + doctype, + reference_name, + "incoming_rate", + ) + + return rate diff --git a/erpnext/templates/pages/demo.html b/erpnext/templates/pages/demo.html deleted file mode 100644 index f9934a33f32..00000000000 --- a/erpnext/templates/pages/demo.html +++ /dev/null @@ -1,77 +0,0 @@ -{% extends "templates/web.html" %} - -{% block script %} - -{% endblock %} - -{% block style %} - -{% endblock %} - -{% block title %} -{{ _("ERPNext Demo") }} -{% endblock %} - -{% block page_content %} -
Some functionality is disabled for the demo and the data will be cleared regularly.
- -