diff --git a/erpnext/accounts/doctype/dunning/dunning.json b/erpnext/accounts/doctype/dunning/dunning.json index b7e8aeaaafd..32734628c1e 100644 --- a/erpnext/accounts/doctype/dunning/dunning.json +++ b/erpnext/accounts/doctype/dunning/dunning.json @@ -185,7 +185,7 @@ }, { "fieldname": "address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "label": "Address", "read_only": 1 }, @@ -204,7 +204,7 @@ }, { "fieldname": "company_address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "label": "Company Address Display", "read_only": 1 }, @@ -381,7 +381,7 @@ ], "is_submittable": 1, "links": [], - "modified": "2023-06-15 15:46:53.865712", + "modified": "2024-03-22 16:01:13.231067", "modified_by": "Administrator", "module": "Accounts", "name": "Dunning", diff --git a/erpnext/accounts/doctype/dunning/dunning.py b/erpnext/accounts/doctype/dunning/dunning.py index e3897bf4aa1..f7c4d90b4bd 100644 --- a/erpnext/accounts/doctype/dunning/dunning.py +++ b/erpnext/accounts/doctype/dunning/dunning.py @@ -32,14 +32,14 @@ class Dunning(AccountsController): from erpnext.accounts.doctype.overdue_payment.overdue_payment import OverduePayment - address_display: DF.SmallText | None + address_display: DF.TextEditor | None amended_from: DF.Link | None base_dunning_amount: DF.Currency body_text: DF.TextEditor | None closing_text: DF.TextEditor | None company: DF.Link company_address: DF.Link | None - company_address_display: DF.SmallText | None + company_address_display: DF.TextEditor | None contact_display: DF.SmallText | None contact_email: DF.Data | None contact_mobile: DF.SmallText | None diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js index 741039acae6..7d467cb085c 100644 --- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js +++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.js @@ -57,13 +57,13 @@ frappe.ui.form.on("Exchange Rate Revaluation", { get_entries: function (frm, account) { frappe.call({ method: "get_accounts_data", - doc: cur_frm.doc, + doc: frm.doc, account: account, callback: function (r) { frappe.model.clear_table(frm.doc, "accounts"); if (r.message) { r.message.forEach((d) => { - cur_frm.add_child("accounts", d); + frm.add_child("accounts", d); }); frm.events.get_total_gain_loss(frm); refresh_field("accounts"); diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js index 6d90c26415b..f65f0c274c0 100644 --- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js +++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js @@ -189,7 +189,7 @@ frappe.ui.form.on("Invoice Discounting", { show_general_ledger: (frm) => { if (frm.doc.docstatus > 0) { - cur_frm.add_custom_button( + frm.add_custom_button( __("Accounting Ledger"), function () { frappe.route_options = { diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index 3186d07adcc..ed134bab879 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -255,7 +255,7 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro } onload_post_render() { - cur_frm.get_field("accounts").grid.set_multiple_add("account"); + this.frm.get_field("accounts").grid.set_multiple_add("account"); } load_defaults() { @@ -402,7 +402,7 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro row.debit = -doc.difference; } } - cur_frm.cscript.update_totals(doc); + this.frm.cscript.update_totals(doc); erpnext.accounts.dimensions.copy_dimension_from_first_row(this.frm, cdt, cdn, "accounts"); } @@ -469,11 +469,11 @@ frappe.ui.form.on("Journal Entry Account", { }, debit: function (frm, dt, dn) { - cur_frm.cscript.update_totals(frm.doc); + frm.cscript.update_totals(frm.doc); }, credit: function (frm, dt, dn) { - cur_frm.cscript.update_totals(frm.doc); + frm.cscript.update_totals(frm.doc); }, exchange_rate: function (frm, cdt, cdn) { @@ -489,7 +489,7 @@ frappe.ui.form.on("Journal Entry Account", { }); frappe.ui.form.on("Journal Entry Account", "accounts_remove", function (frm) { - cur_frm.cscript.update_totals(frm.doc); + frm.cscript.update_totals(frm.doc); }); $.extend(erpnext.journal_entry, { @@ -531,7 +531,7 @@ $.extend(erpnext.journal_entry, { flt(flt(row.credit_in_account_currency) * row.exchange_rate, precision("credit", row)) ); - cur_frm.cscript.update_totals(frm.doc); + frm.cscript.update_totals(frm.doc); }, set_exchange_rate: function (frm, cdt, cdn) { @@ -673,10 +673,10 @@ $.extend(erpnext.journal_entry, { return { filters: filters }; }, - reverse_journal_entry: function () { + reverse_journal_entry: function (frm) { frappe.model.open_mapped_doc({ method: "erpnext.accounts.doctype.journal_entry.journal_entry.make_reverse_journal_entry", - frm: cur_frm, + frm: frm, }); }, }); diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 0cb1a3d4997..781d9f52467 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -322,13 +322,13 @@ frappe.ui.form.on("Payment Entry", { "references" ); - cur_frm.set_df_property( + frm.set_df_property( "source_exchange_rate", "description", "1 " + frm.doc.paid_from_account_currency + " = [?] " + company_currency ); - cur_frm.set_df_property( + frm.set_df_property( "target_exchange_rate", "description", "1 " + frm.doc.paid_to_account_currency + " = [?] " + company_currency diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js index a6e8bfa6286..52c770667cd 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js @@ -193,7 +193,7 @@ erpnext.selling.POSInvoiceController = class POSInvoiceController extends erpnex make_sales_return() { frappe.model.open_mapped_doc({ method: "erpnext.accounts.doctype.pos_invoice.pos_invoice.make_sales_return", - frm: cur_frm, + frm: this.frm, }); } }; @@ -293,7 +293,7 @@ frappe.ui.form.on("POS Invoice", { }); } else if (frappe.dom.freeze_count != 0) { frappe.dom.unfreeze(); - cur_frm.reload_doc(); + frm.reload_doc(); cur_pos.payment.events.submit_invoice(); frappe.show_alert({ diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json index d7b173667ec..467ea54c70a 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json @@ -420,7 +420,7 @@ }, { "fieldname": "address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "label": "Address", "read_only": 1 }, @@ -475,7 +475,7 @@ }, { "fieldname": "shipping_address", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "label": "Shipping Address", "print_hide": 1, "read_only": 1 @@ -489,7 +489,7 @@ }, { "fieldname": "company_address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "hidden": 1, "label": "Company Address", "print_hide": 1, @@ -1563,7 +1563,7 @@ "icon": "fa fa-file-text", "is_submittable": 1, "links": [], - "modified": "2024-03-20 16:00:34.268756", + "modified": "2024-03-22 16:15:08.561034", "modified_by": "Administrator", "module": "Accounts", "name": "POS Invoice", diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index 5db3da1f5ea..8052c4c5057 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -47,7 +47,7 @@ class POSInvoice(SalesInvoice): account_for_change_amount: DF.Link | None additional_discount_percentage: DF.Float - address_display: DF.SmallText | None + address_display: DF.TextEditor | None advances: DF.Table[SalesInvoiceAdvance] against_income_account: DF.SmallText | None allocate_advances_automatically: DF.Check @@ -72,7 +72,7 @@ class POSInvoice(SalesInvoice): commission_rate: DF.Float company: DF.Link company_address: DF.Link | None - company_address_display: DF.SmallText | None + company_address_display: DF.TextEditor | None consolidated_invoice: DF.Link | None contact_display: DF.SmallText | None contact_email: DF.Data | None @@ -138,7 +138,7 @@ class POSInvoice(SalesInvoice): selling_price_list: DF.Link set_posting_time: DF.Check set_warehouse: DF.Link | None - shipping_address: DF.SmallText | None + shipping_address: DF.TextEditor | None shipping_address_name: DF.Link | None shipping_rule: DF.Link | None source: DF.Link | None diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index 17293adb95e..73c5d59be25 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -736,7 +736,6 @@ def get_pricing_rule_items(pr_doc, other_items=False) -> list: def validate_coupon_code(coupon_name): coupon = frappe.get_doc("Coupon Code", coupon_name) - if coupon.valid_from: if coupon.valid_from > getdate(today()): frappe.throw(_("Sorry, this coupon code's validity has not started")) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 957611f7858..2bd39a59778 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -131,17 +131,17 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying. if (doc.docstatus == 1 && doc.outstanding_amount != 0 && !doc.on_hold) { this.frm.add_custom_button(__("Payment"), () => this.make_payment_entry(), __("Create")); - cur_frm.page.set_inner_btn_group_as_primary(__("Create")); + this.frm.page.set_inner_btn_group_as_primary(__("Create")); } if (!doc.is_return && doc.docstatus == 1) { if (doc.outstanding_amount >= 0 || Math.abs(flt(doc.outstanding_amount)) < flt(doc.grand_total)) { - cur_frm.add_custom_button(__("Return / Debit Note"), this.make_debit_note, __("Create")); + this.frm.add_custom_button(__("Return / Debit Note"), this.make_debit_note, __("Create")); } } if (doc.outstanding_amount > 0 && !cint(doc.is_return) && !doc.on_hold) { - cur_frm.add_custom_button( + this.frm.add_custom_button( __("Payment Request"), function () { me.make_payment_request(); @@ -462,7 +462,7 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying. make_debit_note() { frappe.model.open_mapped_doc({ method: "erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_debit_note", - frm: cur_frm, + frm: this.frm, }); } }; diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 44d3d48933f..01a3746dcf2 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -443,7 +443,7 @@ }, { "fieldname": "address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "label": "Address", "read_only": 1 }, @@ -489,7 +489,7 @@ }, { "fieldname": "shipping_address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "label": "Shipping Address", "print_hide": 1, "read_only": 1 @@ -1363,7 +1363,7 @@ }, { "fieldname": "billing_address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "label": "Billing Address", "read_only": 1 }, @@ -1638,7 +1638,7 @@ "idx": 204, "is_submittable": 1, "links": [], - "modified": "2024-03-20 15:57:00.736868", + "modified": "2024-03-22 16:15:09.099187", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index 19b7092afdf..4b5b456361f 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -82,7 +82,7 @@ class PurchaseInvoice(BuyingController): ) additional_discount_percentage: DF.Float - address_display: DF.SmallText | None + address_display: DF.TextEditor | None advance_tax: DF.Table[AdvanceTax] advances: DF.Table[PurchaseInvoiceAdvance] against_expense_account: DF.SmallText | None @@ -107,7 +107,7 @@ class PurchaseInvoice(BuyingController): bill_date: DF.Date | None bill_no: DF.Data | None billing_address: DF.Link | None - billing_address_display: DF.SmallText | None + billing_address_display: DF.TextEditor | None buying_price_list: DF.Link | None cash_bank_account: DF.Link | None clearance_date: DF.Date | None @@ -174,7 +174,7 @@ class PurchaseInvoice(BuyingController): set_posting_time: DF.Check set_warehouse: DF.Link | None shipping_address: DF.Link | None - shipping_address_display: DF.SmallText | None + shipping_address_display: DF.TextEditor | None shipping_rule: DF.Link | None status: DF.Literal[ "", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index c7505ce007d..cf01bdd28e4 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -61,9 +61,9 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends ( refresh(doc, dt, dn) { const me = this; super.refresh(); - if (cur_frm.msgbox && cur_frm.msgbox.$wrapper.is(":visible")) { + if (this.frm.msgbox && this.frm.msgbox.$wrapper.is(":visible")) { // hide new msgbox - cur_frm.msgbox.hide(); + this.frm.msgbox.hide(); } this.frm.toggle_reqd("due_date", !this.frm.doc.is_return); @@ -113,33 +113,33 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends ( if (doc.docstatus == 1 && !doc.is_return) { var is_delivered_by_supplier = false; - is_delivered_by_supplier = cur_frm.doc.items.some(function (item) { + is_delivered_by_supplier = this.frm.doc.items.some(function (item) { return item.is_delivered_by_supplier ? true : false; }); if (doc.outstanding_amount >= 0 || Math.abs(flt(doc.outstanding_amount)) < flt(doc.grand_total)) { - cur_frm.add_custom_button(__("Return / Credit Note"), this.make_sales_return, __("Create")); - cur_frm.page.set_inner_btn_group_as_primary(__("Create")); + this.frm.add_custom_button(__("Return / Credit Note"), this.make_sales_return, __("Create")); + this.frm.page.set_inner_btn_group_as_primary(__("Create")); } if (cint(doc.update_stock) != 1) { // show Make Delivery Note button only if Sales Invoice is not created from Delivery Note var from_delivery_note = false; - from_delivery_note = cur_frm.doc.items.some(function (item) { + from_delivery_note = this.frm.doc.items.some(function (item) { return item.delivery_note ? true : false; }); if (!from_delivery_note && !is_delivered_by_supplier) { - cur_frm.add_custom_button( + this.frm.add_custom_button( __("Delivery"), - cur_frm.cscript["Make Delivery Note"], + this.frm.cscript["Make Delivery Note"], __("Create") ); } } if (doc.outstanding_amount > 0) { - cur_frm.add_custom_button( + this.frm.add_custom_button( __("Payment Request"), function () { me.make_payment_request(); @@ -147,10 +147,10 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends ( __("Create") ); - cur_frm.add_custom_button( + this.frm.add_custom_button( __("Invoice Discounting"), function () { - cur_frm.events.create_invoice_discounting(cur_frm); + this.frm.events.create_invoice_discounting(this.frm); }, __("Create") ); @@ -171,10 +171,10 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends ( } if (doc.docstatus === 1) { - cur_frm.add_custom_button( + this.frm.add_custom_button( __("Maintenance Schedule"), function () { - cur_frm.cscript.make_maintenance_schedule(); + this.frm.cscript.make_maintenance_schedule(); }, __("Create") ); @@ -182,7 +182,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends ( } // Show buttons only when pos view is active - if (cint(doc.docstatus == 0) && cur_frm.page.current_view_name !== "pos" && !doc.is_return) { + if (cint(doc.docstatus == 0) && this.frm.page.current_view_name !== "pos" && !doc.is_return) { this.frm.cscript.sales_order_btn(); this.frm.cscript.delivery_note_btn(); this.frm.cscript.quotation_btn(); @@ -213,7 +213,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends ( make_maintenance_schedule() { frappe.model.open_mapped_doc({ method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.make_maintenance_schedule", - frm: cur_frm, + frm: this.frm, }); } @@ -232,28 +232,27 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends ( set_default_print_format() { // set default print format to POS type or Credit Note - if (cur_frm.doc.is_pos) { - if (cur_frm.pos_print_format) { - cur_frm.meta._default_print_format = cur_frm.meta.default_print_format; - cur_frm.meta.default_print_format = cur_frm.pos_print_format; + if (this.frm.doc.is_pos) { + if (this.frm.pos_print_format) { + this.frm.meta._default_print_format = this.frm.meta.default_print_format; + this.frm.meta.default_print_format = this.frm.pos_print_format; } - } else if (cur_frm.doc.is_return && !cur_frm.meta.default_print_format) { - if (cur_frm.return_print_format) { - cur_frm.meta._default_print_format = cur_frm.meta.default_print_format; - cur_frm.meta.default_print_format = cur_frm.return_print_format; + } else if (this.frm.doc.is_return && !this.frm.meta.default_print_format) { + if (this.frm.return_print_format) { + this.frm.meta._default_print_format = this.frm.meta.default_print_format; + this.frm.meta.default_print_format = this.frm.return_print_format; } } else { - if (cur_frm.meta._default_print_format) { - cur_frm.meta.default_print_format = cur_frm.meta._default_print_format; - cur_frm.meta._default_print_format = null; + if (this.frm.meta._default_print_format) { + this.frm.meta.default_print_format = this.frm.meta._default_print_format; + this.frm.meta._default_print_format = null; } else if ( - in_list( - [cur_frm.pos_print_format, cur_frm.return_print_format], - cur_frm.meta.default_print_format + [this.frm.pos_print_format, this.frm.return_print_format].includes( + this.frm.meta.default_print_format ) ) { - cur_frm.meta.default_print_format = null; - cur_frm.meta._default_print_format = null; + this.frm.meta.default_print_format = null; + this.frm.meta._default_print_format = null; } } } @@ -465,7 +464,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends ( make_sales_return() { frappe.model.open_mapped_doc({ method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.make_sales_return", - frm: cur_frm, + frm: this.frm, }); } diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index 1d8983e8eef..436f5105629 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -90,6 +90,7 @@ "section_break_49", "apply_discount_on", "base_discount_amount", + "coupon_code", "is_cash_or_non_trade_discount", "additional_discount_account", "column_break_51", @@ -494,7 +495,7 @@ }, { "fieldname": "address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "hide_days": 1, "hide_seconds": 1, "label": "Address", @@ -565,7 +566,7 @@ }, { "fieldname": "shipping_address", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "hide_days": 1, "hide_seconds": 1, "label": "Shipping Address", @@ -583,7 +584,7 @@ }, { "fieldname": "company_address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "hide_days": 1, "hide_seconds": 1, "label": "Company Address", @@ -1998,7 +1999,7 @@ { "allow_on_submit": 1, "fieldname": "dispatch_address", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "label": "Dispatch Address", "read_only": 1 }, @@ -2173,6 +2174,12 @@ "label": "Don't Create Loyalty Points", "no_copy": 1 }, + { + "fieldname": "coupon_code", + "fieldtype": "Link", + "label": "Coupon Code", + "options": "Coupon Code" + }, { "default": "1", "depends_on": "eval: doc.is_return && doc.return_against", @@ -2180,7 +2187,8 @@ "fieldname": "update_outstanding_for_self", "fieldtype": "Check", "label": "Update Outstanding for Self", - "no_copy": 1 + "no_copy": 1, + "print_hide": 1 } ], "icon": "fa fa-file-text", @@ -2193,7 +2201,7 @@ "link_fieldname": "consolidated_invoice" } ], - "modified": "2024-03-20 16:02:52.237732", + "modified": "2024-03-22 17:50:34.395602", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", @@ -2248,4 +2256,4 @@ "title_field": "title", "track_changes": 1, "track_seen": 1 -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 1228bbb677f..a35c6b6fc11 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -16,6 +16,10 @@ from erpnext.accounts.doctype.loyalty_program.loyalty_program import ( get_loyalty_program_details_with_points, validate_loyalty_points, ) +from erpnext.accounts.doctype.pricing_rule.utils import ( + update_coupon_code_count, + validate_coupon_code, +) from erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger import ( validate_docs_for_deferred_accounting, validate_docs_for_voucher_types, @@ -73,7 +77,7 @@ class SalesInvoice(SellingController): account_for_change_amount: DF.Link | None additional_discount_account: DF.Link | None additional_discount_percentage: DF.Float - address_display: DF.SmallText | None + address_display: DF.TextEditor | None advances: DF.Table[SalesInvoiceAdvance] against_income_account: DF.SmallText | None allocate_advances_automatically: DF.Check @@ -98,7 +102,7 @@ class SalesInvoice(SellingController): commission_rate: DF.Float company: DF.Link company_address: DF.Link | None - company_address_display: DF.SmallText | None + company_address_display: DF.TextEditor | None company_tax_id: DF.Data | None contact_display: DF.SmallText | None contact_email: DF.Data | None @@ -106,6 +110,7 @@ class SalesInvoice(SellingController): contact_person: DF.Link | None conversion_rate: DF.Float cost_center: DF.Link | None + coupon_code: DF.Link | None currency: DF.Link customer: DF.Link | None customer_address: DF.Link | None @@ -114,7 +119,7 @@ class SalesInvoice(SellingController): debit_to: DF.Link disable_rounded_total: DF.Check discount_amount: DF.Currency - dispatch_address: DF.SmallText | None + dispatch_address: DF.TextEditor | None dispatch_address_name: DF.Link | None dont_create_loyalty_points: DF.Check due_date: DF.Date | None @@ -178,7 +183,7 @@ class SalesInvoice(SellingController): set_posting_time: DF.Check set_target_warehouse: DF.Link | None set_warehouse: DF.Link | None - shipping_address: DF.SmallText | None + shipping_address: DF.TextEditor | None shipping_address_name: DF.Link | None shipping_rule: DF.Link | None source: DF.Link | None @@ -294,6 +299,10 @@ class SalesInvoice(SellingController): self.doctype, self.customer, self.company, self.inter_company_invoice_reference ) + # Validating coupon code + if self.coupon_code: + validate_coupon_code(self.coupon_code) + if cint(self.is_pos): self.validate_pos() @@ -473,6 +482,9 @@ class SalesInvoice(SellingController): self.update_project() update_linked_doc(self.doctype, self.name, self.inter_company_invoice_reference) + if self.coupon_code: + update_coupon_code_count(self.coupon_code, "used") + # create the loyalty point ledger entry if the customer is enrolled in any loyalty program if ( not self.is_return @@ -563,6 +575,9 @@ class SalesInvoice(SellingController): self.db_set("status", "Cancelled") self.db_set("repost_required", 0) + if self.coupon_code: + update_coupon_code_count(self.coupon_code, "cancelled") + if ( frappe.db.get_single_value("Selling Settings", "sales_update_frequency") == "Each Transaction" ): diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index 7162aef8f22..25958692a22 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -669,20 +669,20 @@ class GrossProfitGenerator(object): elif row.sales_order and row.so_detail: incoming_amount = self.get_buying_amount_from_so_dn(row.sales_order, row.so_detail, item_code) if incoming_amount: - return incoming_amount + return flt(row.qty) * incoming_amount else: return flt(row.qty) * self.get_average_buying_rate(row, item_code) return flt(row.qty) * self.get_average_buying_rate(row, item_code) def get_buying_amount_from_so_dn(self, sales_order, so_detail, item_code): - from frappe.query_builder.functions import Sum + from frappe.query_builder.functions import Avg delivery_note_item = frappe.qb.DocType("Delivery Note Item") query = ( frappe.qb.from_(delivery_note_item) - .select(Sum(delivery_note_item.incoming_rate * delivery_note_item.stock_qty)) + .select(Avg(delivery_note_item.incoming_rate)) .where(delivery_note_item.docstatus == 1) .where(delivery_note_item.item_code == item_code) .where(delivery_note_item.against_sales_order == sales_order) diff --git a/erpnext/accounts/report/gross_profit/test_gross_profit.py b/erpnext/accounts/report/gross_profit/test_gross_profit.py index 82fe1a0ba12..aa820aa4c73 100644 --- a/erpnext/accounts/report/gross_profit/test_gross_profit.py +++ b/erpnext/accounts/report/gross_profit/test_gross_profit.py @@ -460,3 +460,95 @@ 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_different_rates_in_si_and_dn(self): + from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order + + """ + Test gp calculation when invoice and delivery note differ in qty and aren't connected + SO -- INV + | + DN + """ + se = make_stock_entry( + company=self.company, + item_code=self.item, + target=self.warehouse, + qty=3, + basic_rate=700, + do_not_submit=True, + ) + item = se.items[0] + se.append( + "items", + { + "item_code": item.item_code, + "s_warehouse": item.s_warehouse, + "t_warehouse": item.t_warehouse, + "qty": 10, + "basic_rate": 700, + "conversion_factor": item.conversion_factor or 1.0, + "transfer_qty": flt(item.qty) * (flt(item.conversion_factor) or 1.0), + "serial_no": item.serial_no, + "batch_no": item.batch_no, + "cost_center": item.cost_center, + "expense_account": item.expense_account, + }, + ) + se = se.save().submit() + + so = make_sales_order( + customer=self.customer, + company=self.company, + warehouse=self.warehouse, + item=self.item, + rate=800, + qty=10, + do_not_save=False, + do_not_submit=False, + ) + + from erpnext.selling.doctype.sales_order.sales_order import ( + make_delivery_note, + make_sales_invoice, + ) + + dn1 = make_delivery_note(so.name) + dn1.items[0].qty = 4 + dn1.items[0].rate = 800 + dn1.save().submit() + + dn2 = make_delivery_note(so.name) + dn2.items[0].qty = 6 + dn2.items[0].rate = 800 + dn2.save().submit() + + sinv = make_sales_invoice(so.name) + sinv.items[0].qty = 4 + sinv.items[0].rate = 800 + sinv.save().submit() + + filters = frappe._dict( + company=self.company, from_date=nowdate(), to_date=nowdate(), group_by="Invoice" + ) + + columns, data = execute(filters=filters) + expected_entry = { + "parent_invoice": sinv.name, + "currency": "INR", + "sales_invoice": self.item, + "customer": self.customer, + "posting_date": frappe.utils.datetime.date.fromisoformat(nowdate()), + "item_code": self.item, + "item_name": self.item, + "warehouse": "Stores - _GP", + "qty": 4.0, + "avg._selling_rate": 800.0, + "valuation_rate": 700.0, + "selling_amount": 3200.0, + "buying_amount": 2800.0, + "gross_profit": 400.0, + "gross_profit_%": 12.5, + } + gp_entry = [x for x in data if x.parent_invoice == sinv.name] + self.assertDictContainsSubset(expected_entry, gp_entry[0]) diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js index 2ffd3b310f9..98c6656ca3f 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js @@ -24,3 +24,10 @@ frappe.query_reports["Profit and Loss Statement"]["filters"].push({ fieldtype: "Check", default: 1, }); + +frappe.query_reports["Profit and Loss Statement"]["filters"].push({ + fieldname: "include_default_book_entries", + label: __("Include Default FB Entries"), + fieldtype: "Check", + default: 1, +}); diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index 0f71e5d6f60..b4fd73956e0 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -48,7 +48,7 @@ frappe.ui.form.on("Asset", { method: "erpnext.assets.doctype.asset.asset.make_asset_movement", freeze: true, args: { - assets: [{ name: cur_frm.doc.name }], + assets: [{ name: frm.doc.name }], }, callback: function (r) { if (r.message) { @@ -791,9 +791,7 @@ erpnext.asset.scrap_asset = function (frm) { asset_name: frm.doc.name, }, method: "erpnext.assets.doctype.asset.depreciation.scrap_asset", - callback: function (r) { - cur_frm.reload_doc(); - }, + callback: (r) => frm.reload_doc(), }); }); }; @@ -805,19 +803,17 @@ erpnext.asset.restore_asset = function (frm) { asset_name: frm.doc.name, }, method: "erpnext.assets.doctype.asset.depreciation.restore_asset", - callback: function (r) { - cur_frm.reload_doc(); - }, + callback: (r) => frm.reload_doc(), }); }); }; -erpnext.asset.transfer_asset = function () { +erpnext.asset.transfer_asset = function (frm) { frappe.call({ method: "erpnext.assets.doctype.asset.asset.make_asset_movement", freeze: true, args: { - assets: [{ name: cur_frm.doc.name }], + assets: [{ name: frm.doc.name }], purpose: "Transfer", }, callback: function (r) { diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index cf383021b06..c3a155a0133 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -86,7 +86,7 @@ frappe.ui.form.on("Purchase Order", { args: { subcontract_order: frm.doc.name, rm_details: po_details, - order_doctype: cur_frm.doc.doctype, + order_doctype: frm.doc.doctype, }, callback: function (r) { if (r && r.message) { @@ -270,8 +270,8 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends ( var allow_receipt = false; var is_drop_ship = false; - for (var i in cur_frm.doc.items) { - var item = cur_frm.doc.items[i]; + for (var i in this.frm.doc.items) { + var item = this.frm.doc.items[i]; if (item.delivered_by_supplier !== 1) { allow_receipt = true; } else { @@ -348,7 +348,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends ( if (doc.status != "Closed") { if (doc.status != "On Hold") { if (flt(doc.per_received, 2) < 100 && allow_receipt) { - cur_frm.add_custom_button( + this.frm.add_custom_button( __("Purchase Receipt"), this.make_purchase_receipt, __("Create") @@ -356,7 +356,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends ( if (doc.is_subcontracted) { if (doc.is_old_subcontracting_flow) { if (me.has_unsupplied_items()) { - cur_frm.add_custom_button( + this.frm.add_custom_button( __("Material to Supplier"), function () { me.make_stock_entry(); @@ -365,7 +365,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends ( ); } } else { - cur_frm.add_custom_button( + this.frm.add_custom_button( __("Subcontracting Order"), this.make_subcontracting_order, __("Create") @@ -374,7 +374,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends ( } } if (flt(doc.per_billed, 2) < 100) - cur_frm.add_custom_button( + this.frm.add_custom_button( __("Purchase Invoice"), this.make_purchase_invoice, __("Create") @@ -418,10 +418,10 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends ( } } - cur_frm.page.set_inner_btn_group_as_primary(__("Create")); + this.frm.page.set_inner_btn_group_as_primary(__("Create")); } } else if (doc.docstatus === 0) { - cur_frm.cscript.add_from_mappers(); + this.frm.cscript.add_from_mappers(); } } @@ -458,8 +458,8 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends ( frappe.call({ method: "erpnext.controllers.subcontracting_controller.make_rm_stock_entry", args: { - subcontract_order: cur_frm.doc.name, - order_doctype: cur_frm.doc.doctype, + subcontract_order: this.frm.doc.name, + order_doctype: this.frm.doc.doctype, }, callback: function (r) { var doclist = frappe.model.sync(r.message); @@ -478,7 +478,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends ( make_purchase_receipt() { frappe.model.open_mapped_doc({ method: "erpnext.buying.doctype.purchase_order.purchase_order.make_purchase_receipt", - frm: cur_frm, + frm: this.frm, freeze_message: __("Creating Purchase Receipt ..."), }); } @@ -486,14 +486,14 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends ( make_purchase_invoice() { frappe.model.open_mapped_doc({ method: "erpnext.buying.doctype.purchase_order.purchase_order.make_purchase_invoice", - frm: cur_frm, + frm: this.frm, }); } make_subcontracting_order() { frappe.model.open_mapped_doc({ method: "erpnext.buying.doctype.purchase_order.purchase_order.make_subcontracting_order", - frm: cur_frm, + frm: this.frm, freeze_message: __("Creating Subcontracting Order ..."), }); } @@ -652,7 +652,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends ( } unhold_purchase_order() { - cur_frm.cscript.update_status("Resume", "Draft"); + this.frm.cscript.update_status("Resume", "Draft"); } hold_purchase_order() { @@ -692,15 +692,15 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends ( } unclose_purchase_order() { - cur_frm.cscript.update_status("Re-open", "Submitted"); + this.frm.cscript.update_status("Re-open", "Submitted"); } close_purchase_order() { - cur_frm.cscript.update_status("Close", "Closed"); + this.frm.cscript.update_status("Close", "Closed"); } delivered_by_supplier() { - cur_frm.cscript.update_status("Deliver", "Delivered"); + this.frm.cscript.update_status("Deliver", "Delivered"); } items_on_form_rendered() { diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index 0916ff5f8d0..1ee97942a8c 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -355,7 +355,7 @@ }, { "fieldname": "address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "label": "Supplier Address Details", "read_only": 1 }, @@ -394,7 +394,7 @@ }, { "fieldname": "shipping_address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "label": "Shipping Address Details", "print_hide": 1, "read_only": 1 @@ -1098,7 +1098,7 @@ }, { "fieldname": "billing_address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "label": "Billing Address Details", "read_only": 1 }, @@ -1288,7 +1288,7 @@ "idx": 105, "is_submittable": 1, "links": [], - "modified": "2024-03-20 16:03:31.611808", + "modified": "2024-03-22 16:15:09.674963", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 4f24ec2acc2..e462820350b 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -55,7 +55,7 @@ class PurchaseOrder(BuyingController): ) additional_discount_percentage: DF.Float - address_display: DF.SmallText | None + address_display: DF.TextEditor | None advance_paid: DF.Currency advance_payment_status: DF.Literal["Not Initiated", "Initiated", "Partially Paid", "Fully Paid"] amended_from: DF.Link | None @@ -74,7 +74,7 @@ class PurchaseOrder(BuyingController): base_total: DF.Currency base_total_taxes_and_charges: DF.Currency billing_address: DF.Link | None - billing_address_display: DF.SmallText | None + billing_address_display: DF.TextEditor | None buying_price_list: DF.Link | None company: DF.Link contact_display: DF.SmallText | None @@ -131,7 +131,7 @@ class PurchaseOrder(BuyingController): set_reserve_warehouse: DF.Link | None set_warehouse: DF.Link | None shipping_address: DF.Link | None - shipping_address_display: DF.SmallText | None + shipping_address_display: DF.TextEditor | None shipping_rule: DF.Link | None status: DF.Literal[ "", diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json index fd73f77ff8f..f386b64ea0e 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json @@ -303,7 +303,7 @@ }, { "fieldname": "billing_address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "label": "Billing Address Details", "read_only": 1 } @@ -312,7 +312,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2023-11-06 12:45:28.898706", + "modified": "2024-03-22 16:01:19.097788", "modified_by": "Administrator", "module": "Buying", "name": "Request for Quotation", diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py index f261773b814..fb4dc6ae53e 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py @@ -40,7 +40,7 @@ class RequestforQuotation(BuyingController): amended_from: DF.Link | None billing_address: DF.Link | None - billing_address_display: DF.SmallText | None + billing_address_display: DF.TextEditor | None company: DF.Link email_template: DF.Link | None incoterm: DF.Link | None diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js index c1698710135..abb57023103 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.js @@ -22,9 +22,9 @@ erpnext.buying.SupplierQuotationController = class SupplierQuotationController e this.frm.set_value("valid_till", frappe.datetime.add_months(this.frm.doc.transaction_date, 1)); } if (this.frm.doc.docstatus === 1) { - cur_frm.add_custom_button(__("Purchase Order"), this.make_purchase_order, __("Create")); - cur_frm.page.set_inner_btn_group_as_primary(__("Create")); - cur_frm.add_custom_button(__("Quotation"), this.make_quotation, __("Create")); + this.frm.add_custom_button(__("Purchase Order"), this.make_purchase_order, __("Create")); + this.frm.page.set_inner_btn_group_as_primary(__("Create")); + this.frm.add_custom_button(__("Quotation"), this.make_quotation, __("Create")); } else if (this.frm.doc.docstatus === 0) { this.frm.add_custom_button( __("Material Request"), @@ -87,13 +87,13 @@ erpnext.buying.SupplierQuotationController = class SupplierQuotationController e make_purchase_order() { frappe.model.open_mapped_doc({ method: "erpnext.buying.doctype.supplier_quotation.supplier_quotation.make_purchase_order", - frm: cur_frm, + frm: this.frm, }); } make_quotation() { frappe.model.open_mapped_doc({ method: "erpnext.buying.doctype.supplier_quotation.supplier_quotation.make_quotation", - frm: cur_frm, + frm: this.frm, }); } }; diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json index 09be247b363..993cde0ab61 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json @@ -228,7 +228,7 @@ }, { "fieldname": "address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "label": "Address", "read_only": 1 }, @@ -865,7 +865,7 @@ }, { "fieldname": "shipping_address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "label": "Shipping Address Details", "print_hide": 1, "read_only": 1 @@ -897,7 +897,7 @@ }, { "fieldname": "billing_address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "label": "Billing Address Details", "read_only": 1 }, @@ -928,7 +928,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2024-03-20 16:03:59.069145", + "modified": "2024-03-22 16:15:10.122197", "modified_by": "Administrator", "module": "Buying", "name": "Supplier Quotation", diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py index b716f7f0427..52bd83bfd2c 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.py @@ -31,7 +31,7 @@ class SupplierQuotation(BuyingController): ) additional_discount_percentage: DF.Float - address_display: DF.SmallText | None + address_display: DF.TextEditor | None amended_from: DF.Link | None apply_discount_on: DF.Literal["", "Grand Total", "Net Total"] auto_repeat: DF.Link | None @@ -46,7 +46,7 @@ class SupplierQuotation(BuyingController): base_total: DF.Currency base_total_taxes_and_charges: DF.Currency billing_address: DF.Link | None - billing_address_display: DF.SmallText | None + billing_address_display: DF.TextEditor | None buying_price_list: DF.Link | None company: DF.Link contact_display: DF.SmallText | None @@ -81,7 +81,7 @@ class SupplierQuotation(BuyingController): rounding_adjustment: DF.Currency select_print_heading: DF.Link | None shipping_address: DF.Link | None - shipping_address_display: DF.SmallText | None + shipping_address_display: DF.TextEditor | None shipping_rule: DF.Link | None status: DF.Literal["", "Draft", "Submitted", "Stopped", "Cancelled", "Expired"] supplier: DF.Link diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 8bdee223213..2aadf599daa 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -380,6 +380,12 @@ class AccountsController(TransactionBase): for bundle in bundles: frappe.delete_doc("Serial and Batch Bundle", bundle.name) + batches = frappe.get_all( + "Batch", filters={"reference_doctype": self.doctype, "reference_name": self.name} + ) + for row in batches: + frappe.delete_doc("Batch", row.name) + def validate_return_against_account(self): if ( self.doctype in ["Sales Invoice", "Purchase Invoice"] and self.is_return and self.return_against diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 15eeff549eb..a1946e8a181 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -48,7 +48,9 @@ class StockController(AccountsController): super(StockController, self).validate() if self.docstatus == 0: - self.validate_duplicate_serial_and_batch_bundle() + for table_name in ["items", "packed_items", "supplied_items"]: + self.validate_duplicate_serial_and_batch_bundle(table_name) + if not self.get("is_return"): self.validate_inspection() self.validate_serialized_batch() @@ -58,12 +60,19 @@ class StockController(AccountsController): self.validate_internal_transfer() self.validate_putaway_capacity() - def validate_duplicate_serial_and_batch_bundle(self): - if sbb_list := [ - item.get("serial_and_batch_bundle") - for item in self.items - if item.get("serial_and_batch_bundle") - ]: + def validate_duplicate_serial_and_batch_bundle(self, table_name): + if not self.get(table_name): + return + + sbb_list = [] + for item in self.get(table_name): + if item.get("serial_and_batch_bundle"): + sbb_list.append(item.get("serial_and_batch_bundle")) + + if item.get("rejected_serial_and_batch_bundle"): + sbb_list.append(item.get("rejected_serial_and_batch_bundle")) + + if sbb_list: SLE = frappe.qb.DocType("Stock Ledger Entry") data = ( frappe.qb.from_(SLE) @@ -188,7 +197,7 @@ class StockController(AccountsController): not row.serial_and_batch_bundle and not row.get("rejected_serial_and_batch_bundle") ): bundle_details = { - "item_code": row.item_code, + "item_code": row.get("rm_item_code") or row.item_code, "posting_date": self.posting_date, "posting_time": self.posting_time, "voucher_type": self.doctype, @@ -200,7 +209,7 @@ class StockController(AccountsController): "do_not_submit": True, } - if row.qty: + if row.get("qty") or row.get("consumed_qty"): self.update_bundle_details(bundle_details, table_name, row) self.create_serial_batch_bundle(bundle_details, row) @@ -219,6 +228,12 @@ class StockController(AccountsController): type_of_transaction = "Inward" if not self.is_return: type_of_transaction = "Outward" + elif table_name == "supplied_items": + qty = row.consumed_qty + warehouse = self.supplier_warehouse + type_of_transaction = "Outward" + if self.is_return: + type_of_transaction = "Inward" else: type_of_transaction = get_type_of_transaction(self, row) @@ -550,13 +565,30 @@ class StockController(AccountsController): ) def delete_auto_created_batches(self): - for row in self.items: - if row.serial_and_batch_bundle: - frappe.db.set_value( - "Serial and Batch Bundle", row.serial_and_batch_bundle, {"is_cancelled": 1} - ) + for table_name in ["items", "packed_items", "supplied_items"]: + if not self.get(table_name): + continue - row.db_set("serial_and_batch_bundle", None) + for row in self.get(table_name): + update_values = {} + if row.get("batch_no"): + update_values["batch_no"] = None + + if row.serial_and_batch_bundle: + update_values["serial_and_batch_bundle"] = None + frappe.db.set_value( + "Serial and Batch Bundle", row.serial_and_batch_bundle, {"is_cancelled": 1} + ) + + if update_values: + row.db_set(update_values) + + if table_name == "items" and row.get("rejected_serial_and_batch_bundle"): + frappe.db.set_value( + "Serial and Batch Bundle", row.rejected_serial_and_batch_bundle, {"is_cancelled": 1} + ) + + row.db_set("rejected_serial_and_batch_bundle", None) def set_serial_and_batch_bundle(self, table_name=None, ignore_validate=False): if not table_name: diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py index e66fe8bcdec..ffc7f919d95 100644 --- a/erpnext/controllers/subcontracting_controller.py +++ b/erpnext/controllers/subcontracting_controller.py @@ -379,10 +379,10 @@ class SubcontractingController(StockController): if row.serial_no: details.serial_no.extend(get_serial_nos(row.serial_no)) - if row.batch_no: + elif row.batch_no: details.batch_no[row.batch_no] += row.qty - if voucher_bundle_data: + elif voucher_bundle_data: bundle_key = (row.rm_item_code, row.main_item_code, row.t_warehouse, row.voucher_no) bundle_data = voucher_bundle_data.get(bundle_key, frappe._dict()) @@ -392,6 +392,9 @@ class SubcontractingController(StockController): if bundle_data.batch_nos: for batch_no, qty in bundle_data.batch_nos.items(): + if qty < 0: + qty = abs(qty) + if qty > 0: details.batch_no[batch_no] += qty bundle_data.batch_nos[batch_no] -= qty @@ -545,17 +548,24 @@ class SubcontractingController(StockController): rm_obj.reference_name = item_row.name + use_serial_batch_fields = frappe.db.get_single_value("Stock Settings", "use_serial_batch_fields") + if self.doctype == self.subcontract_data.order_doctype: rm_obj.required_qty = qty rm_obj.amount = rm_obj.required_qty * rm_obj.rate else: rm_obj.consumed_qty = qty rm_obj.required_qty = bom_item.required_qty or qty + rm_obj.serial_and_batch_bundle = None setattr( rm_obj, self.subcontract_data.order_field, item_row.get(self.subcontract_data.order_field) ) - if self.doctype == "Subcontracting Receipt": + if use_serial_batch_fields: + rm_obj.use_serial_batch_fields = 1 + self.__set_batch_nos(bom_item, item_row, rm_obj, qty) + + if self.doctype == "Subcontracting Receipt" and not use_serial_batch_fields: args = frappe._dict( { "item_code": rm_obj.rm_item_code, @@ -581,6 +591,68 @@ class SubcontractingController(StockController): rm_obj.rate = get_incoming_rate(args) + def __set_batch_nos(self, bom_item, item_row, rm_obj, qty): + key = (rm_obj.rm_item_code, item_row.item_code, item_row.get(self.subcontract_data.order_field)) + + if self.available_materials.get(key) and self.available_materials[key]["batch_no"]: + new_rm_obj = None + for batch_no, batch_qty in self.available_materials[key]["batch_no"].items(): + if batch_qty >= qty or ( + rm_obj.consumed_qty == 0 + and self.backflush_based_on == "BOM" + and len(self.available_materials[key]["batch_no"]) == 1 + ): + if rm_obj.consumed_qty == 0: + self.__set_consumed_qty(rm_obj, qty) + + self.__set_batch_no_as_per_qty(item_row, rm_obj, batch_no, qty) + self.available_materials[key]["batch_no"][batch_no] -= qty + return + + elif qty > 0 and batch_qty > 0: + qty -= batch_qty + new_rm_obj = self.append(self.raw_material_table, bom_item) + new_rm_obj.serial_and_batch_bundle = None + new_rm_obj.use_serial_batch_fields = 1 + new_rm_obj.reference_name = item_row.name + self.__set_batch_no_as_per_qty(item_row, new_rm_obj, batch_no, batch_qty) + self.available_materials[key]["batch_no"][batch_no] = 0 + + if new_rm_obj: + self.remove(rm_obj) + elif abs(qty) > 0: + self.__set_consumed_qty(rm_obj, qty) + + else: + self.__set_consumed_qty(rm_obj, qty, bom_item.required_qty or qty) + self.__set_serial_nos(item_row, rm_obj) + + def __set_consumed_qty(self, rm_obj, consumed_qty, required_qty=0): + rm_obj.required_qty = required_qty + rm_obj.consumed_qty = consumed_qty + + def __set_serial_nos(self, item_row, rm_obj): + key = (rm_obj.rm_item_code, item_row.item_code, item_row.get(self.subcontract_data.order_field)) + if self.available_materials.get(key) and self.available_materials[key]["serial_no"]: + used_serial_nos = self.available_materials[key]["serial_no"][0 : cint(rm_obj.consumed_qty)] + rm_obj.serial_no = "\n".join(used_serial_nos) + + # Removed the used serial nos from the list + for sn in used_serial_nos: + self.available_materials[key]["serial_no"].remove(sn) + + def __set_batch_no_as_per_qty(self, item_row, rm_obj, batch_no, qty): + rm_obj.update( + { + "consumed_qty": qty, + "batch_no": batch_no, + "required_qty": qty, + self.subcontract_data.order_field: item_row.get(self.subcontract_data.order_field), + } + ) + + self.__set_serial_nos(item_row, rm_obj) + def __get_qty_based_on_material_transfer(self, item_row, transfer_item): key = (item_row.item_code, item_row.get(self.subcontract_data.order_field)) @@ -1076,6 +1148,9 @@ def make_rm_stock_entry( "serial_and_batch_bundle": rm_item.get("serial_and_batch_bundle"), "main_item_code": fg_item_code, "allow_alternative_item": item_wh.get(rm_item_code, {}).get("allow_alternative_item"), + "use_serial_batch_fields": rm_item.get("use_serial_batch_fields"), + "serial_no": rm_item.get("serial_no") if rm_item.get("use_serial_batch_fields") else None, + "batch_no": rm_item.get("batch_no") if rm_item.get("use_serial_batch_fields") else None, } } diff --git a/erpnext/controllers/tests/test_subcontracting_controller.py b/erpnext/controllers/tests/test_subcontracting_controller.py index 95a7bcb3982..7374e1e587f 100644 --- a/erpnext/controllers/tests/test_subcontracting_controller.py +++ b/erpnext/controllers/tests/test_subcontracting_controller.py @@ -140,6 +140,7 @@ class TestSubcontractingController(FrappeTestCase): - Create partial SCR against the SCO and check serial nos and batch no. """ + frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 0) set_backflush_based_on("Material Transferred for Subcontract") service_items = [ { @@ -202,6 +203,8 @@ class TestSubcontractingController(FrappeTestCase): if value.get(field): self.assertEqual(value.get(field), transferred_detais.get(field)) + frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1) + def test_subcontracting_with_same_components_different_fg(self): """ - Set backflush based on Material Transfer. @@ -211,6 +214,7 @@ class TestSubcontractingController(FrappeTestCase): - Create partial SCR against the SCO and check serial nos. """ + frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 0) set_backflush_based_on("Material Transferred for Subcontract") service_items = [ { @@ -278,6 +282,8 @@ class TestSubcontractingController(FrappeTestCase): self.assertEqual(value.qty, 6) self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get("serial_no")[6:12])) + frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1) + def test_return_non_consumed_materials(self): """ - Set backflush based on Material Transfer. @@ -288,6 +294,7 @@ class TestSubcontractingController(FrappeTestCase): - After that return the non consumed material back to the store from supplier's warehouse. """ + frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 0) set_backflush_based_on("Material Transferred for Subcontract") service_items = [ { @@ -333,6 +340,7 @@ class TestSubcontractingController(FrappeTestCase): get_serial_nos(doc.items[0].serial_no), itemwise_details.get(doc.items[0].item_code)["serial_no"][5:6], ) + frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1) def test_item_with_batch_based_on_bom(self): """ @@ -578,6 +586,7 @@ class TestSubcontractingController(FrappeTestCase): - Create SCR for remaining qty against the SCO and change the qty manually. """ + frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 0) set_backflush_based_on("Material Transferred for Subcontract") service_items = [ { @@ -643,6 +652,8 @@ class TestSubcontractingController(FrappeTestCase): self.assertEqual(value.qty, details.qty) self.assertEqual(sorted(value.serial_no), sorted(details.serial_no)) + frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1) + def test_incorrect_serial_no_components_based_on_material_transfer(self): """ - Set backflush based on Material Transferred for Subcontract. @@ -652,6 +663,7 @@ class TestSubcontractingController(FrappeTestCase): - System should throw the error and not allowed to save the SCR. """ + frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 0) serial_no = "ABC" if not frappe.db.exists("Serial No", serial_no): frappe.get_doc( @@ -712,6 +724,7 @@ class TestSubcontractingController(FrappeTestCase): scr1.save() self.delete_bundle_from_scr(scr1) scr1.delete() + frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1) @staticmethod def delete_bundle_from_scr(scr): @@ -844,6 +857,223 @@ class TestSubcontractingController(FrappeTestCase): for item in sco.get("supplied_items"): self.assertEqual(item.supplied_qty, 0.0) + def test_sco_with_material_transfer_with_use_serial_batch_fields(self): + """ + - Set backflush based on Material Transfer. + - Create SCO for the item Subcontracted Item SA1 and Subcontracted Item SA5. + - Transfer the components from Stores to Supplier warehouse with batch no and serial nos. + - Transfer extra item Subcontracted SRM Item 4 for the subcontract item Subcontracted Item SA5. + - Create partial SCR against the SCO and check serial nos and batch no. + """ + + set_backflush_based_on("Material Transferred for Subcontract") + service_items = [ + { + "warehouse": "_Test Warehouse - _TC", + "item_code": "Subcontracted Service Item 1", + "qty": 5, + "rate": 100, + "fg_item": "Subcontracted Item SA1", + "fg_item_qty": 5, + }, + { + "warehouse": "_Test Warehouse - _TC", + "item_code": "Subcontracted Service Item 5", + "qty": 6, + "rate": 100, + "fg_item": "Subcontracted Item SA5", + "fg_item_qty": 6, + }, + ] + sco = get_subcontracting_order(service_items=service_items) + rm_items = get_rm_items(sco.supplied_items) + rm_items.append( + { + "main_item_code": "Subcontracted Item SA5", + "item_code": "Subcontracted SRM Item 4", + "qty": 6, + } + ) + itemwise_details = make_stock_in_entry(rm_items=rm_items) + + for item in rm_items: + item["sco_rm_detail"] = sco.items[0].name if item.get("qty") == 5 else sco.items[1].name + + make_stock_transfer_entry( + sco_no=sco.name, + rm_items=rm_items, + itemwise_details=copy.deepcopy(itemwise_details), + ) + + scr1 = make_subcontracting_receipt(sco.name) + scr1.remove(scr1.items[1]) + scr1.save() + scr1.submit() + + for key, value in get_supplied_items(scr1).items(): + transferred_detais = itemwise_details.get(key) + + for field in ["qty", "serial_no", "batch_no"]: + if value.get(field): + data = value.get(field) + if field == "serial_no": + data = sorted(data) + + self.assertEqual(data, transferred_detais.get(field)) + + scr2 = make_subcontracting_receipt(sco.name) + scr2.save() + scr2.submit() + + for key, value in get_supplied_items(scr2).items(): + transferred_detais = itemwise_details.get(key) + + for field in ["qty", "serial_no", "batch_no"]: + if value.get(field): + data = value.get(field) + if field == "serial_no": + data = sorted(data) + + self.assertEqual(data, transferred_detais.get(field)) + + def test_subcontracting_with_same_components_different_fg_with_serial_batch_fields(self): + """ + - Set backflush based on Material Transfer. + - Create SCO for the item Subcontracted Item SA2 and Subcontracted Item SA3. + - Transfer the components from Stores to Supplier warehouse with serial nos. + - Transfer extra qty of components for the item Subcontracted Item SA2. + - Create partial SCR against the SCO and check serial nos. + """ + + set_backflush_based_on("Material Transferred for Subcontract") + service_items = [ + { + "warehouse": "_Test Warehouse - _TC", + "item_code": "Subcontracted Service Item 2", + "qty": 5, + "rate": 100, + "fg_item": "Subcontracted Item SA2", + "fg_item_qty": 5, + }, + { + "warehouse": "_Test Warehouse - _TC", + "item_code": "Subcontracted Service Item 3", + "qty": 6, + "rate": 100, + "fg_item": "Subcontracted Item SA3", + "fg_item_qty": 6, + }, + ] + sco = get_subcontracting_order(service_items=service_items) + rm_items = get_rm_items(sco.supplied_items) + rm_items[0]["qty"] += 1 + itemwise_details = make_stock_in_entry(rm_items=rm_items) + + for item in rm_items: + item["sco_rm_detail"] = sco.items[0].name if item.get("qty") == 5 else sco.items[1].name + item["use_serial_batch_fields"] = 1 + + make_stock_transfer_entry( + sco_no=sco.name, + rm_items=rm_items, + itemwise_details=copy.deepcopy(itemwise_details), + ) + + scr1 = make_subcontracting_receipt(sco.name) + scr1.items[0].qty = 3 + scr1.remove(scr1.items[1]) + scr1.save() + scr1.submit() + + for key, value in get_supplied_items(scr1).items(): + transferred_detais = itemwise_details.get(key) + + self.assertEqual(value.qty, 4) + self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get("serial_no")[0:4])) + + scr2 = make_subcontracting_receipt(sco.name) + scr2.items[0].qty = 2 + scr2.remove(scr2.items[1]) + scr2.save() + scr2.submit() + + for key, value in get_supplied_items(scr2).items(): + transferred_detais = itemwise_details.get(key) + + self.assertEqual(value.qty, 2) + self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get("serial_no")[4:6])) + + scr3 = make_subcontracting_receipt(sco.name) + scr3.save() + scr3.submit() + + for key, value in get_supplied_items(scr3).items(): + transferred_detais = itemwise_details.get(key) + + self.assertEqual(value.qty, 6) + self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get("serial_no")[6:12])) + + def test_return_non_consumed_materials_with_serial_batch_fields(self): + """ + - Set backflush based on Material Transfer. + - Create SCO for item Subcontracted Item SA2. + - Transfer the components from Stores to Supplier warehouse with serial nos. + - Transfer extra qty of component for the subcontracted item Subcontracted Item SA2. + - Create SCR for full qty against the SCO and change the qty of raw material. + - After that return the non consumed material back to the store from supplier's warehouse. + """ + + set_backflush_based_on("Material Transferred for Subcontract") + service_items = [ + { + "warehouse": "_Test Warehouse - _TC", + "item_code": "Subcontracted Service Item 2", + "qty": 5, + "rate": 100, + "fg_item": "Subcontracted Item SA2", + "fg_item_qty": 5, + }, + ] + sco = get_subcontracting_order(service_items=service_items) + rm_items = get_rm_items(sco.supplied_items) + rm_items[0]["qty"] += 1 + itemwise_details = make_stock_in_entry(rm_items=rm_items) + + for item in rm_items: + item["use_serial_batch_fields"] = 1 + item["sco_rm_detail"] = sco.items[0].name + + make_stock_transfer_entry( + sco_no=sco.name, + rm_items=rm_items, + itemwise_details=copy.deepcopy(itemwise_details), + ) + + scr1 = make_subcontracting_receipt(sco.name) + scr1.save() + scr1.supplied_items[0].consumed_qty = 5 + scr1.supplied_items[0].serial_no = "\n".join( + sorted(itemwise_details.get("Subcontracted SRM Item 2").get("serial_no")[0:5]) + ) + scr1.submit() + + for key, value in get_supplied_items(scr1).items(): + transferred_detais = itemwise_details.get(key) + self.assertTrue(value.use_serial_batch_fields) + self.assertEqual(value.qty, 5) + self.assertEqual(sorted(value.serial_no), sorted(transferred_detais.get("serial_no")[0:5])) + + sco.load_from_db() + self.assertEqual(sco.supplied_items[0].consumed_qty, 5) + doc = get_materials_from_supplier(sco.name, [d.name for d in sco.supplied_items]) + self.assertEqual(doc.items[0].qty, 1) + self.assertEqual(doc.items[0].s_warehouse, "_Test Warehouse 1 - _TC") + self.assertEqual(doc.items[0].t_warehouse, "_Test Warehouse - _TC") + self.assertEqual( + get_serial_nos(doc.items[0].serial_no), + itemwise_details.get(doc.items[0].item_code)["serial_no"][5:6], + ) + def add_second_row_in_scr(scr): item_dict = {} @@ -914,6 +1144,7 @@ def update_item_details(child_row, details): else child_row.get("consumed_qty") ) + details.use_serial_batch_fields = child_row.get("use_serial_batch_fields") if child_row.serial_and_batch_bundle: doc = frappe.get_doc("Serial and Batch Bundle", child_row.serial_and_batch_bundle) for row in doc.get("entries"): @@ -945,6 +1176,7 @@ def make_stock_transfer_entry(**args): "rate": row.rate or 100, "stock_uom": row.stock_uom or "Nos", "warehouse": row.warehouse or "_Test Warehouse - _TC", + "use_serial_batch_fields": row.get("use_serial_batch_fields"), } item_details = args.itemwise_details.get(row.item_code) @@ -960,9 +1192,12 @@ def make_stock_transfer_entry(**args): if batch_qty >= row.qty: batches[batch_no] = row.qty item_details.batch_no[batch_no] -= row.qty + if row.get("use_serial_batch_fields"): + item["batch_no"] = batch_no + break - if serial_nos or batches: + if not row.get("use_serial_batch_fields") and (serial_nos or batches): item["serial_and_batch_bundle"] = make_serial_batch_bundle( frappe._dict( { @@ -978,6 +1213,9 @@ def make_stock_transfer_entry(**args): ) ).name + if serial_nos and row.get("use_serial_batch_fields"): + item["serial_no"] = "\n".join(serial_nos) + items.append(item) ste_dict = make_rm_stock_entry(args.sco_no, items) @@ -1132,6 +1370,7 @@ def get_rm_items(supplied_items): "rate": item.rate, "stock_uom": item.stock_uom, "warehouse": item.reserve_warehouse, + "use_serial_batch_fields": 0, } ) diff --git a/erpnext/crm/doctype/campaign/campaign.js b/erpnext/crm/doctype/campaign/campaign.js index 9e4a0a95362..219e8cf3e4f 100644 --- a/erpnext/crm/doctype/campaign/campaign.js +++ b/erpnext/crm/doctype/campaign/campaign.js @@ -11,7 +11,7 @@ frappe.ui.form.on("Campaign", { frappe.boot.sysdefaults.campaign_naming_by == "Naming Series" ); } else { - cur_frm.add_custom_button( + frm.add_custom_button( __("View Leads"), function () { frappe.route_options = { source: "Campaign", campaign_name: frm.doc.name }; diff --git a/erpnext/crm/doctype/lead/lead.js b/erpnext/crm/doctype/lead/lead.js index 609eab7f9a2..848d697461a 100644 --- a/erpnext/crm/doctype/lead/lead.js +++ b/erpnext/crm/doctype/lead/lead.js @@ -89,32 +89,33 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller make_customer() { frappe.model.open_mapped_doc({ method: "erpnext.crm.doctype.lead.lead.make_customer", - frm: cur_frm, + frm: this.frm, }); } make_quotation() { frappe.model.open_mapped_doc({ method: "erpnext.crm.doctype.lead.lead.make_quotation", - frm: cur_frm, + frm: this.frm, }); } make_prospect() { + const me = this; frappe.model.with_doctype("Prospect", function () { let prospect = frappe.model.get_new_doc("Prospect"); - prospect.company_name = cur_frm.doc.company_name; - prospect.no_of_employees = cur_frm.doc.no_of_employees; - prospect.industry = cur_frm.doc.industry; - prospect.market_segment = cur_frm.doc.market_segment; - prospect.territory = cur_frm.doc.territory; - prospect.fax = cur_frm.doc.fax; - prospect.website = cur_frm.doc.website; - prospect.prospect_owner = cur_frm.doc.lead_owner; - prospect.notes = cur_frm.doc.notes; + prospect.company_name = me.frm.doc.company_name; + prospect.no_of_employees = me.frm.doc.no_of_employees; + prospect.industry = me.frm.doc.industry; + prospect.market_segment = me.frm.doc.market_segment; + prospect.territory = me.frm.doc.territory; + prospect.fax = me.frm.doc.fax; + prospect.website = me.frm.doc.website; + prospect.prospect_owner = me.frm.doc.lead_owner; + prospect.notes = me.frm.doc.notes; let leads_row = frappe.model.add_child(prospect, "leads"); - leads_row.lead = cur_frm.doc.name; + leads_row.lead = me.frm.doc.name; frappe.set_route("Form", "Prospect", prospect.name); }); diff --git a/erpnext/crm/doctype/opportunity/opportunity.js b/erpnext/crm/doctype/opportunity/opportunity.js index 1c8a80a0ed6..c7063579d8f 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.js +++ b/erpnext/crm/doctype/opportunity/opportunity.js @@ -318,14 +318,14 @@ erpnext.crm.Opportunity = class Opportunity extends frappe.ui.form.Controller { create_quotation() { frappe.model.open_mapped_doc({ method: "erpnext.crm.doctype.opportunity.opportunity.make_quotation", - frm: cur_frm, + frm: this.frm, }); } make_customer() { frappe.model.open_mapped_doc({ method: "erpnext.crm.doctype.opportunity.opportunity.make_customer", - frm: cur_frm, + frm: this.frm, }); } diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json index 07641d20c33..e6f7bfcb065 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.json +++ b/erpnext/crm/doctype/opportunity/opportunity.json @@ -250,7 +250,7 @@ }, { "fieldname": "address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "hidden": 1, "label": "Address", "oldfieldname": "address", @@ -622,7 +622,7 @@ "icon": "fa fa-info-sign", "idx": 195, "links": [], - "modified": "2022-10-13 12:42:21.545636", + "modified": "2024-03-22 16:01:10.721453", "modified_by": "Administrator", "module": "CRM", "name": "Opportunity", diff --git a/erpnext/crm/doctype/opportunity/opportunity.py b/erpnext/crm/doctype/opportunity/opportunity.py index 72e26de70e6..7abbb63df5f 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.py +++ b/erpnext/crm/doctype/opportunity/opportunity.py @@ -40,7 +40,7 @@ class Opportunity(TransactionBase, CRMNote): OpportunityLostReasonDetail, ) - address_display: DF.SmallText | None + address_display: DF.TextEditor | None amended_from: DF.Link | None annual_revenue: DF.Currency base_opportunity_amount: DF.Currency diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.json b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.json index 08026d031f2..e773d664eb5 100644 --- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.json +++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.json @@ -187,7 +187,7 @@ }, { "fieldname": "address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "hidden": 1, "label": "Address", "read_only": 1 @@ -238,7 +238,7 @@ "link_fieldname": "maintenance_schedule" } ], - "modified": "2023-06-03 16:15:43.958072", + "modified": "2024-03-22 16:01:11.868813", "modified_by": "Administrator", "module": "Maintenance", "name": "Maintenance Schedule", diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py index 75d890c08db..50ec1f2795f 100644 --- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py +++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py @@ -26,7 +26,7 @@ class MaintenanceSchedule(TransactionBase): MaintenanceScheduleItem, ) - address_display: DF.SmallText | None + address_display: DF.TextEditor | None amended_from: DF.Link | None company: DF.Link contact_display: DF.SmallText | None diff --git a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.json b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.json index b0d5cb89964..6124b6f487f 100644 --- a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.json +++ b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.json @@ -83,7 +83,7 @@ }, { "fieldname": "address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "hidden": 1, "label": "Address", "read_only": 1 @@ -295,7 +295,7 @@ "idx": 1, "is_submittable": 1, "links": [], - "modified": "2023-06-03 16:19:07.902723", + "modified": "2024-03-22 16:01:12.354826", "modified_by": "Administrator", "module": "Maintenance", "name": "Maintenance Visit", diff --git a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py index d2511b8cbc0..b43b419ab3d 100644 --- a/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py +++ b/erpnext/maintenance/doctype/maintenance_visit/maintenance_visit.py @@ -22,7 +22,7 @@ class MaintenanceVisit(TransactionBase): MaintenanceVisitPurpose, ) - address_display: DF.SmallText | None + address_display: DF.TextEditor | None amended_from: DF.Link | None company: DF.Link completion_status: DF.Literal["", "Partially Completed", "Fully Completed"] diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index e1c1069f807..b56e9e106ff 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -1208,6 +1208,51 @@ class TestWorkOrder(FrappeTestCase): except frappe.MandatoryError: self.fail("Batch generation causing failing in Work Order") + @change_settings("Manufacturing Settings", {"make_serial_no_batch_from_work_order": 1}) + def test_auto_serial_no_batch_creation(self): + from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom + + fg_item = frappe.generate_hash(length=20) + child_item = frappe.generate_hash(length=20) + + bom_tree = {fg_item: {child_item: {}}} + + create_nested_bom(bom_tree, prefix="") + + item = frappe.get_doc("Item", fg_item) + item.update( + { + "has_serial_no": 1, + "has_batch_no": 1, + "serial_no_series": f"SN-TEST-{item.name}.#####", + "create_new_batch": 1, + "batch_number_series": f"BATCH-TEST-{item.name}.#####", + } + ) + item.save() + + try: + wo_order = make_wo_order_test_record(item=fg_item, batch_size=5, qty=10, skip_transfer=True) + serial_nos = self.get_serial_nos_for_fg(wo_order.name) + + stock_entry = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 10)) + stock_entry.set_work_order_details() + stock_entry.set_serial_no_batch_for_finished_good() + for row in stock_entry.items: + if row.item_code == fg_item: + self.assertTrue(row.serial_and_batch_bundle) + self.assertEqual( + sorted(get_serial_nos_from_bundle(row.serial_and_batch_bundle)), sorted(serial_nos) + ) + + sn_doc = frappe.get_doc("Serial and Batch Bundle", row.serial_and_batch_bundle) + for row in sn_doc.entries: + self.assertTrue(row.serial_no) + self.assertTrue(row.batch_no) + + except frappe.MandatoryError: + self.fail("Batch generation causing failing in Work Order") + def get_serial_nos_for_fg(self, work_order): serial_nos = [] for row in frappe.get_all("Serial No", filters={"work_order": work_order}): @@ -2270,6 +2315,7 @@ def make_wo_order_test_record(**args): wo_order.planned_start_date = args.planned_start_date or now() wo_order.transfer_material_against = args.transfer_material_against or "Work Order" wo_order.from_wip_warehouse = args.from_wip_warehouse or 0 + wo_order.batch_size = args.batch_size or 0 if args.source_warehouse: for item in wo_order.get("required_items"): diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 233ca19f7ed..f897a91a88d 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -536,6 +536,12 @@ class WorkOrder(Document): "Item", self.production_item, ["serial_no_series", "item_name", "description"], as_dict=1 ) + batches = [] + if self.has_batch_no: + batches = frappe.get_all( + "Batch", filters={"reference_name": self.name}, order_by="creation", pluck="name" + ) + serial_nos = [] if item_details.serial_no_series: serial_nos = get_available_serial_nos(item_details.serial_no_series, self.qty) @@ -556,10 +562,20 @@ class WorkOrder(Document): "description", "status", "work_order", + "batch_no", ] serial_nos_details = [] + index = 0 for serial_no in serial_nos: + index += 1 + batch_no = None + if batches and self.batch_size: + batch_no = batches[0] + + if index % self.batch_size == 0: + batches.remove(batch_no) + serial_nos_details.append( ( serial_no, @@ -574,6 +590,7 @@ class WorkOrder(Document): item_details.description, "Inactive", self.name, + batch_no, ) ) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 15dfc368915..7c2c439546f 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -356,7 +356,7 @@ erpnext.patches.v14_0.update_total_asset_cost_field erpnext.patches.v15_0.create_advance_payment_status erpnext.patches.v15_0.allow_on_submit_dimensions_for_repostable_doctypes erpnext.patches.v14_0.create_accounting_dimensions_in_reconciliation_tool -erpnext.patches.v14_0.update_flag_for_return_invoices +erpnext.patches.v14_0.update_flag_for_return_invoices #2024-03-22 # below migration patch should always run last erpnext.patches.v14_0.migrate_gl_to_payment_ledger erpnext.stock.doctype.delivery_note.patches.drop_unused_return_against_index # 2023-12-20 diff --git a/erpnext/patches/v14_0/update_flag_for_return_invoices.py b/erpnext/patches/v14_0/update_flag_for_return_invoices.py index feb43beacf8..bea99575425 100644 --- a/erpnext/patches/v14_0/update_flag_for_return_invoices.py +++ b/erpnext/patches/v14_0/update_flag_for_return_invoices.py @@ -12,6 +12,10 @@ def execute(): creation_date = "2024-01-25" si = qb.DocType("Sales Invoice") + + # unset flag, as migration would have set it for all records, as the field was introduced with default '1' + qb.update(si).set(si.update_outstanding_for_self, False).run() + if cr_notes := ( qb.from_(si) .select(si.name) @@ -37,6 +41,10 @@ def execute(): ).run() pi = qb.DocType("Purchase Invoice") + + # unset flag, as migration would have set it for all records, as the field was introduced with default '1' + qb.update(pi).set(pi.update_outstanding_for_self, False).run() + if dr_notes := ( qb.from_(pi) .select(pi.name) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 664fb918f9c..63f39b58823 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -2267,9 +2267,9 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe } get_method_for_payment() { - var method = "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry"; - if(cur_frm.doc.__onload && cur_frm.doc.__onload.make_payment_via_journal_entry){ - if(['Sales Invoice', 'Purchase Invoice'].includes( cur_frm.doc.doctype)){ + let method = "erpnext.accounts.doctype.payment_entry.payment_entry.get_payment_entry"; + if(this.frm.doc.__onload && this.frm.doc.__onload.make_payment_via_journal_entry){ + if(['Sales Invoice', 'Purchase Invoice'].includes( this.frm.doc.doctype)){ method = "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_against_invoice"; }else { method= "erpnext.accounts.doctype.journal_entry.journal_entry.get_payment_entry_against_order"; diff --git a/erpnext/public/js/projects/timer.js b/erpnext/public/js/projects/timer.js index 8370cc6ffff..10f06272189 100644 --- a/erpnext/public/js/projects/timer.js +++ b/erpnext/public/js/projects/timer.js @@ -110,7 +110,7 @@ erpnext.timesheet.control_timer = function (frm, dialog, row, timestamp = 0) { // Stop the timer and update the time logged by the timer on click of 'Complete' button $btn_complete.click(function () { - var grid_row = cur_frm.fields_dict["time_logs"].grid.get_row(row.idx - 1); + var grid_row = frm.fields_dict["time_logs"].grid.get_row(row.idx - 1); var args = dialog.get_values(); grid_row.doc.completed = 1; grid_row.doc.activity_type = args.activity_type; diff --git a/erpnext/public/scss/erpnext.scss b/erpnext/public/scss/erpnext.scss index 03dd31104e1..3d8ee890203 100644 --- a/erpnext/public/scss/erpnext.scss +++ b/erpnext/public/scss/erpnext.scss @@ -549,6 +549,14 @@ body[data-route="pos"] { justify-content: center; } -.frappe-control[data-fieldname="other_charges_calculation"] .ql-editor { +.frappe-control[data-fieldname="other_charges_calculation"] .ql-editor, +.frappe-control[data-fieldname="address_display"] .ql-editor, +.frappe-control[data-fieldname="shipping_address_display"] .ql-editor, +.frappe-control[data-fieldname="shipping_address"] .ql-editor, +.frappe-control[data-fieldname="dispatch_address"] .ql-editor, +.frappe-control[data-fieldname="source_address_display"] .ql-editor, +.frappe-control[data-fieldname="target_address_display"] .ql-editor, +.frappe-control[data-fieldname="billing_address_display"] .ql-editor, +.frappe-control[data-fieldname="company_address_display"] .ql-editor { white-space: normal; } diff --git a/erpnext/selling/doctype/installation_note/installation_note.json b/erpnext/selling/doctype/installation_note/installation_note.json index 1e22f447667..fc34c73bc17 100644 --- a/erpnext/selling/doctype/installation_note/installation_note.json +++ b/erpnext/selling/doctype/installation_note/installation_note.json @@ -94,7 +94,7 @@ }, { "fieldname": "address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "hidden": 1, "label": "Address", "read_only": 1 @@ -238,7 +238,7 @@ "idx": 1, "is_submittable": 1, "links": [], - "modified": "2024-02-04 18:20:12.020313", + "modified": "2024-03-22 16:01:13.513355", "modified_by": "Administrator", "module": "Selling", "name": "Installation Note", @@ -271,4 +271,4 @@ "states": [], "timeline_field": "customer", "title_field": "customer_name" -} +} \ No newline at end of file diff --git a/erpnext/selling/doctype/installation_note/installation_note.py b/erpnext/selling/doctype/installation_note/installation_note.py index d1bfd910f93..ac7cd60108d 100644 --- a/erpnext/selling/doctype/installation_note/installation_note.py +++ b/erpnext/selling/doctype/installation_note/installation_note.py @@ -23,7 +23,7 @@ class InstallationNote(TransactionBase): InstallationNoteItem, ) - address_display: DF.SmallText | None + address_display: DF.TextEditor | None amended_from: DF.Link | None company: DF.Link contact_display: DF.SmallText | None @@ -38,6 +38,7 @@ class InstallationNote(TransactionBase): inst_time: DF.Time | None items: DF.Table[InstallationNoteItem] naming_series: DF.Literal["MAT-INS-.YYYY.-"] + project: DF.Link | None remarks: DF.SmallText | None status: DF.Literal["Draft", "Submitted", "Cancelled"] territory: DF.Link diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json index 982e7326775..bb6e0bdfaa2 100644 --- a/erpnext/selling/doctype/quotation/quotation.json +++ b/erpnext/selling/doctype/quotation/quotation.json @@ -265,7 +265,7 @@ }, { "fieldname": "address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "label": "Address", "oldfieldname": "customer_address", "oldfieldtype": "Small Text", @@ -318,7 +318,7 @@ }, { "fieldname": "shipping_address", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "label": "Shipping Address", "print_hide": 1, "read_only": 1 @@ -965,7 +965,7 @@ }, { "fieldname": "company_address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "label": "Company Address", "read_only": 1 }, @@ -1073,7 +1073,7 @@ "idx": 82, "is_submittable": 1, "links": [], - "modified": "2024-03-20 16:04:21.567847", + "modified": "2024-03-22 16:15:10.488656", "modified_by": "Administrator", "module": "Selling", "name": "Quotation", diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py index 633e5f56a05..6977f06f868 100644 --- a/erpnext/selling/doctype/quotation/quotation.py +++ b/erpnext/selling/doctype/quotation/quotation.py @@ -34,7 +34,7 @@ class Quotation(SellingController): from erpnext.stock.doctype.packed_item.packed_item import PackedItem additional_discount_percentage: DF.Float - address_display: DF.SmallText | None + address_display: DF.TextEditor | None amended_from: DF.Link | None apply_discount_on: DF.Literal["", "Grand Total", "Net Total"] auto_repeat: DF.Link | None @@ -49,7 +49,7 @@ class Quotation(SellingController): campaign: DF.Link | None company: DF.Link company_address: DF.Link | None - company_address_display: DF.SmallText | None + company_address_display: DF.TextEditor | None competitors: DF.TableMultiSelect[CompetitorDetail] contact_display: DF.SmallText | None contact_email: DF.Data | None @@ -93,7 +93,7 @@ class Quotation(SellingController): scan_barcode: DF.Data | None select_print_heading: DF.Link | None selling_price_list: DF.Link - shipping_address: DF.SmallText | None + shipping_address: DF.TextEditor | None shipping_address_name: DF.Link | None shipping_rule: DF.Link | None source: DF.Link | None diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index 1fb1ae0c040..8f6ae05fc28 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -369,7 +369,7 @@ { "allow_on_submit": 1, "fieldname": "address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "hide_days": 1, "hide_seconds": 1, "label": "Address", @@ -415,7 +415,7 @@ }, { "fieldname": "company_address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "hide_days": 1, "hide_seconds": 1, "label": "Company Address", @@ -450,7 +450,7 @@ { "allow_on_submit": 1, "fieldname": "shipping_address", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "hide_days": 1, "hide_seconds": 1, "label": "Shipping Address", @@ -1525,7 +1525,7 @@ "allow_on_submit": 1, "depends_on": "dispatch_address_name", "fieldname": "dispatch_address", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "label": "Dispatch Address", "read_only": 1 }, @@ -1657,7 +1657,7 @@ "idx": 105, "is_submittable": 1, "links": [], - "modified": "2024-03-20 16:04:43.627183", + "modified": "2024-03-22 16:15:04.884816", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order", diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 79df4d343b3..826ba1e8782 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -64,7 +64,7 @@ class SalesOrder(SellingController): from erpnext.stock.doctype.packed_item.packed_item import PackedItem additional_discount_percentage: DF.Float - address_display: DF.SmallText | None + address_display: DF.TextEditor | None advance_paid: DF.Currency advance_payment_status: DF.Literal["Not Requested", "Requested", "Partially Paid", "Fully Paid"] amended_from: DF.Link | None @@ -84,7 +84,7 @@ class SalesOrder(SellingController): commission_rate: DF.Float company: DF.Link company_address: DF.Link | None - company_address_display: DF.SmallText | None + company_address_display: DF.TextEditor | None contact_display: DF.SmallText | None contact_email: DF.Data | None contact_mobile: DF.SmallText | None @@ -104,7 +104,7 @@ class SalesOrder(SellingController): ] disable_rounded_total: DF.Check discount_amount: DF.Currency - dispatch_address: DF.SmallText | None + dispatch_address: DF.TextEditor | None dispatch_address_name: DF.Link | None from_date: DF.Date | None grand_total: DF.Currency @@ -147,7 +147,7 @@ class SalesOrder(SellingController): select_print_heading: DF.Link | None selling_price_list: DF.Link set_warehouse: DF.Link | None - shipping_address: DF.SmallText | None + shipping_address: DF.TextEditor | None shipping_address_name: DF.Link | None shipping_rule: DF.Link | None skip_delivery_note: DF.Check @@ -755,6 +755,13 @@ def get_list_context(context=None): return list_context +@frappe.whitelist() +def is_enable_cutoff_date_on_bulk_delivery_note_creation(): + return frappe.db.get_single_value( + "Selling Settings", "enable_cutoff_date_on_bulk_delivery_note_creation" + ) + + @frappe.whitelist() def close_or_unclose_sales_orders(names, status): if not frappe.has_permission("Sales Order", "write"): diff --git a/erpnext/selling/doctype/sales_order/sales_order_list.js b/erpnext/selling/doctype/sales_order/sales_order_list.js index 14ba051fc25..65301750c04 100644 --- a/erpnext/selling/doctype/sales_order/sales_order_list.js +++ b/erpnext/selling/doctype/sales_order/sales_order_list.js @@ -68,10 +68,10 @@ frappe.listview_settings["Sales Order"] = { }); listview.page.add_action_item(__("Delivery Note"), () => { - frappe.db - .get_single_value("Selling Settings", "enable_cutoff_date_on_bulk_delivery_note_creation") - .then((value) => { - if (value) { + frappe.call({ + method: "erpnext.selling.doctype.sales_order.sales_order.is_enable_cutoff_date_on_bulk_delivery_note_creation", + callback: (r) => { + if (r.message) { var dialog = new frappe.ui.Dialog({ title: __("Select Items up to Delivery Date"), fields: [ @@ -98,7 +98,8 @@ frappe.listview_settings["Sales Order"] = { } else { erpnext.bulk_transaction_processing.create(listview, "Sales Order", "Delivery Note"); } - }); + }, + }); }); listview.page.add_action_item(__("Advance Payment"), () => { diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json index 9599980418f..d451768eaab 100644 --- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json +++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json @@ -834,7 +834,8 @@ "label": "Purchase Order", "options": "Purchase Order", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "search_index": 1 }, { "fieldname": "column_break_89", @@ -909,7 +910,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2024-01-25 14:24:00.330219", + "modified": "2024-03-21 18:15:56.625005", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order Item", diff --git a/erpnext/stock/deprecated_serial_batch.py b/erpnext/stock/deprecated_serial_batch.py index 7be1418823b..6e32cc2dae6 100644 --- a/erpnext/stock/deprecated_serial_batch.py +++ b/erpnext/stock/deprecated_serial_batch.py @@ -245,6 +245,8 @@ class DeprecatedBatchNoValuation: if self.sle.serial_and_batch_bundle: query = query.where(bundle.name != self.sle.serial_and_batch_bundle) + query = query.where(bundle.voucher_type != "Pick List") + for d in query.run(as_dict=True): self.non_batchwise_balance_value += flt(d.batch_value) self.non_batchwise_balance_qty += flt(d.batch_qty) diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index 87c333370b2..d05392dca75 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -351,7 +351,7 @@ }, { "fieldname": "shipping_address", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "label": "Shipping Address", "read_only": 1 }, @@ -408,7 +408,7 @@ }, { "fieldname": "address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "label": "Billing Address", "read_only": 1 }, @@ -420,7 +420,7 @@ }, { "fieldname": "company_address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "label": "Company Address", "read_only": 1 }, @@ -1289,7 +1289,7 @@ { "depends_on": "dispatch_address_name", "fieldname": "dispatch_address", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "label": "Dispatch Address", "print_hide": 1, "read_only": 1 @@ -1397,7 +1397,7 @@ "idx": 146, "is_submittable": 1, "links": [], - "modified": "2024-03-20 16:05:02.854990", + "modified": "2024-03-22 16:15:07.253135", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note", diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index e17a0a2307f..f13353e7032 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -35,7 +35,7 @@ class DeliveryNote(SellingController): from erpnext.stock.doctype.packed_item.packed_item import PackedItem additional_discount_percentage: DF.Float - address_display: DF.SmallText | None + address_display: DF.TextEditor | None amended_from: DF.Link | None amount_eligible_for_commission: DF.Currency apply_discount_on: DF.Literal["", "Grand Total", "Net Total"] @@ -52,7 +52,7 @@ class DeliveryNote(SellingController): commission_rate: DF.Float company: DF.Link company_address: DF.Link | None - company_address_display: DF.SmallText | None + company_address_display: DF.TextEditor | None contact_display: DF.SmallText | None contact_email: DF.Data | None contact_mobile: DF.SmallText | None @@ -66,7 +66,7 @@ class DeliveryNote(SellingController): customer_name: DF.Data | None disable_rounded_total: DF.Check discount_amount: DF.Currency - dispatch_address: DF.SmallText | None + dispatch_address: DF.TextEditor | None dispatch_address_name: DF.Link | None driver: DF.Link | None driver_name: DF.Data | None @@ -117,7 +117,7 @@ class DeliveryNote(SellingController): set_posting_time: DF.Check set_target_warehouse: DF.Link | None set_warehouse: DF.Link | None - shipping_address: DF.SmallText | None + shipping_address: DF.TextEditor | None shipping_address_name: DF.Link | None shipping_rule: DF.Link | None source: DF.Link | None diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json index 247672fe126..b8164b25753 100644 --- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json +++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json @@ -796,7 +796,8 @@ "label": "Purchase Order", "options": "Purchase Order", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "search_index": 1 }, { "fieldname": "column_break_82", @@ -912,7 +913,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2024-02-04 14:10:31.750340", + "modified": "2024-03-21 18:15:07.603672", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note Item", diff --git a/erpnext/stock/doctype/item_price/item_price.py b/erpnext/stock/doctype/item_price/item_price.py index cd071e4fa49..d64e321649f 100644 --- a/erpnext/stock/doctype/item_price/item_price.py +++ b/erpnext/stock/doctype/item_price/item_price.py @@ -7,7 +7,6 @@ from frappe import _, bold from frappe.model.document import Document from frappe.query_builder import Criterion from frappe.query_builder.functions import Cast_ -from frappe.utils import getdate class ItemPriceDuplicateItem(frappe.ValidationError): @@ -46,7 +45,7 @@ class ItemPrice(Document): def validate(self): self.validate_item() - self.validate_dates() + self.validate_from_to_dates("valid_from", "valid_upto") self.update_price_list_details() self.update_item_details() self.check_duplicates() @@ -61,11 +60,6 @@ class ItemPrice(Document): ): frappe.throw(_("UOM {0} not found in Item {1}").format(self.uom, self.item_code)) - def validate_dates(self): - if self.valid_from and self.valid_upto: - if getdate(self.valid_from) > getdate(self.valid_upto): - frappe.throw(_("Valid From Date must be lesser than Valid Up To Date.")) - def update_price_list_details(self): if self.price_list: price_list_details = frappe.db.get_value( diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index b926e9826eb..a0c6ec37e8b 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -313,7 +313,7 @@ }, { "fieldname": "address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "label": "Address", "read_only": 1 }, @@ -352,7 +352,7 @@ }, { "fieldname": "shipping_address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "label": "Shipping Address", "print_hide": 1, "read_only": 1 @@ -1095,7 +1095,7 @@ }, { "fieldname": "billing_address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "label": "Billing Address", "read_only": 1 }, @@ -1252,7 +1252,7 @@ "idx": 261, "is_submittable": 1, "links": [], - "modified": "2024-03-20 16:05:31.713453", + "modified": "2024-03-22 16:15:10.937188", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 034dd0a799c..e87e20d33b6 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -40,7 +40,7 @@ class PurchaseReceipt(BuyingController): from erpnext.stock.doctype.purchase_receipt_item.purchase_receipt_item import PurchaseReceiptItem additional_discount_percentage: DF.Float - address_display: DF.SmallText | None + address_display: DF.TextEditor | None amended_from: DF.Link | None apply_discount_on: DF.Literal["", "Grand Total", "Net Total"] apply_putaway_rule: DF.Check @@ -56,7 +56,7 @@ class PurchaseReceipt(BuyingController): base_total: DF.Currency base_total_taxes_and_charges: DF.Currency billing_address: DF.Link | None - billing_address_display: DF.SmallText | None + billing_address_display: DF.TextEditor | None buying_price_list: DF.Link | None company: DF.Link contact_display: DF.SmallText | None @@ -109,7 +109,7 @@ class PurchaseReceipt(BuyingController): set_posting_time: DF.Check set_warehouse: DF.Link | None shipping_address: DF.Link | None - shipping_address_display: DF.SmallText | None + shipping_address_display: DF.TextEditor | None shipping_rule: DF.Link | None status: DF.Literal["", "Draft", "To Bill", "Completed", "Return Issued", "Cancelled", "Closed"] subcontracting_receipt: DF.Link | None diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index ac2fe5814d3..96a92096437 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -1081,7 +1081,9 @@ erpnext.stock.StockEntry = class StockEntry extends erpnext.stock.StockControlle cint(frappe.user_defaults?.use_serial_batch_fields) === 1 ) { this.frm.doc.items.forEach((item) => { - frappe.model.set_value(item.doctype, item.name, "use_serial_batch_fields", 1); + if (!item.serial_and_batch_bundle) { + frappe.model.set_value(item.doctype, item.name, "use_serial_batch_fields", 1); + } }); } } diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json index d45296f1310..61e9f254550 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.json +++ b/erpnext/stock/doctype/stock_entry/stock_entry.json @@ -306,7 +306,7 @@ }, { "fieldname": "source_address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "label": "Source Warehouse Address", "read_only": 1 }, @@ -336,7 +336,7 @@ }, { "fieldname": "target_address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "label": "Target Warehouse Address", "read_only": 1 }, @@ -464,7 +464,7 @@ }, { "fieldname": "address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "label": "Address" }, { @@ -681,7 +681,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2024-01-12 11:56:58.644882", + "modified": "2024-03-22 16:23:13.683565", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry", diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 5b321dec81a..d2dab8a1cc4 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -85,7 +85,7 @@ class StockEntry(StockController): add_to_transit: DF.Check additional_costs: DF.Table[LandedCostTaxesandCharges] - address_display: DF.SmallText | None + address_display: DF.TextEditor | None amended_from: DF.Link | None apply_putaway_rule: DF.Check bom_no: DF.Link | None @@ -127,14 +127,14 @@ class StockEntry(StockController): scan_barcode: DF.Data | None select_print_heading: DF.Link | None set_posting_time: DF.Check - source_address_display: DF.SmallText | None + source_address_display: DF.TextEditor | None source_warehouse_address: DF.Link | None stock_entry_type: DF.Link subcontracting_order: DF.Link | None supplier: DF.Link | None supplier_address: DF.Link | None supplier_name: DF.Data | None - target_address_display: DF.SmallText | None + target_address_display: DF.TextEditor | None target_warehouse_address: DF.Link | None to_warehouse: DF.Link | None total_additional_costs: DF.Currency @@ -2541,6 +2541,7 @@ class StockEntry(StockController): ) d.serial_and_batch_bundle = id + d.use_serial_batch_fields = 0 def get_available_serial_nos(self) -> List[str]: serial_nos = [] @@ -2640,7 +2641,9 @@ def make_stock_in_entry(source_name, target_doc=None): def set_missing_values(source, target): target.stock_entry_type = "Material Transfer" target.set_missing_values() - target.make_serial_and_batch_bundle_for_transfer() + + if not frappe.db.get_single_value("Stock Settings", "use_serial_batch_fields"): + target.make_serial_and_batch_bundle_for_transfer() def update_item(source_doc, target_doc, source_parent): target_doc.t_warehouse = "" diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index 12049f12dd1..d09282d7c7c 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -825,7 +825,9 @@ def get_price_list_rate(args, item_doc, out=None): ): if args.price_list and args.rate: insert_item_price(args) - return out + + if not price_list_rate: + return out out.price_list_rate = ( flt(price_list_rate) * flt(args.plc_conversion_rate) / flt(args.conversion_rate) @@ -889,7 +891,7 @@ def insert_item_price(args): ) -def get_item_price(args, item_code, ignore_party=False): +def get_item_price(args, item_code, ignore_party=False) -> list[dict]: """ Get name, price_list_rate from Item Price based on conditions Check if the desired qty is within the increment of the packing list. @@ -911,6 +913,7 @@ def get_item_price(args, item_code, ignore_party=False): .orderby(ip.valid_from, order=frappe.qb.desc) .orderby(IfNull(ip.batch_no, ""), order=frappe.qb.desc) .orderby(ip.uom, order=frappe.qb.desc) + .limit(1) ) if not ignore_party: @@ -927,7 +930,7 @@ def get_item_price(args, item_code, ignore_party=False): & (IfNull(ip.valid_upto, "2500-12-31") >= args["transaction_date"]) ) - return query.run() + return query.run(as_dict=True) def get_price_list_rate_for(args, item_code): @@ -953,7 +956,7 @@ def get_price_list_rate_for(args, item_code): price_list_rate = get_item_price(item_price_args, item_code) if price_list_rate: desired_qty = args.get("qty") - if desired_qty and check_packing_list(price_list_rate[0][0], desired_qty, item_code): + if desired_qty and check_packing_list(price_list_rate[0].name, desired_qty, item_code): item_price_data = price_list_rate else: for field in ["customer", "supplier"]: @@ -973,12 +976,12 @@ def get_price_list_rate_for(args, item_code): item_price_data = general_price_list_rate if item_price_data: - if item_price_data[0][2] == args.get("uom"): - return item_price_data[0][1] + if item_price_data[0].uom == args.get("uom"): + return item_price_data[0].price_list_rate elif not args.get("price_list_uom_dependant"): - return flt(item_price_data[0][1] * flt(args.get("conversion_factor", 1))) + return flt(item_price_data[0].price_list_rate * flt(args.get("conversion_factor", 1))) else: - return item_price_data[0][1] + return item_price_data[0].price_list_rate def check_packing_list(price_list_rate_name, desired_qty, item_code): diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.json b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.json index 507e23365cc..7cbf7db5251 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.json +++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.json @@ -182,7 +182,7 @@ }, { "fieldname": "address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "label": "Supplier Address Details", "read_only": 1 }, @@ -230,7 +230,7 @@ }, { "fieldname": "shipping_address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "label": "Shipping Address Details", "print_hide": 1, "read_only": 1 @@ -243,7 +243,7 @@ }, { "fieldname": "billing_address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "label": "Billing Address Details", "read_only": 1 }, @@ -454,7 +454,7 @@ "icon": "fa fa-file-text", "is_submittable": 1, "links": [], - "modified": "2024-01-03 20:56:04.670380", + "modified": "2024-03-22 16:15:07.797633", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Order", diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py index daccbbbd0f9..321ae517c80 100644 --- a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py +++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py @@ -36,10 +36,10 @@ class SubcontractingOrder(SubcontractingController): ) additional_costs: DF.Table[LandedCostTaxesandCharges] - address_display: DF.SmallText | None + address_display: DF.TextEditor | None amended_from: DF.Link | None billing_address: DF.Link | None - billing_address_display: DF.SmallText | None + billing_address_display: DF.TextEditor | None company: DF.Link contact_display: DF.SmallText | None contact_email: DF.SmallText | None @@ -59,7 +59,7 @@ class SubcontractingOrder(SubcontractingController): set_reserve_warehouse: DF.Link | None set_warehouse: DF.Link | None shipping_address: DF.Link | None - shipping_address_display: DF.SmallText | None + shipping_address_display: DF.TextEditor | None status: DF.Literal[ "Draft", "Open", diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json index 383a83b3fcd..8b9cbe39a7b 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json @@ -192,7 +192,7 @@ }, { "fieldname": "address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "label": "Address", "read_only": 1 }, @@ -231,7 +231,7 @@ }, { "fieldname": "shipping_address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "label": "Shipping Address", "print_hide": 1, "read_only": 1 @@ -521,7 +521,7 @@ }, { "fieldname": "billing_address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "label": "Billing Address", "read_only": 1 }, @@ -645,7 +645,7 @@ "in_create": 1, "is_submittable": 1, "links": [], - "modified": "2023-11-16 13:04:00.710534", + "modified": "2024-03-22 16:15:08.074134", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Receipt", diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index 3467b8283cd..4e2b9c2a3b9 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -33,13 +33,13 @@ class SubcontractingReceipt(SubcontractingController): ) additional_costs: DF.Table[LandedCostTaxesandCharges] - address_display: DF.SmallText | None + address_display: DF.TextEditor | None amended_from: DF.Link | None auto_repeat: DF.Link | None bill_date: DF.Date | None bill_no: DF.Data | None billing_address: DF.Link | None - billing_address_display: DF.SmallText | None + billing_address_display: DF.TextEditor | None company: DF.Link contact_display: DF.SmallText | None contact_email: DF.SmallText | None @@ -69,7 +69,7 @@ class SubcontractingReceipt(SubcontractingController): set_posting_time: DF.Check set_warehouse: DF.Link | None shipping_address: DF.Link | None - shipping_address_display: DF.SmallText | None + shipping_address_display: DF.TextEditor | None status: DF.Literal["", "Draft", "Completed", "Return", "Return Issued", "Cancelled", "Closed"] supplied_items: DF.Table[SubcontractingReceiptSuppliedItem] supplier: DF.Link @@ -149,7 +149,9 @@ class SubcontractingReceipt(SubcontractingController): self.update_prevdoc_status() self.set_subcontracting_order_status() self.set_consumed_qty_in_subcontract_order() - self.make_bundle_using_old_serial_batch_fields() + + for table_name in ["items", "supplied_items"]: + self.make_bundle_using_old_serial_batch_fields(table_name) self.update_stock_ledger() self.make_gl_entries() self.repost_future_sle_and_gle() diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py index 3e74ac91f4b..4f4a1bd5f46 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py @@ -292,6 +292,7 @@ class TestSubcontractingReceipt(FrappeTestCase): self.assertRaises(OverAllowanceError, make_return_subcontracting_receipt, **args) def test_subcontracting_receipt_no_gl_entry(self): + frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 0) sco = get_subcontracting_order() rm_items = get_rm_items(sco.supplied_items) itemwise_details = make_stock_in_entry(rm_items=rm_items) @@ -327,8 +328,10 @@ class TestSubcontractingReceipt(FrappeTestCase): # Service Cost(100 * 10) + Raw Materials Cost(100 * 10) + Additional Costs(10 * 10) = 2100 self.assertEqual(stock_value_difference, 2100) self.assertFalse(get_gl_entries("Subcontracting Receipt", scr.name)) + frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1) def test_subcontracting_receipt_gl_entry(self): + frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 0) sco = get_subcontracting_order( company="_Test Company with perpetual inventory", warehouse="Stores - TCP1", @@ -387,6 +390,7 @@ class TestSubcontractingReceipt(FrappeTestCase): scr.reload() scr.cancel() self.assertTrue(get_gl_entries("Subcontracting Receipt", scr.name)) + frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1) def test_supplied_items_consumed_qty(self): # Set Backflush Based On as "Material Transferred for Subcontracting" to transfer RM's more than the required qty @@ -664,6 +668,7 @@ class TestSubcontractingReceipt(FrappeTestCase): ) def test_subcontracting_receipt_valuation_for_fg_with_auto_created_serial_batch_bundle(self): + frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 0) set_backflush_based_on("BOM") fg_item = make_item( @@ -760,9 +765,11 @@ class TestSubcontractingReceipt(FrappeTestCase): frappe.db.set_single_value( "Stock Settings", "auto_create_serial_and_batch_bundle_for_outward", 0 ) + frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1) def test_subcontracting_receipt_raw_material_rate(self): # Step - 1: Set Backflush Based On as "BOM" + frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 0) set_backflush_based_on("BOM") # Step - 2: Create FG and RM Items @@ -820,6 +827,8 @@ class TestSubcontractingReceipt(FrappeTestCase): self.assertEqual(rm_item.rate, 100) self.assertEqual(rm_item.amount, rm_item.consumed_qty * rm_item.rate) + frappe.db.set_single_value("Stock Settings", "use_serial_batch_fields", 1) + def test_quality_inspection_for_subcontracting_receipt(self): from erpnext.stock.doctype.quality_inspection.test_quality_inspection import ( create_quality_inspection, diff --git a/erpnext/support/doctype/warranty_claim/warranty_claim.json b/erpnext/support/doctype/warranty_claim/warranty_claim.json index 9af2b4606c9..4ff1bcc8621 100644 --- a/erpnext/support/doctype/warranty_claim/warranty_claim.json +++ b/erpnext/support/doctype/warranty_claim/warranty_claim.json @@ -311,7 +311,7 @@ { "depends_on": "customer", "fieldname": "address_display", - "fieldtype": "Small Text", + "fieldtype": "Text Editor", "label": "Address", "read_only": 1 }, @@ -379,7 +379,7 @@ "icon": "fa fa-bug", "idx": 1, "links": [], - "modified": "2023-11-28 17:30:35.676410", + "modified": "2024-03-22 16:01:11.412114", "modified_by": "Administrator", "module": "Support", "name": "Warranty Claim", diff --git a/erpnext/support/doctype/warranty_claim/warranty_claim.py b/erpnext/support/doctype/warranty_claim/warranty_claim.py index e0eb5a3ab88..658be5708df 100644 --- a/erpnext/support/doctype/warranty_claim/warranty_claim.py +++ b/erpnext/support/doctype/warranty_claim/warranty_claim.py @@ -18,7 +18,7 @@ class WarrantyClaim(TransactionBase): if TYPE_CHECKING: from frappe.types import DF - address_display: DF.SmallText | None + address_display: DF.TextEditor | None amc_expiry_date: DF.Date | None amended_from: DF.Link | None company: DF.Link diff --git a/erpnext/utilities/bulk_transaction.py b/erpnext/utilities/bulk_transaction.py index 3538c24aeba..343a88116fa 100644 --- a/erpnext/utilities/bulk_transaction.py +++ b/erpnext/utilities/bulk_transaction.py @@ -176,7 +176,7 @@ def create_log(doc_name, e, from_doctype, to_doctype, status, log_date=None, res transaction_log.from_doctype = from_doctype transaction_log.to_doctype = to_doctype transaction_log.retried = restarted - transaction_log.save() + transaction_log.save(ignore_permissions=True) def show_job_status(fail_count, deserialized_data_count, to_doctype):