From 12b459df8c860f0d8df654ba2339dba9265480f9 Mon Sep 17 00:00:00 2001 From: Dany Robert Date: Mon, 10 Jul 2023 10:35:45 +0000 Subject: [PATCH 01/22] fix: skip twc if not account set --- .../tax_withholding_category.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index 58792d1d8ad..3ff0a40a3e7 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -100,11 +100,14 @@ def get_party_tax_withholding_details(inv, tax_withholding_category=None): tax_details = get_tax_withholding_details(tax_withholding_category, posting_date, inv.company) if not tax_details: - frappe.throw( - _("Please set associated account in Tax Withholding Category {0} against Company {1}").format( - tax_withholding_category, inv.company - ) + frappe.msgprint( + _( + "Skipping Tax Withholding Category {0} as there is no associated account set for Company {1} in it." + ).format(tax_withholding_category, inv.company) ) + if inv.doctype == "Purchase Invoice": + return {}, [], {} + return {} if party_type == "Customer" and not tax_details.cumulative_threshold: # TCS is only chargeable on sum of invoiced value From 17771a55fb7dee46aa7c3428a8d4951468de9a2f Mon Sep 17 00:00:00 2001 From: Ritvik Sardana Date: Tue, 1 Aug 2023 19:28:40 +0530 Subject: [PATCH 02/22] fix: added code for batched items in POS --- .../doctype/pos_invoice/pos_invoice.py | 9 ++-- .../pos_invoice_merge_log.py | 48 ++++++++++++++----- 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index 4b2fcec7579..0c481fa71df 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -276,10 +276,10 @@ class POSInvoice(SalesInvoice): if self.is_return and entry.amount > 0: frappe.throw(_("Row #{0} (Payment Table): Amount must be negative").format(entry.idx)) - if self.is_return: - invoice_total = self.rounded_total or self.grand_total - if total_amount_in_payments and total_amount_in_payments < invoice_total: - frappe.throw(_("Total payments amount can't be greater than {}").format(-invoice_total)) + # if self.is_return: + # invoice_total = self.rounded_total or self.grand_total + # if total_amount_in_payments and total_amount_in_payments < invoice_total: + # frappe.throw(_("Total payments amount can't be greater than {}").format(-invoice_total)) def validate_loyalty_transaction(self): if self.redeem_loyalty_points and ( @@ -595,7 +595,6 @@ def get_pos_reserved_qty(item_code, warehouse): .where( (p_inv.name == p_item.parent) & (IfNull(p_inv.consolidated_invoice, "") == "") - & (p_inv.is_return == 0) & (p_item.docstatus == 1) & (p_item.item_code == item_code) & (p_item.warehouse == warehouse) diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py index d8cbcc141bd..da69e1b12bc 100644 --- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py +++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py @@ -83,20 +83,30 @@ class POSInvoiceMergeLog(Document): pos_invoice_docs = [ frappe.get_cached_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices ] + batched_invoices = self.get_batched_invoices(pos_invoice_docs) - returns = [d for d in pos_invoice_docs if d.get("is_return") == 1] - sales = [d for d in pos_invoice_docs if d.get("is_return") == 0] + for invoice in batched_invoices: + sales_invoice, credit_note = "", "" + if not invoice[0].get("is_return"): + sales_invoice = self.process_merging_into_sales_invoice(invoice) + else: + credit_note = self.process_merging_into_credit_note(invoice) - sales_invoice, credit_note = "", "" - if returns: - credit_note = self.process_merging_into_credit_note(returns) + self.save() # save consolidated_sales_invoice & consolidated_credit_note ref in merge log + self.update_pos_invoices(pos_invoice_docs, sales_invoice, credit_note) - if sales: - sales_invoice = self.process_merging_into_sales_invoice(sales) + # returns = [d for d in pos_invoice_docs if d.get("is_return") == 1] + # sales = [d for d in pos_invoice_docs if d.get("is_return") == 0] - self.save() # save consolidated_sales_invoice & consolidated_credit_note ref in merge log + # sales_invoice, credit_note = "", "" + # if returns: + # credit_note = self.process_merging_into_credit_note(returns) - self.update_pos_invoices(pos_invoice_docs, sales_invoice, credit_note) + # if sales: + # sales_invoice = self.process_merging_into_sales_invoice(sales) + + # self.save() # save consolidated_sales_invoice & consolidated_credit_note ref in merge log + # self.update_pos_invoices(pos_invoice_docs, sales_invoice, credit_note) def on_cancel(self): pos_invoice_docs = [ @@ -108,7 +118,6 @@ class POSInvoiceMergeLog(Document): def process_merging_into_sales_invoice(self, data): sales_invoice = self.get_new_sales_invoice() - sales_invoice = self.merge_pos_invoice_into(sales_invoice, data) sales_invoice.is_consolidated = 1 @@ -276,6 +285,21 @@ class POSInvoiceMergeLog(Document): si.flags.ignore_validate = True si.cancel() + def get_batched_invoices(self, pos_invoice_docs): + grouped_batch = [] + current_batch = [] + for item in pos_invoice_docs: + if not current_batch: + current_batch.append(item) + elif current_batch[-1].get("is_return") != item.get("is_return"): + grouped_batch.append(current_batch) + current_batch = [item] + else: + current_batch.append(item) + + grouped_batch.append(current_batch) + return grouped_batch + def update_item_wise_tax_detail(consolidate_tax_row, tax_row): consolidated_tax_detail = json.loads(consolidate_tax_row.item_wise_tax_detail) @@ -385,13 +409,15 @@ def split_invoices(invoices): for d in invoices if d.is_return and d.return_against ] + print(pos_return_docs, invoices, _invoices, sep="-") + # breakpoint() for pos_invoice in pos_return_docs: for item in pos_invoice.items: if not item.serial_no and not item.serial_and_batch_bundle: continue return_against_is_added = any( - d for d in _invoices if d.pos_invoice == pos_invoice.return_against + d for d in invoices if d.pos_invoice == pos_invoice.return_against ) if return_against_is_added: break From 5b1aa07ecb7e36e71cd61914b392fa0dffaf76b4 Mon Sep 17 00:00:00 2001 From: Ritvik Sardana Date: Thu, 3 Aug 2023 16:10:28 +0530 Subject: [PATCH 03/22] fix: fixed SABB error --- .../pos_invoice_merge_log.py | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py index da69e1b12bc..9978912ecca 100644 --- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py +++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py @@ -171,21 +171,23 @@ class POSInvoiceMergeLog(Document): for item in doc.get("items"): found = False - for i in items: - if ( - i.item_code == item.item_code - and not i.serial_no - and not i.batch_no - and i.uom == item.uom - and i.net_rate == item.net_rate - and i.warehouse == item.warehouse - ): - found = True - i.qty = i.qty + item.qty - i.amount = i.amount + item.net_amount - i.net_amount = i.amount - i.base_amount = i.base_amount + item.base_net_amount - i.base_net_amount = i.base_amount + if not item.serial_and_batch_bundle: + for i in items: + + if ( + i.item_code == item.item_code + and not i.serial_no + and not i.batch_no + and i.uom == item.uom + and i.net_rate == item.net_rate + and i.warehouse == item.warehouse + ): + found = True + i.qty = i.qty + item.qty + i.amount = i.amount + item.net_amount + i.net_amount = i.amount + i.base_amount = i.base_amount + item.base_net_amount + i.base_net_amount = i.base_amount if not found: item.rate = item.net_rate From c9d5a623504b6f9f580f72d19d572029ba99f1e8 Mon Sep 17 00:00:00 2001 From: Ritvik Sardana Date: Fri, 4 Aug 2023 16:47:49 +0530 Subject: [PATCH 04/22] fix: POS Runtime Effect completed --- .../doctype/pos_invoice/pos_invoice.py | 1 + .../pos_invoice_merge_log.py | 75 ++++++------------- .../serial_and_batch_bundle.py | 11 ++- 3 files changed, 33 insertions(+), 54 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index 0c481fa71df..ac39877f88c 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -542,6 +542,7 @@ def get_stock_availability(item_code, warehouse): is_stock_item = True bin_qty = get_bin_qty(item_code, warehouse) pos_sales_qty = get_pos_reserved_qty(item_code, warehouse) + return bin_qty - pos_sales_qty, is_stock_item else: is_stock_item = True diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py index 9978912ecca..c8f14220c15 100644 --- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py +++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py @@ -83,30 +83,19 @@ class POSInvoiceMergeLog(Document): pos_invoice_docs = [ frappe.get_cached_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices ] - batched_invoices = self.get_batched_invoices(pos_invoice_docs) - for invoice in batched_invoices: - sales_invoice, credit_note = "", "" - if not invoice[0].get("is_return"): - sales_invoice = self.process_merging_into_sales_invoice(invoice) - else: - credit_note = self.process_merging_into_credit_note(invoice) + returns = [d for d in pos_invoice_docs if d.get("is_return") == 1] + sales = [d for d in pos_invoice_docs if d.get("is_return") == 0] - self.save() # save consolidated_sales_invoice & consolidated_credit_note ref in merge log - self.update_pos_invoices(pos_invoice_docs, sales_invoice, credit_note) + sales_invoice, credit_note = "", "" + if returns: + credit_note = self.process_merging_into_credit_note(returns) - # returns = [d for d in pos_invoice_docs if d.get("is_return") == 1] - # sales = [d for d in pos_invoice_docs if d.get("is_return") == 0] + if sales: + sales_invoice = self.process_merging_into_sales_invoice(sales) - # sales_invoice, credit_note = "", "" - # if returns: - # credit_note = self.process_merging_into_credit_note(returns) - - # if sales: - # sales_invoice = self.process_merging_into_sales_invoice(sales) - - # self.save() # save consolidated_sales_invoice & consolidated_credit_note ref in merge log - # self.update_pos_invoices(pos_invoice_docs, sales_invoice, credit_note) + self.save() # save consolidated_sales_invoice & consolidated_credit_note ref in merge log + self.update_pos_invoices(pos_invoice_docs, sales_invoice, credit_note) def on_cancel(self): pos_invoice_docs = [ @@ -171,23 +160,20 @@ class POSInvoiceMergeLog(Document): for item in doc.get("items"): found = False - if not item.serial_and_batch_bundle: - for i in items: - - if ( - i.item_code == item.item_code - and not i.serial_no - and not i.batch_no - and i.uom == item.uom - and i.net_rate == item.net_rate - and i.warehouse == item.warehouse - ): - found = True - i.qty = i.qty + item.qty - i.amount = i.amount + item.net_amount - i.net_amount = i.amount - i.base_amount = i.base_amount + item.base_net_amount - i.base_net_amount = i.base_amount + for i in items: + if ( + i.item_code == item.item_code + and not i.serial_and_batch_bundle + and i.uom == item.uom + and i.net_rate == item.net_rate + and i.warehouse == item.warehouse + ): + found = True + i.qty = i.qty + item.qty + i.amount = i.amount + item.net_amount + i.net_amount = i.amount + i.base_amount = i.base_amount + item.base_net_amount + i.base_net_amount = i.base_amount if not found: item.rate = item.net_rate @@ -287,21 +273,6 @@ class POSInvoiceMergeLog(Document): si.flags.ignore_validate = True si.cancel() - def get_batched_invoices(self, pos_invoice_docs): - grouped_batch = [] - current_batch = [] - for item in pos_invoice_docs: - if not current_batch: - current_batch.append(item) - elif current_batch[-1].get("is_return") != item.get("is_return"): - grouped_batch.append(current_batch) - current_batch = [item] - else: - current_batch.append(item) - - grouped_batch.append(current_batch) - return grouped_batch - def update_item_wise_tax_detail(consolidate_tax_row, tax_row): consolidated_tax_detail = json.loads(consolidate_tax_row.item_wise_tax_detail) diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index 43bd7ac78cb..fcf2bce4347 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -3,7 +3,7 @@ import collections import csv -from collections import defaultdict +from collections import Counter, defaultdict from typing import Dict, List import frappe @@ -1197,6 +1197,7 @@ def get_reserved_serial_nos_for_pos(kwargs): filters=[ ["POS Invoice", "consolidated_invoice", "is", "not set"], ["POS Invoice", "docstatus", "=", 1], + ["POS Invoice", "is_return", "=", 0], ["POS Invoice Item", "item_code", "=", kwargs.item_code], ["POS Invoice", "name", "!=", kwargs.ignore_voucher_no], ], @@ -1242,8 +1243,14 @@ def get_reserved_serial_nos_for_pos(kwargs): child_doc, parent_doc, ignore_voucher_detail_no=kwargs.get("ignore_voucher_detail_no") ) ) + # Counter is used to create a hashmap of serial nos, which contains count of each serial no + # ignore serial nos inlcudes serial nos which are sold and returned + # so we need to subtract returned serial nos from ignore serial nos after creating a counter of each - return list(set(ignore_serial_nos) - set(returned_serial_nos)) + ignore_serial_nos_counter = Counter(ignore_serial_nos) + returned_serial_nos_counter = Counter(returned_serial_nos) + + return list(ignore_serial_nos_counter - returned_serial_nos_counter) def get_reserved_batches_for_pos(kwargs): From 510543680b3683ced4fb609f50381a688cd9ccc4 Mon Sep 17 00:00:00 2001 From: Ritvik Sardana Date: Fri, 4 Aug 2023 16:55:30 +0530 Subject: [PATCH 05/22] fix: batched items in POS --- .../pos_invoice_merge_log.py | 35 +++++++++++++------ 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py index c8f14220c15..78a20e8cb4c 100644 --- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py +++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py @@ -83,19 +83,17 @@ class POSInvoiceMergeLog(Document): pos_invoice_docs = [ frappe.get_cached_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices ] + batched_invoices = self.get_batched_invoices(pos_invoice_docs) - returns = [d for d in pos_invoice_docs if d.get("is_return") == 1] - sales = [d for d in pos_invoice_docs if d.get("is_return") == 0] + for invoice in batched_invoices: + sales_invoice, credit_note = "", "" + if not invoice[0].get("is_return"): + sales_invoice = self.process_merging_into_sales_invoice(invoice) + else: + credit_note = self.process_merging_into_credit_note(invoice) - sales_invoice, credit_note = "", "" - if returns: - credit_note = self.process_merging_into_credit_note(returns) - - if sales: - sales_invoice = self.process_merging_into_sales_invoice(sales) - - self.save() # save consolidated_sales_invoice & consolidated_credit_note ref in merge log - self.update_pos_invoices(pos_invoice_docs, sales_invoice, credit_note) + self.save() # save consolidated_sales_invoice & consolidated_credit_note ref in merge log + self.update_pos_invoices(pos_invoice_docs, sales_invoice, credit_note) def on_cancel(self): pos_invoice_docs = [ @@ -273,6 +271,21 @@ class POSInvoiceMergeLog(Document): si.flags.ignore_validate = True si.cancel() + def get_batched_invoices(self, pos_invoice_docs): + grouped_batch = [] + current_batch = [] + for item in pos_invoice_docs: + if not current_batch: + current_batch.append(item) + elif current_batch[-1].get("is_return") != item.get("is_return"): + grouped_batch.append(current_batch) + current_batch = [item] + else: + current_batch.append(item) + + grouped_batch.append(current_batch) + return grouped_batch + def update_item_wise_tax_detail(consolidate_tax_row, tax_row): consolidated_tax_detail = json.loads(consolidate_tax_row.item_wise_tax_detail) From dbc000d6551859db9999c13ca944ae90e676d573 Mon Sep 17 00:00:00 2001 From: Ritvik Sardana Date: Sat, 5 Aug 2023 11:23:07 +0530 Subject: [PATCH 06/22] fix: batched items method giving wrong quantity, so changed it back to previous way --- .../doctype/pos_invoice/pos_invoice.py | 8 +++---- .../pos_invoice_merge_log.py | 23 ++++++++++--------- .../serial_and_batch_bundle.py | 4 +--- 3 files changed, 17 insertions(+), 18 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index ac39877f88c..89a96118ec4 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -276,10 +276,10 @@ class POSInvoice(SalesInvoice): if self.is_return and entry.amount > 0: frappe.throw(_("Row #{0} (Payment Table): Amount must be negative").format(entry.idx)) - # if self.is_return: - # invoice_total = self.rounded_total or self.grand_total - # if total_amount_in_payments and total_amount_in_payments < invoice_total: - # frappe.throw(_("Total payments amount can't be greater than {}").format(-invoice_total)) + if self.is_return: + invoice_total = self.rounded_total or self.grand_total + if total_amount_in_payments and total_amount_in_payments < invoice_total: + frappe.throw(_("Total payments amount can't be greater than {}").format(-invoice_total)) def validate_loyalty_transaction(self): if self.redeem_loyalty_points and ( diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py index 78a20e8cb4c..4f46aa153a7 100644 --- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py +++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py @@ -83,17 +83,19 @@ class POSInvoiceMergeLog(Document): pos_invoice_docs = [ frappe.get_cached_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices ] - batched_invoices = self.get_batched_invoices(pos_invoice_docs) - for invoice in batched_invoices: - sales_invoice, credit_note = "", "" - if not invoice[0].get("is_return"): - sales_invoice = self.process_merging_into_sales_invoice(invoice) - else: - credit_note = self.process_merging_into_credit_note(invoice) + returns = [d for d in pos_invoice_docs if d.get("is_return") == 1] + sales = [d for d in pos_invoice_docs if d.get("is_return") == 0] - self.save() # save consolidated_sales_invoice & consolidated_credit_note ref in merge log - self.update_pos_invoices(pos_invoice_docs, sales_invoice, credit_note) + sales_invoice, credit_note = "", "" + if returns: + credit_note = self.process_merging_into_credit_note(returns) + + if sales: + sales_invoice = self.process_merging_into_sales_invoice(sales) + + self.save() # save consolidated_sales_invoice & consolidated_credit_note ref in merge log + self.update_pos_invoices(pos_invoice_docs, sales_invoice, credit_note) def on_cancel(self): pos_invoice_docs = [ @@ -395,8 +397,7 @@ def split_invoices(invoices): for d in invoices if d.is_return and d.return_against ] - print(pos_return_docs, invoices, _invoices, sep="-") - # breakpoint() + for pos_invoice in pos_return_docs: for item in pos_invoice.items: if not item.serial_no and not item.serial_and_batch_bundle: diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index fcf2bce4347..1f90c5bf7a5 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -1215,7 +1215,6 @@ def get_reserved_serial_nos_for_pos(kwargs): for d in get_serial_batch_ledgers(kwargs.item_code, docstatus=1, name=ids): ignore_serial_nos.append(d.serial_no) - # Will be deprecated in v16 returned_serial_nos = [] for pos_invoice in pos_invoices: if pos_invoice.serial_no: @@ -1244,8 +1243,7 @@ def get_reserved_serial_nos_for_pos(kwargs): ) ) # Counter is used to create a hashmap of serial nos, which contains count of each serial no - # ignore serial nos inlcudes serial nos which are sold and returned - # so we need to subtract returned serial nos from ignore serial nos after creating a counter of each + # so we subtract returned serial nos from ignore serial nos after creating a counter of each to get the items which we need to ignore(which are sold) ignore_serial_nos_counter = Counter(ignore_serial_nos) returned_serial_nos_counter = Counter(returned_serial_nos) From 526f1d18fb0ca06f1e729227e41db6232095d07d Mon Sep 17 00:00:00 2001 From: Ritvik Sardana Date: Thu, 10 Aug 2023 17:35:12 +0530 Subject: [PATCH 07/22] fix: added test for runtime effect --- .../pos_closing_entry/pos_closing_entry.js | 1 + .../pos_closing_entry/pos_closing_entry.json | 3 +- .../test_pos_closing_entry.py | 48 +++++++++++++++++++ .../pos_invoice_merge_log.py | 15 ------ 4 files changed, 51 insertions(+), 16 deletions(-) diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js index a6c0102a7f9..8eed57338c6 100644 --- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js +++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js @@ -185,6 +185,7 @@ function refresh_payments(d, frm) { } if (payment) { payment.expected_amount += flt(p.amount); + payment.closing_amount = payment.expected_amount; payment.difference = payment.closing_amount - payment.expected_amount; } else { frm.add_child("payment_reconciliation", { diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json index 9d15e6cf357..a98a24c463a 100644 --- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json +++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json @@ -221,6 +221,7 @@ "read_only": 1 }, { + "default": "Now", "fieldname": "posting_time", "fieldtype": "Time", "label": "Posting Time", @@ -235,7 +236,7 @@ "link_fieldname": "pos_closing_entry" } ], - "modified": "2022-08-01 11:37:14.991228", + "modified": "2023-08-10 16:25:49.322697", "modified_by": "Administrator", "module": "Accounts", "name": "POS Closing Entry", diff --git a/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py b/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py index 1deb3c52ac6..d2eba1e9776 100644 --- a/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py +++ b/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py @@ -8,9 +8,11 @@ import frappe from erpnext.accounts.doctype.pos_closing_entry.pos_closing_entry import ( make_closing_entry_from_opening, ) +from erpnext.accounts.doctype.pos_invoice.pos_invoice import make_sales_return from erpnext.accounts.doctype.pos_invoice.test_pos_invoice import create_pos_invoice from erpnext.accounts.doctype.pos_opening_entry.test_pos_opening_entry import create_opening_entry from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile +from erpnext.selling.page.point_of_sale.point_of_sale import get_items from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry @@ -67,6 +69,36 @@ class TestPOSClosingEntry(unittest.TestCase): self.assertTrue(pcv_doc.name) + def test_pos_qty_for_item(self): + """ + Test if quantity is calculated correctly for an item in POS Closing Entry + """ + test_user, pos_profile = init_user_and_profile() + opening_entry = create_opening_entry(pos_profile, test_user.name) + + test_item_qty = get_test_item_qty(pos_profile) + + pos_inv1 = create_pos_invoice(rate=3500, do_not_submit=1) + pos_inv1.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3500}) + pos_inv1.submit() + + pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1) + pos_inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3200}) + pos_inv2.submit() + + # make return entry of pos_inv2 + pos_return = make_sales_return(pos_inv2.name) + pos_return.paid_amount = pos_return.grand_total + pos_return.save() + pos_return.submit() + + pcv_doc = make_closing_entry_from_opening(opening_entry) + pcv_doc.submit() + + opening_entry = create_opening_entry(pos_profile, test_user.name) + test_item_qty_after_sales = get_test_item_qty(pos_profile) + self.assertEqual(test_item_qty_after_sales, test_item_qty - 1) + def test_cancelling_of_pos_closing_entry(self): test_user, pos_profile = init_user_and_profile() opening_entry = create_opening_entry(pos_profile, test_user.name) @@ -123,3 +155,19 @@ def init_user_and_profile(**args): pos_profile.save() return test_user, pos_profile + + +def get_test_item_qty(pos_profile): + test_item_pos = get_items( + start=0, + page_length=40, + price_list="Standard Selling", + pos_profile=pos_profile.name, + search_term="_Test Item", + item_group="All Item Groups", + ) + + test_item_qty = [item for item in test_item_pos["items"] if item["item_code"] == "_Test Item"][0][ + "actual_qty" + ] + return test_item_qty diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py index 4f46aa153a7..3a684d4bba4 100644 --- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py +++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py @@ -273,21 +273,6 @@ class POSInvoiceMergeLog(Document): si.flags.ignore_validate = True si.cancel() - def get_batched_invoices(self, pos_invoice_docs): - grouped_batch = [] - current_batch = [] - for item in pos_invoice_docs: - if not current_batch: - current_batch.append(item) - elif current_batch[-1].get("is_return") != item.get("is_return"): - grouped_batch.append(current_batch) - current_batch = [item] - else: - current_batch.append(item) - - grouped_batch.append(current_batch) - return grouped_batch - def update_item_wise_tax_detail(consolidate_tax_row, tax_row): consolidated_tax_detail = json.loads(consolidate_tax_row.item_wise_tax_detail) From f6c055cca9ed489151ae526531ff8b4b8ba5fe5f Mon Sep 17 00:00:00 2001 From: Ritvik Sardana Date: Thu, 10 Aug 2023 18:15:23 +0530 Subject: [PATCH 08/22] fix: now time set in closing POS --- erpnext/selling/page/point_of_sale/pos_controller.js | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js index 720d142ca07..db6255a4bee 100644 --- a/erpnext/selling/page/point_of_sale/pos_controller.js +++ b/erpnext/selling/page/point_of_sale/pos_controller.js @@ -225,6 +225,7 @@ erpnext.PointOfSale.Controller = class { voucher.pos_opening_entry = this.pos_opening; voucher.period_end_date = frappe.datetime.now_datetime(); voucher.posting_date = frappe.datetime.now_date(); + voucher.posting_time = frappe.datetime.now_time(); frappe.set_route('Form', 'POS Closing Entry', voucher.name); } From 68df3f9729940b2f241379e85e20ac9fb8f182ec Mon Sep 17 00:00:00 2001 From: Ritvik Sardana Date: Mon, 14 Aug 2023 11:44:47 +0530 Subject: [PATCH 09/22] fix: get_items call improved --- .../doctype/pos_closing_entry/test_pos_closing_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py b/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py index d2eba1e9776..c551fdc5904 100644 --- a/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py +++ b/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py @@ -160,7 +160,7 @@ def init_user_and_profile(**args): def get_test_item_qty(pos_profile): test_item_pos = get_items( start=0, - page_length=40, + page_length=5, price_list="Standard Selling", pos_profile=pos_profile.name, search_term="_Test Item", From d4cc9daca1ac4ebe56828434181936f30e579c13 Mon Sep 17 00:00:00 2001 From: Ritvik Sardana Date: Mon, 14 Aug 2023 11:52:49 +0530 Subject: [PATCH 10/22] chore: code clean up --- .../doctype/pos_closing_entry/test_pos_closing_entry.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py b/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py index c551fdc5904..93ba90ad9f9 100644 --- a/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py +++ b/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py @@ -167,7 +167,7 @@ def get_test_item_qty(pos_profile): item_group="All Item Groups", ) - test_item_qty = [item for item in test_item_pos["items"] if item["item_code"] == "_Test Item"][0][ - "actual_qty" - ] + test_item_qty = [item for item in test_item_pos["items"] if item["item_code"] == "_Test Item"][ + 0 + ].get("actual_qty") return test_item_qty From deb0d7129438ff7966e1e50f5f7eb0a78e2fdb49 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 15 Aug 2023 05:17:01 +0530 Subject: [PATCH 11/22] perf: pull latest details only for referenced vouchers --- .../doctype/payment_entry/payment_entry.py | 149 +++++++++--------- erpnext/accounts/utils.py | 2 + 2 files changed, 79 insertions(+), 72 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index c6576f7985e..ac31e8a1dbe 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -230,84 +230,88 @@ class PaymentEntry(AccountsController): return False def validate_allocated_amount_with_latest_data(self): - latest_references = get_outstanding_reference_documents( - { - "posting_date": self.posting_date, - "company": self.company, - "party_type": self.party_type, - "payment_type": self.payment_type, - "party": self.party, - "party_account": self.paid_from if self.payment_type == "Receive" else self.paid_to, - "get_outstanding_invoices": True, - "get_orders_to_be_billed": True, - }, - validate=True, - ) + if self.references: + uniq_vouchers = set([(x.reference_doctype, x.reference_name) for x in self.references]) + vouchers = [frappe._dict({"voucher_type": x[0], "voucher_no": x[1]}) for x in uniq_vouchers] + latest_references = get_outstanding_reference_documents( + { + "posting_date": self.posting_date, + "company": self.company, + "party_type": self.party_type, + "payment_type": self.payment_type, + "party": self.party, + "party_account": self.paid_from if self.payment_type == "Receive" else self.paid_to, + "get_outstanding_invoices": True, + "get_orders_to_be_billed": True, + "vouchers": vouchers, + }, + validate=True, + ) - # Group latest_references by (voucher_type, voucher_no) - latest_lookup = {} - for d in latest_references: - d = frappe._dict(d) - latest_lookup.setdefault((d.voucher_type, d.voucher_no), frappe._dict())[d.payment_term] = d + # Group latest_references by (voucher_type, voucher_no) + latest_lookup = {} + for d in latest_references: + d = frappe._dict(d) + latest_lookup.setdefault((d.voucher_type, d.voucher_no), frappe._dict())[d.payment_term] = d - for idx, d in enumerate(self.get("references"), start=1): - latest = latest_lookup.get((d.reference_doctype, d.reference_name)) or frappe._dict() + for idx, d in enumerate(self.get("references"), start=1): + latest = latest_lookup.get((d.reference_doctype, d.reference_name)) or frappe._dict() - # If term based allocation is enabled, throw - if ( - d.payment_term is None or d.payment_term == "" - ) and self.term_based_allocation_enabled_for_reference( - d.reference_doctype, d.reference_name - ): - frappe.throw( - _( - "{0} has Payment Term based allocation enabled. Select a Payment Term for Row #{1} in Payment References section" - ).format(frappe.bold(d.reference_name), frappe.bold(idx)) - ) - - # if no payment template is used by invoice and has a custom term(no `payment_term`), then invoice outstanding will be in 'None' key - latest = latest.get(d.payment_term) or latest.get(None) - - # The reference has already been fully paid - if not latest: - frappe.throw( - _("{0} {1} has already been fully paid.").format(_(d.reference_doctype), d.reference_name) - ) - # The reference has already been partly paid - elif latest.outstanding_amount < latest.invoice_amount and flt( - d.outstanding_amount, d.precision("outstanding_amount") - ) != flt(latest.outstanding_amount, d.precision("outstanding_amount")): - frappe.throw( - _( - "{0} {1} has already been partly paid. Please use the 'Get Outstanding Invoice' or the 'Get Outstanding Orders' button to get the latest outstanding amounts." - ).format(_(d.reference_doctype), d.reference_name) - ) - - fail_message = _("Row #{0}: Allocated Amount cannot be greater than outstanding amount.") - - if ( - d.payment_term - and ( - (flt(d.allocated_amount)) > 0 - and latest.payment_term_outstanding - and (flt(d.allocated_amount) > flt(latest.payment_term_outstanding)) - ) - and self.term_based_allocation_enabled_for_reference(d.reference_doctype, d.reference_name) - ): - frappe.throw( - _( - "Row #{0}: Allocated amount:{1} is greater than outstanding amount:{2} for Payment Term {3}" - ).format( - d.idx, d.allocated_amount, latest.payment_term_outstanding, d.payment_term + # If term based allocation is enabled, throw + if ( + d.payment_term is None or d.payment_term == "" + ) and self.term_based_allocation_enabled_for_reference( + d.reference_doctype, d.reference_name + ): + frappe.throw( + _( + "{0} has Payment Term based allocation enabled. Select a Payment Term for Row #{1} in Payment References section" + ).format(frappe.bold(d.reference_name), frappe.bold(idx)) ) - ) - if (flt(d.allocated_amount)) > 0 and flt(d.allocated_amount) > flt(latest.outstanding_amount): - frappe.throw(fail_message.format(d.idx)) + # if no payment template is used by invoice and has a custom term(no `payment_term`), then invoice outstanding will be in 'None' key + latest = latest.get(d.payment_term) or latest.get(None) - # Check for negative outstanding invoices as well - if flt(d.allocated_amount) < 0 and flt(d.allocated_amount) < flt(latest.outstanding_amount): - frappe.throw(fail_message.format(d.idx)) + # The reference has already been fully paid + if not latest: + frappe.throw( + _("{0} {1} has already been fully paid.").format(_(d.reference_doctype), d.reference_name) + ) + # The reference has already been partly paid + elif latest.outstanding_amount < latest.invoice_amount and flt( + d.outstanding_amount, d.precision("outstanding_amount") + ) != flt(latest.outstanding_amount, d.precision("outstanding_amount")): + frappe.throw( + _( + "{0} {1} has already been partly paid. Please use the 'Get Outstanding Invoice' or the 'Get Outstanding Orders' button to get the latest outstanding amounts." + ).format(_(d.reference_doctype), d.reference_name) + ) + + fail_message = _("Row #{0}: Allocated Amount cannot be greater than outstanding amount.") + + if ( + d.payment_term + and ( + (flt(d.allocated_amount)) > 0 + and latest.payment_term_outstanding + and (flt(d.allocated_amount) > flt(latest.payment_term_outstanding)) + ) + and self.term_based_allocation_enabled_for_reference(d.reference_doctype, d.reference_name) + ): + frappe.throw( + _( + "Row #{0}: Allocated amount:{1} is greater than outstanding amount:{2} for Payment Term {3}" + ).format( + d.idx, d.allocated_amount, latest.payment_term_outstanding, d.payment_term + ) + ) + + if (flt(d.allocated_amount)) > 0 and flt(d.allocated_amount) > flt(latest.outstanding_amount): + frappe.throw(fail_message.format(d.idx)) + + # Check for negative outstanding invoices as well + if flt(d.allocated_amount) < 0 and flt(d.allocated_amount) < flt(latest.outstanding_amount): + frappe.throw(fail_message.format(d.idx)) def delink_advance_entry_references(self): for reference in self.references: @@ -1587,6 +1591,7 @@ def get_outstanding_reference_documents(args, validate=False): min_outstanding=args.get("outstanding_amt_greater_than"), max_outstanding=args.get("outstanding_amt_less_than"), accounting_dimensions=accounting_dimensions_filter, + vouchers=args.get("vouchers") or None, ) outstanding_invoices = split_invoices_based_on_payment_terms( diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index c24442e2c3a..bccf6f10b63 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -908,6 +908,7 @@ def get_outstanding_invoices( min_outstanding=None, max_outstanding=None, accounting_dimensions=None, + vouchers=None, ): ple = qb.DocType("Payment Ledger Entry") @@ -933,6 +934,7 @@ def get_outstanding_invoices( ple_query = QueryPaymentLedger() invoice_list = ple_query.get_voucher_outstandings( + vouchers=vouchers, common_filter=common_filter, posting_date=posting_date, min_outstanding=min_outstanding, From e0c79d3b53399e336bf6ff45489751b7d6c58f6a Mon Sep 17 00:00:00 2001 From: Anand Baburajan Date: Tue, 15 Aug 2023 15:47:15 +0530 Subject: [PATCH 12/22] chore: add validation for depreciation expense account in asset category (#36659) --- erpnext/assets/doctype/asset_category/asset_category.js | 1 + erpnext/assets/doctype/asset_category/asset_category.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/assets/doctype/asset_category/asset_category.js b/erpnext/assets/doctype/asset_category/asset_category.js index c702687072d..7dde14ea0e6 100644 --- a/erpnext/assets/doctype/asset_category/asset_category.js +++ b/erpnext/assets/doctype/asset_category/asset_category.js @@ -33,6 +33,7 @@ frappe.ui.form.on('Asset Category', { var d = locals[cdt][cdn]; return { "filters": { + "account_type": "Depreciation", "root_type": ["in", ["Expense", "Income"]], "is_group": 0, "company": d.company_name diff --git a/erpnext/assets/doctype/asset_category/asset_category.py b/erpnext/assets/doctype/asset_category/asset_category.py index 2e1def98fc3..8d351412ca8 100644 --- a/erpnext/assets/doctype/asset_category/asset_category.py +++ b/erpnext/assets/doctype/asset_category/asset_category.py @@ -53,7 +53,7 @@ class AssetCategory(Document): account_type_map = { "fixed_asset_account": {"account_type": ["Fixed Asset"]}, "accumulated_depreciation_account": {"account_type": ["Accumulated Depreciation"]}, - "depreciation_expense_account": {"root_type": ["Expense", "Income"]}, + "depreciation_expense_account": {"account_type": ["Depreciation"]}, "capital_work_in_progress_account": {"account_type": ["Capital Work in Progress"]}, } for d in self.accounts: From 45662fa646cbd861d89e827bc557a3f2a25964b1 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 15 Aug 2023 16:16:50 +0530 Subject: [PATCH 13/22] fix: re-add permission that was unintentionally removed Remove `Reversal OF ITC` and re-add permissions. Both of them unintended changes --- .../doctype/journal_entry/journal_entry.json | 42 ++++++++++++++++++- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.json b/erpnext/accounts/doctype/journal_entry/journal_entry.json index 80df0ff0dff..2eb54a54d54 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.json +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.json @@ -89,7 +89,7 @@ "label": "Entry Type", "oldfieldname": "voucher_type", "oldfieldtype": "Select", - "options": "Journal Entry\nInter Company Journal Entry\nBank Entry\nCash Entry\nCredit Card Entry\nDebit Note\nCredit Note\nContra Entry\nExcise Entry\nWrite Off Entry\nOpening Entry\nDepreciation Entry\nExchange Rate Revaluation\nExchange Gain Or Loss\nDeferred Revenue\nDeferred Expense\nReversal Of ITC", + "options": "Journal Entry\nInter Company Journal Entry\nBank Entry\nCash Entry\nCredit Card Entry\nDebit Note\nCredit Note\nContra Entry\nExcise Entry\nWrite Off Entry\nOpening Entry\nDepreciation Entry\nExchange Rate Revaluation\nExchange Gain Or Loss\nDeferred Revenue\nDeferred Expense", "reqd": 1, "search_index": 1 }, @@ -555,7 +555,45 @@ "name": "Journal Entry", "naming_rule": "By \"Naming Series\" field", "owner": "Administrator", - "permissions": [], + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "import": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Auditor" + } + ], "search_fields": "voucher_type,posting_date, due_date, cheque_no", "sort_field": "modified", "sort_order": "DESC", From 0d95fc0f20900b0af52d331602a646428225321f Mon Sep 17 00:00:00 2001 From: Ritvik Sardana Date: Wed, 16 Aug 2023 11:41:24 +0530 Subject: [PATCH 14/22] fix: test_serial_no_case_1 test case works --- .../doctype/pos_invoice_merge_log/pos_invoice_merge_log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py index 3a684d4bba4..b587ce603f4 100644 --- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py +++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py @@ -389,7 +389,7 @@ def split_invoices(invoices): continue return_against_is_added = any( - d for d in invoices if d.pos_invoice == pos_invoice.return_against + d for d in _invoices if d.pos_invoice == pos_invoice.return_against ) if return_against_is_added: break From 124c0dbd880dde227dacf6136f4e463acc08737f Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 16 Aug 2023 19:10:39 +0530 Subject: [PATCH 15/22] fix(UX): Ignore prepared report --- erpnext/public/js/controllers/stock_controller.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/public/js/controllers/stock_controller.js b/erpnext/public/js/controllers/stock_controller.js index 720423b0a49..e9c409e32ab 100644 --- a/erpnext/public/js/controllers/stock_controller.js +++ b/erpnext/public/js/controllers/stock_controller.js @@ -57,7 +57,8 @@ erpnext.stock.StockController = class StockController extends frappe.ui.form.Con from_date: me.frm.doc.posting_date, to_date: moment(me.frm.doc.modified).format('YYYY-MM-DD'), company: me.frm.doc.company, - show_cancelled_entries: me.frm.doc.docstatus === 2 + show_cancelled_entries: me.frm.doc.docstatus === 2, + ignore_prepared_report: true }; frappe.set_route("query-report", "Stock Ledger"); }, __("View")); @@ -75,7 +76,8 @@ erpnext.stock.StockController = class StockController extends frappe.ui.form.Con to_date: moment(me.frm.doc.modified).format('YYYY-MM-DD'), company: me.frm.doc.company, group_by: "Group by Voucher (Consolidated)", - show_cancelled_entries: me.frm.doc.docstatus === 2 + show_cancelled_entries: me.frm.doc.docstatus === 2, + ignore_prepared_report: true }; frappe.set_route("query-report", "General Ledger"); }, __("View")); From 35be3ac5a1e38b4e1dc731ac5162d2751524f541 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 17 Aug 2023 11:31:40 +0530 Subject: [PATCH 16/22] feat: Transaction currency columns in GL report --- .../accounts/doctype/gl_entry/gl_entry.json | 36 ++++++++++++++++-- .../report/general_ledger/general_ledger.js | 5 +++ .../report/general_ledger/general_ledger.py | 37 ++++++++++++++++++- erpnext/controllers/accounts_controller.py | 20 ++++++++++ 4 files changed, 94 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.json b/erpnext/accounts/doctype/gl_entry/gl_entry.json index e6d97a1fb26..5063ec60769 100644 --- a/erpnext/accounts/doctype/gl_entry/gl_entry.json +++ b/erpnext/accounts/doctype/gl_entry/gl_entry.json @@ -32,7 +32,11 @@ "finance_book", "to_rename", "due_date", - "is_cancelled" + "is_cancelled", + "transaction_currency", + "debit_in_transaction_currency", + "credit_in_transaction_currency", + "transaction_exchange_rate" ], "fields": [ { @@ -253,15 +257,40 @@ "fieldname": "is_cancelled", "fieldtype": "Check", "label": "Is Cancelled" + }, + { + "fieldname": "transaction_currency", + "fieldtype": "Link", + "label": "Transaction Currency", + "options": "Currency" + }, + { + "fieldname": "transaction_exchange_rate", + "fieldtype": "Float", + "label": "Transaction Exchange Rate" + }, + { + "fieldname": "debit_in_transaction_currency", + "fieldtype": "Currency", + "label": "Debit Amount in Transaction Currency", + "options": "transaction_currency" + }, + { + "fieldname": "credit_in_transaction_currency", + "fieldtype": "Currency", + "label": "Credit Amount in Transaction Currency", + "options": "transaction_currency" } ], "icon": "fa fa-list", "idx": 1, "in_create": 1, - "modified": "2020-04-07 16:22:33.766994", + "links": [], + "modified": "2023-08-16 21:38:44.072267", "modified_by": "Administrator", "module": "Accounts", "name": "GL Entry", + "naming_rule": "Expression (old style)", "owner": "Administrator", "permissions": [ { @@ -290,5 +319,6 @@ "quick_entry": 1, "search_fields": "voucher_no,account,posting_date,against_voucher", "sort_field": "modified", - "sort_order": "DESC" + "sort_order": "DESC", + "states": [] } \ No newline at end of file diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js index 57a9091cf9b..37d0659acfc 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.js +++ b/erpnext/accounts/report/general_ledger/general_ledger.js @@ -188,6 +188,11 @@ frappe.query_reports["General Ledger"] = { "fieldname": "show_net_values_in_party_account", "label": __("Show Net Values in Party Account"), "fieldtype": "Check" + }, + { + "fieldname": "add_values_in_transaction_currency", + "label": __("Add Columns in Transaction Currency"), + "fieldtype": "Check" } ] } diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index d7af167e381..e05a4e79e81 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -182,12 +182,18 @@ def get_gl_entries(filters, accounting_dimensions): if accounting_dimensions: dimension_fields = ", ".join(accounting_dimensions) + "," + transaction_currency_fields = "" + if filters.get("add_values_in_transaction_currency"): + transaction_currency_fields = ( + "debit_in_transaction_currency, credit_in_transaction_currency, transaction_currency," + ) + gl_entries = frappe.db.sql( """ select name as gl_entry, posting_date, account, party_type, party, voucher_type, voucher_no, {dimension_fields} - cost_center, project, + cost_center, project, {transaction_currency_fields} against_voucher_type, against_voucher, account_currency, remarks, against, is_opening, creation {select_fields} from `tabGL Entry` @@ -195,6 +201,7 @@ def get_gl_entries(filters, accounting_dimensions): {order_by_statement} """.format( dimension_fields=dimension_fields, + transaction_currency_fields=transaction_currency_fields, select_fields=select_fields, conditions=get_conditions(filters), order_by_statement=order_by_statement, @@ -562,6 +569,34 @@ def get_columns(filters): "fieldtype": "Float", "width": 130, }, + ] + + if filters.get("add_values_in_transaction_currency"): + columns += [ + { + "label": _("Debit (Transaction)"), + "fieldname": "debit_in_transaction_currency", + "fieldtype": "Currency", + "width": 130, + "options": "transaction_currency", + }, + { + "label": _("Credit (Transaction)"), + "fieldname": "credit_in_transaction_currency", + "fieldtype": "Currency", + "width": 130, + "options": "transaction_currency", + }, + { + "label": "Transaction Currency", + "fieldname": "transaction_currency", + "fieldtype": "Link", + "options": "Currency", + "width": 70, + }, + ] + + columns += [ {"label": _("Voucher Type"), "fieldname": "voucher_type", "width": 120}, { "label": _("Voucher No"), diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index fbf97aab594..7a643a01b6d 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -803,8 +803,28 @@ class AccountsController(TransactionBase): gl_dict, account_currency, self.get("conversion_rate"), self.company_currency ) + # Update details in transaction currency + gl_dict.update( + { + "transaction_currency": self.get("currency") or self.company_currency, + "transaction_exchange_rate": self.get("conversion_rate", 1), + "debit_in_transaction_currency": self.get_value_in_transaction_currency( + account_currency, args, "debit" + ), + "credit_in_transaction_currency": self.get_value_in_transaction_currency( + account_currency, args, "credit" + ), + } + ) + return gl_dict + def get_value_in_transaction_currency(self, account_currency, args, field): + if account_currency == self.get("currency"): + return args.get(field + "_in_account_currency") + else: + return flt(args.get(field, 0) / self.get("conversion_rate", 1)) + def validate_qty_is_not_zero(self): if self.doctype != "Purchase Receipt": for item in self.items: From 7ec6909159bb96a16f368f464f026acaf4354900 Mon Sep 17 00:00:00 2001 From: Shariq Ansari Date: Thu, 17 Aug 2023 11:59:47 +0530 Subject: [PATCH 17/22] fix: check tax and charges if it is passed --- erpnext/controllers/accounts_controller.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index fbf97aab594..1a30ea5bfcc 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -716,7 +716,7 @@ class AccountsController(TransactionBase): def validate_enabled_taxes_and_charges(self): taxes_and_charges_doctype = self.meta.get_options("taxes_and_charges") - if frappe.get_cached_value(taxes_and_charges_doctype, self.taxes_and_charges, "disabled"): + if self.taxes_and_charges and frappe.get_cached_value(taxes_and_charges_doctype, self.taxes_and_charges, "disabled"): frappe.throw( _("{0} '{1}' is disabled").format(taxes_and_charges_doctype, self.taxes_and_charges) ) From 552bbb1d4657352934e1de7120d94078a69c4ae7 Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Thu, 17 Aug 2023 08:34:05 +0200 Subject: [PATCH 18/22] fix(RFQ): make "update password" and "submit quotation" buttons the same size (#36667) fix(RFQ): button styling --- .../doctype/request_for_quotation/request_for_quotation.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 e9385777e5d..56840c11a6e 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py @@ -193,7 +193,7 @@ class RequestforQuotation(BuyingController): "supplier": data.get("supplier"), "supplier_name": data.get("supplier_name"), "update_password_link": f'{_("Set Password")}', - "portal_link": f' {_("Submit your Quotation")} ', + "portal_link": f' {_("Submit your Quotation")} ', "user_fullname": full_name, } ) From 21c1141fdb2fd7adff1056e11a114d6b60523d41 Mon Sep 17 00:00:00 2001 From: Shariq Ansari <30859809+shariquerik@users.noreply.github.com> Date: Thu, 17 Aug 2023 12:14:13 +0530 Subject: [PATCH 19/22] chore: linter fix --- erpnext/controllers/accounts_controller.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 1a30ea5bfcc..00de9cc3f68 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -716,7 +716,9 @@ class AccountsController(TransactionBase): def validate_enabled_taxes_and_charges(self): taxes_and_charges_doctype = self.meta.get_options("taxes_and_charges") - if self.taxes_and_charges and frappe.get_cached_value(taxes_and_charges_doctype, self.taxes_and_charges, "disabled"): + if self.taxes_and_charges and frappe.get_cached_value( + taxes_and_charges_doctype, self.taxes_and_charges, "disabled" + ): frappe.throw( _("{0} '{1}' is disabled").format(taxes_and_charges_doctype, self.taxes_and_charges) ) From 6bf79f18c860f304e9a9008b14f6dcef0d5f4914 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Thu, 17 Aug 2023 12:37:29 +0530 Subject: [PATCH 20/22] chore: skip demo banner if another one present --- erpnext/public/js/utils/demo.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/utils/demo.js b/erpnext/public/js/utils/demo.js index b59c4762e00..3ebc5efcf13 100644 --- a/erpnext/public/js/utils/demo.js +++ b/erpnext/public/js/utils/demo.js @@ -11,7 +11,7 @@ $(document).on("toolbar_setup", function () { function render_clear_demo_button() { let wait_for_onboaring_tours = setInterval(() => { - if ($("#driver-page-overlay").length) { + if ($("#driver-page-overlay").length || $("#show-dialog").length) { return; } setup_clear_demo_button(); From 3f6ff8e0b7a940132d5be9ea7d0edd696944eea4 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 17 Aug 2023 13:48:37 +0530 Subject: [PATCH 21/22] perf: enabled indexing for voucher no in SABB (#36688) --- .../serial_and_batch_bundle/serial_and_batch_bundle.json | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.json b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.json index 15bd2f04756..d46b07a3e1d 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.json +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.json @@ -115,7 +115,8 @@ "fieldtype": "Dynamic Link", "label": "Voucher No", "no_copy": 1, - "options": "voucher_type" + "options": "voucher_type", + "search_index": 1 }, { "default": "0", @@ -229,7 +230,8 @@ "fieldtype": "Data", "label": "Voucher Detail No", "no_copy": 1, - "read_only": 1 + "read_only": 1, + "search_index": 1 }, { "allow_bulk_edit": 1, @@ -248,7 +250,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2023-07-26 12:56:03.072224", + "modified": "2023-07-28 12:56:03.072224", "modified_by": "Administrator", "module": "Stock", "name": "Serial and Batch Bundle", From 96847db0ec78add9eaf781bef66464af6e13e07f Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 18 Aug 2023 14:04:46 +0530 Subject: [PATCH 22/22] fix: broken consolidated report due to finance book filter --- .../consolidated_financial_statement.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py index 080e45a7987..0051ba6aa8d 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py @@ -744,13 +744,18 @@ def get_additional_conditions(from_date, ignore_closing_entries, filters, d): if from_date: additional_conditions.append(gle.posting_date >= from_date) - finance_book = filters.get("finance_book") - company_fb = frappe.get_cached_value("Company", d.name, "default_finance_book") + finance_books = [] + finance_books.append("") + if filter_fb := filters.get("finance_book"): + finance_books.append(filter_fb) if filters.get("include_default_book_entries"): - additional_conditions.append((gle.finance_book.isin([finance_book, company_fb, "", None]))) + if company_fb := frappe.get_cached_value("Company", d.name, "default_finance_book"): + finance_books.append(company_fb) + + additional_conditions.append((gle.finance_book.isin(finance_books)) | gle.finance_book.isnull()) else: - additional_conditions.append((gle.finance_book.isin([finance_book, "", None]))) + additional_conditions.append((gle.finance_book.isin(finance_books)) | gle.finance_book.isnull()) return additional_conditions