Merge pull request #37294 from ruthra-kumar/restrict_payment_as_ref_in_je_from_ui
refactor: block Payment Entry as ref in Journals from UI
diff --git a/erpnext/accounts/doctype/bank_clearance/test_bank_clearance.py b/erpnext/accounts/doctype/bank_clearance/test_bank_clearance.py
index c7404d1..2a18830 100644
--- a/erpnext/accounts/doctype/bank_clearance/test_bank_clearance.py
+++ b/erpnext/accounts/doctype/bank_clearance/test_bank_clearance.py
@@ -35,13 +35,13 @@
from lending.loan_management.doctype.loan.test_loan import (
create_loan,
create_loan_accounts,
- create_loan_type,
+ create_loan_product,
create_repayment_entry,
make_loan_disbursement_entry,
)
def create_loan_masters():
- create_loan_type(
+ create_loan_product(
"Clearance Loan",
2000000,
13.5,
diff --git a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py
index 0c328ff..2a504f6 100644
--- a/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py
+++ b/erpnext/accounts/doctype/bank_transaction/test_bank_transaction.py
@@ -410,7 +410,7 @@
def create_loan_and_repayment():
from lending.loan_management.doctype.loan.test_loan import (
create_loan,
- create_loan_type,
+ create_loan_product,
create_repayment_entry,
make_loan_disbursement_entry,
)
@@ -420,7 +420,7 @@
from erpnext.setup.doctype.employee.test_employee import make_employee
- create_loan_type(
+ create_loan_product(
"Personal Loan",
500000,
8.4,
@@ -441,7 +441,7 @@
"applicant_type": "Employee",
"company": "_Test Company",
"applicant": applicant,
- "loan_type": "Personal Loan",
+ "loan_product": "Personal Loan",
"loan_amount": 5000,
"repayment_method": "Repay Fixed Amount per Period",
"monthly_repayment_amount": 500,
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.json b/erpnext/accounts/doctype/payment_request/payment_request.json
index 5ffd718..66b5c4b 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.json
+++ b/erpnext/accounts/doctype/payment_request/payment_request.json
@@ -268,8 +268,7 @@
"fieldname": "email_to",
"fieldtype": "Data",
"in_global_search": 1,
- "label": "To",
- "options": "Email"
+ "label": "To"
},
{
"depends_on": "eval: doc.payment_channel != \"Phone\"",
@@ -340,8 +339,8 @@
},
{
"fieldname": "payment_url",
- "hidden": 1,
"fieldtype": "Data",
+ "hidden": 1,
"length": 500,
"options": "URL",
"read_only": 1
@@ -396,7 +395,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2023-09-16 14:15:02.510890",
+ "modified": "2023-09-27 09:51:42.277638",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Request",
diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.json b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.json
index 54a76b3..624b5f8 100644
--- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.json
+++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.json
@@ -8,6 +8,7 @@
"transaction_date",
"posting_date",
"fiscal_year",
+ "year_start_date",
"amended_from",
"company",
"column_break1",
@@ -100,16 +101,22 @@
"fieldtype": "Text",
"label": "Error Message",
"read_only": 1
+ },
+ {
+ "fieldname": "year_start_date",
+ "fieldtype": "Date",
+ "label": "Year Start Date"
}
],
"icon": "fa fa-file-text",
"idx": 1,
"is_submittable": 1,
"links": [],
- "modified": "2022-07-20 14:51:04.714154",
+ "modified": "2023-09-11 20:19:11.810533",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Period Closing Voucher",
+ "naming_rule": "Expression (old style)",
"owner": "Administrator",
"permissions": [
{
@@ -144,5 +151,6 @@
"search_fields": "posting_date, fiscal_year",
"sort_field": "modified",
"sort_order": "DESC",
+ "states": [],
"title_field": "closing_account_head"
}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py
index d984d86..674db6c 100644
--- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py
+++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py
@@ -95,15 +95,23 @@
self.check_if_previous_year_closed()
- pce = frappe.db.sql(
- """select name from `tabPeriod Closing Voucher`
- where posting_date > %s and fiscal_year = %s and docstatus = 1 and company = %s""",
- (self.posting_date, self.fiscal_year, self.company),
+ pcv = frappe.qb.DocType("Period Closing Voucher")
+ existing_entry = (
+ frappe.qb.from_(pcv)
+ .select(pcv.name)
+ .where(
+ (pcv.posting_date >= self.posting_date)
+ & (pcv.fiscal_year == self.fiscal_year)
+ & (pcv.docstatus == 1)
+ & (pcv.company == self.company)
+ )
+ .run()
)
- if pce and pce[0][0]:
+
+ if existing_entry and existing_entry[0][0]:
frappe.throw(
_("Another Period Closing Entry {0} has been made after {1}").format(
- pce[0][0], self.posting_date
+ existing_entry[0][0], self.posting_date
)
)
@@ -130,18 +138,27 @@
frappe.enqueue(
process_gl_entries,
gl_entries=gl_entries,
+ voucher_name=self.name,
+ timeout=3000,
+ )
+
+ frappe.enqueue(
+ process_closing_entries,
+ gl_entries=gl_entries,
closing_entries=closing_entries,
voucher_name=self.name,
company=self.company,
closing_date=self.posting_date,
- queue="long",
+ timeout=3000,
)
+
frappe.msgprint(
_("The GL Entries will be processed in the background, it can take a few minutes."),
alert=True,
)
else:
- process_gl_entries(gl_entries, closing_entries, self.name, self.company, self.posting_date)
+ process_gl_entries(gl_entries, self.name)
+ process_closing_entries(gl_entries, closing_entries, self.name, self.company, self.posting_date)
def get_grouped_gl_entries(self, get_opening_entries=False):
closing_entries = []
@@ -322,17 +339,12 @@
return query.run(as_dict=1)
-def process_gl_entries(gl_entries, closing_entries, voucher_name, company, closing_date):
- from erpnext.accounts.doctype.account_closing_balance.account_closing_balance import (
- make_closing_entries,
- )
+def process_gl_entries(gl_entries, voucher_name):
from erpnext.accounts.general_ledger import make_gl_entries
try:
if gl_entries:
make_gl_entries(gl_entries, merge_entries=False)
-
- make_closing_entries(gl_entries + closing_entries, voucher_name, company, closing_date)
frappe.db.set_value("Period Closing Voucher", voucher_name, "gle_processing_status", "Completed")
except Exception as e:
frappe.db.rollback()
@@ -340,6 +352,19 @@
frappe.db.set_value("Period Closing Voucher", voucher_name, "gle_processing_status", "Failed")
+def process_closing_entries(gl_entries, closing_entries, voucher_name, company, closing_date):
+ from erpnext.accounts.doctype.account_closing_balance.account_closing_balance import (
+ make_closing_entries,
+ )
+
+ try:
+ if gl_entries + closing_entries:
+ make_closing_entries(gl_entries + closing_entries, voucher_name, company, closing_date)
+ except Exception as e:
+ frappe.db.rollback()
+ frappe.log_error(e)
+
+
def make_reverse_gl_entries(voucher_type, voucher_no):
from erpnext.accounts.general_ledger import make_reverse_gl_entries
diff --git a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py
index 5d08e8d..1bd565e 100644
--- a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py
+++ b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py
@@ -10,7 +10,7 @@
from erpnext.accounts.doctype.finance_book.test_finance_book import create_finance_book
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
-from erpnext.accounts.utils import get_fiscal_year, now
+from erpnext.accounts.utils import get_fiscal_year
class TestPeriodClosingVoucher(unittest.TestCase):
diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py
index 9a5ad35..52ae951 100644
--- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py
+++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.py
@@ -145,7 +145,8 @@
def get_ar_filters(doc, entry):
return {
"report_date": doc.posting_date if doc.posting_date else None,
- "customer": entry.customer,
+ "party_type": "Customer",
+ "party": [entry.customer],
"customer_name": entry.customer_name if entry.customer_name else None,
"payment_terms_template": doc.payment_terms_template if doc.payment_terms_template else None,
"sales_partner": doc.sales_partner if doc.sales_partner else None,
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index 095617d..2eaa337 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -479,6 +479,12 @@
}
});
+cur_frm.set_query("wip_composite_asset", "items", function() {
+ return {
+ filters: {'is_composite_asset': 1, 'docstatus': 0 }
+ }
+});
+
cur_frm.cscript.expense_account = function(doc, cdt, cdn){
var d = locals[cdt][cdn];
if(d.idx == 1 && d.expense_account){
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
index f3c0181..e489882 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
@@ -1385,6 +1385,7 @@
"depends_on": "eval:doc.is_subcontracted",
"fieldname": "supplier_warehouse",
"fieldtype": "Link",
+ "ignore_user_permissions": 1,
"label": "Supplier Warehouse",
"no_copy": 1,
"options": "Warehouse",
@@ -1593,7 +1594,7 @@
"idx": 204,
"is_submittable": 1,
"links": [],
- "modified": "2023-09-21 12:22:04.545106",
+ "modified": "2023-10-01 21:01:47.282533",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice",
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 3690142..424e942 100644
--- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
+++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
@@ -77,6 +77,7 @@
"manufacturer_part_no",
"accounting",
"expense_account",
+ "wip_composite_asset",
"col_break5",
"is_fixed_asset",
"asset_location",
@@ -903,12 +904,18 @@
"no_copy": 1,
"options": "Serial and Batch Bundle",
"print_hide": 1
+ },
+ {
+ "fieldname": "wip_composite_asset",
+ "fieldtype": "Link",
+ "label": "WIP Composite Asset",
+ "options": "Asset"
}
],
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2023-07-26 12:54:53.178156",
+ "modified": "2023-10-03 21:01:01.824892",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Item",
diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.js b/erpnext/accounts/report/accounts_payable/accounts_payable.js
index 27a8570..9c73cbb 100644
--- a/erpnext/accounts/report/accounts_payable/accounts_payable.js
+++ b/erpnext/accounts/report/accounts_payable/accounts_payable.js
@@ -95,30 +95,27 @@
"options": "Payment Terms Template"
},
{
- "fieldname": "party_type",
+ "fieldname":"party_type",
"label": __("Party Type"),
- "fieldtype": "Link",
- "options": "Party Type",
- get_query: () => {
- return {
- filters: {
- 'account_type': 'Payable'
- }
- };
- },
- on_change: () => {
+ "fieldtype": "Autocomplete",
+ options: get_party_type_options(),
+ on_change: function() {
frappe.query_report.set_filter_value('party', "");
- let party_type = frappe.query_report.get_filter_value('party_type');
frappe.query_report.toggle_filter_display('supplier_group', frappe.query_report.get_filter_value('party_type') !== "Supplier");
-
}
-
},
{
"fieldname":"party",
"label": __("Party"),
- "fieldtype": "Dynamic Link",
- "options": "party_type",
+ "fieldtype": "MultiSelectList",
+ get_data: function(txt) {
+ if (!frappe.query_report.filters) return;
+
+ let party_type = frappe.query_report.get_filter_value('party_type');
+ if (!party_type) return;
+
+ return frappe.db.get_link_options(party_type, txt);
+ },
},
{
"fieldname": "supplier_group",
@@ -167,3 +164,15 @@
}
erpnext.utils.add_dimensions('Accounts Payable', 9);
+
+function get_party_type_options() {
+ let options = [];
+ frappe.db.get_list(
+ "Party Type", {filters:{"account_type": "Payable"}, fields:['name']}
+ ).then((res) => {
+ res.forEach((party_type) => {
+ options.push(party_type.name);
+ });
+ });
+ return options;
+}
\ No newline at end of file
diff --git a/erpnext/accounts/report/accounts_payable/test_accounts_payable.py b/erpnext/accounts/report/accounts_payable/test_accounts_payable.py
index 3cf93cc..9f03d92 100644
--- a/erpnext/accounts/report/accounts_payable/test_accounts_payable.py
+++ b/erpnext/accounts/report/accounts_payable/test_accounts_payable.py
@@ -34,7 +34,7 @@
filters = {
"company": self.company,
"party_type": "Supplier",
- "party": self.supplier,
+ "party": [self.supplier],
"report_date": today(),
"range1": 30,
"range2": 60,
diff --git a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js
index ea20072..9e575e6 100644
--- a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js
+++ b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js
@@ -72,10 +72,27 @@
}
},
{
- "fieldname":"supplier",
- "label": __("Supplier"),
- "fieldtype": "Link",
- "options": "Supplier"
+ "fieldname":"party_type",
+ "label": __("Party Type"),
+ "fieldtype": "Autocomplete",
+ options: get_party_type_options(),
+ on_change: function() {
+ frappe.query_report.set_filter_value('party', "");
+ frappe.query_report.toggle_filter_display('supplier_group', frappe.query_report.get_filter_value('party_type') !== "Supplier");
+ }
+ },
+ {
+ "fieldname":"party",
+ "label": __("Party"),
+ "fieldtype": "MultiSelectList",
+ get_data: function(txt) {
+ if (!frappe.query_report.filters) return;
+
+ let party_type = frappe.query_report.get_filter_value('party_type');
+ if (!party_type) return;
+
+ return frappe.db.get_link_options(party_type, txt);
+ },
},
{
"fieldname":"payment_terms_template",
@@ -105,3 +122,15 @@
}
erpnext.utils.add_dimensions('Accounts Payable Summary', 9);
+
+function get_party_type_options() {
+ let options = [];
+ frappe.db.get_list(
+ "Party Type", {filters:{"account_type": "Payable"}, fields:['name']}
+ ).then((res) => {
+ res.forEach((party_type) => {
+ options.push(party_type.name);
+ });
+ });
+ return options;
+}
\ No newline at end of file
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js
index bb00d61..1073be0 100644
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js
@@ -1,6 +1,8 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
+frappe.provide("erpnext.utils");
+
frappe.query_reports["Accounts Receivable"] = {
"filters": [
{
@@ -38,30 +40,27 @@
}
},
{
- "fieldname": "party_type",
+ "fieldname":"party_type",
"label": __("Party Type"),
- "fieldtype": "Link",
- "options": "Party Type",
- "Default": "Customer",
- get_query: () => {
- return {
- filters: {
- 'account_type': 'Receivable'
- }
- };
- },
- on_change: () => {
+ "fieldtype": "Autocomplete",
+ options: get_party_type_options(),
+ on_change: function() {
frappe.query_report.set_filter_value('party', "");
- let party_type = frappe.query_report.get_filter_value('party_type');
frappe.query_report.toggle_filter_display('customer_group', frappe.query_report.get_filter_value('party_type') !== "Customer");
-
}
},
{
"fieldname":"party",
"label": __("Party"),
- "fieldtype": "Dynamic Link",
- "options": "party_type",
+ "fieldtype": "MultiSelectList",
+ get_data: function(txt) {
+ if (!frappe.query_report.filters) return;
+
+ let party_type = frappe.query_report.get_filter_value('party_type');
+ if (!party_type) return;
+
+ return frappe.db.get_link_options(party_type, txt);
+ },
},
{
"fieldname": "party_account",
@@ -194,3 +193,16 @@
}
erpnext.utils.add_dimensions('Accounts Receivable', 9);
+
+
+function get_party_type_options() {
+ let options = [];
+ frappe.db.get_list(
+ "Party Type", {filters:{"account_type": "Receivable"}, fields:['name']}
+ ).then((res) => {
+ res.forEach((party_type) => {
+ options.push(party_type.name);
+ });
+ });
+ return options;
+}
\ No newline at end of file
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
index 7942402..e3b671f 100755
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
@@ -801,7 +801,7 @@
self.qb_selection_filter.append(self.filters.party_type == self.ple.party_type)
if self.filters.get("party"):
- self.qb_selection_filter.append(self.filters.party == self.ple.party)
+ self.qb_selection_filter.append(self.ple.party.isin(self.filters.party))
if self.filters.party_account:
self.qb_selection_filter.append(self.ple.account == self.filters.party_account)
diff --git a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py
index b98916e..4307689 100644
--- a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py
@@ -573,7 +573,7 @@
filters = {
"company": self.company,
"party_type": "Customer",
- "party": self.customer,
+ "party": [self.customer],
"report_date": today(),
"range1": 30,
"range2": 60,
@@ -605,3 +605,41 @@
for field in expected:
with self.subTest(field=field):
self.assertEqual(report_output.get(field), expected.get(field))
+
+ def test_multi_select_party_filter(self):
+ self.customer1 = self.customer
+ self.create_customer("_Test Customer 2")
+ self.customer2 = self.customer
+ self.create_customer("_Test Customer 3")
+ self.customer3 = self.customer
+
+ filters = {
+ "company": self.company,
+ "party_type": "Customer",
+ "party": [self.customer1, self.customer3],
+ "report_date": today(),
+ "range1": 30,
+ "range2": 60,
+ "range3": 90,
+ "range4": 120,
+ }
+
+ si1 = self.create_sales_invoice(no_payment_schedule=True, do_not_submit=True)
+ si1.customer = self.customer1
+ si1.save().submit()
+
+ si2 = self.create_sales_invoice(no_payment_schedule=True, do_not_submit=True)
+ si2.customer = self.customer2
+ si2.save().submit()
+
+ si3 = self.create_sales_invoice(no_payment_schedule=True, do_not_submit=True)
+ si3.customer = self.customer3
+ si3.save().submit()
+
+ # check invoice grand total and invoiced column's value for 3 payment terms
+ report = execute(filters)
+
+ expected_output = {self.customer1, self.customer3}
+ self.assertEqual(len(report[1]), 2)
+ output_for = set([x.party for x in report[1]])
+ self.assertEqual(output_for, expected_output)
diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js
index 715cd64..5ad10c7 100644
--- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js
+++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js
@@ -72,10 +72,27 @@
}
},
{
- "fieldname":"customer",
- "label": __("Customer"),
- "fieldtype": "Link",
- "options": "Customer"
+ "fieldname":"party_type",
+ "label": __("Party Type"),
+ "fieldtype": "Autocomplete",
+ options: get_party_type_options(),
+ on_change: function() {
+ frappe.query_report.set_filter_value('party', "");
+ frappe.query_report.toggle_filter_display('customer_group', frappe.query_report.get_filter_value('party_type') !== "Customer");
+ }
+ },
+ {
+ "fieldname":"party",
+ "label": __("Party"),
+ "fieldtype": "MultiSelectList",
+ get_data: function(txt) {
+ if (!frappe.query_report.filters) return;
+
+ let party_type = frappe.query_report.get_filter_value('party_type');
+ if (!party_type) return;
+
+ return frappe.db.get_link_options(party_type, txt);
+ },
},
{
"fieldname":"customer_group",
@@ -133,3 +150,15 @@
}
erpnext.utils.add_dimensions('Accounts Receivable Summary', 9);
+
+function get_party_type_options() {
+ let options = [];
+ frappe.db.get_list(
+ "Party Type", {filters:{"account_type": "Receivable"}, fields:['name']}
+ ).then((res) => {
+ res.forEach((party_type) => {
+ options.push(party_type.name);
+ });
+ });
+ return options;
+}
\ No newline at end of file
diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py
index cffc878..60274cd 100644
--- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py
+++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py
@@ -99,13 +99,11 @@
# Add all amount columns
for k in list(self.party_total[d.party]):
- if k not in ["currency", "sales_person"]:
-
- self.party_total[d.party][k] += d.get(k, 0.0)
+ if isinstance(self.party_total[d.party][k], float):
+ self.party_total[d.party][k] += d.get(k) or 0.0
# set territory, customer_group, sales person etc
self.set_party_details(d)
- self.party_total[d.party].update({"party_type": d.party_type})
def init_party_total(self, row):
self.party_total.setdefault(
@@ -124,6 +122,7 @@
"total_due": 0.0,
"future_amount": 0.0,
"sales_person": [],
+ "party_type": row.party_type,
}
),
)
@@ -133,13 +132,12 @@
for key in ("territory", "customer_group", "supplier_group"):
if row.get(key):
- self.party_total[row.party][key] = row.get(key)
-
+ self.party_total[row.party][key] = row.get(key, "")
if row.sales_person:
- self.party_total[row.party].sales_person.append(row.sales_person)
+ self.party_total[row.party].sales_person.append(row.get("sales_person", ""))
if self.filters.sales_partner:
- self.party_total[row.party]["default_sales_partner"] = row.get("default_sales_partner")
+ self.party_total[row.party]["default_sales_partner"] = row.get("default_sales_partner", "")
def get_columns(self):
self.columns = []
diff --git a/erpnext/accounts/report/profitability_analysis/profitability_analysis.js b/erpnext/accounts/report/profitability_analysis/profitability_analysis.js
index c9accef..ebd0ec1 100644
--- a/erpnext/accounts/report/profitability_analysis/profitability_analysis.js
+++ b/erpnext/accounts/report/profitability_analysis/profitability_analysis.js
@@ -112,7 +112,7 @@
"to_fiscal_year": data.fiscal_year
};
- if(data.based_on == 'cost_center'){
+ if(data.based_on == 'Cost Center'){
frappe.route_options["cost_center"] = data.account
} else {
frappe.route_options["project"] = data.account
diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js
index 962292b..5395f15 100644
--- a/erpnext/assets/doctype/asset/asset.js
+++ b/erpnext/assets/doctype/asset/asset.js
@@ -148,6 +148,15 @@
if (frm.doc.docstatus == 0) {
frm.toggle_reqd("finance_books", frm.doc.calculate_depreciation);
+
+ if (frm.doc.is_composite_asset && !frm.doc.capitalized_in) {
+ $('.primary-action').prop('hidden', true);
+ $('.form-message').text('Capitalize this asset to confirm');
+
+ frm.add_custom_button(__("Capitalize Asset"), function() {
+ frm.trigger("create_asset_capitalization");
+ });
+ }
}
},
@@ -169,7 +178,7 @@
frm.set_df_property('purchase_invoice', 'read_only', 1);
frm.set_df_property('purchase_receipt', 'read_only', 1);
}
- else if (frm.doc.is_existing_asset) {
+ else if (frm.doc.is_existing_asset || frm.doc.is_composite_asset) {
frm.toggle_reqd('purchase_receipt', 0);
frm.toggle_reqd('purchase_invoice', 0);
}
@@ -239,7 +248,7 @@
datatable.style.setStyle(`.dt-scrollable`, {'font-size': '0.75rem', 'margin-bottom': '1rem', 'margin-left': '0.35rem', 'margin-right': '0.35rem'});
datatable.style.setStyle(`.dt-header`, {'margin-left': '0.35rem', 'margin-right': '0.35rem'});
- datatable.style.setStyle(`.dt-cell--header`, {'color': 'var(--text-muted)'});
+ datatable.style.setStyle(`.dt-cell--header .dt-cell__content`, {'color': 'var(--gray-600)', 'font-size': 'var(--text-sm)'});
datatable.style.setStyle(`.dt-cell`, {'color': 'var(--text-color)'});
datatable.style.setStyle(`.dt-cell--col-1`, {'text-align': 'center'});
datatable.style.setStyle(`.dt-cell--col-2`, {'font-weight': 600});
@@ -340,7 +349,8 @@
method: "erpnext.assets.doctype.asset.asset.get_item_details",
args: {
item_code: frm.doc.item_code,
- asset_category: frm.doc.asset_category
+ asset_category: frm.doc.asset_category,
+ gross_purchase_amount: frm.doc.gross_purchase_amount
},
callback: function(r, rt) {
if(r.message) {
@@ -352,7 +362,17 @@
is_existing_asset: function(frm) {
frm.trigger("toggle_reference_doc");
- // frm.toggle_reqd("next_depreciation_date", (!frm.doc.is_existing_asset && frm.doc.calculate_depreciation));
+ },
+
+ is_composite_asset: function(frm) {
+ if(frm.doc.is_composite_asset) {
+ frm.set_value('gross_purchase_amount', 0);
+ frm.set_df_property('gross_purchase_amount', 'read_only', 1);
+ } else {
+ frm.set_df_property('gross_purchase_amount', 'read_only', 0);
+ }
+
+ frm.trigger("toggle_reference_doc");
},
make_sales_invoice: function(frm) {
@@ -402,6 +422,19 @@
});
},
+ create_asset_capitalization: function(frm) {
+ frappe.call({
+ args: {
+ "asset": frm.doc.name,
+ },
+ method: "erpnext.assets.doctype.asset.asset.create_asset_capitalization",
+ callback: function(r) {
+ var doclist = frappe.model.sync(r.message);
+ frappe.set_route("Form", doclist[0].doctype, doclist[0].name);
+ }
+ });
+ },
+
split_asset: function(frm) {
const title = __('Split Asset');
@@ -465,9 +498,11 @@
},
gross_purchase_amount: function(frm) {
- frm.doc.finance_books.forEach(d => {
- frm.events.set_depreciation_rate(frm, d);
- })
+ if (frm.doc.finance_books) {
+ frm.doc.finance_books.forEach(d => {
+ frm.events.set_depreciation_rate(frm, d);
+ })
+ }
},
purchase_receipt: (frm) => {
@@ -546,7 +581,21 @@
}
});
}
- }
+ },
+
+ set_salvage_value_percentage_or_expected_value_after_useful_life: function(frm, row, salvage_value_percentage_changed, expected_value_after_useful_life_changed) {
+ if (expected_value_after_useful_life_changed) {
+ frappe.flags.from_set_salvage_value_percentage_or_expected_value_after_useful_life = true;
+ const new_salvage_value_percentage = flt((row.expected_value_after_useful_life * 100) / frm.doc.gross_purchase_amount, precision("salvage_value_percentage", row));
+ frappe.model.set_value(row.doctype, row.name, "salvage_value_percentage", new_salvage_value_percentage);
+ frappe.flags.from_set_salvage_value_percentage_or_expected_value_after_useful_life = false;
+ } else if (salvage_value_percentage_changed) {
+ frappe.flags.from_set_salvage_value_percentage_or_expected_value_after_useful_life = true;
+ const new_expected_value_after_useful_life = flt(frm.doc.gross_purchase_amount * (row.salvage_value_percentage / 100), precision('gross_purchase_amount'));
+ frappe.model.set_value(row.doctype, row.name, "expected_value_after_useful_life", new_expected_value_after_useful_life);
+ frappe.flags.from_set_salvage_value_percentage_or_expected_value_after_useful_life = false;
+ }
+ },
});
frappe.ui.form.on('Asset Finance Book', {
@@ -557,9 +606,19 @@
expected_value_after_useful_life: function(frm, cdt, cdn) {
const row = locals[cdt][cdn];
+ if (!frappe.flags.from_set_salvage_value_percentage_or_expected_value_after_useful_life) {
+ frm.events.set_salvage_value_percentage_or_expected_value_after_useful_life(frm, row, false, true);
+ }
frm.events.set_depreciation_rate(frm, row);
},
+ salvage_value_percentage: function(frm, cdt, cdn) {
+ const row = locals[cdt][cdn];
+ if (!frappe.flags.from_set_salvage_value_percentage_or_expected_value_after_useful_life) {
+ frm.events.set_salvage_value_percentage_or_expected_value_after_useful_life(frm, row, true, false);
+ }
+ },
+
frequency_of_depreciation: function(frm, cdt, cdn) {
const row = locals[cdt][cdn];
frm.events.set_depreciation_rate(frm, row);
diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json
index befb524..c7d08e2 100644
--- a/erpnext/assets/doctype/asset/asset.json
+++ b/erpnext/assets/doctype/asset/asset.json
@@ -14,6 +14,7 @@
"asset_owner",
"asset_owner_company",
"is_existing_asset",
+ "is_composite_asset",
"supplier",
"customer",
"image",
@@ -72,7 +73,8 @@
"purchase_receipt_amount",
"default_finance_book",
"depr_entry_posting_status",
- "amended_from"
+ "amended_from",
+ "capitalized_in"
],
"fields": [
{
@@ -199,7 +201,7 @@
"fieldtype": "Date",
"label": "Purchase Date",
"read_only": 1,
- "read_only_depends_on": "eval:!doc.is_existing_asset",
+ "read_only_depends_on": "eval:!doc.is_existing_asset && !doc.is_composite_asset",
"reqd": 1
},
{
@@ -237,10 +239,12 @@
"default": "0",
"fieldname": "calculate_depreciation",
"fieldtype": "Check",
- "label": "Calculate Depreciation"
+ "label": "Calculate Depreciation",
+ "read_only_depends_on": "eval:doc.is_composite_asset && !doc.gross_purchase_amount"
},
{
"default": "0",
+ "depends_on": "eval:!doc.is_composite_asset",
"fieldname": "is_existing_asset",
"fieldtype": "Check",
"label": "Is Existing Asset"
@@ -478,7 +482,7 @@
"fieldname": "asset_quantity",
"fieldtype": "Int",
"label": "Asset Quantity",
- "read_only_depends_on": "eval:!doc.is_existing_asset"
+ "read_only_depends_on": "eval:!doc.is_existing_asset && !doc.is_composite_asset"
},
{
"fieldname": "depr_entry_posting_status",
@@ -507,6 +511,21 @@
"fieldname": "is_fully_depreciated",
"fieldtype": "Check",
"label": "Is Fully Depreciated"
+ },
+ {
+ "default": "0",
+ "depends_on": "eval:!doc.is_existing_asset",
+ "fieldname": "is_composite_asset",
+ "fieldtype": "Check",
+ "label": "Is Composite Asset"
+ },
+ {
+ "fieldname": "capitalized_in",
+ "fieldtype": "Link",
+ "hidden": 1,
+ "label": "Capitalized In",
+ "options": "Asset Capitalization",
+ "read_only": 1
}
],
"idx": 72,
@@ -545,7 +564,7 @@
"table_fieldname": "accounts"
}
],
- "modified": "2023-07-28 20:12:44.819616",
+ "modified": "2023-10-03 23:28:26.732269",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset",
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index 0dbed87..9d35634 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -198,7 +198,9 @@
self.asset_category = frappe.get_cached_value("Item", self.item_code, "asset_category")
if self.item_code and not self.get("finance_books"):
- finance_books = get_item_details(self.item_code, self.asset_category)
+ finance_books = get_item_details(
+ self.item_code, self.asset_category, self.gross_purchase_amount
+ )
self.set("finance_books", finance_books)
def validate_finance_books(self):
@@ -226,7 +228,7 @@
if not self.asset_category:
self.asset_category = frappe.get_cached_value("Item", self.item_code, "asset_category")
- if not flt(self.gross_purchase_amount):
+ if not flt(self.gross_purchase_amount) and not self.is_composite_asset:
frappe.throw(_("Gross Purchase Amount is mandatory"), frappe.MandatoryError)
if is_cwip_accounting_enabled(self.asset_category):
@@ -767,6 +769,15 @@
@frappe.whitelist()
+def create_asset_capitalization(asset):
+ asset_capitalization = frappe.new_doc("Asset Capitalization")
+ asset_capitalization.update(
+ {"target_asset": asset, "capitalization_method": "Choose a WIP composite asset"}
+ )
+ return asset_capitalization
+
+
+@frappe.whitelist()
def create_asset_value_adjustment(asset, asset_category, company):
asset_value_adjustment = frappe.new_doc("Asset Value Adjustment")
asset_value_adjustment.update(
@@ -797,7 +808,7 @@
@frappe.whitelist()
-def get_item_details(item_code, asset_category):
+def get_item_details(item_code, asset_category, gross_purchase_amount):
asset_category_doc = frappe.get_doc("Asset Category", asset_category)
books = []
for d in asset_category_doc.finance_books:
@@ -807,7 +818,11 @@
"depreciation_method": d.depreciation_method,
"total_number_of_depreciations": d.total_number_of_depreciations,
"frequency_of_depreciation": d.frequency_of_depreciation,
- "start_date": nowdate(),
+ "daily_depreciation": d.daily_depreciation,
+ "salvage_value_percentage": d.salvage_value_percentage,
+ "expected_value_after_useful_life": flt(gross_purchase_amount)
+ * flt(d.salvage_value_percentage / 100),
+ "depreciation_start_date": d.depreciation_start_date or nowdate(),
}
)
diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py
index 39fcb21..88ef69c 100644
--- a/erpnext/assets/doctype/asset/test_asset.py
+++ b/erpnext/assets/doctype/asset/test_asset.py
@@ -1744,6 +1744,7 @@
"location": args.location or "Test Location",
"asset_owner": args.asset_owner or "Company",
"is_existing_asset": args.is_existing_asset or 1,
+ "is_composite_asset": args.is_composite_asset or 0,
"asset_quantity": args.get("asset_quantity") or 1,
"depr_entry_posting_status": args.depr_entry_posting_status or "",
}
diff --git a/erpnext/assets/doctype/asset_activity/asset_activity.json b/erpnext/assets/doctype/asset_activity/asset_activity.json
index 476fb27..00992e2 100644
--- a/erpnext/assets/doctype/asset_activity/asset_activity.json
+++ b/erpnext/assets/doctype/asset_activity/asset_activity.json
@@ -75,13 +75,14 @@
"in_create": 1,
"index_web_pages_for_search": 1,
"links": [],
- "modified": "2023-08-01 11:09:52.584482",
+ "modified": "2023-09-29 15:56:17.608643",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset Activity",
"owner": "Administrator",
"permissions": [
{
+ "delete": 1,
"email": 1,
"read": 1,
"report": 1,
@@ -89,6 +90,7 @@
"share": 1
},
{
+ "delete": 1,
"email": 1,
"read": 1,
"report": 1,
@@ -96,6 +98,7 @@
"share": 1
},
{
+ "delete": 1,
"email": 1,
"read": 1,
"report": 1,
diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js
index 6d55d77..be78d9e 100644
--- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js
+++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js
@@ -16,9 +16,15 @@
refresh() {
this.show_general_ledger();
+
if ((this.frm.doc.stock_items && this.frm.doc.stock_items.length) || !this.frm.doc.target_is_fixed_asset) {
this.show_stock_ledger();
}
+
+ if (this.frm.doc.stock_items && !this.frm.doc.stock_items.length && this.frm.doc.target_asset && this.frm.doc.capitalization_method === "Choose a WIP composite asset") {
+ this.set_consumed_stock_items_tagged_to_wip_composite_asset(this.frm.doc.target_asset);
+ this.get_target_asset_details();
+ }
}
setup_queries() {
@@ -35,18 +41,9 @@
});
me.frm.set_query("target_asset", function() {
- var filters = {};
-
- if (me.frm.doc.target_item_code) {
- filters['item_code'] = me.frm.doc.target_item_code;
- }
-
- filters['status'] = ["not in", ["Draft", "Scrapped", "Sold", "Capitalized", "Decapitalized"]];
- filters['docstatus'] = 1;
-
return {
- filters: filters
- };
+ filters: {'is_composite_asset': 1, 'docstatus': 0 }
+ }
});
me.frm.set_query("asset", "asset_items", function() {
@@ -128,6 +125,39 @@
return this.get_target_item_details();
}
+ target_asset() {
+ if (this.frm.doc.target_asset && this.frm.doc.capitalization_method === "Choose a WIP composite asset") {
+ this.set_consumed_stock_items_tagged_to_wip_composite_asset(this.frm.doc.target_asset);
+ this.get_target_asset_details();
+ }
+ }
+
+ set_consumed_stock_items_tagged_to_wip_composite_asset(asset) {
+ var me = this;
+
+ if (asset) {
+ return me.frm.call({
+ method: "erpnext.assets.doctype.asset_capitalization.asset_capitalization.get_items_tagged_to_wip_composite_asset",
+ args: {
+ asset: asset,
+ },
+ callback: function (r) {
+ if (!r.exc && r.message) {
+ me.frm.clear_table("stock_items");
+
+ for (let item of r.message) {
+ me.frm.add_child("stock_items", item);
+ }
+
+ refresh_field("stock_items");
+
+ me.calculate_totals();
+ }
+ }
+ });
+ }
+ }
+
item_code(doc, cdt, cdn) {
var row = frappe.get_doc(cdt, cdn);
if (cdt === "Asset Capitalization Stock Item") {
@@ -242,6 +272,26 @@
}
}
+ get_target_asset_details() {
+ var me = this;
+
+ if (me.frm.doc.target_asset) {
+ return me.frm.call({
+ method: "erpnext.assets.doctype.asset_capitalization.asset_capitalization.get_target_asset_details",
+ child: me.frm.doc,
+ args: {
+ asset: me.frm.doc.target_asset,
+ company: me.frm.doc.company,
+ },
+ callback: function (r) {
+ if (!r.exc) {
+ me.frm.refresh_fields();
+ }
+ }
+ });
+ }
+ }
+
get_consumed_stock_item_details(row) {
var me = this;
diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.json b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.json
index 04b0c4e..9ddc442 100644
--- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.json
+++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.json
@@ -8,24 +8,25 @@
"engine": "InnoDB",
"field_order": [
"title",
+ "company",
"naming_series",
"entry_type",
- "target_item_code",
- "target_asset",
"target_item_name",
"target_is_fixed_asset",
"target_has_batch_no",
"target_has_serial_no",
"column_break_9",
- "target_asset_name",
+ "capitalization_method",
+ "target_item_code",
"target_asset_location",
+ "target_asset",
+ "target_asset_name",
"target_warehouse",
"target_qty",
"target_stock_uom",
"target_batch_no",
"target_serial_no",
"column_break_5",
- "company",
"finance_book",
"posting_date",
"posting_time",
@@ -57,12 +58,13 @@
"label": "Title"
},
{
+ "depends_on": "eval:(doc.target_item_code && !doc.__islocal && doc.capitalization_method !== 'Choose a WIP composite asset') || ((doc.entry_type=='Capitalization' && doc.capitalization_method=='Create a new composite asset') || doc.entry_type=='Decapitalization')",
"fieldname": "target_item_code",
"fieldtype": "Link",
"in_standard_filter": 1,
"label": "Target Item Code",
- "options": "Item",
- "reqd": 1
+ "mandatory_depends_on": "eval:(doc.entry_type=='Capitalization' && doc.capitalization_method=='Create a new composite asset') || doc.entry_type=='Decapitalization'",
+ "options": "Item"
},
{
"depends_on": "eval:doc.target_item_code && doc.target_item_name != doc.target_item_code",
@@ -86,16 +88,18 @@
"fieldtype": "Column Break"
},
{
+ "depends_on": "eval:(doc.target_asset && !doc.__islocal) || (doc.entry_type=='Capitalization' && doc.capitalization_method=='Choose a WIP composite asset')",
"fieldname": "target_asset",
"fieldtype": "Link",
"in_standard_filter": 1,
"label": "Target Asset",
+ "mandatory_depends_on": "eval:doc.entry_type=='Capitalization' && doc.capitalization_method=='Choose a WIP composite asset'",
"no_copy": 1,
"options": "Asset",
- "read_only": 1
+ "read_only_depends_on": "eval:(doc.entry_type=='Decapitalization') || (doc.entry_type=='Capitalization' && doc.capitalization_method=='Create a new composite asset')"
},
{
- "depends_on": "eval:doc.entry_type=='Capitalization'",
+ "depends_on": "eval:(doc.target_asset_name && !doc.__islocal) || (doc.target_asset && doc.entry_type=='Capitalization' && doc.capitalization_method=='Choose a WIP composite asset')",
"fetch_from": "target_asset.asset_name",
"fieldname": "target_asset_name",
"fieldtype": "Data",
@@ -186,12 +190,14 @@
},
{
"default": "1",
+ "depends_on": "eval:doc.entry_type=='Decapitalization'",
"fieldname": "target_qty",
"fieldtype": "Float",
"label": "Target Qty",
"read_only_depends_on": "eval:doc.entry_type=='Capitalization'"
},
{
+ "depends_on": "eval:doc.entry_type=='Decapitalization'",
"fetch_from": "target_item_code.stock_uom",
"fieldname": "target_stock_uom",
"fieldtype": "Link",
@@ -331,18 +337,26 @@
"read_only": 1
},
{
- "depends_on": "eval:doc.entry_type=='Capitalization'",
+ "depends_on": "eval:doc.entry_type=='Capitalization' && doc.capitalization_method=='Create a new composite asset'",
"fieldname": "target_asset_location",
"fieldtype": "Link",
"label": "Target Asset Location",
- "mandatory_depends_on": "eval:doc.entry_type=='Capitalization'",
+ "mandatory_depends_on": "eval:doc.entry_type=='Capitalization' && doc.capitalization_method=='Create a new composite asset'",
"options": "Location"
+ },
+ {
+ "depends_on": "eval:doc.entry_type=='Capitalization'",
+ "fieldname": "capitalization_method",
+ "fieldtype": "Select",
+ "label": "Capitalization Method",
+ "mandatory_depends_on": "eval:doc.entry_type=='Capitalization'",
+ "options": "\nCreate a new composite asset\nChoose a WIP composite asset"
}
],
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2023-06-22 14:17:07.995120",
+ "modified": "2023-10-03 22:55:59.461456",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset Capitalization",
diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
index 662e4b9..0d6f6b4 100644
--- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
+++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
@@ -53,6 +53,7 @@
self.validate_posting_time()
self.set_missing_values(for_validate=True)
self.validate_target_item()
+ self.validate_target_asset()
self.validate_consumed_stock_item()
self.validate_consumed_asset_item()
self.validate_service_item()
@@ -67,12 +68,12 @@
def before_submit(self):
self.validate_source_mandatory()
- if self.entry_type == "Capitalization":
- self.create_target_asset()
+ self.create_target_asset()
def on_submit(self):
self.update_stock_ledger()
self.make_gl_entries()
+ self.update_target_asset()
def on_cancel(self):
self.ignore_linked_doctypes = (
@@ -94,6 +95,11 @@
if self.meta.has_field(k) and (not self.get(k) or k in force_fields):
self.set(k, v)
+ target_asset_details = get_target_asset_details(self.target_asset, self.company)
+ for k, v in target_asset_details.items():
+ if self.meta.has_field(k) and (not self.get(k) or k in force_fields):
+ self.set(k, v)
+
for d in self.stock_items:
args = self.as_dict()
args.update(d.as_dict())
@@ -155,6 +161,33 @@
self.validate_item(target_item)
+ def validate_target_asset(self):
+ if self.target_asset:
+ target_asset = self.get_asset_for_validation(self.target_asset)
+
+ if not target_asset.is_composite_asset:
+ frappe.throw(_("Target Asset {0} needs to be composite asset").format(target_asset.name))
+
+ if target_asset.item_code != self.target_item_code:
+ frappe.throw(
+ _("Asset {0} does not belong to Item {1}").format(self.target_asset, self.target_item_code)
+ )
+
+ if target_asset.status in ("Scrapped", "Sold", "Capitalized", "Decapitalized"):
+ frappe.throw(
+ _("Target Asset {0} cannot be {1}").format(target_asset.name, target_asset.status)
+ )
+
+ if target_asset.docstatus == 1:
+ frappe.throw(_("Target Asset {0} cannot be submitted").format(target_asset.name))
+ elif target_asset.docstatus == 2:
+ frappe.throw(_("Target Asset {0} cannot be cancelled").format(target_asset.name))
+
+ if target_asset.company != self.company:
+ frappe.throw(
+ _("Target Asset {0} does not belong to company {1}").format(target_asset.name, self.company)
+ )
+
def validate_consumed_stock_item(self):
for d in self.stock_items:
if d.item_code:
@@ -179,7 +212,23 @@
)
asset = self.get_asset_for_validation(d.asset)
- self.validate_asset(asset)
+
+ if asset.status in ("Draft", "Scrapped", "Sold", "Capitalized", "Decapitalized"):
+ frappe.throw(
+ _("Row #{0}: Consumed Asset {1} cannot be {2}").format(d.idx, asset.name, asset.status)
+ )
+
+ if asset.docstatus == 0:
+ frappe.throw(_("Row #{0}: Consumed Asset {1} cannot be Draft").format(d.idx, asset.name))
+ elif asset.docstatus == 2:
+ frappe.throw(_("Row #{0}: Consumed Asset {1} cannot be cancelled").format(d.idx, asset.name))
+
+ if asset.company != self.company:
+ frappe.throw(
+ _("Row #{0}: Consumed Asset {1} does not belong to company {2}").format(
+ d.idx, asset.name, self.company
+ )
+ )
def validate_service_item(self):
for d in self.service_items:
@@ -214,21 +263,12 @@
def get_asset_for_validation(self, asset):
return frappe.db.get_value(
- "Asset", asset, ["name", "item_code", "company", "status", "docstatus"], as_dict=1
+ "Asset",
+ asset,
+ ["name", "item_code", "company", "status", "docstatus", "is_composite_asset"],
+ as_dict=1,
)
- def validate_asset(self, asset):
- if asset.status in ("Draft", "Scrapped", "Sold", "Capitalized", "Decapitalized"):
- frappe.throw(_("Asset {0} is {1}").format(asset.name, asset.status))
-
- if asset.docstatus == 0:
- frappe.throw(_("Asset {0} is Draft").format(asset.name))
- if asset.docstatus == 2:
- frappe.throw(_("Asset {0} is cancelled").format(asset.name))
-
- if asset.company != self.company:
- frappe.throw(_("Asset {0} does not belong to company {1}").format(asset.name, self.company))
-
@frappe.whitelist()
def set_warehouse_details(self):
for d in self.get("stock_items"):
@@ -495,16 +535,25 @@
)
def create_target_asset(self):
+ if (
+ self.entry_type != "Capitalization"
+ or self.capitalization_method != "Create a new composite asset"
+ ):
+ return
+
total_target_asset_value = flt(self.total_value, self.precision("total_value"))
+
asset_doc = frappe.new_doc("Asset")
asset_doc.company = self.company
asset_doc.item_code = self.target_item_code
- asset_doc.is_existing_asset = 1
+ asset_doc.is_composite_asset = 1
asset_doc.location = self.target_asset_location
asset_doc.available_for_use_date = self.posting_date
asset_doc.purchase_date = self.posting_date
asset_doc.gross_purchase_amount = total_target_asset_value
asset_doc.purchase_receipt_amount = total_target_asset_value
+ asset_doc.purchase_receipt_amount = total_target_asset_value
+ asset_doc.capitalized_in = self.name
asset_doc.flags.ignore_validate = True
asset_doc.flags.asset_created_via_asset_capitalization = True
asset_doc.insert()
@@ -528,6 +577,28 @@
).format(get_link_to_form("Asset", asset_doc.name))
)
+ def update_target_asset(self):
+ if (
+ self.entry_type != "Capitalization"
+ or self.capitalization_method != "Choose a WIP composite asset"
+ ):
+ return
+
+ total_target_asset_value = flt(self.total_value, self.precision("total_value"))
+
+ asset_doc = frappe.get_doc("Asset", self.target_asset)
+ asset_doc.gross_purchase_amount = total_target_asset_value
+ asset_doc.purchase_receipt_amount = total_target_asset_value
+ asset_doc.capitalized_in = self.name
+ asset_doc.flags.ignore_validate = True
+ asset_doc.save()
+
+ frappe.msgprint(
+ _(
+ "Asset {0} has been updated. Please set the depreciation details if any and submit it."
+ ).format(get_link_to_form("Asset", asset_doc.name))
+ )
+
def restore_consumed_asset_items(self):
for item in self.asset_items:
asset = frappe.get_doc("Asset", item.asset)
@@ -613,6 +684,33 @@
@frappe.whitelist()
+def get_target_asset_details(asset=None, company=None):
+ out = frappe._dict()
+
+ # Get Asset Details
+ asset_details = frappe._dict()
+ if asset:
+ asset_details = frappe.db.get_value("Asset", asset, ["asset_name", "item_code"], as_dict=1)
+ if not asset_details:
+ frappe.throw(_("Asset {0} does not exist").format(asset))
+
+ # Re-set item code from Asset
+ out.target_item_code = asset_details.item_code
+
+ # Set Asset Details
+ out.asset_name = asset_details.asset_name
+
+ if asset_details.item_code:
+ out.target_fixed_asset_account = get_asset_category_account(
+ "fixed_asset_account", item=asset_details.item_code, company=company
+ )
+ else:
+ out.target_fixed_asset_account = None
+
+ return out
+
+
+@frappe.whitelist()
def get_consumed_stock_item_details(args):
if isinstance(args, str):
args = json.loads(args)
@@ -760,3 +858,30 @@
)
return out
+
+
+@frappe.whitelist()
+def get_items_tagged_to_wip_composite_asset(asset):
+ fields = [
+ "item_code",
+ "item_name",
+ "batch_no",
+ "serial_no",
+ "stock_qty",
+ "stock_uom",
+ "warehouse",
+ "cost_center",
+ "qty",
+ "valuation_rate",
+ "amount",
+ ]
+
+ pi_items = frappe.get_all(
+ "Purchase Invoice Item", filters={"wip_composite_asset": asset}, fields=fields
+ )
+
+ pr_items = frappe.get_all(
+ "Purchase Receipt Item", filters={"wip_composite_asset": asset}, fields=fields
+ )
+
+ return pi_items + pr_items
diff --git a/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py
index 6e0a685..ac7c90d 100644
--- a/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py
+++ b/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py
@@ -58,6 +58,7 @@
# Create and submit Asset Captitalization
asset_capitalization = create_asset_capitalization(
entry_type="Capitalization",
+ capitalization_method="Create a new composite asset",
target_item_code="Macbook Pro",
target_asset_location="Test Location",
stock_qty=stock_qty,
@@ -147,6 +148,7 @@
# Create and submit Asset Captitalization
asset_capitalization = create_asset_capitalization(
entry_type="Capitalization",
+ capitalization_method="Create a new composite asset",
target_item_code="Macbook Pro",
target_asset_location="Test Location",
stock_qty=stock_qty,
@@ -211,6 +213,77 @@
self.assertFalse(get_actual_gle_dict(asset_capitalization.name))
self.assertFalse(get_actual_sle_dict(asset_capitalization.name))
+ def test_capitalization_with_wip_composite_asset(self):
+ company = "_Test Company with perpetual inventory"
+ set_depreciation_settings_in_company(company=company)
+
+ stock_rate = 1000
+ stock_qty = 2
+ stock_amount = 2000
+
+ total_amount = 2000
+
+ wip_composite_asset = create_asset(
+ asset_name="Asset Capitalization WIP Composite Asset",
+ is_composite_asset=1,
+ warehouse="Stores - TCP1",
+ company=company,
+ )
+
+ # Create and submit Asset Captitalization
+ asset_capitalization = create_asset_capitalization(
+ entry_type="Capitalization",
+ capitalization_method="Choose a WIP composite asset",
+ target_asset=wip_composite_asset.name,
+ target_asset_location="Test Location",
+ stock_qty=stock_qty,
+ stock_rate=stock_rate,
+ service_expense_account="Expenses Included In Asset Valuation - TCP1",
+ company=company,
+ submit=1,
+ )
+
+ # Test Asset Capitalization values
+ self.assertEqual(asset_capitalization.entry_type, "Capitalization")
+ self.assertEqual(asset_capitalization.capitalization_method, "Choose a WIP composite asset")
+ self.assertEqual(asset_capitalization.target_qty, 1)
+
+ self.assertEqual(asset_capitalization.stock_items[0].valuation_rate, stock_rate)
+ self.assertEqual(asset_capitalization.stock_items[0].amount, stock_amount)
+ self.assertEqual(asset_capitalization.stock_items_total, stock_amount)
+
+ self.assertEqual(asset_capitalization.total_value, total_amount)
+ self.assertEqual(asset_capitalization.target_incoming_rate, total_amount)
+
+ # Test Target Asset values
+ target_asset = frappe.get_doc("Asset", asset_capitalization.target_asset)
+ self.assertEqual(target_asset.gross_purchase_amount, total_amount)
+ self.assertEqual(target_asset.purchase_receipt_amount, total_amount)
+
+ # Test General Ledger Entries
+ expected_gle = {
+ "_Test Fixed Asset - TCP1": 2000,
+ "_Test Warehouse - TCP1": -2000,
+ }
+ actual_gle = get_actual_gle_dict(asset_capitalization.name)
+
+ self.assertEqual(actual_gle, expected_gle)
+
+ # Test Stock Ledger Entries
+ expected_sle = {
+ ("Capitalization Source Stock Item", "_Test Warehouse - TCP1"): {
+ "actual_qty": -stock_qty,
+ "stock_value_difference": -stock_amount,
+ }
+ }
+ actual_sle = get_actual_sle_dict(asset_capitalization.name)
+ self.assertEqual(actual_sle, expected_sle)
+
+ # Cancel Asset Capitalization and make test entries and status are reversed
+ asset_capitalization.cancel()
+ self.assertFalse(get_actual_gle_dict(asset_capitalization.name))
+ self.assertFalse(get_actual_sle_dict(asset_capitalization.name))
+
def test_decapitalization_with_depreciation(self):
# Variables
purchase_date = "2020-01-01"
@@ -347,6 +420,7 @@
asset_capitalization.update(
{
"entry_type": args.entry_type or "Capitalization",
+ "capitalization_method": args.capitalization_method or None,
"company": company,
"posting_date": args.posting_date or now.strftime("%Y-%m-%d"),
"posting_time": args.posting_time or now.strftime("%H:%M:%S.%f"),
diff --git a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json
index 4121302..2c27dc9 100644
--- a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json
+++ b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json
@@ -12,6 +12,7 @@
"column_break_5",
"frequency_of_depreciation",
"depreciation_start_date",
+ "salvage_value_percentage",
"expected_value_after_useful_life",
"value_after_depreciation",
"rate_of_depreciation"
@@ -91,12 +92,17 @@
"fieldname": "daily_depreciation",
"fieldtype": "Check",
"label": "Daily Depreciation"
+ },
+ {
+ "fieldname": "salvage_value_percentage",
+ "fieldtype": "Percent",
+ "label": "Salvage Value Percentage"
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2023-08-10 22:10:36.576199",
+ "modified": "2023-09-29 15:39:52.740594",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset Finance Book",
diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py
index 7e95cb2..9c2b8bc 100644
--- a/erpnext/assets/doctype/asset_repair/asset_repair.py
+++ b/erpnext/assets/doctype/asset_repair/asset_repair.py
@@ -177,7 +177,7 @@
"item_code": stock_item.item_code,
"qty": stock_item.consumed_quantity,
"basic_rate": stock_item.valuation_rate,
- "serial_no": stock_item.serial_and_batch_bundle,
+ "serial_and_batch_bundle": stock_item.serial_and_batch_bundle,
"cost_center": self.cost_center,
"project": self.project,
},
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json
index 5b5cc2b..f74df66 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.json
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.json
@@ -477,6 +477,7 @@
"depends_on": "eval:doc.is_subcontracted",
"fieldname": "supplier_warehouse",
"fieldtype": "Link",
+ "ignore_user_permissions": 1,
"label": "Supplier Warehouse",
"options": "Warehouse"
},
@@ -1274,7 +1275,7 @@
"idx": 105,
"is_submittable": 1,
"links": [],
- "modified": "2023-09-13 16:21:07.361700",
+ "modified": "2023-10-01 20:58:07.851037",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order",
diff --git a/erpnext/buying/doctype/supplier/supplier.js b/erpnext/buying/doctype/supplier/supplier.js
index 08dc44c..70d2782 100644
--- a/erpnext/buying/doctype/supplier/supplier.js
+++ b/erpnext/buying/doctype/supplier/supplier.js
@@ -88,7 +88,7 @@
}, __("View"));
frm.add_custom_button(__('Accounts Payable'), function () {
- frappe.set_route('query-report', 'Accounts Payable', { supplier: frm.doc.name });
+ frappe.set_route('query-report', 'Accounts Payable', { party_type: "Supplier", party: frm.doc.name });
}, __("View"));
frm.add_custom_button(__('Bank Account'), function () {
diff --git a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py
index a728290..01ff28d 100644
--- a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py
+++ b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py
@@ -35,8 +35,12 @@
sq_item.parent,
sq_item.item_code,
sq_item.qty,
+ sq.currency,
sq_item.stock_qty,
sq_item.amount,
+ sq_item.base_rate,
+ sq_item.base_amount,
+ sq.price_list_currency,
sq_item.uom,
sq_item.stock_uom,
sq_item.request_for_quotation,
@@ -105,7 +109,11 @@
"qty": data.get("qty"),
"price": flt(data.get("amount") * exchange_rate, float_precision),
"uom": data.get("uom"),
+ "price_list_currency": data.get("price_list_currency"),
+ "currency": data.get("currency"),
"stock_uom": data.get("stock_uom"),
+ "base_amount": flt(data.get("base_amount"), float_precision),
+ "base_rate": flt(data.get("base_rate"), float_precision),
"request_for_quotation": data.get("request_for_quotation"),
"valid_till": data.get("valid_till"),
"lead_time_days": data.get("lead_time_days"),
@@ -183,6 +191,8 @@
def get_columns(filters):
+ currency = frappe.get_cached_value("Company", filters.get("company"), "default_currency")
+
group_by_columns = [
{
"fieldname": "supplier_name",
@@ -204,10 +214,17 @@
{"fieldname": "uom", "label": _("UOM"), "fieldtype": "Link", "options": "UOM", "width": 90},
{"fieldname": "qty", "label": _("Quantity"), "fieldtype": "Float", "width": 80},
{
+ "fieldname": "currency",
+ "label": _("Currency"),
+ "fieldtype": "Link",
+ "options": "Currency",
+ "width": 110,
+ },
+ {
"fieldname": "price",
"label": _("Price"),
"fieldtype": "Currency",
- "options": "Company:company:default_currency",
+ "options": "currency",
"width": 110,
},
{
@@ -221,10 +238,24 @@
"fieldname": "price_per_unit",
"label": _("Price per Unit (Stock UOM)"),
"fieldtype": "Currency",
- "options": "Company:company:default_currency",
+ "options": "currency",
"width": 120,
},
{
+ "fieldname": "base_amount",
+ "label": _("Price ({0})").format(currency),
+ "fieldtype": "Currency",
+ "options": "price_list_currency",
+ "width": 180,
+ },
+ {
+ "fieldname": "base_rate",
+ "label": _("Price Per Unit ({0})").format(currency),
+ "fieldtype": "Currency",
+ "options": "price_list_currency",
+ "width": 180,
+ },
+ {
"fieldname": "quotation",
"label": _("Supplier Quotation"),
"fieldtype": "Link",
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 6efa09b..6812940 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -1491,7 +1491,7 @@
"account": self.additional_discount_account,
"against": supplier_or_customer,
dr_or_cr: self.base_discount_amount,
- "cost_center": self.cost_center,
+ "cost_center": self.cost_center or erpnext.get_default_cost_center(self.company),
},
item=self,
)
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json
index 0d0fd5e..4a00416 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.json
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json
@@ -228,7 +228,7 @@
},
{
"default": "0",
- "description": "If enabled, the system won't create material requests for the available items.",
+ "description": "If enabled, the system will create material requests even if the stock exists in the 'Raw Materials Warehouse'.",
"fieldname": "ignore_existing_ordered_qty",
"fieldtype": "Check",
"label": "Ignore Available Stock"
@@ -422,7 +422,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2023-07-28 13:37:43.926686",
+ "modified": "2023-09-29 11:41:03.246059",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Production Plan",
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index e88b791..deef020 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -8,6 +8,7 @@
import frappe
from frappe import _, msgprint
from frappe.model.document import Document
+from frappe.query_builder import Case
from frappe.query_builder.functions import IfNull, Sum
from frappe.utils import (
add_days,
@@ -1509,6 +1510,10 @@
def get_materials_from_other_locations(item, warehouses, new_mr_items, company):
from erpnext.stock.doctype.pick_list.pick_list import get_available_item_locations
+ stock_uom, purchase_uom = frappe.db.get_value(
+ "Item", item.get("item_code"), ["stock_uom", "purchase_uom"]
+ )
+
locations = get_available_item_locations(
item.get("item_code"), warehouses, item.get("quantity"), company, ignore_validation=True
)
@@ -1519,6 +1524,10 @@
if required_qty <= 0:
return
+ conversion_factor = 1.0
+ if purchase_uom != stock_uom and purchase_uom == item["uom"]:
+ conversion_factor = get_uom_conversion_factor(item["item_code"], item["uom"])
+
new_dict = copy.deepcopy(item)
quantity = required_qty if d.get("qty") > required_qty else d.get("qty")
@@ -1531,25 +1540,14 @@
}
)
- required_qty -= quantity
+ required_qty -= quantity / conversion_factor
new_mr_items.append(new_dict)
# raise purchase request for remaining qty
- if required_qty:
- stock_uom, purchase_uom = frappe.db.get_value(
- "Item", item["item_code"], ["stock_uom", "purchase_uom"]
- )
- if purchase_uom != stock_uom and purchase_uom == item["uom"]:
- conversion_factor = get_uom_conversion_factor(item["item_code"], item["uom"])
- if not (conversion_factor or frappe.flags.show_qty_in_stock_uom):
- frappe.throw(
- _("UOM Conversion factor ({0} -> {1}) not found for item: {2}").format(
- purchase_uom, stock_uom, item["item_code"]
- )
- )
-
- required_qty = required_qty / conversion_factor
+ precision = frappe.get_precision("Material Request Plan Item", "quantity")
+ if flt(required_qty, precision) > 0:
+ required_qty = required_qty
if frappe.db.get_value("UOM", purchase_uom, "must_be_whole_number"):
required_qty = ceil(required_qty)
@@ -1620,18 +1618,33 @@
table = frappe.qb.DocType("Production Plan")
child = frappe.qb.DocType("Material Request Plan Item")
+ completed_production_plans = get_completed_production_plans()
+
+ case = Case()
query = (
frappe.qb.from_(table)
.inner_join(child)
.on(table.name == child.parent)
- .select(Sum(child.quantity * IfNull(child.conversion_factor, 1.0)))
+ .select(
+ Sum(
+ child.quantity
+ * IfNull(
+ case.when(child.material_request_type == "Purchase", child.conversion_factor).else_(1.0), 1.0
+ )
+ )
+ )
.where(
(table.docstatus == 1)
& (child.item_code == item_code)
& (child.warehouse == warehouse)
& (table.status.notin(["Completed", "Closed"]))
)
- ).run()
+ )
+
+ if completed_production_plans:
+ query = query.where(table.name.notin(completed_production_plans))
+
+ query = query.run()
if not query:
return 0.0
@@ -1639,7 +1652,9 @@
reserved_qty_for_production_plan = flt(query[0][0])
reserved_qty_for_production = flt(
- get_reserved_qty_for_production(item_code, warehouse, check_production_plan=True)
+ get_reserved_qty_for_production(
+ item_code, warehouse, completed_production_plans, check_production_plan=True
+ )
)
if reserved_qty_for_production > reserved_qty_for_production_plan:
@@ -1648,6 +1663,25 @@
return reserved_qty_for_production_plan - reserved_qty_for_production
+def get_completed_production_plans():
+ table = frappe.qb.DocType("Production Plan")
+ child = frappe.qb.DocType("Production Plan Item")
+
+ query = (
+ frappe.qb.from_(table)
+ .inner_join(child)
+ .on(table.name == child.parent)
+ .select(table.name)
+ .where(
+ (table.docstatus == 1)
+ & (table.status.notin(["Completed", "Closed"]))
+ & (child.ordered_qty >= child.planned_qty)
+ )
+ ).run(as_dict=True)
+
+ return list(set([d.name for d in query]))
+
+
def get_raw_materials_of_sub_assembly_items(
item_details, company, bom_no, include_non_stock_items, sub_assembly_items, planned_qty=1
):
diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
index 2348d2b..4ff9d29 100644
--- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
@@ -6,6 +6,7 @@
from erpnext.controllers.item_variant import create_variant
from erpnext.manufacturing.doctype.production_plan.production_plan import (
+ get_completed_production_plans,
get_items_for_material_requests,
get_sales_orders,
get_warehouse_list,
@@ -1103,6 +1104,49 @@
self.assertEqual(after_qty, before_qty)
+ def test_resered_qty_for_production_plan_for_less_rm_qty(self):
+ from erpnext.stock.utils import get_or_make_bin
+
+ bin_name = get_or_make_bin("Raw Material Item 1", "_Test Warehouse - _TC")
+ before_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan"))
+
+ pln = create_production_plan(item_code="Test Production Item 1", planned_qty=10)
+
+ bin_name = get_or_make_bin("Raw Material Item 1", "_Test Warehouse - _TC")
+ after_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan"))
+
+ self.assertEqual(after_qty - before_qty, 10)
+
+ pln.make_work_order()
+
+ plans = []
+ for row in frappe.get_all("Work Order", filters={"production_plan": pln.name}, fields=["name"]):
+ wo_doc = frappe.get_doc("Work Order", row.name)
+ wo_doc.source_warehouse = "_Test Warehouse - _TC"
+ wo_doc.wip_warehouse = "_Test Warehouse 1 - _TC"
+ wo_doc.fg_warehouse = "_Test Warehouse - _TC"
+ for d in wo_doc.required_items:
+ d.source_warehouse = "_Test Warehouse - _TC"
+ d.required_qty -= 5
+ make_stock_entry(
+ item_code=d.item_code,
+ qty=d.required_qty,
+ rate=100,
+ target="_Test Warehouse - _TC",
+ )
+
+ wo_doc.submit()
+ plans.append(pln.name)
+
+ bin_name = get_or_make_bin("Raw Material Item 1", "_Test Warehouse - _TC")
+ after_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan"))
+
+ self.assertEqual(after_qty, before_qty)
+
+ completed_plans = get_completed_production_plans()
+ for plan in plans:
+ self.assertTrue(plan in completed_plans)
+
def test_resered_qty_for_production_plan_for_material_requests_with_multi_UOM(self):
from erpnext.stock.utils import get_or_make_bin
@@ -1230,6 +1274,64 @@
if row.item_code == "SubAssembly2 For SUB Test":
self.assertEqual(row.quantity, 10)
+ def test_transfer_and_purchase_mrp_for_purchase_uom(self):
+ from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom
+ from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
+
+ bom_tree = {
+ "Test FG Item INK PEN": {
+ "Test RM Item INK": {},
+ }
+ }
+
+ parent_bom = create_nested_bom(bom_tree, prefix="")
+ if not frappe.db.exists("UOM Conversion Detail", {"parent": "Test RM Item INK", "uom": "Kg"}):
+ doc = frappe.get_doc("Item", "Test RM Item INK")
+ doc.purchase_uom = "Kg"
+ doc.append("uoms", {"uom": "Kg", "conversion_factor": 0.5})
+ doc.save()
+
+ wh1 = create_warehouse("PNE Warehouse", company="_Test Company")
+ wh2 = create_warehouse("MBE Warehouse", company="_Test Company")
+ mrp_warhouse = create_warehouse("MRPBE Warehouse", company="_Test Company")
+
+ make_stock_entry(
+ item_code="Test RM Item INK",
+ qty=2,
+ rate=100,
+ target=wh1,
+ )
+
+ make_stock_entry(
+ item_code="Test RM Item INK",
+ qty=2,
+ rate=100,
+ target=wh2,
+ )
+
+ plan = create_production_plan(
+ item_code=parent_bom.item,
+ planned_qty=10,
+ do_not_submit=1,
+ warehouse="_Test Warehouse - _TC",
+ )
+
+ plan.for_warehouse = mrp_warhouse
+
+ items = get_items_for_material_requests(
+ plan.as_dict(), warehouses=[{"warehouse": wh1}, {"warehouse": wh2}]
+ )
+
+ for row in items:
+ row = frappe._dict(row)
+ if row.material_request_type == "Material Transfer":
+ self.assertTrue(row.from_warehouse in [wh1, wh2])
+ self.assertEqual(row.quantity, 2)
+
+ if row.material_request_type == "Purchase":
+ self.assertTrue(row.warehouse == mrp_warhouse)
+ self.assertEqual(row.quantity, 12)
+
def create_production_plan(**args):
"""
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index d8fc220..3dc33ac 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -358,10 +358,10 @@
else:
self.update_work_order_qty_in_so()
+ self.update_ordered_qty()
self.update_reserved_qty_for_production()
self.update_completed_qty_in_material_request()
self.update_planned_qty()
- self.update_ordered_qty()
self.create_job_card()
def on_cancel(self):
@@ -1513,7 +1513,10 @@
def get_reserved_qty_for_production(
- item_code: str, warehouse: str, check_production_plan: bool = False
+ item_code: str,
+ warehouse: str,
+ completed_production_plans: list = None,
+ check_production_plan: bool = False,
) -> float:
"""Get total reserved quantity for any item in specified warehouse"""
wo = frappe.qb.DocType("Work Order")
@@ -1546,6 +1549,9 @@
if check_production_plan:
query = query.where(wo.production_plan.isnotnull())
+ if completed_production_plans:
+ query = query.where(wo.production_plan.notin(completed_production_plans))
+
return query.run()[0][0] or 0.0
diff --git a/erpnext/projects/doctype/task/task.json b/erpnext/projects/doctype/task/task.json
index 05a70c3..25a5455 100644
--- a/erpnext/projects/doctype/task/task.json
+++ b/erpnext/projects/doctype/task/task.json
@@ -60,7 +60,6 @@
"fieldname": "subject",
"fieldtype": "Data",
"in_global_search": 1,
- "in_list_view": 1,
"in_standard_filter": 1,
"label": "Subject",
"reqd": 1,
@@ -140,7 +139,6 @@
"fieldname": "parent_task",
"fieldtype": "Link",
"ignore_user_permissions": 1,
- "in_list_view": 1,
"label": "Parent Task",
"options": "Task",
"search_index": 1
@@ -398,7 +396,7 @@
"is_tree": 1,
"links": [],
"max_attachments": 5,
- "modified": "2023-09-06 13:52:05.861175",
+ "modified": "2023-09-28 13:52:05.861175",
"modified_by": "Administrator",
"module": "Projects",
"name": "Task",
diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js
index 959cf50..907a775 100644
--- a/erpnext/public/js/financial_statements.js
+++ b/erpnext/public/js/financial_statements.js
@@ -6,8 +6,10 @@
if (data && column.fieldname=="account") {
value = data.account_name || value;
- column.link_onclick =
- "erpnext.financial_statements.open_general_ledger(" + JSON.stringify(data) + ")";
+ if (data.account) {
+ column.link_onclick =
+ "erpnext.financial_statements.open_general_ledger(" + JSON.stringify(data) + ")";
+ }
column.is_tree = true;
}
diff --git a/erpnext/regional/__init__.py b/erpnext/regional/__init__.py
index ec2db81..bd5d540 100644
--- a/erpnext/regional/__init__.py
+++ b/erpnext/regional/__init__.py
@@ -10,7 +10,7 @@
def check_deletion_permission(doc, method):
region = get_region(doc.company)
- if region in ["Nepal", "France"] and doc.docstatus != 0:
+ if region in ["Nepal"] and doc.docstatus != 0:
frappe.throw(_("Deletion is not permitted for country {0}").format(region))
@@ -20,7 +20,7 @@
Called on submit of Sales Invoice and Payment Entry.
"""
region = get_region()
- if region not in ["France", "Germany"]:
+ if region not in ["Germany"]:
return
data = str(doc.as_dict())
diff --git a/erpnext/regional/address_template/templates/france.html b/erpnext/regional/address_template/templates/france.html
deleted file mode 100644
index 752331e..0000000
--- a/erpnext/regional/address_template/templates/france.html
+++ /dev/null
@@ -1,5 +0,0 @@
-{% if address_line1 %}{{ address_line1 }}{% endif -%}
-{% if address_line2 %}<br>{{ address_line2 }}{% endif -%}
-{% if pincode %}<br>{{ pincode }}{% endif -%}
-{% if city %} {{ city }}{% endif -%}
-{% if country %}<br>{{ country }}{% endif -%}
diff --git a/erpnext/regional/france/__init__.py b/erpnext/regional/france/__init__.py
deleted file mode 100644
index e69de29..0000000
--- a/erpnext/regional/france/__init__.py
+++ /dev/null
diff --git a/erpnext/regional/france/setup.py b/erpnext/regional/france/setup.py
deleted file mode 100644
index da772d6..0000000
--- a/erpnext/regional/france/setup.py
+++ /dev/null
@@ -1,30 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
-# License: GNU General Public License v3. See license.txt
-
-
-import frappe
-from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
-
-
-def setup(company=None, patch=True):
- make_custom_fields()
- add_custom_roles_for_reports()
-
-
-def make_custom_fields():
- custom_fields = {
- "Company": [
- dict(fieldname="siren_number", label="SIREN Number", fieldtype="Data", insert_after="website")
- ]
- }
-
- create_custom_fields(custom_fields)
-
-
-def add_custom_roles_for_reports():
- report_name = "Fichier des Ecritures Comptables [FEC]"
-
- if not frappe.db.get_value("Custom Role", dict(report=report_name)):
- frappe.get_doc(
- dict(doctype="Custom Role", report=report_name, roles=[dict(role="Accounts Manager")])
- ).insert()
diff --git a/erpnext/regional/france/utils.py b/erpnext/regional/france/utils.py
deleted file mode 100644
index 65dfd2d..0000000
--- a/erpnext/regional/france/utils.py
+++ /dev/null
@@ -1,8 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies and contributors
-# For license information, please see license.txt
-
-
-# don't remove this function it is used in tests
-def test_method():
- """test function"""
- return "overridden"
diff --git "a/erpnext/regional/report/fichier_des_ecritures_comptables_\133fec\135/__init__.py" "b/erpnext/regional/report/fichier_des_ecritures_comptables_\133fec\135/__init__.py"
deleted file mode 100644
index e69de29..0000000
--- "a/erpnext/regional/report/fichier_des_ecritures_comptables_\133fec\135/__init__.py"
+++ /dev/null
diff --git "a/erpnext/regional/report/fichier_des_ecritures_comptables_\133fec\135/fichier_des_ecritures_comptables_\133fec\135.js" "b/erpnext/regional/report/fichier_des_ecritures_comptables_\133fec\135/fichier_des_ecritures_comptables_\133fec\135.js"
deleted file mode 100644
index b85b58f..0000000
--- "a/erpnext/regional/report/fichier_des_ecritures_comptables_\133fec\135/fichier_des_ecritures_comptables_\133fec\135.js"
+++ /dev/null
@@ -1,97 +0,0 @@
-// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-frappe.query_reports["Fichier des Ecritures Comptables [FEC]"] = {
- "filters": [
- {
- "fieldname": "company",
- "label": __("Company"),
- "fieldtype": "Link",
- "options": "Company",
- "default": frappe.defaults.get_user_default("Company"),
- "reqd": 1
- },
- {
- "fieldname": "fiscal_year",
- "label": __("Fiscal Year"),
- "fieldtype": "Link",
- "options": "Fiscal Year",
- "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()),
- "reqd": 1
- }
- ],
- onload: function(query_report) {
- query_report.page.add_inner_button(__("Export"), function() {
- fec_export(query_report);
- });
-
- query_report.add_make_chart_button = function() {
- //
- };
-
- query_report.export_report = function() {
- fec_export(query_report);
- };
- }
-};
-
-let fec_export = function(query_report) {
- const fiscal_year = query_report.get_values().fiscal_year;
- const company = query_report.get_values().company;
-
- frappe.db.get_value("Company", company, "siren_number", (value) => {
- const company_data = value.siren_number;
- if (company_data === null || company_data === undefined) {
- frappe.msgprint(__("Please register the SIREN number in the company information file"));
- } else {
- frappe.db.get_value("Fiscal Year", fiscal_year, "year_end_date", (r) => {
- const fy = r.year_end_date;
- const title = company_data + "FEC" + moment(fy).format('YYYYMMDD');
- const column_row = query_report.columns.map(col => col.label);
- const column_data = query_report.get_data_for_csv(false);
- const result = [column_row].concat(column_data);
- downloadify(result, null, title);
- });
-
- }
- });
-};
-
-let downloadify = function(data, roles, title) {
- if (roles && roles.length && !has_common(roles, roles)) {
- frappe.msgprint(__("Export not allowed. You need {0} role to export.", [frappe.utils.comma_or(roles)]));
- return;
- }
-
- const filename = title + ".txt";
- let csv_data = to_tab_csv(data);
- const a = document.createElement('a');
-
- if ("download" in a) {
- // Used Blob object, because it can handle large files
- let blob_object = new Blob([csv_data], {
- type: 'text/csv;charset=UTF-8'
- });
- a.href = URL.createObjectURL(blob_object);
- a.download = filename;
-
- } else {
- // use old method
- a.href = 'data:attachment/csv,' + encodeURIComponent(csv_data);
- a.download = filename;
- a.target = "_blank";
- }
-
- document.body.appendChild(a);
- a.click();
-
- document.body.removeChild(a);
-};
-
-let to_tab_csv = function(data) {
- let res = [];
- $.each(data, function(i, row) {
- res.push(row.join("\t"));
- });
- return res.join("\n");
-};
diff --git "a/erpnext/regional/report/fichier_des_ecritures_comptables_\133fec\135/fichier_des_ecritures_comptables_\133fec\135.json" "b/erpnext/regional/report/fichier_des_ecritures_comptables_\133fec\135/fichier_des_ecritures_comptables_\133fec\135.json"
deleted file mode 100644
index 9b48e11..0000000
--- "a/erpnext/regional/report/fichier_des_ecritures_comptables_\133fec\135/fichier_des_ecritures_comptables_\133fec\135.json"
+++ /dev/null
@@ -1,19 +0,0 @@
-{
- "add_total_row": 0,
- "apply_user_permissions": 0,
- "creation": "2018-01-10 15:10:16.650129",
- "disabled": 0,
- "docstatus": 0,
- "doctype": "Report",
- "idx": 0,
- "is_standard": "Yes",
- "modified": "2018-01-11 10:27:25.595485",
- "modified_by": "Administrator",
- "module": "Regional",
- "name": "Fichier des Ecritures Comptables [FEC]",
- "owner": "Administrator",
- "ref_doctype": "GL Entry",
- "report_name": "Fichier des Ecritures Comptables [FEC]",
- "report_type": "Script Report",
- "roles": []
-}
\ No newline at end of file
diff --git "a/erpnext/regional/report/fichier_des_ecritures_comptables_\133fec\135/fichier_des_ecritures_comptables_\133fec\135.py" "b/erpnext/regional/report/fichier_des_ecritures_comptables_\133fec\135/fichier_des_ecritures_comptables_\133fec\135.py"
deleted file mode 100644
index 6717989..0000000
--- "a/erpnext/regional/report/fichier_des_ecritures_comptables_\133fec\135/fichier_des_ecritures_comptables_\133fec\135.py"
+++ /dev/null
@@ -1,339 +0,0 @@
-# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-import re
-
-import frappe
-from frappe import _
-from frappe.utils import format_datetime
-
-COLUMNS = [
- {
- "label": "JournalCode",
- "fieldname": "JournalCode",
- "fieldtype": "Data",
- "width": 90,
- },
- {
- "label": "JournalLib",
- "fieldname": "JournalLib",
- "fieldtype": "Data",
- "width": 90,
- },
- {
- "label": "EcritureNum",
- "fieldname": "EcritureNum",
- "fieldtype": "Data",
- "width": 90,
- },
- {
- "label": "EcritureDate",
- "fieldname": "EcritureDate",
- "fieldtype": "Data",
- "width": 90,
- },
- {
- "label": "CompteNum",
- "fieldname": "CompteNum",
- "fieldtype": "Link",
- "options": "Account",
- "width": 100,
- },
- {
- "label": "CompteLib",
- "fieldname": "CompteLib",
- "fieldtype": "Link",
- "options": "Account",
- "width": 200,
- },
- {
- "label": "CompAuxNum",
- "fieldname": "CompAuxNum",
- "fieldtype": "Data",
- "width": 90,
- },
- {
- "label": "CompAuxLib",
- "fieldname": "CompAuxLib",
- "fieldtype": "Data",
- "width": 90,
- },
- {
- "label": "PieceRef",
- "fieldname": "PieceRef",
- "fieldtype": "Data",
- "width": 90,
- },
- {
- "label": "PieceDate",
- "fieldname": "PieceDate",
- "fieldtype": "Data",
- "width": 90,
- },
- {
- "label": "EcritureLib",
- "fieldname": "EcritureLib",
- "fieldtype": "Data",
- "width": 90,
- },
- {
- "label": "Debit",
- "fieldname": "Debit",
- "fieldtype": "Data",
- "width": 90,
- },
- {
- "label": "Credit",
- "fieldname": "Credit",
- "fieldtype": "Data",
- "width": 90,
- },
- {
- "label": "EcritureLet",
- "fieldname": "EcritureLet",
- "fieldtype": "Data",
- "width": 90,
- },
- {
- "label": "DateLet",
- "fieldname": "DateLet",
- "fieldtype": "Data",
- "width": 90,
- },
- {
- "label": "ValidDate",
- "fieldname": "ValidDate",
- "fieldtype": "Data",
- "width": 90,
- },
- {
- "label": "Montantdevise",
- "fieldname": "Montantdevise",
- "fieldtype": "Data",
- "width": 90,
- },
- {
- "label": "Idevise",
- "fieldname": "Idevise",
- "fieldtype": "Data",
- "width": 90,
- },
-]
-
-
-def execute(filters=None):
- validate_filters(filters)
- return COLUMNS, get_result(
- company=filters["company"],
- fiscal_year=filters["fiscal_year"],
- )
-
-
-def validate_filters(filters):
- if not filters.get("company"):
- frappe.throw(_("{0} is mandatory").format(_("Company")))
-
- if not filters.get("fiscal_year"):
- frappe.throw(_("{0} is mandatory").format(_("Fiscal Year")))
-
-
-def get_gl_entries(company, fiscal_year):
- gle = frappe.qb.DocType("GL Entry")
- sales_invoice = frappe.qb.DocType("Sales Invoice")
- purchase_invoice = frappe.qb.DocType("Purchase Invoice")
- journal_entry = frappe.qb.DocType("Journal Entry")
- payment_entry = frappe.qb.DocType("Payment Entry")
- customer = frappe.qb.DocType("Customer")
- supplier = frappe.qb.DocType("Supplier")
- employee = frappe.qb.DocType("Employee")
-
- debit = frappe.query_builder.functions.Sum(gle.debit).as_("debit")
- credit = frappe.query_builder.functions.Sum(gle.credit).as_("credit")
- debit_currency = frappe.query_builder.functions.Sum(gle.debit_in_account_currency).as_(
- "debitCurr"
- )
- credit_currency = frappe.query_builder.functions.Sum(gle.credit_in_account_currency).as_(
- "creditCurr"
- )
-
- query = (
- frappe.qb.from_(gle)
- .left_join(sales_invoice)
- .on(gle.voucher_no == sales_invoice.name)
- .left_join(purchase_invoice)
- .on(gle.voucher_no == purchase_invoice.name)
- .left_join(journal_entry)
- .on(gle.voucher_no == journal_entry.name)
- .left_join(payment_entry)
- .on(gle.voucher_no == payment_entry.name)
- .left_join(customer)
- .on(gle.party == customer.name)
- .left_join(supplier)
- .on(gle.party == supplier.name)
- .left_join(employee)
- .on(gle.party == employee.name)
- .select(
- gle.posting_date.as_("GlPostDate"),
- gle.name.as_("GlName"),
- gle.account,
- gle.transaction_date,
- debit,
- credit,
- debit_currency,
- credit_currency,
- gle.voucher_type,
- gle.voucher_no,
- gle.against_voucher_type,
- gle.against_voucher,
- gle.account_currency,
- gle.against,
- gle.party_type,
- gle.party,
- sales_invoice.name.as_("InvName"),
- sales_invoice.title.as_("InvTitle"),
- sales_invoice.posting_date.as_("InvPostDate"),
- purchase_invoice.name.as_("PurName"),
- purchase_invoice.title.as_("PurTitle"),
- purchase_invoice.posting_date.as_("PurPostDate"),
- journal_entry.cheque_no.as_("JnlRef"),
- journal_entry.posting_date.as_("JnlPostDate"),
- journal_entry.title.as_("JnlTitle"),
- payment_entry.name.as_("PayName"),
- payment_entry.posting_date.as_("PayPostDate"),
- payment_entry.title.as_("PayTitle"),
- customer.customer_name,
- customer.name.as_("cusName"),
- supplier.supplier_name,
- supplier.name.as_("supName"),
- employee.employee_name,
- employee.name.as_("empName"),
- )
- .where((gle.company == company) & (gle.fiscal_year == fiscal_year))
- .groupby(gle.voucher_type, gle.voucher_no, gle.account)
- .orderby(gle.posting_date, gle.voucher_no)
- )
-
- return query.run(as_dict=True)
-
-
-def get_result(company, fiscal_year):
- data = get_gl_entries(company, fiscal_year)
-
- result = []
-
- company_currency = frappe.get_cached_value("Company", company, "default_currency")
- accounts = frappe.get_all(
- "Account", filters={"Company": company}, fields=["name", "account_number"]
- )
-
- for d in data:
- JournalCode = re.split("-|/|[0-9]", d.get("voucher_no"))[0]
-
- if d.get("voucher_no").startswith("{0}-".format(JournalCode)) or d.get("voucher_no").startswith(
- "{0}/".format(JournalCode)
- ):
- EcritureNum = re.split("-|/", d.get("voucher_no"))[1]
- else:
- EcritureNum = re.search(r"{0}(\d+)".format(JournalCode), d.get("voucher_no"), re.IGNORECASE)[1]
-
- EcritureDate = format_datetime(d.get("GlPostDate"), "yyyyMMdd")
-
- account_number = [
- account.account_number for account in accounts if account.name == d.get("account")
- ]
- if account_number[0] is not None:
- CompteNum = account_number[0]
- else:
- frappe.throw(
- _(
- "Account number for account {0} is not available.<br> Please setup your Chart of Accounts correctly."
- ).format(d.get("account"))
- )
-
- if d.get("party_type") == "Customer":
- CompAuxNum = d.get("cusName")
- CompAuxLib = d.get("customer_name")
-
- elif d.get("party_type") == "Supplier":
- CompAuxNum = d.get("supName")
- CompAuxLib = d.get("supplier_name")
-
- elif d.get("party_type") == "Employee":
- CompAuxNum = d.get("empName")
- CompAuxLib = d.get("employee_name")
-
- elif d.get("party_type") == "Student":
- CompAuxNum = d.get("stuName")
- CompAuxLib = d.get("student_name")
-
- elif d.get("party_type") == "Member":
- CompAuxNum = d.get("memName")
- CompAuxLib = d.get("member_name")
-
- else:
- CompAuxNum = ""
- CompAuxLib = ""
-
- ValidDate = format_datetime(d.get("GlPostDate"), "yyyyMMdd")
-
- PieceRef = d.get("voucher_no") or "Sans Reference"
-
- # EcritureLib is the reference title unless it is an opening entry
- if d.get("is_opening") == "Yes":
- EcritureLib = _("Opening Entry Journal")
- if d.get("voucher_type") == "Sales Invoice":
- EcritureLib = d.get("InvTitle")
- elif d.get("voucher_type") == "Purchase Invoice":
- EcritureLib = d.get("PurTitle")
- elif d.get("voucher_type") == "Journal Entry":
- EcritureLib = d.get("JnlTitle")
- elif d.get("voucher_type") == "Payment Entry":
- EcritureLib = d.get("PayTitle")
- else:
- EcritureLib = d.get("voucher_type")
-
- PieceDate = format_datetime(d.get("GlPostDate"), "yyyyMMdd")
-
- debit = "{:.2f}".format(d.get("debit")).replace(".", ",")
-
- credit = "{:.2f}".format(d.get("credit")).replace(".", ",")
-
- Idevise = d.get("account_currency")
-
- if Idevise != company_currency:
- Montantdevise = (
- "{:.2f}".format(d.get("debitCurr")).replace(".", ",")
- if d.get("debitCurr") != 0
- else "{:.2f}".format(d.get("creditCurr")).replace(".", ",")
- )
- else:
- Montantdevise = (
- "{:.2f}".format(d.get("debit")).replace(".", ",")
- if d.get("debit") != 0
- else "{:.2f}".format(d.get("credit")).replace(".", ",")
- )
-
- row = [
- JournalCode,
- d.get("voucher_type"),
- EcritureNum,
- EcritureDate,
- CompteNum,
- d.get("account"),
- CompAuxNum,
- CompAuxLib,
- PieceRef,
- PieceDate,
- EcritureLib,
- debit,
- credit,
- "",
- "",
- ValidDate,
- Montantdevise,
- Idevise,
- ]
-
- result.append(row)
-
- return result
diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js
index e274a52..42932ad 100644
--- a/erpnext/selling/doctype/customer/customer.js
+++ b/erpnext/selling/doctype/customer/customer.js
@@ -138,7 +138,7 @@
// custom buttons
frm.add_custom_button(__('Accounts Receivable'), function () {
- frappe.set_route('query-report', 'Accounts Receivable', {customer:frm.doc.name});
+ frappe.set_route('query-report', 'Accounts Receivable', { party_type: "Customer", party: frm.doc.name });
}, __('View'));
frm.add_custom_button(__('Accounting Ledger'), function () {
diff --git a/erpnext/setup/doctype/employee/employee.json b/erpnext/setup/doctype/employee/employee.json
index 6cb4292..1143ccb 100644
--- a/erpnext/setup/doctype/employee/employee.json
+++ b/erpnext/setup/doctype/employee/employee.json
@@ -616,6 +616,7 @@
"fieldname": "relieving_date",
"fieldtype": "Date",
"label": "Relieving Date",
+ "no_copy": 1,
"mandatory_depends_on": "eval:doc.status == \"Left\"",
"oldfieldname": "relieving_date",
"oldfieldtype": "Date"
@@ -822,7 +823,7 @@
"idx": 24,
"image_field": "image",
"links": [],
- "modified": "2023-03-30 15:57:05.174592",
+ "modified": "2023-10-04 10:57:05.174592",
"modified_by": "Administrator",
"module": "Setup",
"name": "Employee",
@@ -870,4 +871,4 @@
"sort_order": "DESC",
"states": [],
"title_field": "employee_name"
-}
\ No newline at end of file
+}
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
index 2bb479b..6552cd7 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.js
@@ -37,6 +37,12 @@
}
});
+ frm.set_query("wip_composite_asset", "items", function() {
+ return {
+ filters: {'is_composite_asset': 1, 'docstatus': 0 }
+ }
+ });
+
frm.set_query("taxes_and_charges", function() {
return {
filters: {'company': frm.doc.company }
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
index 912b908..c8a9e3e 100755
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
@@ -464,6 +464,7 @@
"depends_on": "eval:doc.is_subcontracted",
"fieldname": "supplier_warehouse",
"fieldtype": "Link",
+ "ignore_user_permissions": 1,
"label": "Supplier Warehouse",
"no_copy": 1,
"oldfieldname": "supplier_warehouse",
@@ -1241,7 +1242,7 @@
"idx": 261,
"is_submittable": 1,
"links": [],
- "modified": "2023-07-04 17:23:17.025390",
+ "modified": "2023-10-01 21:00:44.556816",
"modified_by": "Administrator",
"module": "Stock",
"name": "Purchase Receipt",
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 5eb3656..d7419dc 100644
--- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
+++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
@@ -117,6 +117,7 @@
"accounting_details_section",
"expense_account",
"item_tax_rate",
+ "wip_composite_asset",
"column_break_102",
"provisional_expense_account",
"accounting_dimensions_section",
@@ -1056,12 +1057,18 @@
"fieldname": "add_serial_batch_bundle",
"fieldtype": "Button",
"label": "Add Serial / Batch No"
+ },
+ {
+ "fieldname": "wip_composite_asset",
+ "fieldtype": "Link",
+ "label": "WIP Composite Asset",
+ "options": "Asset"
}
],
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2023-08-11 16:16:16.504549",
+ "modified": "2023-10-03 21:11:50.547261",
"modified_by": "Administrator",
"module": "Stock",
"name": "Purchase Receipt Item",
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index db71fe2..d3807b0 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -1464,8 +1464,6 @@
if not company:
company = frappe.get_cached_value("Warehouse", warehouse, "company")
- last_valuation_rate = None
-
# Get moving average rate of a specific batch number
if warehouse and serial_and_batch_bundle:
batch_obj = BatchNoValuation(
@@ -1482,21 +1480,18 @@
return batch_obj.get_incoming_rate()
# Get valuation rate from last sle for the same item and warehouse
- if not last_valuation_rate or last_valuation_rate[0][0] is None:
- last_valuation_rate = frappe.db.sql(
- """select valuation_rate
- from `tabStock Ledger Entry` force index (item_warehouse)
- where
- item_code = %s
- AND warehouse = %s
- AND valuation_rate >= 0
- AND is_cancelled = 0
- AND NOT (voucher_no = %s AND voucher_type = %s)
- order by posting_date desc, posting_time desc, name desc limit 1""",
- (item_code, warehouse, voucher_no, voucher_type),
- )
-
- if last_valuation_rate:
+ if last_valuation_rate := frappe.db.sql(
+ """select valuation_rate
+ from `tabStock Ledger Entry` force index (item_warehouse)
+ where
+ item_code = %s
+ AND warehouse = %s
+ AND valuation_rate >= 0
+ AND is_cancelled = 0
+ AND NOT (voucher_no = %s AND voucher_type = %s)
+ order by posting_date desc, posting_time desc, name desc limit 1""",
+ (item_code, warehouse, voucher_no, voucher_type),
+ ):
return flt(last_valuation_rate[0][0])
# If negative stock allowed, and item delivered without any incoming entry,
diff --git a/erpnext/tests/test_regional.py b/erpnext/tests/test_regional.py
index 2c16def..55c8bb7 100644
--- a/erpnext/tests/test_regional.py
+++ b/erpnext/tests/test_regional.py
@@ -14,6 +14,3 @@
def test_regional_overrides(self):
frappe.flags.country = "Maldives"
self.assertEqual(test_method(), "original")
-
- frappe.flags.country = "France"
- self.assertEqual(test_method(), "overridden")