diff --git a/.github/helper/install.sh b/.github/helper/install.sh index 915a4637999..30389734429 100644 --- a/.github/helper/install.sh +++ b/.github/helper/install.sh @@ -6,7 +6,7 @@ cd ~ || exit sudo apt update sudo apt remove mysql-server mysql-client -sudo apt install libcups2-dev redis-server mariadb-client-10.6 +sudo apt install libcups2-dev redis-server mariadb-client pip install frappe-bench @@ -44,13 +44,9 @@ fi install_whktml() { - if [ "$(lsb_release -rs)" = "22.04" ]; then - wget -O /tmp/wkhtmltox.deb https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-2/wkhtmltox_0.12.6.1-2.jammy_amd64.deb - sudo apt install /tmp/wkhtmltox.deb - else - echo "Please update this script to support wkhtmltopdf for $(lsb_release -ds)" - exit 1 - fi + wget -O /tmp/wkhtmltox.deb https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-2/wkhtmltox_0.12.6.1-2.jammy_amd64.deb + sudo apt install /tmp/wkhtmltox.deb + } install_whktml & wkpid=$! diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index f2d11ba9ff3..8fd5f00b583 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -1328,6 +1328,24 @@ frappe.ui.form.on("Payment Entry", { if (r.message) { if (!frm.doc.mode_of_payment) { frm.set_value(field, r.message.account); + } else { + frappe.call({ + method: "frappe.client.get_value", + args: { + doctype: "Mode of Payment Account", + filters: { + parent: frm.doc.mode_of_payment, + company: frm.doc.company, + }, + fieldname: "default_account", + parent: "Mode of Payment", + }, + callback: function (res) { + if (!res.message.default_account) { + frm.set_value(field, r.message.account); + } + }, + }); } frm.set_value("bank", r.message.bank); frm.set_value("bank_account_no", r.message.bank_account_no); diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py index 5bb43b3fa72..761ed8d804e 100644 --- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py +++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py @@ -117,12 +117,12 @@ class POSInvoiceMergeLog(Document): sales = [d for d in pos_invoice_docs if d.get("is_return") == 0] sales_invoice, credit_note = "", "" - if returns: - credit_note = self.process_merging_into_credit_note(returns) - if sales: sales_invoice = self.process_merging_into_sales_invoice(sales) + if returns: + credit_note = self.process_merging_into_credit_note(returns, sales_invoice) + self.save() # save consolidated_sales_invoice & consolidated_credit_note ref in merge log self.update_pos_invoices(pos_invoice_docs, sales_invoice, credit_note) @@ -132,6 +132,7 @@ class POSInvoiceMergeLog(Document): self.update_pos_invoices(pos_invoice_docs) self.serial_and_batch_bundle_reference_for_pos_invoice() self.cancel_linked_invoices() + self.delink_serial_and_batch_bundle() def process_merging_into_sales_invoice(self, data): sales_invoice = self.get_new_sales_invoice() @@ -139,8 +140,13 @@ class POSInvoiceMergeLog(Document): sales_invoice.is_consolidated = 1 sales_invoice.set_posting_time = 1 - sales_invoice.posting_date = getdate(self.posting_date) - sales_invoice.posting_time = get_time(self.posting_time) + + if not sales_invoice.posting_date: + sales_invoice.posting_date = getdate(self.posting_date) + + if not sales_invoice.posting_time: + sales_invoice.posting_time = get_time(self.posting_time) + sales_invoice.save() sales_invoice.submit() @@ -148,12 +154,14 @@ class POSInvoiceMergeLog(Document): return sales_invoice.name - def process_merging_into_credit_note(self, data): + def process_merging_into_credit_note(self, data, sales_invoice): credit_note = self.get_new_sales_invoice() credit_note.is_return = 1 credit_note = self.merge_pos_invoice_into(credit_note, data) + credit_note.return_against = sales_invoice + credit_note.is_consolidated = 1 credit_note.set_posting_time = 1 credit_note.posting_date = getdate(self.posting_date) @@ -180,6 +188,10 @@ class POSInvoiceMergeLog(Document): for doc in data: map_doc(doc, invoice, table_map={"doctype": invoice.doctype}) + if doc.get("posting_date"): + invoice.posting_date = getdate(doc.posting_date) + invoice.posting_time = get_time(doc.posting_time) + if doc.redeem_loyalty_points: invoice.loyalty_redemption_account = doc.loyalty_redemption_account invoice.loyalty_redemption_cost_center = doc.loyalty_redemption_cost_center @@ -297,6 +309,8 @@ class POSInvoiceMergeLog(Document): sales_invoice = frappe.new_doc("Sales Invoice") sales_invoice.customer = self.customer sales_invoice.is_pos = 1 + sales_invoice.posting_date = None + sales_invoice.posting_time = None return sales_invoice @@ -319,6 +333,38 @@ class POSInvoiceMergeLog(Document): for table_name in ["items", "packed_items"]: pos_invoice.set_serial_and_batch_bundle(table_name) + def delink_serial_and_batch_bundle(self): + bundles = self.get_serial_and_batch_bundles() + if not bundles: + return + + sle_table = frappe.qb.DocType("Stock Ledger Entry") + query = ( + frappe.qb.update(sle_table) + .set(sle_table.serial_and_batch_bundle, None) + .where(sle_table.serial_and_batch_bundle.isin(bundles) & sle_table.is_cancelled == 1) + ) + + query.run() + + def get_serial_and_batch_bundles(self): + pos_invoices = [] + for d in self.pos_invoices: + pos_invoices.append(d.pos_invoice) + + if pos_invoices: + return frappe.get_all( + "POS Invoice Item", + filters={ + "docstatus": 1, + "parent": ["in", pos_invoices], + "serial_and_batch_bundle": ["is", "set"], + }, + pluck="serial_and_batch_bundle", + ) + + return [] + def cancel_linked_invoices(self): for si_name in [self.consolidated_invoice, self.consolidated_credit_note]: if not si_name: @@ -503,6 +549,9 @@ def cancel_merge_logs(merge_logs, closing_entry=None): try: for log in merge_logs: merge_log = frappe.get_doc("POS Invoice Merge Log", log) + if merge_log.docstatus == 2: + continue + merge_log.flags.ignore_permissions = True merge_log.cancel() diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index 73cb2483811..385cc1a685e 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -415,6 +415,8 @@ def get_pricing_rule_for_item(args, doc=None, for_validate=False): "parent": args.parent, "parenttype": args.parenttype, "child_docname": args.get("child_docname"), + "discount_percentage": 0.0, + "discount_amount": 0, } ) diff --git a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json index 763607c22a1..ed9a4d84f53 100644 --- a/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json +++ b/erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json @@ -244,9 +244,9 @@ }, { "fieldname": "cc_to", - "fieldtype": "Link", + "fieldtype": "Table MultiSelect", "label": "CC To", - "options": "User" + "options": "Process Statement Of Accounts CC" }, { "default": "1", @@ -400,7 +400,7 @@ } ], "links": [], - "modified": "2024-10-18 17:51:39.108481", + "modified": "2024-12-11 12:11:13.543134", "modified_by": "Administrator", "module": "Accounts", "name": "Process Statement Of Accounts", 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 bf1c8c0b66e..6910559b8b4 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 @@ -31,6 +31,9 @@ class ProcessStatementOfAccounts(Document): if TYPE_CHECKING: from frappe.types import DF + from erpnext.accounts.doctype.process_statement_of_accounts_cc.process_statement_of_accounts_cc import ( + ProcessStatementOfAccountsCC, + ) from erpnext.accounts.doctype.process_statement_of_accounts_customer.process_statement_of_accounts_customer import ( ProcessStatementOfAccountsCustomer, ) @@ -41,7 +44,7 @@ class ProcessStatementOfAccounts(Document): ageing_based_on: DF.Literal["Due Date", "Posting Date"] based_on_payment_terms: DF.Check body: DF.TextEditor | None - cc_to: DF.Link | None + cc_to: DF.TableMultiSelect[ProcessStatementOfAccountsCC] collection_name: DF.DynamicLink | None company: DF.Link cost_center: DF.TableMultiSelect[PSOACostCenter] @@ -324,7 +327,7 @@ def get_recipients_and_cc(customer, doc): cc = [] if doc.cc_to != "": try: - cc = [frappe.get_value("User", doc.cc_to, "email")] + cc = [frappe.get_value("User", user.cc, "email") for user in doc.cc_to] except Exception: pass diff --git a/erpnext/accounts/doctype/process_statement_of_accounts_cc/__init__.py b/erpnext/accounts/doctype/process_statement_of_accounts_cc/__init__.py new file mode 100644 index 00000000000..e69de29bb2d diff --git a/erpnext/accounts/doctype/process_statement_of_accounts_cc/process_statement_of_accounts_cc.json b/erpnext/accounts/doctype/process_statement_of_accounts_cc/process_statement_of_accounts_cc.json new file mode 100644 index 00000000000..c6103048b50 --- /dev/null +++ b/erpnext/accounts/doctype/process_statement_of_accounts_cc/process_statement_of_accounts_cc.json @@ -0,0 +1,32 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2024-12-11 12:10:04.654593", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "cc" + ], + "fields": [ + { + "fieldname": "cc", + "fieldtype": "Link", + "in_list_view": 1, + "label": "CC", + "options": "User" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2024-12-11 12:10:39.772598", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Process Statement Of Accounts CC", + "owner": "Administrator", + "permissions": [], + "sort_field": "creation", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/process_statement_of_accounts_cc/process_statement_of_accounts_cc.py b/erpnext/accounts/doctype/process_statement_of_accounts_cc/process_statement_of_accounts_cc.py new file mode 100644 index 00000000000..a500b6c89a7 --- /dev/null +++ b/erpnext/accounts/doctype/process_statement_of_accounts_cc/process_statement_of_accounts_cc.py @@ -0,0 +1,23 @@ +# Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class ProcessStatementOfAccountsCC(Document): + # begin: auto-generated types + # This code is auto-generated. Do not modify anything in this block. + + from typing import TYPE_CHECKING + + if TYPE_CHECKING: + from frappe.types import DF + + cc: DF.Link | None + parent: DF.Data + parentfield: DF.Data + parenttype: DF.Data + # end: auto-generated types + + pass diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 49dce0e299b..1d67109d68d 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -551,9 +551,7 @@ class ReceivablePayableReport: self.append_payment_term(row, d, term) def append_payment_term(self, row, d, term): - if ( - self.filters.get("customer") or self.filters.get("supplier") - ) and d.currency == d.party_account_currency: + if d.currency == d.party_account_currency: invoiced = d.payment_amount else: invoiced = d.base_payment_amount diff --git a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py index cdd5baf3240..bec5d128f0a 100644 --- a/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py +++ b/erpnext/accounts/report/asset_depreciations_and_balances/asset_depreciations_and_balances.py @@ -28,15 +28,14 @@ def get_group_by_asset_category_data(filters): for asset_category in asset_categories: row = frappe._dict() - # row.asset_category = asset_category row.update(asset_category) - row.cost_as_on_to_date = ( - flt(row.cost_as_on_from_date) - + flt(row.cost_of_new_purchase) - - flt(row.cost_of_sold_asset) - - flt(row.cost_of_scrapped_asset) - - flt(row.cost_of_capitalized_asset) + row.value_as_on_to_date = ( + flt(row.value_as_on_from_date) + + flt(row.value_of_new_purchase) + - flt(row.value_of_sold_asset) + - flt(row.value_of_scrapped_asset) + - flt(row.value_of_capitalized_asset) ) row.update( @@ -53,11 +52,11 @@ def get_group_by_asset_category_data(filters): - flt(row.depreciation_eliminated_during_the_period) ) - row.net_asset_value_as_on_from_date = flt(row.cost_as_on_from_date) - flt( + row.net_asset_value_as_on_from_date = flt(row.value_as_on_from_date) - flt( row.accumulated_depreciation_as_on_from_date ) - row.net_asset_value_as_on_to_date = flt(row.cost_as_on_to_date) - flt( + row.net_asset_value_as_on_to_date = flt(row.value_as_on_to_date) - flt( row.accumulated_depreciation_as_on_to_date ) @@ -85,12 +84,12 @@ def get_asset_categories_for_grouped_by_category(filters): end else 0 - end), 0) as cost_as_on_from_date, + end), 0) as value_as_on_from_date, ifnull(sum(case when a.purchase_date >= %(from_date)s then a.gross_purchase_amount else 0 - end), 0) as cost_of_new_purchase, + end), 0) as value_of_new_purchase, ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and a.disposal_date >= %(from_date)s and a.disposal_date <= %(to_date)s then @@ -101,7 +100,7 @@ def get_asset_categories_for_grouped_by_category(filters): end else 0 - end), 0) as cost_of_sold_asset, + end), 0) as value_of_sold_asset, ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and a.disposal_date >= %(from_date)s and a.disposal_date <= %(to_date)s then @@ -112,7 +111,7 @@ def get_asset_categories_for_grouped_by_category(filters): end else 0 - end), 0) as cost_of_scrapped_asset, + end), 0) as value_of_scrapped_asset, ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and a.disposal_date >= %(from_date)s and a.disposal_date <= %(to_date)s then @@ -123,7 +122,7 @@ def get_asset_categories_for_grouped_by_category(filters): end else 0 - end), 0) as cost_of_capitalized_asset + end), 0) as value_of_capitalized_asset from `tabAsset` a where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s {condition} and not exists( @@ -164,12 +163,12 @@ def get_asset_details_for_grouped_by_category(filters): end else 0 - end), 0) as cost_as_on_from_date, + end), 0) as value_as_on_from_date, ifnull(sum(case when a.purchase_date >= %(from_date)s then a.gross_purchase_amount else 0 - end), 0) as cost_of_new_purchase, + end), 0) as value_of_new_purchase, ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and a.disposal_date >= %(from_date)s and a.disposal_date <= %(to_date)s then @@ -180,7 +179,7 @@ def get_asset_details_for_grouped_by_category(filters): end else 0 - end), 0) as cost_of_sold_asset, + end), 0) as value_of_sold_asset, ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and a.disposal_date >= %(from_date)s and a.disposal_date <= %(to_date)s then @@ -191,7 +190,7 @@ def get_asset_details_for_grouped_by_category(filters): end else 0 - end), 0) as cost_of_scrapped_asset, + end), 0) as value_of_scrapped_asset, ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and a.disposal_date >= %(from_date)s and a.disposal_date <= %(to_date)s then @@ -202,7 +201,7 @@ def get_asset_details_for_grouped_by_category(filters): end else 0 - end), 0) as cost_of_capitalized_asset + end), 0) as value_of_capitalized_asset from `tabAsset` a where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s {condition} and not exists( @@ -232,15 +231,14 @@ def get_group_by_asset_data(filters): for asset_detail in asset_details: row = frappe._dict() - # row.asset_category = asset_category row.update(asset_detail) - row.cost_as_on_to_date = ( - flt(row.cost_as_on_from_date) - + flt(row.cost_of_new_purchase) - - flt(row.cost_of_sold_asset) - - flt(row.cost_of_scrapped_asset) - - flt(row.cost_of_capitalized_asset) + row.value_as_on_to_date = ( + flt(row.value_as_on_from_date) + + flt(row.value_of_new_purchase) + - flt(row.value_of_sold_asset) + - flt(row.value_of_scrapped_asset) + - flt(row.value_of_capitalized_asset) ) row.update(next(asset for asset in assets if asset["asset"] == asset_detail.get("name", ""))) @@ -251,11 +249,11 @@ def get_group_by_asset_data(filters): - flt(row.depreciation_eliminated_during_the_period) ) - row.net_asset_value_as_on_from_date = flt(row.cost_as_on_from_date) - flt( + row.net_asset_value_as_on_from_date = flt(row.value_as_on_from_date) - flt( row.accumulated_depreciation_as_on_from_date ) - row.net_asset_value_as_on_to_date = flt(row.cost_as_on_to_date) - flt( + row.net_asset_value_as_on_to_date = flt(row.value_as_on_to_date) - flt( row.accumulated_depreciation_as_on_to_date ) @@ -446,38 +444,38 @@ def get_columns(filters): columns += [ { - "label": _("Cost as on") + " " + formatdate(filters.day_before_from_date), - "fieldname": "cost_as_on_from_date", + "label": _("Value as on") + " " + formatdate(filters.day_before_from_date), + "fieldname": "value_as_on_from_date", "fieldtype": "Currency", "width": 140, }, { - "label": _("Cost of New Purchase"), - "fieldname": "cost_of_new_purchase", + "label": _("Value of New Purchase"), + "fieldname": "value_of_new_purchase", "fieldtype": "Currency", "width": 140, }, { - "label": _("Cost of Sold Asset"), - "fieldname": "cost_of_sold_asset", + "label": _("Value of Sold Asset"), + "fieldname": "value_of_sold_asset", "fieldtype": "Currency", "width": 140, }, { - "label": _("Cost of Scrapped Asset"), - "fieldname": "cost_of_scrapped_asset", + "label": _("Value of Scrapped Asset"), + "fieldname": "value_of_scrapped_asset", "fieldtype": "Currency", "width": 140, }, { - "label": _("Cost of New Capitalized Asset"), - "fieldname": "cost_of_capitalized_asset", + "label": _("Value of New Capitalized Asset"), + "fieldname": "value_of_capitalized_asset", "fieldtype": "Currency", "width": 140, }, { - "label": _("Cost as on") + " " + formatdate(filters.to_date), - "fieldname": "cost_as_on_to_date", + "label": _("Value as on") + " " + formatdate(filters.to_date), + "fieldname": "value_as_on_to_date", "fieldtype": "Currency", "width": 140, }, diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 73e49983fb2..918bafa169a 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -527,9 +527,16 @@ def get_accounting_entries( account_filter_query = get_account_filter_query(root_lft, root_rgt, root_type, gl_entry) query = query.where(ExistsCriterion(account_filter_query)) - entries = query.run(as_dict=True) + from frappe.desk.reportview import build_match_conditions - return entries + match_conditions = build_match_conditions(doctype) + + if match_conditions: + query += "and" + match_conditions + + query, params = query.walk() + + return frappe.db.sql(query, params, as_dict=True) def get_account_filter_query(root_lft, root_rgt, root_type, gl_entry): diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index c59a3bd2a7a..647490dcc90 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -790,7 +790,10 @@ class GrossProfitGenerator: """ if self.filters.group_by == "Sales Person": - sales_person_cols = ", sales.sales_person, sales.allocated_amount, sales.incentives" + sales_person_cols = """, sales.sales_person, + sales.allocated_percentage * `tabSales Invoice Item`.base_net_amount / 100 as allocated_amount, + sales.incentives + """ sales_team_table = "left join `tabSales Team` sales on sales.parent = `tabSales Invoice`.name" else: sales_person_cols = "" diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 05d575ac822..243ad009050 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -804,6 +804,9 @@ class Asset(AccountsController): ): return args.get("rate_of_depreciation") + if args.get("rate_of_depreciation") and not flt(args.get("expected_value_after_useful_life")): + return args.get("rate_of_depreciation") + if self.flags.increase_in_asset_value_due_to_repair: value = flt(args.get("expected_value_after_useful_life")) / flt( args.get("value_after_depreciation") diff --git a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json index c269948b742..b86028a7621 100644 --- a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json +++ b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json @@ -86,7 +86,8 @@ "description": "In Percentage", "fieldname": "rate_of_depreciation", "fieldtype": "Percent", - "label": "Rate of Depreciation" + "label": "Rate of Depreciation (%)", + "mandatory_depends_on": "eval:doc.depreciation_method == 'Written Down Value'" }, { "fieldname": "salvage_value_percentage", @@ -117,7 +118,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2024-05-21 15:48:20.907250", + "modified": "2024-12-13 12:11:03.743209", "modified_by": "Administrator", "module": "Assets", "name": "Asset Finance Book", diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index 696d404d16d..5c8f5a6fbd7 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -76,16 +76,13 @@ def validate_return_against(doc): def validate_returned_items(doc): valid_items = frappe._dict() - select_fields = "item_code, qty, stock_qty, rate, parenttype, conversion_factor" + select_fields = "item_code, qty, stock_qty, rate, parenttype, conversion_factor, name" if doc.doctype != "Purchase Invoice": select_fields += ",serial_no, batch_no" if doc.doctype in ["Purchase Invoice", "Purchase Receipt", "Subcontracting Receipt"]: select_fields += ",rejected_qty, received_qty" - if doc.doctype in ["Purchase Receipt", "Purchase Invoice"]: - select_fields += ",name" - for d in frappe.db.sql( f"""select {select_fields} from `tab{doc.doctype} Item` where parent = %s""", doc.return_against, @@ -113,11 +110,13 @@ def validate_returned_items(doc): for d in doc.get("items"): key = d.item_code raise_exception = False - if doc.doctype in ["Purchase Receipt", "Purchase Invoice"]: + if doc.doctype in ["Purchase Receipt", "Purchase Invoice", "Sales Invoice"]: field = frappe.scrub(doc.doctype) + "_item" if d.get(field): key = (d.item_code, d.get(field)) raise_exception = True + elif doc.doctype == "Delivery Note": + key = (d.item_code, d.get("dn_detail")) if d.item_code and (flt(d.qty) < 0 or flt(d.get("received_qty")) < 0): if key not in valid_items: @@ -129,7 +128,7 @@ def validate_returned_items(doc): ) else: ref = valid_items.get(key, frappe._dict()) - validate_quantity(doc, d, ref, valid_items, already_returned_items) + validate_quantity(doc, key, d, ref, valid_items, already_returned_items) if ( ref.rate @@ -159,7 +158,7 @@ def validate_returned_items(doc): frappe.throw(_("Atleast one item should be entered with negative quantity in return document")) -def validate_quantity(doc, args, ref, valid_items, already_returned_items): +def validate_quantity(doc, key, args, ref, valid_items, already_returned_items): fields = ["stock_qty"] if doc.doctype in ["Purchase Receipt", "Purchase Invoice", "Subcontracting Receipt"]: if not args.get("return_qty_from_rejected_warehouse"): @@ -167,7 +166,7 @@ def validate_quantity(doc, args, ref, valid_items, already_returned_items): else: fields.extend(["received_qty"]) - already_returned_data = already_returned_items.get(args.item_code) or {} + already_returned_data = already_returned_items.get(key) or {} company_currency = erpnext.get_company_currency(doc.company) stock_qty_precision = get_field_precision( @@ -253,15 +252,20 @@ def get_already_returned_items(doc): column += """, sum(abs(child.rejected_qty) * child.conversion_factor) as rejected_qty, sum(abs(child.received_qty) * child.conversion_factor) as received_qty""" + field = ( + frappe.scrub(doc.doctype) + "_item" + if doc.doctype in ["Purchase Invoice", "Purchase Receipt", "Sales Invoice"] + else "dn_detail" + ) data = frappe.db.sql( f""" - select {column} + select {column}, {field} from `tab{doc.doctype} Item` child, `tab{doc.doctype}` par where child.parent = par.name and par.docstatus = 1 and par.is_return = 1 and par.return_against = %s - group by item_code + group by item_code, {field} """, doc.return_against, as_dict=1, @@ -271,7 +275,7 @@ def get_already_returned_items(doc): for d in data: items.setdefault( - d.item_code, + (d.item_code, d.get(field)), frappe._dict( { "qty": d.get("qty"), diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index fe498ca32f9..4b52859cabd 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -255,15 +255,6 @@ frappe.ui.form.on("BOM", { }); } - if (!skip_qty_field) { - fields.push({ - fieldtype: "Check", - label: __("Use Multi-Level BOM"), - fieldname: "use_multi_level_bom", - default: 1, - }); - } - if (!skip_qty_field) { fields.push({ fieldtype: "Float", diff --git a/erpnext/patches.txt b/erpnext/patches.txt index a916478d4b3..7e03ef9394c 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -385,3 +385,4 @@ erpnext.patches.v15_0.update_sub_voucher_type_in_gl_entries erpnext.patches.v14_0.update_stock_uom_in_work_order_item erpnext.patches.v15_0.set_is_exchange_gain_loss_in_payment_entry_deductions erpnext.patches.v15_0.enable_allow_existing_serial_no +erpnext.patches.v15_0.update_cc_in_process_statement_of_accounts diff --git a/erpnext/patches/v15_0/update_cc_in_process_statement_of_accounts.py b/erpnext/patches/v15_0/update_cc_in_process_statement_of_accounts.py new file mode 100644 index 00000000000..75aa8b9faf1 --- /dev/null +++ b/erpnext/patches/v15_0/update_cc_in_process_statement_of_accounts.py @@ -0,0 +1,16 @@ +import frappe + + +def execute(): + process_statement_of_accounts = frappe.qb.DocType("Process Statement Of Accounts") + + data = ( + frappe.qb.from_(process_statement_of_accounts) + .select(process_statement_of_accounts.name, process_statement_of_accounts.cc_to) + .where(process_statement_of_accounts.cc_to.isnotnull()) + ).run(as_dict=True) + + for d in data: + doc = frappe.get_doc("Process Statement Of Accounts", d.name) + doc.append("cc_to", {"cc": d.cc_to}) + doc.save() diff --git a/erpnext/public/js/queries.js b/erpnext/public/js/queries.js index fd5b7603844..46958092199 100644 --- a/erpnext/public/js/queries.js +++ b/erpnext/public/js/queries.js @@ -99,9 +99,12 @@ $.extend(erpnext.queries, { }, dispatch_address_query: function (doc) { + var filters = { link_doctype: "Company", link_name: doc.company || "" }; + var is_drop_ship = doc.items.some((item) => item.delivered_by_supplier); + if (is_drop_ship) filters = {}; return { query: "frappe.contacts.doctype.address.address.address_query", - filters: { link_doctype: "Company", link_name: doc.company || "" }, + filters: filters, }; }, diff --git a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js index 4a2d8911d1a..ed6e6e02dcc 100644 --- a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js +++ b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js @@ -85,7 +85,7 @@ erpnext.PointOfSale.PastOrderSummary = class {
${doc.name}
- ${doc.status} + ${__(doc.status)}
`; } diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index dc2071b9ee0..f96a6760bec 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -505,9 +505,9 @@ class SerialandBatchBundle(Document): elif (d.incoming_rate == rate) and d.qty and d.stock_value_difference: continue - d.incoming_rate = rate + d.incoming_rate = flt(rate) if d.qty: - d.stock_value_difference = d.qty * d.incoming_rate + d.stock_value_difference = flt(d.qty) * d.incoming_rate if save: d.db_set( @@ -519,6 +519,8 @@ class SerialandBatchBundle(Document): if not self.voucher_no or self.voucher_no != row.parent: values_to_set["voucher_no"] = row.parent + self.db_set("is_cancelled", 0) + if self.voucher_type != parent.doctype: values_to_set["voucher_type"] = parent.doctype