Merge branch 'develop' into fixed_asset_report_asset_value

This commit is contained in:
Anand Baburajan 2023-01-12 20:30:39 +05:30 committed by GitHub
commit 29088e1777
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 367 additions and 73 deletions

View File

@ -21,13 +21,22 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
frm.trigger('bank_account'); frm.trigger('bank_account');
}, },
filter_by_reference_date: function (frm) {
if (frm.doc.filter_by_reference_date) {
frm.set_value("bank_statement_from_date", "");
frm.set_value("bank_statement_to_date", "");
} else {
frm.set_value("from_reference_date", "");
frm.set_value("to_reference_date", "");
}
},
refresh: function (frm) { refresh: function (frm) {
frappe.require("bank-reconciliation-tool.bundle.js", () => frappe.require("bank-reconciliation-tool.bundle.js", () =>
frm.trigger("make_reconciliation_tool") frm.trigger("make_reconciliation_tool")
); );
frm.upload_statement_button = frm.page.set_secondary_action(
__("Upload Bank Statement"), frm.add_custom_button(__("Upload Bank Statement"), () =>
() =>
frappe.call({ frappe.call({
method: method:
"erpnext.accounts.doctype.bank_statement_import.bank_statement_import.upload_bank_statement", "erpnext.accounts.doctype.bank_statement_import.bank_statement_import.upload_bank_statement",
@ -49,6 +58,20 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
}, },
}) })
); );
frm.add_custom_button(__('Auto Reconcile'), function() {
frappe.call({
method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.auto_reconcile_vouchers",
args: {
bank_account: frm.doc.bank_account,
from_date: frm.doc.bank_statement_from_date,
to_date: frm.doc.bank_statement_to_date,
filter_by_reference_date: frm.doc.filter_by_reference_date,
from_reference_date: frm.doc.from_reference_date,
to_reference_date: frm.doc.to_reference_date,
},
})
});
}, },
after_save: function (frm) { after_save: function (frm) {
@ -160,6 +183,9 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
).$wrapper, ).$wrapper,
bank_statement_from_date: frm.doc.bank_statement_from_date, bank_statement_from_date: frm.doc.bank_statement_from_date,
bank_statement_to_date: frm.doc.bank_statement_to_date, bank_statement_to_date: frm.doc.bank_statement_to_date,
filter_by_reference_date: frm.doc.filter_by_reference_date,
from_reference_date: frm.doc.from_reference_date,
to_reference_date: frm.doc.to_reference_date,
bank_statement_closing_balance: bank_statement_closing_balance:
frm.doc.bank_statement_closing_balance, frm.doc.bank_statement_closing_balance,
cards_manager: frm.cards_manager, cards_manager: frm.cards_manager,

View File

@ -10,6 +10,9 @@
"column_break_1", "column_break_1",
"bank_statement_from_date", "bank_statement_from_date",
"bank_statement_to_date", "bank_statement_to_date",
"from_reference_date",
"to_reference_date",
"filter_by_reference_date",
"column_break_2", "column_break_2",
"account_opening_balance", "account_opening_balance",
"bank_statement_closing_balance", "bank_statement_closing_balance",
@ -36,13 +39,13 @@
"fieldtype": "Column Break" "fieldtype": "Column Break"
}, },
{ {
"depends_on": "eval: doc.bank_account", "depends_on": "eval: doc.bank_account && !doc.filter_by_reference_date",
"fieldname": "bank_statement_from_date", "fieldname": "bank_statement_from_date",
"fieldtype": "Date", "fieldtype": "Date",
"label": "From Date" "label": "From Date"
}, },
{ {
"depends_on": "eval: doc.bank_statement_from_date", "depends_on": "eval: doc.bank_account && !doc.filter_by_reference_date",
"fieldname": "bank_statement_to_date", "fieldname": "bank_statement_to_date",
"fieldtype": "Date", "fieldtype": "Date",
"label": "To Date" "label": "To Date"
@ -81,14 +84,33 @@
}, },
{ {
"fieldname": "no_bank_transactions", "fieldname": "no_bank_transactions",
"fieldtype": "HTML" "fieldtype": "HTML",
"options": "<div class=\"text-muted text-center\">No Matching Bank Transactions Found</div>"
},
{
"depends_on": "eval:doc.filter_by_reference_date",
"fieldname": "from_reference_date",
"fieldtype": "Date",
"label": "From Reference Date"
},
{
"depends_on": "eval:doc.filter_by_reference_date",
"fieldname": "to_reference_date",
"fieldtype": "Date",
"label": "To Reference Date"
},
{
"default": "0",
"fieldname": "filter_by_reference_date",
"fieldtype": "Check",
"label": "Filter by Reference Date"
} }
], ],
"hide_toolbar": 1, "hide_toolbar": 1,
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"issingle": 1, "issingle": 1,
"links": [], "links": [],
"modified": "2021-04-21 11:13:49.831769", "modified": "2023-01-13 13:00:02.022919",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Bank Reconciliation Tool", "name": "Bank Reconciliation Tool",
@ -107,5 +129,6 @@
], ],
"quick_entry": 1, "quick_entry": 1,
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC" "sort_order": "DESC",
} "states": []
}

View File

@ -8,7 +8,7 @@ import frappe
from frappe import _ from frappe import _
from frappe.model.document import Document from frappe.model.document import Document
from frappe.query_builder.custom import ConstantColumn from frappe.query_builder.custom import ConstantColumn
from frappe.utils import flt from frappe.utils import cint, flt
from erpnext.accounts.doctype.bank_transaction.bank_transaction import get_paid_amount from erpnext.accounts.doctype.bank_transaction.bank_transaction import get_paid_amount
from erpnext.accounts.report.bank_reconciliation_statement.bank_reconciliation_statement import ( from erpnext.accounts.report.bank_reconciliation_statement.bank_reconciliation_statement import (
@ -50,6 +50,7 @@ def get_bank_transactions(bank_account, from_date=None, to_date=None):
"party", "party",
], ],
filters=filters, filters=filters,
order_by="date",
) )
return transactions return transactions
@ -265,6 +266,80 @@ def create_payment_entry_bts(
return reconcile_vouchers(bank_transaction.name, vouchers) return reconcile_vouchers(bank_transaction.name, vouchers)
@frappe.whitelist()
def auto_reconcile_vouchers(
bank_account,
from_date=None,
to_date=None,
filter_by_reference_date=None,
from_reference_date=None,
to_reference_date=None,
):
frappe.flags.auto_reconcile_vouchers = True
document_types = ["payment_entry", "journal_entry"]
bank_transactions = get_bank_transactions(bank_account)
matched_transaction = []
for transaction in bank_transactions:
linked_payments = get_linked_payments(
transaction.name,
document_types,
from_date,
to_date,
filter_by_reference_date,
from_reference_date,
to_reference_date,
)
vouchers = []
for r in linked_payments:
vouchers.append(
{
"payment_doctype": r[1],
"payment_name": r[2],
"amount": r[4],
}
)
transaction = frappe.get_doc("Bank Transaction", transaction.name)
account = frappe.db.get_value("Bank Account", transaction.bank_account, "account")
matched_trans = 0
for voucher in vouchers:
gl_entry = frappe.db.get_value(
"GL Entry",
dict(
account=account, voucher_type=voucher["payment_doctype"], voucher_no=voucher["payment_name"]
),
["credit", "debit"],
as_dict=1,
)
gl_amount, transaction_amount = (
(gl_entry.credit, transaction.deposit)
if gl_entry.credit > 0
else (gl_entry.debit, transaction.withdrawal)
)
allocated_amount = gl_amount if gl_amount >= transaction_amount else transaction_amount
transaction.append(
"payment_entries",
{
"payment_document": voucher["payment_doctype"],
"payment_entry": voucher["payment_name"],
"allocated_amount": allocated_amount,
},
)
matched_transaction.append(str(transaction.name))
transaction.save()
transaction.update_allocations()
matched_transaction_len = len(set(matched_transaction))
if matched_transaction_len == 0:
frappe.msgprint(_("No matching references found for auto reconciliation"))
elif matched_transaction_len == 1:
frappe.msgprint(_("{0} transaction is reconcilied").format(matched_transaction_len))
else:
frappe.msgprint(_("{0} transactions are reconcilied").format(matched_transaction_len))
frappe.flags.auto_reconcile_vouchers = False
return frappe.get_doc("Bank Transaction", transaction.name)
@frappe.whitelist() @frappe.whitelist()
def reconcile_vouchers(bank_transaction_name, vouchers): def reconcile_vouchers(bank_transaction_name, vouchers):
# updated clear date of all the vouchers based on the bank transaction # updated clear date of all the vouchers based on the bank transaction
@ -327,20 +402,58 @@ def reconcile_vouchers(bank_transaction_name, vouchers):
@frappe.whitelist() @frappe.whitelist()
def get_linked_payments(bank_transaction_name, document_types=None): def get_linked_payments(
bank_transaction_name,
document_types=None,
from_date=None,
to_date=None,
filter_by_reference_date=None,
from_reference_date=None,
to_reference_date=None,
):
# get all matching payments for a bank transaction # get all matching payments for a bank transaction
transaction = frappe.get_doc("Bank Transaction", bank_transaction_name) transaction = frappe.get_doc("Bank Transaction", bank_transaction_name)
bank_account = frappe.db.get_values( bank_account = frappe.db.get_values(
"Bank Account", transaction.bank_account, ["account", "company"], as_dict=True "Bank Account", transaction.bank_account, ["account", "company"], as_dict=True
)[0] )[0]
(account, company) = (bank_account.account, bank_account.company) (account, company) = (bank_account.account, bank_account.company)
matching = check_matching(account, company, transaction, document_types) matching = check_matching(
account,
company,
transaction,
document_types,
from_date,
to_date,
filter_by_reference_date,
from_reference_date,
to_reference_date,
)
return matching return matching
def check_matching(bank_account, company, transaction, document_types): def check_matching(
bank_account,
company,
transaction,
document_types,
from_date,
to_date,
filter_by_reference_date,
from_reference_date,
to_reference_date,
):
# combine all types of vouchers # combine all types of vouchers
subquery = get_queries(bank_account, company, transaction, document_types) subquery = get_queries(
bank_account,
company,
transaction,
document_types,
from_date,
to_date,
filter_by_reference_date,
from_reference_date,
to_reference_date,
)
filters = { filters = {
"amount": transaction.unallocated_amount, "amount": transaction.unallocated_amount,
"payment_type": "Receive" if transaction.deposit > 0 else "Pay", "payment_type": "Receive" if transaction.deposit > 0 else "Pay",
@ -361,11 +474,20 @@ def check_matching(bank_account, company, transaction, document_types):
filters, filters,
) )
) )
return sorted(matching_vouchers, key=lambda x: x[0], reverse=True) if matching_vouchers else [] return sorted(matching_vouchers, key=lambda x: x[0], reverse=True) if matching_vouchers else []
def get_queries(bank_account, company, transaction, document_types): def get_queries(
bank_account,
company,
transaction,
document_types,
from_date,
to_date,
filter_by_reference_date,
from_reference_date,
to_reference_date,
):
# get queries to get matching vouchers # get queries to get matching vouchers
amount_condition = "=" if "exact_match" in document_types else "<=" amount_condition = "=" if "exact_match" in document_types else "<="
account_from_to = "paid_to" if transaction.deposit > 0 else "paid_from" account_from_to = "paid_to" if transaction.deposit > 0 else "paid_from"
@ -381,6 +503,11 @@ def get_queries(bank_account, company, transaction, document_types):
document_types, document_types,
amount_condition, amount_condition,
account_from_to, account_from_to,
from_date,
to_date,
filter_by_reference_date,
from_reference_date,
to_reference_date,
) )
or [] or []
) )
@ -389,15 +516,42 @@ def get_queries(bank_account, company, transaction, document_types):
def get_matching_queries( def get_matching_queries(
bank_account, company, transaction, document_types, amount_condition, account_from_to bank_account,
company,
transaction,
document_types,
amount_condition,
account_from_to,
from_date,
to_date,
filter_by_reference_date,
from_reference_date,
to_reference_date,
): ):
queries = [] queries = []
if "payment_entry" in document_types: if "payment_entry" in document_types:
pe_amount_matching = get_pe_matching_query(amount_condition, account_from_to, transaction) pe_amount_matching = get_pe_matching_query(
amount_condition,
account_from_to,
transaction,
from_date,
to_date,
filter_by_reference_date,
from_reference_date,
to_reference_date,
)
queries.extend([pe_amount_matching]) queries.extend([pe_amount_matching])
if "journal_entry" in document_types: if "journal_entry" in document_types:
je_amount_matching = get_je_matching_query(amount_condition, transaction) je_amount_matching = get_je_matching_query(
amount_condition,
transaction,
from_date,
to_date,
filter_by_reference_date,
from_reference_date,
to_reference_date,
)
queries.extend([je_amount_matching]) queries.extend([je_amount_matching])
if transaction.deposit > 0 and "sales_invoice" in document_types: if transaction.deposit > 0 and "sales_invoice" in document_types:
@ -504,47 +658,81 @@ def get_lr_matching_query(bank_account, amount_condition, filters):
return vouchers return vouchers
def get_pe_matching_query(amount_condition, account_from_to, transaction): def get_pe_matching_query(
amount_condition,
account_from_to,
transaction,
from_date,
to_date,
filter_by_reference_date,
from_reference_date,
to_reference_date,
):
# get matching payment entries query # get matching payment entries query
if transaction.deposit > 0: if transaction.deposit > 0:
currency_field = "paid_to_account_currency as currency" currency_field = "paid_to_account_currency as currency"
else: else:
currency_field = "paid_from_account_currency as currency" currency_field = "paid_from_account_currency as currency"
filter_by_date = f"AND posting_date between '{from_date}' and '{to_date}'"
order_by = " posting_date"
filter_by_reference_no = ""
if cint(filter_by_reference_date):
filter_by_date = f"AND reference_date between '{from_reference_date}' and '{to_reference_date}'"
order_by = " reference_date"
if frappe.flags.auto_reconcile_vouchers == True:
filter_by_reference_no = f"AND reference_no = '{transaction.reference_number}'"
return f""" return f"""
SELECT SELECT
(CASE WHEN reference_no=%(reference_no)s THEN 1 ELSE 0 END (CASE WHEN reference_no=%(reference_no)s THEN 1 ELSE 0 END
+ CASE WHEN (party_type = %(party_type)s AND party = %(party)s ) THEN 1 ELSE 0 END + CASE WHEN (party_type = %(party_type)s AND party = %(party)s ) THEN 1 ELSE 0 END
+ 1 ) AS rank, + 1 ) AS rank,
'Payment Entry' as doctype, 'Payment Entry' as doctype,
name, name,
paid_amount, paid_amount,
reference_no, reference_no,
reference_date, reference_date,
party, party,
party_type, party_type,
posting_date, posting_date,
{currency_field} {currency_field}
FROM FROM
`tabPayment Entry` `tabPayment Entry`
WHERE WHERE
paid_amount {amount_condition} %(amount)s paid_amount {amount_condition} %(amount)s
AND docstatus = 1 AND docstatus = 1
AND payment_type IN (%(payment_type)s, 'Internal Transfer') AND payment_type IN (%(payment_type)s, 'Internal Transfer')
AND ifnull(clearance_date, '') = "" AND ifnull(clearance_date, '') = ""
AND {account_from_to} = %(bank_account)s AND {account_from_to} = %(bank_account)s
{filter_by_date}
{filter_by_reference_no}
order by{order_by}
""" """
def get_je_matching_query(amount_condition, transaction): def get_je_matching_query(
amount_condition,
transaction,
from_date,
to_date,
filter_by_reference_date,
from_reference_date,
to_reference_date,
):
# get matching journal entry query # get matching journal entry query
# We have mapping at the bank level # We have mapping at the bank level
# So one bank could have both types of bank accounts like asset and liability # So one bank could have both types of bank accounts like asset and liability
# So cr_or_dr should be judged only on basis of withdrawal and deposit and not account type # So cr_or_dr should be judged only on basis of withdrawal and deposit and not account type
cr_or_dr = "credit" if transaction.withdrawal > 0 else "debit" cr_or_dr = "credit" if transaction.withdrawal > 0 else "debit"
filter_by_date = f"AND je.posting_date between '{from_date}' and '{to_date}'"
order_by = " je.posting_date"
filter_by_reference_no = ""
if cint(filter_by_reference_date):
filter_by_date = f"AND je.cheque_date between '{from_reference_date}' and '{to_reference_date}'"
order_by = " je.cheque_date"
if frappe.flags.auto_reconcile_vouchers == True:
filter_by_reference_no = f"AND je.cheque_no = '{transaction.reference_number}'"
return f""" return f"""
SELECT SELECT
(CASE WHEN je.cheque_no=%(reference_no)s THEN 1 ELSE 0 END (CASE WHEN je.cheque_no=%(reference_no)s THEN 1 ELSE 0 END
+ 1) AS rank , + 1) AS rank ,
@ -568,6 +756,9 @@ def get_je_matching_query(amount_condition, transaction):
AND jea.account = %(bank_account)s AND jea.account = %(bank_account)s
AND jea.{cr_or_dr}_in_account_currency {amount_condition} %(amount)s AND jea.{cr_or_dr}_in_account_currency {amount_condition} %(amount)s
AND je.docstatus = 1 AND je.docstatus = 1
{filter_by_date}
{filter_by_reference_no}
order by {order_by}
""" """

View File

@ -5,6 +5,7 @@ import json
import unittest import unittest
import frappe import frappe
from frappe import utils
from frappe.tests.utils import FrappeTestCase from frappe.tests.utils import FrappeTestCase
from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import ( from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import (
@ -40,7 +41,12 @@ class TestBankTransaction(FrappeTestCase):
"Bank Transaction", "Bank Transaction",
dict(description="Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic"), dict(description="Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic"),
) )
linked_payments = get_linked_payments(bank_transaction.name, ["payment_entry", "exact_match"]) linked_payments = get_linked_payments(
bank_transaction.name,
["payment_entry", "exact_match"],
from_date=bank_transaction.date,
to_date=utils.today(),
)
self.assertTrue(linked_payments[0][6] == "Conrad Electronic") self.assertTrue(linked_payments[0][6] == "Conrad Electronic")
# This test validates a simple reconciliation leading to the clearance of the bank transaction and the payment # This test validates a simple reconciliation leading to the clearance of the bank transaction and the payment
@ -81,7 +87,12 @@ class TestBankTransaction(FrappeTestCase):
"Bank Transaction", "Bank Transaction",
dict(description="Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07"), dict(description="Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07"),
) )
linked_payments = get_linked_payments(bank_transaction.name, ["payment_entry", "exact_match"]) linked_payments = get_linked_payments(
bank_transaction.name,
["payment_entry", "exact_match"],
from_date=bank_transaction.date,
to_date=utils.today(),
)
self.assertTrue(linked_payments[0][3]) self.assertTrue(linked_payments[0][3])
# Check error if already reconciled # Check error if already reconciled

View File

@ -219,20 +219,16 @@ class PurchaseOrder(BuyingController):
else: else:
if not frappe.get_value("Item", item.fg_item, "is_sub_contracted_item"): if not frappe.get_value("Item", item.fg_item, "is_sub_contracted_item"):
frappe.throw( frappe.throw(
_( _("Row #{0}: Finished Good Item {1} must be a sub-contracted item").format(
"Row #{0}: Finished Good Item {1} must be a sub-contracted item for service item {2}" item.idx, item.fg_item
).format(item.idx, item.fg_item, item.item_code) )
) )
elif not frappe.get_value("Item", item.fg_item, "default_bom"): elif not frappe.get_value("Item", item.fg_item, "default_bom"):
frappe.throw( frappe.throw(
_("Row #{0}: Default BOM not found for FG Item {1}").format(item.idx, item.fg_item) _("Row #{0}: Default BOM not found for FG Item {1}").format(item.idx, item.fg_item)
) )
if not item.fg_item_qty: if not item.fg_item_qty:
frappe.throw( frappe.throw(_("Row #{0}: Finished Good Item Qty can not be zero").format(item.idx))
_("Row #{0}: Finished Good Item Qty is not specified for service item {0}").format(
item.idx, item.item_code
)
)
else: else:
for item in self.items: for item in self.items:
item.set("fg_item", None) item.set("fg_item", None)

View File

@ -74,24 +74,25 @@ class SubcontractingController(StockController):
) )
if not is_stock_item: if not is_stock_item:
msg = f"Item {item.item_name} must be a stock item." frappe.throw(_("Row {0}: Item {1} must be a stock item.").format(item.idx, item.item_name))
frappe.throw(_(msg))
if not is_sub_contracted_item: if not is_sub_contracted_item:
msg = f"Item {item.item_name} must be a subcontracted item." frappe.throw(
frappe.throw(_(msg)) _("Row {0}: Item {1} must be a subcontracted item.").format(item.idx, item.item_name)
)
if item.bom: if item.bom:
bom = frappe.get_doc("BOM", item.bom) bom = frappe.get_doc("BOM", item.bom)
if not bom.is_active: if not bom.is_active:
msg = f"Please select an active BOM for Item {item.item_name}." frappe.throw(
frappe.throw(_(msg)) _("Row {0}: Please select an active BOM for Item {1}.").format(item.idx, item.item_name)
)
if bom.item != item.item_code: if bom.item != item.item_code:
msg = f"Please select an valid BOM for Item {item.item_name}." frappe.throw(
frappe.throw(_(msg)) _("Row {0}: Please select an valid BOM for Item {1}.").format(item.idx, item.item_name)
)
else: else:
msg = f"Please select a BOM for Item {item.item_name}." frappe.throw(_("Row {0}: Please select a BOM for Item {1}.").format(item.idx, item.item_name))
frappe.throw(_(msg))
def __get_data_before_save(self): def __get_data_before_save(self):
item_dict = {} item_dict = {}

View File

@ -5,7 +5,12 @@ erpnext.accounts.bank_reconciliation.DataTableManager = class DataTableManager {
Object.assign(this, opts); Object.assign(this, opts);
this.dialog_manager = new erpnext.accounts.bank_reconciliation.DialogManager( this.dialog_manager = new erpnext.accounts.bank_reconciliation.DialogManager(
this.company, this.company,
this.bank_account this.bank_account,
this.bank_statement_from_date,
this.bank_statement_to_date,
this.filter_by_reference_date,
this.from_reference_date,
this.to_reference_date
); );
this.make_dt(); this.make_dt();
} }
@ -17,6 +22,8 @@ erpnext.accounts.bank_reconciliation.DataTableManager = class DataTableManager {
"erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_bank_transactions", "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.get_bank_transactions",
args: { args: {
bank_account: this.bank_account, bank_account: this.bank_account,
from_date: this.bank_statement_from_date,
to_date: this.bank_statement_to_date
}, },
callback: function (response) { callback: function (response) {
me.format_data(response.message); me.format_data(response.message);

View File

@ -5,8 +5,12 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager {
this.bank_account = bank_account; this.bank_account = bank_account;
this.company = company; this.company = company;
this.make_dialog(); this.make_dialog();
this.bank_statement_from_date = bank_statement_from_date;
this.bank_statement_to_date = bank_statement_to_date;
this.filter_by_reference_date = filter_by_reference_date;
this.from_reference_date = from_reference_date;
this.to_reference_date = to_reference_date;
} }
show_dialog(bank_transaction_name, update_dt_cards) { show_dialog(bank_transaction_name, update_dt_cards) {
this.bank_transaction_name = bank_transaction_name; this.bank_transaction_name = bank_transaction_name;
this.update_dt_cards = update_dt_cards; this.update_dt_cards = update_dt_cards;
@ -35,13 +39,13 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager {
if (r.message) { if (r.message) {
this.bank_transaction = r.message; this.bank_transaction = r.message;
r.message.payment_entry = 1; r.message.payment_entry = 1;
r.message.journal_entry = 1;
this.dialog.set_values(r.message); this.dialog.set_values(r.message);
this.dialog.show(); this.dialog.show();
} }
}, },
}); });
} }
get_linked_vouchers(document_types) { get_linked_vouchers(document_types) {
frappe.call({ frappe.call({
method: method:
@ -49,6 +53,11 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager {
args: { args: {
bank_transaction_name: this.bank_transaction_name, bank_transaction_name: this.bank_transaction_name,
document_types: document_types, document_types: document_types,
from_date: this.bank_statement_from_date,
to_date: this.bank_statement_to_date,
filter_by_reference_date: this.filter_by_reference_date,
from_reference_date:this.from_reference_date,
to_reference_date:this.to_reference_date
}, },
callback: (result) => { callback: (result) => {
@ -66,6 +75,7 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager {
row[1], row[1],
row[2], row[2],
reference_date, reference_date,
row[8],
format_currency(row[3], row[9]), format_currency(row[3], row[9]),
row[6], row[6],
row[4], row[4],
@ -101,6 +111,11 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager {
editable: false, editable: false,
width: 120, width: 120,
}, },
{
name: "Posting Date",
editable: false,
width: 120,
},
{ {
name: __("Amount"), name: __("Amount"),
editable: false, editable: false,
@ -578,4 +593,4 @@ erpnext.accounts.bank_reconciliation.DialogManager = class DialogManager {
} }
} }
}; };

View File

@ -638,6 +638,7 @@
"width": "70px" "width": "70px"
}, },
{ {
"allow_on_submit": 1,
"fieldname": "ordered_qty", "fieldname": "ordered_qty",
"fieldtype": "Float", "fieldtype": "Float",
"label": "Ordered Qty", "label": "Ordered Qty",
@ -864,7 +865,7 @@
"idx": 1, "idx": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2022-12-25 02:51:10.247569", "modified": "2023-01-12 13:13:28.691585",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Selling", "module": "Selling",
"name": "Sales Order Item", "name": "Sales Order Item",

View File

@ -230,7 +230,8 @@ class PickList(Document):
frappe.throw(_("Qty of Finished Goods Item should be greater than 0.")) frappe.throw(_("Qty of Finished Goods Item should be greater than 0."))
def before_print(self, settings=None): def before_print(self, settings=None):
self.group_similar_items() if self.group_same_items:
self.group_similar_items()
def group_similar_items(self): def group_similar_items(self):
group_item_qty = defaultdict(float) group_item_qty = defaultdict(float)

View File

@ -445,6 +445,20 @@ class TestPickList(FrappeTestCase):
pl.before_print() pl.before_print()
self.assertEqual(len(pl.locations), 4) self.assertEqual(len(pl.locations), 4)
# grouping should not happen if group_same_items is False
pl = frappe.get_doc(
doctype="Pick List",
group_same_items=False,
locations=[
_dict(item_code="A", warehouse="X", qty=5, picked_qty=1),
_dict(item_code="B", warehouse="Y", qty=4, picked_qty=2),
_dict(item_code="A", warehouse="X", qty=3, picked_qty=2),
_dict(item_code="B", warehouse="Y", qty=2, picked_qty=2),
],
)
pl.before_print()
self.assertEqual(len(pl.locations), 4)
# grouping should halve the number of items # grouping should halve the number of items
pl = frappe.get_doc( pl = frappe.get_doc(
doctype="Pick List", doctype="Pick List",

View File

@ -41,7 +41,7 @@ def get_data(report_filters):
key = (d.voucher_type, d.voucher_no) key = (d.voucher_type, d.voucher_no)
gl_data = voucher_wise_gl_data.get(key) or {} gl_data = voucher_wise_gl_data.get(key) or {}
d.account_value = gl_data.get("account_value", 0) d.account_value = gl_data.get("account_value", 0)
d.difference_value = abs(d.stock_value - d.account_value) d.difference_value = d.stock_value - d.account_value
if abs(d.difference_value) > 0.1: if abs(d.difference_value) > 0.1:
data.append(d) data.append(d)

View File

@ -57,6 +57,7 @@ class SubcontractingReceipt(SubcontractingController):
def before_validate(self): def before_validate(self):
super(SubcontractingReceipt, self).before_validate() super(SubcontractingReceipt, self).before_validate()
self.validate_items_qty()
self.set_items_bom() self.set_items_bom()
self.set_items_cost_center() self.set_items_cost_center()
self.set_items_expense_account() self.set_items_expense_account()
@ -157,7 +158,7 @@ class SubcontractingReceipt(SubcontractingController):
total_qty = total_amount = 0 total_qty = total_amount = 0
for item in self.items: for item in self.items:
if item.name in rm_supp_cost: if item.qty and item.name in rm_supp_cost:
item.rm_supp_cost = rm_supp_cost[item.name] item.rm_supp_cost = rm_supp_cost[item.name]
item.rm_cost_per_qty = item.rm_supp_cost / item.qty item.rm_cost_per_qty = item.rm_supp_cost / item.qty
rm_supp_cost.pop(item.name) rm_supp_cost.pop(item.name)
@ -194,6 +195,13 @@ class SubcontractingReceipt(SubcontractingController):
).format(item.idx) ).format(item.idx)
) )
def validate_items_qty(self):
for item in self.items:
if not (item.qty or item.rejected_qty):
frappe.throw(
_("Row {0}: Accepted Qty and Rejected Qty can't be zero at the same time.").format(item.idx)
)
def set_items_bom(self): def set_items_bom(self):
if self.is_return: if self.is_return:
for item in self.items: for item in self.items: