From b593f57637a30d19c812a6f86db2797f37ac3417 Mon Sep 17 00:00:00 2001 From: Frappe PR Bot Date: Wed, 26 Jun 2024 18:02:23 +0530 Subject: [PATCH] chore: release v14 (#42023) * fix(Sales Order): only show permitted actions (cherry picked from commit c29d95537185d909612103b65573242a91ef0d70) # Conflicts: # erpnext/selling/doctype/sales_order/sales_order.js * fix(Delivery Note): only show permitted actions (cherry picked from commit 418bdc1dcc0c8c8eaaa6555b3689436515270c7c) # Conflicts: # erpnext/stock/doctype/delivery_note/delivery_note.js * fix: consistent query field name in item wise purchase register with item wise sales register * chore: resolve conflicts * refactor: remove use of can_create for Payment Request (#41647) (cherry picked from commit 47bc5691a1579b88e0a430f6fbe5ff6309486ccf) * fix: incorrect against_account upon reposting (cherry picked from commit 20c4098399d5cb276d373074036a738e6cee67b0) * fix: asset value correction in fixed asset register (#41943) * fix: timeout while cancelling LCV (#42030) fix: timeout while canelling LCV --------- Co-authored-by: barredterra <14891507+barredterra@users.noreply.github.com> Co-authored-by: ljain112 Co-authored-by: rohitwaghchaure Co-authored-by: ruthra kumar Co-authored-by: Khushi Rawat <142375893+khushi8112@users.noreply.github.com> --- .../purchase_invoice/purchase_invoice.py | 6 +- .../repost_accounting_ledger.py | 4 + .../doctype/sales_invoice/sales_invoice.py | 4 + .../item_wise_purchase_register.py | 2 +- .../fixed_asset_register.py | 9 +- erpnext/public/js/controllers/transaction.js | 6 +- .../doctype/sales_order/sales_order.js | 132 ++++++++++++++---- .../doctype/delivery_note/delivery_note.js | 123 ++++++++++------ erpnext/stock/stock_ledger.py | 1 + 9 files changed, 214 insertions(+), 73 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index e0debba6fd5..2015dd1b634 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -413,7 +413,7 @@ class PurchaseInvoice(BuyingController): for item in self.get("items"): validate_account_head(item.idx, item.expense_account, self.company, "Expense") - def set_against_expense_account(self): + def set_against_expense_account(self, force=False): against_accounts = [] for item in self.get("items"): if item.expense_account and (item.expense_account not in against_accounts): @@ -421,6 +421,10 @@ class PurchaseInvoice(BuyingController): self.against_expense_account = ",".join(against_accounts) + def force_set_against_expense_account(self): + self.set_against_expense_account() + frappe.db.set_value(self.doctype, self.name, "against_expense_account", self.against_expense_account) + def po_required(self): if frappe.db.get_value("Buying Settings", None, "po_required") == "Yes": if frappe.get_value( diff --git a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py index 15478ab8633..550e80ec8bc 100644 --- a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py +++ b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py @@ -149,6 +149,10 @@ def start_repost(account_repost_doc=str) -> None: doc.make_gl_entries_on_cancel() doc.docstatus = 1 + if doc.doctype == "Sales Invoice": + doc.force_set_against_income_account() + else: + doc.force_set_against_expense_account() doc.make_gl_entries() elif doc.doctype in ["Payment Entry", "Journal Entry", "Expense Claim"]: diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 3ea4b91641c..009c48a385c 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -768,6 +768,10 @@ class SalesInvoice(SellingController): against_acc.append(d.income_account) self.against_income_account = ",".join(against_acc) + def force_set_against_income_account(self): + self.set_against_income_account() + frappe.db.set_value(self.doctype, self.name, "against_income_account", self.against_income_account) + def add_remarks(self): if not self.remarks: if self.po_no and self.po_date: diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py index 80c246cad55..06afb4c0e8f 100644 --- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py +++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py @@ -322,7 +322,7 @@ def get_items(filters, additional_table_columns): .left_join(Item) .on(pii.item_code == Item.name) .select( - pii.name.as_("pii_name"), + pii.name, pii.parent, pi.posting_date, pi.credit_to, diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py index 4ff04c80434..61e0828aae4 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -125,9 +125,10 @@ def get_data(filters): if assets_linked_to_fb and asset.calculate_depreciation and asset.asset_id not in assets_linked_to_fb: continue - asset_value = get_asset_value_after_depreciation( - asset.asset_id, finance_book - ) or get_asset_value_after_depreciation(asset.asset_id) + depreciation_amount = depreciation_amount_map.get(asset.asset_id) or 0.0 + asset_value = ( + asset.gross_purchase_amount - asset.opening_accumulated_depreciation - depreciation_amount + ) row = { "asset_id": asset.asset_id, @@ -139,7 +140,7 @@ def get_data(filters): or pi_supplier_map.get(asset.purchase_invoice), "gross_purchase_amount": asset.gross_purchase_amount, "opening_accumulated_depreciation": asset.opening_accumulated_depreciation, - "depreciated_amount": depreciation_amount_map.get(asset.asset_id) or 0.0, + "depreciated_amount": depreciation_amount, "available_for_use_date": asset.available_for_use_date, "location": asset.location, "asset_category": asset.asset_category, diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index e3e25499b84..e3dae3e6f97 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -265,7 +265,11 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe } const me = this; - if (!this.frm.is_new() && this.frm.doc.docstatus === 0) { + if ( + !this.frm.is_new() + && this.frm.doc.docstatus === 0 + && frappe.model.can_create("Quality Inspection") + ) { this.frm.add_custom_button(__("Quality Inspection(s)"), () => { me.make_quality_inspection(); }, __("Create")); diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index db3d6c2ca54..1a6658f5418 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -48,8 +48,13 @@ frappe.ui.form.on("Sales Order", { frm.set_df_property('packed_items', 'cannot_delete_rows', true); }, refresh: function(frm) { - if(frm.doc.docstatus === 1 && frm.doc.status !== 'Closed' - && flt(frm.doc.per_delivered, 6) < 100 && flt(frm.doc.per_billed, 6) < 100) { + if( + frm.doc.docstatus === 1 + && frm.doc.status !== "Closed" + && flt(frm.doc.per_delivered, 6) < 100 + && flt(frm.doc.per_billed, 6) < 100 + && frm.has_perm("write") + ) { frm.add_custom_button(__('Update Items'), () => { erpnext.utils.update_child_items({ frm: frm, @@ -66,6 +71,10 @@ frappe.ui.form.on("Sales Order", { }, get_items_from_internal_purchase_order(frm) { + if (!frappe.model.can_read("Purchase Order")) { + return; + } + frm.add_custom_button(__('Purchase Order'), () => { erpnext.utils.map_current_doc({ method: 'erpnext.buying.doctype.purchase_order.purchase_order.make_inter_company_sales_order', @@ -227,7 +236,11 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex } } - if (flt(doc.per_picked, 6) < 100 && flt(doc.per_delivered, 6) < 100) { + if ( + flt(doc.per_picked, 6) < 100 + && flt(doc.per_delivered, 6) < 100 + && frappe.model.can_create("Pick List") + ) { this.frm.add_custom_button(__('Pick List'), () => this.create_pick_list(), __('Create')); } @@ -237,45 +250,105 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex const order_is_a_custom_sale = ["Sales", "Shopping Cart", "Maintenance"].indexOf(doc.order_type) === -1; // delivery note - if(flt(doc.per_delivered, 6) < 100 && (order_is_a_sale || order_is_a_custom_sale) && allow_delivery) { - this.frm.add_custom_button(__('Delivery Note'), () => this.make_delivery_note_based_on_delivery_date(), __('Create')); - this.frm.add_custom_button(__('Work Order'), () => this.make_work_order(), __('Create')); + if( + flt(doc.per_delivered, 6) < 100 + && (order_is_a_sale || order_is_a_custom_sale) + && allow_delivery + ) { + if (frappe.model.can_create("Delivery Note")) { + this.frm.add_custom_button( + __("Delivery Note"), + () => this.make_delivery_note_based_on_delivery_date(), + __("Create") + ); + } + if (frappe.model.can_create("Work Order")) { + this.frm.add_custom_button( + __("Work Order"), + () => this.make_work_order(), + __("Create") + ); + } } // sales invoice - if(flt(doc.per_billed, 6) < 100) { - this.frm.add_custom_button(__('Sales Invoice'), () => me.make_sales_invoice(), __('Create')); + if(flt(doc.per_billed, 6) < 100 && frappe.model.can_create("Sales Invoice")) { + this.frm.add_custom_button( + __("Sales Invoice"), + () => me.make_sales_invoice(), + __("Create") + ); } // material request - if(!doc.order_type || (order_is_a_sale || order_is_a_custom_sale) && flt(doc.per_delivered, 6) < 100) { - this.frm.add_custom_button(__('Material Request'), () => this.make_material_request(), __('Create')); - this.frm.add_custom_button(__('Request for Raw Materials'), () => this.make_raw_material_request(), __('Create')); + if ( + ( + !doc.order_type + || (order_is_a_sale || order_is_a_custom_sale) + && flt(doc.per_delivered, 6) < 100 + ) + && frappe.model.can_create("Material Request") + ) { + this.frm.add_custom_button( + __('Material Request'), + () => this.make_material_request(), + __('Create') + ); + this.frm.add_custom_button( + __('Request for Raw Materials'), + () => this.make_raw_material_request(), + __('Create') + ); } // Make Purchase Order - if (!this.frm.doc.is_internal_customer) { - this.frm.add_custom_button(__('Purchase Order'), () => this.make_purchase_order(), __('Create')); + if (!this.frm.doc.is_internal_customer && frappe.model.can_create("Purchase Order")) { + this.frm.add_custom_button( + __('Purchase Order'), + () => this.make_purchase_order(), + __('Create') + ); } // maintenance if(flt(doc.per_delivered, 2) < 100 && (order_is_maintenance || order_is_a_custom_sale)) { - this.frm.add_custom_button(__('Maintenance Visit'), () => this.make_maintenance_visit(), __('Create')); - this.frm.add_custom_button(__('Maintenance Schedule'), () => this.make_maintenance_schedule(), __('Create')); + if(frappe.model.can_create("Maintenance Visit")) { + this.frm.add_custom_button( + __('Maintenance Visit'), + () => this.make_maintenance_visit(), + __('Create') + ); + } + + if(frappe.model.can_create("Maintenance Schedule")) { + this.frm.add_custom_button( + __('Maintenance Schedule'), + () => this.make_maintenance_schedule(), + __('Create') + ); + } } // project - if(flt(doc.per_delivered, 2) < 100) { + if(flt(doc.per_delivered, 2) < 100 && frappe.model.can_create("Project")) { this.frm.add_custom_button(__('Project'), () => this.make_project(), __('Create')); } - if(!doc.auto_repeat) { - this.frm.add_custom_button(__('Subscription'), function() { - erpnext.utils.make_subscription(doc.doctype, doc.name) - }, __('Create')) + if(!doc.auto_repeat && frappe.model.can_create("Auto Repeat")) { + this.frm.add_custom_button( + __('Subscription'), + function() { + erpnext.utils.make_subscription(doc.doctype, doc.name) + }, + __('Create') + ); } - if (doc.docstatus === 1 && !doc.inter_company_order_reference) { + if ( + doc.docstatus === 1 && + !doc.inter_company_order_reference && + frappe.model.can_create("Purchase Order") + ) { let me = this; let internal = me.frm.doc.is_internal_customer; if (internal) { @@ -290,14 +363,25 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex } // payment request if(flt(doc.per_billed, precision('per_billed', doc)) < 100 + frappe.boot.sysdefaults.over_billing_allowance) { - this.frm.add_custom_button(__('Payment Request'), () => this.make_payment_request(), __('Create')); - this.frm.add_custom_button(__('Payment'), () => this.make_payment_entry(), __('Create')); + this.frm.add_custom_button( + __("Payment Request"), + () => this.make_payment_request(), + __("Create") + ); + + if (frappe.model.can_create("Payment Entry")) { + this.frm.add_custom_button( + __("Payment"), + () => this.make_payment_entry(), + __("Create") + ); + } } this.frm.page.set_inner_btn_group_as_primary(__('Create')); } } - if (this.frm.doc.docstatus===0) { + if (this.frm.doc.docstatus===0 && frappe.model.can_read("Quotation")) { this.frm.add_custom_button(__('Quotation'), function() { let d = erpnext.utils.map_current_doc({ diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js index 77545e0e1ad..22f9728cce0 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.js +++ b/erpnext/stock/doctype/delivery_note/delivery_note.js @@ -86,7 +86,7 @@ frappe.ui.form.on("Delivery Note", { }, refresh: function(frm) { - if (frm.doc.docstatus === 1 && frm.doc.is_return === 1 && frm.doc.per_billed !== 100) { + if (frm.doc.docstatus === 1 && frm.doc.is_return === 1 && frm.doc.per_billed !== 100 && frappe.model.can_create("Sales Invoice")) { frm.add_custom_button(__('Credit Note'), function() { frappe.model.open_mapped_doc({ method: "erpnext.stock.doctype.delivery_note.delivery_note.make_sales_invoice", @@ -96,7 +96,11 @@ frappe.ui.form.on("Delivery Note", { frm.page.set_inner_btn_group_as_primary(__('Create')); } - if (frm.doc.docstatus == 1 && !frm.doc.inter_company_reference) { + if ( + frm.doc.docstatus == 1 && + !frm.doc.inter_company_reference && + frappe.model.can_create("Purchase Receipt") + ) { let internal = frm.doc.is_internal_customer; if (internal) { let button_label = (frm.doc.company === frm.doc.represents_company) ? "Internal Purchase Receipt" : @@ -135,64 +139,88 @@ erpnext.stock.DeliveryNoteController = class DeliveryNoteController extends erpn refresh(doc, dt, dn) { var me = this; super.refresh(); - if ((!doc.is_return) && (doc.status!="Closed" || this.frm.is_new())) { - if (this.frm.doc.docstatus===0) { - this.frm.add_custom_button(__('Sales Order'), - function() { - if (!me.frm.doc.customer) { - frappe.throw({ - title: __("Mandatory"), - message: __("Please Select a Customer") - }); + if ( + !doc.is_return + && (doc.status!="Closed" || this.frm.is_new()) + && this.frm.has_perm("write") + && frappe.model.can_read("Sales Order") + && this.frm.doc.docstatus===0 + ) { + this.frm.add_custom_button( + __('Sales Order'), + function() { + if (!me.frm.doc.customer) { + frappe.throw({ + title: __("Mandatory"), + message: __("Please Select a Customer") + }); + } + erpnext.utils.map_current_doc({ + method: "erpnext.selling.doctype.sales_order.sales_order.make_delivery_note", + source_doctype: "Sales Order", + target: me.frm, + setters: { + customer: me.frm.doc.customer, + }, + get_query_filters: { + docstatus: 1, + status: ["not in", ["Closed", "On Hold"]], + per_delivered: ["<", 99.99], + company: me.frm.doc.company, + project: me.frm.doc.project || undefined, } - erpnext.utils.map_current_doc({ - method: "erpnext.selling.doctype.sales_order.sales_order.make_delivery_note", - source_doctype: "Sales Order", - target: me.frm, - setters: { - customer: me.frm.doc.customer, - }, - get_query_filters: { - docstatus: 1, - status: ["not in", ["Closed", "On Hold"]], - per_delivered: ["<", 99.99], - company: me.frm.doc.company, - project: me.frm.doc.project || undefined, - } - }) - }, __("Get Items From")); - } + }) + }, + __("Get Items From") + ); } if (!doc.is_return && doc.status!="Closed") { - if(doc.docstatus == 1) { + if (doc.docstatus == 1 && frappe.model.can_create("Shipment")) { this.frm.add_custom_button(__('Shipment'), function() { me.make_shipment() }, __('Create')); } - if(flt(doc.per_installed, 2) < 100 && doc.docstatus==1) - this.frm.add_custom_button(__('Installation Note'), function() { - me.make_installation_note() }, __('Create')); + if ( + flt(doc.per_installed, 2) < 100 + && doc.docstatus==1 + && frappe.model.can_create("Installation Note") + ) { + this.frm.add_custom_button( + __('Installation Note'), + function() { + me.make_installation_note() + }, + __('Create') + ); + } - if (doc.docstatus==1) { + if (doc.docstatus==1 && this.frm.has_perm("create")) { this.frm.add_custom_button(__('Sales Return'), function() { me.make_sales_return() }, __('Create')); } - if (doc.docstatus==1) { + if (doc.docstatus==1 && frappe.model.can_create("Delivery Trip")) { this.frm.add_custom_button(__('Delivery Trip'), function() { me.make_delivery_trip() }, __('Create')); } - if(doc.docstatus==0 && !doc.__islocal) { - if (doc.__onload && doc.__onload.has_unpacked_items) { - this.frm.add_custom_button(__('Packing Slip'), function() { + if ( + doc.docstatus==0 + && !doc.__islocal + && frappe.model.can_create("Packing Slip") + && (doc.__onload && doc.__onload.has_unpacked_items) + ) { + this.frm.add_custom_button( + __('Packing Slip'), + function() { frappe.model.open_mapped_doc({ method: "erpnext.stock.doctype.delivery_note.delivery_note.make_packing_slip", frm: me.frm - }) }, __('Create') - ); - } + }); + }, + __('Create') + ); } if (!doc.__islocal && doc.docstatus==1) { @@ -211,7 +239,13 @@ erpnext.stock.DeliveryNoteController = class DeliveryNoteController extends erpn } } - if(doc.docstatus==1 && !doc.is_return && doc.status!="Closed" && flt(doc.per_billed) < 100) { + if( + doc.docstatus==1 + && !doc.is_return + && doc.status!="Closed" + && flt(doc.per_billed) < 100 + && frappe.model.can_create("Sales Invoice") + ) { // show Make Invoice button only if Delivery Note is not created from Sales Invoice var from_sales_invoice = false; from_sales_invoice = me.frm.doc.items.some(function(item) { @@ -230,7 +264,12 @@ erpnext.stock.DeliveryNoteController = class DeliveryNoteController extends erpn } erpnext.stock.delivery_note.set_print_hide(doc, dt, dn); - if(doc.docstatus==1 && !doc.is_return && !doc.auto_repeat) { + if( + doc.docstatus==1 + && !doc.is_return + && !doc.auto_repeat + && frappe.model.can_create("Auto Repeat") + ) { cur_frm.add_custom_button(__('Subscription'), function() { erpnext.utils.make_subscription(doc.doctype, doc.name) }, __('Create')) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 0cd2ecdaaf5..71f4ca00707 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1529,6 +1529,7 @@ def get_next_stock_reco(kwargs): sle.batch_no, sle.actual_qty, ) + .force_index("item_warehouse") .where( (sle.item_code == kwargs.get("item_code")) & (sle.warehouse == kwargs.get("warehouse"))