Merge branch 'develop' into new-sql-functions-syntax-qb-query

This commit is contained in:
mergify[bot]
2025-06-23 11:03:08 +00:00
committed by GitHub
16 changed files with 1541 additions and 1032 deletions

View File

@@ -70,7 +70,7 @@ frappe.ui.form.on("Bank Statement Import", {
frm.get_field("import_file").df.options = { frm.get_field("import_file").df.options = {
restrictions: { restrictions: {
allowed_file_types: [".csv", ".xls", ".xlsx"], allowed_file_types: [".csv", ".xls", ".xlsx", ".TXT", ".txt"],
}, },
}; };
@@ -81,6 +81,7 @@ frappe.ui.form.on("Bank Statement Import", {
refresh(frm) { refresh(frm) {
frm.page.hide_icon_group(); frm.page.hide_icon_group();
frm.trigger("toggle_mt940_note");
frm.trigger("update_indicators"); frm.trigger("update_indicators");
frm.trigger("import_file"); frm.trigger("import_file");
frm.trigger("show_import_log"); frm.trigger("show_import_log");
@@ -192,6 +193,24 @@ frappe.ui.form.on("Bank Statement Import", {
}); });
}, },
import_mt940_fromat(frm) {
frm.trigger("toggle_mt940_note");
frm.save();
},
toggle_mt940_note(frm) {
if (!frm.doc.import_mt940_fromat) {
frm.set_df_property("custom_delimiters", "hidden", 0);
frm.set_df_property("google_sheets_url", "hidden", 0);
frm.set_df_property("html_5", "hidden", 0);
} else {
frm.set_df_property("custom_delimiters", "hidden", 1);
frm.set_df_property("google_sheets_url", "hidden", 1);
frm.set_df_property("html_5", "hidden", 1);
}
frm.set_value("import_mt940_fromat", frm.doc.import_mt940_fromat);
},
show_report_error_button(frm) { show_report_error_button(frm) {
if (frm.doc.status === "Error") { if (frm.doc.status === "Error") {
frappe.db frappe.db
@@ -290,23 +309,45 @@ frappe.ui.form.on("Bank Statement Import", {
.html(__("Loading import file...")) .html(__("Loading import file..."))
.appendTo(frm.get_field("import_preview").$wrapper); .appendTo(frm.get_field("import_preview").$wrapper);
frm.call({ frappe.run_serially([
method: "get_preview_from_template", // Convert MT940 to CSV if .txt file
args: { () => {
data_import: frm.doc.name, if (frm.doc.import_file && frm.doc.import_file.toLowerCase().endsWith(".txt")) {
import_file: frm.doc.import_file, return frm
google_sheets_url: frm.doc.google_sheets_url, .call({
method: "convert_mt940_to_csv",
args: {
data_import: frm.doc.name,
mt940_file_path: frm.doc.import_file,
},
})
.then((r) => {
const file_url = r.message;
frm.set_value("import_file", file_url);
frm.save();
});
}
}, },
error_handlers: { () => {
TimestampMismatchError() { frm.call({
// ignore this error method: "get_preview_from_template",
}, args: {
data_import: frm.doc.name,
import_file: frm.doc.import_file,
google_sheets_url: frm.doc.google_sheets_url,
},
error_handlers: {
TimestampMismatchError() {
// ignore this error
},
},
}).then((r) => {
let preview_data = r.message;
frm.events.show_import_preview(frm, preview_data);
frm.events.show_import_warnings(frm, preview_data);
});
}, },
}).then((r) => { ]);
let preview_data = r.message;
frm.events.show_import_preview(frm, preview_data);
frm.events.show_import_warnings(frm, preview_data);
});
}, },
// method: 'frappe.core.doctype.data_import.data_import.get_preview_from_template', // method: 'frappe.core.doctype.data_import.data_import.get_preview_from_template',

View File

@@ -11,6 +11,7 @@
"bank_account", "bank_account",
"bank", "bank",
"column_break_4", "column_break_4",
"import_mt940_fromat",
"custom_delimiters", "custom_delimiters",
"delimiter_options", "delimiter_options",
"google_sheets_url", "google_sheets_url",
@@ -20,6 +21,7 @@
"download_template", "download_template",
"status", "status",
"template_options", "template_options",
"use_csv_sniffer",
"import_warnings_section", "import_warnings_section",
"template_warnings", "template_warnings",
"import_warnings", "import_warnings",
@@ -207,14 +209,28 @@
"fieldname": "delimiter_options", "fieldname": "delimiter_options",
"fieldtype": "Data", "fieldtype": "Data",
"label": "Delimiter options" "label": "Delimiter options"
},
{
"default": "0",
"fieldname": "use_csv_sniffer",
"fieldtype": "Check",
"hidden": 1,
"label": "Use CSV Sniffer"
},
{
"default": "0",
"fieldname": "import_mt940_fromat",
"fieldtype": "Check",
"label": "Import MT940 Fromat"
} }
], ],
"hide_toolbar": 1, "hide_toolbar": 1,
"links": [], "links": [],
"modified": "2024-06-25 17:32:07.658250", "modified": "2025-06-11 02:23:22.159961",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Bank Statement Import", "name": "Bank Statement Import",
"naming_rule": "Expression",
"owner": "Administrator", "owner": "Administrator",
"permissions": [ "permissions": [
{ {
@@ -230,6 +246,7 @@
"write": 1 "write": 1
} }
], ],
"row_format": "Dynamic",
"sort_field": "creation", "sort_field": "creation",
"sort_order": "DESC", "sort_order": "DESC",
"states": [], "states": [],

View File

@@ -3,15 +3,19 @@
import csv import csv
import io
import json import json
import re import re
from datetime import date, datetime
import frappe import frappe
import mt940
import openpyxl import openpyxl
from frappe import _ from frappe import _
from frappe.core.doctype.data_import.data_import import DataImport from frappe.core.doctype.data_import.data_import import DataImport
from frappe.core.doctype.data_import.importer import Importer, ImportFile from frappe.core.doctype.data_import.importer import Importer, ImportFile
from frappe.utils.background_jobs import enqueue from frappe.utils.background_jobs import enqueue
from frappe.utils.file_manager import get_file, save_file
from frappe.utils.xlsxutils import ILLEGAL_CHARACTERS_RE, handle_html from frappe.utils.xlsxutils import ILLEGAL_CHARACTERS_RE, handle_html
from openpyxl.styles import Font from openpyxl.styles import Font
from openpyxl.utils import get_column_letter from openpyxl.utils import get_column_letter
@@ -35,6 +39,7 @@ class BankStatementImport(DataImport):
delimiter_options: DF.Data | None delimiter_options: DF.Data | None
google_sheets_url: DF.Data | None google_sheets_url: DF.Data | None
import_file: DF.Attach | None import_file: DF.Attach | None
import_mt940_fromat: DF.Check
import_type: DF.Literal["", "Insert New Records", "Update Existing Records"] import_type: DF.Literal["", "Insert New Records", "Update Existing Records"]
mute_emails: DF.Check mute_emails: DF.Check
reference_doctype: DF.Link reference_doctype: DF.Link
@@ -43,6 +48,7 @@ class BankStatementImport(DataImport):
submit_after_import: DF.Check submit_after_import: DF.Check
template_options: DF.Code | None template_options: DF.Code | None
template_warnings: DF.Code | None template_warnings: DF.Code | None
use_csv_sniffer: DF.Check
# end: auto-generated types # end: auto-generated types
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
@@ -65,8 +71,9 @@ class BankStatementImport(DataImport):
self.template_warnings = "" self.template_warnings = ""
self.validate_import_file() if self.import_file and not self.import_file.lower().endswith(".txt"):
self.validate_google_sheets_url() self.validate_import_file()
self.validate_google_sheets_url()
def start_import(self): def start_import(self):
preview = frappe.get_doc("Bank Statement Import", self.name).get_preview_from_template( preview = frappe.get_doc("Bank Statement Import", self.name).get_preview_from_template(
@@ -104,6 +111,68 @@ class BankStatementImport(DataImport):
return None return None
@frappe.whitelist()
def convert_mt940_to_csv(data_import, mt940_file_path):
doc = frappe.get_doc("Bank Statement Import", data_import)
file_doc, content = get_file(mt940_file_path)
if not is_mt940_format(content):
frappe.throw(_("The uploaded file does not appear to be in valid MT940 format."))
if is_mt940_format(content) and not doc.import_mt940_fromat:
frappe.throw(_("MT940 file detected. Please enable 'Import MT940 Format' to proceed."))
try:
transactions = mt940.parse(content)
except Exception as e:
frappe.throw(_("Failed to parse MT940 format. Error: {0}").format(str(e)))
if not transactions:
frappe.throw(_("Parsed file is not in valid MT940 format or contains no transactions."))
# Use in-memory file buffer instead of writing to temp file
csv_buffer = io.StringIO()
writer = csv.writer(csv_buffer)
headers = ["Date", "Deposit", "Withdrawal", "Description", "Reference Number", "Bank Account", "Currency"]
writer.writerow(headers)
for txn in transactions:
txn_date = getattr(txn, "date", None)
raw_date = txn.data.get("date", "")
if txn_date:
date_str = txn_date.strftime("%Y-%m-%d")
elif isinstance(raw_date, date | datetime):
date_str = raw_date.strftime("%Y-%m-%d")
else:
date_str = str(raw_date)
raw_amount = str(txn.data.get("amount", ""))
parts = raw_amount.strip().split()
amount_value = float(parts[0]) if parts else 0.0
deposit = amount_value if amount_value > 0 else ""
withdrawal = abs(amount_value) if amount_value < 0 else ""
description = txn.data.get("extra_details") or ""
reference = txn.data.get("transaction_reference") or ""
currency = txn.data.get("currency", "")
writer.writerow([date_str, deposit, withdrawal, description, reference, doc.bank_account, currency])
# Prepare in-memory CSV for upload
csv_content = csv_buffer.getvalue().encode("utf-8")
csv_buffer.close()
filename = f"{frappe.utils.now_datetime().strftime('%Y%m%d%H%M%S')}_converted_mt940.csv"
# Save to File Manager
saved_file = save_file(filename, csv_content, doc.doctype, doc.name, is_private=True, df="import_file")
return saved_file.file_url
@frappe.whitelist() @frappe.whitelist()
def get_preview_from_template(data_import, import_file=None, google_sheets_url=None): def get_preview_from_template(data_import, import_file=None, google_sheets_url=None):
return frappe.get_doc("Bank Statement Import", data_import).get_preview_from_template( return frappe.get_doc("Bank Statement Import", data_import).get_preview_from_template(
@@ -128,6 +197,12 @@ def download_import_log(data_import_name):
return frappe.get_doc("Bank Statement Import", data_import_name).download_import_log() return frappe.get_doc("Bank Statement Import", data_import_name).download_import_log()
def is_mt940_format(content: str) -> bool:
"""Check if the content has key MT940 tags"""
required_tags = [":20:", ":25:", ":28C:", ":61:"]
return all(tag in content for tag in required_tags)
def parse_data_from_template(raw_data): def parse_data_from_template(raw_data):
data = [] data = []

View File

@@ -20,6 +20,39 @@ frappe.ui.form.on("Journal Entry", {
"Unreconcile Payment Entries", "Unreconcile Payment Entries",
"Bank Transaction", "Bank Transaction",
]; ];
frm.trigger("set_queries");
},
set_queries(frm) {
frm.set_query("periodic_entry_difference_account", function () {
return {
filters: {
is_group: 0,
company: frm.doc.company,
},
};
});
frm.set_query("stock_asset_account", function () {
return {
filters: {
is_group: 0,
account_type: "Stock",
company: frm.doc.company,
},
};
});
},
get_balance_for_periodic_accounting(frm) {
frm.call({
method: "get_balance_for_periodic_accounting",
doc: frm.doc,
callback: function (r) {
refresh_field("accounts");
},
});
}, },
refresh: function (frm) { refresh: function (frm) {

View File

@@ -13,15 +13,21 @@
"title", "title",
"voucher_type", "voucher_type",
"naming_series", "naming_series",
"finance_book",
"process_deferred_accounting", "process_deferred_accounting",
"reversal_of", "reversal_of",
"tax_withholding_category",
"column_break1", "column_break1",
"from_template", "from_template",
"company", "company",
"posting_date", "posting_date",
"finance_book",
"apply_tds", "apply_tds",
"tax_withholding_category",
"section_break_tcvw",
"for_all_stock_asset_accounts",
"column_break_wpau",
"stock_asset_account",
"periodic_entry_difference_account",
"get_balance_for_periodic_accounting",
"2_add_edit_gl_entries", "2_add_edit_gl_entries",
"accounts", "accounts",
"section_break99", "section_break99",
@@ -89,7 +95,7 @@
"label": "Entry Type", "label": "Entry Type",
"oldfieldname": "voucher_type", "oldfieldname": "voucher_type",
"oldfieldtype": "Select", "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\nAsset Disposal\nExchange Rate Revaluation\nExchange Gain Or Loss\nDeferred Revenue\nDeferred Expense", "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\nAsset Disposal\nPeriodic Accounting Entry\nExchange Rate Revaluation\nExchange Gain Or Loss\nDeferred Revenue\nDeferred Expense",
"reqd": 1, "reqd": 1,
"search_index": 1 "search_index": 1
}, },
@@ -543,6 +549,42 @@
"label": "Is System Generated", "label": "Is System Generated",
"no_copy": 1, "no_copy": 1,
"read_only": 1 "read_only": 1
},
{
"depends_on": "eval:doc.voucher_type === \"Periodic Accounting Entry\"",
"fieldname": "periodic_entry_difference_account",
"fieldtype": "Link",
"label": "Periodic Entry Difference Account",
"mandatory_depends_on": "eval:doc.voucher_type === \"Periodic Accounting Entry\"",
"options": "Account"
},
{
"depends_on": "eval:doc.voucher_type === \"Periodic Accounting Entry\"",
"fieldname": "section_break_tcvw",
"fieldtype": "Section Break",
"label": "Periodic Accounting"
},
{
"default": "1",
"fieldname": "for_all_stock_asset_accounts",
"fieldtype": "Check",
"label": "For All Stock Asset Accounts"
},
{
"depends_on": "eval:doc.for_all_stock_asset_accounts === 0",
"fieldname": "stock_asset_account",
"fieldtype": "Link",
"label": "Stock Asset Account",
"options": "Account"
},
{
"fieldname": "column_break_wpau",
"fieldtype": "Column Break"
},
{
"fieldname": "get_balance_for_periodic_accounting",
"fieldtype": "Button",
"label": "Get Balance"
} }
], ],
"icon": "fa fa-file-text", "icon": "fa fa-file-text",
@@ -557,7 +599,7 @@
"table_fieldname": "payment_entries" "table_fieldname": "payment_entries"
} }
], ],
"modified": "2024-12-26 15:32:20.730666", "modified": "2025-06-17 15:18:13.322681",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Journal Entry", "name": "Journal Entry",
@@ -602,6 +644,7 @@
"role": "Auditor" "role": "Auditor"
} }
], ],
"row_format": "Dynamic",
"search_fields": "voucher_type,posting_date, due_date, cheque_no", "search_fields": "voucher_type,posting_date, due_date, cheque_no",
"sort_field": "creation", "sort_field": "creation",
"sort_order": "DESC", "sort_order": "DESC",

View File

@@ -62,6 +62,7 @@ class JournalEntry(AccountsController):
difference: DF.Currency difference: DF.Currency
due_date: DF.Date | None due_date: DF.Date | None
finance_book: DF.Link | None finance_book: DF.Link | None
for_all_stock_asset_accounts: DF.Check
from_template: DF.Link | None from_template: DF.Link | None
inter_company_journal_entry_reference: DF.Link | None inter_company_journal_entry_reference: DF.Link | None
is_opening: DF.Literal["No", "Yes"] is_opening: DF.Literal["No", "Yes"]
@@ -73,11 +74,13 @@ class JournalEntry(AccountsController):
paid_loan: DF.Data | None paid_loan: DF.Data | None
pay_to_recd_from: DF.Data | None pay_to_recd_from: DF.Data | None
payment_order: DF.Link | None payment_order: DF.Link | None
periodic_entry_difference_account: DF.Link | None
posting_date: DF.Date posting_date: DF.Date
process_deferred_accounting: DF.Link | None process_deferred_accounting: DF.Link | None
remark: DF.SmallText | None remark: DF.SmallText | None
reversal_of: DF.Link | None reversal_of: DF.Link | None
select_print_heading: DF.Link | None select_print_heading: DF.Link | None
stock_asset_account: DF.Link | None
stock_entry: DF.Link | None stock_entry: DF.Link | None
tax_withholding_category: DF.Link | None tax_withholding_category: DF.Link | None
title: DF.Data | None title: DF.Data | None
@@ -101,6 +104,7 @@ class JournalEntry(AccountsController):
"Opening Entry", "Opening Entry",
"Depreciation Entry", "Depreciation Entry",
"Asset Disposal", "Asset Disposal",
"Periodic Accounting Entry",
"Exchange Rate Revaluation", "Exchange Rate Revaluation",
"Exchange Gain Or Loss", "Exchange Gain Or Loss",
"Deferred Revenue", "Deferred Revenue",
@@ -198,6 +202,76 @@ class JournalEntry(AccountsController):
self.update_inter_company_jv() self.update_inter_company_jv()
self.update_invoice_discounting() self.update_invoice_discounting()
@frappe.whitelist()
def get_balance_for_periodic_accounting(self):
self.validate_company_for_periodic_accounting()
stock_accounts = self.get_stock_accounts_for_periodic_accounting()
self.set("accounts", [])
for account in stock_accounts:
account_bal, stock_bal, warehouse_list = get_stock_and_account_balance(
account, self.posting_date, self.company
)
difference_value = flt(stock_bal - account_bal, self.precision("difference"))
if difference_value == 0:
frappe.msgprint(
_("No difference found for stock account {0}").format(frappe.bold(account)),
alert=True,
)
continue
self.append(
"accounts",
{
"account": account,
"debit_in_account_currency": difference_value if difference_value > 0 else 0,
"credit_in_account_currency": abs(difference_value) if difference_value < 0 else 0,
},
)
self.append(
"accounts",
{
"account": self.periodic_entry_difference_account,
"credit_in_account_currency": difference_value if difference_value > 0 else 0,
"debit_in_account_currency": abs(difference_value) if difference_value < 0 else 0,
},
)
def validate_company_for_periodic_accounting(self):
if erpnext.is_perpetual_inventory_enabled(self.company):
frappe.throw(
_(
"Periodic Accounting Entry is not allowed for company {0} with perpetual inventory enabled"
).format(self.company)
)
if not self.periodic_entry_difference_account:
frappe.throw(_("Please select Periodic Accounting Entry Difference Account"))
def get_stock_accounts_for_periodic_accounting(self):
if self.voucher_type != "Periodic Accounting Entry":
return []
if self.for_all_stock_asset_accounts:
return frappe.get_all(
"Account",
filters={
"company": self.company,
"account_type": "Stock",
"root_type": "Asset",
"is_group": 0,
},
pluck="name",
)
if not self.stock_asset_account:
frappe.throw(_("Please select Stock Asset Account"))
return [self.stock_asset_account]
def on_update_after_submit(self): def on_update_after_submit(self):
# Flag will be set on Reconciliation # Flag will be set on Reconciliation
# Reconciliation tool will anyways repost ledger entries. So, no need to check and do implicit repost. # Reconciliation tool will anyways repost ledger entries. So, no need to check and do implicit repost.
@@ -280,6 +354,10 @@ class JournalEntry(AccountsController):
frappe.throw(_("Account {0} should be of type Expense").format(d.account)) frappe.throw(_("Account {0} should be of type Expense").format(d.account))
def validate_stock_accounts(self): def validate_stock_accounts(self):
if self.voucher_type == "Periodic Accounting Entry":
# Skip validation for periodic accounting entry
return
stock_accounts = get_stock_accounts(self.company, accounts=self.accounts) stock_accounts = get_stock_accounts(self.company, accounts=self.accounts)
for account in stock_accounts: for account in stock_accounts:
account_bal, stock_bal, warehouse_list = get_stock_and_account_balance( account_bal, stock_bal, warehouse_list = get_stock_and_account_balance(

View File

@@ -788,29 +788,28 @@ class TestPaymentRequest(IntegrationTestCase):
pr = make_payment_request(dt="Sales Invoice", dn=si.name, mute_email=1) pr = make_payment_request(dt="Sales Invoice", dn=si.name, mute_email=1)
self.assertEqual(pr.grand_total, si.outstanding_amount) self.assertEqual(pr.grand_total, si.outstanding_amount)
def test_partial_paid_invoice_with_submitted_payment_entry(self):
pi = make_purchase_invoice(currency="INR", qty=1, rate=5000)
pi.save()
pi.submit()
def test_partial_paid_invoice_with_submitted_payment_entry(self): pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
pi = make_purchase_invoice(currency="INR", qty=1, rate=5000) pe.reference_no = "PURINV0001"
pi.save() pe.reference_date = frappe.utils.nowdate()
pi.submit() pe.paid_amount = 2500
pe.references[0].allocated_amount = 2500
pe.save()
pe.submit()
pe.cancel()
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC") pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
pe.reference_no = "PURINV0001" pe.reference_no = "PURINV0002"
pe.reference_date = frappe.utils.nowdate() pe.reference_date = frappe.utils.nowdate()
pe.paid_amount = 2500 pe.paid_amount = 2500
pe.references[0].allocated_amount = 2500 pe.references[0].allocated_amount = 2500
pe.save() pe.save()
pe.submit() pe.submit()
pe.cancel()
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC") pi.load_from_db()
pe.reference_no = "PURINV0002" pr = make_payment_request(dt="Purchase Invoice", dn=pi.name, mute_email=1)
pe.reference_date = frappe.utils.nowdate() self.assertEqual(pr.grand_total, pi.outstanding_amount)
pe.paid_amount = 2500
pe.references[0].allocated_amount = 2500
pe.save()
pe.submit()
pi.load_from_db()
pr = make_payment_request(dt="Purchase Invoice", dn=pi.name, mute_email=1)
self.assertEqual(pr.grand_total, pi.outstanding_amount)

View File

@@ -24,7 +24,7 @@ def get_chart_data(data, conditions, filters):
datapoints = [] datapoints = []
start = 2 if filters.get("based_on") in ["Item", "Supplier"] else 1 start = 3 if filters.get("based_on") in ["Item", "Supplier"] else 1
if filters.get("group_by"): if filters.get("group_by"):
start += 1 start += 1

View File

@@ -98,9 +98,10 @@ def get_data(filters, conditions):
sel_col = "t1.supplier" sel_col = "t1.supplier"
if filters.get("based_on") in ["Item", "Customer", "Supplier"]: if filters.get("based_on") in ["Item", "Customer", "Supplier"]:
inc = 2 inc = 3
else: else:
inc = 1 inc = 1
data1 = frappe.db.sql( data1 = frappe.db.sql(
""" select {} from `tab{}` t1, `tab{} Item` t2 {} """ select {} from `tab{}` t1, `tab{} Item` t2 {}
where t2.parent = t1.name and t1.company = {} and {} between {} and {} and where t2.parent = t1.name and t1.company = {} and {} between {} and {} and
@@ -330,11 +331,20 @@ def based_wise_columns_query(based_on, trans):
based_on_details["addl_tables"] = "" based_on_details["addl_tables"] = ""
elif based_on == "Customer": elif based_on == "Customer":
based_on_details["based_on_cols"] = [ if trans == "Quotation":
"Customer:Link/Customer:120", based_on_details["based_on_cols"] = [
"Territory:Link/Territory:120", "Party:Link/Customer:120",
] "Party Name:Data:120",
based_on_details["based_on_select"] = "t1.customer_name, t1.territory, " "Territory:Link/Territory:120",
]
based_on_details["based_on_select"] = "t1.party_name, t1.customer_name, t1.territory,"
else:
based_on_details["based_on_cols"] = [
"Customer:Link/Customer:120",
"Customer Name:Data:120",
"Territory:Link/Territory:120",
]
based_on_details["based_on_select"] = "t1.customer, t1.customer_name, t1.territory,"
based_on_details["based_on_group_by"] = "t1.party_name" if trans == "Quotation" else "t1.customer" based_on_details["based_on_group_by"] = "t1.party_name" if trans == "Quotation" else "t1.customer"
based_on_details["addl_tables"] = "" based_on_details["addl_tables"] = ""
@@ -347,9 +357,10 @@ def based_wise_columns_query(based_on, trans):
elif based_on == "Supplier": elif based_on == "Supplier":
based_on_details["based_on_cols"] = [ based_on_details["based_on_cols"] = [
"Supplier:Link/Supplier:120", "Supplier:Link/Supplier:120",
"Supplier Name:Data:120",
"Supplier Group:Link/Supplier Group:140", "Supplier Group:Link/Supplier Group:140",
] ]
based_on_details["based_on_select"] = "t1.supplier, t3.supplier_group," based_on_details["based_on_select"] = "t1.supplier, t1.supplier_name, t3.supplier_group,"
based_on_details["based_on_group_by"] = "t1.supplier" based_on_details["based_on_group_by"] = "t1.supplier"
based_on_details["addl_tables"] = ",`tabSupplier` t3" based_on_details["addl_tables"] = ",`tabSupplier` t3"
based_on_details["addl_tables_relational_cond"] = " and t1.supplier = t3.name" based_on_details["addl_tables_relational_cond"] = " and t1.supplier = t3.name"

File diff suppressed because it is too large Load Diff

View File

@@ -722,7 +722,7 @@ erpnext.utils.update_child_items = function (opts) {
} = r.message; } = r.message;
const row = dialog.fields_dict.trans_items.df.data.find( const row = dialog.fields_dict.trans_items.df.data.find(
(doc) => doc.idx == me.doc.idx (row) => row.name == me.doc.name
); );
if (row) { if (row) {
Object.assign(row, { Object.assign(row, {

View File

@@ -181,14 +181,20 @@ frappe.ui.form.on("Sales Order", {
} }
erpnext.queries.setup_queries(frm, "Warehouse", function () { erpnext.queries.setup_queries(frm, "Warehouse", function () {
return { return {
filters: [["Warehouse", "company", "in", ["", cstr(frm.doc.company)]]], filters: [
["Warehouse", "company", "in", ["", cstr(frm.doc.company)]],
["Warehouse", "is_group", "=", 0],
],
}; };
}); });
frm.set_query("warehouse", "items", function (doc, cdt, cdn) { frm.set_query("warehouse", "items", function (doc, cdt, cdn) {
let row = locals[cdt][cdn]; let row = locals[cdt][cdn];
let query = { let query = {
filters: [["Warehouse", "company", "in", ["", cstr(frm.doc.company)]]], filters: [
["Warehouse", "company", "in", ["", cstr(frm.doc.company)]],
["Warehouse", "is_group", "=", 0],
],
}; };
if (row.item_code) { if (row.item_code) {
query.query = "erpnext.controllers.queries.warehouse_query"; query.query = "erpnext.controllers.queries.warehouse_query";

View File

@@ -25,7 +25,7 @@ def get_chart_data(data, conditions, filters):
datapoints = [] datapoints = []
start = 2 if filters.get("based_on") in ["Item", "Customer"] else 1 start = 3 if filters.get("based_on") in ["Item", "Customer"] else 1
if filters.get("group_by"): if filters.get("group_by"):
start += 1 start += 1

View File

@@ -24,7 +24,7 @@ def get_chart_data(data, conditions, filters):
datapoints = [] datapoints = []
start = 2 if filters.get("based_on") in ["Item", "Customer"] else 1 start = 3 if filters.get("based_on") in ["Item", "Customer"] else 1
if filters.get("group_by"): if filters.get("group_by"):
start += 1 start += 1

View File

@@ -2016,6 +2016,75 @@ class TestStockEntry(IntegrationTestCase):
self.assertEqual(se.items[0].basic_rate, 300) self.assertEqual(se.items[0].basic_rate, 300)
def test_periodic_accounting_entries(self):
item_code = "_Test Periodic Accounting Item"
make_item(item_code, {"is_stock_item": 1})
company = "_Test Periodic Accounting Company"
frappe.get_doc(
{
"doctype": "Company",
"company_name": company,
"abbr": "_TPC",
"default_currency": "INR",
"enable_perpetual_inventory": 0,
}
).insert(ignore_permissions=True)
warehouse = frappe.db.get_value("Warehouse", {"company": company, "is_group": 0}, "name")
make_stock_entry(
item_code=item_code,
qty=10,
to_warehouse=warehouse,
basic_rate=100,
posting_date=add_days(nowdate(), -2),
)
jv = frappe.new_doc("Journal Entry")
jv.voucher_type = "Periodic Accounting Entry"
jv.posting_date = add_days(nowdate(), -1)
jv.posting_time = nowtime()
jv.company = company
jv.for_all_stock_asset_accounts = 1
jv.periodic_entry_difference_account = "Stock Adjustment - _TPC"
jv.get_balance_for_periodic_accounting()
jv.save()
jv.submit()
self.assertEqual(len(jv.accounts), 2)
self.assertEqual(jv.accounts[0].debit_in_account_currency, 1000)
self.assertEqual(jv.accounts[1].credit_in_account_currency, 1000)
self.assertEqual(jv.accounts[0].account, "Stock In Hand - _TPC")
self.assertEqual(jv.accounts[1].account, "Stock Adjustment - _TPC")
make_stock_entry(
item_code=item_code,
qty=5,
from_warehouse=warehouse,
company=company,
posting_date=nowdate(),
posting_time=nowtime(),
)
jv = frappe.new_doc("Journal Entry")
jv.voucher_type = "Periodic Accounting Entry"
jv.posting_date = nowdate()
jv.posting_time = nowtime()
jv.company = company
jv.for_all_stock_asset_accounts = 1
jv.periodic_entry_difference_account = "Stock Adjustment - _TPC"
jv.get_balance_for_periodic_accounting()
jv.save()
jv.submit()
self.assertEqual(len(jv.accounts), 2)
self.assertEqual(jv.accounts[0].credit_in_account_currency, 500)
self.assertEqual(jv.accounts[1].debit_in_account_currency, 500)
self.assertEqual(jv.accounts[0].account, "Stock In Hand - _TPC")
self.assertEqual(jv.accounts[1].account, "Stock Adjustment - _TPC")
def make_serialized_item(self, **args): def make_serialized_item(self, **args):
args = frappe._dict(args) args = frappe._dict(args)

View File

@@ -22,6 +22,9 @@ dependencies = [
# Not used directly - required by PyQRCode for PNG generation # Not used directly - required by PyQRCode for PNG generation
"pypng~=0.20220715.0", "pypng~=0.20220715.0",
# MT940 parser for bank statements
"mt-940>=4.26.0"
] ]
[build-system] [build-system]