diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.json b/erpnext/accounts/doctype/fiscal_year/fiscal_year.json index ff4ac50850f..496dd44ad86 100644 --- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.json +++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.json @@ -82,7 +82,7 @@ "icon": "fa fa-calendar", "idx": 1, "links": [], - "modified": "2020-11-05 12:16:53.081573", + "modified": "2024-05-27 17:29:55.560840", "modified_by": "Administrator", "module": "Accounts", "name": "Fiscal Year", @@ -126,6 +126,10 @@ { "read": 1, "role": "Stock Manager" + }, + { + "read": 1, + "role": "Auditor" } ], "show_name_in_global_search": 1, diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js index cda0adc7257..f3785800de0 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.js +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js @@ -171,7 +171,7 @@ frappe.ui.form.on("Journal Entry", { !(frm.doc.accounts || []).length || ((frm.doc.accounts || []).length === 1 && !frm.doc.accounts[0].account) ) { - if (in_list(["Bank Entry", "Cash Entry"], frm.doc.voucher_type)) { + if (["Bank Entry", "Cash Entry"].includes(frm.doc.voucher_type)) { return frappe.call({ type: "GET", method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_default_bank_cash_account", @@ -283,7 +283,7 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro filters: [[jvd.reference_type, "docstatus", "=", 1]], }; - if (in_list(["Sales Invoice", "Purchase Invoice"], jvd.reference_type)) { + if (["Sales Invoice", "Purchase Invoice"].includes(jvd.reference_type)) { out.filters.push([jvd.reference_type, "outstanding_amount", "!=", 0]); // Filter by cost center if (jvd.cost_center) { @@ -295,7 +295,7 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro out.filters.push([jvd.reference_type, party_account_field, "=", jvd.account]); } - if (in_list(["Sales Order", "Purchase Order"], jvd.reference_type)) { + if (["Sales Order", "Purchase Order"].includes(jvd.reference_type)) { // party_type and party mandatory frappe.model.validate_missing(jvd, "party_type"); frappe.model.validate_missing(jvd, "party"); diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index e184a3e00fd..ad1d200ec53 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -21,7 +21,7 @@ frappe.ui.form.on('Payment Entry', { frm.set_query("paid_from", function() { frm.events.validate_company(frm); - var account_types = in_list(["Pay", "Internal Transfer"], frm.doc.payment_type) ? + var account_types = ["Pay", "Internal Transfer"].includes(frm.doc.payment_type) ? ["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]]; return { filters: { @@ -75,7 +75,7 @@ frappe.ui.form.on('Payment Entry', { frm.set_query("paid_to", function() { frm.events.validate_company(frm); - var account_types = in_list(["Receive", "Internal Transfer"], frm.doc.payment_type) ? + var account_types = ["Receive", "Internal Transfer"].includes(frm.doc.payment_type) ? ["Bank", "Cash"] : [frappe.boot.party_account_types[frm.doc.party_type]]; return { filters: { @@ -121,7 +121,7 @@ frappe.ui.form.on('Payment Entry', { frm.set_query('payment_term', 'references', function(frm, cdt, cdn) { const child = locals[cdt][cdn]; - if (in_list(['Purchase Invoice', 'Sales Invoice'], child.reference_doctype) && child.reference_name) { + if (['Purchase Invoice', 'Sales Invoice'].includes(child.reference_doctype) && child.reference_name) { return { query: "erpnext.controllers.queries.get_payment_terms_for_references", filters: { @@ -485,7 +485,7 @@ frappe.ui.form.on('Payment Entry', { if (frm.doc.paid_from_account_currency == company_currency) { frm.set_value("source_exchange_rate", 1); } else if (frm.doc.paid_from){ - if (in_list(["Internal Transfer", "Pay"], frm.doc.payment_type)) { + if (["Internal Transfer", "Pay"].includes(frm.doc.payment_type)) { let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency; frappe.call({ method: "erpnext.setup.utils.get_exchange_rate", @@ -853,7 +853,7 @@ frappe.ui.form.on('Payment Entry', { } var allocated_positive_outstanding = paid_amount + allocated_negative_outstanding; - } else if (in_list(["Customer", "Supplier"], frm.doc.party_type)) { + } else if (["Customer", "Supplier"].includes(frm.doc.party_type)) { total_negative_outstanding = flt(total_negative_outstanding, precision("outstanding_amount")) if(paid_amount > total_negative_outstanding) { if(total_negative_outstanding == 0) { @@ -988,7 +988,7 @@ frappe.ui.form.on('Payment Entry', { } if(frm.doc.party_type=="Customer" && - !in_list(["Sales Order", "Sales Invoice", "Journal Entry", "Dunning"], row.reference_doctype) + !["Sales Order", "Sales Invoice", "Journal Entry", "Dunning"].includes(row.reference_doctype) ) { frappe.model.set_value(row.doctype, row.name, "reference_doctype", null); frappe.msgprint(__("Row #{0}: Reference Document Type must be one of Sales Order, Sales Invoice, Journal Entry or Dunning", [row.idx])); @@ -996,7 +996,7 @@ frappe.ui.form.on('Payment Entry', { } if(frm.doc.party_type=="Supplier" && - !in_list(["Purchase Order", "Purchase Invoice", "Journal Entry"], row.reference_doctype) + !["Purchase Order", "Purchase Invoice", "Journal Entry"].includes(row.reference_doctype) ) { frappe.model.set_value(row.doctype, row.name, "against_voucher_type", null); frappe.msgprint(__("Row #{0}: Reference Document Type must be one of Purchase Order, Purchase Invoice or Journal Entry", [row.idx])); @@ -1080,7 +1080,7 @@ frappe.ui.form.on('Payment Entry', { bank_account: function(frm) { const field = frm.doc.payment_type == "Pay" ? "paid_from":"paid_to"; - if (frm.doc.bank_account && in_list(['Pay', 'Receive'], frm.doc.payment_type)) { + if (frm.doc.bank_account && ['Pay', 'Receive'].includes(frm.doc.payment_type)) { frappe.call({ method: "erpnext.accounts.doctype.bank_account.bank_account.get_bank_account_details", args: { diff --git a/erpnext/accounts/doctype/payment_request/payment_request.js b/erpnext/accounts/doctype/payment_request/payment_request.js index e5a6040c735..e45aa512fe8 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.js +++ b/erpnext/accounts/doctype/payment_request/payment_request.js @@ -28,7 +28,7 @@ frappe.ui.form.on("Payment Request", "refresh", function (frm) { if ( frm.doc.payment_request_type == "Inward" && frm.doc.payment_channel !== "Phone" && - !in_list(["Initiated", "Paid"], frm.doc.status) && + !["Initiated", "Paid"].includes(frm.doc.status) && !frm.doc.__islocal && frm.doc.docstatus == 1 ) { diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index b2672424af9..ad857840147 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -200,7 +200,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e 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; - } else if(in_list([cur_frm.pos_print_format, cur_frm.return_print_format], cur_frm.meta.default_print_format)) { + } else if([cur_frm.pos_print_format, cur_frm.return_print_format].includes(cur_frm.meta.default_print_format)) { cur_frm.meta.default_print_format = null; cur_frm.meta._default_print_format = null; } diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index 49c0b24e4cb..b6afe11e4ff 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -78,7 +78,7 @@ frappe.ui.form.on("Asset", { frm.events.make_schedules_editable(frm); if (frm.doc.docstatus == 1) { - if (in_list(["Submitted", "Partially Depreciated", "Fully Depreciated"], frm.doc.status)) { + if (["Submitted", "Partially Depreciated", "Fully Depreciated"].includes(frm.doc.status)) { frm.add_custom_button( __("Transfer Asset"), function () { @@ -280,7 +280,7 @@ frappe.ui.form.on("Asset", { if (v.journal_entry) { asset_values.push(asset_value); } else { - if (in_list(["Scrapped", "Sold"], frm.doc.status)) { + if (["Scrapped", "Sold"].includes(frm.doc.status)) { asset_values.push(null); } else { asset_values.push(asset_value); @@ -312,7 +312,7 @@ frappe.ui.form.on("Asset", { }); } - if (in_list(["Scrapped", "Sold"], frm.doc.status)) { + if (["Scrapped", "Sold"].includes(frm.doc.status)) { x_intervals.push(frappe.format(frm.doc.disposal_date, { fieldtype: "Date" })); asset_values.push(0); } diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js index 52a0f4ac2a2..d6fd53dc778 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.js +++ b/erpnext/buying/doctype/purchase_order/purchase_order.js @@ -6,8 +6,8 @@ frappe.provide("erpnext.accounts.dimensions"); {% include 'erpnext/public/js/controllers/buying.js' %}; frappe.ui.form.on("Purchase Order", { - setup: function(frm) { - + setup: function (frm) { + frm.ignore_doctypes_on_cancel_all = ["Unreconcile Payment", "Unreconcile Payment Entries"]; if (frm.doc.is_old_subcontracting_flow) { frm.set_query("reserve_warehouse", "supplied_items", function() { return { @@ -180,7 +180,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends e this.frm.fields_dict.items_section.wrapper.removeClass("hide-border"); } - if(!in_list(["Closed", "Delivered"], doc.status)) { + if(!["Closed", "Delivered"].includes(doc.status)) { if(this.frm.doc.status !== 'Closed' && flt(this.frm.doc.per_received) < 100 && flt(this.frm.doc.per_billed) < 100) { // Don't add Update Items button if the PO is following the new subcontracting flow. if (!(this.frm.doc.is_subcontracted && !this.frm.doc.is_old_subcontracting_flow)) { @@ -211,7 +211,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends e this.frm.page.set_inner_btn_group_as_primary(__("Status")); } - } else if(in_list(["Closed", "Delivered"], doc.status)) { + } else if(["Closed", "Delivered"].includes(doc.status)) { if (this.frm.has_perm("submit")) { this.frm.add_custom_button(__('Re-open'), () => this.unclose_purchase_order(), __("Status")); } diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 58d7440211c..705e8fd6dd8 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -345,7 +345,13 @@ class PurchaseOrder(BuyingController): update_linked_doc(self.doctype, self.name, self.inter_company_order_reference) def on_cancel(self): - self.ignore_linked_doctypes = ("GL Entry", "Payment Ledger Entry") + self.ignore_linked_doctypes = ( + "GL Entry", + "Payment Ledger Entry", + "Unreconcile Payment", + "Unreconcile Payment Entries", + ) + super().on_cancel() if self.is_against_so(): diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 89226453b7f..364aadc8eae 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -8,6 +8,7 @@ from frappe.contacts.doctype.address.address import render_address from frappe.utils import cint, cstr, flt, getdate from frappe.utils.data import nowtime +import erpnext from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget from erpnext.accounts.party import get_party_details from erpnext.buying.utils import update_last_purchase_rate, validate_for_items @@ -305,6 +306,8 @@ class BuyingController(SubcontractingController): else: item.valuation_rate = 0.0 + update_regional_item_valuation_rate(self) + def set_incoming_rate(self): if self.doctype not in ("Purchase Receipt", "Purchase Invoice", "Purchase Order"): return @@ -894,3 +897,8 @@ def validate_item_type(doc, fieldname, message): ).format(items, message) frappe.throw(error_message) + + +@erpnext.allow_regional +def update_regional_item_valuation_rate(doc): + pass diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 41085181720..a4d8e3efee1 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -51,7 +51,7 @@ class StockController(AccountsController): self.validate_internal_transfer() self.validate_putaway_capacity() - def make_gl_entries(self, gl_entries=None, from_repost=False): + def make_gl_entries(self, gl_entries=None, from_repost=False, via_landed_cost_voucher=False): if self.docstatus == 2: make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name) @@ -72,7 +72,11 @@ class StockController(AccountsController): if self.docstatus == 1: if not gl_entries: - gl_entries = self.get_gl_entries(warehouse_account) + gl_entries = ( + self.get_gl_entries(warehouse_account, via_landed_cost_voucher) + if self.doctype == "Purchase Receipt" + else self.get_gl_entries(warehouse_account) + ) make_gl_entries(gl_entries, from_repost=from_repost) def validate_serialized_batch(self): diff --git a/erpnext/manufacturing/doctype/bom/bom.js b/erpnext/manufacturing/doctype/bom/bom.js index b7ab6ef3872..bf8cb05d8ce 100644 --- a/erpnext/manufacturing/doctype/bom/bom.js +++ b/erpnext/manufacturing/doctype/bom/bom.js @@ -400,7 +400,7 @@ frappe.ui.form.on("BOM", { }, rm_cost_as_per(frm) { - if (in_list(["Valuation Rate", "Last Purchase Rate"], frm.doc.rm_cost_as_per)) { + if (["Valuation Rate", "Last Purchase Rate"].includes(frm.doc.rm_cost_as_per)) { frm.set_value("plc_conversion_rate", 1.0); } }, diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js index 54d1414c814..6db901c71a4 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.js +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js @@ -129,7 +129,7 @@ frappe.ui.form.on("Production Plan", { if ( frm.doc.mr_items && frm.doc.mr_items.length && - !in_list(["Material Requested", "Closed"], frm.doc.status) + !["Material Requested", "Closed"].includes(frm.doc.status) ) { frm.add_custom_button( __("Material Request"), diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index 5e386035514..ea86d0a939c 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -194,7 +194,7 @@ frappe.ui.form.on("Work Order", { }, add_custom_button_to_return_components: function (frm) { - if (frm.doc.docstatus === 1 && in_list(["Closed", "Completed"], frm.doc.status)) { + if (frm.doc.docstatus === 1 && ["Closed", "Completed"].includes(frm.doc.status)) { let non_consumed_items = frm.doc.required_items.filter((d) => { return flt(d.consumed_qty) < flt(d.transferred_qty - d.returned_qty); }); @@ -594,7 +594,7 @@ erpnext.work_order = { ); } - if (doc.docstatus === 1 && !in_list(["Closed", "Completed"], doc.status)) { + if (doc.docstatus === 1 && !["Closed", "Completed"].includes(doc.status)) { if (doc.status != "Stopped" && doc.status != "Completed") { frm.add_custom_button( __("Stop"), diff --git a/erpnext/patches/v14_0/migrate_existing_lead_notes_as_per_the_new_format.py b/erpnext/patches/v14_0/migrate_existing_lead_notes_as_per_the_new_format.py index ec72527552c..d740a5b9c8e 100644 --- a/erpnext/patches/v14_0/migrate_existing_lead_notes_as_per_the_new_format.py +++ b/erpnext/patches/v14_0/migrate_existing_lead_notes_as_per_the_new_format.py @@ -9,15 +9,13 @@ def execute(): dt = frappe.qb.DocType(doctype) records = ( - frappe.qb.from_(dt) - .select(dt.name, dt.notes, dt.modified_by, dt.modified) - .where(dt.notes.isnotnull() & dt.notes != "") + frappe.qb.from_(dt).select(dt.name, dt.notes).where(dt.notes.isnotnull() & dt.notes != "") ).run(as_dict=True) for d in records: if strip_html(cstr(d.notes)).strip(): doc = frappe.get_doc(doctype, d.name) - doc.append("notes", {"note": d.notes, "added_by": d.modified_by, "added_on": d.modified}) + doc.append("notes", {"note": d.notes}) doc.update_child_table("notes") frappe.db.sql_ddl(f"alter table `tab{doctype}` drop column `notes`") diff --git a/erpnext/public/js/account_tree_grid.js b/erpnext/public/js/account_tree_grid.js index 6b4cdf1177a..1d4596ba1a4 100644 --- a/erpnext/public/js/account_tree_grid.js +++ b/erpnext/public/js/account_tree_grid.js @@ -240,7 +240,7 @@ erpnext.AccountTreeGrid = class AccountTreeGrid extends frappe.views.TreeGridRep flt(account.closing_dr) - flt(account.closing_cr); me.set_debit_or_credit(parent_account, "closing", bal); - } else if (in_list(["debit", "credit"], col.field)) { + } else if (["debit", "credit"].includes(col.field)) { parent_account[col.field] = flt(parent_account[col.field]) + flt(account[col.field]); } diff --git a/erpnext/public/js/communication.js b/erpnext/public/js/communication.js index d9187f8b678..c8905e14af2 100644 --- a/erpnext/public/js/communication.js +++ b/erpnext/public/js/communication.js @@ -20,7 +20,7 @@ frappe.ui.form.on("Communication", { ); } - if (!in_list(["Lead", "Opportunity"], frm.doc.reference_doctype)) { + if (!["Lead", "Opportunity"].includes(frm.doc.reference_doctype)) { frm.add_custom_button( __("Lead"), () => { diff --git a/erpnext/public/js/controllers/accounts.js b/erpnext/public/js/controllers/accounts.js index 72845b20742..1ba941ab6f7 100644 --- a/erpnext/public/js/controllers/accounts.js +++ b/erpnext/public/js/controllers/accounts.js @@ -9,7 +9,7 @@ frappe.ui.form.on(cur_frm.doctype, { setup: function(frm) { // set conditional display for rate column in taxes $(frm.wrapper).on('grid-row-render', function(e, grid_row) { - if(in_list(['Sales Taxes and Charges', 'Purchase Taxes and Charges'], grid_row.doc.doctype)) { + if(['Sales Taxes and Charges', 'Purchase Taxes and Charges'].includes(grid_row.doc.doctype)) { erpnext.taxes.set_conditional_mandatory_rate_or_amount(grid_row); } }); diff --git a/erpnext/public/js/controllers/buying.js b/erpnext/public/js/controllers/buying.js index b0e08cc6f26..aa7dfbd90d6 100644 --- a/erpnext/public/js/controllers/buying.js +++ b/erpnext/public/js/controllers/buying.js @@ -136,7 +136,7 @@ erpnext.buying.BuyingController = class BuyingController extends erpnext.Transac } toggle_subcontracting_fields() { - if (in_list(['Purchase Receipt', 'Purchase Invoice'], this.frm.doc.doctype)) { + if (['Purchase Receipt', 'Purchase Invoice'].includes(this.frm.doc.doctype)) { this.frm.fields_dict.supplied_items.grid.update_docfield_property('consumed_qty', 'read_only', this.frm.doc.__onload && this.frm.doc.__onload.backflush_based_on === 'BOM'); diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index 186c342a75e..670cf35bb11 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -9,7 +9,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { apply_pricing_rule_on_item(item) { let effective_item_rate = item.price_list_rate; let item_rate = item.rate; - if (in_list(["Sales Order", "Quotation"], item.parenttype) && item.blanket_order_rate) { + if (["Sales Order", "Quotation"].includes(item.parenttype) && item.blanket_order_rate) { effective_item_rate = item.blanket_order_rate; } if (item.margin_type == "Percentage") { @@ -52,7 +52,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { // Advance calculation applicable to Sales/Purchase Invoice if ( - in_list(["Sales Invoice", "POS Invoice", "Purchase Invoice"], this.frm.doc.doctype) + ["Sales Invoice", "POS Invoice", "Purchase Invoice"].includes(this.frm.doc.doctype) && this.frm.doc.docstatus < 2 && !this.frm.doc.is_return ) { @@ -60,7 +60,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { } if ( - in_list(["Sales Invoice", "POS Invoice"], this.frm.doc.doctype) + ["Sales Invoice", "POS Invoice"].includes(this.frm.doc.doctype) && this.frm.doc.is_pos && this.frm.doc.is_return ) { @@ -69,7 +69,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { } // Sales person's commission - if (in_list(["Quotation", "Sales Order", "Delivery Note", "Sales Invoice"], this.frm.doc.doctype)) { + if (["Quotation", "Sales Order", "Delivery Note", "Sales Invoice"].includes(this.frm.doc.doctype)) { this.calculate_commission(); this.calculate_contribution(); } @@ -547,7 +547,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { ? this.frm.doc["taxes"][tax_count - 1].total + flt(this.frm.doc.rounding_adjustment) : this.frm.doc.net_total); - if(in_list(["Quotation", "Sales Order", "Delivery Note", "Sales Invoice", "POS Invoice"], this.frm.doc.doctype)) { + if(["Quotation", "Sales Order", "Delivery Note", "Sales Invoice", "POS Invoice"].includes(this.frm.doc.doctype)) { this.frm.doc.base_grand_total = (this.frm.doc.total_taxes_and_charges) ? flt(this.frm.doc.grand_total * this.frm.doc.conversion_rate) : this.frm.doc.base_net_total; } else { @@ -555,7 +555,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { this.frm.doc.taxes_and_charges_added = this.frm.doc.taxes_and_charges_deducted = 0.0; if(tax_count) { $.each(this.frm.doc["taxes"] || [], function(i, tax) { - if (in_list(["Valuation and Total", "Total"], tax.category)) { + if (["Valuation and Total", "Total"].includes(tax.category)) { if(tax.add_deduct_tax == "Add") { me.frm.doc.taxes_and_charges_added += flt(tax.tax_amount_after_discount_amount); } else { @@ -702,7 +702,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { var actual_taxes_dict = {}; $.each(this.frm.doc["taxes"] || [], function(i, tax) { - if (in_list(["Actual", "On Item Quantity"], tax.charge_type)) { + if (["Actual", "On Item Quantity"].includes(tax.charge_type)) { var tax_amount = (tax.category == "Valuation") ? 0.0 : tax.tax_amount; tax_amount *= (tax.add_deduct_tax == "Deduct") ? -1.0 : 1.0; actual_taxes_dict[tax.idx] = tax_amount; @@ -747,7 +747,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { // NOTE: // paid_amount and write_off_amount is only for POS/Loyalty Point Redemption Invoice // total_advance is only for non POS Invoice - if(in_list(["Sales Invoice", "POS Invoice"], this.frm.doc.doctype) && this.frm.doc.is_return){ + if(["Sales Invoice", "POS Invoice"].includes(this.frm.doc.doctype) && this.frm.doc.is_return){ this.calculate_paid_amount(); } @@ -755,7 +755,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { frappe.model.round_floats_in(this.frm.doc, ["grand_total", "total_advance", "write_off_amount"]); - if(in_list(["Sales Invoice", "POS Invoice", "Purchase Invoice"], this.frm.doc.doctype)) { + if(["Sales Invoice", "POS Invoice", "Purchase Invoice"].includes(this.frm.doc.doctype)) { let grand_total = this.frm.doc.rounded_total || this.frm.doc.grand_total; let base_grand_total = this.frm.doc.base_rounded_total || this.frm.doc.base_grand_total; @@ -778,7 +778,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { this.frm.refresh_field("base_paid_amount"); } - if(in_list(["Sales Invoice", "POS Invoice"], this.frm.doc.doctype)) { + if(["Sales Invoice", "POS Invoice"].includes(this.frm.doc.doctype)) { let total_amount_for_payment = (this.frm.doc.redeem_loyalty_points && this.frm.doc.loyalty_amount) ? flt(total_amount_to_pay - this.frm.doc.loyalty_amount, precision("base_grand_total")) : total_amount_to_pay; @@ -882,7 +882,7 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { calculate_change_amount(){ this.frm.doc.change_amount = 0.0; this.frm.doc.base_change_amount = 0.0; - if(in_list(["Sales Invoice", "POS Invoice"], this.frm.doc.doctype) + if(["Sales Invoice", "POS Invoice"].includes(this.frm.doc.doctype) && this.frm.doc.paid_amount > this.frm.doc.grand_total && !this.frm.doc.is_return) { var payment_types = $.map(this.frm.doc.payments, function(d) { return d.type; }); diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 66ac9582b2d..e3e25499b84 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -260,7 +260,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe } setup_quality_inspection() { - if(!in_list(["Delivery Note", "Sales Invoice", "Purchase Receipt", "Purchase Invoice"], this.frm.doc.doctype)) { + if(!["Delivery Note", "Sales Invoice", "Purchase Receipt", "Purchase Invoice"].includes(this.frm.doc.doctype)) { return; } @@ -272,7 +272,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe this.frm.page.set_inner_btn_group_as_primary(__('Create')); } - const inspection_type = in_list(["Purchase Receipt", "Purchase Invoice"], this.frm.doc.doctype) + const inspection_type = ["Purchase Receipt", "Purchase Invoice"].includes(this.frm.doc.doctype) ? "Incoming" : "Outgoing"; let quality_inspection_field = this.frm.get_docfield("items", "quality_inspection"); @@ -304,7 +304,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe make_payment_request() { let me = this; - const payment_request_type = (in_list(['Sales Order', 'Sales Invoice'], this.frm.doc.doctype)) + const payment_request_type = (['Sales Order', 'Sales Invoice'].includes(this.frm.doc.doctype)) ? "Inward" : "Outward"; frappe.call({ @@ -417,7 +417,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe setup_sms() { var me = this; let blacklist = ['Purchase Invoice', 'BOM']; - if(this.frm.doc.docstatus===1 && !in_list(["Lost", "Stopped", "Closed"], this.frm.doc.status) + if(this.frm.doc.docstatus===1 && !["Lost", "Stopped", "Closed"].includes(this.frm.doc.status) && !blacklist.includes(this.frm.doctype)) { this.frm.page.add_menu_item(__('Send SMS'), function() { me.send_sms(); }); } @@ -780,7 +780,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe } var set_party_account = function(set_pricing) { - if (in_list(["Sales Invoice", "Purchase Invoice"], me.frm.doc.doctype)) { + if (["Sales Invoice", "Purchase Invoice"].includes(me.frm.doc.doctype)) { if(me.frm.doc.doctype=="Sales Invoice") { var party_type = "Customer"; var party_account_field = 'debit_to'; @@ -815,7 +815,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe } if (frappe.meta.get_docfield(this.frm.doctype, "shipping_address") && - in_list(['Purchase Order', 'Purchase Receipt', 'Purchase Invoice'], this.frm.doctype)) { + ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice'].includes(this.frm.doctype)) { erpnext.utils.get_shipping_address(this.frm, function() { set_party_account(set_pricing); }); @@ -1482,7 +1482,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe "doctype": me.frm.doc.doctype, "name": me.frm.doc.name, "is_return": cint(me.frm.doc.is_return), - "update_stock": in_list(['Sales Invoice', 'Purchase Invoice'], me.frm.doc.doctype) ? cint(me.frm.doc.update_stock) : 0, + "update_stock": ['Sales Invoice', 'Purchase Invoice'].includes(me.frm.doc.doctype) ? cint(me.frm.doc.update_stock) : 0, "conversion_factor": me.frm.doc.conversion_factor, "pos_profile": me.frm.doc.doctype == 'Sales Invoice' ? me.frm.doc.pos_profile : '', "coupon_code": me.frm.doc.coupon_code @@ -2126,7 +2126,7 @@ 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(in_list(['Sales Invoice', 'Purchase Invoice'], cur_frm.doc.doctype)){ + if(['Sales Invoice', 'Purchase Invoice'].includes( cur_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/payment/payments.js b/erpnext/public/js/payment/payments.js index 0e584205396..c91bb046a52 100644 --- a/erpnext/public/js/payment/payments.js +++ b/erpnext/public/js/payment/payments.js @@ -218,7 +218,7 @@ erpnext.payments = class payments extends erpnext.stock.StockController { update_paid_amount(update_write_off) { var me = this; - if (in_list(["change_amount", "write_off_amount"], this.idx)) { + if (["change_amount", "write_off_amount"].includes(this.idx)) { var value = me.selected_mode.val(); if (me.idx == "change_amount") { me.change_amount(value); diff --git a/erpnext/public/js/sms_manager.js b/erpnext/public/js/sms_manager.js index d3147bb4600..63833da5af3 100644 --- a/erpnext/public/js/sms_manager.js +++ b/erpnext/public/js/sms_manager.js @@ -28,11 +28,11 @@ erpnext.SMSManager = function SMSManager(doc) { "Purchase Receipt": "Items has been received against purchase receipt: " + doc.name, }; - if (in_list(["Sales Order", "Delivery Note", "Sales Invoice"], doc.doctype)) + if (["Sales Order", "Delivery Note", "Sales Invoice"].includes(doc.doctype)) this.show(doc.contact_person, "Customer", doc.customer, "", default_msg[doc.doctype]); else if (doc.doctype === "Quotation") this.show(doc.contact_person, "Customer", doc.party_name, "", default_msg[doc.doctype]); - else if (in_list(["Purchase Order", "Purchase Receipt"], doc.doctype)) + else if (["Purchase Order", "Purchase Receipt"].includes(doc.doctype)) this.show(doc.contact_person, "Supplier", doc.supplier, "", default_msg[doc.doctype]); else if (doc.doctype == "Lead") this.show("", "", "", doc.mobile_no, default_msg[doc.doctype]); else if (doc.doctype == "Opportunity") diff --git a/erpnext/public/js/templates/crm_notes.html b/erpnext/public/js/templates/crm_notes.html index 53df9330784..a20e6c2723c 100644 --- a/erpnext/public/js/templates/crm_notes.html +++ b/erpnext/public/js/templates/crm_notes.html @@ -12,6 +12,7 @@ {% for(var i=0, l=notes.length; i
+ {% if (notes[i].added_by && notes[i].added_on) %}
{{ frappe.avatar(notes[i].added_by) }} @@ -25,6 +26,7 @@
+ {% } %}
{{ notes[i].note }} diff --git a/erpnext/public/js/utils/party.js b/erpnext/public/js/utils/party.js index 801376b2ed7..623941755d1 100644 --- a/erpnext/public/js/utils/party.js +++ b/erpnext/public/js/utils/party.js @@ -14,10 +14,10 @@ erpnext.utils.get_party_details = function (frm, method, args, callback) { if (!args) { if ( (frm.doctype != "Purchase Order" && frm.doc.customer) || - (frm.doc.party_name && in_list(["Quotation", "Opportunity"], frm.doc.doctype)) + (frm.doc.party_name && ["Quotation", "Opportunity"].includes(frm.doc.doctype)) ) { let party_type = "Customer"; - if (frm.doc.quotation_to && in_list(["Lead", "Prospect"], frm.doc.quotation_to)) { + if (frm.doc.quotation_to && ["Lead", "Prospect"].includes(frm.doc.quotation_to)) { party_type = frm.doc.quotation_to; } diff --git a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.js b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.js index 5fbb5cb7e01..7aa8012f0b6 100644 --- a/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.js +++ b/erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.js @@ -34,7 +34,7 @@ frappe.ui.form.on("Import Supplier Invoice", { }, toggle_read_only_fields: function (frm) { - if (in_list(["File Import Completed", "Processing File Data"], frm.doc.status)) { + if (["File Import Completed", "Processing File Data"].includes(frm.doc.status)) { cur_frm.set_read_only(); cur_frm.refresh_fields(); frm.set_df_property("import_invoices", "hidden", 1); diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 2995a1e99bc..db3d6c2ca54 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -150,7 +150,11 @@ frappe.ui.form.on("Sales Order", { frm.set_value("advance_paid", 0) } - frm.ignore_doctypes_on_cancel_all = ['Purchase Order']; + frm.ignore_doctypes_on_cancel_all = [ + "Purchase Order", + "Unreconcile Payment", + "Unreconcile Payment Entries", + ]; }, delivery_date: function(frm) { diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 52b5e4051bb..5b4597125fb 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -269,7 +269,13 @@ class SalesOrder(SellingController): update_coupon_code_count(self.coupon_code, "used") def on_cancel(self): - self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Payment Ledger Entry") + self.ignore_linked_doctypes = ( + "GL Entry", + "Stock Ledger Entry", + "Payment Ledger Entry", + "Unreconcile Payment", + "Unreconcile Payment Entries", + ) super().on_cancel() # Cannot cancel closed SO diff --git a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js index 96b2c051e72..b0b64e64747 100644 --- a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js +++ b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js @@ -73,7 +73,7 @@ erpnext.PointOfSale.PastOrderSummary = class { const { status } = doc; let indicator_color = ""; - in_list(["Paid", "Consolidated"], status) && (indicator_color = "green"); + ["Paid", "Consolidated"].includes(status) && (indicator_color = "green"); status === "Draft" && (indicator_color = "red"); status === "Return" && (indicator_color = "grey"); diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index b2a64a5d461..ad2f1e5f282 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -219,7 +219,7 @@ erpnext.selling.SellingController = class SellingController extends erpnext.Tran serial_no: item.serial_no || "", }, callback:function(r){ - if (in_list(['Delivery Note', 'Sales Invoice'], doc.doctype)) { + if (['Delivery Note', 'Sales Invoice'].includes(doc.doctype)) { if (doc.doctype === 'Sales Invoice' && (!doc.update_stock)) return; if (has_batch_no) { me.set_batch_number(cdt, cdn); @@ -332,7 +332,7 @@ erpnext.selling.SellingController = class SellingController extends erpnext.Tran if ((doc.packed_items || []).length) { $(cur_frm.fields_dict.packing_list.row.wrapper).toggle(true); - if (in_list(['Delivery Note', 'Sales Invoice'], doc.doctype)) { + if (['Delivery Note', 'Sales Invoice'].includes(doc.doctype)) { var help_msg = "
" + __("For 'Product Bundle' items, Warehouse, Serial No and Batch No will be considered from the 'Packing List' table. If Warehouse and Batch No are same for all packing items for any 'Product Bundle' item, those values can be entered in the main Item table, values will be copied to 'Packing List' table.")+ "
"; @@ -340,7 +340,7 @@ erpnext.selling.SellingController = class SellingController extends erpnext.Tran } } else { $(cur_frm.fields_dict.packing_list.row.wrapper).toggle(false); - if (in_list(['Delivery Note', 'Sales Invoice'], doc.doctype)) { + if (['Delivery Note', 'Sales Invoice'].includes(doc.doctype)) { frappe.meta.get_docfield(doc.doctype, 'product_bundle_help', doc.name).options = ''; } } @@ -367,7 +367,7 @@ erpnext.selling.SellingController = class SellingController extends erpnext.Tran conversion_factor(doc, cdt, cdn, dont_fetch_price_list_rate) { super.conversion_factor(doc, cdt, cdn, dont_fetch_price_list_rate); if(frappe.meta.get_docfield(cdt, "stock_qty", cdn) && - in_list(['Delivery Note', 'Sales Invoice'], doc.doctype)) { + ['Delivery Note', 'Sales Invoice'].includes(doc.doctype)) { if (doc.doctype === 'Sales Invoice' && (!doc.update_stock)) return; this.set_batch_number(cdt, cdn); } @@ -376,7 +376,7 @@ erpnext.selling.SellingController = class SellingController extends erpnext.Tran qty(doc, cdt, cdn) { super.qty(doc, cdt, cdn); - if(in_list(['Delivery Note', 'Sales Invoice'], doc.doctype)) { + if(['Delivery Note', 'Sales Invoice'].includes(doc.doctype)) { if (doc.doctype === 'Sales Invoice' && (!doc.update_stock)) return; this.set_batch_number(cdt, cdn); } @@ -440,7 +440,7 @@ erpnext.selling.SellingController = class SellingController extends erpnext.Tran }; frappe.ui.form.on(cur_frm.doctype,"project", function(frm) { - if(in_list(["Delivery Note", "Sales Invoice"], frm.doc.doctype)) { + if(["Delivery Note", "Sales Invoice"].includes(frm.doc.doctype)) { if(frm.doc.project) { frappe.call({ method:'erpnext.projects.doctype.project.project.get_cost_center_name' , diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 349c328c06c..70a0872a2c4 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -712,7 +712,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2023-10-23 10:19:24.322898", + "modified": "2024-05-27 17:32:49.057386", "modified_by": "Administrator", "module": "Setup", "name": "Company", @@ -768,6 +768,10 @@ "role": "Accounts Manager", "share": 1, "write": 1 + }, + { + "role": "Auditor", + "select": 1 } ], "show_name_in_global_search": 1, diff --git a/erpnext/setup/doctype/department/department.json b/erpnext/setup/doctype/department/department.json index 99deca5c19d..fa6b9ad4a55 100644 --- a/erpnext/setup/doctype/department/department.json +++ b/erpnext/setup/doctype/department/department.json @@ -90,7 +90,7 @@ "idx": 1, "is_tree": 1, "links": [], - "modified": "2023-08-28 17:26:46.826501", + "modified": "2024-06-12 16:10:31.451257", "modified_by": "Administrator", "module": "Setup", "name": "Department", @@ -132,6 +132,10 @@ "role": "HR Manager", "share": 1, "write": 1 + }, + { + "role": "Employee", + "select": 1 } ], "show_name_in_global_search": 1, diff --git a/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.js b/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.js index 0f0221fa562..aec752aec77 100644 --- a/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.js +++ b/erpnext/stock/doctype/closing_stock_balance/closing_stock_balance.js @@ -8,7 +8,7 @@ frappe.ui.form.on("Closing Stock Balance", { }, generate_closing_balance(frm) { - if (in_list(["Queued", "Failed"], frm.doc.status)) { + if (["Queued", "Failed"].includes(frm.doc.status)) { frm.add_custom_button(__("Generate Closing Stock Balance"), () => { frm.call({ method: "enqueue_job", diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip_list.js b/erpnext/stock/doctype/delivery_trip/delivery_trip_list.js index 230107caadb..65a1be33224 100644 --- a/erpnext/stock/doctype/delivery_trip/delivery_trip_list.js +++ b/erpnext/stock/doctype/delivery_trip/delivery_trip_list.js @@ -1,9 +1,9 @@ frappe.listview_settings["Delivery Trip"] = { add_fields: ["status"], get_indicator: function (doc) { - if (in_list(["Cancelled", "Draft"], doc.status)) { + if (["Cancelled", "Draft"].includes(doc.status)) { return [__(doc.status), "red", "status,=," + doc.status]; - } else if (in_list(["In Transit", "Scheduled"], doc.status)) { + } else if (["In Transit", "Scheduled"].includes(doc.status)) { return [__(doc.status), "orange", "status,=," + doc.status]; } else if (doc.status === "Completed") { return [__(doc.status), "green", "status,=," + doc.status]; diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py index f6b7e0d8435..81dea899f39 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py @@ -224,7 +224,10 @@ class LandedCostVoucher(Document): # update stock & gl entries for submit state of PR doc.docstatus = 1 doc.update_stock_ledger(allow_negative_stock=True, via_landed_cost_voucher=True) - doc.make_gl_entries() + if d.receipt_document_type == "Purchase Receipt": + doc.make_gl_entries(via_landed_cost_voucher=True) + else: + doc.make_gl_entries() doc.repost_future_sle_and_gle() def validate_asset_qty_and_status(self, receipt_document_type, receipt_document): diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index a6fd929ea34..7bbe7b9ff75 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -307,13 +307,13 @@ class PurchaseReceipt(BuyingController): self.delete_auto_created_batches() self.set_consumed_qty_in_subcontract_order() - def get_gl_entries(self, warehouse_account=None): + def get_gl_entries(self, warehouse_account=None, via_landed_cost_voucher=False): from erpnext.accounts.general_ledger import process_gl_map gl_entries = [] self.make_item_gl_entries(gl_entries, warehouse_account=warehouse_account) - self.make_tax_gl_entries(gl_entries) + self.make_tax_gl_entries(gl_entries, via_landed_cost_voucher) update_regional_gl_entries(gl_entries, self) return process_gl_map(gl_entries) @@ -661,7 +661,7 @@ class PurchaseReceipt(BuyingController): posting_date=posting_date, ) - def make_tax_gl_entries(self, gl_entries): + def make_tax_gl_entries(self, gl_entries, via_landed_cost_voucher=False): negative_expense_to_be_booked = sum([flt(d.item_tax_amount) for d in self.get("items")]) is_asset_pr = any(d.is_fixed_asset for d in self.get("items")) # Cost center-wise amount breakup for other charges included for valuation @@ -696,18 +696,17 @@ class PurchaseReceipt(BuyingController): i = 1 for tax in self.get("taxes"): if valuation_tax.get(tax.name): - negative_expense_booked_in_pi = frappe.db.sql( - """select name from `tabPurchase Invoice Item` pi - where docstatus = 1 and purchase_receipt=%s - and exists(select name from `tabGL Entry` where voucher_type='Purchase Invoice' - and voucher_no=pi.parent and account=%s)""", - (self.name, tax.account_head), - ) - - if negative_expense_booked_in_pi: - account = stock_rbnb - else: + if via_landed_cost_voucher: account = tax.account_head + else: + negative_expense_booked_in_pi = frappe.db.sql( + """select name from `tabPurchase Invoice Item` pi + where docstatus = 1 and purchase_receipt=%s + and exists(select name from `tabGL Entry` where voucher_type='Purchase Invoice' + and voucher_no=pi.parent and account=%s)""", + (self.name, tax.account_head), + ) + account = stock_rbnb if negative_expense_booked_in_pi else tax.account_head if i == len(valuation_tax): applicable_amount = amount_including_divisional_loss diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index b185fd471f9..52dd85767f8 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -9,6 +9,7 @@ from pypika import functions as fn import erpnext from erpnext.accounts.doctype.account.test_account import get_inventory_account from erpnext.controllers.buying_controller import QtyMismatchError +from erpnext.stock import get_warehouse_account_map from erpnext.stock.doctype.item.test_item import create_item, make_item from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos @@ -1640,7 +1641,6 @@ class TestPurchaseReceipt(FrappeTestCase): frappe.db.set_single_value("Stock Settings", "over_delivery_receipt_allowance", 0) def test_internal_pr_gl_entries(self): - from erpnext.stock import get_warehouse_account_map from erpnext.stock.doctype.delivery_note.delivery_note import make_inter_company_purchase_receipt from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry @@ -2301,6 +2301,54 @@ class TestPurchaseReceipt(FrappeTestCase): for index, d in enumerate(data): self.assertEqual(d.qty_after_transaction, 11 + index) + def test_valuation_taxes_lcv_repost_after_billing(self): + from erpnext.stock.doctype.landed_cost_voucher.test_landed_cost_voucher import ( + make_landed_cost_voucher, + ) + + old_perpetual_inventory = erpnext.is_perpetual_inventory_enabled("_Test Company") + frappe.local.enable_perpetual_inventory["_Test Company"] = 1 + frappe.db.set_value( + "Company", + "_Test Company", + "stock_received_but_not_billed", + "Stock Received But Not Billed - _TC", + ) + + pr = make_purchase_receipt(qty=10, rate=1000, do_not_submit=1) + pr.append( + "taxes", + { + "category": "Valuation and Total", + "charge_type": "Actual", + "account_head": "Freight and Forwarding Charges - _TC", + "tax_amount": 2000, + "description": "Test", + }, + ) + pr.submit() + pi = make_purchase_invoice(pr.name) + pi.submit() + make_landed_cost_voucher( + company=pr.company, + receipt_document_type="Purchase Receipt", + receipt_document=pr.name, + charges=2000, + distribute_charges_based_on="Qty", + expense_account="Expenses Included In Valuation - _TC", + ) + + gl_entries = get_gl_entries("Purchase Receipt", pr.name, skip_cancelled=True, as_dict=False) + warehouse_account = get_warehouse_account_map("_Test Company") + expected_gle = ( + ("Stock Received But Not Billed - _TC", 0, 10000, "Main - _TC"), + ("Freight and Forwarding Charges - _TC", 0, 2000, "Main - _TC"), + ("Expenses Included In Valuation - _TC", 0, 2000, "Main - _TC"), + (warehouse_account[pr.items[0].warehouse]["account"], 14000, 0, "Main - _TC"), + ) + self.assertSequenceEqual(expected_gle, gl_entries) + frappe.local.enable_perpetual_inventory["_Test Company"] = old_perpetual_inventory + def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier @@ -2347,14 +2395,24 @@ def get_sl_entries(voucher_type, voucher_no): ) -def get_gl_entries(voucher_type, voucher_no): - return frappe.db.sql( - """select account, debit, credit, cost_center, is_cancelled - from `tabGL Entry` where voucher_type=%s and voucher_no=%s - order by account desc""", - (voucher_type, voucher_no), - as_dict=1, +def get_gl_entries(voucher_type, voucher_no, skip_cancelled=False, as_dict=True): + gl = frappe.qb.DocType("GL Entry") + gl_query = ( + frappe.qb.from_(gl) + .select( + gl.account, + gl.debit, + gl.credit, + gl.cost_center, + ) + .where((gl.voucher_type == voucher_type) & (gl.voucher_no == voucher_no)) + .orderby(gl.account, order=frappe.qb.desc) ) + if skip_cancelled: + gl_query = gl_query.where(gl.is_cancelled == 0) + else: + gl_query = gl_query.select(gl.is_cancelled) + return gl_query.run(as_dict=as_dict) def get_taxes(**args): diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py index e47c1f8aba7..60a7707228d 100644 --- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py +++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py @@ -45,7 +45,7 @@ class RepostItemValuation(Document): def validate_period_closing_voucher(self): # Period Closing Voucher - year_end_date = self.get_max_year_end_date(self.company) + year_end_date = self.get_max_period_closing_date(self.company) if year_end_date and getdate(self.posting_date) <= getdate(year_end_date): date = frappe.format(year_end_date, "Date") msg = f"Due to period closing, you cannot repost item valuation before {date}" @@ -88,24 +88,16 @@ class RepostItemValuation(Document): return frappe.get_all("Closing Stock Balance", fields=["name", "to_date"], filters=filters) @staticmethod - def get_max_year_end_date(company): - data = frappe.get_all( - "Period Closing Voucher", fields=["fiscal_year"], filters={"docstatus": 1, "company": company} - ) - - if not data: - return - - fiscal_years = [d.fiscal_year for d in data] - table = frappe.qb.DocType("Fiscal Year") + def get_max_period_closing_date(company): + table = frappe.qb.DocType("Period Closing Voucher") query = ( frappe.qb.from_(table) - .select(Max(table.year_end_date)) - .where((table.name.isin(fiscal_years)) & (table.disabled == 0)) + .select(Max(table.posting_date)) + .where((table.company == company) & (table.docstatus == 1)) ).run() - return query[0][0] if query else None + return query[0][0] if query and query[0][0] else None def validate_accounts_freeze(self): acc_settings = frappe.db.get_value( diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index b6459ba1e28..d58361b2d3b 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -77,7 +77,7 @@ frappe.ui.form.on('Stock Entry', { if(!item.item_code) { frappe.throw(__("Please enter Item Code to get Batch Number")); } else { - if (in_list(["Material Transfer for Manufacture", "Manufacture", "Repack", "Send to Subcontractor"], doc.purpose)) { + if (["Material Transfer for Manufacture", "Manufacture", "Repack", "Send to Subcontractor"].includes(doc.purpose)) { var filters = { 'item_code': item.item_code, 'posting_date': frm.doc.posting_date || frappe.datetime.nowdate() diff --git a/erpnext/templates/form_grid/item_grid.html b/erpnext/templates/form_grid/item_grid.html index c596890aa32..449edd14e15 100644 --- a/erpnext/templates/form_grid/item_grid.html +++ b/erpnext/templates/form_grid/item_grid.html @@ -18,7 +18,7 @@ actual_qty = (frm.doc.doctype==="Sales Order" ? doc.projected_qty : doc.actual_qty); if(flt(frm.doc.per_delivered) < 100 - && in_list(["Sales Order Item", "Delivery Note Item"], doc.doctype)) { + && ["Sales Order Item", "Delivery Note Item"].includes(doc.doctype)) { if(actual_qty != undefined) { if(actual_qty >= doc.qty) { var color = "green";