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/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", 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/doctype/pos_closing_entry/pos_closing_entry.js b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js index 91e71e90dd8..faceaf35d59 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..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 @@ -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=5, + 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 + ].get("actual_qty") + return test_item_qty diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index 4b2fcec7579..89a96118ec4 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 @@ -595,7 +596,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..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 @@ -95,7 +95,6 @@ class POSInvoiceMergeLog(Document): 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): @@ -108,7 +107,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 @@ -165,8 +163,7 @@ class POSInvoiceMergeLog(Document): for i in items: if ( i.item_code == item.item_code - and not i.serial_no - and not i.batch_no + and not i.serial_and_batch_bundle and i.uom == item.uom and i.net_rate == item.net_rate and i.warehouse == item.warehouse @@ -385,6 +382,7 @@ def split_invoices(invoices): for d in invoices if d.is_return and d.return_against ] + 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/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index d17ca08c408..954b4e7957e 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 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 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/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, 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: 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, } ) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index fbf97aab594..340ec01bee7 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 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) ) @@ -803,8 +805,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: 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")); 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); } 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", 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..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 @@ -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], ], @@ -1214,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: @@ -1242,8 +1242,13 @@ 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 + # 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) - 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):