diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json index 5581ab038c5..16c5479c210 100644 --- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json +++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json @@ -44,6 +44,7 @@ "section_break_jpd0", "auto_reconcile_payments", "stale_days", + "exchange_gain_loss_posting_date", "invoicing_settings_tab", "accounts_transactions_settings_section", "over_billing_allowance", @@ -383,7 +384,7 @@ { "fieldname": "section_break_jpd0", "fieldtype": "Section Break", - "label": "Payment Reconciliations" + "label": "Payment Reconciliation Settings" }, { "default": "0", @@ -462,6 +463,14 @@ "fieldname": "remarks_section", "fieldtype": "Section Break", "label": "Remarks Column Length" + }, + { + "default": "Payment", + "description": "Only applies for Normal Payments", + "fieldname": "exchange_gain_loss_posting_date", + "fieldtype": "Select", + "label": "Posting Date Inheritance for Exchange Gain / Loss", + "options": "Invoice\nPayment\nReconciliation Date" } ], "icon": "icon-cog", @@ -469,7 +478,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2024-01-22 12:10:10.151819", + "modified": "2025-01-23 13:15:44.077853", "modified_by": "Administrator", "module": "Accounts", "name": "Accounts Settings", @@ -498,4 +507,4 @@ "sort_order": "ASC", "states": [], "track_changes": 1 -} +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js index 10c0781e1d3..47f1b869df5 100644 --- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js +++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js @@ -19,10 +19,15 @@ frappe.ui.form.on("Bank Reconciliation Tool", { }, onload: function (frm) { + if (!frm.doc.company) { + frm.set_value("company", frappe.defaults.get_default("company")); + } + // Set default filter dates let today = frappe.datetime.get_today(); frm.doc.bank_statement_from_date = frappe.datetime.add_months(today, -1); frm.doc.bank_statement_to_date = today; + frm.trigger("bank_account"); }, @@ -94,7 +99,7 @@ frappe.ui.form.on("Bank Reconciliation Tool", { make_reconciliation_tool(frm) { frm.get_field("reconciliation_tool_cards").$wrapper.empty(); - if (frm.doc.bank_account && frm.doc.bank_statement_to_date) { + if (frm.doc.company && frm.doc.bank_account && frm.doc.bank_statement_to_date) { frm.trigger("get_cleared_balance").then(() => { if ( frm.doc.bank_account && @@ -110,7 +115,7 @@ frappe.ui.form.on("Bank Reconciliation Tool", { }, get_account_opening_balance(frm) { - if (frm.doc.bank_account && frm.doc.bank_statement_from_date) { + if (frm.doc.company && frm.doc.bank_account && frm.doc.bank_statement_from_date) { frappe.call({ method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_account_balance", args: { @@ -125,7 +130,7 @@ frappe.ui.form.on("Bank Reconciliation Tool", { }, get_cleared_balance(frm) { - if (frm.doc.bank_account && frm.doc.bank_statement_to_date) { + if (frm.doc.company && frm.doc.bank_account && frm.doc.bank_statement_to_date) { return frappe.call({ method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_account_balance", args: { diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 5224db65f03..25b67e2db31 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -465,7 +465,7 @@ class PaymentEntry(AccountsController): if d.reference_doctype not in valid_reference_doctypes: frappe.throw( _("Reference Doctype must be one of {0}").format( - comma_or(_(d) for d in valid_reference_doctypes) + comma_or([_(d) for d in valid_reference_doctypes]) ) ) @@ -2481,6 +2481,7 @@ def get_payment_entry( pe.paid_amount = paid_amount pe.received_amount = received_amount pe.letter_head = doc.get("letter_head") + pe.bank_account = frappe.db.get_value("Bank Account", {"is_company_account": 1, "is_default": 1}, "name") if dt in ["Purchase Order", "Sales Order", "Sales Invoice", "Purchase Invoice"]: pe.project = doc.get("project") or reduce( diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index e452d729ccf..3aee5c89c9d 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -270,6 +270,7 @@ class PaymentReconciliation(Document): for payment in non_reconciled_payments: row = self.append("payments", {}) row.update(payment) + row.is_advance = payment.book_advance_payments_in_separate_party_account def get_invoice_entries(self): # Fetch JVs, Sales and Purchase Invoices for 'invoices' to reconcile against @@ -354,6 +355,9 @@ class PaymentReconciliation(Document): def allocate_entries(self, args): self.validate_entries() + exc_gain_loss_posting_date = frappe.db.get_single_value( + "Accounts Settings", "exchange_gain_loss_posting_date", cache=True + ) invoice_exchange_map = self.get_invoice_exchange_map(args.get("invoices"), args.get("payments")) default_exchange_gain_loss_account = frappe.get_cached_value( "Company", self.company, "exchange_gain_loss_account" @@ -380,6 +384,11 @@ class PaymentReconciliation(Document): res.difference_account = default_exchange_gain_loss_account res.exchange_rate = inv.get("exchange_rate") res.update({"gain_loss_posting_date": pay.get("posting_date")}) + if not pay.get("is_advance"): + if exc_gain_loss_posting_date == "Invoice": + res.update({"gain_loss_posting_date": inv.get("invoice_date")}) + elif exc_gain_loss_posting_date == "Reconciliation Date": + res.update({"gain_loss_posting_date": nowdate()}) if pay.get("amount") == 0: entries.append(res) diff --git a/erpnext/accounts/doctype/process_payment_reconciliation_log_allocations/process_payment_reconciliation_log_allocations.json b/erpnext/accounts/doctype/process_payment_reconciliation_log_allocations/process_payment_reconciliation_log_allocations.json index b97d73886a9..d7ea1c2ca68 100644 --- a/erpnext/accounts/doctype/process_payment_reconciliation_log_allocations/process_payment_reconciliation_log_allocations.json +++ b/erpnext/accounts/doctype/process_payment_reconciliation_log_allocations/process_payment_reconciliation_log_allocations.json @@ -20,6 +20,7 @@ "is_advance", "section_break_5", "difference_amount", + "gain_loss_posting_date", "column_break_7", "difference_account", "exchange_rate", @@ -153,11 +154,16 @@ "fieldtype": "Check", "in_list_view": 1, "label": "Reconciled" + }, + { + "fieldname": "gain_loss_posting_date", + "fieldtype": "Date", + "label": "Difference Posting Date" } ], "istable": 1, "links": [], - "modified": "2023-03-20 21:05:43.121945", + "modified": "2025-01-23 16:09:01.058574", "modified_by": "Administrator", "module": "Accounts", "name": "Process Payment Reconciliation Log Allocations", 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 06c037dba84..a6a2b2410c9 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 @@ -262,9 +262,12 @@ def get_recipients_and_cc(customer, doc): recipients = [] for clist in doc.customers: if clist.customer == customer: - recipients.append(clist.billing_email) + if clist.billing_email: + for email in clist.billing_email.split(","): + recipients.append(email.strip()) if doc.primary_mandatory and clist.primary_email: - recipients.append(clist.primary_email) + for email in clist.primary_email.split(","): + recipients.append(email.strip()) cc = [] if doc.cc_to != "": try: diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 4a8c8ad82a7..11bfc5e8dde 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -302,7 +302,11 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying. if (this.frm.doc.__onload && this.frm.doc.__onload.load_after_mapping) return; - erpnext.utils.get_party_details(this.frm, "erpnext.accounts.party.get_party_details", + let payment_terms_template = this.frm.doc.payment_terms_template; + + erpnext.utils.get_party_details( + this.frm, + "erpnext.accounts.party.get_party_details", { posting_date: this.frm.doc.posting_date, bill_date: this.frm.doc.bill_date, @@ -320,7 +324,14 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying. me.frm.doc.tax_withholding_category = me.frm.supplier_tds; me.frm.set_df_property("apply_tds", "read_only", me.frm.supplier_tds ? 0 : 1); me.frm.set_df_property("tax_withholding_category", "hidden", me.frm.supplier_tds ? 0 : 1); - }) + + // while duplicating, don't change payment terms + if (me.frm.doc.__run_link_triggers === false) { + me.frm.set_value("payment_terms_template", payment_terms_template); + me.frm.refresh_field("payment_terms_template"); + } + } + ); } apply_tds(frm) { diff --git a/erpnext/accounts/report/gross_profit/gross_profit.json b/erpnext/accounts/report/gross_profit/gross_profit.json index 0730ffd77e5..dfb7a991e3e 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.json +++ b/erpnext/accounts/report/gross_profit/gross_profit.json @@ -1,5 +1,5 @@ { - "add_total_row": 1, + "add_total_row": 0, "columns": [], "creation": "2013-02-25 17:03:34", "disable_prepared_report": 0, @@ -9,7 +9,7 @@ "filters": [], "idx": 3, "is_standard": "Yes", - "modified": "2022-02-11 10:18:36.956558", + "modified": "2025-01-27 18:40:24.493829", "modified_by": "Administrator", "module": "Accounts", "name": "Gross Profit", diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index 5539954231a..07d85983a84 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -166,7 +166,14 @@ def get_data_when_grouped_by_invoice(columns, gross_profit_data, filters, group_ # removing Item Code and Item Name columns del columns[4:6] + total_base_amount = 0 + total_buying_amount = 0 + for src in gross_profit_data.si_list: + if src.indent == 1: + total_base_amount += src.base_amount or 0.0 + total_buying_amount += src.buying_amount or 0.0 + row = frappe._dict() row.indent = src.indent row.parent_invoice = src.parent_invoice @@ -177,6 +184,27 @@ def get_data_when_grouped_by_invoice(columns, gross_profit_data, filters, group_ data.append(row) + total_gross_profit = total_base_amount - total_buying_amount + data.append( + frappe._dict( + { + "sales_invoice": "Total", + "qty": None, + "avg._selling_rate": None, + "valuation_rate": None, + "selling_amount": total_base_amount, + "buying_amount": total_buying_amount, + "gross_profit": total_gross_profit, + "gross_profit_%": flt( + (total_gross_profit / total_base_amount) * 100.0, + cint(frappe.db.get_default("currency_precision")) or 3, + ) + if total_base_amount + else 0, + } + ) + ) + def get_data_when_not_grouped_by_invoice(gross_profit_data, filters, group_wise_columns, data): for src in gross_profit_data.grouped_data: diff --git a/erpnext/accounts/report/gross_profit/test_gross_profit.py b/erpnext/accounts/report/gross_profit/test_gross_profit.py index 8b1621ae782..94e44b76fcd 100644 --- a/erpnext/accounts/report/gross_profit/test_gross_profit.py +++ b/erpnext/accounts/report/gross_profit/test_gross_profit.py @@ -558,3 +558,33 @@ class TestGrossProfit(FrappeTestCase): } gp_entry = [x for x in data if x.parent_invoice == sinv.name] self.assertDictContainsSubset(expected_entry, gp_entry[0]) + + def test_gross_profit_groupby_invoices(self): + create_sales_invoice( + qty=1, + rate=100, + company=self.company, + customer=self.customer, + item_code=self.item, + item_name=self.item, + cost_center=self.cost_center, + warehouse=self.warehouse, + debit_to=self.debit_to, + parent_cost_center=self.cost_center, + update_stock=0, + currency="INR", + income_account=self.income_account, + expense_account=self.expense_account, + ) + + filters = frappe._dict( + company=self.company, from_date=nowdate(), to_date=nowdate(), group_by="Invoice" + ) + + _, data = execute(filters=filters) + total = data[-1] + + self.assertEqual(total.selling_amount, 100.0) + self.assertEqual(total.buying_amount, 0.0) + self.assertEqual(total.gross_profit, 100.0) + self.assertEqual(total.get("gross_profit_%"), 100.0) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index d6fd53dc778..20dec47faa9 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -205,9 +205,12 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends e } } - if(is_drop_ship && doc.status!="Delivered") { - this.frm.add_custom_button(__('Delivered'), - this.delivered_by_supplier, __("Status")); + if (is_drop_ship && doc.status != "Delivered") { + this.frm.add_custom_button( + __("Delivered"), + this.delivered_by_supplier.bind(this), + __("Status") + ); this.frm.page.set_inner_btn_group_as_primary(__("Status")); } @@ -582,4 +585,4 @@ frappe.ui.form.on("Purchase Order", "is_subcontracted", function(frm) { if (frm.doc.is_old_subcontracting_flow) { erpnext.buying.get_default_bom(frm); } -}); \ No newline at end of file +}); diff --git a/erpnext/controllers/tests/test_accounts_controller.py b/erpnext/controllers/tests/test_accounts_controller.py index 1e0f4cce27b..7470f4eda25 100644 --- a/erpnext/controllers/tests/test_accounts_controller.py +++ b/erpnext/controllers/tests/test_accounts_controller.py @@ -7,8 +7,9 @@ from datetime import datetime import frappe from frappe import qb from frappe.query_builder.functions import Sum -from frappe.tests.utils import FrappeTestCase +from frappe.tests.utils import FrappeTestCase, change_settings from frappe.utils import add_days, getdate, nowdate +from frappe.utils.data import getdate as convert_to_date from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry @@ -707,6 +708,67 @@ class TestAccountsController(FrappeTestCase): self.assertEqual(exc_je_for_si, []) self.assertEqual(exc_je_for_pe, []) + @change_settings("Accounts Settings", {"exchange_gain_loss_posting_date": "Reconciliation Date"}) + def test_17_gain_loss_posting_date_for_normal_payment(self): + # Sales Invoice in Foreign Currency + rate = 80 + rate_in_account_currency = 1 + + adv_date = convert_to_date(add_days(nowdate(), -2)) + inv_date = convert_to_date(add_days(nowdate(), -1)) + + si = self.create_sales_invoice(posting_date=inv_date, qty=1, rate=rate_in_account_currency) + + # Test payments with different exchange rates + pe = self.create_payment_entry(posting_date=adv_date, amount=1, source_exc_rate=75.1).save().submit() + + pr = self.create_payment_reconciliation() + pr.from_invoice_date = add_days(nowdate(), -1) + pr.to_invoice_date = nowdate() + pr.from_payment_date = add_days(nowdate(), -2) + pr.to_payment_date = nowdate() + + pr.get_unreconciled_entries() + self.assertEqual(len(pr.invoices), 1) + self.assertEqual(len(pr.payments), 1) + invoices = [x.as_dict() for x in pr.invoices] + payments = [x.as_dict() for x in pr.payments] + pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments})) + pr.reconcile() + self.assertEqual(len(pr.invoices), 0) + self.assertEqual(len(pr.payments), 0) + + # Outstanding in both currencies should be '0' + si.reload() + self.assertEqual(si.outstanding_amount, 0) + self.assert_ledger_outstanding(si.doctype, si.name, 0.0, 0.0) + + # Exchange Gain/Loss Journal should've been created. + exc_je_for_si = self.get_journals_for(si.doctype, si.name) + exc_je_for_pe = self.get_journals_for(pe.doctype, pe.name) + self.assertNotEqual(exc_je_for_si, []) + self.assertEqual(len(exc_je_for_si), 1) + self.assertEqual(len(exc_je_for_pe), 1) + self.assertEqual(exc_je_for_si[0], exc_je_for_pe[0]) + + self.assertEqual( + getdate(nowdate()), frappe.db.get_value("Journal Entry", exc_je_for_pe[0].parent, "posting_date") + ) + # Cancel Payment + pe.reload() + pe.cancel() + + # outstanding should be same as grand total + si.reload() + self.assertEqual(si.outstanding_amount, rate_in_account_currency) + self.assert_ledger_outstanding(si.doctype, si.name, rate, rate_in_account_currency) + + # Exchange Gain/Loss Journal should've been cancelled + exc_je_for_si = self.get_journals_for(si.doctype, si.name) + exc_je_for_pe = self.get_journals_for(pe.doctype, pe.name) + self.assertEqual(exc_je_for_si, []) + self.assertEqual(exc_je_for_pe, []) + def test_20_journal_against_sales_invoice(self): # Invoice in Foreign Currency si = self.create_sales_invoice(qty=1, conversion_rate=80, rate=1) diff --git a/erpnext/crm/doctype/lead/lead.json b/erpnext/crm/doctype/lead/lead.json index b7292a77f7d..e5ae004df1b 100644 --- a/erpnext/crm/doctype/lead/lead.json +++ b/erpnext/crm/doctype/lead/lead.json @@ -375,7 +375,7 @@ "depends_on": "eval:!doc.__islocal", "fieldname": "notes_tab", "fieldtype": "Tab Break", - "label": "Comments" + "label": "Notes" }, { "collapsible": 1, @@ -514,7 +514,7 @@ "idx": 5, "image_field": "image", "links": [], - "modified": "2023-12-01 18:46:49.468526", + "modified": "2025-01-31 13:40:08.094759", "modified_by": "Administrator", "module": "CRM", "name": "Lead", diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 8cc7ada03c2..94e48ef135d 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -368,3 +368,5 @@ erpnext.patches.v14_0.remove_cancelled_asset_capitalization_from_asset erpnext.patches.v14_0.enable_set_priority_for_pricing_rules #1 erpnext.patches.v14_0.update_currency_exchange_settings_for_frankfurter erpnext.patches.v14_0.update_stock_uom_in_work_order_item +erpnext.patches.v14_0.disable_add_row_in_gross_profit +execute:frappe.db.set_single_value("Accounts Settings", "exchange_gain_loss_posting_date", "Payment") diff --git a/erpnext/patches/v14_0/disable_add_row_in_gross_profit.py b/erpnext/patches/v14_0/disable_add_row_in_gross_profit.py new file mode 100644 index 00000000000..d95503bef0a --- /dev/null +++ b/erpnext/patches/v14_0/disable_add_row_in_gross_profit.py @@ -0,0 +1,5 @@ +import frappe + + +def execute(): + frappe.db.set_value("Report", "Gross Profit", "add_total_row", 0) diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js index 35622a77e2c..a81bef3db32 100644 --- a/erpnext/selling/page/point_of_sale/pos_controller.js +++ b/erpnext/selling/page/point_of_sale/pos_controller.js @@ -635,7 +635,7 @@ erpnext.PointOfSale.Controller = class { i.item_code === item_code && (!has_batch_no || (has_batch_no && i.batch_no === batch_no)) && i.uom === uom && - i.rate == rate + i.price_list_rate === flt(rate) ); } diff --git a/erpnext/setup/doctype/employee/employee.json b/erpnext/setup/doctype/employee/employee.json index daf2df5a590..8948211b2e5 100644 --- a/erpnext/setup/doctype/employee/employee.json +++ b/erpnext/setup/doctype/employee/employee.json @@ -871,5 +871,6 @@ "sort_field": "modified", "sort_order": "DESC", "states": [], - "title_field": "employee_name" -} \ No newline at end of file + "title_field": "employee_name", + "track_changes": 1 +} diff --git a/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.py b/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.py index eda768b1c56..38e15887e44 100644 --- a/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.py +++ b/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.py @@ -95,7 +95,6 @@ class ClosingStockBalance(Document): "item_group": self.item_group, "warehouse_type": self.warehouse_type, "include_uom": self.include_uom, - "ignore_closing_balance": 1, "show_variant_attributes": 1, "show_stock_ageing_data": 1, } diff --git a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py index 7c23e4c8f1e..b56d2f99328 100644 --- a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py +++ b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py @@ -4,7 +4,7 @@ import frappe from frappe import _ -from frappe.utils import cint, flt, get_table_name, getdate +from frappe.utils import cint, flt, get_datetime, get_table_name, getdate from pypika import functions as fn from erpnext.stock.doctype.warehouse.warehouse import apply_warehouse_filter @@ -99,6 +99,8 @@ def get_stock_ledger_entries(filters): if not filters.get("to_date"): frappe.throw(_("'To Date' is required")) + to_date = get_datetime(filters.get("to_date") + " 23:59:59") + sle = frappe.qb.DocType("Stock Ledger Entry") query = ( frappe.qb.from_(sle) @@ -113,7 +115,7 @@ def get_stock_ledger_entries(filters): (sle.docstatus < 2) & (sle.is_cancelled == 0) & (fn.IfNull(sle.batch_no, "") != "") - & (sle.posting_date <= filters["to_date"]) + & (sle.posting_datetime <= to_date) ) .groupby(sle.voucher_no, sle.batch_no, sle.item_code, sle.warehouse) .orderby(sle.item_code, sle.warehouse) diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py index c09137e645b..ea94c15c0e8 100644 --- a/erpnext/stock/report/stock_ageing/stock_ageing.py +++ b/erpnext/stock/report/stock_ageing/stock_ageing.py @@ -6,7 +6,7 @@ from operator import itemgetter import frappe from frappe import _ -from frappe.utils import cint, date_diff, flt +from frappe.utils import cint, date_diff, flt, get_datetime from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos @@ -387,6 +387,7 @@ class FIFOSlots: def __get_stock_ledger_entries(self) -> list[dict]: sle = frappe.qb.DocType("Stock Ledger Entry") item = self.__get_item_query() # used as derived table in sle query + to_date = get_datetime(self.filters.get("to_date") + " 23:59:59") sle_query = ( frappe.qb.from_(sle) @@ -411,7 +412,7 @@ class FIFOSlots: .where( (sle.item_code == item.name) & (sle.company == self.filters.get("company")) - & (sle.posting_date <= self.filters.get("to_date")) + & (sle.posting_datetime <= to_date) & (sle.is_cancelled != 1) ) ) @@ -428,7 +429,7 @@ class FIFOSlots: if warehouses: sle_query = sle_query.where(sle.warehouse.isin(warehouses)) - sle_query = sle_query.orderby(sle.posting_date, sle.posting_time, sle.creation, sle.actual_qty) + sle_query = sle_query.orderby(sle.posting_datetime, sle.creation) return sle_query.run(as_dict=True, as_iterator=True) diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py index eee582ac054..78112682a7e 100644 --- a/erpnext/stock/report/stock_balance/stock_balance.py +++ b/erpnext/stock/report/stock_balance/stock_balance.py @@ -289,7 +289,6 @@ class StockBalanceReport: .where((sle.docstatus < 2) & (sle.is_cancelled == 0)) .orderby(sle.posting_datetime) .orderby(sle.creation) - .orderby(sle.actual_qty) ) query = self.apply_inventory_dimensions_filters(query, sle) diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py index bde1434e600..4341feec985 100644 --- a/erpnext/stock/report/stock_ledger/stock_ledger.py +++ b/erpnext/stock/report/stock_ledger/stock_ledger.py @@ -5,7 +5,7 @@ import frappe from frappe import _ from frappe.query_builder.functions import CombineDatetime -from frappe.utils import cint, flt +from frappe.utils import cint, flt, get_datetime from erpnext.stock.doctype.inventory_dimension.inventory_dimension import get_inventory_dimensions from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos @@ -264,6 +264,9 @@ def get_columns(filters): def get_stock_ledger_entries(filters, items): + from_date = get_datetime(filters.from_date + " 00:00:00") + to_date = get_datetime(filters.to_date + " 23:59:59") + sle = frappe.qb.DocType("Stock Ledger Entry") query = ( frappe.qb.from_(sle) @@ -286,12 +289,8 @@ def get_stock_ledger_entries(filters, items): sle.serial_no, sle.project, ) - .where( - (sle.docstatus < 2) - & (sle.is_cancelled == 0) - & (sle.posting_date[filters.from_date : filters.to_date]) - ) - .orderby(CombineDatetime(sle.posting_date, sle.posting_time)) + .where((sle.docstatus < 2) & (sle.is_cancelled == 0) & (sle.posting_datetime[from_date:to_date])) + .orderby(sle.posting_datetime) .orderby(sle.creation) )