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 2a9b624a47a..1d4ee25241e 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py @@ -417,7 +417,7 @@ class PeriodClosingVoucher(AccountsController): "Period Closing Voucher", {"company": self.company, "docstatus": 1}, "name", - order_by="period_end_date", + order_by="period_end_date asc", ) if not first_pcv or first_pcv == self.name: diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index d05da0dbf19..db6fd41e439 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -4005,6 +4005,25 @@ class TestSalesInvoice(FrappeTestCase): si.submit() self.assertEqual(si.remarks, f"Against Customer Order Test PO dated {format_date(nowdate())}") + def test_gl_voucher_subtype(self): + si = create_sales_invoice() + gl_entries = frappe.get_all( + "GL Entry", + filters={"voucher_type": "Sales Invoice", "voucher_no": si.name}, + pluck="voucher_subtype", + ) + + self.assertTrue(all([x == "Sales Invoice" for x in gl_entries])) + + si = create_sales_invoice(is_return=1, qty=-1) + gl_entries = frappe.get_all( + "GL Entry", + filters={"voucher_type": "Sales Invoice", "voucher_no": si.name}, + pluck="voucher_subtype", + ) + + self.assertTrue(all([x == "Credit Note" for x in gl_entries])) + def set_advance_flag(company, flag, default_account): frappe.db.set_value( diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index c03c896c29f..6ba9cb69f9f 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -581,7 +581,7 @@ class PurchaseOrder(BuyingController): def update_receiving_percentage(self): total_qty, received_qty = 0.0, 0.0 for item in self.items: - received_qty += item.received_qty + received_qty += min(item.received_qty, item.qty) total_qty += item.qty if total_qty: self.db_set("per_received", flt(received_qty / total_qty) * 100, update_modified=False) @@ -625,9 +625,11 @@ class PurchaseOrder(BuyingController): if not self.is_against_so(): return for item in removed_items: - prev_ordered_qty = frappe.get_cached_value( - "Sales Order Item", item.get("sales_order_item"), "ordered_qty" + prev_ordered_qty = ( + frappe.get_cached_value("Sales Order Item", item.get("sales_order_item"), "ordered_qty") + or 0.0 ) + frappe.db.set_value( "Sales Order Item", item.get("sales_order_item"), "ordered_qty", prev_ordered_qty - item.qty ) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index e47e9917149..b14cf428c53 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1096,9 +1096,11 @@ class AccountsController(TransactionBase): return "Purchase Return" elif self.doctype == "Delivery Note" and self.is_return: return "Sales Return" - elif (self.doctype == "Sales Invoice" and self.is_return) or self.doctype == "Purchase Invoice": + elif self.doctype == "Sales Invoice" and self.is_return: return "Credit Note" - elif (self.doctype == "Purchase Invoice" and self.is_return) or self.doctype == "Sales Invoice": + elif self.doctype == "Sales Invoice" and self.is_debit_note: + return "Debit Note" + elif self.doctype == "Purchase Invoice" and self.is_return: return "Debit Note" return self.doctype diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 64c74f9d645..ab416d9d6c3 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -315,7 +315,7 @@ erpnext.patches.v15_0.update_asset_value_for_manual_depr_entries erpnext.patches.v15_0.update_gpa_and_ndb_for_assdeprsch erpnext.patches.v14_0.create_accounting_dimensions_for_closing_balance erpnext.patches.v14_0.set_period_start_end_date_in_pcv -erpnext.patches.v14_0.update_closing_balances #29-10-2024 +erpnext.patches.v14_0.update_closing_balances #08-11-2024 execute:frappe.db.set_single_value("Accounts Settings", "merge_similar_account_heads", 0) erpnext.patches.v14_0.update_reference_type_in_journal_entry_accounts erpnext.patches.v14_0.update_subscription_details @@ -381,3 +381,4 @@ erpnext.patches.v15_0.set_standard_stock_entry_type erpnext.patches.v15_0.link_purchase_item_to_asset_doc erpnext.patches.v14_0.update_currency_exchange_settings_for_frankfurter erpnext.patches.v15_0.update_task_assignee_email_field_in_asset_maintenance_log +erpnext.patches.v15_0.update_sub_voucher_type_in_gl_entries diff --git a/erpnext/patches/v15_0/update_sub_voucher_type_in_gl_entries.py b/erpnext/patches/v15_0/update_sub_voucher_type_in_gl_entries.py new file mode 100644 index 00000000000..7160a6ba87d --- /dev/null +++ b/erpnext/patches/v15_0/update_sub_voucher_type_in_gl_entries.py @@ -0,0 +1,57 @@ +import frappe + + +def execute(): + update_purchase_invoices() + update_sales_invoices() + update_sales_debit_notes() + + +def update_purchase_invoices(): + invoices = frappe.get_all( + "Purchase Invoice", + filters={"docstatus": 1, "is_return": 0}, + pluck="name", + ) + + if not invoices: + return + + update_gl_entry(doctype="Purchase Invoice", invoices=invoices, value="Purchase Invoice") + + +def update_sales_invoices(): + invoices = frappe.get_all( + "Sales Invoice", + filters={"docstatus": 1, "is_return": 0, "is_debit_note": 0}, + pluck="name", + ) + if not invoices: + return + + update_gl_entry(doctype="Sales Invoice", invoices=invoices, value="Sales Invoice") + + +def update_sales_debit_notes(): + invoices = frappe.get_all( + "Sales Invoice", + filters={"docstatus": 1, "is_debit_note": 1}, + pluck="name", + ) + + if not invoices: + return + + update_gl_entry(doctype="Sales Invoice", invoices=invoices, value="Debit Note") + + +def update_gl_entry(doctype, invoices, value): + gl_entry = frappe.qb.DocType("GL Entry") + ( + frappe.qb.update(gl_entry) + .set("voucher_subtype", value) + .where(gl_entry.voucher_subtype.isnotnull()) + .where(gl_entry.voucher_no.isin(invoices)) + .where(gl_entry.voucher_type == doctype) + .run() + ) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index adb72818fe8..1773586ac48 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -289,28 +289,6 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe } ]); } - - if(this.frm.fields_dict['items'].grid.get_field('serial_and_batch_bundle')) { - let sbb_field = this.frm.get_docfield('items', 'serial_and_batch_bundle'); - if (sbb_field) { - sbb_field.get_route_options_for_new_doc = (row) => { - return { - 'item_code': row.doc.item_code, - } - }; - } - } - - if(this.frm.fields_dict['items'].grid.get_field('batch_no')) { - let batch_no_field = this.frm.get_docfield('items', 'batch_no'); - if (batch_no_field) { - batch_no_field.get_route_options_for_new_doc = function(row) { - return { - 'item': row.doc.item_code - } - }; - } - } } is_return() { @@ -409,6 +387,35 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe this.setup_quality_inspection(); this.validate_has_items(); erpnext.utils.view_serial_batch_nos(this.frm); + this.set_route_options_for_new_doc(); + } + + set_route_options_for_new_doc() { + // While creating the batch from the link field, copy item from line item to batch form + + if(this.frm.fields_dict['items'].grid.get_field('batch_no')) { + let batch_no_field = this.frm.get_docfield('items', 'batch_no'); + if (batch_no_field) { + batch_no_field.get_route_options_for_new_doc = function(row) { + return { + 'item': row.doc.item_code + } + }; + } + } + + // While creating the SABB from the link field, copy item, doctype from line item to SABB form + if(this.frm.fields_dict['items'].grid.get_field('serial_and_batch_bundle')) { + let sbb_field = this.frm.get_docfield('items', 'serial_and_batch_bundle'); + if (sbb_field) { + sbb_field.get_route_options_for_new_doc = (row) => { + return { + "item_code": row.doc.item_code, + "voucher_type": this.frm.doc.doctype, + } + }; + } + } } scan_barcode() { diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index a87c1352471..62262b41021 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -584,7 +584,7 @@ class SalesOrder(SellingController): item_delivered_qty = item_delivered_qty[0][0] if item_delivered_qty else 0 item.db_set("delivered_qty", flt(item_delivered_qty), update_modified=False) - delivered_qty += item.delivered_qty + delivered_qty += min(item.delivered_qty, item.qty) tot_qty += item.qty if tot_qty != 0: @@ -1347,6 +1347,8 @@ def make_purchase_order_for_default_supplier(source_name, selected_items=None, t "discount_percentage", "discount_amount", "pricing_rules", + "margin_type", + "margin_rate_or_amount", ], "postprocess": update_item, "condition": lambda doc: doc.ordered_qty < doc.stock_qty