diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index 7525369d4f6..bb8f9dc0472 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -298,20 +298,22 @@ def make_round_off_gle(gl_map, debit_credit_diff, precision): round_off_account, round_off_cost_center = get_round_off_account_and_cost_center( gl_map[0].company, gl_map[0].voucher_type, gl_map[0].voucher_no ) - round_off_account_exists = False round_off_gle = frappe._dict() - for d in gl_map: - if d.account == round_off_account: - round_off_gle = d - if d.debit: - debit_credit_diff -= flt(d.debit) - else: - debit_credit_diff += flt(d.credit) - round_off_account_exists = True + round_off_account_exists = False - if round_off_account_exists and abs(debit_credit_diff) < (1.0 / (10**precision)): - gl_map.remove(round_off_gle) - return + if gl_map[0].voucher_type != "Period Closing Voucher": + for d in gl_map: + if d.account == round_off_account: + round_off_gle = d + if d.debit: + debit_credit_diff -= flt(d.debit) - flt(d.credit) + else: + debit_credit_diff += flt(d.credit) + round_off_account_exists = True + + if round_off_account_exists and abs(debit_credit_diff) < (1.0 / (10**precision)): + gl_map.remove(round_off_gle) + return if not round_off_gle: for k in ["voucher_type", "voucher_no", "company", "posting_date", "remarks"]: @@ -334,7 +336,6 @@ def make_round_off_gle(gl_map, debit_credit_diff, precision): ) update_accounting_dimensions(round_off_gle) - if not round_off_account_exists: gl_map.append(round_off_gle) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index c9567f23a34..1ca95099fe8 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -685,10 +685,10 @@ class ReceivablePayableReport(object): if self.filters.get(scrub(self.party_type)): select_fields = "debit_in_account_currency as debit, credit_in_account_currency as credit" + doc_currency_fields = "debit as debit_in_account_currency, credit as credit_in_account_currency" else: select_fields = "debit, credit" - - doc_currency_fields = "debit_in_account_currency, credit_in_account_currency" + doc_currency_fields = "debit_in_account_currency, credit_in_account_currency" remarks = ", remarks" if self.filters.get("show_remarks") else "" diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index f4cb63b2ed7..59187d36865 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -825,7 +825,9 @@ def update_maintenance_status(): for asset in assets: asset = frappe.get_doc("Asset", asset.name) - if frappe.db.exists("Asset Repair", {"asset_name": asset.name, "repair_status": "Pending"}): + if frappe.db.exists( + "Asset Repair", {"asset_name": asset.name, "repair_status": "Pending", "docstatus": 0} + ): asset.set_status("Out of Order") elif frappe.db.exists( "Asset Maintenance Task", {"parent": asset.name, "next_due_date": today()} diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 76a717b8317..791e39fed88 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -578,6 +578,7 @@ class SellingController(StockController): "customer_address": "address_display", "shipping_address_name": "shipping_address", "company_address": "company_address_display", + "dispatch_address_name": "dispatch_address", } for address_field, address_display_field in address_dict.items(): diff --git a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py index 8a493a5fc77..64e5cde0ace 100644 --- a/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py +++ b/erpnext/loan_management/doctype/loan_disbursement/loan_disbursement.py @@ -191,7 +191,9 @@ def get_total_pledged_security_value(loan): for security, qty in pledged_securities.items(): after_haircut_percentage = 100 - hair_cut_map.get(security) - security_value += (loan_security_price_map.get(security) * qty * after_haircut_percentage) / 100 + security_value += ( + loan_security_price_map.get(security, 0) * qty * after_haircut_percentage + ) / 100 return security_value diff --git a/erpnext/manufacturing/report/job_card_summary/job_card_summary.js b/erpnext/manufacturing/report/job_card_summary/job_card_summary.js index cb771e49941..782ce8110a8 100644 --- a/erpnext/manufacturing/report/job_card_summary/job_card_summary.js +++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.js @@ -54,11 +54,11 @@ frappe.query_reports["Job Card Summary"] = { options: ["", "Open", "Work In Progress", "Completed", "On Hold"] }, { - label: __("Sales Orders"), - fieldname: "sales_order", + label: __("Work Orders"), + fieldname: "work_order", fieldtype: "MultiSelectList", get_data: function(txt) { - return frappe.db.get_link_options('Sales Order', txt); + return frappe.db.get_link_options('Work Order', txt); } }, { diff --git a/erpnext/manufacturing/report/job_card_summary/job_card_summary.py b/erpnext/manufacturing/report/job_card_summary/job_card_summary.py index a86c7a47c36..009dc862e99 100644 --- a/erpnext/manufacturing/report/job_card_summary/job_card_summary.py +++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.py @@ -36,10 +36,14 @@ def get_data(filters): "total_time_in_mins", ] - for field in ["work_order", "workstation", "operation", "company"]: + for field in ["work_order", "production_item"]: if filters.get(field): query_filters[field] = ("in", filters.get(field)) + for field in ["workstation", "operation", "status", "company"]: + if filters.get(field): + query_filters[field] = filters.get(field) + data = frappe.get_all("Job Card", fields=fields, filters=query_filters) if not data: diff --git a/erpnext/manufacturing/report/work_order_summary/work_order_summary.py b/erpnext/manufacturing/report/work_order_summary/work_order_summary.py index 2368bfdf2c6..08a7e0ccd39 100644 --- a/erpnext/manufacturing/report/work_order_summary/work_order_summary.py +++ b/erpnext/manufacturing/report/work_order_summary/work_order_summary.py @@ -39,10 +39,14 @@ def get_data(filters): "lead_time", ] - for field in ["sales_order", "production_item", "status", "company"]: + for field in ["sales_order", "production_item"]: if filters.get(field): query_filters[field] = ("in", filters.get(field)) + for field in ["status", "company"]: + if filters.get(field): + query_filters[field] = filters.get(field) + query_filters["planned_start_date"] = (">=", filters.get("from_date")) query_filters["planned_end_date"] = ("<=", filters.get("to_date")) diff --git a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py index 5f2af74dca6..e6b1bfac4a9 100644 --- a/erpnext/payroll/doctype/payroll_entry/payroll_entry.py +++ b/erpnext/payroll/doctype/payroll_entry/payroll_entry.py @@ -346,6 +346,8 @@ class PayrollEntry(Document): "credit_in_account_currency": flt(payable_amt, precision), "exchange_rate": flt(exchange_rate), "cost_center": self.cost_center, + "reference_type": self.doctype, + "reference_name": self.name, }, accounting_dimensions, ) @@ -720,12 +722,21 @@ def get_month_details(year, month): def get_payroll_entry_bank_entries(payroll_entry_name): journal_entries = frappe.db.sql( - "select name from `tabJournal Entry Account` " - 'where reference_type="Payroll Entry" ' - "and reference_name=%s and docstatus=1", + """ + select + je.name + from + `tabJournal Entry` je, + `tabJournal Entry Account` jea + where + je.name = jea.parent + and je.voucher_type = 'Bank Entry' + and jea.reference_type = 'Payroll Entry' + and jea.reference_name = %s + """, payroll_entry_name, - as_dict=1, - ) + as_dict=True, + ) # nosemgrep return journal_entries diff --git a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py index d94edef1b07..21dab42b80f 100644 --- a/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py +++ b/erpnext/payroll/doctype/payroll_entry/test_payroll_entry.py @@ -133,9 +133,17 @@ class TestPayrollEntry(FrappeTestCase): payment_entry = frappe.db.sql( """ - Select ifnull(sum(je.total_debit),0) as total_debit, ifnull(sum(je.total_credit),0) as total_credit from `tabJournal Entry` je, `tabJournal Entry Account` jea - Where je.name = jea.parent - And jea.reference_name = %s + select + ifnull(sum(je.total_debit),0) as total_debit, + ifnull(sum(je.total_credit),0) as total_credit + from + `tabJournal Entry` je, + `tabJournal Entry Account` jea + Where + je.name = jea.parent + and je.voucher_type = 'Bank Entry' + and jea.reference_type = 'Payroll Entry' + and jea.reference_name = %s """, (payroll_entry.name), as_dict=1, diff --git a/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.py b/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.py index 897d8d86da4..3ce55c2622a 100644 --- a/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.py +++ b/erpnext/regional/doctype/e_invoice_settings/e_invoice_settings.py @@ -10,3 +10,8 @@ class EInvoiceSettings(Document): def validate(self): if self.enable and not self.credentials: frappe.throw(_("You must add atleast one credentials to be able to use E Invoicing.")) + + prev_doc = self.get_doc_before_save() + if prev_doc.client_secret != self.client_secret or prev_doc.client_id != self.client_id: + self.auth_token = None + self.token_expiry = None diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index cc64b5caa52..e83deae7fba 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -370,9 +370,6 @@ frappe.ui.form.on("Material Request Item", { if (flt(d.qty) < flt(d.min_order_qty)) { frappe.msgprint(__("Warning: Material Requested Qty is less than Minimum Order Qty")); } - - const item = locals[doctype][name]; - frm.events.get_item_data(frm, item, false); }, from_warehouse: function(frm, doctype, name) { diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js index f764859b26c..e78e57cd275 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js @@ -34,6 +34,22 @@ frappe.ui.form.on('Repost Item Valuation', { frm.trigger('setup_realtime_progress'); }, + based_on: function(frm) { + var fields_to_reset = []; + + if (frm.doc.based_on == 'Transaction') { + fields_to_reset = ['item_code', 'warehouse']; + } else if (frm.doc.based_on == 'Item and Warehouse') { + fields_to_reset = ['voucher_type', 'voucher_no']; + } + + if (fields_to_reset) { + fields_to_reset.forEach(field => { + frm.set_value(field, undefined); + }); + } + }, + setup_realtime_progress: function(frm) { frappe.realtime.on('item_reposting_progress', data => { if (frm.doc.name !== data.name) { diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.json b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.json index 2c97d0f5173..d0736619761 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.json +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.json @@ -50,13 +50,15 @@ "fieldname": "posting_date", "fieldtype": "Date", "label": "Posting Date", + "read_only_depends_on": "eval: doc.based_on == \"Transaction\"", "reqd": 1 }, { "fetch_from": "voucher_no.posting_time", "fieldname": "posting_time", "fieldtype": "Time", - "label": "Posting Time" + "label": "Posting Time", + "read_only_depends_on": "eval: doc.based_on == \"Transaction\"" }, { "default": "Queued", @@ -195,7 +197,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2022-06-13 12:20:22.182322", + "modified": "2022-11-28 16:00:05.637440", "modified_by": "Administrator", "module": "Stock", "name": "Repost Item Valuation", diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index 9cc8e237b6a..3503e3e4b29 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -1050,7 +1050,8 @@ erpnext.stock.select_batch_and_serial_no = (frm, item) => { if (frm.doc.purpose === 'Material Receipt') return; frappe.require("assets/erpnext/js/utils/serial_no_batch_selector.js", function() { - new erpnext.SerialNoBatchSelector({ + if (frm.batch_selector?.dialog?.display) return; + frm.batch_selector = new erpnext.SerialNoBatchSelector({ frm: frm, item: item, warehouse_details: get_warehouse_type_and_name(item), diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 823d0ed0b31..b8ba5347510 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -229,7 +229,7 @@ class StockReconciliation(StockController): if item.has_serial_no or item.has_batch_no: has_serial_no = True - self.get_sle_for_serialized_items(row, sl_entries) + self.get_sle_for_serialized_items(row, sl_entries, item) else: if row.serial_no or row.batch_no: frappe.throw( @@ -281,7 +281,7 @@ class StockReconciliation(StockController): if has_serial_no and sl_entries: self.update_valuation_rate_for_serial_no() - def get_sle_for_serialized_items(self, row, sl_entries): + def get_sle_for_serialized_items(self, row, sl_entries, item): from erpnext.stock.stock_ledger import get_previous_sle serial_nos = get_serial_nos(row.serial_no) @@ -347,6 +347,9 @@ class StockReconciliation(StockController): if row.qty: args = self.get_sle_for_items(row) + if item.has_serial_no and item.has_batch_no: + args["qty_after_transaction"] = row.qty + args.update( { "actual_qty": row.qty, diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py index 190ae9edaf9..a7f5f4a8ae2 100644 --- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py @@ -643,6 +643,38 @@ class TestStockReconciliation(FrappeTestCase, StockTestMixin): ) self.assertEqual(len(active_sr_no), 0) + def test_serial_no_batch_no_item(self): + item = self.make_item( + "Test Serial No Batch No Item", + { + "is_stock_item": 1, + "has_serial_no": 1, + "has_batch_no": 1, + "serial_no_series": "SRS9.####", + "batch_number_series": "BNS9.####", + "create_new_batch": 1, + }, + ) + + warehouse = "_Test Warehouse - _TC" + + sr = create_stock_reconciliation( + item_code=item.name, + warehouse=warehouse, + qty=1, + rate=100, + ) + + sl_entry = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_type": "Stock Reconciliation", "voucher_no": sr.name}, + ["actual_qty", "qty_after_transaction"], + as_dict=1, + ) + + self.assertEqual(flt(sl_entry.actual_qty), 1.0) + self.assertEqual(flt(sl_entry.qty_after_transaction), 1.0) + def create_batch_item_with_batch(item_name, batch_id): batch_item_doc = create_item(item_name, is_stock_item=1)