mirror of
https://github.com/frappe/erpnext.git
synced 2026-05-31 10:49:09 +00:00
Merge branch 'develop' into so-mr-po
This commit is contained in:
@@ -52,7 +52,7 @@ class ERPNextAddress(Address):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_shipping_address(company, address=None):
|
def get_shipping_address(company: str, address: str | None = None):
|
||||||
filters = [
|
filters = [
|
||||||
["Dynamic Link", "link_doctype", "=", "Company"],
|
["Dynamic Link", "link_doctype", "=", "Company"],
|
||||||
["Dynamic Link", "link_name", "=", company],
|
["Dynamic Link", "link_name", "=", company],
|
||||||
|
|||||||
@@ -13,15 +13,15 @@ from frappe.utils.nestedset import get_descendants_of
|
|||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@cache_source
|
@cache_source
|
||||||
def get(
|
def get(
|
||||||
chart_name=None,
|
chart_name: str | None = None,
|
||||||
chart=None,
|
chart: str | dict | None = None,
|
||||||
no_cache=None,
|
no_cache: bool | None = None,
|
||||||
filters=None,
|
filters: str | dict | None = None,
|
||||||
from_date=None,
|
from_date: str | None = None,
|
||||||
to_date=None,
|
to_date: str | None = None,
|
||||||
timespan=None,
|
timespan: str | None = None,
|
||||||
time_interval=None,
|
time_interval: str | None = None,
|
||||||
heatmap_year=None,
|
heatmap_year: str | None = None,
|
||||||
):
|
):
|
||||||
if chart_name:
|
if chart_name:
|
||||||
chart = frappe.get_doc("Dashboard Chart", chart_name)
|
chart = frappe.get_doc("Dashboard Chart", chart_name)
|
||||||
|
|||||||
@@ -471,7 +471,7 @@ class Account(NestedSet):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@frappe.validate_and_sanitize_search_inputs
|
@frappe.validate_and_sanitize_search_inputs
|
||||||
def get_parent_account(doctype, txt, searchfield, start, page_len, filters):
|
def get_parent_account(doctype: str, txt: str, searchfield: str, start: int, page_len: int, filters: dict):
|
||||||
return frappe.db.sql(
|
return frappe.db.sql(
|
||||||
"""select name from tabAccount
|
"""select name from tabAccount
|
||||||
where is_group = 1 and docstatus != 2 and company = {}
|
where is_group = 1 and docstatus != 2 and company = {}
|
||||||
@@ -515,7 +515,9 @@ def get_account_autoname(account_number, account_name, company):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def update_account_number(name, account_name, account_number=None, from_descendant=False):
|
def update_account_number(
|
||||||
|
name: str, account_name: str, account_number: str | None = None, from_descendant: bool = False
|
||||||
|
):
|
||||||
_ensure_idle_system()
|
_ensure_idle_system()
|
||||||
account = frappe.get_cached_doc("Account", name)
|
account = frappe.get_cached_doc("Account", name)
|
||||||
if not account:
|
if not account:
|
||||||
@@ -577,7 +579,7 @@ def update_account_number(name, account_name, account_number=None, from_descenda
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def merge_account(old, new):
|
def merge_account(old: str, new: str):
|
||||||
_ensure_idle_system()
|
_ensure_idle_system()
|
||||||
# Validate properties before merging
|
# Validate properties before merging
|
||||||
new_account = frappe.get_cached_doc("Account", new)
|
new_account = frappe.get_cached_doc("Account", new)
|
||||||
@@ -614,7 +616,7 @@ def merge_account(old, new):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_root_company(company):
|
def get_root_company(company: str):
|
||||||
# return the topmost company in the hierarchy
|
# return the topmost company in the hierarchy
|
||||||
ancestors = get_ancestors_of("Company", company, "lft asc")
|
ancestors = get_ancestors_of("Company", company, "lft asc")
|
||||||
return [ancestors[0]] if ancestors else []
|
return [ancestors[0]] if ancestors else []
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ def identify_is_group(child):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_chart(chart_template, existing_company=None):
|
def get_chart(chart_template: str | None, existing_company: str | None = None):
|
||||||
chart = {}
|
chart = {}
|
||||||
if existing_company:
|
if existing_company:
|
||||||
return get_account_tree_from_existing_company(existing_company)
|
return get_account_tree_from_existing_company(existing_company)
|
||||||
@@ -132,7 +132,7 @@ def get_chart(chart_template, existing_company=None):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_charts_for_country(country, with_standard=False):
|
def get_charts_for_country(country: str, with_standard: bool = False):
|
||||||
charts = []
|
charts = []
|
||||||
|
|
||||||
def _get_chart_name(content):
|
def _get_chart_name(content):
|
||||||
@@ -225,7 +225,7 @@ def build_account_tree(tree, parent, all_accounts):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def validate_bank_account(coa, bank_account):
|
def validate_bank_account(coa: str, bank_account: str):
|
||||||
accounts = []
|
accounts = []
|
||||||
chart = get_chart(coa)
|
chart = get_chart(coa)
|
||||||
|
|
||||||
@@ -244,7 +244,9 @@ def validate_bank_account(coa, bank_account):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def build_tree_from_json(chart_template, chart_data=None, from_coa_importer=False):
|
def build_tree_from_json(
|
||||||
|
chart_template: str, chart_data: dict | None = None, from_coa_importer: bool = False
|
||||||
|
):
|
||||||
"""get chart template from its folder and parse the json to be rendered as tree"""
|
"""get chart template from its folder and parse the json to be rendered as tree"""
|
||||||
chart = chart_data or get_chart(chart_template)
|
chart = chart_data or get_chart(chart_template)
|
||||||
|
|
||||||
|
|||||||
@@ -103,10 +103,6 @@ class AccountingDimension(Document):
|
|||||||
if not self.fieldname:
|
if not self.fieldname:
|
||||||
self.fieldname = scrub(self.label)
|
self.fieldname = scrub(self.label)
|
||||||
|
|
||||||
def on_update(self):
|
|
||||||
frappe.flags.accounting_dimensions = None
|
|
||||||
frappe.flags.accounting_dimensions_details = None
|
|
||||||
|
|
||||||
|
|
||||||
def make_dimension_in_accounting_doctypes(doc, doclist=None):
|
def make_dimension_in_accounting_doctypes(doc, doclist=None):
|
||||||
if not doclist:
|
if not doclist:
|
||||||
@@ -210,7 +206,7 @@ def delete_accounting_dimension(doc):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def disable_dimension(doc):
|
def disable_dimension(doc: str):
|
||||||
if frappe.in_test:
|
if frappe.in_test:
|
||||||
toggle_disabling(doc=doc)
|
toggle_disabling(doc=doc)
|
||||||
else:
|
else:
|
||||||
@@ -241,34 +237,26 @@ def get_doctypes_with_dimensions():
|
|||||||
return frappe.get_hooks("accounting_dimension_doctypes")
|
return frappe.get_hooks("accounting_dimension_doctypes")
|
||||||
|
|
||||||
|
|
||||||
def get_accounting_dimensions(as_list=True, filters=None):
|
def get_accounting_dimensions(as_list=True):
|
||||||
if not filters:
|
accounting_dimensions = frappe.get_all(
|
||||||
filters = {"disabled": 0}
|
"Accounting Dimension",
|
||||||
|
fields=["label", "fieldname", "disabled", "document_type"],
|
||||||
if frappe.flags.accounting_dimensions is None:
|
filters={"disabled": 0},
|
||||||
frappe.flags.accounting_dimensions = frappe.get_all(
|
)
|
||||||
"Accounting Dimension",
|
|
||||||
fields=["label", "fieldname", "disabled", "document_type"],
|
|
||||||
filters=filters,
|
|
||||||
)
|
|
||||||
|
|
||||||
if as_list:
|
if as_list:
|
||||||
return [d.fieldname for d in frappe.flags.accounting_dimensions]
|
return [d.fieldname for d in accounting_dimensions]
|
||||||
else:
|
else:
|
||||||
return frappe.flags.accounting_dimensions
|
return accounting_dimensions
|
||||||
|
|
||||||
|
|
||||||
def get_checks_for_pl_and_bs_accounts():
|
def get_checks_for_pl_and_bs_accounts():
|
||||||
if frappe.flags.accounting_dimensions_details is None:
|
return frappe.db.sql(
|
||||||
# nosemgrep
|
"""SELECT p.label, p.disabled, p.fieldname, c.default_dimension, c.company, c.mandatory_for_pl, c.mandatory_for_bs
|
||||||
frappe.flags.accounting_dimensions_details = frappe.db.sql(
|
|
||||||
"""SELECT p.label, p.disabled, p.fieldname, c.default_dimension, c.company, c.mandatory_for_pl, c.mandatory_for_bs
|
|
||||||
FROM `tabAccounting Dimension`p ,`tabAccounting Dimension Detail` c
|
FROM `tabAccounting Dimension`p ,`tabAccounting Dimension Detail` c
|
||||||
WHERE p.name = c.parent AND p.disabled = 0""",
|
WHERE p.name = c.parent AND p.disabled = 0""",
|
||||||
as_dict=1,
|
as_dict=1,
|
||||||
)
|
)
|
||||||
|
|
||||||
return frappe.flags.accounting_dimensions_details
|
|
||||||
|
|
||||||
|
|
||||||
def get_dimension_with_children(doctype, dimensions):
|
def get_dimension_with_children(doctype, dimensions):
|
||||||
@@ -286,7 +274,7 @@ def get_dimension_with_children(doctype, dimensions):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_dimensions(with_cost_center_and_project=False):
|
def get_dimensions(with_cost_center_and_project: str | bool = False):
|
||||||
c = frappe.qb.DocType("Accounting Dimension Detail")
|
c = frappe.qb.DocType("Accounting Dimension Detail")
|
||||||
p = frappe.qb.DocType("Accounting Dimension")
|
p = frappe.qb.DocType("Accounting Dimension")
|
||||||
dimension_filters = (
|
dimension_filters = (
|
||||||
|
|||||||
@@ -69,37 +69,34 @@ class AccountingDimensionFilter(Document):
|
|||||||
|
|
||||||
|
|
||||||
def get_dimension_filter_map():
|
def get_dimension_filter_map():
|
||||||
if not frappe.flags.get("dimension_filter_map"):
|
filters = frappe.db.sql(
|
||||||
filters = frappe.db.sql(
|
"""
|
||||||
"""
|
SELECT
|
||||||
SELECT
|
a.applicable_on_account, d.dimension_value, p.accounting_dimension,
|
||||||
a.applicable_on_account, d.dimension_value, p.accounting_dimension,
|
p.allow_or_restrict, p.fieldname, a.is_mandatory
|
||||||
p.allow_or_restrict, p.fieldname, a.is_mandatory
|
FROM
|
||||||
FROM
|
`tabApplicable On Account` a,
|
||||||
`tabApplicable On Account` a,
|
`tabAccounting Dimension Filter` p
|
||||||
`tabAccounting Dimension Filter` p
|
LEFT JOIN `tabAllowed Dimension` d ON d.parent = p.name
|
||||||
LEFT JOIN `tabAllowed Dimension` d ON d.parent = p.name
|
WHERE
|
||||||
WHERE
|
p.name = a.parent
|
||||||
p.name = a.parent
|
AND p.disabled = 0
|
||||||
AND p.disabled = 0
|
""",
|
||||||
""",
|
as_dict=1,
|
||||||
as_dict=1,
|
)
|
||||||
|
|
||||||
|
dimension_filter_map = {}
|
||||||
|
|
||||||
|
for f in filters:
|
||||||
|
build_map(
|
||||||
|
dimension_filter_map,
|
||||||
|
f.fieldname,
|
||||||
|
f.applicable_on_account,
|
||||||
|
f.dimension_value,
|
||||||
|
f.allow_or_restrict,
|
||||||
|
f.is_mandatory,
|
||||||
)
|
)
|
||||||
|
return dimension_filter_map
|
||||||
dimension_filter_map = {}
|
|
||||||
|
|
||||||
for f in filters:
|
|
||||||
build_map(
|
|
||||||
dimension_filter_map,
|
|
||||||
f.fieldname,
|
|
||||||
f.applicable_on_account,
|
|
||||||
f.dimension_value,
|
|
||||||
f.allow_or_restrict,
|
|
||||||
f.is_mandatory,
|
|
||||||
)
|
|
||||||
frappe.flags.dimension_filter_map = dimension_filter_map
|
|
||||||
|
|
||||||
return frappe.flags.dimension_filter_map
|
|
||||||
|
|
||||||
|
|
||||||
def build_map(map_object, dimension, account, filter_value, allow_or_restrict, is_mandatory):
|
def build_map(map_object, dimension, account, filter_value, allow_or_restrict, is_mandatory):
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ def get_default_company_bank_account(company, party_type, party):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_bank_account_details(bank_account):
|
def get_bank_account_details(bank_account: str):
|
||||||
return frappe.get_cached_value(
|
return frappe.get_cached_value(
|
||||||
"Bank Account", bank_account, ["account", "bank", "bank_account_no"], as_dict=1
|
"Bank Account", bank_account, ["account", "bank", "bank_account_no"], as_dict=1
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
from datetime import date
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
@@ -47,7 +47,9 @@ class BankReconciliationTool(Document):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_bank_transactions(bank_account, from_date=None, to_date=None):
|
def get_bank_transactions(
|
||||||
|
bank_account: str, from_date: str | date | None = None, to_date: str | date | None = None
|
||||||
|
):
|
||||||
# returns bank transactions for a bank account
|
# returns bank transactions for a bank account
|
||||||
filters = []
|
filters = []
|
||||||
filters.append(["bank_account", "=", bank_account])
|
filters.append(["bank_account", "=", bank_account])
|
||||||
@@ -80,7 +82,7 @@ def get_bank_transactions(bank_account, from_date=None, to_date=None):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_account_balance(bank_account, till_date, company):
|
def get_account_balance(bank_account: str, till_date: str | date, company: str):
|
||||||
# returns account balance till the specified date
|
# returns account balance till the specified date
|
||||||
account = frappe.db.get_value("Bank Account", bank_account, "account")
|
account = frappe.db.get_value("Bank Account", bank_account, "account")
|
||||||
filters = frappe._dict(
|
filters = frappe._dict(
|
||||||
@@ -106,7 +108,9 @@ def get_account_balance(bank_account, till_date, company):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def update_bank_transaction(bank_transaction_name, reference_number, party_type=None, party=None):
|
def update_bank_transaction(
|
||||||
|
bank_transaction_name: str, reference_number: str, party_type: str | None = None, party: str | None = None
|
||||||
|
):
|
||||||
# updates bank transaction based on the new parameters provided by the user from Vouchers
|
# updates bank transaction based on the new parameters provided by the user from Vouchers
|
||||||
bank_transaction = frappe.get_doc("Bank Transaction", bank_transaction_name)
|
bank_transaction = frappe.get_doc("Bank Transaction", bank_transaction_name)
|
||||||
bank_transaction.reference_number = reference_number
|
bank_transaction.reference_number = reference_number
|
||||||
@@ -135,16 +139,16 @@ def update_bank_transaction(bank_transaction_name, reference_number, party_type=
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def create_journal_entry_bts(
|
def create_journal_entry_bts(
|
||||||
bank_transaction_name,
|
bank_transaction_name: str,
|
||||||
reference_number=None,
|
reference_number: str | None = None,
|
||||||
reference_date=None,
|
reference_date: str | None = None,
|
||||||
posting_date=None,
|
posting_date: str | date | None = None,
|
||||||
entry_type=None,
|
entry_type: str | None = None,
|
||||||
second_account=None,
|
second_account: str | None = None,
|
||||||
mode_of_payment=None,
|
mode_of_payment: str | None = None,
|
||||||
party_type=None,
|
party_type: str | None = None,
|
||||||
party=None,
|
party: str | None = None,
|
||||||
allow_edit=None,
|
allow_edit: bool | None = None,
|
||||||
):
|
):
|
||||||
# Create a new journal entry based on the bank transaction
|
# Create a new journal entry based on the bank transaction
|
||||||
bank_transaction = frappe.db.get_values(
|
bank_transaction = frappe.db.get_values(
|
||||||
@@ -294,17 +298,17 @@ def create_journal_entry_bts(
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def create_payment_entry_bts(
|
def create_payment_entry_bts(
|
||||||
bank_transaction_name,
|
bank_transaction_name: str,
|
||||||
reference_number=None,
|
reference_number: str | None = None,
|
||||||
reference_date=None,
|
reference_date: str | None = None,
|
||||||
party_type=None,
|
party_type: str | None = None,
|
||||||
party=None,
|
party: str | None = None,
|
||||||
posting_date=None,
|
posting_date: str | None = None,
|
||||||
mode_of_payment=None,
|
mode_of_payment: str | None = None,
|
||||||
project=None,
|
project: str | None = None,
|
||||||
cost_center=None,
|
cost_center: str | None = None,
|
||||||
allow_edit=None,
|
allow_edit: bool | None = None,
|
||||||
company_bank_account=None,
|
company_bank_account: str | None = None,
|
||||||
):
|
):
|
||||||
# Create a new payment entry based on the bank transaction
|
# Create a new payment entry based on the bank transaction
|
||||||
bank_transaction = frappe.db.get_values(
|
bank_transaction = frappe.db.get_values(
|
||||||
@@ -371,12 +375,12 @@ def create_payment_entry_bts(
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def auto_reconcile_vouchers(
|
def auto_reconcile_vouchers(
|
||||||
bank_account,
|
bank_account: str,
|
||||||
from_date=None,
|
from_date: str | date | None = None,
|
||||||
to_date=None,
|
to_date: str | date | None = None,
|
||||||
filter_by_reference_date=None,
|
filter_by_reference_date: bool | None = None,
|
||||||
from_reference_date=None,
|
from_reference_date: bool | None = None,
|
||||||
to_reference_date=None,
|
to_reference_date: str | None = None,
|
||||||
):
|
):
|
||||||
bank_transactions = get_bank_transactions(bank_account)
|
bank_transactions = get_bank_transactions(bank_account)
|
||||||
|
|
||||||
@@ -471,7 +475,7 @@ def get_auto_reconcile_message(partially_reconciled, reconciled):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def reconcile_vouchers(bank_transaction_name, vouchers):
|
def reconcile_vouchers(bank_transaction_name: str, vouchers: str):
|
||||||
# updated clear date of all the vouchers based on the bank transaction
|
# updated clear date of all the vouchers based on the bank transaction
|
||||||
vouchers = json.loads(vouchers)
|
vouchers = json.loads(vouchers)
|
||||||
transaction = frappe.get_doc("Bank Transaction", bank_transaction_name)
|
transaction = frappe.get_doc("Bank Transaction", bank_transaction_name)
|
||||||
@@ -487,13 +491,13 @@ def reconcile_vouchers(bank_transaction_name, vouchers):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_linked_payments(
|
def get_linked_payments(
|
||||||
bank_transaction_name,
|
bank_transaction_name: str,
|
||||||
document_types=None,
|
document_types: str | list[str] | None = None,
|
||||||
from_date=None,
|
from_date: str | date | None = None,
|
||||||
to_date=None,
|
to_date: str | date | None = None,
|
||||||
filter_by_reference_date=None,
|
filter_by_reference_date: bool | None = None,
|
||||||
from_reference_date=None,
|
from_reference_date: bool | None = None,
|
||||||
to_reference_date=None,
|
to_reference_date: str | None = 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)
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ def preprocess_mt940_content(content: str) -> str:
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def convert_mt940_to_csv(data_import, mt940_file_path):
|
def convert_mt940_to_csv(data_import: str, mt940_file_path: str):
|
||||||
doc = frappe.get_doc("Bank Statement Import", data_import)
|
doc = frappe.get_doc("Bank Statement Import", data_import)
|
||||||
|
|
||||||
_file_doc, content = get_file(mt940_file_path)
|
_file_doc, content = get_file(mt940_file_path)
|
||||||
@@ -208,26 +208,28 @@ def convert_mt940_to_csv(data_import, mt940_file_path):
|
|||||||
|
|
||||||
|
|
||||||
@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: str, import_file: str | None = None, google_sheets_url: str | None = 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(
|
||||||
import_file, google_sheets_url
|
import_file, google_sheets_url
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def form_start_import(data_import):
|
def form_start_import(data_import: str):
|
||||||
job_id = frappe.get_doc("Bank Statement Import", data_import).start_import()
|
job_id = frappe.get_doc("Bank Statement Import", data_import).start_import()
|
||||||
return job_id is not None
|
return job_id is not None
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def download_errored_template(data_import_name):
|
def download_errored_template(data_import_name: str):
|
||||||
data_import = frappe.get_doc("Bank Statement Import", data_import_name)
|
data_import = frappe.get_doc("Bank Statement Import", data_import_name)
|
||||||
data_import.export_errored_rows()
|
data_import.export_errored_rows()
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def download_import_log(data_import_name):
|
def download_import_log(data_import_name: str):
|
||||||
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()
|
||||||
|
|
||||||
|
|
||||||
@@ -363,7 +365,7 @@ def write_xlsx(data, sheet_name, wb=None, column_widths=None, file_path=None):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_import_status(docname):
|
def get_import_status(docname: str):
|
||||||
import_status = {}
|
import_status = {}
|
||||||
|
|
||||||
data_import = frappe.get_doc("Bank Statement Import", docname)
|
data_import = frappe.get_doc("Bank Statement Import", docname)
|
||||||
|
|||||||
@@ -375,11 +375,12 @@ def get_clearance_details(transaction, payment_entry, bt_allocations, gl_entries
|
|||||||
("unallocated_amount", "bank_account"),
|
("unallocated_amount", "bank_account"),
|
||||||
as_dict=True,
|
as_dict=True,
|
||||||
)
|
)
|
||||||
|
bt_bank_account = frappe.db.get_value("Bank Account", bt.bank_account, "account")
|
||||||
|
|
||||||
if bt.bank_account != gl_bank_account:
|
if bt_bank_account != gl_bank_account:
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("Bank Account {} in Bank Transaction {} is not matching with Bank Account {}").format(
|
_("Bank Account {} in Bank Transaction {} is not matching with Bank Account {}").format(
|
||||||
bt.bank_account, payment_entry.payment_entry, gl_bank_account
|
bt_bank_account, payment_entry.payment_entry, gl_bank_account
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ def upload_bank_statement():
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def create_bank_entries(columns, data, bank_account):
|
def create_bank_entries(columns: str, data: str, bank_account: str):
|
||||||
header_map = get_header_mapping(columns, bank_account)
|
header_map = get_header_mapping(columns, bank_account)
|
||||||
|
|
||||||
success = 0
|
success = 0
|
||||||
|
|||||||
@@ -845,7 +845,7 @@ def get_fiscal_year_date_range(from_fiscal_year, to_fiscal_year):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def revise_budget(budget_name):
|
def revise_budget(budget_name: str):
|
||||||
old_budget = frappe.get_doc("Budget", budget_name)
|
old_budget = frappe.get_doc("Budget", budget_name)
|
||||||
|
|
||||||
if old_budget.docstatus == 1:
|
if old_budget.docstatus == 1:
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ def validate_columns(data):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def validate_company(company):
|
def validate_company(company: str):
|
||||||
parent_company, allow_account_creation_against_child_company = frappe.get_cached_value(
|
parent_company, allow_account_creation_against_child_company = frappe.get_cached_value(
|
||||||
"Company", company, ["parent_company", "allow_account_creation_against_child_company"]
|
"Company", company, ["parent_company", "allow_account_creation_against_child_company"]
|
||||||
)
|
)
|
||||||
@@ -74,7 +74,7 @@ def validate_company(company):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def import_coa(file_name, company):
|
def import_coa(file_name: str, company: str):
|
||||||
# delete existing data for accounts
|
# delete existing data for accounts
|
||||||
unset_existing_data(company)
|
unset_existing_data(company)
|
||||||
|
|
||||||
@@ -159,7 +159,9 @@ def generate_data_from_excel(file_doc, extension, as_dict=False):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_coa(doctype, parent, is_root=False, file_name=None, for_validate=0):
|
def get_coa(
|
||||||
|
doctype: str, parent: str, is_root: bool = False, file_name: str | None = None, for_validate: int = 0
|
||||||
|
):
|
||||||
"""called by tree view (to fetch node's children)"""
|
"""called by tree view (to fetch node's children)"""
|
||||||
|
|
||||||
file_doc, extension = get_file(file_name)
|
file_doc, extension = get_file(file_name)
|
||||||
@@ -307,7 +309,7 @@ def build_response_as_excel(writer):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def download_template(file_type, template_type, company):
|
def download_template(file_type: str, template_type: str, company: str):
|
||||||
writer = get_template(template_type, company)
|
writer = get_template(template_type, company)
|
||||||
|
|
||||||
if file_type == "CSV":
|
if file_type == "CSV":
|
||||||
@@ -361,7 +363,7 @@ def get_sample_template(writer, company):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def validate_accounts(file_doc, extension):
|
def validate_accounts(file_doc: Document, extension: str):
|
||||||
if extension == "csv":
|
if extension == "csv":
|
||||||
accounts = generate_data_from_csv(file_doc, as_dict=True)
|
accounts = generate_data_from_csv(file_doc, as_dict=True)
|
||||||
else:
|
else:
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ class ChequePrintTemplate(Document):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def create_or_update_cheque_print_format(template_name):
|
def create_or_update_cheque_print_format(template_name: str):
|
||||||
if not frappe.db.exists("Print Format", template_name):
|
if not frappe.db.exists("Print Format", template_name):
|
||||||
cheque_print = frappe.new_doc("Print Format")
|
cheque_print = frappe.new_doc("Print Format")
|
||||||
cheque_print.update(
|
cheque_print.update(
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
from datetime import date
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _, qb
|
from frappe import _, qb
|
||||||
@@ -614,7 +615,12 @@ def calculate_exchange_rate_using_last_gle(company, account, party_type, party):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_account_details(
|
def get_account_details(
|
||||||
company, posting_date, account, party_type=None, party=None, rounding_loss_allowance: float | None = None
|
company: str,
|
||||||
|
posting_date: str | date,
|
||||||
|
account: str,
|
||||||
|
party_type: str | None = None,
|
||||||
|
party: str | None = None,
|
||||||
|
rounding_loss_allowance: float = 0.0,
|
||||||
):
|
):
|
||||||
if not (company and posting_date):
|
if not (company and posting_date):
|
||||||
frappe.throw(_("Company and Posting Date is mandatory"))
|
frappe.throw(_("Company and Posting Date is mandatory"))
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from dateutil.relativedelta import relativedelta
|
from dateutil.relativedelta import relativedelta
|
||||||
from frappe import _
|
from frappe import _, cint
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
from frappe.utils import add_days, add_years, cstr, getdate
|
from frappe.utils import add_days, add_years, cstr, getdate
|
||||||
|
|
||||||
@@ -33,23 +33,11 @@ class FiscalYear(Document):
|
|||||||
self.validate_dates()
|
self.validate_dates()
|
||||||
self.validate_overlap()
|
self.validate_overlap()
|
||||||
|
|
||||||
if not self.is_new():
|
def on_update(self):
|
||||||
year_start_end_dates = frappe.db.sql(
|
frappe.cache().delete_key("fiscal_years")
|
||||||
"""select year_start_date, year_end_date
|
|
||||||
from `tabFiscal Year` where name=%s""",
|
|
||||||
(self.name),
|
|
||||||
)
|
|
||||||
|
|
||||||
if year_start_end_dates:
|
def on_trash(self):
|
||||||
if (
|
frappe.cache().delete_key("fiscal_years")
|
||||||
getdate(self.year_start_date) != year_start_end_dates[0][0]
|
|
||||||
or getdate(self.year_end_date) != year_start_end_dates[0][1]
|
|
||||||
):
|
|
||||||
frappe.throw(
|
|
||||||
_(
|
|
||||||
"Cannot change Fiscal Year Start Date and Fiscal Year End Date once the Fiscal Year is saved."
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
def validate_dates(self):
|
def validate_dates(self):
|
||||||
self.validate_from_to_dates("year_start_date", "year_end_date")
|
self.validate_from_to_dates("year_start_date", "year_end_date")
|
||||||
@@ -66,28 +54,20 @@ class FiscalYear(Document):
|
|||||||
frappe.exceptions.InvalidDates,
|
frappe.exceptions.InvalidDates,
|
||||||
)
|
)
|
||||||
|
|
||||||
def on_update(self):
|
|
||||||
check_duplicate_fiscal_year(self)
|
|
||||||
frappe.cache().delete_value("fiscal_years")
|
|
||||||
|
|
||||||
def on_trash(self):
|
|
||||||
frappe.cache().delete_value("fiscal_years")
|
|
||||||
|
|
||||||
def validate_overlap(self):
|
def validate_overlap(self):
|
||||||
existing_fiscal_years = frappe.db.sql(
|
fy = frappe.qb.DocType("Fiscal Year")
|
||||||
"""select name from `tabFiscal Year`
|
|
||||||
where (
|
name = self.name or self.year
|
||||||
(%(year_start_date)s between year_start_date and year_end_date)
|
|
||||||
or (%(year_end_date)s between year_start_date and year_end_date)
|
existing_fiscal_years = (
|
||||||
or (year_start_date between %(year_start_date)s and %(year_end_date)s)
|
frappe.qb.from_(fy)
|
||||||
or (year_end_date between %(year_start_date)s and %(year_end_date)s)
|
.select(fy.name)
|
||||||
) and name!=%(name)s""",
|
.where(
|
||||||
{
|
(fy.year_start_date <= self.year_end_date)
|
||||||
"year_start_date": self.year_start_date,
|
& (fy.year_end_date >= self.year_start_date)
|
||||||
"year_end_date": self.year_end_date,
|
& (fy.name != name)
|
||||||
"name": self.name or "No Name",
|
)
|
||||||
},
|
.run(as_dict=True)
|
||||||
as_dict=True,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if existing_fiscal_years:
|
if existing_fiscal_years:
|
||||||
@@ -110,37 +90,30 @@ class FiscalYear(Document):
|
|||||||
frappe.throw(
|
frappe.throw(
|
||||||
_(
|
_(
|
||||||
"Year start date or end date is overlapping with {0}. To avoid please set company"
|
"Year start date or end date is overlapping with {0}. To avoid please set company"
|
||||||
).format(existing.name),
|
).format(frappe.get_desk_link("Fiscal Year", existing.name, open_in_new_tab=True)),
|
||||||
frappe.NameError,
|
frappe.NameError,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
|
||||||
def check_duplicate_fiscal_year(doc):
|
|
||||||
year_start_end_dates = frappe.db.sql(
|
|
||||||
"""select name, year_start_date, year_end_date from `tabFiscal Year` where name!=%s""",
|
|
||||||
(doc.name),
|
|
||||||
)
|
|
||||||
for fiscal_year, ysd, yed in year_start_end_dates:
|
|
||||||
if (getdate(doc.year_start_date) == ysd and getdate(doc.year_end_date) == yed) and (
|
|
||||||
not frappe.in_test
|
|
||||||
):
|
|
||||||
frappe.throw(
|
|
||||||
_(
|
|
||||||
"Fiscal Year Start Date and Fiscal Year End Date are already set in Fiscal Year {0}"
|
|
||||||
).format(fiscal_year)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
|
||||||
def auto_create_fiscal_year():
|
def auto_create_fiscal_year():
|
||||||
for d in frappe.db.sql(
|
fy = frappe.qb.DocType("Fiscal Year")
|
||||||
"""select name from `tabFiscal Year` where year_end_date = date_add(current_date, interval 3 day)"""
|
|
||||||
):
|
# Skipped auto-creating Short Year, as it has very rare use case.
|
||||||
|
# Reference: https://www.irs.gov/businesses/small-businesses-self-employed/tax-years (US)
|
||||||
|
follow_up_date = add_days(getdate(), days=3)
|
||||||
|
fiscal_year = (
|
||||||
|
frappe.qb.from_(fy)
|
||||||
|
.select(fy.name)
|
||||||
|
.where((fy.year_end_date == follow_up_date) & (fy.is_short_year == 0))
|
||||||
|
.run()
|
||||||
|
)
|
||||||
|
|
||||||
|
for d in fiscal_year:
|
||||||
try:
|
try:
|
||||||
current_fy = frappe.get_doc("Fiscal Year", d[0])
|
current_fy = frappe.get_doc("Fiscal Year", d[0])
|
||||||
|
|
||||||
new_fy = frappe.copy_doc(current_fy, ignore_no_copy=False)
|
new_fy = frappe.new_doc("Fiscal Year")
|
||||||
|
new_fy.disabled = cint(current_fy.disabled)
|
||||||
|
|
||||||
new_fy.year_start_date = add_days(current_fy.year_end_date, 1)
|
new_fy.year_start_date = add_days(current_fy.year_end_date, 1)
|
||||||
new_fy.year_end_date = add_years(current_fy.year_end_date, 1)
|
new_fy.year_end_date = add_years(current_fy.year_end_date, 1)
|
||||||
@@ -148,6 +121,10 @@ def auto_create_fiscal_year():
|
|||||||
start_year = cstr(new_fy.year_start_date.year)
|
start_year = cstr(new_fy.year_start_date.year)
|
||||||
end_year = cstr(new_fy.year_end_date.year)
|
end_year = cstr(new_fy.year_end_date.year)
|
||||||
new_fy.year = start_year if start_year == end_year else (start_year + "-" + end_year)
|
new_fy.year = start_year if start_year == end_year else (start_year + "-" + end_year)
|
||||||
|
|
||||||
|
for row in current_fy.companies:
|
||||||
|
new_fy.append("companies", {"company": row.company})
|
||||||
|
|
||||||
new_fy.auto_created = 1
|
new_fy.auto_created = 1
|
||||||
|
|
||||||
new_fy.insert(ignore_permissions=True)
|
new_fy.insert(ignore_permissions=True)
|
||||||
|
|||||||
@@ -15,20 +15,22 @@
|
|||||||
"ignore_user_permissions": 1,
|
"ignore_user_permissions": 1,
|
||||||
"in_list_view": 1,
|
"in_list_view": 1,
|
||||||
"label": "Company",
|
"label": "Company",
|
||||||
"options": "Company"
|
"options": "Company",
|
||||||
|
"reqd": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2024-03-27 13:09:44.659251",
|
"modified": "2026-02-20 23:02:26.193606",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Fiscal Year Company",
|
"name": "Fiscal Year Company",
|
||||||
"owner": "Administrator",
|
"owner": "Administrator",
|
||||||
"permissions": [],
|
"permissions": [],
|
||||||
|
"row_format": "Dynamic",
|
||||||
"sort_field": "creation",
|
"sort_field": "creation",
|
||||||
"sort_order": "DESC",
|
"sort_order": "DESC",
|
||||||
"states": [],
|
"states": [],
|
||||||
"track_changes": 1
|
"track_changes": 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class FiscalYearCompany(Document):
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from frappe.types import DF
|
from frappe.types import DF
|
||||||
|
|
||||||
company: DF.Link | None
|
company: DF.Link
|
||||||
parent: DF.Data
|
parent: DF.Data
|
||||||
parentfield: DF.Data
|
parentfield: DF.Data
|
||||||
parenttype: DF.Data
|
parenttype: DF.Data
|
||||||
|
|||||||
@@ -317,7 +317,7 @@ class InvoiceDiscounting(AccountsController):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_invoices(filters):
|
def get_invoices(filters: str):
|
||||||
filters = frappe._dict(json.loads(filters))
|
filters = frappe._dict(json.loads(filters))
|
||||||
cond = []
|
cond = []
|
||||||
if filters.customer:
|
if filters.customer:
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
from datetime import date
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _, msgprint, scrub
|
from frappe import _, msgprint, scrub
|
||||||
from frappe.core.doctype.submission_queue.submission_queue import queue_submission
|
from frappe.core.doctype.submission_queue.submission_queue import queue_submission
|
||||||
|
from frappe.model.document import Document
|
||||||
from frappe.utils import comma_and, cstr, flt, fmt_money, formatdate, get_link_to_form, nowdate
|
from frappe.utils import comma_and, cstr, flt, fmt_money, formatdate, get_link_to_form, nowdate
|
||||||
|
|
||||||
import erpnext
|
import erpnext
|
||||||
@@ -1215,7 +1216,7 @@ class JournalEntry(AccountsController):
|
|||||||
cancel_exchange_gain_loss_journal(frappe._dict(doctype=self.doctype, name=self.name))
|
cancel_exchange_gain_loss_journal(frappe._dict(doctype=self.doctype, name=self.name))
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_balance(self, difference_account=None):
|
def get_balance(self, difference_account: str | None = None):
|
||||||
if not self.get("accounts"):
|
if not self.get("accounts"):
|
||||||
msgprint(_("'Entries' cannot be empty"), raise_exception=True)
|
msgprint(_("'Entries' cannot be empty"), raise_exception=True)
|
||||||
else:
|
else:
|
||||||
@@ -1321,7 +1322,12 @@ class JournalEntry(AccountsController):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_default_bank_cash_account(
|
def get_default_bank_cash_account(
|
||||||
company, account_type=None, mode_of_payment=None, account=None, *, fetch_balance=True
|
company: str,
|
||||||
|
account_type: str | None = None,
|
||||||
|
mode_of_payment: str | None = None,
|
||||||
|
account: str | None = None,
|
||||||
|
*,
|
||||||
|
fetch_balance: bool = True,
|
||||||
):
|
):
|
||||||
from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_bank_cash_account
|
from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_bank_cash_account
|
||||||
|
|
||||||
@@ -1370,7 +1376,12 @@ def get_default_bank_cash_account(
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_payment_entry_against_order(
|
def get_payment_entry_against_order(
|
||||||
dt, dn, amount=None, debit_in_account_currency=None, journal_entry=False, bank_account=None
|
dt: str,
|
||||||
|
dn: str,
|
||||||
|
amount: float | None = None,
|
||||||
|
debit_in_account_currency: str | float | None = None,
|
||||||
|
journal_entry: bool = False,
|
||||||
|
bank_account: str | None = None,
|
||||||
):
|
):
|
||||||
ref_doc = frappe.get_doc(dt, dn)
|
ref_doc = frappe.get_doc(dt, dn)
|
||||||
|
|
||||||
@@ -1415,7 +1426,12 @@ def get_payment_entry_against_order(
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_payment_entry_against_invoice(
|
def get_payment_entry_against_invoice(
|
||||||
dt, dn, amount=None, debit_in_account_currency=None, journal_entry=False, bank_account=None
|
dt: str,
|
||||||
|
dn: str,
|
||||||
|
amount: float | None = None,
|
||||||
|
debit_in_account_currency: str | None = None,
|
||||||
|
journal_entry: bool = False,
|
||||||
|
bank_account: str | None = None,
|
||||||
):
|
):
|
||||||
ref_doc = frappe.get_doc(dt, dn)
|
ref_doc = frappe.get_doc(dt, dn)
|
||||||
if dt == "Sales Invoice":
|
if dt == "Sales Invoice":
|
||||||
@@ -1528,7 +1544,7 @@ def get_payment_entry(ref_doc, args):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@frappe.validate_and_sanitize_search_inputs
|
@frappe.validate_and_sanitize_search_inputs
|
||||||
def get_against_jv(doctype, txt, searchfield, start, page_len, filters):
|
def get_against_jv(doctype: str, txt: str, searchfield: str, start: int, page_len: int, filters: dict):
|
||||||
if not frappe.db.has_column("Journal Entry", searchfield):
|
if not frappe.db.has_column("Journal Entry", searchfield):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
@@ -1559,7 +1575,7 @@ def get_against_jv(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_outstanding(args):
|
def get_outstanding(args: str | dict):
|
||||||
if not frappe.has_permission("Account"):
|
if not frappe.has_permission("Account"):
|
||||||
frappe.msgprint(_("No Permission"), raise_exception=1)
|
frappe.msgprint(_("No Permission"), raise_exception=1)
|
||||||
|
|
||||||
@@ -1619,7 +1635,7 @@ def get_outstanding(args):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_party_account_and_currency(company, party_type, party):
|
def get_party_account_and_currency(company: str, party_type: str, party: str):
|
||||||
if not frappe.has_permission("Account"):
|
if not frappe.has_permission("Account"):
|
||||||
frappe.msgprint(_("No Permission"), raise_exception=1)
|
frappe.msgprint(_("No Permission"), raise_exception=1)
|
||||||
|
|
||||||
@@ -1632,7 +1648,14 @@ def get_party_account_and_currency(company, party_type, party):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_account_details_and_party_type(account, date, company, debit=None, credit=None, exchange_rate=None):
|
def get_account_details_and_party_type(
|
||||||
|
account: str,
|
||||||
|
date: str,
|
||||||
|
company: str,
|
||||||
|
debit: float | str | None = None,
|
||||||
|
credit: float | str | None = None,
|
||||||
|
exchange_rate: float | str | None = None,
|
||||||
|
):
|
||||||
"""Returns dict of account details and party type to be set in Journal Entry on selection of account."""
|
"""Returns dict of account details and party type to be set in Journal Entry on selection of account."""
|
||||||
if not frappe.has_permission("Account"):
|
if not frappe.has_permission("Account"):
|
||||||
frappe.msgprint(_("No Permission"), raise_exception=1)
|
frappe.msgprint(_("No Permission"), raise_exception=1)
|
||||||
@@ -1681,15 +1704,15 @@ def get_account_details_and_party_type(account, date, company, debit=None, credi
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_exchange_rate(
|
def get_exchange_rate(
|
||||||
posting_date,
|
posting_date: str | date,
|
||||||
account=None,
|
account: str | None = None,
|
||||||
account_currency=None,
|
account_currency: str | None = None,
|
||||||
company=None,
|
company: str | None = None,
|
||||||
reference_type=None,
|
reference_type: str | None = None,
|
||||||
reference_name=None,
|
reference_name: str | None = None,
|
||||||
debit=None,
|
debit: float | str | None = None,
|
||||||
credit=None,
|
credit: float | str | None = None,
|
||||||
exchange_rate=None,
|
exchange_rate: str | float | None = None,
|
||||||
):
|
):
|
||||||
# Ensure exchange_rate is always numeric to avoid calculation errors
|
# Ensure exchange_rate is always numeric to avoid calculation errors
|
||||||
if isinstance(exchange_rate, str):
|
if isinstance(exchange_rate, str):
|
||||||
@@ -1726,7 +1749,7 @@ def get_exchange_rate(
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_average_exchange_rate(account):
|
def get_average_exchange_rate(account: str):
|
||||||
exchange_rate = 0
|
exchange_rate = 0
|
||||||
bank_balance_in_account_currency = get_balance_on(account)
|
bank_balance_in_account_currency = get_balance_on(account)
|
||||||
if bank_balance_in_account_currency:
|
if bank_balance_in_account_currency:
|
||||||
@@ -1737,7 +1760,7 @@ def get_average_exchange_rate(account):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_inter_company_journal_entry(name, voucher_type, company):
|
def make_inter_company_journal_entry(name: str, voucher_type: str, company: str):
|
||||||
journal_entry = frappe.new_doc("Journal Entry")
|
journal_entry = frappe.new_doc("Journal Entry")
|
||||||
journal_entry.voucher_type = voucher_type
|
journal_entry.voucher_type = voucher_type
|
||||||
journal_entry.company = company
|
journal_entry.company = company
|
||||||
@@ -1747,7 +1770,7 @@ def make_inter_company_journal_entry(name, voucher_type, company):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_reverse_journal_entry(source_name, target_doc=None):
|
def make_reverse_journal_entry(source_name: str, target_doc: str | Document | None = None):
|
||||||
existing_reverse = frappe.db.exists("Journal Entry", {"reversal_of": source_name, "docstatus": 1})
|
existing_reverse = frappe.db.exists("Journal Entry", {"reversal_of": source_name, "docstatus": 1})
|
||||||
if existing_reverse:
|
if existing_reverse:
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ class LedgerMerge(Document):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def form_start_merge(docname):
|
def form_start_merge(docname: str):
|
||||||
return frappe.get_doc("Ledger Merge", docname).start_merge()
|
return frappe.get_doc("Ledger Merge", docname).start_merge()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
from datetime import date
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
@@ -88,13 +89,13 @@ def get_loyalty_details(
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_loyalty_program_details_with_points(
|
def get_loyalty_program_details_with_points(
|
||||||
customer,
|
customer: str,
|
||||||
loyalty_program=None,
|
loyalty_program: str | None = None,
|
||||||
expiry_date=None,
|
expiry_date: str | date | None = None,
|
||||||
company=None,
|
company: str | None = None,
|
||||||
silent=False,
|
silent: bool = False,
|
||||||
include_expired_entry=False,
|
include_expired_entry: bool = False,
|
||||||
current_transaction_amount=0,
|
current_transaction_amount: int | float = 0,
|
||||||
):
|
):
|
||||||
lp_details = get_loyalty_program_details(customer, loyalty_program, company=company, silent=silent)
|
lp_details = get_loyalty_program_details(customer, loyalty_program, company=company, silent=silent)
|
||||||
loyalty_program = frappe.get_doc("Loyalty Program", loyalty_program)
|
loyalty_program = frappe.get_doc("Loyalty Program", loyalty_program)
|
||||||
@@ -119,12 +120,12 @@ def get_loyalty_program_details_with_points(
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_loyalty_program_details(
|
def get_loyalty_program_details(
|
||||||
customer,
|
customer: str,
|
||||||
loyalty_program=None,
|
loyalty_program: str | None = None,
|
||||||
expiry_date=None,
|
expiry_date: str | date | None = None,
|
||||||
company=None,
|
company: str | None = None,
|
||||||
silent=False,
|
silent: bool = False,
|
||||||
include_expired_entry=False,
|
include_expired_entry: bool = False,
|
||||||
):
|
):
|
||||||
lp_details = frappe._dict()
|
lp_details = frappe._dict()
|
||||||
|
|
||||||
@@ -146,7 +147,7 @@ def get_loyalty_program_details(
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_redeemption_factor(loyalty_program=None, customer=None):
|
def get_redeemption_factor(loyalty_program: str | None = None, customer: str | None = None):
|
||||||
customer_loyalty_program = None
|
customer_loyalty_program = None
|
||||||
if not loyalty_program:
|
if not loyalty_program:
|
||||||
customer_loyalty_program = frappe.db.get_value("Customer", customer, "loyalty_program")
|
customer_loyalty_program = frappe.db.get_value("Customer", customer, "loyalty_program")
|
||||||
|
|||||||
@@ -293,7 +293,7 @@ def publish(index, total, doctype):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_temporary_opening_account(company=None):
|
def get_temporary_opening_account(company: str | None = None):
|
||||||
if not company:
|
if not company:
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ class PartyLink(Document):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def create_party_link(primary_role, primary_party, secondary_party):
|
def create_party_link(primary_role: str, primary_party: str, secondary_party: str):
|
||||||
party_link = frappe.new_doc("Party Link")
|
party_link = frappe.new_doc("Party Link")
|
||||||
party_link.primary_role = primary_role
|
party_link.primary_role = primary_role
|
||||||
party_link.primary_party = primary_party
|
party_link.primary_party = primary_party
|
||||||
|
|||||||
@@ -512,12 +512,16 @@ frappe.ui.form.on("Payment Entry", {
|
|||||||
frm.set_value("contact_email", "");
|
frm.set_value("contact_email", "");
|
||||||
frm.set_value("contact_person", "");
|
frm.set_value("contact_person", "");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (frm.doc.payment_type && frm.doc.party_type && frm.doc.party && frm.doc.company) {
|
if (frm.doc.payment_type && frm.doc.party_type && frm.doc.party && frm.doc.company) {
|
||||||
if (!frm.doc.posting_date) {
|
if (!frm.doc.posting_date) {
|
||||||
frappe.msgprint(__("Please select Posting Date before selecting Party"));
|
frappe.msgprint(__("Please select Posting Date before selecting Party"));
|
||||||
frm.set_value("party", "");
|
frm.set_value("party", "");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
erpnext.utils.get_employee_contact_details(frm);
|
||||||
|
|
||||||
frm.set_party_account_based_on_party = true;
|
frm.set_party_account_based_on_party = true;
|
||||||
|
|
||||||
let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
|
let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
|
||||||
|
|||||||
@@ -701,7 +701,6 @@
|
|||||||
"fetch_from": "company.book_advance_payments_in_separate_party_account",
|
"fetch_from": "company.book_advance_payments_in_separate_party_account",
|
||||||
"fieldname": "book_advance_payments_in_separate_party_account",
|
"fieldname": "book_advance_payments_in_separate_party_account",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"hidden": 1,
|
|
||||||
"label": "Book Advance Payments in Separate Party Account",
|
"label": "Book Advance Payments in Separate Party Account",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"read_only": 1
|
"read_only": 1
|
||||||
@@ -793,7 +792,7 @@
|
|||||||
"table_fieldname": "payment_entries"
|
"table_fieldname": "payment_entries"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2025-12-18 13:56:40.206038",
|
"modified": "2026-02-03 16:08:49.800381",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Entry",
|
"name": "Payment Entry",
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
from datetime import date
|
||||||
from functools import reduce
|
from functools import reduce
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import ValidationError, _, qb, scrub, throw
|
from frappe import ValidationError, _, qb, scrub, throw
|
||||||
|
from frappe.model.document import Document
|
||||||
from frappe.model.meta import get_field_precision
|
from frappe.model.meta import get_field_precision
|
||||||
from frappe.query_builder import Tuple
|
from frappe.query_builder import Tuple
|
||||||
from frappe.query_builder.functions import Count
|
from frappe.query_builder.functions import Count
|
||||||
@@ -1064,8 +1065,12 @@ class PaymentEntry(AccountsController):
|
|||||||
total_allocated_amount += flt(d.allocated_amount)
|
total_allocated_amount += flt(d.allocated_amount)
|
||||||
base_total_allocated_amount += self.calculate_base_allocated_amount_for_reference(d)
|
base_total_allocated_amount += self.calculate_base_allocated_amount_for_reference(d)
|
||||||
|
|
||||||
self.total_allocated_amount = abs(total_allocated_amount)
|
self.total_allocated_amount = flt(
|
||||||
self.base_total_allocated_amount = abs(base_total_allocated_amount)
|
abs(total_allocated_amount), self.precision("total_allocated_amount")
|
||||||
|
)
|
||||||
|
self.base_total_allocated_amount = flt(
|
||||||
|
abs(base_total_allocated_amount), self.precision("base_total_allocated_amount")
|
||||||
|
)
|
||||||
|
|
||||||
def set_unallocated_amount(self):
|
def set_unallocated_amount(self):
|
||||||
self.unallocated_amount = 0
|
self.unallocated_amount = 0
|
||||||
@@ -1081,20 +1086,32 @@ class PaymentEntry(AccountsController):
|
|||||||
self.base_paid_amount + deductions_to_consider
|
self.base_paid_amount + deductions_to_consider
|
||||||
):
|
):
|
||||||
self.unallocated_amount = (
|
self.unallocated_amount = (
|
||||||
self.base_paid_amount
|
flt(
|
||||||
+ deductions_to_consider
|
(
|
||||||
- self.base_total_allocated_amount
|
self.base_paid_amount
|
||||||
- included_taxes
|
+ deductions_to_consider
|
||||||
) / self.source_exchange_rate
|
- self.base_total_allocated_amount
|
||||||
|
- included_taxes
|
||||||
|
),
|
||||||
|
self.precision("unallocated_amount"),
|
||||||
|
)
|
||||||
|
/ self.source_exchange_rate
|
||||||
|
)
|
||||||
elif self.payment_type == "Pay" and self.base_total_allocated_amount < (
|
elif self.payment_type == "Pay" and self.base_total_allocated_amount < (
|
||||||
self.base_received_amount - deductions_to_consider
|
self.base_received_amount - deductions_to_consider
|
||||||
):
|
):
|
||||||
self.unallocated_amount = (
|
self.unallocated_amount = (
|
||||||
self.base_received_amount
|
flt(
|
||||||
- deductions_to_consider
|
(
|
||||||
- self.base_total_allocated_amount
|
self.base_received_amount
|
||||||
- included_taxes
|
- deductions_to_consider
|
||||||
) / self.target_exchange_rate
|
- self.base_total_allocated_amount
|
||||||
|
- included_taxes
|
||||||
|
),
|
||||||
|
self.precision("unallocated_amount"),
|
||||||
|
)
|
||||||
|
/ self.target_exchange_rate
|
||||||
|
)
|
||||||
|
|
||||||
def set_exchange_gain_loss(self):
|
def set_exchange_gain_loss(self):
|
||||||
exchange_gain_loss = flt(
|
exchange_gain_loss = flt(
|
||||||
@@ -1867,7 +1884,9 @@ class PaymentEntry(AccountsController):
|
|||||||
frappe.response["matched_payment_requests"] = matched_payment_requests
|
frappe.response["matched_payment_requests"] = matched_payment_requests
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def allocate_amount_to_references(self, paid_amount, paid_amount_change, allocate_payment_amount):
|
def allocate_amount_to_references(
|
||||||
|
self, paid_amount: float, paid_amount_change: bool, allocate_payment_amount: bool
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Allocate `Allocated Amount` and `Payment Request` against `Reference` based on `Paid Amount` and `Outstanding Amount`.\n
|
Allocate `Allocated Amount` and `Payment Request` against `Reference` based on `Paid Amount` and `Outstanding Amount`.\n
|
||||||
:param paid_amount: Paid Amount / Received Amount.
|
:param paid_amount: Paid Amount / Received Amount.
|
||||||
@@ -2039,7 +2058,7 @@ class PaymentEntry(AccountsController):
|
|||||||
)
|
)
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def set_matched_payment_requests(self, matched_payment_requests):
|
def set_matched_payment_requests(self, matched_payment_requests: str | list | None):
|
||||||
"""
|
"""
|
||||||
Set `Payment Request` against `Reference` based on `matched_payment_requests`.\n
|
Set `Payment Request` against `Reference` based on `matched_payment_requests`.\n
|
||||||
:param matched_payment_requests: List of tuple of matched Payment Requests.
|
:param matched_payment_requests: List of tuple of matched Payment Requests.
|
||||||
@@ -2255,7 +2274,7 @@ def validate_inclusive_tax(tax, doc):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_outstanding_reference_documents(args, validate=False):
|
def get_outstanding_reference_documents(args: str | dict, validate: bool = False):
|
||||||
if isinstance(args, str):
|
if isinstance(args, str):
|
||||||
args = json.loads(args)
|
args = json.loads(args)
|
||||||
|
|
||||||
@@ -2670,7 +2689,7 @@ def get_negative_outstanding_invoices(
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_party_details(company, party_type, party, date, cost_center=None):
|
def get_party_details(company: str, party_type: str, party: str, date: str, cost_center: str | None = None):
|
||||||
bank_account = ""
|
bank_account = ""
|
||||||
party_bank_account = ""
|
party_bank_account = ""
|
||||||
|
|
||||||
@@ -2696,7 +2715,7 @@ def get_party_details(company, party_type, party, date, cost_center=None):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_account_details(account, date, cost_center=None):
|
def get_account_details(account: str, date: str | date, cost_center: str | None = None):
|
||||||
frappe.has_permission("Payment Entry", throw=True)
|
frappe.has_permission("Payment Entry", throw=True)
|
||||||
|
|
||||||
# to check if the passed account is accessible under reference doctype Payment Entry
|
# to check if the passed account is accessible under reference doctype Payment Entry
|
||||||
@@ -2716,7 +2735,7 @@ def get_account_details(account, date, cost_center=None):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_company_defaults(company):
|
def get_company_defaults(company: str):
|
||||||
fields = ["write_off_account", "exchange_gain_loss_account", "cost_center"]
|
fields = ["write_off_account", "exchange_gain_loss_account", "cost_center"]
|
||||||
return frappe.get_cached_value("Company", company, fields, as_dict=1)
|
return frappe.get_cached_value("Company", company, fields, as_dict=1)
|
||||||
|
|
||||||
@@ -2755,7 +2774,11 @@ def get_outstanding_on_journal_entry(voucher_no, party_type, party):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_reference_details(
|
def get_reference_details(
|
||||||
reference_doctype, reference_name, party_account_currency, party_type=None, party=None
|
reference_doctype: str,
|
||||||
|
reference_name: str,
|
||||||
|
party_account_currency: str,
|
||||||
|
party_type: str | None = None,
|
||||||
|
party: str | None = None,
|
||||||
):
|
):
|
||||||
total_amount = outstanding_amount = exchange_rate = account = None
|
total_amount = outstanding_amount = exchange_rate = account = None
|
||||||
|
|
||||||
@@ -2846,15 +2869,15 @@ def get_reference_details(
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_payment_entry(
|
def get_payment_entry(
|
||||||
dt,
|
dt: str,
|
||||||
dn,
|
dn: str,
|
||||||
party_amount=None,
|
party_amount: int | float | None = None,
|
||||||
bank_account=None,
|
bank_account: str | None = None,
|
||||||
bank_amount=None,
|
bank_amount: int | float | None = None,
|
||||||
party_type=None,
|
party_type: str | None = None,
|
||||||
payment_type=None,
|
payment_type: str | None = None,
|
||||||
reference_date=None,
|
reference_date: str | date | None = None,
|
||||||
created_from_payment_request=False,
|
created_from_payment_request: bool | None = None,
|
||||||
):
|
):
|
||||||
doc = frappe.get_doc(dt, dn)
|
doc = frappe.get_doc(dt, dn)
|
||||||
over_billing_allowance = frappe.get_single_value("Accounts Settings", "over_billing_allowance")
|
over_billing_allowance = frappe.get_single_value("Accounts Settings", "over_billing_allowance")
|
||||||
@@ -3520,7 +3543,7 @@ def get_paid_amount(dt, dn, party_type, party, account, due_date):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_payment_order(source_name, target_doc=None):
|
def make_payment_order(source_name: str, target_doc: str | Document | None = None):
|
||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
|
|
||||||
def set_missing_values(source, target):
|
def set_missing_values(source, target):
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ class PaymentOrder(Document):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@frappe.validate_and_sanitize_search_inputs
|
@frappe.validate_and_sanitize_search_inputs
|
||||||
def get_mop_query(doctype, txt, searchfield, start, page_len, filters):
|
def get_mop_query(doctype: str, txt: str, searchfield: str, start: int, page_len: int, filters: dict):
|
||||||
return frappe.db.sql(
|
return frappe.db.sql(
|
||||||
""" select mode_of_payment from `tabPayment Order Reference`
|
""" select mode_of_payment from `tabPayment Order Reference`
|
||||||
where parent = %(parent)s and mode_of_payment like %(txt)s
|
where parent = %(parent)s and mode_of_payment like %(txt)s
|
||||||
@@ -70,7 +70,7 @@ def get_mop_query(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@frappe.validate_and_sanitize_search_inputs
|
@frappe.validate_and_sanitize_search_inputs
|
||||||
def get_supplier_query(doctype, txt, searchfield, start, page_len, filters):
|
def get_supplier_query(doctype: str, txt: str, searchfield: str, start: int, page_len: int, filters: dict):
|
||||||
return frappe.db.sql(
|
return frappe.db.sql(
|
||||||
""" select supplier from `tabPayment Order Reference`
|
""" select supplier from `tabPayment Order Reference`
|
||||||
where parent = %(parent)s and supplier like %(txt)s and
|
where parent = %(parent)s and supplier like %(txt)s and
|
||||||
@@ -81,7 +81,7 @@ def get_supplier_query(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_payment_records(name, supplier, mode_of_payment=None):
|
def make_payment_records(name: str, supplier: str, mode_of_payment: str | None = None):
|
||||||
doc = frappe.get_doc("Payment Order", name)
|
doc = frappe.get_doc("Payment Order", name)
|
||||||
make_journal_entry(doc, supplier, mode_of_payment)
|
make_journal_entry(doc, supplier, mode_of_payment)
|
||||||
|
|
||||||
|
|||||||
@@ -433,7 +433,9 @@ class PaymentReconciliation(Document):
|
|||||||
return frappe.get_single_value("Accounts Settings", "auto_reconcile_payments")
|
return frappe.get_single_value("Accounts Settings", "auto_reconcile_payments")
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def calculate_difference_on_allocation_change(self, payment_entry, invoice, allocated_amount):
|
def calculate_difference_on_allocation_change(
|
||||||
|
self, payment_entry: list, invoice: list, allocated_amount: float
|
||||||
|
):
|
||||||
invoice_exchange_map = self.get_invoice_exchange_map(invoice, payment_entry)
|
invoice_exchange_map = self.get_invoice_exchange_map(invoice, payment_entry)
|
||||||
invoice[0]["exchange_rate"] = invoice_exchange_map.get(invoice[0].get("invoice_number"))
|
invoice[0]["exchange_rate"] = invoice_exchange_map.get(invoice[0].get("invoice_number"))
|
||||||
if payment_entry[0].get("reference_type") in ["Sales Invoice", "Purchase Invoice"]:
|
if payment_entry[0].get("reference_type") in ["Sales Invoice", "Purchase Invoice"]:
|
||||||
@@ -445,7 +447,7 @@ class PaymentReconciliation(Document):
|
|||||||
return new_difference_amount
|
return new_difference_amount
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def allocate_entries(self, args):
|
def allocate_entries(self, args: dict):
|
||||||
self.validate_entries()
|
self.validate_entries()
|
||||||
|
|
||||||
exc_gain_loss_posting_date = frappe.db.get_single_value(
|
exc_gain_loss_posting_date = frappe.db.get_single_value(
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
{
|
||||||
|
"actions": [],
|
||||||
|
"allow_rename": 1,
|
||||||
|
"creation": "2025-12-02 17:50:08.648006",
|
||||||
|
"doctype": "DocType",
|
||||||
|
"editable_grid": 1,
|
||||||
|
"engine": "InnoDB",
|
||||||
|
"field_order": [
|
||||||
|
"payment_term",
|
||||||
|
"column_break_lnjp",
|
||||||
|
"payment_schedule",
|
||||||
|
"section_break_fjhh",
|
||||||
|
"description",
|
||||||
|
"section_break_mjlv",
|
||||||
|
"due_date",
|
||||||
|
"column_break_qghl",
|
||||||
|
"amount"
|
||||||
|
],
|
||||||
|
"fields": [
|
||||||
|
{
|
||||||
|
"fieldname": "payment_term",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Payment Term",
|
||||||
|
"options": "Payment Term"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"collapsible": 1,
|
||||||
|
"fieldname": "section_break_fjhh",
|
||||||
|
"fieldtype": "Section Break",
|
||||||
|
"label": "Description"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "description",
|
||||||
|
"fieldtype": "Small Text",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Description"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_mjlv",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "due_date",
|
||||||
|
"fieldtype": "Date",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Due Date"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_qghl",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "amount",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"in_list_view": 1,
|
||||||
|
"label": "Amount",
|
||||||
|
"precision": "2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_lnjp",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"allow_on_submit": 1,
|
||||||
|
"fieldname": "payment_schedule",
|
||||||
|
"fieldtype": "Link",
|
||||||
|
"label": "Payment Schedule",
|
||||||
|
"options": "Payment Schedule",
|
||||||
|
"read_only": 1
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"grid_page_length": 50,
|
||||||
|
"index_web_pages_for_search": 1,
|
||||||
|
"istable": 1,
|
||||||
|
"links": [],
|
||||||
|
"modified": "2026-01-19 02:21:36.455830",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Accounts",
|
||||||
|
"name": "Payment Reference",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"permissions": [],
|
||||||
|
"row_format": "Dynamic",
|
||||||
|
"rows_threshold_for_grid_search": 20,
|
||||||
|
"sort_field": "creation",
|
||||||
|
"sort_order": "DESC",
|
||||||
|
"states": []
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# Copyright (c) 2025, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
# import frappe
|
||||||
|
from frappe.model.document import Document
|
||||||
|
|
||||||
|
|
||||||
|
class PaymentReference(Document):
|
||||||
|
# begin: auto-generated types
|
||||||
|
# This code is auto-generated. Do not modify anything in this block.
|
||||||
|
|
||||||
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from frappe.types import DF
|
||||||
|
|
||||||
|
amount: DF.Currency
|
||||||
|
description: DF.SmallText | None
|
||||||
|
due_date: DF.Date | None
|
||||||
|
parent: DF.Data
|
||||||
|
parentfield: DF.Data
|
||||||
|
parenttype: DF.Data
|
||||||
|
payment_schedule: DF.Link | None
|
||||||
|
payment_term: DF.Link | None
|
||||||
|
# end: auto-generated types
|
||||||
|
|
||||||
|
pass
|
||||||
@@ -105,3 +105,29 @@ frappe.ui.form.on("Payment Request", "is_a_subscription", function (frm) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
frappe.ui.form.on("Payment Request", "calculate_total_amount_by_selected_rows", function (frm) {
|
||||||
|
if (frm.doc.docstatus !== 0) {
|
||||||
|
frappe.msgprint(__("Cannot fetch selected rows for submitted Payment Request"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const selected = frm.get_selected()?.payment_reference || [];
|
||||||
|
if (!selected.length) {
|
||||||
|
frappe.throw(__("No rows selected"));
|
||||||
|
}
|
||||||
|
let total = 0;
|
||||||
|
selected.forEach((name) => {
|
||||||
|
const row = frm.doc.payment_reference.find((d) => d.name === name);
|
||||||
|
if (row) {
|
||||||
|
row.manually_selected = 1;
|
||||||
|
|
||||||
|
total += row.amount;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
frm.doc.payment_reference.forEach((row) => {
|
||||||
|
row.auto_selected = 0;
|
||||||
|
});
|
||||||
|
frm.set_value("grand_total", total);
|
||||||
|
frm.refresh_field("grand_total");
|
||||||
|
frm.save();
|
||||||
|
});
|
||||||
|
|||||||
@@ -19,6 +19,8 @@
|
|||||||
"column_break_4",
|
"column_break_4",
|
||||||
"reference_doctype",
|
"reference_doctype",
|
||||||
"reference_name",
|
"reference_name",
|
||||||
|
"payment_reference_section",
|
||||||
|
"payment_reference",
|
||||||
"transaction_details",
|
"transaction_details",
|
||||||
"grand_total",
|
"grand_total",
|
||||||
"currency",
|
"currency",
|
||||||
@@ -157,6 +159,7 @@
|
|||||||
"label": "Amount",
|
"label": "Amount",
|
||||||
"non_negative": 1,
|
"non_negative": 1,
|
||||||
"options": "currency",
|
"options": "currency",
|
||||||
|
"read_only_depends_on": "eval:doc.payment_reference.length>0",
|
||||||
"reqd": 1
|
"reqd": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -457,6 +460,17 @@
|
|||||||
"fieldname": "phone_number",
|
"fieldname": "phone_number",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"label": "Phone Number"
|
"label": "Phone Number"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "payment_reference_section",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "payment_reference",
|
||||||
|
"fieldtype": "Table",
|
||||||
|
"label": "Payment Reference",
|
||||||
|
"options": "Payment Reference",
|
||||||
|
"read_only": 1
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"grid_page_length": 50,
|
"grid_page_length": 50,
|
||||||
@@ -464,7 +478,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2025-08-29 11:52:48.555415",
|
"modified": "2026-01-13 12:53:00.963274",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Payment Request",
|
"name": "Payment Request",
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ class PaymentRequest(Document):
|
|||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from frappe.types import DF
|
from frappe.types import DF
|
||||||
|
|
||||||
|
from erpnext.accounts.doctype.payment_reference.payment_reference import PaymentReference
|
||||||
from erpnext.accounts.doctype.subscription_plan_detail.subscription_plan_detail import (
|
from erpnext.accounts.doctype.subscription_plan_detail.subscription_plan_detail import (
|
||||||
SubscriptionPlanDetail,
|
SubscriptionPlanDetail,
|
||||||
)
|
)
|
||||||
@@ -78,6 +79,7 @@ class PaymentRequest(Document):
|
|||||||
payment_gateway: DF.ReadOnly | None
|
payment_gateway: DF.ReadOnly | None
|
||||||
payment_gateway_account: DF.Link | None
|
payment_gateway_account: DF.Link | None
|
||||||
payment_order: DF.Link | None
|
payment_order: DF.Link | None
|
||||||
|
payment_reference: DF.Table[PaymentReference]
|
||||||
payment_request_type: DF.Literal["Outward", "Inward"]
|
payment_request_type: DF.Literal["Outward", "Inward"]
|
||||||
payment_url: DF.Data | None
|
payment_url: DF.Data | None
|
||||||
phone_number: DF.Data | None
|
phone_number: DF.Data | None
|
||||||
@@ -109,15 +111,36 @@ class PaymentRequest(Document):
|
|||||||
if self.get("__islocal"):
|
if self.get("__islocal"):
|
||||||
self.status = "Draft"
|
self.status = "Draft"
|
||||||
self.validate_reference_document()
|
self.validate_reference_document()
|
||||||
|
self.validate_against_payment_reference()
|
||||||
self.validate_payment_request_amount()
|
self.validate_payment_request_amount()
|
||||||
# self.validate_currency()
|
# self.validate_currency()
|
||||||
self.validate_subscription_details()
|
self.validate_subscription_details()
|
||||||
|
|
||||||
|
def validate_against_payment_reference(self):
|
||||||
|
if not self.payment_reference:
|
||||||
|
return
|
||||||
|
|
||||||
|
expected = sum(flt(r.amount) for r in self.payment_reference)
|
||||||
|
if flt(expected, self.precision("grand_total")) != flt(self.grand_total):
|
||||||
|
frappe.throw(_("Grand Total must match sum of Payment References"))
|
||||||
|
|
||||||
|
seen = set()
|
||||||
|
for r in self.payment_reference:
|
||||||
|
if not r.payment_schedule:
|
||||||
|
continue # legacy mode → skip
|
||||||
|
|
||||||
|
if r.payment_schedule in seen:
|
||||||
|
frappe.throw(_("Duplicate Payment Schedule selected"))
|
||||||
|
|
||||||
|
seen.add(r.payment_schedule)
|
||||||
|
|
||||||
def validate_reference_document(self):
|
def validate_reference_document(self):
|
||||||
if not self.reference_doctype or not self.reference_name:
|
if not self.reference_doctype or not self.reference_name:
|
||||||
frappe.throw(_("To create a Payment Request reference document is required"))
|
frappe.throw(_("To create a Payment Request reference document is required"))
|
||||||
|
|
||||||
def validate_payment_request_amount(self):
|
def validate_payment_request_amount(self):
|
||||||
|
if self.payment_reference:
|
||||||
|
return
|
||||||
if self.grand_total == 0:
|
if self.grand_total == 0:
|
||||||
frappe.throw(
|
frappe.throw(
|
||||||
_("{0} cannot be zero").format(self.get_label_from_fieldname("grand_total")),
|
_("{0} cannot be zero").format(self.get_label_from_fieldname("grand_total")),
|
||||||
@@ -552,9 +575,63 @@ def make_payment_request(**args):
|
|||||||
ref_doc = args.ref_doc or frappe.get_doc(args.dt, args.dn)
|
ref_doc = args.ref_doc or frappe.get_doc(args.dt, args.dn)
|
||||||
if not args.get("company"):
|
if not args.get("company"):
|
||||||
args.company = ref_doc.company
|
args.company = ref_doc.company
|
||||||
|
|
||||||
gateway_account = get_gateway_details(args) or frappe._dict()
|
gateway_account = get_gateway_details(args) or frappe._dict()
|
||||||
|
|
||||||
grand_total = get_amount(ref_doc, gateway_account.get("payment_account"))
|
# Schedule-based PRs are allowed only if no Payment Entry exists for this document.
|
||||||
|
# Any existing Payment Entry forces legacy (amount-based) flow.
|
||||||
|
selected_payment_schedules = json.loads(args.get("schedules")) if args.get("schedules") else []
|
||||||
|
|
||||||
|
# Backend guard:
|
||||||
|
# If any Payment Entry exists, schedule-based PRs are not allowed.
|
||||||
|
if selected_payment_schedules and get_existing_payment_entry(ref_doc.name):
|
||||||
|
frappe.throw(
|
||||||
|
_(
|
||||||
|
"Payment Schedule based Payment Requests cannot be created because a Payment Entry already exists for this document."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
has_payment_entry = bool(get_existing_payment_entry(ref_doc.name))
|
||||||
|
|
||||||
|
payment_reference = []
|
||||||
|
|
||||||
|
if selected_payment_schedules:
|
||||||
|
existing_payment_references = get_existing_payment_references(ref_doc.name)
|
||||||
|
|
||||||
|
if existing_payment_references:
|
||||||
|
existing_ids = {r["payment_schedule"] for r in existing_payment_references}
|
||||||
|
selected_ids = {r["name"] for r in selected_payment_schedules}
|
||||||
|
duplicate_ids = existing_ids & selected_ids
|
||||||
|
|
||||||
|
if duplicate_ids:
|
||||||
|
duplicate_schedules = []
|
||||||
|
for row in selected_payment_schedules:
|
||||||
|
if row["name"] in duplicate_ids:
|
||||||
|
existing_ref = next(
|
||||||
|
(r for r in existing_payment_references if r["payment_schedule"] == row["name"]),
|
||||||
|
{},
|
||||||
|
)
|
||||||
|
existing_pr = existing_ref.get("parent")
|
||||||
|
duplicate_schedules.append(
|
||||||
|
f"Payment Term: {row.get('payment_term')}, "
|
||||||
|
f"Due Date: {row.get('due_date')}, "
|
||||||
|
f"Amount: {row.get('payment_amount')} "
|
||||||
|
f"(already requested in PR {existing_pr})"
|
||||||
|
)
|
||||||
|
frappe.throw(
|
||||||
|
_("The following payment schedule(s) already exist:\n{0}").format(
|
||||||
|
"\n".join(duplicate_schedules)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
payment_reference = set_payment_references(args.get("schedules"))
|
||||||
|
|
||||||
|
# Determine grand_total
|
||||||
|
if selected_payment_schedules and not has_payment_entry:
|
||||||
|
grand_total = sum(row.get("payment_amount") for row in selected_payment_schedules)
|
||||||
|
else:
|
||||||
|
grand_total = get_amount(ref_doc, gateway_account.get("payment_account"))
|
||||||
|
|
||||||
if not grand_total:
|
if not grand_total:
|
||||||
frappe.throw(_("Payment Entry is already created"))
|
frappe.throw(_("Payment Entry is already created"))
|
||||||
|
|
||||||
@@ -564,7 +641,6 @@ def make_payment_request(**args):
|
|||||||
loyalty_amount = validate_loyalty_points(ref_doc, int(args.loyalty_points)) # sets fields on ref_doc
|
loyalty_amount = validate_loyalty_points(ref_doc, int(args.loyalty_points)) # sets fields on ref_doc
|
||||||
ref_doc.db_update()
|
ref_doc.db_update()
|
||||||
grand_total = grand_total - loyalty_amount
|
grand_total = grand_total - loyalty_amount
|
||||||
|
|
||||||
# fetches existing payment request `grand_total` amount
|
# fetches existing payment request `grand_total` amount
|
||||||
existing_payment_request_amount = get_existing_payment_request_amount(ref_doc)
|
existing_payment_request_amount = get_existing_payment_request_amount(ref_doc)
|
||||||
|
|
||||||
@@ -584,19 +660,20 @@ def make_payment_request(**args):
|
|||||||
else:
|
else:
|
||||||
# If PR's are processed, cancel all of them.
|
# If PR's are processed, cancel all of them.
|
||||||
cancel_old_payment_requests(ref_doc.doctype, ref_doc.name)
|
cancel_old_payment_requests(ref_doc.doctype, ref_doc.name)
|
||||||
else:
|
elif not selected_payment_schedules:
|
||||||
grand_total = validate_and_calculate_grand_total(grand_total, existing_payment_request_amount)
|
grand_total = validate_and_calculate_grand_total(grand_total, existing_payment_request_amount)
|
||||||
|
|
||||||
draft_payment_request = frappe.db.get_value(
|
draft_payment_request = frappe.db.get_value(
|
||||||
"Payment Request",
|
"Payment Request",
|
||||||
{"reference_doctype": ref_doc.doctype, "reference_name": ref_doc.name, "docstatus": 0},
|
{"reference_doctype": ref_doc.doctype, "reference_name": ref_doc.name, "docstatus": 0},
|
||||||
)
|
)
|
||||||
|
|
||||||
if draft_payment_request:
|
if draft_payment_request:
|
||||||
frappe.db.set_value(
|
|
||||||
"Payment Request", draft_payment_request, "grand_total", grand_total, update_modified=False
|
|
||||||
)
|
|
||||||
pr = frappe.get_doc("Payment Request", draft_payment_request)
|
pr = frappe.get_doc("Payment Request", draft_payment_request)
|
||||||
|
|
||||||
|
if selected_payment_schedules:
|
||||||
|
apply_payment_references(pr, payment_reference)
|
||||||
|
pr.save()
|
||||||
|
|
||||||
else:
|
else:
|
||||||
bank_account = (
|
bank_account = (
|
||||||
get_party_bank_account(args.get("party_type"), args.get("party"))
|
get_party_bank_account(args.get("party_type"), args.get("party"))
|
||||||
@@ -651,7 +728,10 @@ def make_payment_request(**args):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
# Update dimensions
|
if selected_payment_schedules:
|
||||||
|
apply_payment_references(pr, payment_reference)
|
||||||
|
|
||||||
|
# Dimensions
|
||||||
pr.update(
|
pr.update(
|
||||||
{
|
{
|
||||||
"cost_center": ref_doc.get("cost_center"),
|
"cost_center": ref_doc.get("cost_center"),
|
||||||
@@ -680,6 +760,51 @@ def make_payment_request(**args):
|
|||||||
return pr.as_dict()
|
return pr.as_dict()
|
||||||
|
|
||||||
|
|
||||||
|
def apply_payment_references(pr, payment_reference):
|
||||||
|
existing_refs = pr.get("payment_reference") or []
|
||||||
|
|
||||||
|
existing_ids = {r.get("payment_schedule") for r in existing_refs if r.get("payment_schedule")}
|
||||||
|
new_refs = [r for r in (payment_reference or []) if r.get("payment_schedule") not in existing_ids]
|
||||||
|
pr.set("payment_reference", existing_refs + new_refs)
|
||||||
|
pr.set("grand_total", sum(flt(r.get("amount")) for r in pr.get("payment_reference")))
|
||||||
|
|
||||||
|
|
||||||
|
def set_payment_references(payment_schedules):
|
||||||
|
payment_schedules = json.loads(payment_schedules) if payment_schedules else []
|
||||||
|
payment_reference = []
|
||||||
|
|
||||||
|
for row in payment_schedules:
|
||||||
|
payment_reference.append(
|
||||||
|
{
|
||||||
|
"payment_term": row.get("payment_term"),
|
||||||
|
"payment_schedule": row.get("name"),
|
||||||
|
"description": row.get("description"),
|
||||||
|
"due_date": row.get("due_date"),
|
||||||
|
"amount": row.get("payment_amount"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return payment_reference
|
||||||
|
|
||||||
|
|
||||||
|
def get_existing_payment_entry(ref_docname):
|
||||||
|
pe = frappe.qb.DocType("Payment Entry")
|
||||||
|
per = frappe.qb.DocType("Payment Entry Reference")
|
||||||
|
|
||||||
|
existing_pe = (
|
||||||
|
frappe.qb.from_(pe)
|
||||||
|
.join(per)
|
||||||
|
.on(per.parent == pe.name)
|
||||||
|
.select(pe.name)
|
||||||
|
.where(pe.docstatus < 2)
|
||||||
|
.where(per.reference_name == ref_docname)
|
||||||
|
.limit(1)
|
||||||
|
.run()
|
||||||
|
)
|
||||||
|
|
||||||
|
return existing_pe
|
||||||
|
|
||||||
|
|
||||||
def get_amount(ref_doc, payment_account=None):
|
def get_amount(ref_doc, payment_account=None):
|
||||||
"""get amount based on doctype"""
|
"""get amount based on doctype"""
|
||||||
grand_total = 0
|
grand_total = 0
|
||||||
@@ -812,7 +937,7 @@ def get_payment_gateway_account(filter):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_print_format_list(ref_doctype):
|
def get_print_format_list(ref_doctype: str):
|
||||||
print_format_list = ["Standard"]
|
print_format_list = ["Standard"]
|
||||||
|
|
||||||
print_format_list.extend(
|
print_format_list.extend(
|
||||||
@@ -823,12 +948,12 @@ def get_print_format_list(ref_doctype):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def resend_payment_email(docname):
|
def resend_payment_email(docname: str):
|
||||||
return frappe.get_doc("Payment Request", docname).send_email()
|
return frappe.get_doc("Payment Request", docname).send_email()
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_payment_entry(docname):
|
def make_payment_entry(docname: str):
|
||||||
doc = frappe.get_doc("Payment Request", docname)
|
doc = frappe.get_doc("Payment Request", docname)
|
||||||
return doc.create_payment_entry(submit=False).as_dict()
|
return doc.create_payment_entry(submit=False).as_dict()
|
||||||
|
|
||||||
@@ -921,7 +1046,7 @@ def get_dummy_message(doc):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_subscription_details(reference_doctype, reference_name):
|
def get_subscription_details(reference_doctype: str, reference_name: str):
|
||||||
if reference_doctype == "Sales Invoice":
|
if reference_doctype == "Sales Invoice":
|
||||||
subscriptions = frappe.db.sql(
|
subscriptions = frappe.db.sql(
|
||||||
"""SELECT parent as sub_name FROM `tabSubscription Invoice` WHERE invoice=%s""",
|
"""SELECT parent as sub_name FROM `tabSubscription Invoice` WHERE invoice=%s""",
|
||||||
@@ -937,7 +1062,7 @@ def get_subscription_details(reference_doctype, reference_name):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_payment_order(source_name, target_doc=None):
|
def make_payment_order(source_name: str, target_doc: str | Document | None = None):
|
||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
|
|
||||||
def set_missing_values(source, target):
|
def set_missing_values(source, target):
|
||||||
@@ -985,7 +1110,9 @@ def validate_payment(doc, method=None):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_open_payment_requests_query(doctype, txt, searchfield, start, page_len, filters):
|
def get_open_payment_requests_query(
|
||||||
|
doctype: str, txt: str, searchfield: str, start: int, page_len: int, filters: dict
|
||||||
|
):
|
||||||
# permission checks in `get_list()`
|
# permission checks in `get_list()`
|
||||||
filters = frappe._dict(filters)
|
filters = frappe._dict(filters)
|
||||||
|
|
||||||
@@ -1024,3 +1151,44 @@ def get_irequests_of_payment_request(doc: str | None = None) -> list:
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
@frappe.whitelist()
|
||||||
|
def get_available_payment_schedules(reference_doctype, reference_name):
|
||||||
|
ref_doc = frappe.get_doc(reference_doctype, reference_name)
|
||||||
|
|
||||||
|
if not hasattr(ref_doc, "payment_schedule") or not ref_doc.payment_schedule:
|
||||||
|
return []
|
||||||
|
|
||||||
|
if get_existing_payment_entry(reference_name):
|
||||||
|
return []
|
||||||
|
|
||||||
|
existing_refs = get_existing_payment_references(reference_name)
|
||||||
|
existing_ids = {r["payment_schedule"] for r in existing_refs if r.get("payment_schedule")}
|
||||||
|
|
||||||
|
return [r for r in ref_doc.payment_schedule if r.name not in existing_ids]
|
||||||
|
|
||||||
|
|
||||||
|
def get_existing_payment_references(reference_name):
|
||||||
|
PR = frappe.qb.DocType("Payment Request")
|
||||||
|
PRF = frappe.qb.DocType("Payment Reference")
|
||||||
|
|
||||||
|
result = (
|
||||||
|
frappe.qb.from_(PR)
|
||||||
|
.join(PRF)
|
||||||
|
.on(PR.name == PRF.parent)
|
||||||
|
.select(
|
||||||
|
PRF.payment_term,
|
||||||
|
PRF.due_date,
|
||||||
|
PRF.amount.as_("payment_amount"),
|
||||||
|
PRF.payment_schedule,
|
||||||
|
PRF.parent,
|
||||||
|
)
|
||||||
|
.where(PR.reference_name == reference_name)
|
||||||
|
.where(PR.docstatus < 2)
|
||||||
|
.where(
|
||||||
|
PR.status.isin(["Draft", "Requested", "Initiated", "Partially Paid", "Payment Ordered", "Paid"])
|
||||||
|
)
|
||||||
|
).run(as_dict=True)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|||||||
@@ -1,11 +1,13 @@
|
|||||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
# See license.txt
|
# See license.txt
|
||||||
|
|
||||||
|
import json
|
||||||
import re
|
import re
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe.tests import IntegrationTestCase
|
from frappe.tests import IntegrationTestCase
|
||||||
|
from frappe.utils import add_days, nowdate
|
||||||
|
|
||||||
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
|
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
|
||||||
from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_terms_template
|
from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_terms_template
|
||||||
@@ -850,3 +852,130 @@ class TestPaymentRequest(IntegrationTestCase):
|
|||||||
pr.load_from_db()
|
pr.load_from_db()
|
||||||
|
|
||||||
self.assertEqual(pr.grand_total, pi.outstanding_amount)
|
self.assertEqual(pr.grand_total, pi.outstanding_amount)
|
||||||
|
|
||||||
|
def test_payment_request_grand_total_from_selected_schedules(self):
|
||||||
|
po = create_purchase_order(do_not_save=1, currency="INR", qty=1, rate=100)
|
||||||
|
po.payment_schedule = []
|
||||||
|
|
||||||
|
po.append("payment_schedule", {"due_date": nowdate(), "payment_amount": 30})
|
||||||
|
po.append("payment_schedule", {"due_date": add_days(nowdate(), 1), "payment_amount": 30})
|
||||||
|
po.append("payment_schedule", {"due_date": add_days(nowdate(), 2), "payment_amount": 40})
|
||||||
|
|
||||||
|
po.save()
|
||||||
|
po.submit()
|
||||||
|
|
||||||
|
schedules = json.dumps(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"payment_term": row.payment_term,
|
||||||
|
"name": row.name,
|
||||||
|
"due_date": row.due_date,
|
||||||
|
"payment_amount": row.payment_amount,
|
||||||
|
"description": row.description,
|
||||||
|
}
|
||||||
|
for row in [po.payment_schedule[0], po.payment_schedule[2]]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
pr = make_payment_request(
|
||||||
|
dt="Purchase Order",
|
||||||
|
dn=po.name,
|
||||||
|
mute_email=1,
|
||||||
|
submit_doc=False,
|
||||||
|
return_doc=True,
|
||||||
|
schedules=schedules,
|
||||||
|
)
|
||||||
|
|
||||||
|
pr.submit()
|
||||||
|
|
||||||
|
self.assertEqual(pr.grand_total, 70)
|
||||||
|
self.assertEqual(len(pr.payment_reference), 2)
|
||||||
|
|
||||||
|
def test_draft_pr_reuse_merges_payment_references(self):
|
||||||
|
from frappe.utils import add_days, nowdate
|
||||||
|
|
||||||
|
po = create_purchase_order(do_not_save=1, currency="INR", qty=1, rate=100)
|
||||||
|
po.payment_schedule = []
|
||||||
|
po.append("payment_schedule", {"due_date": nowdate(), "payment_amount": 50})
|
||||||
|
po.append("payment_schedule", {"due_date": add_days(nowdate(), 1), "payment_amount": 50})
|
||||||
|
po.save()
|
||||||
|
po.submit()
|
||||||
|
schedules = json.dumps(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"payment_term": row.payment_term,
|
||||||
|
"name": row.name,
|
||||||
|
"due_date": row.due_date,
|
||||||
|
"payment_amount": row.payment_amount,
|
||||||
|
"description": row.description,
|
||||||
|
}
|
||||||
|
for row in [po.payment_schedule[0]]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
pr = make_payment_request(
|
||||||
|
dt="Purchase Order",
|
||||||
|
dn=po.name,
|
||||||
|
mute_email=1,
|
||||||
|
submit_doc=False,
|
||||||
|
return_doc=True,
|
||||||
|
schedules=schedules,
|
||||||
|
)
|
||||||
|
|
||||||
|
pr.save()
|
||||||
|
schedules = json.dumps(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"payment_term": row.payment_term,
|
||||||
|
"name": row.name,
|
||||||
|
"due_date": row.due_date,
|
||||||
|
"payment_amount": row.payment_amount,
|
||||||
|
"description": row.description,
|
||||||
|
}
|
||||||
|
for row in [po.payment_schedule[1]]
|
||||||
|
]
|
||||||
|
)
|
||||||
|
# call make_payment_request again → reuse draft
|
||||||
|
pr_reused = make_payment_request(
|
||||||
|
dt="Purchase Order",
|
||||||
|
dn=po.name,
|
||||||
|
mute_email=1,
|
||||||
|
submit_doc=False,
|
||||||
|
return_doc=True,
|
||||||
|
schedules=schedules,
|
||||||
|
)
|
||||||
|
|
||||||
|
self.assertEqual(pr.name, pr_reused.name)
|
||||||
|
self.assertEqual(pr_reused.grand_total, 100)
|
||||||
|
self.assertEqual(len(pr_reused.payment_reference), 2)
|
||||||
|
|
||||||
|
def test_schedule_pr_not_allowed_if_payment_entry_exists(self):
|
||||||
|
po = create_purchase_order(do_not_save=1, currency="INR", qty=1, rate=100)
|
||||||
|
po.payment_schedule = []
|
||||||
|
row = po.append("payment_schedule", {"due_date": nowdate(), "payment_amount": 100})
|
||||||
|
po.save()
|
||||||
|
po.submit()
|
||||||
|
|
||||||
|
# create PE first
|
||||||
|
pr = make_payment_request(dt="Purchase Order", dn=po.name, mute_email=1, submit_doc=1, return_doc=1)
|
||||||
|
pr.create_payment_entry()
|
||||||
|
|
||||||
|
schedules = json.dumps(
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": row.name,
|
||||||
|
"payment_term": row.payment_term,
|
||||||
|
"due_date": row.due_date,
|
||||||
|
"payment_amount": row.payment_amount,
|
||||||
|
"description": row.description,
|
||||||
|
}
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
with self.assertRaises(frappe.ValidationError):
|
||||||
|
make_payment_request(
|
||||||
|
dt="Purchase Order",
|
||||||
|
dn=po.name,
|
||||||
|
mute_email=1,
|
||||||
|
submit_doc=False,
|
||||||
|
return_doc=True,
|
||||||
|
schedules=schedules,
|
||||||
|
)
|
||||||
|
|||||||
@@ -515,7 +515,7 @@ def delete_closing_entries(voucher_no):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_period_start_end_date(fiscal_year, company):
|
def get_period_start_end_date(fiscal_year: str, company: str):
|
||||||
fy_start_date, fy_end_date = frappe.db.get_value(
|
fy_start_date, fy_end_date = frappe.db.get_value(
|
||||||
"Fiscal Year", fiscal_year, ["year_start_date", "year_end_date"]
|
"Fiscal Year", fiscal_year, ["year_start_date", "year_end_date"]
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.query_builder import DocType
|
from frappe.query_builder import DocType
|
||||||
@@ -252,13 +254,13 @@ class POSClosingEntry(StatusUpdater):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@frappe.validate_and_sanitize_search_inputs
|
@frappe.validate_and_sanitize_search_inputs
|
||||||
def get_cashiers(doctype, txt, searchfield, start, page_len, filters):
|
def get_cashiers(doctype: str, txt: str, searchfield: str, start: int, page_len: int, filters: dict):
|
||||||
cashiers_list = frappe.get_all("POS Profile User", filters=filters, fields=["user"], as_list=1)
|
cashiers_list = frappe.get_all("POS Profile User", filters=filters, fields=["user"], as_list=1)
|
||||||
return [c for c in cashiers_list]
|
return [c for c in cashiers_list]
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_invoices(start, end, pos_profile, user):
|
def get_invoices(start: str | datetime, end: str | datetime, pos_profile: str, user: str):
|
||||||
invoice_doctype = frappe.db.get_single_value("POS Settings", "invoice_type")
|
invoice_doctype = frappe.db.get_single_value("POS Settings", "invoice_type")
|
||||||
|
|
||||||
sales_inv_query = build_invoice_query("Sales Invoice", user, pos_profile, start, end)
|
sales_inv_query = build_invoice_query("Sales Invoice", user, pos_profile, start, end)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _, bold
|
from frappe import _, bold
|
||||||
|
from frappe.model.document import Document
|
||||||
from frappe.model.mapper import map_child_doc, map_doc
|
from frappe.model.mapper import map_child_doc, map_doc
|
||||||
from frappe.query_builder.functions import IfNull, Sum
|
from frappe.query_builder.functions import IfNull, Sum
|
||||||
from frappe.utils import cint, flt, get_link_to_form, getdate, nowdate
|
from frappe.utils import cint, flt, get_link_to_form, getdate, nowdate
|
||||||
@@ -753,7 +754,7 @@ class POSInvoice(SalesInvoice):
|
|||||||
return profile
|
return profile
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def set_missing_values(self, for_validate=False):
|
def set_missing_values(self, for_validate: bool = False):
|
||||||
profile = self.set_pos_fields(for_validate)
|
profile = self.set_pos_fields(for_validate)
|
||||||
|
|
||||||
if not self.debit_to:
|
if not self.debit_to:
|
||||||
@@ -854,7 +855,7 @@ class POSInvoice(SalesInvoice):
|
|||||||
return frappe.get_doc("Payment Request", pr)
|
return frappe.get_doc("Payment Request", pr)
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def update_payments(self, payments):
|
def update_payments(self, payments: list):
|
||||||
if self.status == "Consolidated":
|
if self.status == "Consolidated":
|
||||||
frappe.throw(_("Create Payment Entry for Consolidated POS Invoices."))
|
frappe.throw(_("Create Payment Entry for Consolidated POS Invoices."))
|
||||||
|
|
||||||
@@ -897,7 +898,7 @@ class POSInvoice(SalesInvoice):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_stock_availability(item_code, warehouse):
|
def get_stock_availability(item_code: str | None, warehouse: str):
|
||||||
if frappe.db.get_value("Item", item_code, "is_stock_item"):
|
if frappe.db.get_value("Item", item_code, "is_stock_item"):
|
||||||
is_stock_item = True
|
is_stock_item = True
|
||||||
bin_qty = get_bin_qty(item_code, warehouse)
|
bin_qty = get_bin_qty(item_code, warehouse)
|
||||||
@@ -1020,14 +1021,14 @@ def get_pos_reserved_qty_from_table(child_table, item_code, warehouse):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_sales_return(source_name, target_doc=None):
|
def make_sales_return(source_name: str, target_doc: Document | None = None):
|
||||||
from erpnext.controllers.sales_and_purchase_return import make_return_doc
|
from erpnext.controllers.sales_and_purchase_return import make_return_doc
|
||||||
|
|
||||||
return make_return_doc("POS Invoice", source_name, target_doc)
|
return make_return_doc("POS Invoice", source_name, target_doc)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_merge_log(invoices):
|
def make_merge_log(invoices: str | list):
|
||||||
import json
|
import json
|
||||||
|
|
||||||
if isinstance(invoices, str):
|
if isinstance(invoices, str):
|
||||||
@@ -1077,7 +1078,15 @@ def add_return_modes(doc, pos_profile):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@frappe.validate_and_sanitize_search_inputs
|
@frappe.validate_and_sanitize_search_inputs
|
||||||
def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False):
|
def item_query(
|
||||||
|
doctype: str,
|
||||||
|
txt: str,
|
||||||
|
searchfield: str,
|
||||||
|
start: int,
|
||||||
|
page_len: int,
|
||||||
|
filters: dict,
|
||||||
|
as_dict: bool = False,
|
||||||
|
):
|
||||||
if pos_profile := filters.get("pos_profile")[1]:
|
if pos_profile := filters.get("pos_profile")[1]:
|
||||||
pos_profile = frappe.get_cached_doc("POS Profile", pos_profile)
|
pos_profile = frappe.get_cached_doc("POS Profile", pos_profile)
|
||||||
if item_groups := get_item_group(pos_profile):
|
if item_groups := get_item_group(pos_profile):
|
||||||
|
|||||||
@@ -275,7 +275,7 @@ def get_child_nodes(group_type, root):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@frappe.validate_and_sanitize_search_inputs
|
@frappe.validate_and_sanitize_search_inputs
|
||||||
def pos_profile_query(doctype, txt, searchfield, start, page_len, filters):
|
def pos_profile_query(doctype: str, txt: str, searchfield: str, start: int, page_len: int, filters: dict):
|
||||||
user = frappe.session["user"]
|
user = frappe.session["user"]
|
||||||
company = filters.get("company") or frappe.defaults.get_user_default("company")
|
company = filters.get("company") or frappe.defaults.get_user_default("company")
|
||||||
|
|
||||||
@@ -319,7 +319,7 @@ def pos_profile_query(doctype, txt, searchfield, start, page_len, filters):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def set_default_profile(pos_profile, company):
|
def set_default_profile(pos_profile: str, company: str):
|
||||||
modified = now()
|
modified = now()
|
||||||
user = frappe.session.user
|
user = frappe.session.user
|
||||||
|
|
||||||
|
|||||||
@@ -320,7 +320,7 @@ class PricingRule(Document):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def apply_pricing_rule(args, doc=None):
|
def apply_pricing_rule(args: str | dict, doc: str | dict | Document | None = None):
|
||||||
"""
|
"""
|
||||||
args = {
|
args = {
|
||||||
"items": [{"doctype": "", "name": "", "item_code": "", "brand": "", "item_group": ""}, ...],
|
"items": [{"doctype": "", "name": "", "item_code": "", "brand": "", "item_group": ""}, ...],
|
||||||
@@ -618,7 +618,12 @@ def apply_price_discount_rule(pricing_rule, item_details, args):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def remove_pricing_rule_for_item(pricing_rules, item_details, item_code=None, rate=None):
|
def remove_pricing_rule_for_item(
|
||||||
|
pricing_rules: str | None,
|
||||||
|
item_details: str | frappe._dict,
|
||||||
|
item_code: str | None = None,
|
||||||
|
rate: float | None = None,
|
||||||
|
):
|
||||||
from erpnext.accounts.doctype.pricing_rule.utils import (
|
from erpnext.accounts.doctype.pricing_rule.utils import (
|
||||||
get_applied_pricing_rules,
|
get_applied_pricing_rules,
|
||||||
get_pricing_rule_items,
|
get_pricing_rule_items,
|
||||||
@@ -666,7 +671,7 @@ def remove_pricing_rule_for_item(pricing_rules, item_details, item_code=None, ra
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def remove_pricing_rules(item_list):
|
def remove_pricing_rules(item_list: str | list):
|
||||||
if isinstance(item_list, str):
|
if isinstance(item_list, str):
|
||||||
item_list = json.loads(item_list)
|
item_list = json.loads(item_list)
|
||||||
|
|
||||||
@@ -704,7 +709,7 @@ def set_transaction_type(args):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@frappe.validate_and_sanitize_search_inputs
|
@frappe.validate_and_sanitize_search_inputs
|
||||||
def get_item_uoms(doctype, txt, searchfield, start, page_len, filters):
|
def get_item_uoms(doctype: str, txt: str, searchfield: str, start: int, page_len: int, filters: dict):
|
||||||
items = [filters.get("value")]
|
items = [filters.get("value")]
|
||||||
if filters.get("apply_on") != "Item Code":
|
if filters.get("apply_on") != "Item Code":
|
||||||
field = frappe.scrub(filters.get("apply_on"))
|
field = frappe.scrub(filters.get("apply_on"))
|
||||||
|
|||||||
@@ -420,7 +420,7 @@ def get_context(customer, doc):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def fetch_customers(customer_collection, collection_name, primary_mandatory):
|
def fetch_customers(customer_collection: str, collection_name: str, primary_mandatory: str | int):
|
||||||
customer_list = []
|
customer_list = []
|
||||||
customers = []
|
customers = []
|
||||||
|
|
||||||
@@ -460,7 +460,7 @@ def fetch_customers(customer_collection, collection_name, primary_mandatory):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_customer_emails(customer_name, primary_mandatory, billing_and_primary=True):
|
def get_customer_emails(customer_name: str, primary_mandatory: str | int, billing_and_primary: bool = True):
|
||||||
"""Returns first email from Contact Email table as a Billing email
|
"""Returns first email from Contact Email table as a Billing email
|
||||||
when Is Billing Contact checked
|
when Is Billing Contact checked
|
||||||
and Primary email- email with Is Primary checked"""
|
and Primary email- email with Is Primary checked"""
|
||||||
@@ -506,7 +506,7 @@ def get_customer_emails(customer_name, primary_mandatory, billing_and_primary=Tr
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def download_statements(document_name):
|
def download_statements(document_name: str):
|
||||||
doc = frappe.get_doc("Process Statement Of Accounts", document_name)
|
doc = frappe.get_doc("Process Statement Of Accounts", document_name)
|
||||||
report = get_report_pdf(doc)
|
report = get_report_pdf(doc)
|
||||||
if report:
|
if report:
|
||||||
@@ -516,7 +516,7 @@ def download_statements(document_name):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def send_emails(document_name, from_scheduler=False, posting_date=None):
|
def send_emails(document_name: str, from_scheduler: bool = False, posting_date: str | None = None):
|
||||||
doc = frappe.get_doc("Process Statement Of Accounts", document_name)
|
doc = frappe.get_doc("Process Statement Of Accounts", document_name)
|
||||||
report = get_report_pdf(doc, consolidated=False)
|
report = get_report_pdf(doc, consolidated=False)
|
||||||
|
|
||||||
|
|||||||
@@ -18,8 +18,19 @@ class TestProcessStatementOfAccounts(AccountsTestMixin, IntegrationTestCase):
|
|||||||
@classmethod
|
@classmethod
|
||||||
def setUpClass(cls):
|
def setUpClass(cls):
|
||||||
super().setUpClass()
|
super().setUpClass()
|
||||||
|
letterhead = frappe.get_doc("Letter Head", "Company Letterhead - Grey")
|
||||||
|
letterhead.is_default = 0
|
||||||
|
letterhead.save()
|
||||||
cls.enterClassContext(cls.change_settings("Selling Settings", validate_selling_price=0))
|
cls.enterClassContext(cls.change_settings("Selling Settings", validate_selling_price=0))
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def tearDownClass(cls):
|
||||||
|
super().tearDownClass()
|
||||||
|
letterhead = frappe.get_doc("Letter Head", "Company Letterhead - Grey")
|
||||||
|
letterhead.is_default = 1
|
||||||
|
letterhead.save()
|
||||||
|
frappe.db.commit() # nosemgrep
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
self.create_company()
|
self.create_company()
|
||||||
self.create_customer()
|
self.create_customer()
|
||||||
|
|||||||
@@ -134,7 +134,7 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
|
|||||||
this.frm.add_custom_button(
|
this.frm.add_custom_button(
|
||||||
__("Payment Request"),
|
__("Payment Request"),
|
||||||
function () {
|
function () {
|
||||||
me.make_payment_request();
|
me.make_payment_request_with_schedule();
|
||||||
},
|
},
|
||||||
__("Create")
|
__("Create")
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -86,18 +86,18 @@
|
|||||||
"taxes_and_charges_deducted",
|
"taxes_and_charges_deducted",
|
||||||
"total_taxes_and_charges",
|
"total_taxes_and_charges",
|
||||||
"totals_section",
|
"totals_section",
|
||||||
|
"use_company_roundoff_cost_center",
|
||||||
"grand_total",
|
"grand_total",
|
||||||
|
"in_words",
|
||||||
|
"column_break8",
|
||||||
"disable_rounded_total",
|
"disable_rounded_total",
|
||||||
"rounding_adjustment",
|
"rounding_adjustment",
|
||||||
"column_break8",
|
|
||||||
"use_company_roundoff_cost_center",
|
|
||||||
"in_words",
|
|
||||||
"rounded_total",
|
"rounded_total",
|
||||||
"base_totals_section",
|
"base_totals_section",
|
||||||
"base_grand_total",
|
"base_grand_total",
|
||||||
"base_rounding_adjustment",
|
|
||||||
"column_break_hcca",
|
|
||||||
"base_in_words",
|
"base_in_words",
|
||||||
|
"column_break_hcca",
|
||||||
|
"base_rounding_adjustment",
|
||||||
"base_rounded_total",
|
"base_rounded_total",
|
||||||
"section_break_ttrv",
|
"section_break_ttrv",
|
||||||
"total_advance",
|
"total_advance",
|
||||||
@@ -1689,7 +1689,7 @@
|
|||||||
"idx": 204,
|
"idx": 204,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2026-02-05 20:45:16.964500",
|
"modified": "2026-02-23 13:23:57.269770",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Purchase Invoice",
|
"name": "Purchase Invoice",
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import json
|
|||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _, qb, throw
|
from frappe import _, qb, throw
|
||||||
|
from frappe.model.document import Document
|
||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
from frappe.query_builder.functions import Sum
|
from frappe.query_builder.functions import Sum
|
||||||
from frappe.utils import cint, cstr, flt, formatdate, get_link_to_form, getdate, nowdate
|
from frappe.utils import cint, cstr, flt, formatdate, get_link_to_form, getdate, nowdate
|
||||||
@@ -1745,10 +1746,6 @@ class PurchaseInvoice(BuyingController):
|
|||||||
project_doc.db_update()
|
project_doc.db_update()
|
||||||
|
|
||||||
def validate_supplier_invoice(self):
|
def validate_supplier_invoice(self):
|
||||||
if self.bill_date:
|
|
||||||
if getdate(self.bill_date) > getdate(self.posting_date):
|
|
||||||
frappe.throw(_("Supplier Invoice Date cannot be greater than Posting Date"))
|
|
||||||
|
|
||||||
if self.bill_no:
|
if self.bill_no:
|
||||||
if cint(frappe.get_single_value("Accounts Settings", "check_supplier_invoice_uniqueness")):
|
if cint(frappe.get_single_value("Accounts Settings", "check_supplier_invoice_uniqueness")):
|
||||||
fiscal_year = get_fiscal_year(self.posting_date, company=self.company, as_dict=True)
|
fiscal_year = get_fiscal_year(self.posting_date, company=self.company, as_dict=True)
|
||||||
@@ -1945,14 +1942,14 @@ def make_regional_gl_entries(gl_entries, doc):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_debit_note(source_name, target_doc=None):
|
def make_debit_note(source_name: str, target_doc: str | Document | None = None):
|
||||||
from erpnext.controllers.sales_and_purchase_return import make_return_doc
|
from erpnext.controllers.sales_and_purchase_return import make_return_doc
|
||||||
|
|
||||||
return make_return_doc("Purchase Invoice", source_name, target_doc)
|
return make_return_doc("Purchase Invoice", source_name, target_doc)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_stock_entry(source_name, target_doc=None):
|
def make_stock_entry(source_name: str, target_doc: str | Document | None = None):
|
||||||
doc = get_mapped_doc(
|
doc = get_mapped_doc(
|
||||||
"Purchase Invoice",
|
"Purchase Invoice",
|
||||||
source_name,
|
source_name,
|
||||||
@@ -1970,35 +1967,37 @@ def make_stock_entry(source_name, target_doc=None):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def change_release_date(name, release_date=None):
|
def change_release_date(name: str, release_date: str | None = None):
|
||||||
if frappe.db.exists("Purchase Invoice", name):
|
if frappe.db.exists("Purchase Invoice", name):
|
||||||
pi = frappe.get_lazy_doc("Purchase Invoice", name)
|
pi = frappe.get_lazy_doc("Purchase Invoice", name)
|
||||||
pi.db_set("release_date", release_date)
|
pi.db_set("release_date", release_date)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def unblock_invoice(name):
|
def unblock_invoice(name: str):
|
||||||
if frappe.db.exists("Purchase Invoice", name):
|
if frappe.db.exists("Purchase Invoice", name):
|
||||||
pi = frappe.get_lazy_doc("Purchase Invoice", name)
|
pi = frappe.get_lazy_doc("Purchase Invoice", name)
|
||||||
pi.unblock_invoice()
|
pi.unblock_invoice()
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def block_invoice(name, release_date, hold_comment=None):
|
def block_invoice(name: str, release_date: str, hold_comment: str | None = None):
|
||||||
if frappe.db.exists("Purchase Invoice", name):
|
if frappe.db.exists("Purchase Invoice", name):
|
||||||
pi = frappe.get_lazy_doc("Purchase Invoice", name)
|
pi = frappe.get_lazy_doc("Purchase Invoice", name)
|
||||||
pi.block_invoice(hold_comment, release_date)
|
pi.block_invoice(hold_comment, release_date)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_inter_company_sales_invoice(source_name, target_doc=None):
|
def make_inter_company_sales_invoice(source_name: str, target_doc: Document | None = None):
|
||||||
from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_company_transaction
|
from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_company_transaction
|
||||||
|
|
||||||
return make_inter_company_transaction("Purchase Invoice", source_name, target_doc)
|
return make_inter_company_transaction("Purchase Invoice", source_name, target_doc)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_purchase_receipt(source_name, target_doc=None, args=None):
|
def make_purchase_receipt(
|
||||||
|
source_name: str, target_doc: str | Document | None = None, args: str | dict | None = None
|
||||||
|
):
|
||||||
if args is None:
|
if args is None:
|
||||||
args = {}
|
args = {}
|
||||||
if isinstance(args, str):
|
if isinstance(args, str):
|
||||||
|
|||||||
@@ -152,7 +152,7 @@ class RepostAccountingLedger(Document):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def start_repost(account_repost_doc=str) -> None:
|
def start_repost(account_repost_doc: str | None = None) -> None:
|
||||||
from erpnext.accounts.general_ledger import make_reverse_gl_entries
|
from erpnext.accounts.general_ledger import make_reverse_gl_entries
|
||||||
|
|
||||||
frappe.flags.through_repost_accounting_ledger = True
|
frappe.flags.through_repost_accounting_ledger = True
|
||||||
@@ -286,7 +286,9 @@ def validate_docs_for_voucher_types(doc_voucher_types):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@frappe.validate_and_sanitize_search_inputs
|
@frappe.validate_and_sanitize_search_inputs
|
||||||
def get_repost_allowed_types(doctype, txt, searchfield, start, page_len, filters):
|
def get_repost_allowed_types(
|
||||||
|
doctype: str, txt: str, searchfield: str, start: int, page_len: int, filters: dict
|
||||||
|
):
|
||||||
filters = {"allowed": True}
|
filters = {"allowed": True}
|
||||||
|
|
||||||
if txt:
|
if txt:
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ def repost_ple_for_voucher(voucher_type, voucher_no, gle_map=None):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def start_payment_ledger_repost(docname=None):
|
def start_payment_ledger_repost(docname: str | None = None):
|
||||||
"""
|
"""
|
||||||
Repost Payment Ledger Entries for Vouchers through Background Job
|
Repost Payment Ledger Entries for Vouchers through Background Job
|
||||||
"""
|
"""
|
||||||
@@ -119,7 +119,7 @@ class RepostPaymentLedger(Document):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def execute_repost_payment_ledger(docname):
|
def execute_repost_payment_ledger(docname: str):
|
||||||
"""Repost Payment Ledger Entries by background job."""
|
"""Repost Payment Ledger Entries by background job."""
|
||||||
|
|
||||||
job_name = "payment_ledger_repost_" + docname
|
job_name = "payment_ledger_repost_" + docname
|
||||||
|
|||||||
@@ -138,7 +138,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
|
|||||||
this.frm.add_custom_button(
|
this.frm.add_custom_button(
|
||||||
__("Payment Request"),
|
__("Payment Request"),
|
||||||
function () {
|
function () {
|
||||||
me.make_payment_request();
|
me.make_payment_request_with_schedule();
|
||||||
},
|
},
|
||||||
__("Create")
|
__("Create")
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -78,21 +78,23 @@
|
|||||||
"column_break_47",
|
"column_break_47",
|
||||||
"total_taxes_and_charges",
|
"total_taxes_and_charges",
|
||||||
"totals_section",
|
"totals_section",
|
||||||
|
"use_company_roundoff_cost_center",
|
||||||
"grand_total",
|
"grand_total",
|
||||||
"rounding_adjustment",
|
|
||||||
"in_words",
|
"in_words",
|
||||||
"column_break5",
|
"column_break5",
|
||||||
"rounded_total",
|
|
||||||
"disable_rounded_total",
|
"disable_rounded_total",
|
||||||
"total_advance",
|
"rounding_adjustment",
|
||||||
"outstanding_amount",
|
"rounded_total",
|
||||||
"use_company_roundoff_cost_center",
|
|
||||||
"base_totals_section",
|
"base_totals_section",
|
||||||
"base_grand_total",
|
"base_grand_total",
|
||||||
"base_rounding_adjustment",
|
|
||||||
"base_in_words",
|
"base_in_words",
|
||||||
"column_break_xjag",
|
"column_break_xjag",
|
||||||
|
"base_rounding_adjustment",
|
||||||
"base_rounded_total",
|
"base_rounded_total",
|
||||||
|
"section_break_vacb",
|
||||||
|
"total_advance",
|
||||||
|
"column_break_rdks",
|
||||||
|
"outstanding_amount",
|
||||||
"section_tax_withholding_entry",
|
"section_tax_withholding_entry",
|
||||||
"tax_withholding_group",
|
"tax_withholding_group",
|
||||||
"ignore_tax_withholding_threshold",
|
"ignore_tax_withholding_threshold",
|
||||||
@@ -269,6 +271,7 @@
|
|||||||
"oldfieldtype": "Link",
|
"oldfieldtype": "Link",
|
||||||
"options": "Customer",
|
"options": "Customer",
|
||||||
"print_hide": 1,
|
"print_hide": 1,
|
||||||
|
"reqd": 1,
|
||||||
"search_index": 1
|
"search_index": 1
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -797,8 +800,7 @@
|
|||||||
"hide_seconds": 1,
|
"hide_seconds": 1,
|
||||||
"label": "Time Sheets",
|
"label": "Time Sheets",
|
||||||
"options": "Sales Invoice Timesheet",
|
"options": "Sales Invoice Timesheet",
|
||||||
"print_hide": 1,
|
"print_hide": 1
|
||||||
"read_only": 1
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"default": "0",
|
"default": "0",
|
||||||
@@ -2307,6 +2309,14 @@
|
|||||||
"fieldname": "utm_analytics_section",
|
"fieldname": "utm_analytics_section",
|
||||||
"fieldtype": "Section Break",
|
"fieldtype": "Section Break",
|
||||||
"label": "UTM Analytics"
|
"label": "UTM Analytics"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "section_break_vacb",
|
||||||
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"fieldname": "column_break_rdks",
|
||||||
|
"fieldtype": "Column Break"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"grid_page_length": 50,
|
"grid_page_length": 50,
|
||||||
@@ -2320,7 +2330,7 @@
|
|||||||
"link_fieldname": "consolidated_invoice"
|
"link_fieldname": "consolidated_invoice"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"modified": "2026-02-10 11:59:07.819903",
|
"modified": "2026-02-28 17:58:56.453076",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Sales Invoice",
|
"name": "Sales Invoice",
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import frappe
|
|||||||
import frappe.utils
|
import frappe.utils
|
||||||
from frappe import _, msgprint, throw
|
from frappe import _, msgprint, throw
|
||||||
from frappe.contacts.doctype.address.address import get_address_display
|
from frappe.contacts.doctype.address.address import get_address_display
|
||||||
|
from frappe.model.document import Document
|
||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
from frappe.model.utils import get_fetch_values
|
from frappe.model.utils import get_fetch_values
|
||||||
from frappe.query_builder import Case
|
from frappe.query_builder import Case
|
||||||
@@ -119,7 +120,7 @@ class SalesInvoice(SellingController):
|
|||||||
cost_center: DF.Link | None
|
cost_center: DF.Link | None
|
||||||
coupon_code: DF.Link | None
|
coupon_code: DF.Link | None
|
||||||
currency: DF.Link
|
currency: DF.Link
|
||||||
customer: DF.Link | None
|
customer: DF.Link
|
||||||
customer_address: DF.Link | None
|
customer_address: DF.Link | None
|
||||||
customer_group: DF.Link | None
|
customer_group: DF.Link | None
|
||||||
customer_name: DF.SmallText | None
|
customer_name: DF.SmallText | None
|
||||||
@@ -741,7 +742,7 @@ class SalesInvoice(SellingController):
|
|||||||
pos_invoice_doc.cancel()
|
pos_invoice_doc.cancel()
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def set_missing_values(self, for_validate=False):
|
def set_missing_values(self, for_validate: bool = False):
|
||||||
pos = self.set_pos_fields(for_validate)
|
pos = self.set_pos_fields(for_validate)
|
||||||
|
|
||||||
if not self.debit_to:
|
if not self.debit_to:
|
||||||
@@ -2409,7 +2410,7 @@ def get_list_context(context=None):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_bank_cash_account(mode_of_payment, company):
|
def get_bank_cash_account(mode_of_payment: str, company: str):
|
||||||
account = frappe.db.get_value(
|
account = frappe.db.get_value(
|
||||||
"Mode of Payment Account", {"parent": mode_of_payment, "company": company}, "default_account"
|
"Mode of Payment Account", {"parent": mode_of_payment, "company": company}, "default_account"
|
||||||
)
|
)
|
||||||
@@ -2424,7 +2425,7 @@ def get_bank_cash_account(mode_of_payment, company):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_maintenance_schedule(source_name, target_doc=None):
|
def make_maintenance_schedule(source_name: str, target_doc: str | Document | None = None):
|
||||||
doclist = get_mapped_doc(
|
doclist = get_mapped_doc(
|
||||||
"Sales Invoice",
|
"Sales Invoice",
|
||||||
source_name,
|
source_name,
|
||||||
@@ -2441,7 +2442,7 @@ def make_maintenance_schedule(source_name, target_doc=None):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_delivery_note(source_name, target_doc=None):
|
def make_delivery_note(source_name: str, target_doc: Document | None = None):
|
||||||
def set_missing_values(source, target):
|
def set_missing_values(source, target):
|
||||||
target.run_method("set_missing_values")
|
target.run_method("set_missing_values")
|
||||||
target.run_method("set_po_nos")
|
target.run_method("set_po_nos")
|
||||||
@@ -2490,7 +2491,7 @@ def make_delivery_note(source_name, target_doc=None):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_sales_return(source_name, target_doc=None):
|
def make_sales_return(source_name: str, target_doc: Document | None = None):
|
||||||
from erpnext.controllers.sales_and_purchase_return import make_return_doc
|
from erpnext.controllers.sales_and_purchase_return import make_return_doc
|
||||||
|
|
||||||
return make_return_doc("Sales Invoice", source_name, target_doc)
|
return make_return_doc("Sales Invoice", source_name, target_doc)
|
||||||
@@ -2584,7 +2585,7 @@ def validate_inter_company_transaction(doc, doctype):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_inter_company_purchase_invoice(source_name, target_doc=None):
|
def make_inter_company_purchase_invoice(source_name: str, target_doc: Document | None = None):
|
||||||
return make_inter_company_transaction("Sales Invoice", source_name, target_doc)
|
return make_inter_company_transaction("Sales Invoice", source_name, target_doc)
|
||||||
|
|
||||||
|
|
||||||
@@ -2962,7 +2963,7 @@ def update_address(doc, address_field, address_display_field, address_name):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_loyalty_programs(customer):
|
def get_loyalty_programs(customer: str):
|
||||||
"""sets applicable loyalty program to the customer or returns a list of applicable programs"""
|
"""sets applicable loyalty program to the customer or returns a list of applicable programs"""
|
||||||
from erpnext.selling.doctype.customer.customer import get_loyalty_programs
|
from erpnext.selling.doctype.customer.customer import get_loyalty_programs
|
||||||
|
|
||||||
@@ -2980,7 +2981,7 @@ def get_loyalty_programs(customer):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def create_invoice_discounting(source_name, target_doc=None):
|
def create_invoice_discounting(source_name: str, target_doc: str | Document | None = None):
|
||||||
invoice = frappe.get_doc("Sales Invoice", source_name)
|
invoice = frappe.get_doc("Sales Invoice", source_name)
|
||||||
invoice_discounting = frappe.new_doc("Invoice Discounting")
|
invoice_discounting = frappe.new_doc("Invoice Discounting")
|
||||||
invoice_discounting.company = invoice.company
|
invoice_discounting.company = invoice.company
|
||||||
@@ -3072,7 +3073,9 @@ def get_mode_of_payment_info(mode_of_payment, company):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def create_dunning(source_name, target_doc=None, ignore_permissions=False):
|
def create_dunning(
|
||||||
|
source_name: str, target_doc: str | Document | None = None, ignore_permissions: bool = False
|
||||||
|
):
|
||||||
from frappe.model.mapper import get_mapped_doc
|
from frappe.model.mapper import get_mapped_doc
|
||||||
|
|
||||||
def postprocess_dunning(source, target):
|
def postprocess_dunning(source, target):
|
||||||
|
|||||||
@@ -843,6 +843,7 @@
|
|||||||
"fieldtype": "Currency",
|
"fieldtype": "Currency",
|
||||||
"label": "Incoming Rate (Costing)",
|
"label": "Incoming Rate (Costing)",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
|
"non_negative": 1,
|
||||||
"options": "Company:company:default_currency",
|
"options": "Company:company:default_currency",
|
||||||
"print_hide": 1
|
"print_hide": 1
|
||||||
},
|
},
|
||||||
@@ -1009,7 +1010,7 @@
|
|||||||
"idx": 1,
|
"idx": 1,
|
||||||
"istable": 1,
|
"istable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2026-02-15 21:08:57.341638",
|
"modified": "2026-02-23 14:37:14.853941",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Sales Invoice Item",
|
"name": "Sales Invoice Item",
|
||||||
|
|||||||
@@ -342,14 +342,14 @@ class ShareTransfer(Document):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def make_jv_entry(
|
def make_jv_entry(
|
||||||
company,
|
company: str,
|
||||||
account,
|
account: str,
|
||||||
amount,
|
amount: float,
|
||||||
payment_account,
|
payment_account: str,
|
||||||
credit_applicant_type,
|
credit_applicant_type: str,
|
||||||
credit_applicant,
|
credit_applicant: str,
|
||||||
debit_applicant_type,
|
debit_applicant_type: str,
|
||||||
debit_applicant,
|
debit_applicant: str,
|
||||||
):
|
):
|
||||||
journal_entry = frappe.new_doc("Journal Entry")
|
journal_entry = frappe.new_doc("Journal Entry")
|
||||||
journal_entry.voucher_type = "Journal Entry"
|
journal_entry.voucher_type = "Journal Entry"
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||||
# For license information, please see license.txt
|
# For license information, please see license.txt
|
||||||
|
|
||||||
|
from datetime import date
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from dateutil import relativedelta
|
from dateutil import relativedelta
|
||||||
@@ -43,7 +44,13 @@ class SubscriptionPlan(Document):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_plan_rate(
|
def get_plan_rate(
|
||||||
plan, quantity=1, customer=None, start_date=None, end_date=None, prorate_factor=1, party=None
|
plan: str,
|
||||||
|
quantity: int = 1,
|
||||||
|
customer: str | None = None,
|
||||||
|
start_date: str | date | None = None,
|
||||||
|
end_date: str | date | None = None,
|
||||||
|
prorate_factor: float = 1,
|
||||||
|
party: str | None = None,
|
||||||
):
|
):
|
||||||
plan = frappe.get_doc("Subscription Plan", plan)
|
plan = frappe.get_doc("Subscription Plan", plan)
|
||||||
if plan.price_determination == "Fixed Rate":
|
if plan.price_determination == "Fixed Rate":
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import frappe
|
|||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.contacts.doctype.address.address import get_default_address
|
from frappe.contacts.doctype.address.address import get_default_address
|
||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
from frappe.query_builder import DocType
|
||||||
|
from frappe.query_builder.functions import IfNull
|
||||||
from frappe.utils import cstr
|
from frappe.utils import cstr
|
||||||
from frappe.utils.nestedset import get_root_of
|
from frappe.utils.nestedset import get_root_of
|
||||||
|
|
||||||
@@ -83,6 +85,8 @@ class TaxRule(Document):
|
|||||||
frappe.throw(_("Tax Template is mandatory."))
|
frappe.throw(_("Tax Template is mandatory."))
|
||||||
|
|
||||||
def validate_filters(self):
|
def validate_filters(self):
|
||||||
|
TaxRule = DocType("Tax Rule")
|
||||||
|
|
||||||
filters = {
|
filters = {
|
||||||
"tax_type": self.tax_type,
|
"tax_type": self.tax_type,
|
||||||
"customer": self.customer,
|
"customer": self.customer,
|
||||||
@@ -105,37 +109,38 @@ class TaxRule(Document):
|
|||||||
"company": self.company,
|
"company": self.company,
|
||||||
}
|
}
|
||||||
|
|
||||||
conds = ""
|
query = (
|
||||||
for d in filters:
|
frappe.qb.from_(TaxRule).select(TaxRule.name, TaxRule.priority).where(TaxRule.name != self.name)
|
||||||
if conds:
|
|
||||||
conds += " and "
|
|
||||||
conds += f"""ifnull({d}, '') = {frappe.db.escape(cstr(filters[d]))}"""
|
|
||||||
|
|
||||||
if self.from_date and self.to_date:
|
|
||||||
conds += f""" and ((from_date > '{self.from_date}' and from_date < '{self.to_date}') or
|
|
||||||
(to_date > '{self.from_date}' and to_date < '{self.to_date}') or
|
|
||||||
('{self.from_date}' > from_date and '{self.from_date}' < to_date) or
|
|
||||||
('{self.from_date}' = from_date and '{self.to_date}' = to_date))"""
|
|
||||||
|
|
||||||
elif self.from_date and not self.to_date:
|
|
||||||
conds += f""" and to_date > '{self.from_date}'"""
|
|
||||||
|
|
||||||
elif self.to_date and not self.from_date:
|
|
||||||
conds += f""" and from_date < '{self.to_date}'"""
|
|
||||||
|
|
||||||
tax_rule = frappe.db.sql(
|
|
||||||
f"select name, priority \
|
|
||||||
from `tabTax Rule` where {conds} and name != '{self.name}'",
|
|
||||||
as_dict=1,
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if tax_rule:
|
for field, value in filters.items():
|
||||||
if tax_rule[0].priority == self.priority:
|
query = query.where(IfNull(TaxRule[field], "") == cstr(value))
|
||||||
frappe.throw(_("Tax Rule Conflicts with {0}").format(tax_rule[0].name), ConflictingTaxRule)
|
|
||||||
|
if self.from_date and self.to_date:
|
||||||
|
query = query.where(
|
||||||
|
((TaxRule.from_date > self.from_date) & (TaxRule.from_date < self.to_date))
|
||||||
|
| ((TaxRule.to_date > self.from_date) & (TaxRule.to_date < self.to_date))
|
||||||
|
| ((self.from_date > TaxRule.from_date) & (self.from_date < TaxRule.to_date))
|
||||||
|
| ((TaxRule.from_date == self.from_date) & (TaxRule.to_date == self.to_date))
|
||||||
|
)
|
||||||
|
|
||||||
|
elif self.from_date:
|
||||||
|
query = query.where(TaxRule.to_date > self.from_date)
|
||||||
|
|
||||||
|
elif self.to_date:
|
||||||
|
query = query.where(TaxRule.from_date < self.to_date)
|
||||||
|
|
||||||
|
tax_rule = query.run(as_dict=True)
|
||||||
|
|
||||||
|
if tax_rule and tax_rule[0].priority == self.priority:
|
||||||
|
frappe.throw(
|
||||||
|
_("Tax Rule Conflicts with {0}").format(tax_rule[0].name),
|
||||||
|
ConflictingTaxRule,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_party_details(party, party_type, args=None):
|
def get_party_details(party: str | None, party_type: str, args: dict | None = None):
|
||||||
out = {}
|
out = {}
|
||||||
billing_address, shipping_address = None, None
|
billing_address, shipping_address = None, None
|
||||||
if args:
|
if args:
|
||||||
|
|||||||
@@ -194,7 +194,7 @@ def get_linked_advances(company, docname):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def create_unreconcile_doc_for_selection(selections=None):
|
def create_unreconcile_doc_for_selection(selections: str | None = None):
|
||||||
if selections:
|
if selections:
|
||||||
selections = json.loads(selections)
|
selections = json.loads(selections)
|
||||||
# assuming each row is a unique voucher
|
# assuming each row is a unique voucher
|
||||||
|
|||||||
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"allow_roles": [
|
||||||
|
{
|
||||||
|
"role": "Accounts Manager"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Accounts User"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"creation": "2026-02-22 18:26:42.015787",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Module Onboarding",
|
||||||
|
"idx": 4,
|
||||||
|
"is_complete": 0,
|
||||||
|
"modified": "2026-02-23 22:51:34.267812",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Accounts",
|
||||||
|
"name": "Accounting Onboarding",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"step": "Chart of Accounts"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": "Setup Sales taxes"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": "Create Sales Invoice"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": "Create Payment Entry"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": "View Balance Sheet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": "Review Accounts Settings"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Accounting Onboarding"
|
||||||
|
}
|
||||||
@@ -1,3 +1,43 @@
|
|||||||
<h3>{{ _("Fiscal Year") }}</h3>
|
<h4>{{ _("New Fiscal Year - {0}").format(doc.name) }}</h4>
|
||||||
|
|
||||||
<p>{{ _("New fiscal year created :- ") }} {{ doc.name }}</p>
|
<p>{{ _("A new fiscal year has been automatically created.") }}</p>
|
||||||
|
|
||||||
|
<p>{{ _("Fiscal Year Details") }}</p>
|
||||||
|
|
||||||
|
<table style="margin-bottom: 1rem; width: 70%">
|
||||||
|
<tr>
|
||||||
|
<td style="font-weight:bold; width: 40%">{{ _("Year Name") }}</td>
|
||||||
|
<td>{{ doc.name }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="font-weight:bold; width: 40%">{{ _("Start Date") }}</td>
|
||||||
|
<td>{{ frappe.format_value(doc.year_start_date) }}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="font-weight:bold; width: 40%">{{ _("End Date") }}</td>
|
||||||
|
<td>{{ frappe.format_value(doc.year_end_date) }}</td>
|
||||||
|
</tr>
|
||||||
|
{% if doc.companies|length > 0 %}
|
||||||
|
<tr>
|
||||||
|
<td style="vertical-align: top; font-weight: bold; width: 40%" rowspan="{{ doc.companies|length }}">
|
||||||
|
{% if doc.companies|length < 2 %}
|
||||||
|
{{ _("Company") }}
|
||||||
|
{% else %}
|
||||||
|
{{ _("Companies") }}
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
<td>{{ doc.companies[0].company }}</td>
|
||||||
|
</tr>
|
||||||
|
{% for idx in range(1, doc.companies|length) %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ doc.companies[idx].company }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{% if doc.disabled %}
|
||||||
|
<p>{{ _("The fiscal year has been automatically created in a Disabled state to maintain consistency with the previous fiscal year's status.") }}</p>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<p>{{ _("Please review the {0} configuration and complete any required financial setup activities.").format(frappe.utils.get_link_to_form("Fiscal Year", doc.name, frappe.bold("Fiscal Year"))) }}</p>
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
{
|
{
|
||||||
"attach_print": 0,
|
"attach_print": 0,
|
||||||
"channel": "Email",
|
"channel": "Email",
|
||||||
"condition": "doc.auto_created",
|
"condition": "doc.auto_created == 1",
|
||||||
|
"condition_type": "Python",
|
||||||
"creation": "2018-04-25 14:19:05.440361",
|
"creation": "2018-04-25 14:19:05.440361",
|
||||||
"days_in_advance": 0,
|
"days_in_advance": 0,
|
||||||
"docstatus": 0,
|
"docstatus": 0,
|
||||||
@@ -11,8 +12,10 @@
|
|||||||
"event": "New",
|
"event": "New",
|
||||||
"idx": 0,
|
"idx": 0,
|
||||||
"is_standard": 1,
|
"is_standard": 1,
|
||||||
|
"message": "<h4>{{ _(\"New Fiscal Year - {0}\").format(doc.name) }}</h4>\n\n<p>{{ _(\"A new fiscal year has been automatically created.\") }}</p>\n\n<p>{{ _(\"Fiscal Year Details\") }}</p>\n\n<table style=\"margin-bottom: 1rem; width: 70%\">\n <tr>\n <td style=\"font-weight:bold; width: 40%\">{{ _(\"Year Name\") }}</td>\n <td>{{ doc.name }}</td>\n </tr>\n <tr>\n <td style=\"font-weight:bold; width: 40%\">{{ _(\"Start Date\") }}</td>\n <td>{{ frappe.format_value(doc.year_start_date) }}</td>\n </tr>\n <tr>\n <td style=\"font-weight:bold; width: 40%\">{{ _(\"End Date\") }}</td>\n <td>{{ frappe.format_value(doc.year_end_date) }}</td>\n </tr>\n {% if doc.companies|length > 0 %}\n <tr>\n <td style=\"vertical-align: top; font-weight: bold; width: 40%\" rowspan=\"{{ doc.companies|length }}\">\n {% if doc.companies|length < 2 %}\n {{ _(\"Company\") }}\n {% else %}\n {{ _(\"Companies\") }}\n {% endif %}\n </td>\n <td>{{ doc.companies[0].company }}</td>\n </tr>\n {% for idx in range(1, doc.companies|length) %}\n <tr>\n <td>{{ doc.companies[idx].company }}</td>\n </tr>\n {% endfor %}\n {% endif %}\n</table>\n\n{% if doc.disabled %}\n<p>{{ _(\"The fiscal year has been automatically created in a Disabled state to maintain consistency with the previous fiscal year's status.\") }}</p>\n{% endif %}\n\n<p>{{ _(\"Please review the {0} configuration and complete any required financial setup activities.\").format(frappe.utils.get_link_to_form(\"Fiscal Year\", doc.name, frappe.bold(\"Fiscal Year\"))) }}</p>",
|
||||||
"message_type": "HTML",
|
"message_type": "HTML",
|
||||||
"modified": "2023-11-17 08:54:51.532104",
|
"minutes_offset": 0,
|
||||||
|
"modified": "2026-02-23 17:37:03.755394",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Notification for new fiscal year",
|
"name": "Notification for new fiscal year",
|
||||||
@@ -27,5 +30,5 @@
|
|||||||
],
|
],
|
||||||
"send_system_notification": 0,
|
"send_system_notification": 0,
|
||||||
"send_to_all_assignees": 0,
|
"send_to_all_assignees": 0,
|
||||||
"subject": "Notification for new fiscal year {{ doc.name }}"
|
"subject": "New Fiscal Year {{ doc.name }} - Review Required"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"action": "Go to Page",
|
||||||
|
"action_label": "Configure Chart of Accounts",
|
||||||
|
"creation": "2026-02-22 18:28:15.401383",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Onboarding Step",
|
||||||
|
"idx": 1,
|
||||||
|
"is_complete": 0,
|
||||||
|
"is_single": 0,
|
||||||
|
"is_skipped": 0,
|
||||||
|
"modified": "2026-02-23 22:44:45.540780",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"name": "Chart of Accounts",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"path": "Tree/Account",
|
||||||
|
"show_form_tour": 0,
|
||||||
|
"show_full_form": 0,
|
||||||
|
"title": "Review Chart of Accounts",
|
||||||
|
"validate_action": 1
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"action": "Create Entry",
|
||||||
|
"action_label": "Create Payment Entry",
|
||||||
|
"creation": "2026-02-23 19:22:12.005360",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Onboarding Step",
|
||||||
|
"idx": 0,
|
||||||
|
"is_complete": 0,
|
||||||
|
"is_single": 0,
|
||||||
|
"is_skipped": 0,
|
||||||
|
"modified": "2026-02-23 20:19:56.482245",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"name": "Create Payment Entry",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"reference_document": "Payment Entry",
|
||||||
|
"route_options": "{\n \"payment_type\": \"Receive\",\n \"party_type\": \"Customer\"\n}",
|
||||||
|
"show_form_tour": 0,
|
||||||
|
"show_full_form": 0,
|
||||||
|
"title": "Create Payment Entry",
|
||||||
|
"validate_action": 1
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"action": "Create Entry",
|
||||||
|
"action_label": "Create Sales Invoice",
|
||||||
|
"creation": "2026-02-20 13:42:38.439574",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Onboarding Step",
|
||||||
|
"idx": 2,
|
||||||
|
"is_complete": 0,
|
||||||
|
"is_single": 0,
|
||||||
|
"is_skipped": 0,
|
||||||
|
"modified": "2026-02-23 22:16:40.931428",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"name": "Create Sales Invoice",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"reference_document": "Sales Invoice",
|
||||||
|
"show_form_tour": 0,
|
||||||
|
"show_full_form": 0,
|
||||||
|
"title": "Create Sales Invoice",
|
||||||
|
"validate_action": 1
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"action": "Update Settings",
|
||||||
|
"action_label": "Review Accounts Settings",
|
||||||
|
"creation": "2026-02-23 19:27:06.055104",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Onboarding Step",
|
||||||
|
"idx": 0,
|
||||||
|
"is_complete": 0,
|
||||||
|
"is_single": 1,
|
||||||
|
"is_skipped": 0,
|
||||||
|
"modified": "2026-02-23 22:16:40.855407",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"name": "Review Accounts Settings",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"path": "desk/accounts-settings",
|
||||||
|
"reference_document": "Accounts Settings",
|
||||||
|
"show_form_tour": 0,
|
||||||
|
"show_full_form": 0,
|
||||||
|
"title": "Review Accounts Settings",
|
||||||
|
"validate_action": 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"action": "Go to Page",
|
||||||
|
"action_label": "Setup Sales Taxes",
|
||||||
|
"creation": "2026-02-22 18:30:18.750391",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Onboarding Step",
|
||||||
|
"form_tour": "",
|
||||||
|
"idx": 1,
|
||||||
|
"is_complete": 0,
|
||||||
|
"is_single": 0,
|
||||||
|
"is_skipped": 0,
|
||||||
|
"modified": "2026-02-23 22:44:42.373227",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"name": "Setup Sales taxes",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"path": "/desk/sales-taxes-and-charges-template",
|
||||||
|
"reference_document": "Sales Taxes and Charges Template",
|
||||||
|
"show_form_tour": 0,
|
||||||
|
"show_full_form": 0,
|
||||||
|
"title": "Setup Sales taxes",
|
||||||
|
"validate_action": 1
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"action": "View Report",
|
||||||
|
"action_label": "View Balance Sheet",
|
||||||
|
"creation": "2026-02-23 19:22:57.651194",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Onboarding Step",
|
||||||
|
"idx": 0,
|
||||||
|
"is_complete": 0,
|
||||||
|
"is_single": 0,
|
||||||
|
"is_skipped": 0,
|
||||||
|
"modified": "2026-02-23 22:44:39.178107",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"name": "View Balance Sheet",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"reference_report": "Balance Sheet",
|
||||||
|
"report_description": "View Balance Sheet",
|
||||||
|
"report_reference_doctype": "GL Entry",
|
||||||
|
"report_type": "Script Report",
|
||||||
|
"show_form_tour": 0,
|
||||||
|
"show_full_form": 0,
|
||||||
|
"title": "View Balance Sheet",
|
||||||
|
"validate_action": 1
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||||
# License: GNU General Public License v3. See license.txt
|
# License: GNU General Public License v3. See license.txt
|
||||||
|
|
||||||
|
from datetime import date
|
||||||
|
|
||||||
import frappe
|
import frappe
|
||||||
from frappe import _, msgprint, qb, scrub
|
from frappe import _, msgprint, qb, scrub
|
||||||
@@ -55,22 +56,22 @@ class DuplicatePartyAccountError(frappe.ValidationError):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_party_details(
|
def get_party_details(
|
||||||
party=None,
|
party: str | None = None,
|
||||||
account=None,
|
account: str | None = None,
|
||||||
party_type="Customer",
|
party_type: str = "Customer",
|
||||||
company=None,
|
company: str | None = None,
|
||||||
posting_date=None,
|
posting_date: str | None = None,
|
||||||
bill_date=None,
|
bill_date: str | None = None,
|
||||||
price_list=None,
|
price_list: str | None = None,
|
||||||
currency=None,
|
currency: str | None = None,
|
||||||
doctype=None,
|
doctype: str | None = None,
|
||||||
ignore_permissions=False,
|
ignore_permissions: bool | None = False,
|
||||||
fetch_payment_terms_template=True,
|
fetch_payment_terms_template: bool = True,
|
||||||
party_address=None,
|
party_address: str | None = None,
|
||||||
company_address=None,
|
company_address: str | None = None,
|
||||||
shipping_address=None,
|
shipping_address: str | None = None,
|
||||||
dispatch_address=None,
|
dispatch_address: str | None = None,
|
||||||
pos_profile=None,
|
pos_profile: str | None = None,
|
||||||
):
|
):
|
||||||
if not party:
|
if not party:
|
||||||
return frappe._dict()
|
return frappe._dict()
|
||||||
@@ -296,19 +297,9 @@ def complete_contact_details(party_details):
|
|||||||
contact_details = frappe._dict()
|
contact_details = frappe._dict()
|
||||||
|
|
||||||
if party_details.party_type == "Employee":
|
if party_details.party_type == "Employee":
|
||||||
contact_details = frappe.db.get_value(
|
from erpnext.setup.doctype.employee.employee import _get_contact_details as get_employee_contact
|
||||||
"Employee",
|
|
||||||
party_details.party,
|
|
||||||
[
|
|
||||||
"employee_name as contact_display",
|
|
||||||
"prefered_email as contact_email",
|
|
||||||
"cell_number as contact_mobile",
|
|
||||||
"designation as contact_designation",
|
|
||||||
"department as contact_department",
|
|
||||||
],
|
|
||||||
as_dict=True,
|
|
||||||
)
|
|
||||||
|
|
||||||
|
contact_details = get_employee_contact(party_details.party)
|
||||||
contact_details.update({"contact_person": None, "contact_phone": None})
|
contact_details.update({"contact_person": None, "contact_phone": None})
|
||||||
elif party_details.contact_person:
|
elif party_details.contact_person:
|
||||||
contact_details = frappe.db.get_value(
|
contact_details = frappe.db.get_value(
|
||||||
@@ -416,7 +407,9 @@ def set_account_and_due_date(party, account, party_type, company, posting_date,
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_party_account(party_type, party=None, company=None, include_advance=False):
|
def get_party_account(
|
||||||
|
party_type: str, party: str | None = None, company: str | None = None, include_advance: bool = False
|
||||||
|
):
|
||||||
"""Returns the account for the given `party`.
|
"""Returns the account for the given `party`.
|
||||||
Will first search in party (Customer / Supplier) record, if not found,
|
Will first search in party (Customer / Supplier) record, if not found,
|
||||||
will search in group (Customer Group / Supplier Group),
|
will search in group (Customer Group / Supplier Group),
|
||||||
@@ -501,7 +494,7 @@ def get_party_advance_account(party_type, party, company):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_party_bank_account(party_type, party):
|
def get_party_bank_account(party_type: str, party: str):
|
||||||
return frappe.db.get_value("Bank Account", {"party_type": party_type, "party": party, "is_default": 1})
|
return frappe.db.get_value("Bank Account", {"party_type": party_type, "party": party, "is_default": 1})
|
||||||
|
|
||||||
|
|
||||||
@@ -619,7 +612,14 @@ def validate_party_accounts(doc):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_due_date(posting_date, party_type, party, company=None, bill_date=None, template_name=None):
|
def get_due_date(
|
||||||
|
posting_date: str | date | None,
|
||||||
|
party_type: str | None,
|
||||||
|
party: str | None,
|
||||||
|
company: str | None = None,
|
||||||
|
bill_date: str | None = None,
|
||||||
|
template_name: str | None = None,
|
||||||
|
):
|
||||||
"""Get due date from `Payment Terms Template`"""
|
"""Get due date from `Payment Terms Template`"""
|
||||||
due_date = None
|
due_date = None
|
||||||
if (bill_date or posting_date) and party:
|
if (bill_date or posting_date) and party:
|
||||||
@@ -701,7 +701,9 @@ def validate_due_date_with_template(posting_date, due_date, bill_date, template_
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_address_tax_category(tax_category=None, billing_address=None, shipping_address=None):
|
def get_address_tax_category(
|
||||||
|
tax_category: str | None = None, billing_address: str | None = None, shipping_address: str | None = None
|
||||||
|
):
|
||||||
addr_tax_category_from = frappe.get_single_value(
|
addr_tax_category_from = frappe.get_single_value(
|
||||||
"Accounts Settings", "determine_address_tax_category_from"
|
"Accounts Settings", "determine_address_tax_category_from"
|
||||||
)
|
)
|
||||||
@@ -717,16 +719,16 @@ def get_address_tax_category(tax_category=None, billing_address=None, shipping_a
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def set_taxes(
|
def set_taxes(
|
||||||
party,
|
party: str | None,
|
||||||
party_type,
|
party_type: str,
|
||||||
posting_date,
|
posting_date: str | date | None,
|
||||||
company,
|
company: str | None,
|
||||||
customer_group=None,
|
customer_group: str | None = None,
|
||||||
supplier_group=None,
|
supplier_group: str | None = None,
|
||||||
tax_category=None,
|
tax_category: str | None = None,
|
||||||
billing_address=None,
|
billing_address: str | None = None,
|
||||||
shipping_address=None,
|
shipping_address: str | None = None,
|
||||||
use_for_shopping_cart=None,
|
use_for_shopping_cart: int | None = None,
|
||||||
):
|
):
|
||||||
from erpnext.accounts.doctype.tax_rule.tax_rule import get_party_details, get_tax_template
|
from erpnext.accounts.doctype.tax_rule.tax_rule import get_party_details, get_tax_template
|
||||||
|
|
||||||
@@ -766,7 +768,7 @@ def set_taxes(
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_payment_terms_template(party_name, party_type, company=None):
|
def get_payment_terms_template(party_name: str, party_type: str, company: str | None = None):
|
||||||
if party_type not in ("Customer", "Supplier"):
|
if party_type not in ("Customer", "Supplier"):
|
||||||
return
|
return
|
||||||
template = None
|
template = None
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -102,7 +102,7 @@ def execute(filters=None):
|
|||||||
filters.periodicity, period_list, filters.accumulated_values, company=filters.company
|
filters.periodicity, period_list, filters.accumulated_values, company=filters.company
|
||||||
)
|
)
|
||||||
|
|
||||||
chart = get_chart_data(filters, columns, asset, liability, equity, currency)
|
chart = get_chart_data(filters, period_list, asset, liability, equity, currency)
|
||||||
|
|
||||||
report_summary, primitive_summary = get_report_summary(
|
report_summary, primitive_summary = get_report_summary(
|
||||||
period_list, asset, liability, equity, provisional_profit_loss, currency, filters
|
period_list, asset, liability, equity, provisional_profit_loss, currency, filters
|
||||||
@@ -231,18 +231,19 @@ def get_report_summary(
|
|||||||
], (net_asset - net_liability + net_equity)
|
], (net_asset - net_liability + net_equity)
|
||||||
|
|
||||||
|
|
||||||
def get_chart_data(filters, columns, asset, liability, equity, currency):
|
def get_chart_data(filters, chart_columns, asset, liability, equity, currency):
|
||||||
labels = [d.get("label") for d in columns[4:]]
|
labels = [col.get("label") for col in chart_columns]
|
||||||
|
|
||||||
asset_data, liability_data, equity_data = [], [], []
|
asset_data, liability_data, equity_data = [], [], []
|
||||||
|
|
||||||
for p in columns[4:]:
|
for col in chart_columns:
|
||||||
|
key = col.get("key") or col.get("fieldname")
|
||||||
if asset:
|
if asset:
|
||||||
asset_data.append(asset[-2].get(p.get("fieldname")))
|
asset_data.append(asset[-2].get(key))
|
||||||
if liability:
|
if liability:
|
||||||
liability_data.append(liability[-2].get(p.get("fieldname")))
|
liability_data.append(liability[-2].get(key))
|
||||||
if equity:
|
if equity:
|
||||||
equity_data.append(equity[-2].get(p.get("fieldname")))
|
equity_data.append(equity[-2].get(key))
|
||||||
|
|
||||||
datasets = []
|
datasets = []
|
||||||
if asset_data:
|
if asset_data:
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import frappe
|
|||||||
from frappe import _
|
from frappe import _
|
||||||
from frappe.utils import add_months, flt, formatdate
|
from frappe.utils import add_months, flt, formatdate
|
||||||
|
|
||||||
|
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_dimensions
|
||||||
from erpnext.accounts.utils import get_fiscal_year
|
from erpnext.accounts.utils import get_fiscal_year
|
||||||
from erpnext.controllers.trends import get_period_date_ranges
|
from erpnext.controllers.trends import get_period_date_ranges
|
||||||
|
|
||||||
@@ -13,6 +14,8 @@ def execute(filters=None):
|
|||||||
if not filters:
|
if not filters:
|
||||||
filters = {}
|
filters = {}
|
||||||
|
|
||||||
|
validate_filters(filters)
|
||||||
|
|
||||||
columns = get_columns(filters)
|
columns = get_columns(filters)
|
||||||
if filters.get("budget_against_filter"):
|
if filters.get("budget_against_filter"):
|
||||||
dimensions = filters.get("budget_against_filter")
|
dimensions = filters.get("budget_against_filter")
|
||||||
@@ -31,6 +34,10 @@ def execute(filters=None):
|
|||||||
return columns, data, None, chart_data
|
return columns, data, None, chart_data
|
||||||
|
|
||||||
|
|
||||||
|
def validate_filters(filters):
|
||||||
|
validate_budget_dimensions(filters)
|
||||||
|
|
||||||
|
|
||||||
def get_budget_records(filters, dimensions):
|
def get_budget_records(filters, dimensions):
|
||||||
budget_against_field = frappe.scrub(filters["budget_against"])
|
budget_against_field = frappe.scrub(filters["budget_against"])
|
||||||
|
|
||||||
@@ -51,7 +58,7 @@ def get_budget_records(filters, dimensions):
|
|||||||
b.company = %s
|
b.company = %s
|
||||||
AND b.docstatus = 1
|
AND b.docstatus = 1
|
||||||
AND b.budget_against = %s
|
AND b.budget_against = %s
|
||||||
AND b.{budget_against_field} IN ({', '.join(['%s'] * len(dimensions))})
|
AND b.{budget_against_field} IN ({", ".join(["%s"] * len(dimensions))})
|
||||||
AND (
|
AND (
|
||||||
b.from_fiscal_year <= %s
|
b.from_fiscal_year <= %s
|
||||||
AND b.to_fiscal_year >= %s
|
AND b.to_fiscal_year >= %s
|
||||||
@@ -404,6 +411,17 @@ def get_budget_dimensions(filters):
|
|||||||
) # nosec
|
) # nosec
|
||||||
|
|
||||||
|
|
||||||
|
def validate_budget_dimensions(filters):
|
||||||
|
dimensions = [d.get("document_type") for d in get_dimensions(with_cost_center_and_project=True)[0]]
|
||||||
|
if filters.get("budget_against") and filters.get("budget_against") not in dimensions:
|
||||||
|
frappe.throw(
|
||||||
|
title=_("Invalid Accounting Dimension"),
|
||||||
|
msg=_("{0} is not a valid Accounting Dimension.").format(
|
||||||
|
frappe.bold(filters.get("budget_against"))
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def build_comparison_chart_data(filters, columns, data):
|
def build_comparison_chart_data(filters, columns, data):
|
||||||
if not data:
|
if not data:
|
||||||
return None
|
return None
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ def execute(filters=None):
|
|||||||
True,
|
True,
|
||||||
)
|
)
|
||||||
|
|
||||||
chart = get_chart_data(columns, data, company_currency)
|
chart = get_chart_data(period_list, data, company_currency)
|
||||||
|
|
||||||
report_summary = get_report_summary(summary_data, company_currency)
|
report_summary = get_report_summary(summary_data, company_currency)
|
||||||
|
|
||||||
@@ -417,12 +417,12 @@ def get_report_summary(summary_data, currency):
|
|||||||
return report_summary
|
return report_summary
|
||||||
|
|
||||||
|
|
||||||
def get_chart_data(columns, data, currency):
|
def get_chart_data(period_list, data, currency):
|
||||||
labels = [d.get("label") for d in columns[2:]]
|
labels = [period.get("label") for period in period_list]
|
||||||
datasets = [
|
datasets = [
|
||||||
{
|
{
|
||||||
"name": section.get("section").replace("'", ""),
|
"name": section.get("section").replace("'", ""),
|
||||||
"values": [section.get(d.get("fieldname")) for d in columns[2:]],
|
"values": [section.get(period.get("key")) for period in period_list],
|
||||||
}
|
}
|
||||||
for section in data
|
for section in data
|
||||||
if section.get("parent_section") is None and section.get("currency")
|
if section.get("parent_section") is None and section.get("currency")
|
||||||
|
|||||||
@@ -48,22 +48,25 @@ def execute(filters=None):
|
|||||||
return columns, data, message, chart
|
return columns, data, message, chart
|
||||||
|
|
||||||
fiscal_year = get_fiscal_year_data(filters.get("from_fiscal_year"), filters.get("to_fiscal_year"))
|
fiscal_year = get_fiscal_year_data(filters.get("from_fiscal_year"), filters.get("to_fiscal_year"))
|
||||||
companies_column, companies = get_companies(filters)
|
company_list, companies = get_companies(filters)
|
||||||
columns = get_columns(companies_column, filters)
|
company_columns = get_company_columns(company_list, filters)
|
||||||
|
columns = get_columns(company_columns)
|
||||||
|
|
||||||
if filters.get("report") == "Balance Sheet":
|
if filters.get("report") == "Balance Sheet":
|
||||||
data, message, chart, report_summary = get_balance_sheet_data(
|
data, message, chart, report_summary = get_balance_sheet_data(
|
||||||
fiscal_year, companies, columns, filters
|
fiscal_year, companies, company_columns, filters
|
||||||
)
|
)
|
||||||
elif filters.get("report") == "Profit and Loss Statement":
|
elif filters.get("report") == "Profit and Loss Statement":
|
||||||
data, message, chart, report_summary = get_profit_loss_data(fiscal_year, companies, columns, filters)
|
data, message, chart, report_summary = get_profit_loss_data(
|
||||||
|
fiscal_year, companies, company_columns, filters
|
||||||
|
)
|
||||||
else:
|
else:
|
||||||
data, report_summary = get_cash_flow_data(fiscal_year, companies, filters)
|
data, report_summary = get_cash_flow_data(fiscal_year, companies, filters)
|
||||||
|
|
||||||
return columns, data, message, chart, report_summary
|
return columns, data, message, chart, report_summary
|
||||||
|
|
||||||
|
|
||||||
def get_balance_sheet_data(fiscal_year, companies, columns, filters):
|
def get_balance_sheet_data(fiscal_year, companies, company_columns, filters):
|
||||||
asset = get_data(companies, "Asset", "Debit", fiscal_year, filters=filters)
|
asset = get_data(companies, "Asset", "Debit", fiscal_year, filters=filters)
|
||||||
|
|
||||||
liability = get_data(companies, "Liability", "Credit", fiscal_year, filters=filters)
|
liability = get_data(companies, "Liability", "Credit", fiscal_year, filters=filters)
|
||||||
@@ -116,7 +119,7 @@ def get_balance_sheet_data(fiscal_year, companies, columns, filters):
|
|||||||
True,
|
True,
|
||||||
)
|
)
|
||||||
|
|
||||||
chart = get_chart_data(filters, columns, asset, liability, equity, company_currency)
|
chart = get_chart_data(filters, company_columns, asset, liability, equity, company_currency)
|
||||||
|
|
||||||
return data, message, chart, report_summary
|
return data, message, chart, report_summary
|
||||||
|
|
||||||
@@ -164,7 +167,7 @@ def get_root_account_name(root_type, company):
|
|||||||
return root_account[0][0]
|
return root_account[0][0]
|
||||||
|
|
||||||
|
|
||||||
def get_profit_loss_data(fiscal_year, companies, columns, filters):
|
def get_profit_loss_data(fiscal_year, companies, company_columns, filters):
|
||||||
income, expense, net_profit_loss = get_income_expense_data(companies, fiscal_year, filters)
|
income, expense, net_profit_loss = get_income_expense_data(companies, fiscal_year, filters)
|
||||||
company_currency = get_company_currency(filters)
|
company_currency = get_company_currency(filters)
|
||||||
|
|
||||||
@@ -174,7 +177,7 @@ def get_profit_loss_data(fiscal_year, companies, columns, filters):
|
|||||||
if net_profit_loss:
|
if net_profit_loss:
|
||||||
data.append(net_profit_loss)
|
data.append(net_profit_loss)
|
||||||
|
|
||||||
chart = get_pl_chart_data(filters, columns, income, expense, net_profit_loss, company_currency)
|
chart = get_pl_chart_data(filters, company_columns, income, expense, net_profit_loss, company_currency)
|
||||||
|
|
||||||
report_summary, primitive_summary = get_pl_summary(
|
report_summary, primitive_summary = get_pl_summary(
|
||||||
companies, "", income, expense, net_profit_loss, company_currency, filters, True
|
companies, "", income, expense, net_profit_loss, company_currency, filters, True
|
||||||
@@ -280,7 +283,30 @@ def get_account_type_based_data(account_type, companies, fiscal_year, filters):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def get_columns(companies, filters):
|
def get_company_columns(companies, filters):
|
||||||
|
company_columns = []
|
||||||
|
for company in companies:
|
||||||
|
apply_currency_formatter = 1 if not filters.presentation_currency else 0
|
||||||
|
currency = filters.presentation_currency
|
||||||
|
if not currency:
|
||||||
|
currency = erpnext.get_company_currency(company)
|
||||||
|
|
||||||
|
company_columns.append(
|
||||||
|
{
|
||||||
|
"fieldname": company,
|
||||||
|
"label": f"{company} ({currency})",
|
||||||
|
"fieldtype": "Currency",
|
||||||
|
"options": "currency",
|
||||||
|
"width": 150,
|
||||||
|
"apply_currency_formatter": apply_currency_formatter,
|
||||||
|
"company_name": company,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return company_columns
|
||||||
|
|
||||||
|
|
||||||
|
def get_columns(company_columns):
|
||||||
columns = [
|
columns = [
|
||||||
{
|
{
|
||||||
"fieldname": "account",
|
"fieldname": "account",
|
||||||
@@ -298,23 +324,7 @@ def get_columns(companies, filters):
|
|||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
for company in companies:
|
columns.extend(company_columns)
|
||||||
apply_currency_formatter = 1 if not filters.presentation_currency else 0
|
|
||||||
currency = filters.presentation_currency
|
|
||||||
if not currency:
|
|
||||||
currency = erpnext.get_company_currency(company)
|
|
||||||
|
|
||||||
columns.append(
|
|
||||||
{
|
|
||||||
"fieldname": company,
|
|
||||||
"label": f"{company} ({currency})",
|
|
||||||
"fieldtype": "Currency",
|
|
||||||
"options": "currency",
|
|
||||||
"width": 150,
|
|
||||||
"apply_currency_formatter": apply_currency_formatter,
|
|
||||||
"company_name": company,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
return columns
|
return columns
|
||||||
|
|
||||||
|
|||||||
@@ -86,6 +86,12 @@ frappe.query_reports["Consolidated Trial Balance"] = {
|
|||||||
fieldtype: "Check",
|
fieldtype: "Check",
|
||||||
default: 1,
|
default: 1,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
fieldname: "show_net_values",
|
||||||
|
label: __("Show net values in opening and closing columns"),
|
||||||
|
fieldtype: "Check",
|
||||||
|
default: 1,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
fieldname: "show_group_accounts",
|
fieldname: "show_group_accounts",
|
||||||
label: __("Show Group Accounts"),
|
label: __("Show Group Accounts"),
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ from erpnext.accounts.report.financial_statements import (
|
|||||||
)
|
)
|
||||||
from erpnext.accounts.report.trial_balance.trial_balance import (
|
from erpnext.accounts.report.trial_balance.trial_balance import (
|
||||||
accumulate_values_into_parents,
|
accumulate_values_into_parents,
|
||||||
|
calculate_total_row,
|
||||||
calculate_values,
|
calculate_values,
|
||||||
get_opening_balances,
|
get_opening_balances,
|
||||||
hide_group_accounts,
|
hide_group_accounts,
|
||||||
@@ -44,7 +45,6 @@ def execute(filters: dict | None = None):
|
|||||||
|
|
||||||
def validate_filters(filters):
|
def validate_filters(filters):
|
||||||
validate_companies(filters)
|
validate_companies(filters)
|
||||||
filters.show_net_values = True
|
|
||||||
tb_validate_filters(filters)
|
tb_validate_filters(filters)
|
||||||
|
|
||||||
|
|
||||||
@@ -99,16 +99,20 @@ def get_data(filters) -> list[list]:
|
|||||||
tb_data = get_company_wise_tb_data(company_filter, reporting_currency, ignore_reporting_currency)
|
tb_data = get_company_wise_tb_data(company_filter, reporting_currency, ignore_reporting_currency)
|
||||||
consolidate_trial_balance_data(data, tb_data)
|
consolidate_trial_balance_data(data, tb_data)
|
||||||
|
|
||||||
for d in data:
|
if filters.get("show_net_values"):
|
||||||
prepare_opening_closing(d)
|
prepare_opening_closing_for_ctb(data)
|
||||||
|
|
||||||
total_row = calculate_total_row(data, reporting_currency)
|
|
||||||
|
|
||||||
data.extend([{}, total_row])
|
|
||||||
|
|
||||||
if not filters.get("show_group_accounts"):
|
if not filters.get("show_group_accounts"):
|
||||||
data = hide_group_accounts(data)
|
data = hide_group_accounts(data)
|
||||||
|
|
||||||
|
total_row = calculate_total_row(
|
||||||
|
data, reporting_currency, show_group_accounts=filters.get("show_group_accounts")
|
||||||
|
)
|
||||||
|
|
||||||
|
calculate_foreign_currency_translation_reserve(total_row, data, filters=filters)
|
||||||
|
|
||||||
|
data.extend([total_row])
|
||||||
|
|
||||||
if filters.get("presentation_currency"):
|
if filters.get("presentation_currency"):
|
||||||
update_to_presentation_currency(
|
update_to_presentation_currency(
|
||||||
data,
|
data,
|
||||||
@@ -207,10 +211,6 @@ def prepare_companywise_tb_data(accounts, filters, parent_children_map, reportin
|
|||||||
data = []
|
data = []
|
||||||
|
|
||||||
for d in accounts:
|
for d in accounts:
|
||||||
# Prepare opening closing for group account
|
|
||||||
if parent_children_map.get(d.account) and filters.get("show_net_values"):
|
|
||||||
prepare_opening_closing(d)
|
|
||||||
|
|
||||||
has_value = False
|
has_value = False
|
||||||
row = {
|
row = {
|
||||||
"account": d.name,
|
"account": d.name,
|
||||||
@@ -242,35 +242,9 @@ def prepare_companywise_tb_data(accounts, filters, parent_children_map, reportin
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
|
|
||||||
def calculate_total_row(data, reporting_currency):
|
def calculate_foreign_currency_translation_reserve(total_row, data, filters):
|
||||||
total_row = {
|
if not data or not total_row:
|
||||||
"account": "'" + _("Total") + "'",
|
return
|
||||||
"account_name": "'" + _("Total") + "'",
|
|
||||||
"warn_if_negative": True,
|
|
||||||
"opening_debit": 0.0,
|
|
||||||
"opening_credit": 0.0,
|
|
||||||
"debit": 0.0,
|
|
||||||
"credit": 0.0,
|
|
||||||
"closing_debit": 0.0,
|
|
||||||
"closing_credit": 0.0,
|
|
||||||
"parent_account": None,
|
|
||||||
"indent": 0,
|
|
||||||
"has_value": True,
|
|
||||||
"currency": reporting_currency,
|
|
||||||
}
|
|
||||||
|
|
||||||
for d in data:
|
|
||||||
if not d.get("parent_account"):
|
|
||||||
for field in value_fields:
|
|
||||||
total_row[field] += d[field]
|
|
||||||
|
|
||||||
if data:
|
|
||||||
calculate_foreign_currency_translation_reserve(total_row, data)
|
|
||||||
|
|
||||||
return total_row
|
|
||||||
|
|
||||||
|
|
||||||
def calculate_foreign_currency_translation_reserve(total_row, data):
|
|
||||||
opening_dr_cr_diff = total_row["opening_debit"] - total_row["opening_credit"]
|
opening_dr_cr_diff = total_row["opening_debit"] - total_row["opening_credit"]
|
||||||
dr_cr_diff = total_row["debit"] - total_row["credit"]
|
dr_cr_diff = total_row["debit"] - total_row["credit"]
|
||||||
|
|
||||||
@@ -289,7 +263,7 @@ def calculate_foreign_currency_translation_reserve(total_row, data):
|
|||||||
"root_type": data[idx].get("root_type"),
|
"root_type": data[idx].get("root_type"),
|
||||||
"account_type": "Equity",
|
"account_type": "Equity",
|
||||||
"parent_account": data[idx].get("account"),
|
"parent_account": data[idx].get("account"),
|
||||||
"indent": data[idx].get("indent") + 1,
|
"indent": data[idx].get("indent") + 1 if filters.get("show_group_accounts") else 0,
|
||||||
"has_value": True,
|
"has_value": True,
|
||||||
"currency": total_row.get("currency"),
|
"currency": total_row.get("currency"),
|
||||||
}
|
}
|
||||||
@@ -297,7 +271,8 @@ def calculate_foreign_currency_translation_reserve(total_row, data):
|
|||||||
fctr_row["closing_debit"] = fctr_row["opening_debit"] + fctr_row["debit"]
|
fctr_row["closing_debit"] = fctr_row["opening_debit"] + fctr_row["debit"]
|
||||||
fctr_row["closing_credit"] = fctr_row["opening_credit"] + fctr_row["credit"]
|
fctr_row["closing_credit"] = fctr_row["opening_credit"] + fctr_row["credit"]
|
||||||
|
|
||||||
prepare_opening_closing(fctr_row)
|
if filters.get("show_net_values"):
|
||||||
|
prepare_opening_closing(fctr_row)
|
||||||
|
|
||||||
data.insert(idx + 1, fctr_row)
|
data.insert(idx + 1, fctr_row)
|
||||||
|
|
||||||
@@ -396,6 +371,11 @@ def update_to_presentation_currency(data, from_currency, to_currency, date, igno
|
|||||||
d.update(currency=to_currency)
|
d.update(currency=to_currency)
|
||||||
|
|
||||||
|
|
||||||
|
def prepare_opening_closing_for_ctb(data):
|
||||||
|
for d in data:
|
||||||
|
prepare_opening_closing(d)
|
||||||
|
|
||||||
|
|
||||||
def get_columns():
|
def get_columns():
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -444,6 +444,7 @@ class TestGrossProfit(IntegrationTestCase):
|
|||||||
qty=-1, rate=100, posting_date=nowdate(), do_not_save=True, do_not_submit=True
|
qty=-1, rate=100, posting_date=nowdate(), do_not_save=True, do_not_submit=True
|
||||||
)
|
)
|
||||||
sinv.is_return = 1
|
sinv.is_return = 1
|
||||||
|
sinv.items[0].allow_zero_valuation_rate = 1
|
||||||
sinv = sinv.save().submit()
|
sinv = sinv.save().submit()
|
||||||
|
|
||||||
filters = frappe._dict(
|
filters = frappe._dict(
|
||||||
|
|||||||
@@ -68,7 +68,7 @@ def execute(filters=None):
|
|||||||
currency = filters.presentation_currency or frappe.get_cached_value(
|
currency = filters.presentation_currency or frappe.get_cached_value(
|
||||||
"Company", filters.company, "default_currency"
|
"Company", filters.company, "default_currency"
|
||||||
)
|
)
|
||||||
chart = get_chart_data(filters, columns, income, expense, net_profit_loss, currency)
|
chart = get_chart_data(filters, period_list, income, expense, net_profit_loss, currency)
|
||||||
|
|
||||||
report_summary, primitive_summary = get_report_summary(
|
report_summary, primitive_summary = get_report_summary(
|
||||||
period_list, filters.periodicity, income, expense, net_profit_loss, currency, filters
|
period_list, filters.periodicity, income, expense, net_profit_loss, currency, filters
|
||||||
@@ -162,18 +162,19 @@ def get_net_profit_loss(income, expense, period_list, company, currency=None, co
|
|||||||
return net_profit_loss
|
return net_profit_loss
|
||||||
|
|
||||||
|
|
||||||
def get_chart_data(filters, columns, income, expense, net_profit_loss, currency):
|
def get_chart_data(filters, chart_columns, income, expense, net_profit_loss, currency):
|
||||||
labels = [d.get("label") for d in columns[4:]]
|
labels = [col.get("label") for col in chart_columns]
|
||||||
|
|
||||||
income_data, expense_data, net_profit = [], [], []
|
income_data, expense_data, net_profit = [], [], []
|
||||||
|
|
||||||
for p in columns[4:]:
|
for col in chart_columns:
|
||||||
|
key = col.get("key") or col.get("fieldname")
|
||||||
if income:
|
if income:
|
||||||
income_data.append(income[-2].get(p.get("fieldname")))
|
income_data.append(income[-2].get(key))
|
||||||
if expense:
|
if expense:
|
||||||
expense_data.append(expense[-2].get(p.get("fieldname")))
|
expense_data.append(expense[-2].get(key))
|
||||||
if net_profit_loss:
|
if net_profit_loss:
|
||||||
net_profit.append(net_profit_loss.get(p.get("fieldname")))
|
net_profit.append(net_profit_loss.get(key))
|
||||||
|
|
||||||
datasets = []
|
datasets = []
|
||||||
if income_data:
|
if income_data:
|
||||||
|
|||||||
@@ -390,7 +390,7 @@ def calculate_values(
|
|||||||
prepare_opening_closing(d)
|
prepare_opening_closing(d)
|
||||||
|
|
||||||
|
|
||||||
def calculate_total_row(accounts, company_currency):
|
def calculate_total_row(data, company_currency, show_group_accounts=True):
|
||||||
total_row = {
|
total_row = {
|
||||||
"account": "'" + _("Total") + "'",
|
"account": "'" + _("Total") + "'",
|
||||||
"account_name": "'" + _("Total") + "'",
|
"account_name": "'" + _("Total") + "'",
|
||||||
@@ -407,10 +407,16 @@ def calculate_total_row(accounts, company_currency):
|
|||||||
"currency": company_currency,
|
"currency": company_currency,
|
||||||
}
|
}
|
||||||
|
|
||||||
for d in accounts:
|
def sum_value_fields(row):
|
||||||
if not d.parent_account:
|
for field in value_fields:
|
||||||
for field in value_fields:
|
total_row[field] += row[field]
|
||||||
total_row[field] += d[field]
|
|
||||||
|
for d in data:
|
||||||
|
if not show_group_accounts:
|
||||||
|
sum_value_fields(d)
|
||||||
|
|
||||||
|
elif show_group_accounts and not d.get("parent_account"):
|
||||||
|
sum_value_fields(d)
|
||||||
|
|
||||||
return total_row
|
return total_row
|
||||||
|
|
||||||
@@ -456,11 +462,13 @@ def prepare_data(accounts, filters, parent_children_map, company_currency):
|
|||||||
row["has_value"] = has_value
|
row["has_value"] = has_value
|
||||||
data.append(row)
|
data.append(row)
|
||||||
|
|
||||||
total_row = calculate_total_row(accounts, company_currency)
|
|
||||||
|
|
||||||
if not filters.get("show_group_accounts"):
|
if not filters.get("show_group_accounts"):
|
||||||
data = hide_group_accounts(data)
|
data = hide_group_accounts(data)
|
||||||
|
|
||||||
|
total_row = calculate_total_row(
|
||||||
|
data, company_currency, show_group_accounts=filters.get("show_group_accounts")
|
||||||
|
)
|
||||||
|
|
||||||
data.extend([{}, total_row])
|
data.extend([{}, total_row])
|
||||||
|
|
||||||
return data
|
return data
|
||||||
|
|||||||
@@ -147,7 +147,12 @@ def get_appropriate_company(filters):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_invoiced_item_gross_margin(sales_invoice=None, item_code=None, company=None, with_item_data=False):
|
def get_invoiced_item_gross_margin(
|
||||||
|
sales_invoice: str | None = None,
|
||||||
|
item_code: str | None = None,
|
||||||
|
company: str | None = None,
|
||||||
|
with_item_data: bool = False,
|
||||||
|
):
|
||||||
from erpnext.accounts.report.gross_profit.gross_profit import GrossProfitGenerator
|
from erpnext.accounts.report.gross_profit.gross_profit import GrossProfitGenerator
|
||||||
|
|
||||||
sales_invoice = sales_invoice or frappe.form_dict.get("sales_invoice")
|
sales_invoice = sales_invoice or frappe.form_dict.get("sales_invoice")
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
|
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
|
from datetime import date, datetime
|
||||||
from json import loads
|
from json import loads
|
||||||
from typing import TYPE_CHECKING, Optional
|
from typing import TYPE_CHECKING, Optional
|
||||||
|
|
||||||
@@ -60,15 +61,15 @@ OUTSTANDING_DOCTYPES = frozenset(["Sales Invoice", "Purchase Invoice", "Fees"])
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_fiscal_year(
|
def get_fiscal_year(
|
||||||
date=None,
|
date: str | datetime | None = None,
|
||||||
fiscal_year=None,
|
fiscal_year: str | None = None,
|
||||||
label="Date",
|
label: str = "Date",
|
||||||
verbose=1,
|
verbose: int = 1,
|
||||||
company=None,
|
company: str | None = None,
|
||||||
as_dict=False,
|
as_dict: bool = False,
|
||||||
boolean=None,
|
boolean: str | bool | None = None,
|
||||||
raise_on_missing=True,
|
raise_on_missing: bool = True,
|
||||||
truncate=False,
|
truncate: bool = False,
|
||||||
):
|
):
|
||||||
if isinstance(raise_on_missing, str):
|
if isinstance(raise_on_missing, str):
|
||||||
raise_on_missing = loads(raise_on_missing)
|
raise_on_missing = loads(raise_on_missing)
|
||||||
@@ -93,14 +94,14 @@ def get_fiscal_year(
|
|||||||
|
|
||||||
|
|
||||||
def get_fiscal_years(
|
def get_fiscal_years(
|
||||||
transaction_date=None,
|
transaction_date: str | None = None,
|
||||||
fiscal_year=None,
|
fiscal_year: str | None = None,
|
||||||
label="Date",
|
label: str = "Date",
|
||||||
verbose=1,
|
verbose: int = 1,
|
||||||
company=None,
|
company: str | None = None,
|
||||||
as_dict=False,
|
as_dict: bool = False,
|
||||||
boolean=None,
|
boolean: str | None = None,
|
||||||
raise_on_missing=True,
|
raise_on_missing: bool = True,
|
||||||
):
|
):
|
||||||
if transaction_date:
|
if transaction_date:
|
||||||
transaction_date = getdate(transaction_date)
|
transaction_date = getdate(transaction_date)
|
||||||
@@ -171,7 +172,7 @@ def _get_fiscal_years(company=None):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_fiscal_year_filter_field(company=None):
|
def get_fiscal_year_filter_field(company: str | None = None):
|
||||||
field = {"fieldtype": "Select", "options": [], "operator": "Between", "query_value": True}
|
field = {"fieldtype": "Select", "options": [], "operator": "Between", "query_value": True}
|
||||||
fiscal_years = get_fiscal_years(company=company)
|
fiscal_years = get_fiscal_years(company=company)
|
||||||
for fiscal_year in fiscal_years:
|
for fiscal_year in fiscal_years:
|
||||||
@@ -199,18 +200,18 @@ def validate_fiscal_year(date, fiscal_year, company, label="Date", doc=None):
|
|||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_balance_on(
|
def get_balance_on(
|
||||||
account=None,
|
account: str | None = None,
|
||||||
date=None,
|
date: str | date | None = None,
|
||||||
party_type=None,
|
party_type: str | None = None,
|
||||||
party=None,
|
party: str | None = None,
|
||||||
company=None,
|
company: str | None = None,
|
||||||
in_account_currency=True,
|
in_account_currency: bool = True,
|
||||||
cost_center=None,
|
cost_center: str | None = None,
|
||||||
ignore_account_permission=False,
|
ignore_account_permission: bool = False,
|
||||||
account_type=None,
|
account_type: str | None = None,
|
||||||
start_date=None,
|
start_date: str | None = None,
|
||||||
finance_book=None,
|
finance_book: str | None = None,
|
||||||
include_default_fb_balances=False,
|
include_default_fb_balances: bool = False,
|
||||||
):
|
):
|
||||||
if not account and frappe.form_dict.get("account"):
|
if not account and frappe.form_dict.get("account"):
|
||||||
account = frappe.form_dict.get("account")
|
account = frappe.form_dict.get("account")
|
||||||
@@ -437,7 +438,7 @@ def get_count_on(account, fieldname, date):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def add_ac(args=None):
|
def add_ac(args: frappe._dict | None = None):
|
||||||
from frappe.desk.treeview import make_tree_args
|
from frappe.desk.treeview import make_tree_args
|
||||||
|
|
||||||
if not args:
|
if not args:
|
||||||
@@ -469,7 +470,7 @@ def add_ac(args=None):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def add_cc(args=None):
|
def add_cc(args: frappe._dict | None = None):
|
||||||
from frappe.desk.treeview import make_tree_args
|
from frappe.desk.treeview import make_tree_args
|
||||||
|
|
||||||
if not args:
|
if not args:
|
||||||
@@ -500,7 +501,8 @@ def _build_dimensions_dict_for_exc_gain_loss(
|
|||||||
dimensions_dict = frappe._dict()
|
dimensions_dict = frappe._dict()
|
||||||
if entry and active_dimensions:
|
if entry and active_dimensions:
|
||||||
for dim in active_dimensions:
|
for dim in active_dimensions:
|
||||||
dimensions_dict[dim.fieldname] = entry.get(dim.fieldname)
|
if entry_dimension := entry.get(dim.fieldname):
|
||||||
|
dimensions_dict[dim.fieldname] = entry_dimension
|
||||||
return dimensions_dict
|
return dimensions_dict
|
||||||
|
|
||||||
|
|
||||||
@@ -1153,7 +1155,7 @@ def remove_ref_doc_link_from_pe(
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_company_default(company, fieldname, ignore_validation=False):
|
def get_company_default(company: str, fieldname: str, ignore_validation: bool = False):
|
||||||
value = frappe.get_cached_value("Company", company, fieldname)
|
value = frappe.get_cached_value("Company", company, fieldname)
|
||||||
|
|
||||||
if not ignore_validation and not value:
|
if not ignore_validation and not value:
|
||||||
@@ -1338,7 +1340,9 @@ def get_companies():
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_children(doctype, parent, company, is_root=False, include_disabled=False):
|
def get_children(
|
||||||
|
doctype: str, parent: str, company: str, is_root: bool = False, include_disabled: bool = False
|
||||||
|
):
|
||||||
if isinstance(include_disabled, str):
|
if isinstance(include_disabled, str):
|
||||||
include_disabled = loads(include_disabled)
|
include_disabled = loads(include_disabled)
|
||||||
from erpnext.accounts.report.financial_statements import sort_accounts
|
from erpnext.accounts.report.financial_statements import sort_accounts
|
||||||
@@ -1371,7 +1375,12 @@ def get_children(doctype, parent, company, is_root=False, include_disabled=False
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_account_balances(accounts, company, finance_book=None, include_default_fb_balances=False):
|
def get_account_balances(
|
||||||
|
accounts: str | list,
|
||||||
|
company: str,
|
||||||
|
finance_book: str | None = None,
|
||||||
|
include_default_fb_balances: bool = False,
|
||||||
|
):
|
||||||
if isinstance(accounts, str):
|
if isinstance(accounts, str):
|
||||||
accounts = loads(accounts)
|
accounts = loads(accounts)
|
||||||
|
|
||||||
@@ -1464,7 +1473,9 @@ def create_payment_gateway_account(gateway, payment_channel="Email", company=Non
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def update_cost_center(docname, cost_center_name, cost_center_number, company, merge):
|
def update_cost_center(
|
||||||
|
docname: str, cost_center_name: str, cost_center_number: str, company: str, merge: bool
|
||||||
|
):
|
||||||
"""
|
"""
|
||||||
Renames the document by adding the number as a prefix to the current name and updates
|
Renames the document by adding the number as a prefix to the current name and updates
|
||||||
all transaction where it was present.
|
all transaction where it was present.
|
||||||
@@ -1544,7 +1555,7 @@ def parse_naming_series_variable(doc, variable):
|
|||||||
|
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
def get_coa(doctype, parent, is_root=None, chart=None):
|
def get_coa(doctype: str, parent: str, is_root: bool | None = None, chart: str | None = None):
|
||||||
from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import (
|
from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import (
|
||||||
build_tree_from_json,
|
build_tree_from_json,
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
{
|
||||||
|
"allow_roles": [
|
||||||
|
{
|
||||||
|
"role": "Accounts Manager"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Accounts User"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Quality Manager"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"creation": "2026-02-23 20:56:50.917521",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Module Onboarding",
|
||||||
|
"idx": 0,
|
||||||
|
"is_complete": 0,
|
||||||
|
"modified": "2026-02-26 10:45:47.970714",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Assets",
|
||||||
|
"name": "Asset Onboarding",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"step": "Learn Asset"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": "Create Asset Category"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": "Create Asset Item"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": "Create Asset Location"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": "Create Existing Asset"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": "View Balance Sheet"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Assets Setup"
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"action": "Create Entry",
|
||||||
|
"action_label": "Create Asset Category",
|
||||||
|
"creation": "2026-02-23 20:50:50.211884",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Onboarding Step",
|
||||||
|
"idx": 1,
|
||||||
|
"is_complete": 0,
|
||||||
|
"is_single": 0,
|
||||||
|
"is_skipped": 0,
|
||||||
|
"modified": "2026-02-23 20:50:50.211884",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"name": "Create Asset Category",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"reference_document": "Asset Category",
|
||||||
|
"show_form_tour": 0,
|
||||||
|
"show_full_form": 0,
|
||||||
|
"title": "Create Asset Category",
|
||||||
|
"validate_action": 1
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"action": "Create Entry",
|
||||||
|
"action_label": "Create Asset Item",
|
||||||
|
"creation": "2026-02-23 20:52:40.135614",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Onboarding Step",
|
||||||
|
"idx": 1,
|
||||||
|
"is_complete": 0,
|
||||||
|
"is_single": 0,
|
||||||
|
"is_skipped": 0,
|
||||||
|
"modified": "2026-02-23 22:31:53.211343",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"name": "Create Asset Item",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"reference_document": "Item",
|
||||||
|
"route_options": "{\n \"is_fixed_asset\": 1,\n \"is_stock_item\": 0\n}",
|
||||||
|
"show_form_tour": 0,
|
||||||
|
"show_full_form": 1,
|
||||||
|
"title": "Create Asset Item",
|
||||||
|
"validate_action": 1
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"action": "Create Entry",
|
||||||
|
"action_label": "Create Asset Location",
|
||||||
|
"creation": "2026-02-23 20:53:07.450876",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Onboarding Step",
|
||||||
|
"idx": 0,
|
||||||
|
"is_complete": 0,
|
||||||
|
"is_single": 0,
|
||||||
|
"is_skipped": 0,
|
||||||
|
"modified": "2026-02-23 20:53:07.450876",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"name": "Create Asset Location",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"reference_document": "Location",
|
||||||
|
"show_form_tour": 0,
|
||||||
|
"show_full_form": 0,
|
||||||
|
"title": "Create Asset Location",
|
||||||
|
"validate_action": 1
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"action": "Create Entry",
|
||||||
|
"action_label": "Create Existing Asset",
|
||||||
|
"creation": "2026-02-23 20:54:25.961869",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Onboarding Step",
|
||||||
|
"idx": 3,
|
||||||
|
"is_complete": 0,
|
||||||
|
"is_single": 0,
|
||||||
|
"is_skipped": 0,
|
||||||
|
"modified": "2026-02-23 22:31:48.789836",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"name": "Create Existing Asset",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"reference_document": "Asset",
|
||||||
|
"route_options": "{\n \"asset_type\": \"Existing Asset\"\n}",
|
||||||
|
"show_form_tour": 0,
|
||||||
|
"show_full_form": 1,
|
||||||
|
"title": "Create Existing Asset",
|
||||||
|
"validate_action": 1
|
||||||
|
}
|
||||||
20
erpnext/assets/onboarding_step/learn_asset/learn_asset.json
Normal file
20
erpnext/assets/onboarding_step/learn_asset/learn_asset.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"action": "View Docs",
|
||||||
|
"action_label": "Learn Asset",
|
||||||
|
"creation": "2026-02-23 21:00:47.254648",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Onboarding Step",
|
||||||
|
"idx": 0,
|
||||||
|
"is_complete": 0,
|
||||||
|
"is_single": 0,
|
||||||
|
"is_skipped": 0,
|
||||||
|
"modified": "2026-02-26 10:44:59.557156",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"name": "Learn Asset",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"path": "https://docs.frappe.io/erpnext/assets/introduction",
|
||||||
|
"show_form_tour": 0,
|
||||||
|
"show_full_form": 0,
|
||||||
|
"title": "Learn Asset",
|
||||||
|
"validate_action": 1
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"action": "View Report",
|
||||||
|
"action_label": "View Balance Sheet",
|
||||||
|
"creation": "2026-02-23 19:22:57.651194",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Onboarding Step",
|
||||||
|
"idx": 0,
|
||||||
|
"is_complete": 0,
|
||||||
|
"is_single": 0,
|
||||||
|
"is_skipped": 0,
|
||||||
|
"modified": "2026-02-23 22:44:39.178107",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"name": "View Balance Sheet",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"reference_report": "Balance Sheet",
|
||||||
|
"report_description": "View Balance Sheet",
|
||||||
|
"report_reference_doctype": "GL Entry",
|
||||||
|
"report_type": "Script Report",
|
||||||
|
"show_form_tour": 0,
|
||||||
|
"show_full_form": 0,
|
||||||
|
"title": "View Balance Sheet",
|
||||||
|
"validate_action": 1
|
||||||
|
}
|
||||||
@@ -428,7 +428,7 @@ erpnext.buying.PurchaseOrderController = class PurchaseOrderController extends (
|
|||||||
this.frm.add_custom_button(
|
this.frm.add_custom_button(
|
||||||
__("Payment Request"),
|
__("Payment Request"),
|
||||||
function () {
|
function () {
|
||||||
me.make_payment_request();
|
me.make_payment_request_with_schedule();
|
||||||
},
|
},
|
||||||
__("Create")
|
__("Create")
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
"order_confirmation_date",
|
"order_confirmation_date",
|
||||||
"column_break_7",
|
"column_break_7",
|
||||||
"transaction_date",
|
"transaction_date",
|
||||||
|
"transaction_time",
|
||||||
"schedule_date",
|
"schedule_date",
|
||||||
"column_break1",
|
"column_break1",
|
||||||
"is_subcontracted",
|
"is_subcontracted",
|
||||||
@@ -77,16 +78,16 @@
|
|||||||
"total_taxes_and_charges",
|
"total_taxes_and_charges",
|
||||||
"totals_section",
|
"totals_section",
|
||||||
"grand_total",
|
"grand_total",
|
||||||
|
"in_words",
|
||||||
|
"column_break4",
|
||||||
"disable_rounded_total",
|
"disable_rounded_total",
|
||||||
"rounding_adjustment",
|
"rounding_adjustment",
|
||||||
"column_break4",
|
|
||||||
"in_words",
|
|
||||||
"rounded_total",
|
"rounded_total",
|
||||||
"base_totals_section",
|
"base_totals_section",
|
||||||
"base_grand_total",
|
"base_grand_total",
|
||||||
"base_rounding_adjustment",
|
|
||||||
"column_break_jkoz",
|
|
||||||
"base_in_words",
|
"base_in_words",
|
||||||
|
"column_break_jkoz",
|
||||||
|
"base_rounding_adjustment",
|
||||||
"base_rounded_total",
|
"base_rounded_total",
|
||||||
"section_break_tnkm",
|
"section_break_tnkm",
|
||||||
"advance_paid",
|
"advance_paid",
|
||||||
@@ -1311,6 +1312,14 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "section_break_tnkm",
|
"fieldname": "section_break_tnkm",
|
||||||
"fieldtype": "Section Break"
|
"fieldtype": "Section Break"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "Now",
|
||||||
|
"depends_on": "is_internal_supplier",
|
||||||
|
"fieldname": "transaction_time",
|
||||||
|
"fieldtype": "Time",
|
||||||
|
"label": "Time",
|
||||||
|
"mandatory_depends_on": "is_internal_supplier"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"grid_page_length": 50,
|
"grid_page_length": 50,
|
||||||
@@ -1318,7 +1327,7 @@
|
|||||||
"idx": 105,
|
"idx": 105,
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2026-02-06 17:07:24.249692",
|
"modified": "2026-03-02 00:40:47.119584",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Buying",
|
"module": "Buying",
|
||||||
"name": "Purchase Order",
|
"name": "Purchase Order",
|
||||||
|
|||||||
@@ -167,6 +167,7 @@ class PurchaseOrder(BuyingController):
|
|||||||
total_qty: DF.Float
|
total_qty: DF.Float
|
||||||
total_taxes_and_charges: DF.Currency
|
total_taxes_and_charges: DF.Currency
|
||||||
transaction_date: DF.Date
|
transaction_date: DF.Date
|
||||||
|
transaction_time: DF.Time | None
|
||||||
# end: auto-generated types
|
# end: auto-generated types
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
{
|
||||||
|
"creation": "2026-02-22 16:46:17.299107",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Form Tour",
|
||||||
|
"first_document": 0,
|
||||||
|
"idx": 0,
|
||||||
|
"include_name_field": 0,
|
||||||
|
"is_standard": 1,
|
||||||
|
"list_name": "List",
|
||||||
|
"modified": "2026-02-22 16:46:17.299107",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Buying",
|
||||||
|
"name": "Supplier Form Tour",
|
||||||
|
"new_document_form": 0,
|
||||||
|
"owner": "Administrator",
|
||||||
|
"reference_doctype": "Supplier",
|
||||||
|
"report_name": "",
|
||||||
|
"save_on_complete": 1,
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"description": "Enter the Full Name of the Supplier",
|
||||||
|
"fieldname": "supplier_name",
|
||||||
|
"fieldtype": "Data",
|
||||||
|
"has_next_condition": 0,
|
||||||
|
"hide_buttons": 0,
|
||||||
|
"is_table_field": 0,
|
||||||
|
"label": "Supplier Name",
|
||||||
|
"modal_trigger": 0,
|
||||||
|
"next_on_click": 0,
|
||||||
|
"offset_x": 0,
|
||||||
|
"offset_y": 0,
|
||||||
|
"popover_element": 0,
|
||||||
|
"position": "Left",
|
||||||
|
"title": "Full Name",
|
||||||
|
"ui_tour": 0
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Supplier Form Tour",
|
||||||
|
"track_steps": 0,
|
||||||
|
"ui_tour": 0,
|
||||||
|
"view_name": "Workspaces"
|
||||||
|
}
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
{
|
||||||
|
"allow_roles": [
|
||||||
|
{
|
||||||
|
"role": "Purchase Manager"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"role": "Purchase User"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"creation": "2026-02-19 10:53:58.761773",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Module Onboarding",
|
||||||
|
"idx": 0,
|
||||||
|
"is_complete": 0,
|
||||||
|
"modified": "2026-02-25 16:59:28.328912",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"module": "Buying",
|
||||||
|
"name": "Buying Onboarding",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"step": "Create Supplier"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": "Create Item"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": "Create Purchase Order"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": "Create Purchase Invoice"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": "View Purchase Order Analysis"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"step": "Review Buying Settings"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"title": "Buying Setup"
|
||||||
|
}
|
||||||
20
erpnext/buying/onboarding_step/create_item/create_item.json
Normal file
20
erpnext/buying/onboarding_step/create_item/create_item.json
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"action": "Create Entry",
|
||||||
|
"action_label": "Create Item",
|
||||||
|
"creation": "2026-02-19 12:38:40.865013",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Onboarding Step",
|
||||||
|
"idx": 8,
|
||||||
|
"is_complete": 0,
|
||||||
|
"is_single": 0,
|
||||||
|
"is_skipped": 0,
|
||||||
|
"modified": "2026-02-25 16:58:56.384284",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"name": "Create Item",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"reference_document": "Item",
|
||||||
|
"show_form_tour": 0,
|
||||||
|
"show_full_form": 0,
|
||||||
|
"title": "Create Item",
|
||||||
|
"validate_action": 1
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"action": "Create Entry",
|
||||||
|
"action_label": "Create Purchase Invoice",
|
||||||
|
"creation": "2026-02-19 12:38:14.868162",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Onboarding Step",
|
||||||
|
"idx": 5,
|
||||||
|
"is_complete": 0,
|
||||||
|
"is_single": 0,
|
||||||
|
"is_skipped": 0,
|
||||||
|
"modified": "2026-02-25 16:58:56.386439",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"name": "Create Purchase Invoice",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"reference_document": "Purchase Invoice",
|
||||||
|
"show_form_tour": 0,
|
||||||
|
"show_full_form": 0,
|
||||||
|
"title": "Create Purchase Invoice",
|
||||||
|
"validate_action": 1
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"action": "Create Entry",
|
||||||
|
"action_label": "Create Purchase Order",
|
||||||
|
"creation": "2026-02-19 12:13:44.068135",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Onboarding Step",
|
||||||
|
"form_tour": "",
|
||||||
|
"idx": 2,
|
||||||
|
"is_complete": 0,
|
||||||
|
"is_single": 0,
|
||||||
|
"is_skipped": 0,
|
||||||
|
"modified": "2026-02-25 16:58:56.379480",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"name": "Create Purchase Order",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"reference_document": "Purchase Order",
|
||||||
|
"show_form_tour": 0,
|
||||||
|
"show_full_form": 1,
|
||||||
|
"title": "Create Purchase Order",
|
||||||
|
"validate_action": 1
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"action": "Create Entry",
|
||||||
|
"action_label": "Create supplier",
|
||||||
|
"creation": "2026-02-19 10:53:56.936107",
|
||||||
|
"description": "",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Onboarding Step",
|
||||||
|
"form_tour": "Supplier Form Tour",
|
||||||
|
"idx": 2,
|
||||||
|
"is_complete": 0,
|
||||||
|
"is_single": 0,
|
||||||
|
"is_skipped": 0,
|
||||||
|
"modified": "2026-02-25 16:58:56.375824",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"name": "Create Supplier",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"reference_document": "Supplier",
|
||||||
|
"show_form_tour": 1,
|
||||||
|
"show_full_form": 1,
|
||||||
|
"title": "Create Supplier",
|
||||||
|
"validate_action": 1
|
||||||
|
}
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"action": "Update Settings",
|
||||||
|
"action_label": "Review Buying Settings",
|
||||||
|
"creation": "2026-02-23 20:27:23.664752",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Onboarding Step",
|
||||||
|
"idx": 0,
|
||||||
|
"is_complete": 0,
|
||||||
|
"is_single": 1,
|
||||||
|
"is_skipped": 0,
|
||||||
|
"modified": "2026-02-25 16:58:56.388794",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"name": "Review Buying Settings",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"path": "desk/buying-settings",
|
||||||
|
"reference_document": "Buying Settings",
|
||||||
|
"show_form_tour": 0,
|
||||||
|
"show_full_form": 0,
|
||||||
|
"title": "Review Buying Settings",
|
||||||
|
"validate_action": 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"action": "View Report",
|
||||||
|
"action_label": "View Purchase Order Analysis",
|
||||||
|
"creation": "2026-02-23 20:26:29.245112",
|
||||||
|
"docstatus": 0,
|
||||||
|
"doctype": "Onboarding Step",
|
||||||
|
"idx": 0,
|
||||||
|
"is_complete": 0,
|
||||||
|
"is_single": 0,
|
||||||
|
"is_skipped": 0,
|
||||||
|
"modified": "2026-02-25 10:33:43.731928",
|
||||||
|
"modified_by": "Administrator",
|
||||||
|
"name": "View Purchase Order Analysis",
|
||||||
|
"owner": "Administrator",
|
||||||
|
"reference_report": "Purchase Order Analysis",
|
||||||
|
"report_description": "View Purchase Order Analysis",
|
||||||
|
"report_reference_doctype": "Purchase Order",
|
||||||
|
"report_type": "Script Report",
|
||||||
|
"show_form_tour": 0,
|
||||||
|
"show_full_form": 0,
|
||||||
|
"title": "View Purchase Order Analysis",
|
||||||
|
"validate_action": 1
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user