mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-15 03:01:22 +00:00
Merge pull request #49423 from frappe/version-15-hotfix
chore: release v15
This commit is contained in:
@@ -142,8 +142,10 @@ def validate_expense_against_budget(args, expense_amount=0):
|
|||||||
if not frappe.get_all("Budget", limit=1):
|
if not frappe.get_all("Budget", limit=1):
|
||||||
return
|
return
|
||||||
|
|
||||||
if args.get("company") and not args.fiscal_year:
|
if not args.fiscal_year:
|
||||||
args.fiscal_year = get_fiscal_year(args.get("posting_date"), company=args.get("company"))[0]
|
args.fiscal_year = get_fiscal_year(args.get("posting_date"), company=args.get("company"))[0]
|
||||||
|
|
||||||
|
if args.get("company"):
|
||||||
frappe.flags.exception_approver_role = frappe.get_cached_value(
|
frappe.flags.exception_approver_role = frappe.get_cached_value(
|
||||||
"Company", args.get("company"), "exception_budget_approver_role"
|
"Company", args.get("company"), "exception_budget_approver_role"
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -134,7 +134,8 @@ class ExchangeRateRevaluation(Document):
|
|||||||
accounts = self.get_accounts_data()
|
accounts = self.get_accounts_data()
|
||||||
if accounts:
|
if accounts:
|
||||||
for acc in accounts:
|
for acc in accounts:
|
||||||
self.append("accounts", acc)
|
if acc.get("gain_loss"):
|
||||||
|
self.append("accounts", acc)
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_accounts_data(self):
|
def get_accounts_data(self):
|
||||||
|
|||||||
@@ -311,7 +311,7 @@ def validate_balance_type(account, adv_adj=False):
|
|||||||
if balance_must_be:
|
if balance_must_be:
|
||||||
balance = frappe.db.sql(
|
balance = frappe.db.sql(
|
||||||
"""select sum(debit) - sum(credit)
|
"""select sum(debit) - sum(credit)
|
||||||
from `tabGL Entry` where account = %s""",
|
from `tabGL Entry` where is_cancelled = 0 and account = %s""",
|
||||||
account,
|
account,
|
||||||
)[0][0]
|
)[0][0]
|
||||||
|
|
||||||
|
|||||||
@@ -75,6 +75,17 @@ class PeriodClosingVoucher(AccountsController):
|
|||||||
return
|
return
|
||||||
|
|
||||||
previous_fiscal_year_start_date = previous_fiscal_year[0][1]
|
previous_fiscal_year_start_date = previous_fiscal_year[0][1]
|
||||||
|
previous_fiscal_year_closed = frappe.db.exists(
|
||||||
|
"Period Closing Voucher",
|
||||||
|
{
|
||||||
|
"period_end_date": ("between", [previous_fiscal_year_start_date, last_year_closing]),
|
||||||
|
"docstatus": 1,
|
||||||
|
"company": self.company,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
if previous_fiscal_year_closed:
|
||||||
|
return
|
||||||
|
|
||||||
gle_exists_in_previous_year = frappe.db.exists(
|
gle_exists_in_previous_year = frappe.db.exists(
|
||||||
"GL Entry",
|
"GL Entry",
|
||||||
{
|
{
|
||||||
@@ -86,16 +97,7 @@ class PeriodClosingVoucher(AccountsController):
|
|||||||
if not gle_exists_in_previous_year:
|
if not gle_exists_in_previous_year:
|
||||||
return
|
return
|
||||||
|
|
||||||
previous_fiscal_year_closed = frappe.db.exists(
|
frappe.throw(_("Previous Year is not closed, please close it first"))
|
||||||
"Period Closing Voucher",
|
|
||||||
{
|
|
||||||
"period_end_date": ("between", [previous_fiscal_year_start_date, last_year_closing]),
|
|
||||||
"docstatus": 1,
|
|
||||||
"company": self.company,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
if not previous_fiscal_year_closed:
|
|
||||||
frappe.throw(_("Previous Year is not closed, please close it first"))
|
|
||||||
|
|
||||||
def block_if_future_closing_voucher_exists(self):
|
def block_if_future_closing_voucher_exists(self):
|
||||||
future_closing_voucher = self.get_future_closing_voucher()
|
future_closing_voucher = self.get_future_closing_voucher()
|
||||||
|
|||||||
@@ -668,7 +668,13 @@ class POSInvoice(SalesInvoice):
|
|||||||
"Account", self.debit_to, "account_currency"
|
"Account", self.debit_to, "account_currency"
|
||||||
)
|
)
|
||||||
if not self.due_date and self.customer:
|
if not self.due_date and self.customer:
|
||||||
self.due_date = get_due_date(self.posting_date, "Customer", self.customer, self.company)
|
self.due_date = get_due_date(
|
||||||
|
self.posting_date,
|
||||||
|
"Customer",
|
||||||
|
self.customer,
|
||||||
|
self.company,
|
||||||
|
template_name=self.payment_terms_template,
|
||||||
|
)
|
||||||
|
|
||||||
super(SalesInvoice, self).set_missing_values(for_validate)
|
super(SalesInvoice, self).set_missing_values(for_validate)
|
||||||
|
|
||||||
|
|||||||
@@ -174,6 +174,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
|
"depends_on": "eval:doc.apply_on != 'Transaction'",
|
||||||
"fieldname": "is_cumulative",
|
"fieldname": "is_cumulative",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Is Cumulative"
|
"label": "Is Cumulative"
|
||||||
@@ -656,7 +657,7 @@
|
|||||||
"icon": "fa fa-gift",
|
"icon": "fa fa-gift",
|
||||||
"idx": 1,
|
"idx": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2025-02-17 18:15:39.824639",
|
"modified": "2025-08-20 11:40:07.096854",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Pricing Rule",
|
"name": "Pricing Rule",
|
||||||
|
|||||||
@@ -583,11 +583,7 @@ def apply_pricing_rule_on_transaction(doc):
|
|||||||
if not d.get(pr_field):
|
if not d.get(pr_field):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if (
|
if d.validate_applied_rule and (doc.get(field) or 0) < d.get(pr_field):
|
||||||
d.validate_applied_rule
|
|
||||||
and doc.get(field) is not None
|
|
||||||
and doc.get(field) < d.get(pr_field)
|
|
||||||
):
|
|
||||||
frappe.msgprint(_("User has not applied rule on the invoice {0}").format(doc.name))
|
frappe.msgprint(_("User has not applied rule on the invoice {0}").format(doc.name))
|
||||||
else:
|
else:
|
||||||
if not d.coupon_code_based:
|
if not d.coupon_code_based:
|
||||||
|
|||||||
@@ -93,12 +93,14 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
|
"depends_on": "eval:doc.apply_on != 'Transaction'",
|
||||||
"fieldname": "mixed_conditions",
|
"fieldname": "mixed_conditions",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Mixed Conditions"
|
"label": "Mixed Conditions"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
|
"depends_on": "eval:doc.apply_on != 'Transaction'",
|
||||||
"fieldname": "is_cumulative",
|
"fieldname": "is_cumulative",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Is Cumulative"
|
"label": "Is Cumulative"
|
||||||
@@ -278,7 +280,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-05-06 16:20:22.039078",
|
"modified": "2025-08-20 11:48:23.231081",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Promotional Scheme",
|
"name": "Promotional Scheme",
|
||||||
@@ -336,4 +338,4 @@
|
|||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"track_changes": 1
|
"track_changes": 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -644,7 +644,7 @@ frappe.ui.form.on("Purchase Invoice", {
|
|||||||
},
|
},
|
||||||
|
|
||||||
add_custom_buttons: function (frm) {
|
add_custom_buttons: function (frm) {
|
||||||
if (frm.doc.docstatus == 1 && frm.doc.per_received < 100) {
|
if (frm.doc.docstatus == 1 && frm.doc.per_received < 100 && frm.doc.update_stock == 0) {
|
||||||
frm.add_custom_button(
|
frm.add_custom_button(
|
||||||
__("Purchase Receipt"),
|
__("Purchase Receipt"),
|
||||||
() => {
|
() => {
|
||||||
|
|||||||
@@ -340,7 +340,12 @@ class PurchaseInvoice(BuyingController):
|
|||||||
)
|
)
|
||||||
if not self.due_date:
|
if not self.due_date:
|
||||||
self.due_date = get_due_date(
|
self.due_date = get_due_date(
|
||||||
self.posting_date, "Supplier", self.supplier, self.company, self.bill_date
|
self.posting_date,
|
||||||
|
"Supplier",
|
||||||
|
self.supplier,
|
||||||
|
self.company,
|
||||||
|
self.bill_date,
|
||||||
|
template_name=self.payment_terms_template,
|
||||||
)
|
)
|
||||||
|
|
||||||
tds_category = frappe.db.get_value("Supplier", self.supplier, "tax_withholding_category")
|
tds_category = frappe.db.get_value("Supplier", self.supplier, "tax_withholding_category")
|
||||||
|
|||||||
@@ -532,6 +532,7 @@ def get_accounting_entries(
|
|||||||
query = query.select(gl_entry.posting_date, gl_entry.is_opening, gl_entry.fiscal_year)
|
query = query.select(gl_entry.posting_date, gl_entry.is_opening, gl_entry.fiscal_year)
|
||||||
query = query.where(gl_entry.is_cancelled == 0)
|
query = query.where(gl_entry.is_cancelled == 0)
|
||||||
query = query.where(gl_entry.posting_date <= to_date)
|
query = query.where(gl_entry.posting_date <= to_date)
|
||||||
|
query = query.force_index("posting_date_company_index")
|
||||||
|
|
||||||
if ignore_opening_entries and not ignore_is_opening:
|
if ignore_opening_entries and not ignore_is_opening:
|
||||||
query = query.where(gl_entry.is_opening == "No")
|
query = query.where(gl_entry.is_opening == "No")
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ from frappe.query_builder import functions as fn
|
|||||||
from frappe.utils import cstr, flt
|
from frappe.utils import cstr, flt
|
||||||
from frappe.utils.nestedset import get_descendants_of
|
from frappe.utils.nestedset import get_descendants_of
|
||||||
from frappe.utils.xlsxutils import handle_html
|
from frappe.utils.xlsxutils import handle_html
|
||||||
from pypika import Order
|
|
||||||
|
|
||||||
from erpnext.accounts.report.sales_register.sales_register import get_mode_of_payments
|
from erpnext.accounts.report.sales_register.sales_register import get_mode_of_payments
|
||||||
from erpnext.accounts.report.utils import get_values_for_columns
|
from erpnext.accounts.report.utils import get_values_for_columns
|
||||||
|
|||||||
@@ -4,15 +4,25 @@
|
|||||||
"disabled": 0,
|
"disabled": 0,
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
"doctype": "Report",
|
"doctype": "Report",
|
||||||
|
"filters": [
|
||||||
|
{
|
||||||
|
"fieldname": "company",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Company",
|
||||||
|
"mandatory": 1,
|
||||||
|
"options": "Company",
|
||||||
|
"wildcard_filter": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"is_standard": "Yes",
|
"is_standard": "Yes",
|
||||||
"modified": "2019-01-17 17:20:42.374958",
|
"modified": "2025-08-28 19:06:54.273322",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Trial Balance (Simple)",
|
"name": "Trial Balance (Simple)",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"prepared_report": 0,
|
"prepared_report": 0,
|
||||||
"query": "select fiscal_year as \"Fiscal Year:Data:80\",\n\tcompany as \"Company:Data:220\",\n\tposting_date as \"Posting Date:Date:100\",\n\taccount as \"Account:Data:380\",\n\tsum(debit) as \"Debit:Currency:140\",\n\tsum(credit) as \"Credit:Currency:140\",\n\tfinance_book as \"Finance Book:Link/Finance Book:140\"\nfrom `tabGL Entry`\ngroup by fiscal_year, company, posting_date, account\norder by fiscal_year, company, posting_date, account",
|
"query": "select fiscal_year as \"Fiscal Year:Data:80\",\n\tcompany as \"Company:Data:220\",\n\tposting_date as \"Posting Date:Date:100\",\n\taccount as \"Account:Data:380\",\n\tsum(debit) as \"Debit:Currency:140\",\n\tsum(credit) as \"Credit:Currency:140\",\n\tfinance_book as \"Finance Book:Link/Finance Book:140\"\nfrom `tabGL Entry`\nwhere is_cancelled = 0 and company = %(company)s\ngroup by fiscal_year, company, posting_date, account\norder by fiscal_year, company, posting_date, account",
|
||||||
"ref_doctype": "GL Entry",
|
"ref_doctype": "GL Entry",
|
||||||
"report_name": "Trial Balance (Simple)",
|
"report_name": "Trial Balance (Simple)",
|
||||||
"report_type": "Query Report",
|
"report_type": "Query Report",
|
||||||
|
|||||||
@@ -39,7 +39,9 @@
|
|||||||
"section_break_xcug",
|
"section_break_xcug",
|
||||||
"auto_create_subcontracting_order",
|
"auto_create_subcontracting_order",
|
||||||
"column_break_izrr",
|
"column_break_izrr",
|
||||||
"auto_create_purchase_receipt"
|
"auto_create_purchase_receipt",
|
||||||
|
"request_for_quotation_tab",
|
||||||
|
"fixed_email"
|
||||||
],
|
],
|
||||||
"fields": [
|
"fields": [
|
||||||
{
|
{
|
||||||
@@ -255,6 +257,19 @@
|
|||||||
"fieldname": "set_valuation_rate_for_rejected_materials",
|
"fieldname": "set_valuation_rate_for_rejected_materials",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Set Valuation Rate for Rejected Materials"
|
"label": "Set Valuation Rate for Rejected Materials"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "request_for_quotation_tab",
|
||||||
|
"fieldtype": "Tab Break",
|
||||||
|
"label": "Request for Quotation"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"description": "If set, the system does not use the user's Email or the standard outgoing Email account for sending request for quotations.",
|
||||||
|
"fieldname": "fixed_email",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Fixed Outgoing Email Account",
|
||||||
|
"link_filters": "[[\"Email Account\",\"enable_outgoing\",\"=\",1]]",
|
||||||
|
"options": "Email Account"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"grid_page_length": 50,
|
"grid_page_length": 50,
|
||||||
@@ -263,7 +278,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2025-05-16 15:56:38.321369",
|
"modified": "2025-08-20 22:13:38.506889",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Buying Settings",
|
"name": "Buying Settings",
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ class BuyingSettings(Document):
|
|||||||
blanket_order_allowance: DF.Float
|
blanket_order_allowance: DF.Float
|
||||||
buying_price_list: DF.Link | None
|
buying_price_list: DF.Link | None
|
||||||
disable_last_purchase_rate: DF.Check
|
disable_last_purchase_rate: DF.Check
|
||||||
|
fixed_email: DF.Link | None
|
||||||
maintain_same_rate: DF.Check
|
maintain_same_rate: DF.Check
|
||||||
maintain_same_rate_action: DF.Literal["Stop", "Warn"]
|
maintain_same_rate_action: DF.Literal["Stop", "Warn"]
|
||||||
over_transfer_allowance: DF.Float
|
over_transfer_allowance: DF.Float
|
||||||
|
|||||||
@@ -289,7 +289,11 @@ class RequestforQuotation(BuyingController):
|
|||||||
email_template = frappe.get_doc("Email Template", self.email_template)
|
email_template = frappe.get_doc("Email Template", self.email_template)
|
||||||
message = frappe.render_template(email_template.response_, doc_args)
|
message = frappe.render_template(email_template.response_, doc_args)
|
||||||
subject = frappe.render_template(email_template.subject, doc_args)
|
subject = frappe.render_template(email_template.subject, doc_args)
|
||||||
sender = frappe.session.user not in STANDARD_USERS and frappe.session.user or None
|
fixed_procurement_email = frappe.db.get_single_value("Buying Settings", "fixed_email")
|
||||||
|
if fixed_procurement_email:
|
||||||
|
sender = frappe.db.get_value("Email Account", fixed_procurement_email, "email_id")
|
||||||
|
else:
|
||||||
|
sender = frappe.session.user not in STANDARD_USERS and frappe.session.user or None
|
||||||
|
|
||||||
if preview:
|
if preview:
|
||||||
return {"message": message, "subject": subject}
|
return {"message": message, "subject": subject}
|
||||||
|
|||||||
@@ -188,30 +188,10 @@
|
|||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"is_query_report": 0,
|
"is_query_report": 0,
|
||||||
"label": "Payments",
|
"label": "Payments",
|
||||||
"link_count": 3,
|
"link_count": 1,
|
||||||
"onboard": 0,
|
"onboard": 0,
|
||||||
"type": "Card Break"
|
"type": "Card Break"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"hidden": 0,
|
|
||||||
"is_query_report": 0,
|
|
||||||
"label": "GoCardless Settings",
|
|
||||||
"link_count": 0,
|
|
||||||
"link_to": "GoCardless Settings",
|
|
||||||
"link_type": "DocType",
|
|
||||||
"onboard": 0,
|
|
||||||
"type": "Link"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"hidden": 0,
|
|
||||||
"is_query_report": 0,
|
|
||||||
"label": "Mpesa Settings",
|
|
||||||
"link_count": 0,
|
|
||||||
"link_to": "Mpesa Settings",
|
|
||||||
"link_type": "DocType",
|
|
||||||
"onboard": 0,
|
|
||||||
"type": "Link"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"hidden": 0,
|
"hidden": 0,
|
||||||
"is_query_report": 0,
|
"is_query_report": 0,
|
||||||
|
|||||||
@@ -116,6 +116,20 @@ frappe.ui.form.on("Work Order", {
|
|||||||
frm.set_indicator_formatter("operation", function (doc) {
|
frm.set_indicator_formatter("operation", function (doc) {
|
||||||
return frm.doc.qty == doc.completed_qty ? "green" : "orange";
|
return frm.doc.qty == doc.completed_qty ? "green" : "orange";
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (frm.doc.docstatus == 0 && frm.doc.bom_no) {
|
||||||
|
frappe.call({
|
||||||
|
method: "erpnext.manufacturing.doctype.work_order.work_order.check_if_scrap_warehouse_mandatory",
|
||||||
|
args: {
|
||||||
|
bom_no: frm.doc.bom_no,
|
||||||
|
},
|
||||||
|
callback: function (r) {
|
||||||
|
if (r.message["set_scrap_wh_mandatory"]) {
|
||||||
|
frm.toggle_reqd("scrap_warehouse", true);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onload: function (frm) {
|
onload: function (frm) {
|
||||||
|
|||||||
@@ -390,7 +390,7 @@ erpnext.patches.v15_0.enable_allow_existing_serial_no
|
|||||||
erpnext.patches.v15_0.update_cc_in_process_statement_of_accounts
|
erpnext.patches.v15_0.update_cc_in_process_statement_of_accounts
|
||||||
erpnext.patches.v15_0.update_asset_status_to_work_in_progress
|
erpnext.patches.v15_0.update_asset_status_to_work_in_progress
|
||||||
erpnext.patches.v15_0.rename_manufacturing_settings_field
|
erpnext.patches.v15_0.rename_manufacturing_settings_field
|
||||||
erpnext.patches.v15_0.sync_auto_reconcile_config
|
erpnext.patches.v15_0.sync_auto_reconcile_config #2025-08-26
|
||||||
execute:frappe.db.set_single_value("Accounts Settings", "exchange_gain_loss_posting_date", "Payment")
|
execute:frappe.db.set_single_value("Accounts Settings", "exchange_gain_loss_posting_date", "Payment")
|
||||||
erpnext.patches.v14_0.disable_add_row_in_gross_profit
|
erpnext.patches.v14_0.disable_add_row_in_gross_profit
|
||||||
erpnext.patches.v15_0.set_difference_amount_in_asset_value_adjustment
|
erpnext.patches.v15_0.set_difference_amount_in_asset_value_adjustment
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ def execute():
|
|||||||
frappe.db.set_single_value("Accounts Settings", "reconciliation_queue_size", 5)
|
frappe.db.set_single_value("Accounts Settings", "reconciliation_queue_size", 5)
|
||||||
|
|
||||||
# Create Scheduler Event record if it doesn't exist
|
# Create Scheduler Event record if it doesn't exist
|
||||||
if frappe.reload_doc("core", "doctype", "scheduler_event"):
|
if frappe.reload_doc("core", "doctype", "scheduler_event", force=True):
|
||||||
method = "erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.trigger_reconciliation_for_queued_docs"
|
method = "erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.trigger_reconciliation_for_queued_docs"
|
||||||
if not frappe.db.get_all(
|
if not frappe.db.get_all(
|
||||||
"Scheduler Event", {"scheduled_against": "Process Payment Reconciliation", "method": method}
|
"Scheduler Event", {"scheduled_against": "Process Payment Reconciliation", "method": method}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ erpnext.accounts.bank_reconciliation.NumberCardManager = class NumberCardManager
|
|||||||
currency: this.currency,
|
currency: this.currency,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
value: this.bank_statement_closing_balance - this.cleared_balance,
|
value: flt(this.bank_statement_closing_balance) - flt(this.cleared_balance),
|
||||||
label: __("Difference"),
|
label: __("Difference"),
|
||||||
datatype: "Currency",
|
datatype: "Currency",
|
||||||
currency: this.currency,
|
currency: this.currency,
|
||||||
|
|||||||
@@ -159,8 +159,9 @@ erpnext.buying = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
company(){
|
company() {
|
||||||
if(!frappe.meta.has_field(this.frm.doc.doctype, "billing_address")) return;
|
super.company();
|
||||||
|
if (!frappe.meta.has_field(this.frm.doc.doctype, "billing_address")) return;
|
||||||
|
|
||||||
frappe.call({
|
frappe.call({
|
||||||
method: "erpnext.setup.doctype.company.company.get_billing_shipping_address",
|
method: "erpnext.setup.doctype.company.company.get_billing_shipping_address",
|
||||||
|
|||||||
@@ -459,12 +459,8 @@ erpnext.utils.BarcodeScanner = class BarcodeScanner {
|
|||||||
const item_scanned = row.has_item_scanned;
|
const item_scanned = row.has_item_scanned;
|
||||||
|
|
||||||
let warehouse_match = true;
|
let warehouse_match = true;
|
||||||
if (has_warehouse_field) {
|
if (has_warehouse_field && warehouse && row[warehouse_field]) {
|
||||||
if (warehouse) {
|
warehouse_match = row[warehouse_field] === warehouse;
|
||||||
warehouse_match = row[warehouse_field] === warehouse;
|
|
||||||
} else {
|
|
||||||
warehouse_match = !row[warehouse_field];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -19,6 +19,9 @@ def create_transaction_log(doc, method):
|
|||||||
Appends the transaction to a chain of hashed logs for legal resons.
|
Appends the transaction to a chain of hashed logs for legal resons.
|
||||||
Called on submit of Sales Invoice and Payment Entry.
|
Called on submit of Sales Invoice and Payment Entry.
|
||||||
"""
|
"""
|
||||||
|
if frappe.conf.get("disable_transaction_log", False):
|
||||||
|
return
|
||||||
|
|
||||||
region = get_region()
|
region = get_region()
|
||||||
if region not in ["Germany"]:
|
if region not in ["Germany"]:
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -365,6 +365,7 @@
|
|||||||
"fieldname": "base_net_rate",
|
"fieldname": "base_net_rate",
|
||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Net Rate (Company Currency)",
|
"label": "Net Rate (Company Currency)",
|
||||||
|
"options": "Company:company:default_currency",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
},
|
},
|
||||||
@@ -698,7 +699,7 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-12-12 13:49:18.765883",
|
"modified": "2025-08-26 20:31:47.775890",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Selling",
|
"module": "Selling",
|
||||||
"name": "Quotation Item",
|
"name": "Quotation Item",
|
||||||
|
|||||||
@@ -766,6 +766,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
|
"description": "Upon enabling this, the JV will be submitted for a different exchange rate.",
|
||||||
"fieldname": "submit_err_jv",
|
"fieldname": "submit_err_jv",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Submit ERR Journals?"
|
"label": "Submit ERR Journals?"
|
||||||
@@ -857,7 +858,7 @@
|
|||||||
"image_field": "company_logo",
|
"image_field": "company_logo",
|
||||||
"is_tree": 1,
|
"is_tree": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2025-01-09 20:12:25.471544",
|
"modified": "2025-08-25 18:34:03.602046",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Setup",
|
"module": "Setup",
|
||||||
"name": "Company",
|
"name": "Company",
|
||||||
@@ -919,6 +920,7 @@
|
|||||||
"select": 1
|
"select": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"row_format": "Dynamic",
|
||||||
"show_name_in_global_search": 1,
|
"show_name_in_global_search": 1,
|
||||||
"sort_field": "modified",
|
"sort_field": "modified",
|
||||||
"sort_order": "ASC",
|
"sort_order": "ASC",
|
||||||
|
|||||||
@@ -4146,6 +4146,36 @@ class TestPurchaseReceipt(FrappeTestCase):
|
|||||||
|
|
||||||
self.assertTrue(sles)
|
self.assertTrue(sles)
|
||||||
|
|
||||||
|
def test_validate_recreate_stock_ledgers_for_sn_item(self):
|
||||||
|
item_code = "Test SN Item for Recreate Stock Ledgers"
|
||||||
|
make_item(item_code, {"has_serial_no": 1, "serial_no_series": "SN-TRSLR-.#####"})
|
||||||
|
|
||||||
|
pr = make_purchase_receipt(item_code=item_code, qty=10, rate=100)
|
||||||
|
pr.submit()
|
||||||
|
|
||||||
|
sles = frappe.get_all(
|
||||||
|
"Stock Ledger Entry",
|
||||||
|
filters={"voucher_type": pr.doctype, "voucher_no": pr.name},
|
||||||
|
pluck="name",
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertTrue(sles)
|
||||||
|
|
||||||
|
repost_doc = frappe.get_doc(
|
||||||
|
{
|
||||||
|
"doctype": "Repost Item Valuation",
|
||||||
|
"based_on": "Transaction",
|
||||||
|
"voucher_type": pr.doctype,
|
||||||
|
"voucher_no": pr.name,
|
||||||
|
"posting_date": pr.posting_date,
|
||||||
|
"posting_time": pr.posting_time,
|
||||||
|
"company": pr.company,
|
||||||
|
"recreate_stock_ledgers": 1,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertRaises(frappe.ValidationError, repost_doc.save)
|
||||||
|
|
||||||
def test_internal_pr_qty_change_only_single_batch(self):
|
def test_internal_pr_qty_change_only_single_batch(self):
|
||||||
from erpnext.stock.doctype.delivery_note.delivery_note import make_inter_company_purchase_receipt
|
from erpnext.stock.doctype.delivery_note.delivery_note import make_inter_company_purchase_receipt
|
||||||
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
|
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
|
||||||
|
|||||||
@@ -132,4 +132,18 @@ frappe.ui.form.on("Repost Item Valuation", {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
voucher_type: function (frm) {
|
||||||
|
frm.trigger("set_company_on_transaction");
|
||||||
|
},
|
||||||
|
|
||||||
|
voucher_no: function (frm) {
|
||||||
|
frm.trigger("set_company_on_transaction");
|
||||||
|
},
|
||||||
|
|
||||||
|
set_company_on_transaction(frm) {
|
||||||
|
if (frm.doc.voucher_no && frm.doc.voucher_type) {
|
||||||
|
frm.call("set_company");
|
||||||
|
}
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -70,12 +70,33 @@ class RepostItemValuation(Document):
|
|||||||
)
|
)
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
|
self.set_company()
|
||||||
self.validate_period_closing_voucher()
|
self.validate_period_closing_voucher()
|
||||||
self.set_status(write=False)
|
self.set_status(write=False)
|
||||||
self.reset_field_values()
|
self.reset_field_values()
|
||||||
self.set_company()
|
|
||||||
self.validate_accounts_freeze()
|
self.validate_accounts_freeze()
|
||||||
self.reset_recreate_stock_ledgers()
|
self.reset_recreate_stock_ledgers()
|
||||||
|
self.validate_recreate_stock_ledgers()
|
||||||
|
|
||||||
|
def validate_recreate_stock_ledgers(self):
|
||||||
|
if not self.recreate_stock_ledgers:
|
||||||
|
return
|
||||||
|
|
||||||
|
items = []
|
||||||
|
if self.based_on == "Item and Warehouse":
|
||||||
|
items.append(self.item_code)
|
||||||
|
else:
|
||||||
|
items = get_items_to_be_repost(self.voucher_type, self.voucher_no)
|
||||||
|
items = list(set([d.item_code for d in items]))
|
||||||
|
|
||||||
|
if serial_batch_items := frappe.get_all(
|
||||||
|
"Item", or_filters={"has_serial_no": 1, "has_batch_no": 1}, filters={"name": ("in", items)}
|
||||||
|
):
|
||||||
|
item_list = ", ".join([d.name for d in serial_batch_items])
|
||||||
|
msg = _(
|
||||||
|
"Since {0} are Serial No/Batch No items, you cannot enable 'Recreate Stock Ledgers' in Repost Item Valuation."
|
||||||
|
).format(item_list)
|
||||||
|
frappe.throw(msg)
|
||||||
|
|
||||||
def validate_period_closing_voucher(self):
|
def validate_period_closing_voucher(self):
|
||||||
# Period Closing Voucher
|
# Period Closing Voucher
|
||||||
@@ -167,6 +188,7 @@ class RepostItemValuation(Document):
|
|||||||
def on_trash(self):
|
def on_trash(self):
|
||||||
self.clear_attachment()
|
self.clear_attachment()
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
def set_company(self):
|
def set_company(self):
|
||||||
if self.based_on == "Transaction":
|
if self.based_on == "Transaction":
|
||||||
self.company = frappe.get_cached_value(self.voucher_type, self.voucher_no, "company")
|
self.company = frappe.get_cached_value(self.voucher_type, self.voucher_no, "company")
|
||||||
|
|||||||
@@ -1006,7 +1006,7 @@ erpnext.stock.StockEntry = class StockEntry extends erpnext.stock.StockControlle
|
|||||||
this.barcode_scanner = new erpnext.utils.BarcodeScanner({
|
this.barcode_scanner = new erpnext.utils.BarcodeScanner({
|
||||||
frm: this.frm,
|
frm: this.frm,
|
||||||
warehouse_field: (doc) => {
|
warehouse_field: (doc) => {
|
||||||
return doc.purpose === "Material Transfer" ? "t_warehouse" : "s_warehouse";
|
return doc.purpose === "Material Receipt" ? "t_warehouse" : "s_warehouse";
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -2123,7 +2123,16 @@ class StockEntry(StockController):
|
|||||||
"Work Order", self.work_order, "allow_alternative_item"
|
"Work Order", self.work_order, "allow_alternative_item"
|
||||||
)
|
)
|
||||||
|
|
||||||
item.from_warehouse = self.from_warehouse or item.source_warehouse or item.default_warehouse
|
item.from_warehouse = (
|
||||||
|
frappe.get_value(
|
||||||
|
"Work Order Item",
|
||||||
|
{"parent": self.work_order, "item_code": item.item_code},
|
||||||
|
"source_warehouse",
|
||||||
|
)
|
||||||
|
if frappe.get_value("Work Order", self.work_order, "skip_transfer")
|
||||||
|
and not frappe.get_value("Work Order", self.work_order, "from_wip_warehouse")
|
||||||
|
else self.from_warehouse or item.source_warehouse or item.default_warehouse
|
||||||
|
)
|
||||||
if item.item_code in used_alternative_items:
|
if item.item_code in used_alternative_items:
|
||||||
alternative_item_data = used_alternative_items.get(item.item_code)
|
alternative_item_data = used_alternative_items.get(item.item_code)
|
||||||
item.item_code = alternative_item_data.item_code
|
item.item_code = alternative_item_data.item_code
|
||||||
|
|||||||
@@ -770,12 +770,15 @@ class BatchNoValuation(DeprecatedBatchNoValuation):
|
|||||||
self.non_batchwise_valuation_batches = self.batches
|
self.non_batchwise_valuation_batches = self.batches
|
||||||
return
|
return
|
||||||
|
|
||||||
batches = frappe.get_all(
|
if get_valuation_method(self.sle.item_code) == "FIFO":
|
||||||
"Batch", filters={"name": ("in", self.batches), "use_batchwise_valuation": 1}, fields=["name"]
|
self.batchwise_valuation_batches = self.batches
|
||||||
)
|
else:
|
||||||
|
batches = frappe.get_all(
|
||||||
|
"Batch", filters={"name": ("in", self.batches), "use_batchwise_valuation": 1}, fields=["name"]
|
||||||
|
)
|
||||||
|
|
||||||
for batch in batches:
|
for batch in batches:
|
||||||
self.batchwise_valuation_batches.append(batch.name)
|
self.batchwise_valuation_batches.append(batch.name)
|
||||||
|
|
||||||
self.non_batchwise_valuation_batches = list(set(self.batches) - set(self.batchwise_valuation_batches))
|
self.non_batchwise_valuation_batches = list(set(self.batches) - set(self.batchwise_valuation_batches))
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user