diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py index 6a990a5cf7418c79cc193bddea1f183beb838c4f..cbdc758a523629014616723299b058b8327c5498 100644 --- a/erpnext/manufacturing/doctype/bom/bom.py +++ b/erpnext/manufacturing/doctype/bom/bom.py @@ -1329,12 +1329,18 @@ def get_children(parent=None, is_root=False, **filters): def add_additional_cost(stock_entry, work_order): # Add non stock items cost in the additional cost stock_entry.additional_costs = [] - expenses_included_in_valuation = frappe.get_cached_value( - "Company", work_order.company, "expenses_included_in_valuation" + company_account = frappe.db.get_value( + "Company", + work_order.company, + ["default_expense_account", "default_operating_cost_account"], + as_dict=1, ) - add_non_stock_items_cost(stock_entry, work_order, expenses_included_in_valuation) - add_operations_cost(stock_entry, work_order, expenses_included_in_valuation) + expecnse_account = ( + company_account.default_operating_cost_account or company_account.default_expense_account + ) + add_non_stock_items_cost(stock_entry, work_order, expecnse_account) + add_operations_cost(stock_entry, work_order, expecnse_account) def add_non_stock_items_cost(stock_entry, work_order, expense_account): diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index 96d0bc8647646e06504c434cf427a76a28902b71..2453733417a93508c91d763521607584624f3153 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -1769,6 +1769,81 @@ class TestWorkOrder(FrappeTestCase): job_card2.time_logs = [] job_card2.save() + def test_operating_cost_account(self): + operating_cost_account = "Test Operating Cost Account - _TC" + company = "_Test Company" + if not frappe.db.exists("Account", operating_cost_account): + frappe.get_doc( + { + "doctype": "Account", + "account_name": "Test Operating Cost Account", + "account_type": "Expense Account", + "company": company, + "parent_account": "Expenses - _TC", + "root_type": "Expense", + } + ).insert() + + frappe.db.set_value("Company", company, "default_operating_cost_account", operating_cost_account) + + for item in ["TEST RM OP COST Item 1", "TEST FG OP COST Item"]: + if not frappe.db.exists("Item", item): + make_item(item_code=item, properties={"is_stock_item": 1}) + + fg_item = "TEST FG OP COST Item" + bom_doc = make_bom( + item=fg_item, + raw_materials=["TEST RM OP COST Item 1"], + rate=150, + with_operations=1, + do_not_save=True, + ) + + workstation = "Test Workstation For Capacity Planning 1" + if not frappe.db.exists("Workstation", workstation): + make_workstation(workstation=workstation, production_capacity=1) + + operation = "Test Operation For Capacity Planning 1" + if not frappe.db.exists("Operation", operation): + make_operation(operation=operation, workstation=workstation) + + bom_doc.append( + "operations", + {"operation": operation, "time_in_mins": 60, "hour_rate": 100, "workstation": workstation}, + ) + + bom_doc.save() + bom_doc.submit() + + wo = make_wo_order_test_record( + production_item=fg_item, + bom_no=bom_doc.name, + qty=1, + skip_transfer=1, + ) + + job_cards = frappe.get_all("Job Card", filters={"work_order": wo.name}) + for job_card in job_cards: + job_card_doc = frappe.get_doc("Job Card", job_card.name) + job_card_doc.time_logs = [] + job_card_doc.append( + "time_logs", + { + "from_time": now(), + "to_time": add_to_date(now(), minutes=60), + "time_in_mins": 60, + "completed_qty": 1, + }, + ) + + job_card_doc.submit() + + se_doc = frappe.get_doc(make_stock_entry(wo.name, "Manufacture", 1)) + se_doc.save() + + for row in se_doc.additional_costs: + self.assertEqual(row.expense_account, operating_cost_account) + def test_op_cost_and_scrap_based_on_sub_assemblies(self): # Make Sub Assembly BOM 1 diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 11c2828f90b6115e72f4be5c478a8913687fc59b..cbeb467d830ab0be183af6e68d5db01c41cdd9a9 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -28,7 +28,13 @@ frappe.ui.form.on("Company", { } }); - frm.set_query("default_selling_terms", function() { + frm.set_query("default_operating_cost_account", function (doc) { + return { + filters: { company: doc.name, root_type: "Expense" }, + }; + }); + + frm.set_query("default_selling_terms", function () { return { filters: { selling: 1 } }; }); diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 2758f6174180dfa232f444338e026744c3afc745..c18b4fb01bafbbb57b5b2a1bd0b99e24012b77ee 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -108,6 +108,8 @@ "stock_received_but_not_billed", "default_provisional_account", "default_in_transit_warehouse", + "manufacturing_section", + "default_operating_cost_account", "dashboard_tab" ], "fields": [ @@ -764,13 +766,24 @@ { "fieldname": "stock_tab", "fieldtype": "Tab Break", - "label": "Stock" + "label": "Stock and Manufacturing" }, { "fieldname": "dashboard_tab", "fieldtype": "Tab Break", "label": "Dashboard", "show_dashboard": 1 + }, + { + "fieldname": "manufacturing_section", + "fieldtype": "Section Break", + "label": "Manufacturing" + }, + { + "fieldname": "default_operating_cost_account", + "fieldtype": "Link", + "label": "Default Operating Cost Account", + "options": "Account" } ], "icon": "fa fa-building", @@ -778,7 +791,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2024-05-27 17:32:49.057386", + "modified": "2024-06-21 17:46:25.567565", "modified_by": "Administrator", "module": "Setup", "name": "Company", diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py index d6c75edbd2769a6258281b51bf299eb601f8e1a9..1b2537d2bb2aaa96e648bf0413cc8b8d6c5a93a8 100644 --- a/erpnext/setup/doctype/company/company.py +++ b/erpnext/setup/doctype/company/company.py @@ -17,6 +17,91 @@ from erpnext.accounts.doctype.account.account import get_account_currency class Company(NestedSet): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + abbr: DF.Data + accumulated_depreciation_account: DF.Link | None + allow_account_creation_against_child_company: DF.Check + asset_received_but_not_billed: DF.Link | None + auto_err_frequency: DF.Literal["Daily", "Weekly"] + auto_exchange_rate_revaluation: DF.Check + book_advance_payments_in_separate_party_account: DF.Check + capital_work_in_progress_account: DF.Link | None + chart_of_accounts: DF.Literal[None] + company_description: DF.TextEditor | None + company_logo: DF.AttachImage | None + company_name: DF.Data + cost_center: DF.Link | None + country: DF.Link + create_chart_of_accounts_based_on: DF.Literal["", "Standard Template", "Existing Company"] + credit_limit: DF.Currency + date_of_commencement: DF.Date | None + date_of_establishment: DF.Date | None + date_of_incorporation: DF.Date | None + default_advance_paid_account: DF.Link | None + default_advance_received_account: DF.Link | None + default_bank_account: DF.Link | None + default_buying_terms: DF.Link | None + default_cash_account: DF.Link | None + default_currency: DF.Link + default_deferred_expense_account: DF.Link | None + default_deferred_revenue_account: DF.Link | None + default_discount_account: DF.Link | None + default_expense_account: DF.Link | None + default_finance_book: DF.Link | None + default_holiday_list: DF.Link | None + default_in_transit_warehouse: DF.Link | None + default_income_account: DF.Link | None + default_inventory_account: DF.Link | None + default_letter_head: DF.Link | None + default_operating_cost_account: DF.Link | None + default_payable_account: DF.Link | None + default_provisional_account: DF.Link | None + default_receivable_account: DF.Link | None + default_selling_terms: DF.Link | None + default_warehouse_for_sales_return: DF.Link | None + depreciation_cost_center: DF.Link | None + depreciation_expense_account: DF.Link | None + disposal_account: DF.Link | None + domain: DF.Data | None + email: DF.Data | None + enable_perpetual_inventory: DF.Check + enable_provisional_accounting_for_non_stock_items: DF.Check + exception_budget_approver_role: DF.Link | None + exchange_gain_loss_account: DF.Link | None + existing_company: DF.Link | None + fax: DF.Data | None + is_group: DF.Check + lft: DF.Int + monthly_sales_target: DF.Currency + old_parent: DF.Data | None + parent_company: DF.Link | None + payment_terms: DF.Link | None + phone_no: DF.Data | None + registration_details: DF.Code | None + rgt: DF.Int + round_off_account: DF.Link | None + round_off_cost_center: DF.Link | None + sales_monthly_history: DF.SmallText | None + series_for_depreciation_entry: DF.Data | None + stock_adjustment_account: DF.Link | None + stock_received_but_not_billed: DF.Link | None + submit_err_jv: DF.Check + tax_id: DF.Data | None + total_monthly_sales: DF.Currency + transactions_annual_history: DF.Code | None + unrealized_exchange_gain_loss_account: DF.Link | None + unrealized_profit_loss_account: DF.Link | None + website: DF.Data | None + write_off_account: DF.Link | None + # end: auto-generated types + nsm_parent_field = "parent_company" def onload(self):