Merge pull request #41032 from frappe/version-14-hotfix

chore: release v14
This commit is contained in:
rohitwaghchaure
2024-04-17 11:41:15 +05:30
committed by GitHub
627 changed files with 4512 additions and 6554 deletions

View File

@@ -29,3 +29,6 @@ b147b85e6ac19a9220cd1e2958a6ebd99373283a
# bulk format python code with black # bulk format python code with black
baec607ff5905b1c67531096a9cf50ec7ff00a5d baec607ff5905b1c67531096a9cf50ec7ff00a5d
# ruff
4d34b1ead73baf4c5430a2ecbe44b9e8468d7626

View File

@@ -65,27 +65,15 @@ repos:
.*/loan_write_off.js .*/loan_write_off.js
)$ )$
- repo: https://github.com/PyCQA/flake8 - repo: https://github.com/astral-sh/ruff-pre-commit
rev: 5.0.4 rev: v0.2.0
hooks: hooks:
- id: flake8 - id: ruff
additional_dependencies: [ name: "Run ruff linter and apply fixes"
'flake8-bugbear', args: ["--fix"]
]
args: ['--config', '.github/helper/.flake8_strict']
exclude: ".*setup.py$"
- repo: https://github.com/adityahase/black - id: ruff-format
rev: 9cb0a69f4d0030cdf687eddf314468b39ed54119 name: "Format Python code"
hooks:
- id: black
additional_dependencies: ['click==8.0.4']
- repo: https://github.com/PyCQA/isort
rev: 5.12.0
hooks:
- id: isort
exclude: ".*setup.py$"
ci: ci:

View File

@@ -36,10 +36,8 @@ def get_default_cost_center(company):
if not frappe.flags.company_cost_center: if not frappe.flags.company_cost_center:
frappe.flags.company_cost_center = {} frappe.flags.company_cost_center = {}
if not company in frappe.flags.company_cost_center: if company not in frappe.flags.company_cost_center:
frappe.flags.company_cost_center[company] = frappe.get_cached_value( frappe.flags.company_cost_center[company] = frappe.get_cached_value("Company", company, "cost_center")
"Company", company, "cost_center"
)
return frappe.flags.company_cost_center[company] return frappe.flags.company_cost_center[company]
@@ -47,7 +45,7 @@ def get_company_currency(company):
"""Returns the default company currency""" """Returns the default company currency"""
if not frappe.flags.company_currency: if not frappe.flags.company_currency:
frappe.flags.company_currency = {} frappe.flags.company_currency = {}
if not company in frappe.flags.company_currency: if company not in frappe.flags.company_currency:
frappe.flags.company_currency[company] = frappe.db.get_value( frappe.flags.company_currency[company] = frappe.db.get_value(
"Company", company, "default_currency", cache=True "Company", company, "default_currency", cache=True
) )
@@ -81,7 +79,7 @@ def is_perpetual_inventory_enabled(company):
if not hasattr(frappe.local, "enable_perpetual_inventory"): if not hasattr(frappe.local, "enable_perpetual_inventory"):
frappe.local.enable_perpetual_inventory = {} frappe.local.enable_perpetual_inventory = {}
if not company in frappe.local.enable_perpetual_inventory: if company not in frappe.local.enable_perpetual_inventory:
frappe.local.enable_perpetual_inventory[company] = ( frappe.local.enable_perpetual_inventory[company] = (
frappe.get_cached_value("Company", company, "enable_perpetual_inventory") or 0 frappe.get_cached_value("Company", company, "enable_perpetual_inventory") or 0
) )
@@ -96,7 +94,7 @@ def get_default_finance_book(company=None):
if not hasattr(frappe.local, "default_finance_book"): if not hasattr(frappe.local, "default_finance_book"):
frappe.local.default_finance_book = {} frappe.local.default_finance_book = {}
if not company in frappe.local.default_finance_book: if company not in frappe.local.default_finance_book:
frappe.local.default_finance_book[company] = frappe.get_cached_value( frappe.local.default_finance_book[company] = frappe.get_cached_value(
"Company", company, "default_finance_book" "Company", company, "default_finance_book"
) )
@@ -108,7 +106,7 @@ def get_party_account_type(party_type):
if not hasattr(frappe.local, "party_account_types"): if not hasattr(frappe.local, "party_account_types"):
frappe.local.party_account_types = {} frappe.local.party_account_types = {}
if not party_type in frappe.local.party_account_types: if party_type not in frappe.local.party_account_types:
frappe.local.party_account_types[party_type] = ( frappe.local.party_account_types[party_type] = (
frappe.db.get_value("Party Type", party_type, "account_type") or "" frappe.db.get_value("Party Type", party_type, "account_type") or ""
) )

View File

@@ -11,14 +11,14 @@ class ERPNextAddress(Address):
def validate(self): def validate(self):
self.validate_reference() self.validate_reference()
self.update_compnay_address() self.update_compnay_address()
super(ERPNextAddress, self).validate() super().validate()
def link_address(self): def link_address(self):
"""Link address based on owner""" """Link address based on owner"""
if self.is_your_company_address: if self.is_your_company_address:
return return
return super(ERPNextAddress, self).link_address() return super().link_address()
def update_compnay_address(self): def update_compnay_address(self):
for link in self.get("links"): for link in self.get("links"):
@@ -26,11 +26,11 @@ class ERPNextAddress(Address):
self.is_your_company_address = 1 self.is_your_company_address = 1
def validate_reference(self): def validate_reference(self):
if self.is_your_company_address and not [ if self.is_your_company_address and not [row for row in self.links if row.link_doctype == "Company"]:
row for row in self.links if row.link_doctype == "Company"
]:
frappe.throw( frappe.throw(
_("Address needs to be linked to a Company. Please add a row for Company in the Links table."), _(
"Address needs to be linked to a Company. Please add a row for Company in the Links table."
),
title=_("Company Not Linked"), title=_("Company Not Linked"),
) )

View File

@@ -37,7 +37,7 @@ def get(
filters = frappe.parse_json(filters) or frappe.parse_json(chart.filters_json) filters = frappe.parse_json(filters) or frappe.parse_json(chart.filters_json)
account = filters.get("account") account = filters.get("account")
company = filters.get("company") filters.get("company")
if not account and chart_name: if not account and chart_name:
frappe.throw( frappe.throw(
@@ -83,7 +83,6 @@ def build_result(account, dates, gl_entries):
# get balances in debit # get balances in debit
for entry in gl_entries: for entry in gl_entries:
# entry date is after the current pointer, so move the pointer forward # entry date is after the current pointer, so move the pointer forward
while getdate(entry.posting_date) > result[date_index][0]: while getdate(entry.posting_date) > result[date_index][0]:
date_index += 1 date_index += 1
@@ -133,8 +132,6 @@ def get_dates_from_timegrain(from_date, to_date, timegrain):
dates = [get_period_ending(from_date, timegrain)] dates = [get_period_ending(from_date, timegrain)]
while getdate(dates[-1]) < getdate(to_date): while getdate(dates[-1]) < getdate(to_date):
date = get_period_ending( date = get_period_ending(add_to_date(dates[-1], years=years, months=months, days=days), timegrain)
add_to_date(dates[-1], years=years, months=months, days=days), timegrain
)
dates.append(date) dates.append(date)
return dates return dates

View File

@@ -24,14 +24,10 @@ from erpnext.accounts.utils import get_account_currency
def validate_service_stop_date(doc): def validate_service_stop_date(doc):
"""Validates service_stop_date for Purchase Invoice and Sales Invoice""" """Validates service_stop_date for Purchase Invoice and Sales Invoice"""
enable_check = ( enable_check = "enable_deferred_revenue" if doc.doctype == "Sales Invoice" else "enable_deferred_expense"
"enable_deferred_revenue" if doc.doctype == "Sales Invoice" else "enable_deferred_expense"
)
old_stop_dates = {} old_stop_dates = {}
old_doc = frappe.db.get_all( old_doc = frappe.db.get_all(f"{doc.doctype} Item", {"parent": doc.name}, ["name", "service_stop_date"])
"{0} Item".format(doc.doctype), {"parent": doc.name}, ["name", "service_stop_date"]
)
for d in old_doc: for d in old_doc:
old_stop_dates[d.name] = d.service_stop_date or "" old_stop_dates[d.name] = d.service_stop_date or ""
@@ -62,16 +58,14 @@ def build_conditions(process_type, account, company):
) )
if account: if account:
conditions += "AND %s='%s'" % (deferred_account, account) conditions += f"AND {deferred_account}='{account}'"
elif company: elif company:
conditions += f"AND p.company = {frappe.db.escape(company)}" conditions += f"AND p.company = {frappe.db.escape(company)}"
return conditions return conditions
def convert_deferred_expense_to_expense( def convert_deferred_expense_to_expense(deferred_process, start_date=None, end_date=None, conditions=""):
deferred_process, start_date=None, end_date=None, conditions=""
):
# book the expense/income on the last day, but it will be trigger on the 1st of month at 12:00 AM # book the expense/income on the last day, but it will be trigger on the 1st of month at 12:00 AM
if not start_date: if not start_date:
@@ -81,16 +75,14 @@ def convert_deferred_expense_to_expense(
# check for the purchase invoice for which GL entries has to be done # check for the purchase invoice for which GL entries has to be done
invoices = frappe.db.sql_list( invoices = frappe.db.sql_list(
""" f"""
select distinct item.parent select distinct item.parent
from `tabPurchase Invoice Item` item, `tabPurchase Invoice` p from `tabPurchase Invoice Item` item, `tabPurchase Invoice` p
where item.service_start_date<=%s and item.service_end_date>=%s where item.service_start_date<=%s and item.service_end_date>=%s
and item.enable_deferred_expense = 1 and item.parent=p.name and item.enable_deferred_expense = 1 and item.parent=p.name
and item.docstatus = 1 and ifnull(item.amount, 0) > 0 and item.docstatus = 1 and ifnull(item.amount, 0) > 0
{0} {conditions}
""".format( """,
conditions
),
(end_date, start_date), (end_date, start_date),
) # nosec ) # nosec
@@ -103,9 +95,7 @@ def convert_deferred_expense_to_expense(
send_mail(deferred_process) send_mail(deferred_process)
def convert_deferred_revenue_to_income( def convert_deferred_revenue_to_income(deferred_process, start_date=None, end_date=None, conditions=""):
deferred_process, start_date=None, end_date=None, conditions=""
):
# book the expense/income on the last day, but it will be trigger on the 1st of month at 12:00 AM # book the expense/income on the last day, but it will be trigger on the 1st of month at 12:00 AM
if not start_date: if not start_date:
@@ -115,16 +105,14 @@ def convert_deferred_revenue_to_income(
# check for the sales invoice for which GL entries has to be done # check for the sales invoice for which GL entries has to be done
invoices = frappe.db.sql_list( invoices = frappe.db.sql_list(
""" f"""
select distinct item.parent select distinct item.parent
from `tabSales Invoice Item` item, `tabSales Invoice` p from `tabSales Invoice Item` item, `tabSales Invoice` p
where item.service_start_date<=%s and item.service_end_date>=%s where item.service_start_date<=%s and item.service_end_date>=%s
and item.enable_deferred_revenue = 1 and item.parent=p.name and item.enable_deferred_revenue = 1 and item.parent=p.name
and item.docstatus = 1 and ifnull(item.amount, 0) > 0 and item.docstatus = 1 and ifnull(item.amount, 0) > 0
{0} {conditions}
""".format( """,
conditions
),
(end_date, start_date), (end_date, start_date),
) # nosec ) # nosec
@@ -243,9 +231,7 @@ def calculate_monthly_amount(
already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount( already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(
doc, item doc, item
) )
base_amount = flt( base_amount = flt(item.base_net_amount - already_booked_amount, item.precision("base_net_amount"))
item.base_net_amount - already_booked_amount, item.precision("base_net_amount")
)
if account_currency == doc.company_currency: if account_currency == doc.company_currency:
amount = base_amount amount = base_amount
else: else:
@@ -265,17 +251,13 @@ def calculate_amount(doc, item, last_gl_entry, total_days, total_booking_days, a
if account_currency == doc.company_currency: if account_currency == doc.company_currency:
amount = base_amount amount = base_amount
else: else:
amount = flt( amount = flt(item.net_amount * total_booking_days / flt(total_days), item.precision("net_amount"))
item.net_amount * total_booking_days / flt(total_days), item.precision("net_amount")
)
else: else:
already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount( already_booked_amount, already_booked_amount_in_account_currency = get_already_booked_amount(
doc, item doc, item
) )
base_amount = flt( base_amount = flt(item.base_net_amount - already_booked_amount, item.precision("base_net_amount"))
item.base_net_amount - already_booked_amount, item.precision("base_net_amount")
)
if account_currency == doc.company_currency: if account_currency == doc.company_currency:
amount = base_amount amount = base_amount
else: else:
@@ -296,26 +278,22 @@ def get_already_booked_amount(doc, item):
gl_entries_details = frappe.db.sql( gl_entries_details = frappe.db.sql(
""" """
select sum({0}) as total_credit, sum({1}) as total_credit_in_account_currency, voucher_detail_no select sum({}) as total_credit, sum({}) as total_credit_in_account_currency, voucher_detail_no
from `tabGL Entry` where company=%s and account=%s and voucher_type=%s and voucher_no=%s and voucher_detail_no=%s from `tabGL Entry` where company=%s and account=%s and voucher_type=%s and voucher_no=%s and voucher_detail_no=%s
and is_cancelled = 0 and is_cancelled = 0
group by voucher_detail_no group by voucher_detail_no
""".format( """.format(total_credit_debit, total_credit_debit_currency),
total_credit_debit, total_credit_debit_currency
),
(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name),
as_dict=True, as_dict=True,
) )
journal_entry_details = frappe.db.sql( journal_entry_details = frappe.db.sql(
""" """
SELECT sum(c.{0}) as total_credit, sum(c.{1}) as total_credit_in_account_currency, reference_detail_no SELECT sum(c.{}) as total_credit, sum(c.{}) as total_credit_in_account_currency, reference_detail_no
FROM `tabJournal Entry` p , `tabJournal Entry Account` c WHERE p.name = c.parent and FROM `tabJournal Entry` p , `tabJournal Entry Account` c WHERE p.name = c.parent and
p.company = %s and c.account=%s and c.reference_type=%s and c.reference_name=%s and c.reference_detail_no=%s p.company = %s and c.account=%s and c.reference_type=%s and c.reference_name=%s and c.reference_detail_no=%s
and p.docstatus < 2 group by reference_detail_no and p.docstatus < 2 group by reference_detail_no
""".format( """.format(total_credit_debit, total_credit_debit_currency),
total_credit_debit, total_credit_debit_currency
),
(doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name),
as_dict=True, as_dict=True,
) )
@@ -337,9 +315,7 @@ def get_already_booked_amount(doc, item):
def book_deferred_income_or_expense(doc, deferred_process, posting_date=None): def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
enable_check = ( enable_check = "enable_deferred_revenue" if doc.doctype == "Sales Invoice" else "enable_deferred_expense"
"enable_deferred_revenue" if doc.doctype == "Sales Invoice" else "enable_deferred_expense"
)
accounts_frozen_upto = frappe.db.get_single_value("Accounts Settings", "acc_frozen_upto") accounts_frozen_upto = frappe.db.get_single_value("Accounts Settings", "acc_frozen_upto")
@@ -440,9 +416,7 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
via_journal_entry = cint( via_journal_entry = cint(
frappe.db.get_singles_value("Accounts Settings", "book_deferred_entries_via_journal_entry") frappe.db.get_singles_value("Accounts Settings", "book_deferred_entries_via_journal_entry")
) )
submit_journal_entry = cint( submit_journal_entry = cint(frappe.db.get_singles_value("Accounts Settings", "submit_journal_entries"))
frappe.db.get_singles_value("Accounts Settings", "submit_journal_entries")
)
book_deferred_entries_based_on = frappe.db.get_singles_value( book_deferred_entries_based_on = frappe.db.get_singles_value(
"Accounts Settings", "book_deferred_entries_based_on" "Accounts Settings", "book_deferred_entries_based_on"
) )
@@ -462,9 +436,7 @@ def process_deferred_accounting(posting_date=None):
posting_date = today() posting_date = today()
if not cint( if not cint(
frappe.db.get_singles_value( frappe.db.get_singles_value("Accounts Settings", "automatically_process_deferred_accounting_entry")
"Accounts Settings", "automatically_process_deferred_accounting_entry"
)
): ):
return return
@@ -587,16 +559,13 @@ def book_revenue_via_journal_entry(
deferred_process=None, deferred_process=None,
submit="No", submit="No",
): ):
if amount == 0: if amount == 0:
return return
journal_entry = frappe.new_doc("Journal Entry") journal_entry = frappe.new_doc("Journal Entry")
journal_entry.posting_date = posting_date journal_entry.posting_date = posting_date
journal_entry.company = doc.company journal_entry.company = doc.company
journal_entry.voucher_type = ( journal_entry.voucher_type = "Deferred Revenue" if doc.doctype == "Sales Invoice" else "Deferred Expense"
"Deferred Revenue" if doc.doctype == "Sales Invoice" else "Deferred Expense"
)
journal_entry.process_deferred_accounting = deferred_process journal_entry.process_deferred_accounting = deferred_process
debit_entry = { debit_entry = {
@@ -645,7 +614,6 @@ def book_revenue_via_journal_entry(
def get_deferred_booking_accounts(doctype, voucher_detail_no, dr_or_cr): def get_deferred_booking_accounts(doctype, voucher_detail_no, dr_or_cr):
if doctype == "Sales Invoice": if doctype == "Sales Invoice":
credit_account, debit_account = frappe.db.get_value( credit_account, debit_account = frappe.db.get_value(
"Sales Invoice Item", "Sales Invoice Item",

View File

@@ -29,7 +29,7 @@ class Account(NestedSet):
if frappe.local.flags.ignore_update_nsm: if frappe.local.flags.ignore_update_nsm:
return return
else: else:
super(Account, self).on_update() super().on_update()
def onload(self): def onload(self):
frozen_accounts_modifier = frappe.db.get_value( frozen_accounts_modifier = frappe.db.get_value(
@@ -87,9 +87,7 @@ class Account(NestedSet):
def set_root_and_report_type(self): def set_root_and_report_type(self):
if self.parent_account: if self.parent_account:
par = frappe.db.get_value( par = frappe.db.get_value("Account", self.parent_account, ["report_type", "root_type"], as_dict=1)
"Account", self.parent_account, ["report_type", "root_type"], as_dict=1
)
if par.report_type: if par.report_type:
self.report_type = par.report_type self.report_type = par.report_type
@@ -144,9 +142,7 @@ class Account(NestedSet):
def validate_root_company_and_sync_account_to_children(self): def validate_root_company_and_sync_account_to_children(self):
# ignore validation while creating new compnay or while syncing to child companies # ignore validation while creating new compnay or while syncing to child companies
if ( if frappe.local.flags.ignore_root_company_validation or self.flags.ignore_root_company_validation:
frappe.local.flags.ignore_root_company_validation or self.flags.ignore_root_company_validation
):
return return
ancestors = get_root_company(self.company) ancestors = get_root_company(self.company)
if ancestors: if ancestors:
@@ -341,7 +337,7 @@ class Account(NestedSet):
if self.check_gle_exists(): if self.check_gle_exists():
throw(_("Account with existing transaction can not be deleted")) throw(_("Account with existing transaction can not be deleted"))
super(Account, self).on_trash(True) super().on_trash(True)
@frappe.whitelist() @frappe.whitelist()
@@ -349,9 +345,8 @@ class Account(NestedSet):
def get_parent_account(doctype, txt, searchfield, start, page_len, filters): def get_parent_account(doctype, txt, searchfield, start, page_len, filters):
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 = %s where is_group = 1 and docstatus != 2 and company = {}
and %s like %s order by name limit %s offset %s""" and {} like {} order by name limit {} offset {}""".format("%s", searchfield, "%s", "%s", "%s"),
% ("%s", searchfield, "%s", "%s", "%s"),
(filters["company"], "%%%s%%" % txt, page_len, start), (filters["company"], "%%%s%%" % txt, page_len, start),
as_list=1, as_list=1,
) )
@@ -409,9 +404,7 @@ def update_account_number(name, account_name, account_number=None, from_descenda
if not account: if not account:
return return
old_acc_name, old_acc_number = frappe.db.get_value( old_acc_name, old_acc_number = frappe.db.get_value("Account", name, ["account_name", "account_number"])
"Account", name, ["account_name", "account_number"]
)
# check if account exists in parent company # check if account exists in parent company
ancestors = get_ancestors_of("Company", account.company) ancestors = get_ancestors_of("Company", account.company)
@@ -519,7 +512,5 @@ def sync_update_account_number_in_child(
if old_acc_number: if old_acc_number:
filters["account_number"] = old_acc_number filters["account_number"] = old_acc_number
for d in frappe.db.get_values( for d in frappe.db.get_values("Account", filters=filters, fieldname=["company", "name"], as_dict=True):
"Account", filters=filters, fieldname=["company", "name"], as_dict=True
):
update_account_number(d["name"], account_name, account_number, from_descendant=True) update_account_number(d["name"], account_name, account_number, from_descendant=True)

View File

@@ -31,7 +31,6 @@ def create_charts(
"tax_rate", "tax_rate",
"account_currency", "account_currency",
]: ]:
account_number = cstr(child.get("account_number")).strip() account_number = cstr(child.get("account_number")).strip()
account_name, account_name_in_db = add_suffix_if_duplicate( account_name, account_name_in_db = add_suffix_if_duplicate(
account_name, account_number, accounts account_name, account_number, accounts
@@ -39,7 +38,9 @@ def create_charts(
is_group = identify_is_group(child) is_group = identify_is_group(child)
report_type = ( report_type = (
"Balance Sheet" if root_type in ["Asset", "Liability", "Equity"] else "Profit and Loss" "Balance Sheet"
if root_type in ["Asset", "Liability", "Equity"]
else "Profit and Loss"
) )
account = frappe.get_doc( account = frappe.get_doc(
@@ -141,7 +142,7 @@ def get_chart(chart_template, existing_company=None):
for fname in os.listdir(path): for fname in os.listdir(path):
fname = frappe.as_unicode(fname) fname = frappe.as_unicode(fname)
if fname.endswith(".json"): if fname.endswith(".json"):
with open(os.path.join(path, fname), "r") as f: with open(os.path.join(path, fname)) as f:
chart = f.read() chart = f.read()
if chart and json.loads(chart).get("name") == chart_template: if chart and json.loads(chart).get("name") == chart_template:
return json.loads(chart).get("tree") return json.loads(chart).get("tree")
@@ -173,7 +174,7 @@ def get_charts_for_country(country, with_standard=False):
for fname in os.listdir(path): for fname in os.listdir(path):
fname = frappe.as_unicode(fname) fname = frappe.as_unicode(fname)
if (fname.startswith(country_code) or fname.startswith(country)) and fname.endswith(".json"): if (fname.startswith(country_code) or fname.startswith(country)) and fname.endswith(".json"):
with open(os.path.join(path, fname), "r") as f: with open(os.path.join(path, fname)) as f:
_get_chart_name(f.read()) _get_chart_name(f.read())
# if more than one charts, returned then add the standard # if more than one charts, returned then add the standard
@@ -247,7 +248,13 @@ def validate_bank_account(coa, bank_account):
def _get_account_names(account_master): def _get_account_names(account_master):
for account_name, child in account_master.items(): for account_name, child in account_master.items():
if account_name not in ["account_number", "account_type", "root_type", "is_group", "tax_rate"]: if account_name not in [
"account_number",
"account_type",
"root_type",
"is_group",
"tax_rate",
]:
accounts.append(account_name) accounts.append(account_name)
_get_account_names(child) _get_account_names(child)

View File

@@ -26,7 +26,7 @@ def go():
default_account_types = get_default_account_types() default_account_types = get_default_account_types()
country_dirs = [] country_dirs = []
for basepath, folders, files in os.walk(path): for basepath, _folders, _files in os.walk(path):
basename = os.path.basename(basepath) basename = os.path.basename(basepath)
if basename.startswith("l10n_"): if basename.startswith("l10n_"):
country_dirs.append(basename) country_dirs.append(basename)
@@ -35,9 +35,7 @@ def go():
accounts, charts = {}, {} accounts, charts = {}, {}
country_path = os.path.join(path, country_dir) country_path = os.path.join(path, country_dir)
manifest = ast.literal_eval(open(os.path.join(country_path, "__openerp__.py")).read()) manifest = ast.literal_eval(open(os.path.join(country_path, "__openerp__.py")).read())
data_files = ( data_files = manifest.get("data", []) + manifest.get("init_xml", []) + manifest.get("update_xml", [])
manifest.get("data", []) + manifest.get("init_xml", []) + manifest.get("update_xml", [])
)
files_path = [os.path.join(country_path, d) for d in data_files] files_path = [os.path.join(country_path, d) for d in data_files]
xml_roots = get_xml_roots(files_path) xml_roots = get_xml_roots(files_path)
csv_content = get_csv_contents(files_path) csv_content = get_csv_contents(files_path)
@@ -90,10 +88,10 @@ def get_csv_contents(files_path):
fname = os.path.basename(filepath) fname = os.path.basename(filepath)
for file_type in ["account.account.template", "account.account.type", "account.chart.template"]: for file_type in ["account.account.template", "account.account.type", "account.chart.template"]:
if fname.startswith(file_type) and fname.endswith(".csv"): if fname.startswith(file_type) and fname.endswith(".csv"):
with open(filepath, "r") as csvfile: with open(filepath) as csvfile:
try: try:
csv_content.setdefault(file_type, []).append(read_csv_content(csvfile.read())) csv_content.setdefault(file_type, []).append(read_csv_content(csvfile.read()))
except Exception as e: except Exception:
continue continue
return csv_content return csv_content
@@ -138,7 +136,7 @@ def get_account_types(root_list, csv_content, prefix=None):
if csv_content and csv_content[0][0] == "id": if csv_content and csv_content[0][0] == "id":
for row in csv_content[1:]: for row in csv_content[1:]:
row_dict = dict(zip(csv_content[0], row)) row_dict = dict(zip(csv_content[0], row, strict=False))
data = {} data = {}
if row_dict.get("code") and account_type_map.get(row_dict["code"]): if row_dict.get("code") and account_type_map.get(row_dict["code"]):
data["account_type"] = account_type_map[row_dict["code"]] data["account_type"] = account_type_map[row_dict["code"]]
@@ -150,7 +148,7 @@ def get_account_types(root_list, csv_content, prefix=None):
def make_maps_for_xml(xml_roots, account_types, country_dir): def make_maps_for_xml(xml_roots, account_types, country_dir):
"""make maps for `charts` and `accounts`""" """make maps for `charts` and `accounts`"""
for model, root_list in xml_roots.items(): for _model, root_list in xml_roots.items():
for root in root_list: for root in root_list:
for node in root[0].findall("record"): for node in root[0].findall("record"):
if node.get("model") == "account.account.template": if node.get("model") == "account.account.template":
@@ -186,7 +184,7 @@ def make_maps_for_xml(xml_roots, account_types, country_dir):
def make_maps_for_csv(csv_content, account_types, country_dir): def make_maps_for_csv(csv_content, account_types, country_dir):
for content in csv_content.get("account.account.template", []): for content in csv_content.get("account.account.template", []):
for row in content[1:]: for row in content[1:]:
data = dict(zip(content[0], row)) data = dict(zip(content[0], row, strict=False))
account = { account = {
"name": data.get("name"), "name": data.get("name"),
"parent_id": data.get("parent_id:id") or data.get("parent_id/id"), "parent_id": data.get("parent_id:id") or data.get("parent_id/id"),
@@ -206,7 +204,7 @@ def make_maps_for_csv(csv_content, account_types, country_dir):
for content in csv_content.get("account.chart.template", []): for content in csv_content.get("account.chart.template", []):
for row in content[1:]: for row in content[1:]:
if row: if row:
data = dict(zip(content[0], row)) data = dict(zip(content[0], row, strict=False))
charts.setdefault(data.get("id"), {}).update( charts.setdefault(data.get("id"), {}).update(
{ {
"account_root_id": data.get("account_root_id:id") or data.get("account_root_id/id"), "account_root_id": data.get("account_root_id:id") or data.get("account_root_id/id"),
@@ -241,7 +239,7 @@ def make_charts():
if not src.get("name") or not src.get("account_root_id"): if not src.get("name") or not src.get("account_root_id"):
continue continue
if not src["account_root_id"] in accounts: if src["account_root_id"] not in accounts:
continue continue
filename = src["id"][5:] + "_" + chart_id filename = src["id"][5:] + "_" + chart_id
@@ -255,14 +253,20 @@ def make_charts():
for key, val in chart["tree"].items(): for key, val in chart["tree"].items():
if key in ["name", "parent_id"]: if key in ["name", "parent_id"]:
chart["tree"].pop(key) chart["tree"].pop(key)
if type(val) == dict: if isinstance(val, dict):
val["root_type"] = "" val["root_type"] = ""
if chart: if chart:
fpath = os.path.join( fpath = os.path.join(
"erpnext", "erpnext", "accounts", "doctype", "account", "chart_of_accounts", filename + ".json" "erpnext",
"erpnext",
"accounts",
"doctype",
"account",
"chart_of_accounts",
filename + ".json",
) )
with open(fpath, "r") as chartfile: with open(fpath) as chartfile:
old_content = chartfile.read() old_content = chartfile.read()
if not old_content or ( if not old_content or (
json.loads(old_content).get("is_active", "No") == "No" json.loads(old_content).get("is_active", "No") == "No"

View File

@@ -260,28 +260,20 @@ class TestAccount(unittest.TestCase):
acc.insert() acc.insert()
self.assertTrue( self.assertTrue(
frappe.db.exists( frappe.db.exists("Account", {"account_name": "Test Group Account", "company": "_Test Company 4"})
"Account", {"account_name": "Test Group Account", "company": "_Test Company 4"}
)
) )
self.assertTrue( self.assertTrue(
frappe.db.exists( frappe.db.exists("Account", {"account_name": "Test Group Account", "company": "_Test Company 5"})
"Account", {"account_name": "Test Group Account", "company": "_Test Company 5"}
)
) )
# Try renaming child company account # Try renaming child company account
acc_tc_5 = frappe.db.get_value( acc_tc_5 = frappe.db.get_value(
"Account", {"account_name": "Test Group Account", "company": "_Test Company 5"} "Account", {"account_name": "Test Group Account", "company": "_Test Company 5"}
) )
self.assertRaises( self.assertRaises(frappe.ValidationError, update_account_number, acc_tc_5, "Test Modified Account")
frappe.ValidationError, update_account_number, acc_tc_5, "Test Modified Account"
)
# Rename child company account with allow_account_creation_against_child_company enabled # Rename child company account with allow_account_creation_against_child_company enabled
frappe.db.set_value( frappe.db.set_value("Company", "_Test Company 5", "allow_account_creation_against_child_company", 1)
"Company", "_Test Company 5", "allow_account_creation_against_child_company", 1
)
update_account_number(acc_tc_5, "Test Modified Account") update_account_number(acc_tc_5, "Test Modified Account")
self.assertTrue( self.assertTrue(
@@ -290,9 +282,7 @@ class TestAccount(unittest.TestCase):
) )
) )
frappe.db.set_value( frappe.db.set_value("Company", "_Test Company 5", "allow_account_creation_against_child_company", 0)
"Company", "_Test Company 5", "allow_account_creation_against_child_company", 0
)
to_delete = [ to_delete = [
"Test Group Account - _TC3", "Test Group Account - _TC3",
@@ -317,9 +307,7 @@ class TestAccount(unittest.TestCase):
self.assertEqual(acc.account_currency, "INR") self.assertEqual(acc.account_currency, "INR")
# Make a JV against this account # Make a JV against this account
make_journal_entry( make_journal_entry("Test Currency Account - _TC", "Miscellaneous Expenses - _TC", 100, submit=True)
"Test Currency Account - _TC", "Miscellaneous Expenses - _TC", 100, submit=True
)
acc.account_currency = "USD" acc.account_currency = "USD"
self.assertRaises(frappe.ValidationError, acc.save) self.assertRaises(frappe.ValidationError, acc.save)

View File

@@ -17,16 +17,12 @@ class AccountClosingBalance(Document):
def make_closing_entries(closing_entries, voucher_name, company, closing_date): def make_closing_entries(closing_entries, voucher_name, company, closing_date):
accounting_dimensions = get_accounting_dimensions() accounting_dimensions = get_accounting_dimensions()
previous_closing_entries = get_previous_closing_entries( previous_closing_entries = get_previous_closing_entries(company, closing_date, accounting_dimensions)
company, closing_date, accounting_dimensions
)
combined_entries = closing_entries + previous_closing_entries combined_entries = closing_entries + previous_closing_entries
merged_entries = aggregate_with_last_account_closing_balance( merged_entries = aggregate_with_last_account_closing_balance(combined_entries, accounting_dimensions)
combined_entries, accounting_dimensions
)
for key, value in merged_entries.items(): for _key, value in merged_entries.items():
cle = frappe.new_doc("Account Closing Balance") cle = frappe.new_doc("Account Closing Balance")
cle.update(value) cle.update(value)
cle.update(value["dimensions"]) cle.update(value["dimensions"])

View File

@@ -17,7 +17,8 @@ class AccountingDimension(Document):
self.set_fieldname_and_label() self.set_fieldname_and_label()
def validate(self): def validate(self):
if self.document_type in core_doctypes_list + ( if self.document_type in (
*core_doctypes_list,
"Accounting Dimension", "Accounting Dimension",
"Project", "Project",
"Cost Center", "Cost Center",
@@ -25,13 +26,10 @@ class AccountingDimension(Document):
"Company", "Company",
"Account", "Account",
): ):
msg = _("Not allowed to create accounting dimension for {0}").format(self.document_type) msg = _("Not allowed to create accounting dimension for {0}").format(self.document_type)
frappe.throw(msg) frappe.throw(msg)
exists = frappe.db.get_value( exists = frappe.db.get_value("Accounting Dimension", {"document_type": self.document_type}, ["name"])
"Accounting Dimension", {"document_type": self.document_type}, ["name"]
)
if exists and self.is_new(): if exists and self.is_new():
frappe.throw(_("Document Type already used as a dimension")) frappe.throw(_("Document Type already used as a dimension"))
@@ -89,7 +87,6 @@ def make_dimension_in_accounting_doctypes(doc, doclist=None):
count = 0 count = 0
for doctype in doclist: for doctype in doclist:
if (doc_count + 1) % 2 == 0: if (doc_count + 1) % 2 == 0:
insert_after_field = "dimension_col_break" insert_after_field = "dimension_col_break"
else: else:
@@ -123,7 +120,7 @@ def add_dimension_to_budget_doctype(df, doc):
df.update( df.update(
{ {
"insert_after": "cost_center", "insert_after": "cost_center",
"depends_on": "eval:doc.budget_against == '{0}'".format(doc.document_type), "depends_on": f"eval:doc.budget_against == '{doc.document_type}'",
} }
) )
@@ -157,19 +154,17 @@ def delete_accounting_dimension(doc):
frappe.db.sql( frappe.db.sql(
""" """
DELETE FROM `tabCustom Field` DELETE FROM `tabCustom Field`
WHERE fieldname = %s WHERE fieldname = {}
AND dt IN (%s)""" AND dt IN ({})""".format("%s", ", ".join(["%s"] * len(doclist))), # nosec
% ("%s", ", ".join(["%s"] * len(doclist))), # nosec tuple([doc.fieldname, *doclist]),
tuple([doc.fieldname] + doclist),
) )
frappe.db.sql( frappe.db.sql(
""" """
DELETE FROM `tabProperty Setter` DELETE FROM `tabProperty Setter`
WHERE field_name = %s WHERE field_name = {}
AND doc_type IN (%s)""" AND doc_type IN ({})""".format("%s", ", ".join(["%s"] * len(doclist))), # nosec
% ("%s", ", ".join(["%s"] * len(doclist))), # nosec tuple([doc.fieldname, *doclist]),
tuple([doc.fieldname] + doclist),
) )
budget_against_property = frappe.get_doc("Property Setter", "Budget-budget_against-options") budget_against_property = frappe.get_doc("Property Setter", "Budget-budget_against-options")
@@ -218,7 +213,6 @@ def get_doctypes_with_dimensions():
def get_accounting_dimensions(as_list=True, filters=None): def get_accounting_dimensions(as_list=True, filters=None):
if not filters: if not filters:
filters = {"disabled": 0} filters = {"disabled": 0}
@@ -249,7 +243,6 @@ def get_checks_for_pl_and_bs_accounts():
def get_dimension_with_children(doctype, dimensions): def get_dimension_with_children(doctype, dimensions):
if isinstance(dimensions, str): if isinstance(dimensions, str):
dimensions = [dimensions] dimensions = [dimensions]
@@ -257,9 +250,7 @@ def get_dimension_with_children(doctype, dimensions):
for dimension in dimensions: for dimension in dimensions:
lft, rgt = frappe.db.get_value(doctype, dimension, ["lft", "rgt"]) lft, rgt = frappe.db.get_value(doctype, dimension, ["lft", "rgt"])
children = frappe.get_all( children = frappe.get_all(doctype, filters={"lft": [">=", lft], "rgt": ["<=", rgt]}, order_by="lft")
doctype, filters={"lft": [">=", lft], "rgt": ["<=", rgt]}, order_by="lft"
)
all_dimensions += [c.name for c in children] all_dimensions += [c.name for c in children]
return all_dimensions return all_dimensions

View File

@@ -57,9 +57,7 @@ class TestAccountingDimensionFilter(unittest.TestCase):
def create_accounting_dimension_filter(): def create_accounting_dimension_filter():
if not frappe.db.get_value( if not frappe.db.get_value("Accounting Dimension Filter", {"accounting_dimension": "Cost Center"}):
"Accounting Dimension Filter", {"accounting_dimension": "Cost Center"}
):
frappe.get_doc( frappe.get_doc(
{ {
"doctype": "Accounting Dimension Filter", "doctype": "Accounting Dimension Filter",

View File

@@ -67,7 +67,10 @@ class AccountingPeriod(Document):
for doctype_for_closing in self.get_doctypes_for_closing(): for doctype_for_closing in self.get_doctypes_for_closing():
self.append( self.append(
"closed_documents", "closed_documents",
{"document_type": doctype_for_closing.document_type, "closed": doctype_for_closing.closed}, {
"document_type": doctype_for_closing.document_type,
"closed": doctype_for_closing.closed,
},
) )

View File

@@ -34,9 +34,7 @@ class TestAccountingPeriod(unittest.TestCase):
ap1 = create_accounting_period(period_name="Test Accounting Period 2") ap1 = create_accounting_period(period_name="Test Accounting Period 2")
ap1.save() ap1.save()
doc = create_sales_invoice( doc = create_sales_invoice(do_not_save=1, cost_center="_Test Company - _TC", warehouse="Stores - _TC")
do_not_save=1, cost_center="_Test Company - _TC", warehouse="Stores - _TC"
)
self.assertRaises(ClosedAccountingPeriod, doc.save) self.assertRaises(ClosedAccountingPeriod, doc.save)
def tearDown(self): def tearDown(self):

View File

@@ -37,11 +37,11 @@ class TestBankAccount(unittest.TestCase):
try: try:
bank_account.validate_iban() bank_account.validate_iban()
except ValidationError: except ValidationError:
msg = "BankAccount.validate_iban() failed for valid IBAN {}".format(iban) msg = f"BankAccount.validate_iban() failed for valid IBAN {iban}"
self.fail(msg=msg) self.fail(msg=msg)
for not_iban in invalid_ibans: for not_iban in invalid_ibans:
bank_account.iban = not_iban bank_account.iban = not_iban
msg = "BankAccount.validate_iban() accepted invalid IBAN {}".format(not_iban) msg = f"BankAccount.validate_iban() accepted invalid IBAN {not_iban}"
with self.assertRaises(ValidationError, msg=msg): with self.assertRaises(ValidationError, msg=msg):
bank_account.validate_iban() bank_account.validate_iban()

View File

@@ -27,7 +27,7 @@ class BankClearance(Document):
condition = "and (clearance_date IS NULL or clearance_date='0000-00-00')" condition = "and (clearance_date IS NULL or clearance_date='0000-00-00')"
journal_entries = frappe.db.sql( journal_entries = frappe.db.sql(
""" f"""
select select
"Journal Entry" as payment_document, t1.name as payment_entry, "Journal Entry" as payment_document, t1.name as payment_entry,
t1.cheque_no as cheque_number, t1.cheque_date, t1.cheque_no as cheque_number, t1.cheque_date,
@@ -41,9 +41,7 @@ class BankClearance(Document):
and ifnull(t1.is_opening, 'No') = 'No' {condition} and ifnull(t1.is_opening, 'No') = 'No' {condition}
group by t2.account, t1.name group by t2.account, t1.name
order by t1.posting_date ASC, t1.name DESC order by t1.posting_date ASC, t1.name DESC
""".format( """,
condition=condition
),
{"account": self.account, "from": self.from_date, "to": self.to_date}, {"account": self.account, "from": self.from_date, "to": self.to_date},
as_dict=1, as_dict=1,
) )
@@ -52,7 +50,7 @@ class BankClearance(Document):
condition += "and bank_account = %(bank_account)s" condition += "and bank_account = %(bank_account)s"
payment_entries = frappe.db.sql( payment_entries = frappe.db.sql(
""" f"""
select select
"Payment Entry" as payment_document, name as payment_entry, "Payment Entry" as payment_document, name as payment_entry,
reference_no as cheque_number, reference_date as cheque_date, reference_no as cheque_number, reference_date as cheque_date,
@@ -67,9 +65,7 @@ class BankClearance(Document):
{condition} {condition}
order by order by
posting_date ASC, name DESC posting_date ASC, name DESC
""".format( """,
condition=condition
),
{ {
"account": self.account, "account": self.account,
"from": self.from_date, "from": self.from_date,
@@ -132,11 +128,9 @@ class BankClearance(Document):
query = query.where(loan_repayment.clearance_date.isnull()) query = query.where(loan_repayment.clearance_date.isnull())
if frappe.db.has_column("Loan Repayment", "repay_from_salary"): if frappe.db.has_column("Loan Repayment", "repay_from_salary"):
query = query.where((loan_repayment.repay_from_salary == 0)) query = query.where(loan_repayment.repay_from_salary == 0)
query = query.orderby(loan_repayment.posting_date).orderby( query = query.orderby(loan_repayment.posting_date).orderby(loan_repayment.name, order=frappe.qb.desc)
loan_repayment.name, order=frappe.qb.desc
)
loan_repayments = query.run(as_dict=True) loan_repayments = query.run(as_dict=True)

View File

@@ -61,9 +61,7 @@ def get_bank_transactions(bank_account, from_date=None, to_date=None):
def get_account_balance(bank_account, till_date): def get_account_balance(bank_account, till_date):
# 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({"account": account, "report_date": till_date, "include_pos_transactions": 1})
{"account": account, "report_date": till_date, "include_pos_transactions": 1}
)
data = get_entries(filters) data = get_entries(filters)
balance_as_per_system = get_balance_on(filters["account"], filters["report_date"]) balance_as_per_system = get_balance_on(filters["account"], filters["report_date"])
@@ -76,10 +74,7 @@ def get_account_balance(bank_account, till_date):
amounts_not_reflected_in_system = get_amounts_not_reflected_in_system(filters) amounts_not_reflected_in_system = get_amounts_not_reflected_in_system(filters)
bank_bal = ( bank_bal = (
flt(balance_as_per_system) flt(balance_as_per_system) - flt(total_debit) + flt(total_credit) + amounts_not_reflected_in_system
- flt(total_debit)
+ flt(total_credit)
+ amounts_not_reflected_in_system
) )
return bank_bal return bank_bal
@@ -377,12 +372,13 @@ def auto_reconcile_vouchers(
) )
transaction = frappe.get_doc("Bank Transaction", transaction.name) transaction = frappe.get_doc("Bank Transaction", transaction.name)
account = frappe.db.get_value("Bank Account", transaction.bank_account, "account") account = frappe.db.get_value("Bank Account", transaction.bank_account, "account")
matched_trans = 0
for voucher in vouchers: for voucher in vouchers:
gl_entry = frappe.db.get_value( gl_entry = frappe.db.get_value(
"GL Entry", "GL Entry",
dict( dict(
account=account, voucher_type=voucher["payment_doctype"], voucher_no=voucher["payment_name"] account=account,
voucher_type=voucher["payment_doctype"],
voucher_no=voucher["payment_name"],
), ),
["credit", "debit"], ["credit", "debit"],
as_dict=1, as_dict=1,
@@ -731,7 +727,7 @@ def get_lr_matching_query(bank_account, exact_match, filters):
) )
if frappe.db.has_column("Loan Repayment", "repay_from_salary"): if frappe.db.has_column("Loan Repayment", "repay_from_salary"):
query = query.where((loan_repayment.repay_from_salary == 0)) query = query.where(loan_repayment.repay_from_salary == 0)
if exact_match: if exact_match:
query.where(loan_repayment.amount_paid == filters.get("amount")) query.where(loan_repayment.amount_paid == filters.get("amount"))
@@ -764,7 +760,7 @@ def get_pe_matching_query(
if cint(filter_by_reference_date): if cint(filter_by_reference_date):
filter_by_date = f"AND reference_date between '{from_reference_date}' and '{to_reference_date}'" filter_by_date = f"AND reference_date between '{from_reference_date}' and '{to_reference_date}'"
order_by = " reference_date" order_by = " reference_date"
if frappe.flags.auto_reconcile_vouchers == True: if frappe.flags.auto_reconcile_vouchers is True:
filter_by_reference_no = f"AND reference_no = '{transaction.reference_number}'" filter_by_reference_no = f"AND reference_no = '{transaction.reference_number}'"
return f""" return f"""
SELECT SELECT
@@ -815,7 +811,7 @@ def get_je_matching_query(
if cint(filter_by_reference_date): if cint(filter_by_reference_date):
filter_by_date = f"AND je.cheque_date between '{from_reference_date}' and '{to_reference_date}'" filter_by_date = f"AND je.cheque_date between '{from_reference_date}' and '{to_reference_date}'"
order_by = " je.cheque_date" order_by = " je.cheque_date"
if frappe.flags.auto_reconcile_vouchers == True: if frappe.flags.auto_reconcile_vouchers is True:
filter_by_reference_no = f"AND je.cheque_no = '{transaction.reference_number}'" filter_by_reference_no = f"AND je.cheque_no = '{transaction.reference_number}'"
return f""" return f"""
SELECT SELECT

View File

@@ -21,7 +21,7 @@ INVALID_VALUES = ("", None)
class BankStatementImport(DataImport): class BankStatementImport(DataImport):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(BankStatementImport, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def validate(self): def validate(self):
doc_before_save = self.get_doc_before_save() doc_before_save = self.get_doc_before_save()
@@ -30,7 +30,6 @@ class BankStatementImport(DataImport):
or (doc_before_save and doc_before_save.import_file != self.import_file) or (doc_before_save and doc_before_save.import_file != self.import_file)
or (doc_before_save and doc_before_save.google_sheets_url != self.google_sheets_url) or (doc_before_save and doc_before_save.google_sheets_url != self.google_sheets_url)
): ):
template_options_dict = {} template_options_dict = {}
column_to_field_map = {} column_to_field_map = {}
bank = frappe.get_doc("Bank", self.bank) bank = frappe.get_doc("Bank", self.bank)
@@ -45,7 +44,6 @@ class BankStatementImport(DataImport):
self.validate_google_sheets_url() self.validate_google_sheets_url()
def start_import(self): def start_import(self):
preview = frappe.get_doc("Bank Statement Import", self.name).get_preview_from_template( preview = frappe.get_doc("Bank Statement Import", self.name).get_preview_from_template(
self.import_file, self.google_sheets_url self.import_file, self.google_sheets_url
) )
@@ -102,7 +100,7 @@ def download_errored_template(data_import_name):
def parse_data_from_template(raw_data): def parse_data_from_template(raw_data):
data = [] data = []
for i, row in enumerate(raw_data): for _i, row in enumerate(raw_data):
if all(v in INVALID_VALUES for v in row): if all(v in INVALID_VALUES for v in row):
# empty row # empty row
continue continue
@@ -112,9 +110,7 @@ def parse_data_from_template(raw_data):
return data return data
def start_import( def start_import(data_import, bank_account, import_file_path, google_sheets_url, bank, template_options):
data_import, bank_account, import_file_path, google_sheets_url, bank, template_options
):
"""This method runs in background job""" """This method runs in background job"""
update_mapping_db(bank, template_options) update_mapping_db(bank, template_options)

View File

@@ -1,5 +1,3 @@
from typing import Tuple, Union
import frappe import frappe
from frappe.utils import flt from frappe.utils import flt
from rapidfuzz import fuzz, process from rapidfuzz import fuzz, process
@@ -19,7 +17,7 @@ class AutoMatchParty:
def get(self, key): def get(self, key):
return self.__dict__.get(key, None) return self.__dict__.get(key, None)
def match(self) -> Union[Tuple, None]: def match(self) -> tuple | None:
result = None result = None
result = AutoMatchbyAccountIBAN( result = AutoMatchbyAccountIBAN(
bank_party_account_number=self.bank_party_account_number, bank_party_account_number=self.bank_party_account_number,
@@ -50,7 +48,7 @@ class AutoMatchbyAccountIBAN:
result = self.match_account_in_party() result = self.match_account_in_party()
return result return result
def match_account_in_party(self) -> Union[Tuple, None]: def match_account_in_party(self) -> tuple | None:
"""Check if there is a IBAN/Account No. match in Customer/Supplier/Employee""" """Check if there is a IBAN/Account No. match in Customer/Supplier/Employee"""
result = None result = None
parties = get_parties_in_order(self.deposit) parties = get_parties_in_order(self.deposit)
@@ -97,7 +95,7 @@ class AutoMatchbyPartyNameDescription:
def get(self, key): def get(self, key):
return self.__dict__.get(key, None) return self.__dict__.get(key, None)
def match(self) -> Union[Tuple, None]: def match(self) -> tuple | None:
# fuzzy search by customer/supplier & employee # fuzzy search by customer/supplier & employee
if not (self.bank_party_name or self.description): if not (self.bank_party_name or self.description):
return None return None
@@ -105,7 +103,7 @@ class AutoMatchbyPartyNameDescription:
result = self.match_party_name_desc_in_party() result = self.match_party_name_desc_in_party()
return result return result
def match_party_name_desc_in_party(self) -> Union[Tuple, None]: def match_party_name_desc_in_party(self) -> tuple | None:
"""Fuzzy search party name and/or description against parties in the system""" """Fuzzy search party name and/or description against parties in the system"""
result = None result = None
parties = get_parties_in_order(self.deposit) parties = get_parties_in_order(self.deposit)
@@ -130,7 +128,7 @@ class AutoMatchbyPartyNameDescription:
return result return result
def fuzzy_search_and_return_result(self, party, names, field) -> Union[Tuple, None]: def fuzzy_search_and_return_result(self, party, names, field) -> tuple | None:
skip = False skip = False
result = process.extract( result = process.extract(
query=self.get(field), query=self.get(field),
@@ -147,7 +145,7 @@ class AutoMatchbyPartyNameDescription:
party_name, party_name,
), skip ), skip
def process_fuzzy_result(self, result: Union[list, None]): def process_fuzzy_result(self, result: list | None):
""" """
If there are multiple valid close matches return None as result may be faulty. If there are multiple valid close matches return None as result may be faulty.
Return the result only if one accurate match stands out. Return the result only if one accurate match stands out.

View File

@@ -186,9 +186,7 @@ def get_clearance_details(transaction, payment_entry):
""" """
gl_bank_account = frappe.db.get_value("Bank Account", transaction.bank_account, "account") gl_bank_account = frappe.db.get_value("Bank Account", transaction.bank_account, "account")
gles = get_related_bank_gl_entries(payment_entry.payment_document, payment_entry.payment_entry) gles = get_related_bank_gl_entries(payment_entry.payment_document, payment_entry.payment_entry)
bt_allocations = get_total_allocated_amount( bt_allocations = get_total_allocated_amount(payment_entry.payment_document, payment_entry.payment_entry)
payment_entry.payment_document, payment_entry.payment_entry
)
unallocated_amount = min( unallocated_amount = min(
transaction.unallocated_amount, transaction.unallocated_amount,
@@ -286,7 +284,6 @@ def get_total_allocated_amount(doctype, docname):
def get_paid_amount(payment_entry, currency, gl_bank_account): def get_paid_amount(payment_entry, currency, gl_bank_account):
if payment_entry.payment_document in ["Payment Entry", "Sales Invoice", "Purchase Invoice"]: if payment_entry.payment_document in ["Payment Entry", "Sales Invoice", "Purchase Invoice"]:
paid_amount_field = "paid_amount" paid_amount_field = "paid_amount"
if payment_entry.payment_document == "Payment Entry": if payment_entry.payment_document == "Payment Entry":
doc = frappe.get_doc("Payment Entry", payment_entry.payment_entry) doc = frappe.get_doc("Payment Entry", payment_entry.payment_entry)
@@ -325,9 +322,7 @@ def get_paid_amount(payment_entry, currency, gl_bank_account):
) )
elif payment_entry.payment_document == "Loan Repayment": elif payment_entry.payment_document == "Loan Repayment":
return frappe.db.get_value( return frappe.db.get_value(payment_entry.payment_document, payment_entry.payment_entry, "amount_paid")
payment_entry.payment_document, payment_entry.payment_entry, "amount_paid"
)
elif payment_entry.payment_document == "Bank Transaction": elif payment_entry.payment_document == "Bank Transaction":
dep, wth = frappe.db.get_value( dep, wth = frappe.db.get_value(
@@ -337,9 +332,7 @@ def get_paid_amount(payment_entry, currency, gl_bank_account):
else: else:
frappe.throw( frappe.throw(
"Please reconcile {0}: {1} manually".format( f"Please reconcile {payment_entry.payment_document}: {payment_entry.payment_entry} manually"
payment_entry.payment_document, payment_entry.payment_entry
)
) )

View File

@@ -18,12 +18,12 @@ def upload_bank_statement():
fcontent = frappe.local.uploaded_file fcontent = frappe.local.uploaded_file
fname = frappe.local.uploaded_filename fname = frappe.local.uploaded_filename
if frappe.safe_encode(fname).lower().endswith("csv".encode("utf-8")): if frappe.safe_encode(fname).lower().endswith(b"csv"):
from frappe.utils.csvutils import read_csv_content from frappe.utils.csvutils import read_csv_content
rows = read_csv_content(fcontent, False) rows = read_csv_content(fcontent, False)
elif frappe.safe_encode(fname).lower().endswith("xlsx".encode("utf-8")): elif frappe.safe_encode(fname).lower().endswith(b"xlsx"):
from frappe.utils.xlsxutils import read_xlsx_file_from_attached_file from frappe.utils.xlsxutils import read_xlsx_file_from_attached_file
rows = read_xlsx_file_from_attached_file(fcontent=fcontent) rows = read_xlsx_file_from_attached_file(fcontent=fcontent)

View File

@@ -430,9 +430,7 @@ def add_vouchers(gl_account="_Test Bank - _TC"):
mode_of_payment = frappe.get_doc({"doctype": "Mode of Payment", "name": "Cash"}) mode_of_payment = frappe.get_doc({"doctype": "Mode of Payment", "name": "Cash"})
if not frappe.db.get_value( if not frappe.db.get_value("Mode of Payment Account", {"company": "_Test Company", "parent": "Cash"}):
"Mode of Payment Account", {"company": "_Test Company", "parent": "Cash"}
):
mode_of_payment.append("accounts", {"company": "_Test Company", "default_account": gl_account}) mode_of_payment.append("accounts", {"company": "_Test Company", "default_account": gl_account})
mode_of_payment.save() mode_of_payment.save()

View File

@@ -14,26 +14,6 @@ from frappe.utils.data import guess_date_format
class BisectAccountingStatements(Document): class BisectAccountingStatements(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
algorithm: DF.Literal["BFS", "DFS"]
b_s_summary: DF.Float
company: DF.Link | None
current_from_date: DF.Datetime | None
current_node: DF.Link | None
current_to_date: DF.Datetime | None
difference: DF.Float
from_date: DF.Datetime | None
p_l_summary: DF.Float
to_date: DF.Datetime | None
# end: auto-generated types
def validate(self): def validate(self):
self.validate_dates() self.validate_dates()

View File

@@ -40,10 +40,11 @@ class Budget(Document):
select select
b.name, ba.account from `tabBudget` b, `tabBudget Account` ba b.name, ba.account from `tabBudget` b, `tabBudget Account` ba
where where
ba.parent = b.name and b.docstatus < 2 and b.company = %s and %s=%s and ba.parent = b.name and b.docstatus < 2 and b.company = {} and {}={} and
b.fiscal_year=%s and b.name != %s and ba.account in (%s) """ b.fiscal_year={} and b.name != {} and ba.account in ({}) """.format(
% ("%s", budget_against_field, "%s", "%s", "%s", ",".join(["%s"] * len(accounts))), "%s", budget_against_field, "%s", "%s", "%s", ",".join(["%s"] * len(accounts))
(self.company, budget_against, self.fiscal_year, self.name) + tuple(accounts), ),
(self.company, budget_against, self.fiscal_year, self.name, *tuple(accounts)),
as_dict=1, as_dict=1,
) )
@@ -66,12 +67,14 @@ class Budget(Document):
if account_details.is_group: if account_details.is_group:
frappe.throw(_("Budget cannot be assigned against Group Account {0}").format(d.account)) frappe.throw(_("Budget cannot be assigned against Group Account {0}").format(d.account))
elif account_details.company != self.company: elif account_details.company != self.company:
frappe.throw(_("Account {0} does not belongs to company {1}").format(d.account, self.company)) frappe.throw(
_("Account {0} does not belongs to company {1}").format(d.account, self.company)
)
elif account_details.report_type != "Profit and Loss": elif account_details.report_type != "Profit and Loss":
frappe.throw( frappe.throw(
_("Budget cannot be assigned against {0}, as it's not an Income or Expense account").format( _(
d.account "Budget cannot be assigned against {0}, as it's not an Income or Expense account"
) ).format(d.account)
) )
if d.account in account_list: if d.account in account_list:
@@ -118,9 +121,7 @@ def validate_expense_against_budget(args, expense_amount=0):
"Company", args.get("company"), "exception_budget_approver_role" "Company", args.get("company"), "exception_budget_approver_role"
) )
if not frappe.get_cached_value( if not frappe.get_cached_value("Budget", {"fiscal_year": args.fiscal_year, "company": args.company}): # nosec
"Budget", {"fiscal_year": args.fiscal_year, "company": args.company}
): # nosec
return return
if not args.account: if not args.account:
@@ -151,30 +152,24 @@ def validate_expense_against_budget(args, expense_amount=0):
and args.account and args.account
and (frappe.get_cached_value("Account", args.account, "root_type") == "Expense") and (frappe.get_cached_value("Account", args.account, "root_type") == "Expense")
): ):
doctype = dimension.get("document_type") doctype = dimension.get("document_type")
if frappe.get_cached_value("DocType", doctype, "is_tree"): if frappe.get_cached_value("DocType", doctype, "is_tree"):
lft, rgt = frappe.get_cached_value(doctype, args.get(budget_against), ["lft", "rgt"]) lft, rgt = frappe.get_cached_value(doctype, args.get(budget_against), ["lft", "rgt"])
condition = """and exists(select name from `tab%s` condition = f"""and exists(select name from `tab{doctype}`
where lft<=%s and rgt>=%s and name=b.%s)""" % ( where lft<={lft} and rgt>={rgt} and name=b.{budget_against})""" # nosec
doctype,
lft,
rgt,
budget_against,
) # nosec
args.is_tree = True args.is_tree = True
else: else:
condition = "and b.%s=%s" % (budget_against, frappe.db.escape(args.get(budget_against))) condition = f"and b.{budget_against}={frappe.db.escape(args.get(budget_against))}"
args.is_tree = False args.is_tree = False
args.budget_against_field = budget_against args.budget_against_field = budget_against
args.budget_against_doctype = doctype args.budget_against_doctype = doctype
budget_records = frappe.db.sql( budget_records = frappe.db.sql(
""" f"""
select select
b.{budget_against_field} as budget_against, ba.budget_amount, b.monthly_distribution, b.{budget_against} as budget_against, ba.budget_amount, b.monthly_distribution,
ifnull(b.applicable_on_material_request, 0) as for_material_request, ifnull(b.applicable_on_material_request, 0) as for_material_request,
ifnull(applicable_on_purchase_order, 0) as for_purchase_order, ifnull(applicable_on_purchase_order, 0) as for_purchase_order,
ifnull(applicable_on_booking_actual_expenses,0) as for_actual_expenses, ifnull(applicable_on_booking_actual_expenses,0) as for_actual_expenses,
@@ -187,9 +182,7 @@ def validate_expense_against_budget(args, expense_amount=0):
b.name=ba.parent and b.fiscal_year=%s b.name=ba.parent and b.fiscal_year=%s
and ba.account=%s and b.docstatus=1 and ba.account=%s and b.docstatus=1
{condition} {condition}
""".format( """,
condition=condition, budget_against_field=budget_against
),
(args.fiscal_year, args.account), (args.fiscal_year, args.account),
as_dict=True, as_dict=True,
) # nosec ) # nosec
@@ -217,7 +210,12 @@ def validate_budget_records(args, budget_records, expense_amount):
args["month_end_date"] = get_last_day(args.posting_date) args["month_end_date"] = get_last_day(args.posting_date)
compare_expense_with_budget( compare_expense_with_budget(
args, budget_amount, _("Accumulated Monthly"), monthly_action, budget.budget_against, amount args,
budget_amount,
_("Accumulated Monthly"),
monthly_action,
budget.budget_against,
amount,
) )
@@ -245,9 +243,8 @@ def compare_expense_with_budget(args, budget_amount, action_for, action, budget_
frappe.bold(fmt_money(diff, currency=currency)), frappe.bold(fmt_money(diff, currency=currency)),
) )
if ( if frappe.flags.exception_approver_role and frappe.flags.exception_approver_role in frappe.get_roles(
frappe.flags.exception_approver_role frappe.session.user
and frappe.flags.exception_approver_role in frappe.get_roles(frappe.session.user)
): ):
action = "Warn" action = "Warn"
@@ -293,10 +290,8 @@ def get_requested_amount(args, budget):
data = frappe.db.sql( data = frappe.db.sql(
""" select ifnull((sum(child.stock_qty - child.ordered_qty) * rate), 0) as amount """ select ifnull((sum(child.stock_qty - child.ordered_qty) * rate), 0) as amount
from `tabMaterial Request Item` child, `tabMaterial Request` parent where parent.name = child.parent and from `tabMaterial Request Item` child, `tabMaterial Request` parent where parent.name = child.parent and
child.item_code = %s and parent.docstatus = 1 and child.stock_qty > child.ordered_qty and {0} and child.item_code = %s and parent.docstatus = 1 and child.stock_qty > child.ordered_qty and {} and
parent.material_request_type = 'Purchase' and parent.status != 'Stopped'""".format( parent.material_request_type = 'Purchase' and parent.status != 'Stopped'""".format(condition),
condition
),
item_code, item_code,
as_list=1, as_list=1,
) )
@@ -309,12 +304,10 @@ def get_ordered_amount(args, budget):
condition = get_other_condition(args, budget, "Purchase Order") condition = get_other_condition(args, budget, "Purchase Order")
data = frappe.db.sql( data = frappe.db.sql(
""" select ifnull(sum(child.amount - child.billed_amt), 0) as amount f""" select ifnull(sum(child.amount - child.billed_amt), 0) as amount
from `tabPurchase Order Item` child, `tabPurchase Order` parent where from `tabPurchase Order Item` child, `tabPurchase Order` parent where
parent.name = child.parent and child.item_code = %s and parent.docstatus = 1 and child.amount > child.billed_amt parent.name = child.parent and child.item_code = %s and parent.docstatus = 1 and child.amount > child.billed_amt
and parent.status != 'Closed' and {0}""".format( and parent.status != 'Closed' and {condition}""",
condition
),
item_code, item_code,
as_list=1, as_list=1,
) )
@@ -327,7 +320,7 @@ def get_other_condition(args, budget, for_doc):
budget_against_field = args.get("budget_against_field") budget_against_field = args.get("budget_against_field")
if budget_against_field and args.get(budget_against_field): if budget_against_field and args.get(budget_against_field):
condition += " and child.%s = '%s'" % (budget_against_field, args.get(budget_against_field)) condition += f" and child.{budget_against_field} = '{args.get(budget_against_field)}'"
if args.get("fiscal_year"): if args.get("fiscal_year"):
date_field = "schedule_date" if for_doc == "Material Request" else "transaction_date" date_field = "schedule_date" if for_doc == "Material Request" else "transaction_date"
@@ -335,12 +328,8 @@ def get_other_condition(args, budget, for_doc):
"Fiscal Year", args.get("fiscal_year"), ["year_start_date", "year_end_date"] "Fiscal Year", args.get("fiscal_year"), ["year_start_date", "year_end_date"]
) )
condition += """ and parent.%s condition += f""" and parent.{date_field}
between '%s' and '%s' """ % ( between '{start_date}' and '{end_date}' """
date_field,
start_date,
end_date,
)
return condition return condition
@@ -359,21 +348,17 @@ def get_actual_expense(args):
args.update(lft_rgt) args.update(lft_rgt)
condition2 = """and exists(select name from `tab{doctype}` condition2 = f"""and exists(select name from `tab{args.budget_against_doctype}`
where lft>=%(lft)s and rgt<=%(rgt)s where lft>=%(lft)s and rgt<=%(rgt)s
and name=gle.{budget_against_field})""".format( and name=gle.{budget_against_field})"""
doctype=args.budget_against_doctype, budget_against_field=budget_against_field # nosec
)
else: else:
condition2 = """and exists(select name from `tab{doctype}` condition2 = f"""and exists(select name from `tab{args.budget_against_doctype}`
where name=gle.{budget_against} and where name=gle.{budget_against_field} and
gle.{budget_against} = %({budget_against})s)""".format( gle.{budget_against_field} = %({budget_against_field})s)"""
doctype=args.budget_against_doctype, budget_against=budget_against_field
)
amount = flt( amount = flt(
frappe.db.sql( frappe.db.sql(
""" f"""
select sum(gle.debit) - sum(gle.credit) select sum(gle.debit) - sum(gle.credit)
from `tabGL Entry` gle from `tabGL Entry` gle
where where
@@ -384,9 +369,7 @@ def get_actual_expense(args):
and gle.company=%(company)s and gle.company=%(company)s
and gle.docstatus=1 and gle.docstatus=1
{condition2} {condition2}
""".format( """,
condition1=condition1, condition2=condition2
),
(args), (args),
)[0][0] )[0][0]
) # nosec ) # nosec

View File

@@ -41,9 +41,7 @@ class TestBudget(unittest.TestCase):
budget = make_budget(budget_against="Cost Center") budget = make_budget(budget_against="Cost Center")
frappe.db.set_value( frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
)
jv = make_journal_entry( jv = make_journal_entry(
"_Test Account Cost for Goods Sold - _TC", "_Test Account Cost for Goods Sold - _TC",
@@ -63,9 +61,7 @@ class TestBudget(unittest.TestCase):
budget = make_budget(budget_against="Cost Center") budget = make_budget(budget_against="Cost Center")
frappe.db.set_value( frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
)
jv = make_journal_entry( jv = make_journal_entry(
"_Test Account Cost for Goods Sold - _TC", "_Test Account Cost for Goods Sold - _TC",
@@ -97,9 +93,7 @@ class TestBudget(unittest.TestCase):
) )
fiscal_year = get_fiscal_year(nowdate())[0] fiscal_year = get_fiscal_year(nowdate())[0]
frappe.db.set_value( frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
)
frappe.db.set_value("Budget", budget.name, "fiscal_year", fiscal_year) frappe.db.set_value("Budget", budget.name, "fiscal_year", fiscal_year)
mr = frappe.get_doc( mr = frappe.get_doc(
@@ -138,9 +132,7 @@ class TestBudget(unittest.TestCase):
) )
fiscal_year = get_fiscal_year(nowdate())[0] fiscal_year = get_fiscal_year(nowdate())[0]
frappe.db.set_value( frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
)
frappe.db.set_value("Budget", budget.name, "fiscal_year", fiscal_year) frappe.db.set_value("Budget", budget.name, "fiscal_year", fiscal_year)
po = create_purchase_order(transaction_date=nowdate(), do_not_submit=True) po = create_purchase_order(transaction_date=nowdate(), do_not_submit=True)
@@ -158,9 +150,7 @@ class TestBudget(unittest.TestCase):
budget = make_budget(budget_against="Project") budget = make_budget(budget_against="Project")
frappe.db.set_value( frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
)
project = frappe.get_value("Project", {"project_name": "_Test Project"}) project = frappe.get_value("Project", {"project_name": "_Test Project"})
@@ -223,7 +213,7 @@ class TestBudget(unittest.TestCase):
if month > 9: if month > 9:
month = 9 month = 9
for i in range(month + 1): for _i in range(month + 1):
jv = make_journal_entry( jv = make_journal_entry(
"_Test Account Cost for Goods Sold - _TC", "_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", "_Test Bank - _TC",
@@ -237,9 +227,7 @@ class TestBudget(unittest.TestCase):
frappe.db.get_value("GL Entry", {"voucher_type": "Journal Entry", "voucher_no": jv.name}) frappe.db.get_value("GL Entry", {"voucher_type": "Journal Entry", "voucher_no": jv.name})
) )
frappe.db.set_value( frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
)
self.assertRaises(BudgetError, jv.cancel) self.assertRaises(BudgetError, jv.cancel)
@@ -255,7 +243,7 @@ class TestBudget(unittest.TestCase):
month = 9 month = 9
project = frappe.get_value("Project", {"project_name": "_Test Project"}) project = frappe.get_value("Project", {"project_name": "_Test Project"})
for i in range(month + 1): for _i in range(month + 1):
jv = make_journal_entry( jv = make_journal_entry(
"_Test Account Cost for Goods Sold - _TC", "_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC", "_Test Bank - _TC",
@@ -270,9 +258,7 @@ class TestBudget(unittest.TestCase):
frappe.db.get_value("GL Entry", {"voucher_type": "Journal Entry", "voucher_no": jv.name}) frappe.db.get_value("GL Entry", {"voucher_type": "Journal Entry", "voucher_no": jv.name})
) )
frappe.db.set_value( frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
)
self.assertRaises(BudgetError, jv.cancel) self.assertRaises(BudgetError, jv.cancel)
@@ -284,9 +270,7 @@ class TestBudget(unittest.TestCase):
set_total_expense_zero(nowdate(), "cost_center", "_Test Cost Center 2 - _TC") set_total_expense_zero(nowdate(), "cost_center", "_Test Cost Center 2 - _TC")
budget = make_budget(budget_against="Cost Center", cost_center="_Test Company - _TC") budget = make_budget(budget_against="Cost Center", cost_center="_Test Company - _TC")
frappe.db.set_value( frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
)
jv = make_journal_entry( jv = make_journal_entry(
"_Test Account Cost for Goods Sold - _TC", "_Test Account Cost for Goods Sold - _TC",
@@ -316,9 +300,7 @@ class TestBudget(unittest.TestCase):
).insert(ignore_permissions=True) ).insert(ignore_permissions=True)
budget = make_budget(budget_against="Cost Center", cost_center=cost_center) budget = make_budget(budget_against="Cost Center", cost_center=cost_center)
frappe.db.set_value( frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop")
"Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop"
)
jv = make_journal_entry( jv = make_journal_entry(
"_Test Account Cost for Goods Sold - _TC", "_Test Account Cost for Goods Sold - _TC",
@@ -423,13 +405,11 @@ def make_budget(**args):
fiscal_year = get_fiscal_year(nowdate())[0] fiscal_year = get_fiscal_year(nowdate())[0]
if budget_against == "Project": if budget_against == "Project":
project_name = "{0}%".format("_Test Project/" + fiscal_year) project_name = "{}%".format("_Test Project/" + fiscal_year)
budget_list = frappe.get_all("Budget", fields=["name"], filters={"name": ("like", project_name)}) budget_list = frappe.get_all("Budget", fields=["name"], filters={"name": ("like", project_name)})
else: else:
cost_center_name = "{0}%".format(cost_center or "_Test Cost Center - _TC/" + fiscal_year) cost_center_name = "{}%".format(cost_center or "_Test Cost Center - _TC/" + fiscal_year)
budget_list = frappe.get_all( budget_list = frappe.get_all("Budget", fields=["name"], filters={"name": ("like", cost_center_name)})
"Budget", fields=["name"], filters={"name": ("like", cost_center_name)}
)
for d in budget_list: for d in budget_list:
frappe.db.sql("delete from `tabBudget` where name = %(name)s", d) frappe.db.sql("delete from `tabBudget` where name = %(name)s", d)
frappe.db.sql("delete from `tabBudget Account` where parent = %(name)s", d) frappe.db.sql("delete from `tabBudget Account` where parent = %(name)s", d)
@@ -451,24 +431,18 @@ def make_budget(**args):
budget.action_if_annual_budget_exceeded = "Stop" budget.action_if_annual_budget_exceeded = "Stop"
budget.action_if_accumulated_monthly_budget_exceeded = "Ignore" budget.action_if_accumulated_monthly_budget_exceeded = "Ignore"
budget.budget_against = budget_against budget.budget_against = budget_against
budget.append( budget.append("accounts", {"account": "_Test Account Cost for Goods Sold - _TC", "budget_amount": 200000})
"accounts", {"account": "_Test Account Cost for Goods Sold - _TC", "budget_amount": 200000}
)
if args.applicable_on_material_request: if args.applicable_on_material_request:
budget.applicable_on_material_request = 1 budget.applicable_on_material_request = 1
budget.action_if_annual_budget_exceeded_on_mr = ( budget.action_if_annual_budget_exceeded_on_mr = args.action_if_annual_budget_exceeded_on_mr or "Warn"
args.action_if_annual_budget_exceeded_on_mr or "Warn"
)
budget.action_if_accumulated_monthly_budget_exceeded_on_mr = ( budget.action_if_accumulated_monthly_budget_exceeded_on_mr = (
args.action_if_accumulated_monthly_budget_exceeded_on_mr or "Warn" args.action_if_accumulated_monthly_budget_exceeded_on_mr or "Warn"
) )
if args.applicable_on_purchase_order: if args.applicable_on_purchase_order:
budget.applicable_on_purchase_order = 1 budget.applicable_on_purchase_order = 1
budget.action_if_annual_budget_exceeded_on_po = ( budget.action_if_annual_budget_exceeded_on_po = args.action_if_annual_budget_exceeded_on_po or "Warn"
args.action_if_annual_budget_exceeded_on_po or "Warn"
)
budget.action_if_accumulated_monthly_budget_exceeded_on_po = ( budget.action_if_accumulated_monthly_budget_exceeded_on_po = (
args.action_if_accumulated_monthly_budget_exceeded_on_po or "Warn" args.action_if_accumulated_monthly_budget_exceeded_on_po or "Warn"
) )

View File

@@ -26,9 +26,7 @@ from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import
class ChartofAccountsImporter(Document): class ChartofAccountsImporter(Document):
def validate(self): def validate(self):
if self.import_file: if self.import_file:
get_coa( get_coa("Chart of Accounts Importer", "All Accounts", file_name=self.import_file, for_validate=1)
"Chart of Accounts Importer", "All Accounts", file_name=self.import_file, for_validate=1
)
def validate_columns(data): def validate_columns(data):
@@ -104,7 +102,7 @@ def generate_data_from_csv(file_doc, as_dict=False):
file_path = file_doc.get_full_path() file_path = file_doc.get_full_path()
data = [] data = []
with open(file_path, "r") as in_file: with open(file_path) as in_file:
csv_reader = list(csv.reader(in_file)) csv_reader = list(csv.reader(in_file))
headers = csv_reader[0] headers = csv_reader[0]
del csv_reader[0] # delete top row and headers row del csv_reader[0] # delete top row and headers row
@@ -203,10 +201,10 @@ def build_forest(data):
for row in data: for row in data:
account_name, parent_account, account_number, parent_account_number = row[0:4] account_name, parent_account, account_number, parent_account_number = row[0:4]
if account_number: if account_number:
account_name = "{} - {}".format(account_number, account_name) account_name = f"{account_number} - {account_name}"
if parent_account_number: if parent_account_number:
parent_account_number = cstr(parent_account_number).strip() parent_account_number = cstr(parent_account_number).strip()
parent_account = "{} - {}".format(parent_account_number, parent_account) parent_account = f"{parent_account_number} - {parent_account}"
if parent_account == account_name == child: if parent_account == account_name == child:
return [parent_account] return [parent_account]
@@ -218,7 +216,7 @@ def build_forest(data):
frappe.bold(parent_account) frappe.bold(parent_account)
) )
) )
return [child] + parent_account_list return [child, *parent_account_list]
charts_map, paths = {}, [] charts_map, paths = {}, []
@@ -238,12 +236,12 @@ def build_forest(data):
) = i ) = i
if not account_name: if not account_name:
error_messages.append("Row {0}: Please enter Account Name".format(line_no)) error_messages.append(f"Row {line_no}: Please enter Account Name")
name = account_name name = account_name
if account_number: if account_number:
account_number = cstr(account_number).strip() account_number = cstr(account_number).strip()
account_name = "{} - {}".format(account_number, account_name) account_name = f"{account_number} - {account_name}"
charts_map[account_name] = {} charts_map[account_name] = {}
charts_map[account_name]["account_name"] = name charts_map[account_name]["account_name"] = name
@@ -340,9 +338,9 @@ def get_template(template_type, company):
def get_sample_template(writer, company): def get_sample_template(writer, company):
currency = frappe.db.get_value("Company", company, "default_currency") currency = frappe.db.get_value("Company", company, "default_currency")
with open(os.path.join(os.path.dirname(__file__), "coa_sample_template.csv"), "r") as f: with open(os.path.join(os.path.dirname(__file__), "coa_sample_template.csv")) as f:
for row in f: for row in f:
row = row.strip().split(",") + [currency] row = [*row.strip().split(","), currency]
writer.writerow(row) writer.writerow(row)
return writer return writer
@@ -451,7 +449,7 @@ def unset_existing_data(company):
"Purchase Taxes and Charges Template", "Purchase Taxes and Charges Template",
]: ]:
frappe.db.sql( frappe.db.sql(
'''delete from `tab{0}` where `company`="%s"'''.format(doctype) % (company) # nosec f'''delete from `tab{doctype}` where `company`="%s"''' % (company) # nosec
) )

View File

@@ -31,71 +31,71 @@ def create_or_update_cheque_print_format(template_name):
cheque_print.html = """ cheque_print.html = """
<style> <style>
.print-format { .print-format {{
padding: 0px; padding: 0px;
} }}
@media screen { @media screen {{
.print-format { .print-format {{
padding: 0in; padding: 0in;
} }}
} }}
</style> </style>
<div style="position: relative; top:%(starting_position_from_top_edge)scm"> <div style="position: relative; top:{starting_position_from_top_edge}cm">
<div style="width:%(cheque_width)scm;height:%(cheque_height)scm;"> <div style="width:{cheque_width}cm;height:{cheque_height}cm;">
<span style="top:%(acc_pay_dist_from_top_edge)scm; left:%(acc_pay_dist_from_left_edge)scm; <span style="top:{acc_pay_dist_from_top_edge}cm; left:{acc_pay_dist_from_left_edge}cm;
border-bottom: solid 1px;border-top:solid 1px; width:2cm;text-align: center; position: absolute;"> border-bottom: solid 1px;border-top:solid 1px; width:2cm;text-align: center; position: absolute;">
%(message_to_show)s {message_to_show}
</span> </span>
<span style="top:%(date_dist_from_top_edge)scm; left:%(date_dist_from_left_edge)scm; <span style="top:{date_dist_from_top_edge}cm; left:{date_dist_from_left_edge}cm;
position: absolute;"> position: absolute;">
{{ frappe.utils.formatdate(doc.reference_date) or '' }} {{{{ frappe.utils.formatdate(doc.reference_date) or '' }}}}
</span> </span>
<span style="top:%(acc_no_dist_from_top_edge)scm;left:%(acc_no_dist_from_left_edge)scm; <span style="top:{acc_no_dist_from_top_edge}cm;left:{acc_no_dist_from_left_edge}cm;
position: absolute; min-width: 6cm;"> position: absolute; min-width: 6cm;">
{{ doc.account_no or '' }} {{{{ doc.account_no or '' }}}}
</span> </span>
<span style="top:%(payer_name_from_top_edge)scm;left: %(payer_name_from_left_edge)scm; <span style="top:{payer_name_from_top_edge}cm;left: {payer_name_from_left_edge}cm;
position: absolute; min-width: 6cm;"> position: absolute; min-width: 6cm;">
{{doc.party_name}} {{{{doc.party_name}}}}
</span> </span>
<span style="top:%(amt_in_words_from_top_edge)scm; left:%(amt_in_words_from_left_edge)scm; <span style="top:{amt_in_words_from_top_edge}cm; left:{amt_in_words_from_left_edge}cm;
position: absolute; display: block; width: %(amt_in_word_width)scm; position: absolute; display: block; width: {amt_in_word_width}cm;
line-height:%(amt_in_words_line_spacing)scm; word-wrap: break-word;"> line-height:{amt_in_words_line_spacing}cm; word-wrap: break-word;">
{{frappe.utils.money_in_words(doc.base_paid_amount or doc.base_received_amount)}} {{{{frappe.utils.money_in_words(doc.base_paid_amount or doc.base_received_amount)}}}}
</span> </span>
<span style="top:%(amt_in_figures_from_top_edge)scm;left: %(amt_in_figures_from_left_edge)scm; <span style="top:{amt_in_figures_from_top_edge}cm;left: {amt_in_figures_from_left_edge}cm;
position: absolute; min-width: 4cm;"> position: absolute; min-width: 4cm;">
{{doc.get_formatted("base_paid_amount") or doc.get_formatted("base_received_amount")}} {{{{doc.get_formatted("base_paid_amount") or doc.get_formatted("base_received_amount")}}}}
</span> </span>
<span style="top:%(signatory_from_top_edge)scm;left: %(signatory_from_left_edge)scm; <span style="top:{signatory_from_top_edge}cm;left: {signatory_from_left_edge}cm;
position: absolute; min-width: 6cm;"> position: absolute; min-width: 6cm;">
{{doc.company}} {{{{doc.company}}}}
</span> </span>
</div> </div>
</div>""" % { </div>""".format(
"starting_position_from_top_edge": doc.starting_position_from_top_edge starting_position_from_top_edge=doc.starting_position_from_top_edge
if doc.cheque_size == "A4" if doc.cheque_size == "A4"
else 0.0, else 0.0,
"cheque_width": doc.cheque_width, cheque_width=doc.cheque_width,
"cheque_height": doc.cheque_height, cheque_height=doc.cheque_height,
"acc_pay_dist_from_top_edge": doc.acc_pay_dist_from_top_edge, acc_pay_dist_from_top_edge=doc.acc_pay_dist_from_top_edge,
"acc_pay_dist_from_left_edge": doc.acc_pay_dist_from_left_edge, acc_pay_dist_from_left_edge=doc.acc_pay_dist_from_left_edge,
"message_to_show": doc.message_to_show if doc.message_to_show else _("Account Pay Only"), message_to_show=doc.message_to_show if doc.message_to_show else _("Account Pay Only"),
"date_dist_from_top_edge": doc.date_dist_from_top_edge, date_dist_from_top_edge=doc.date_dist_from_top_edge,
"date_dist_from_left_edge": doc.date_dist_from_left_edge, date_dist_from_left_edge=doc.date_dist_from_left_edge,
"acc_no_dist_from_top_edge": doc.acc_no_dist_from_top_edge, acc_no_dist_from_top_edge=doc.acc_no_dist_from_top_edge,
"acc_no_dist_from_left_edge": doc.acc_no_dist_from_left_edge, acc_no_dist_from_left_edge=doc.acc_no_dist_from_left_edge,
"payer_name_from_top_edge": doc.payer_name_from_top_edge, payer_name_from_top_edge=doc.payer_name_from_top_edge,
"payer_name_from_left_edge": doc.payer_name_from_left_edge, payer_name_from_left_edge=doc.payer_name_from_left_edge,
"amt_in_words_from_top_edge": doc.amt_in_words_from_top_edge, amt_in_words_from_top_edge=doc.amt_in_words_from_top_edge,
"amt_in_words_from_left_edge": doc.amt_in_words_from_left_edge, amt_in_words_from_left_edge=doc.amt_in_words_from_left_edge,
"amt_in_word_width": doc.amt_in_word_width, amt_in_word_width=doc.amt_in_word_width,
"amt_in_words_line_spacing": doc.amt_in_words_line_spacing, amt_in_words_line_spacing=doc.amt_in_words_line_spacing,
"amt_in_figures_from_top_edge": doc.amt_in_figures_from_top_edge, amt_in_figures_from_top_edge=doc.amt_in_figures_from_top_edge,
"amt_in_figures_from_left_edge": doc.amt_in_figures_from_left_edge, amt_in_figures_from_left_edge=doc.amt_in_figures_from_left_edge,
"signatory_from_top_edge": doc.signatory_from_top_edge, signatory_from_top_edge=doc.signatory_from_top_edge,
"signatory_from_left_edge": doc.signatory_from_left_edge, signatory_from_left_edge=doc.signatory_from_left_edge,
} )
cheque_print.save(ignore_permissions=True) cheque_print.save(ignore_permissions=True)

View File

@@ -15,9 +15,7 @@ class CostCenter(NestedSet):
def autoname(self): def autoname(self):
from erpnext.accounts.utils import get_autoname_with_number from erpnext.accounts.utils import get_autoname_with_number
self.name = get_autoname_with_number( self.name = get_autoname_with_number(self.cost_center_number, self.cost_center_name, self.company)
self.cost_center_number, self.cost_center_name, self.company
)
def validate(self): def validate(self):
self.validate_mandatory() self.validate_mandatory()
@@ -90,14 +88,14 @@ class CostCenter(NestedSet):
new_cost_center = get_name_with_abbr(newdn, self.company) new_cost_center = get_name_with_abbr(newdn, self.company)
# Validate properties before merging # Validate properties before merging
super(CostCenter, self).before_rename(olddn, new_cost_center, merge, "is_group") super().before_rename(olddn, new_cost_center, merge, "is_group")
if not merge: if not merge:
new_cost_center = get_name_with_number(new_cost_center, self.cost_center_number) new_cost_center = get_name_with_number(new_cost_center, self.cost_center_number)
return new_cost_center return new_cost_center
def after_rename(self, olddn, newdn, merge=False): def after_rename(self, olddn, newdn, merge=False):
super(CostCenter, self).after_rename(olddn, newdn, merge) super().after_rename(olddn, newdn, merge)
if not merge: if not merge:
new_cost_center = frappe.db.get_value( new_cost_center = frappe.db.get_value(

View File

@@ -10,7 +10,6 @@ test_records = frappe.get_test_records("Cost Center")
class TestCostCenter(unittest.TestCase): class TestCostCenter(unittest.TestCase):
def test_cost_center_creation_against_child_node(self): def test_cost_center_creation_against_child_node(self):
if not frappe.db.get_value("Cost Center", {"name": "_Test Cost Center 2 - _TC"}): if not frappe.db.get_value("Cost Center", {"name": "_Test Cost Center 2 - _TC"}):
frappe.get_doc(test_records[1]).insert() frappe.get_doc(test_records[1]).insert()

View File

@@ -29,7 +29,7 @@ class InvalidDateError(frappe.ValidationError):
class CostCenterAllocation(Document): class CostCenterAllocation(Document):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(CostCenterAllocation, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self._skip_from_date_validation = False self._skip_from_date_validation = False
def validate(self): def validate(self):
@@ -44,9 +44,7 @@ class CostCenterAllocation(Document):
total_percentage = sum([d.percentage for d in self.get("allocation_percentages", [])]) total_percentage = sum([d.percentage for d in self.get("allocation_percentages", [])])
if total_percentage != 100: if total_percentage != 100:
frappe.throw( frappe.throw(_("Total percentage against cost centers should be 100"), WrongPercentageAllocation)
_("Total percentage against cost centers should be 100"), WrongPercentageAllocation
)
def validate_from_date_based_on_existing_gle(self): def validate_from_date_based_on_existing_gle(self):
# Check if GLE exists against the main cost center # Check if GLE exists against the main cost center

View File

@@ -18,7 +18,6 @@ class CurrencyExchangeSettings(Document):
def set_parameters_and_result(self): def set_parameters_and_result(self):
if self.service_provider == "exchangerate.host": if self.service_provider == "exchangerate.host":
if not self.access_key: if not self.access_key:
frappe.throw( frappe.throw(
_("Access Key is required for Service Provider: {0}").format( _("Access Key is required for Service Provider: {0}").format(
@@ -53,9 +52,7 @@ class CurrencyExchangeSettings(Document):
transaction_date=nowdate(), to_currency="INR", from_currency="USD" transaction_date=nowdate(), to_currency="INR", from_currency="USD"
) )
api_url = self.api_endpoint.format( api_url = self.api_endpoint.format(transaction_date=nowdate(), to_currency="INR", from_currency="USD")
transaction_date=nowdate(), to_currency="INR", from_currency="USD"
)
try: try:
response = requests.get(api_url, params=params) response = requests.get(api_url, params=params)
@@ -75,14 +72,14 @@ class CurrencyExchangeSettings(Document):
] ]
except Exception: except Exception:
frappe.throw(_("Invalid result key. Response:") + " " + response.text) frappe.throw(_("Invalid result key. Response:") + " " + response.text)
if not isinstance(value, (int, float)): if not isinstance(value, int | float):
frappe.throw(_("Returned exchange rate is neither integer not float.")) frappe.throw(_("Returned exchange rate is neither integer not float."))
self.url = response.url self.url = response.url
@frappe.whitelist() @frappe.whitelist()
def get_api_endpoint(service_provider: str = None, use_http: bool = False): def get_api_endpoint(service_provider: str | None = None, use_http: bool = False):
if service_provider and service_provider in ["exchangerate.host", "frankfurter.app"]: if service_provider and service_provider in ["exchangerate.host", "frankfurter.app"]:
if service_provider == "exchangerate.host": if service_provider == "exchangerate.host":
api = "api.exchangerate.host/convert" api = "api.exchangerate.host/convert"

View File

@@ -246,7 +246,6 @@ class ExchangeRateRevaluation(Document):
# Handle Accounts with '0' balance in Account/Base Currency # Handle Accounts with '0' balance in Account/Base Currency
for d in [x for x in account_details if x.zero_balance]: for d in [x for x in account_details if x.zero_balance]:
if d.balance != 0: if d.balance != 0:
current_exchange_rate = new_exchange_rate = 0 current_exchange_rate = new_exchange_rate = 0
@@ -259,7 +258,8 @@ class ExchangeRateRevaluation(Document):
new_balance_in_account_currency = 0 new_balance_in_account_currency = 0
current_exchange_rate = ( current_exchange_rate = (
calculate_exchange_rate_using_last_gle(company, d.account, d.party_type, d.party) or 0.0 calculate_exchange_rate_using_last_gle(company, d.account, d.party_type, d.party)
or 0.0
) )
gain_loss = new_balance_in_account_currency - ( gain_loss = new_balance_in_account_currency - (
@@ -313,9 +313,7 @@ class ExchangeRateRevaluation(Document):
revaluation_jv = self.make_jv_for_revaluation() revaluation_jv = self.make_jv_for_revaluation()
if revaluation_jv: if revaluation_jv:
frappe.msgprint( frappe.msgprint(f"Revaluation Journal: {get_link_to_form('Journal Entry', revaluation_jv.name)}")
f"Revaluation Journal: {get_link_to_form('Journal Entry', revaluation_jv.name)}"
)
return { return {
"revaluation_jv": revaluation_jv.name if revaluation_jv else None, "revaluation_jv": revaluation_jv.name if revaluation_jv else None,
@@ -372,7 +370,8 @@ class ExchangeRateRevaluation(Document):
journal_account.update( journal_account.update(
{ {
dr_or_cr: flt( dr_or_cr: flt(
abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency") abs(d.get("balance_in_account_currency")),
d.precision("balance_in_account_currency"),
), ),
reverse_dr_or_cr: 0, reverse_dr_or_cr: 0,
"debit": 0, "debit": 0,
@@ -498,7 +497,9 @@ class ExchangeRateRevaluation(Document):
abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency") abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")
), ),
"cost_center": erpnext.get_default_cost_center(self.company), "cost_center": erpnext.get_default_cost_center(self.company),
"exchange_rate": flt(d.get("current_exchange_rate"), d.precision("current_exchange_rate")), "exchange_rate": flt(
d.get("current_exchange_rate"), d.precision("current_exchange_rate")
),
"reference_type": "Exchange Rate Revaluation", "reference_type": "Exchange Rate Revaluation",
"reference_name": self.name, "reference_name": self.name,
} }
@@ -576,7 +577,7 @@ 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 company, posting_date, account, party_type=None, party=None, rounding_loss_allowance: float | None = None
): ):
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"))
@@ -589,7 +590,7 @@ def get_account_details(
frappe.throw(_("Party Type and Party is mandatory for {0} account").format(account_type)) frappe.throw(_("Party Type and Party is mandatory for {0} account").format(account_type))
account_details = {} account_details = {}
company_currency = erpnext.get_company_currency(company) erpnext.get_company_currency(company)
account_details = { account_details = {
"account_currency": account_currency, "account_currency": account_currency,
@@ -603,9 +604,7 @@ def get_account_details(
rounding_loss_allowance=rounding_loss_allowance, rounding_loss_allowance=rounding_loss_allowance,
) )
if account_balance and ( if account_balance and (account_balance[0].balance or account_balance[0].balance_in_account_currency):
account_balance[0].balance or account_balance[0].balance_in_account_currency
):
if account_with_new_balance := ExchangeRateRevaluation.calculate_new_account_balance( if account_with_new_balance := ExchangeRateRevaluation.calculate_new_account_balance(
company, posting_date, account_balance company, posting_date, account_balance
): ):

View File

@@ -1,21 +1,14 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt # See license.txt
import unittest
import frappe import frappe
from frappe import qb
from frappe.tests.utils import FrappeTestCase, change_settings from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import add_days, flt, today from frappe.utils import add_days, flt, today
from erpnext import get_default_cost_center
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_entry
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.party import get_party_account
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
from erpnext.stock.doctype.item.test_item import create_item
class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase): class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase):
@@ -73,9 +66,7 @@ class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase):
err.extend("accounts", accounts) err.extend("accounts", accounts)
row = err.accounts[0] row = err.accounts[0]
row.new_exchange_rate = 85 row.new_exchange_rate = 85
row.new_balance_in_base_currency = flt( row.new_balance_in_base_currency = flt(row.new_exchange_rate * flt(row.balance_in_account_currency))
row.new_exchange_rate * flt(row.balance_in_account_currency)
)
row.gain_loss = row.new_balance_in_base_currency - flt(row.balance_in_base_currency) row.gain_loss = row.new_balance_in_base_currency - flt(row.balance_in_base_currency)
err.set_total_gain_loss() err.set_total_gain_loss()
err = err.save().submit() err = err.save().submit()
@@ -127,9 +118,9 @@ class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase):
pe.save().submit() pe.save().submit()
# Cancel the auto created gain/loss JE to simulate balance only in base currency # Cancel the auto created gain/loss JE to simulate balance only in base currency
je = frappe.db.get_all( je = frappe.db.get_all("Journal Entry Account", filters={"reference_name": si.name}, pluck="parent")[
"Journal Entry Account", filters={"reference_name": si.name}, pluck="parent" 0
)[0] ]
frappe.get_doc("Journal Entry", je).cancel() frappe.get_doc("Journal Entry", je).cancel()
err = frappe.new_doc("Exchange Rate Revaluation") err = frappe.new_doc("Exchange Rate Revaluation")
@@ -235,9 +226,9 @@ class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase):
self.assertEqual(flt(acc.debit, precision), 0.0) self.assertEqual(flt(acc.debit, precision), 0.0)
self.assertEqual(flt(acc.credit, precision), 0.0) self.assertEqual(flt(acc.credit, precision), 0.0)
row = [x for x in je.accounts if x.account == self.debtors_usd][0] row = next(x for x in je.accounts if x.account == self.debtors_usd)
self.assertEqual(flt(row.credit_in_account_currency, precision), 5.0) # in USD self.assertEqual(flt(row.credit_in_account_currency, precision), 5.0) # in USD
row = [x for x in je.accounts if x.account != self.debtors_usd][0] row = next(x for x in je.accounts if x.account != self.debtors_usd)
self.assertEqual(flt(row.debit_in_account_currency, precision), 421.06) # in INR self.assertEqual(flt(row.debit_in_account_currency, precision), 421.06) # in INR
# total_debit and total_credit will be 0.0, as JV is posting only to account currency fields # total_debit and total_credit will be 0.0, as JV is posting only to account currency fields
@@ -294,5 +285,5 @@ class TestExchangeRateRevaluation(AccountsTestMixin, FrappeTestCase):
"new_balance_in_account_currency": 100.0, "new_balance_in_account_currency": 100.0,
} }
for key, val in expected_data.items(): for key, _val in expected_data.items():
self.assertEqual(expected_data.get(key), account_details.get(key)) self.assertEqual(expected_data.get(key), account_details.get(key))

View File

@@ -98,9 +98,9 @@ class FiscalYear(Document):
if overlap: if overlap:
frappe.throw( frappe.throw(
_("Year start date or end date is overlapping with {0}. To avoid please set company").format( _(
existing.name "Year start date or end date is overlapping with {0}. To avoid please set company"
), ).format(existing.name),
frappe.NameError, frappe.NameError,
) )
@@ -116,9 +116,9 @@ def check_duplicate_fiscal_year(doc):
not frappe.flags.in_test not frappe.flags.in_test
): ):
frappe.throw( frappe.throw(
_("Fiscal Year Start Date and Fiscal Year End Date are already set in Fiscal Year {0}").format( _(
fiscal_year "Fiscal Year Start Date and Fiscal Year End Date are already set in Fiscal Year {0}"
) ).format(fiscal_year)
) )

View File

@@ -63,13 +63,18 @@ class GLEntry(Document):
]: ]:
# Update outstanding amt on against voucher # Update outstanding amt on against voucher
if ( if (
self.against_voucher_type in ["Journal Entry", "Sales Invoice", "Purchase Invoice", "Fees"] self.against_voucher_type
in ["Journal Entry", "Sales Invoice", "Purchase Invoice", "Fees"]
and self.against_voucher and self.against_voucher
and self.flags.update_outstanding == "Yes" and self.flags.update_outstanding == "Yes"
and not frappe.flags.is_reverse_depr_entry and not frappe.flags.is_reverse_depr_entry
): ):
update_outstanding_amt( update_outstanding_amt(
self.account, self.party_type, self.party, self.against_voucher_type, self.against_voucher self.account,
self.party_type,
self.party,
self.against_voucher_type,
self.against_voucher,
) )
def check_mandatory(self): def check_mandatory(self):
@@ -139,9 +144,9 @@ class GLEntry(Document):
): ):
if not self.get(dimension.fieldname): if not self.get(dimension.fieldname):
frappe.throw( frappe.throw(
_("Accounting Dimension <b>{0}</b> is required for 'Profit and Loss' account {1}.").format( _(
dimension.label, self.account "Accounting Dimension <b>{0}</b> is required for 'Profit and Loss' account {1}."
) ).format(dimension.label, self.account)
) )
if ( if (
@@ -153,9 +158,9 @@ class GLEntry(Document):
): ):
if not self.get(dimension.fieldname): if not self.get(dimension.fieldname):
frappe.throw( frappe.throw(
_("Accounting Dimension <b>{0}</b> is required for 'Balance Sheet' account {1}.").format( _(
dimension.label, self.account "Accounting Dimension <b>{0}</b> is required for 'Balance Sheet' account {1}."
) ).format(dimension.label, self.account)
) )
def check_pl_account(self): def check_pl_account(self):
@@ -203,9 +208,7 @@ class GLEntry(Document):
if not self.cost_center: if not self.cost_center:
return return
is_group, company = frappe.get_cached_value( is_group, company = frappe.get_cached_value("Cost Center", self.cost_center, ["is_group", "company"])
"Cost Center", self.cost_center, ["is_group", "company"]
)
if company != self.company: if company != self.company:
frappe.throw( frappe.throw(
@@ -274,7 +277,7 @@ def update_outstanding_amt(
account, party_type, party, against_voucher_type, against_voucher, on_cancel=False account, party_type, party, against_voucher_type, against_voucher, on_cancel=False
): ):
if party_type and party: if party_type and party:
party_condition = " and party_type={0} and party={1}".format( party_condition = " and party_type={} and party={}".format(
frappe.db.escape(party_type), frappe.db.escape(party) frappe.db.escape(party_type), frappe.db.escape(party)
) )
else: else:
@@ -282,23 +285,19 @@ def update_outstanding_amt(
if against_voucher_type == "Sales Invoice": if against_voucher_type == "Sales Invoice":
party_account = frappe.get_cached_value(against_voucher_type, against_voucher, "debit_to") party_account = frappe.get_cached_value(against_voucher_type, against_voucher, "debit_to")
account_condition = "and account in ({0}, {1})".format( account_condition = f"and account in ({frappe.db.escape(account)}, {frappe.db.escape(party_account)})"
frappe.db.escape(account), frappe.db.escape(party_account)
)
else: else:
account_condition = " and account = {0}".format(frappe.db.escape(account)) account_condition = f" and account = {frappe.db.escape(account)}"
# get final outstanding amt # get final outstanding amt
bal = flt( bal = flt(
frappe.db.sql( frappe.db.sql(
""" f"""
select sum(debit_in_account_currency) - sum(credit_in_account_currency) select sum(debit_in_account_currency) - sum(credit_in_account_currency)
from `tabGL Entry` from `tabGL Entry`
where against_voucher_type=%s and against_voucher=%s where against_voucher_type=%s and against_voucher=%s
and voucher_type != 'Invoice Discounting' and voucher_type != 'Invoice Discounting'
{0} {1}""".format( {party_condition} {account_condition}""",
party_condition, account_condition
),
(against_voucher_type, against_voucher), (against_voucher_type, against_voucher),
)[0][0] )[0][0]
or 0.0 or 0.0
@@ -309,12 +308,10 @@ def update_outstanding_amt(
elif against_voucher_type == "Journal Entry": elif against_voucher_type == "Journal Entry":
against_voucher_amount = flt( against_voucher_amount = flt(
frappe.db.sql( frappe.db.sql(
""" f"""
select sum(debit_in_account_currency) - sum(credit_in_account_currency) select sum(debit_in_account_currency) - sum(credit_in_account_currency)
from `tabGL Entry` where voucher_type = 'Journal Entry' and voucher_no = %s from `tabGL Entry` where voucher_type = 'Journal Entry' and voucher_no = %s
and account = %s and (against_voucher is null or against_voucher='') {0}""".format( and account = %s and (against_voucher is null or against_voucher='') {party_condition}""",
party_condition
),
(against_voucher, account), (against_voucher, account),
)[0][0] )[0][0]
) )
@@ -333,7 +330,9 @@ def update_outstanding_amt(
# Validation : Outstanding can not be negative for JV # Validation : Outstanding can not be negative for JV
if bal < 0 and not on_cancel: if bal < 0 and not on_cancel:
frappe.throw( frappe.throw(
_("Outstanding for {0} cannot be less than zero ({1})").format(against_voucher, fmt_money(bal)) _("Outstanding for {0} cannot be less than zero ({1})").format(
against_voucher, fmt_money(bal)
)
) )
if against_voucher_type in ["Sales Invoice", "Purchase Invoice", "Fees"]: if against_voucher_type in ["Sales Invoice", "Purchase Invoice", "Fees"]:
@@ -406,7 +405,7 @@ def rename_temporarily_named_docs(doctype):
set_name_from_naming_options(frappe.get_meta(doctype).autoname, doc) set_name_from_naming_options(frappe.get_meta(doctype).autoname, doc)
newname = doc.name newname = doc.name
frappe.db.sql( frappe.db.sql(
"UPDATE `tab{}` SET name = %s, to_rename = 0 where name = %s".format(doctype), f"UPDATE `tab{doctype}` SET name = %s, to_rename = 0 where name = %s",
(newname, oldname), (newname, oldname),
auto_commit=True, auto_commit=True,
) )

View File

@@ -14,9 +14,7 @@ from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journ
class TestGLEntry(unittest.TestCase): class TestGLEntry(unittest.TestCase):
def test_round_off_entry(self): def test_round_off_entry(self):
frappe.db.set_value("Company", "_Test Company", "round_off_account", "_Test Write Off - _TC") frappe.db.set_value("Company", "_Test Company", "round_off_account", "_Test Write Off - _TC")
frappe.db.set_value( frappe.db.set_value("Company", "_Test Company", "round_off_cost_center", "_Test Cost Center - _TC")
"Company", "_Test Company", "round_off_cost_center", "_Test Cost Center - _TC"
)
jv = make_journal_entry( jv = make_journal_entry(
"_Test Account Cost for Goods Sold - _TC", "_Test Account Cost for Goods Sold - _TC",
@@ -73,7 +71,9 @@ class TestGLEntry(unittest.TestCase):
) )
self.assertTrue(all(entry.to_rename == 0 for entry in new_gl_entries)) self.assertTrue(all(entry.to_rename == 0 for entry in new_gl_entries))
self.assertTrue(all(new.name != old.name for new, old in zip(gl_entries, new_gl_entries))) self.assertTrue(
all(new.name != old.name for new, old in zip(gl_entries, new_gl_entries, strict=False))
)
new_naming_series_current_value = frappe.db.sql( new_naming_series_current_value = frappe.db.sql(
"SELECT current from tabSeries where name = %s", naming_series "SELECT current from tabSeries where name = %s", naming_series

View File

@@ -55,9 +55,7 @@ class InvoiceDiscounting(AccountsController):
frappe.throw( frappe.throw(
_( _(
"Row({0}): Outstanding Amount cannot be greater than actual Outstanding Amount {1} in {2}" "Row({0}): Outstanding Amount cannot be greater than actual Outstanding Amount {1} in {2}"
).format( ).format(record.idx, frappe.bold(actual_outstanding), frappe.bold(record.sales_invoice))
record.idx, frappe.bold(actual_outstanding), frappe.bold(record.sales_invoice)
)
) )
def calculate_total_amount(self): def calculate_total_amount(self):
@@ -77,7 +75,9 @@ class InvoiceDiscounting(AccountsController):
self.status = status self.status = status
self.db_set("status", status) self.db_set("status", status)
for d in self.invoices: for d in self.invoices:
frappe.get_doc("Sales Invoice", d.sales_invoice).set_status(update=True, update_modified=False) frappe.get_doc("Sales Invoice", d.sales_invoice).set_status(
update=True, update_modified=False
)
else: else:
self.status = "Draft" self.status = "Draft"
if self.docstatus == 1: if self.docstatus == 1:

View File

@@ -75,8 +75,8 @@ class TestInvoiceDiscounting(unittest.TestCase):
gle = get_gl_entries("Invoice Discounting", inv_disc.name) gle = get_gl_entries("Invoice Discounting", inv_disc.name)
expected_gle = {inv.debit_to: [0.0, 200], self.ar_credit: [200, 0.0]} expected_gle = {inv.debit_to: [0.0, 200], self.ar_credit: [200, 0.0]}
for i, gle in enumerate(gle): for _i, gle_value in enumerate(gle):
self.assertEqual([gle.debit, gle.credit], expected_gle.get(gle.account)) self.assertEqual([gle_value.debit, gle_value.credit], expected_gle.get(gle_value.account))
def test_loan_on_submit(self): def test_loan_on_submit(self):
inv = create_sales_invoice(rate=300) inv = create_sales_invoice(rate=300)
@@ -92,9 +92,7 @@ class TestInvoiceDiscounting(unittest.TestCase):
period=60, period=60,
) )
self.assertEqual(inv_disc.status, "Sanctioned") self.assertEqual(inv_disc.status, "Sanctioned")
self.assertEqual( self.assertEqual(inv_disc.loan_end_date, add_days(inv_disc.loan_start_date, inv_disc.loan_period))
inv_disc.loan_end_date, add_days(inv_disc.loan_start_date, inv_disc.loan_period)
)
def test_on_disbursed(self): def test_on_disbursed(self):
inv = create_sales_invoice(rate=500) inv = create_sales_invoice(rate=500)
@@ -262,13 +260,9 @@ class TestInvoiceDiscounting(unittest.TestCase):
je_on_payment.submit() je_on_payment.submit()
self.assertEqual(je_on_payment.accounts[0].account, self.ar_discounted) self.assertEqual(je_on_payment.accounts[0].account, self.ar_discounted)
self.assertEqual( self.assertEqual(je_on_payment.accounts[0].credit_in_account_currency, flt(inv.outstanding_amount))
je_on_payment.accounts[0].credit_in_account_currency, flt(inv.outstanding_amount)
)
self.assertEqual(je_on_payment.accounts[1].account, self.bank_account) self.assertEqual(je_on_payment.accounts[1].account, self.bank_account)
self.assertEqual( self.assertEqual(je_on_payment.accounts[1].debit_in_account_currency, flt(inv.outstanding_amount))
je_on_payment.accounts[1].debit_in_account_currency, flt(inv.outstanding_amount)
)
inv.reload() inv.reload()
self.assertEqual(inv.outstanding_amount, 0) self.assertEqual(inv.outstanding_amount, 0)
@@ -304,13 +298,9 @@ class TestInvoiceDiscounting(unittest.TestCase):
je_on_payment.submit() je_on_payment.submit()
self.assertEqual(je_on_payment.accounts[0].account, self.ar_unpaid) self.assertEqual(je_on_payment.accounts[0].account, self.ar_unpaid)
self.assertEqual( self.assertEqual(je_on_payment.accounts[0].credit_in_account_currency, flt(inv.outstanding_amount))
je_on_payment.accounts[0].credit_in_account_currency, flt(inv.outstanding_amount)
)
self.assertEqual(je_on_payment.accounts[1].account, self.bank_account) self.assertEqual(je_on_payment.accounts[1].account, self.bank_account)
self.assertEqual( self.assertEqual(je_on_payment.accounts[1].debit_in_account_currency, flt(inv.outstanding_amount))
je_on_payment.accounts[1].debit_in_account_currency, flt(inv.outstanding_amount)
)
inv.reload() inv.reload()
self.assertEqual(inv.outstanding_amount, 0) self.assertEqual(inv.outstanding_amount, 0)

View File

@@ -14,7 +14,7 @@ class ItemTaxTemplate(Document):
def autoname(self): def autoname(self):
if self.company and self.title: if self.company and self.title:
abbr = frappe.get_cached_value("Company", self.company, "abbr") abbr = frappe.get_cached_value("Company", self.company, "abbr")
self.name = "{0} - {1}".format(self.title, abbr) self.name = f"{self.title} - {abbr}"
def validate_tax_accounts(self): def validate_tax_accounts(self):
"""Check whether Tax Rate is not entered twice for same Tax Type""" """Check whether Tax Rate is not entered twice for same Tax Type"""

View File

@@ -33,7 +33,7 @@ class StockAccountInvalidTransaction(frappe.ValidationError):
class JournalEntry(AccountsController): class JournalEntry(AccountsController):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(JournalEntry, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def get_feed(self): def get_feed(self):
return self.voucher_type return self.voucher_type
@@ -102,7 +102,7 @@ class JournalEntry(AccountsController):
def on_cancel(self): def on_cancel(self):
# References for this Journal are removed on the `on_cancel` event in accounts_controller # References for this Journal are removed on the `on_cancel` event in accounts_controller
super(JournalEntry, self).on_cancel() super().on_cancel()
self.ignore_linked_doctypes = ( self.ignore_linked_doctypes = (
"GL Entry", "GL Entry",
"Stock Ledger Entry", "Stock Ledger Entry",
@@ -137,10 +137,7 @@ class JournalEntry(AccountsController):
frappe.get_doc(voucher_type, voucher_no).set_total_advance_paid() frappe.get_doc(voucher_type, voucher_no).set_total_advance_paid()
def validate_inter_company_accounts(self): def validate_inter_company_accounts(self):
if ( if self.voucher_type == "Inter Company Journal Entry" and self.inter_company_journal_entry_reference:
self.voucher_type == "Inter Company Journal Entry"
and self.inter_company_journal_entry_reference
):
doc = frappe.get_doc("Journal Entry", self.inter_company_journal_entry_reference) doc = frappe.get_doc("Journal Entry", self.inter_company_journal_entry_reference)
account_currency = frappe.get_cached_value("Company", self.company, "default_currency") account_currency = frappe.get_cached_value("Company", self.company, "default_currency")
previous_account_currency = frappe.get_cached_value("Company", doc.company, "default_currency") previous_account_currency = frappe.get_cached_value("Company", doc.company, "default_currency")
@@ -286,10 +283,7 @@ class JournalEntry(AccountsController):
asset.set_status() asset.set_status()
def update_inter_company_jv(self): def update_inter_company_jv(self):
if ( if self.voucher_type == "Inter Company Journal Entry" and self.inter_company_journal_entry_reference:
self.voucher_type == "Inter Company Journal Entry"
and self.inter_company_journal_entry_reference
):
frappe.db.set_value( frappe.db.set_value(
"Journal Entry", "Journal Entry",
self.inter_company_journal_entry_reference, self.inter_company_journal_entry_reference,
@@ -317,17 +311,25 @@ class JournalEntry(AccountsController):
if d.account == inv_disc_doc.short_term_loan and d.reference_name == inv_disc: if d.account == inv_disc_doc.short_term_loan and d.reference_name == inv_disc:
if self.docstatus == 1: if self.docstatus == 1:
if d.credit > 0: if d.credit > 0:
_validate_invoice_discounting_status(inv_disc, inv_disc_doc.status, "Sanctioned", d.idx) _validate_invoice_discounting_status(
inv_disc, inv_disc_doc.status, "Sanctioned", d.idx
)
status = "Disbursed" status = "Disbursed"
elif d.debit > 0: elif d.debit > 0:
_validate_invoice_discounting_status(inv_disc, inv_disc_doc.status, "Disbursed", d.idx) _validate_invoice_discounting_status(
inv_disc, inv_disc_doc.status, "Disbursed", d.idx
)
status = "Settled" status = "Settled"
else: else:
if d.credit > 0: if d.credit > 0:
_validate_invoice_discounting_status(inv_disc, inv_disc_doc.status, "Disbursed", d.idx) _validate_invoice_discounting_status(
inv_disc, inv_disc_doc.status, "Disbursed", d.idx
)
status = "Sanctioned" status = "Sanctioned"
elif d.debit > 0: elif d.debit > 0:
_validate_invoice_discounting_status(inv_disc, inv_disc_doc.status, "Settled", d.idx) _validate_invoice_discounting_status(
inv_disc, inv_disc_doc.status, "Settled", d.idx
)
status = "Disbursed" status = "Disbursed"
break break
if status: if status:
@@ -384,10 +386,7 @@ class JournalEntry(AccountsController):
) )
def unlink_inter_company_jv(self): def unlink_inter_company_jv(self):
if ( if self.voucher_type == "Inter Company Journal Entry" and self.inter_company_journal_entry_reference:
self.voucher_type == "Inter Company Journal Entry"
and self.inter_company_journal_entry_reference
):
frappe.db.set_value( frappe.db.set_value(
"Journal Entry", "Journal Entry",
self.inter_company_journal_entry_reference, self.inter_company_journal_entry_reference,
@@ -409,9 +408,9 @@ class JournalEntry(AccountsController):
if account_type in ["Receivable", "Payable"]: if account_type in ["Receivable", "Payable"]:
if not (d.party_type and d.party): if not (d.party_type and d.party):
frappe.throw( frappe.throw(
_("Row {0}: Party Type and Party is required for Receivable / Payable account {1}").format( _(
d.idx, d.account "Row {0}: Party Type and Party is required for Receivable / Payable account {1}"
) ).format(d.idx, d.account)
) )
elif ( elif (
d.party_type d.party_type
@@ -476,16 +475,18 @@ class JournalEntry(AccountsController):
def system_generated_gain_loss(self): def system_generated_gain_loss(self):
return ( return (
self.voucher_type == "Exchange Gain Or Loss" self.voucher_type == "Exchange Gain Or Loss" and self.multi_currency and self.is_system_generated
and self.multi_currency
and self.is_system_generated
) )
def validate_against_jv(self): def validate_against_jv(self):
for d in self.get("accounts"): for d in self.get("accounts"):
if d.reference_type == "Journal Entry": if d.reference_type == "Journal Entry":
account_root_type = frappe.get_cached_value("Account", d.account, "root_type") account_root_type = frappe.get_cached_value("Account", d.account, "root_type")
if account_root_type == "Asset" and flt(d.debit) > 0 and not self.system_generated_gain_loss(): if (
account_root_type == "Asset"
and flt(d.debit) > 0
and not self.system_generated_gain_loss()
):
frappe.throw( frappe.throw(
_( _(
"Row #{0}: For {1}, you can select reference document only if account gets credited" "Row #{0}: For {1}, you can select reference document only if account gets credited"
@@ -567,11 +568,13 @@ class JournalEntry(AccountsController):
if d.reference_type == "Purchase Order" and flt(d.credit) > 0: if d.reference_type == "Purchase Order" and flt(d.credit) > 0:
frappe.throw( frappe.throw(
_("Row {0}: Credit entry can not be linked with a {1}").format(d.idx, d.reference_type) _("Row {0}: Credit entry can not be linked with a {1}").format(
d.idx, d.reference_type
)
) )
# set totals # set totals
if not d.reference_name in self.reference_totals: if d.reference_name not in self.reference_totals:
self.reference_totals[d.reference_name] = 0.0 self.reference_totals[d.reference_name] = 0.0
if self.voucher_type not in ("Deferred Revenue", "Deferred Expense"): if self.voucher_type not in ("Deferred Revenue", "Deferred Expense"):
@@ -589,7 +592,10 @@ class JournalEntry(AccountsController):
# check if party and account match # check if party and account match
if d.reference_type in ("Sales Invoice", "Purchase Invoice"): if d.reference_type in ("Sales Invoice", "Purchase Invoice"):
if self.voucher_type in ("Deferred Revenue", "Deferred Expense") and d.reference_detail_no: if (
self.voucher_type in ("Deferred Revenue", "Deferred Expense")
and d.reference_detail_no
):
debit_or_credit = "Debit" if d.debit else "Credit" debit_or_credit = "Debit" if d.debit else "Credit"
party_account = get_deferred_booking_accounts( party_account = get_deferred_booking_accounts(
d.reference_type, d.reference_detail_no, debit_or_credit d.reference_type, d.reference_detail_no, debit_or_credit
@@ -598,7 +604,8 @@ class JournalEntry(AccountsController):
else: else:
if d.reference_type == "Sales Invoice": if d.reference_type == "Sales Invoice":
party_account = ( party_account = (
get_party_account_based_on_invoice_discounting(d.reference_name) or against_voucher[1] get_party_account_based_on_invoice_discounting(d.reference_name)
or against_voucher[1]
) )
else: else:
party_account = against_voucher[1] party_account = against_voucher[1]
@@ -722,7 +729,9 @@ class JournalEntry(AccountsController):
if not (self.voucher_type == "Exchange Gain Or Loss" and self.multi_currency): if not (self.voucher_type == "Exchange Gain Or Loss" and self.multi_currency):
if self.difference: if self.difference:
frappe.throw( frappe.throw(
_("Total Debit must be equal to Total Credit. The difference is {0}").format(self.difference) _("Total Debit must be equal to Total Credit. The difference is {0}").format(
self.difference
)
) )
def set_total_debit_credit(self): def set_total_debit_credit(self):
@@ -786,7 +795,6 @@ class JournalEntry(AccountsController):
and self.posting_date and self.posting_date
) )
): ):
ignore_exchange_rate = False ignore_exchange_rate = False
if self.get("flags") and self.flags.get("ignore_exchange_rate"): if self.get("flags") and self.flags.get("ignore_exchange_rate"):
ignore_exchange_rate = True ignore_exchange_rate = True
@@ -1032,27 +1040,21 @@ class JournalEntry(AccountsController):
self.validate_total_debit_and_credit() self.validate_total_debit_and_credit()
def get_values(self): def get_values(self):
cond = ( cond = f" and outstanding_amount <= {self.write_off_amount}" if flt(self.write_off_amount) > 0 else ""
" and outstanding_amount <= {0}".format(self.write_off_amount)
if flt(self.write_off_amount) > 0
else ""
)
if self.write_off_based_on == "Accounts Receivable": if self.write_off_based_on == "Accounts Receivable":
return frappe.db.sql( return frappe.db.sql(
"""select name, debit_to as account, customer as party, outstanding_amount """select name, debit_to as account, customer as party, outstanding_amount
from `tabSales Invoice` where docstatus = 1 and company = %s from `tabSales Invoice` where docstatus = 1 and company = {}
and outstanding_amount > 0 %s""" and outstanding_amount > 0 {}""".format("%s", cond),
% ("%s", cond),
self.company, self.company,
as_dict=True, as_dict=True,
) )
elif self.write_off_based_on == "Accounts Payable": elif self.write_off_based_on == "Accounts Payable":
return frappe.db.sql( return frappe.db.sql(
"""select name, credit_to as account, supplier as party, outstanding_amount """select name, credit_to as account, supplier as party, outstanding_amount
from `tabPurchase Invoice` where docstatus = 1 and company = %s from `tabPurchase Invoice` where docstatus = 1 and company = {}
and outstanding_amount > 0 %s""" and outstanding_amount > 0 {}""".format("%s", cond),
% ("%s", cond),
self.company, self.company,
as_dict=True, as_dict=True,
) )
@@ -1161,7 +1163,7 @@ def get_payment_entry_against_order(
"amount_field_bank": amount_field_bank, "amount_field_bank": amount_field_bank,
"amount": amount, "amount": amount,
"debit_in_account_currency": debit_in_account_currency, "debit_in_account_currency": debit_in_account_currency,
"remarks": "Advance Payment received against {0} {1}".format(dt, dn), "remarks": f"Advance Payment received against {dt} {dn}",
"is_advance": "Yes", "is_advance": "Yes",
"bank_account": bank_account, "bank_account": bank_account,
"journal_entry": journal_entry, "journal_entry": journal_entry,
@@ -1200,7 +1202,7 @@ def get_payment_entry_against_invoice(
"amount_field_bank": amount_field_bank, "amount_field_bank": amount_field_bank,
"amount": amount if amount else abs(ref_doc.outstanding_amount), "amount": amount if amount else abs(ref_doc.outstanding_amount),
"debit_in_account_currency": debit_in_account_currency, "debit_in_account_currency": debit_in_account_currency,
"remarks": "Payment received against {0} {1}. {2}".format(dt, dn, ref_doc.remarks), "remarks": f"Payment received against {dt} {dn}. {ref_doc.remarks}",
"is_advance": "No", "is_advance": "No",
"bank_account": bank_account, "bank_account": bank_account,
"journal_entry": journal_entry, "journal_entry": journal_entry,
@@ -1226,9 +1228,7 @@ def get_payment_entry(ref_doc, args):
) )
je = frappe.new_doc("Journal Entry") je = frappe.new_doc("Journal Entry")
je.update( je.update({"voucher_type": "Bank Entry", "company": ref_doc.company, "remark": args.get("remarks")})
{"voucher_type": "Bank Entry", "company": ref_doc.company, "remark": args.get("remarks")}
)
party_row = je.append( party_row = je.append(
"accounts", "accounts",
@@ -1251,9 +1251,7 @@ def get_payment_entry(ref_doc, args):
bank_row = je.append("accounts") bank_row = je.append("accounts")
# Make it bank_details # Make it bank_details
bank_account = get_default_bank_cash_account( bank_account = get_default_bank_cash_account(ref_doc.company, "Bank", account=args.get("bank_account"))
ref_doc.company, "Bank", account=args.get("bank_account")
)
if bank_account: if bank_account:
bank_row.update(bank_account) bank_row.update(bank_account)
# Modified to include the posting date for which the exchange rate is required. # Modified to include the posting date for which the exchange rate is required.
@@ -1293,7 +1291,7 @@ def get_against_jv(doctype, txt, searchfield, start, page_len, filters):
return [] return []
return frappe.db.sql( return frappe.db.sql(
""" f"""
SELECT jv.name, jv.posting_date, jv.user_remark SELECT jv.name, jv.posting_date, jv.user_remark
FROM `tabJournal Entry` jv, `tabJournal Entry Account` jv_detail FROM `tabJournal Entry` jv, `tabJournal Entry Account` jv_detail
WHERE jv_detail.parent = jv.name WHERE jv_detail.parent = jv.name
@@ -1304,16 +1302,14 @@ def get_against_jv(doctype, txt, searchfield, start, page_len, filters):
OR jv_detail.reference_type = '' OR jv_detail.reference_type = ''
) )
AND jv.docstatus = 1 AND jv.docstatus = 1
AND jv.`{0}` LIKE %(txt)s AND jv.`{searchfield}` LIKE %(txt)s
ORDER BY jv.name DESC ORDER BY jv.name DESC
LIMIT %(limit)s offset %(offset)s LIMIT %(limit)s offset %(offset)s
""".format( """,
searchfield
),
dict( dict(
account=filters.get("account"), account=filters.get("account"),
party=cstr(filters.get("party")), party=cstr(filters.get("party")),
txt="%{0}%".format(txt), txt=f"%{txt}%",
offset=start, offset=start,
limit=page_len, limit=page_len,
), ),
@@ -1335,19 +1331,15 @@ def get_outstanding(args):
condition = " and party=%(party)s" if args.get("party") else "" condition = " and party=%(party)s" if args.get("party") else ""
against_jv_amount = frappe.db.sql( against_jv_amount = frappe.db.sql(
""" f"""
select sum(debit_in_account_currency) - sum(credit_in_account_currency) select sum(debit_in_account_currency) - sum(credit_in_account_currency)
from `tabJournal Entry Account` where parent=%(docname)s and account=%(account)s {0} from `tabJournal Entry Account` where parent=%(docname)s and account=%(account)s {condition}
and (reference_type is null or reference_type = '')""".format( and (reference_type is null or reference_type = '')""",
condition
),
args, args,
) )
against_jv_amount = flt(against_jv_amount[0][0]) if against_jv_amount else 0 against_jv_amount = flt(against_jv_amount[0][0]) if against_jv_amount else 0
amount_field = ( amount_field = "credit_in_account_currency" if against_jv_amount > 0 else "debit_in_account_currency"
"credit_in_account_currency" if against_jv_amount > 0 else "debit_in_account_currency"
)
return {amount_field: abs(against_jv_amount)} return {amount_field: abs(against_jv_amount)}
elif args.get("doctype") in ("Sales Invoice", "Purchase Invoice"): elif args.get("doctype") in ("Sales Invoice", "Purchase Invoice"):
party_type = "Customer" if args.get("doctype") == "Sales Invoice" else "Supplier" party_type = "Customer" if args.get("doctype") == "Sales Invoice" else "Supplier"
@@ -1360,9 +1352,7 @@ def get_outstanding(args):
due_date = invoice.get("due_date") due_date = invoice.get("due_date")
exchange_rate = ( exchange_rate = invoice.conversion_rate if (args.get("account_currency") != company_currency) else 1
invoice.conversion_rate if (args.get("account_currency") != company_currency) else 1
)
if args["doctype"] == "Sales Invoice": if args["doctype"] == "Sales Invoice":
amount_field = ( amount_field = (
@@ -1400,17 +1390,13 @@ def get_party_account_and_currency(company, party_type, party):
@frappe.whitelist() @frappe.whitelist()
def get_account_details_and_party_type( def get_account_details_and_party_type(account, date, company, debit=None, credit=None, exchange_rate=None):
account, date, company, debit=None, credit=None, exchange_rate=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)
company_currency = erpnext.get_company_currency(company) company_currency = erpnext.get_company_currency(company)
account_details = frappe.db.get_value( account_details = frappe.db.get_value("Account", account, ["account_type", "account_currency"], as_dict=1)
"Account", account, ["account_type", "account_currency"], as_dict=1
)
if not account_details: if not account_details:
return return

View File

@@ -69,10 +69,8 @@ class TestJournalEntry(unittest.TestCase):
self.assertTrue( self.assertTrue(
frappe.db.sql( frappe.db.sql(
"""select name from `tabJournal Entry Account` f"""select name from `tabJournal Entry Account`
where reference_type = %s and reference_name = %s and {0}=400""".format( where reference_type = %s and reference_name = %s and {dr_or_cr}=400""",
dr_or_cr
),
(submitted_voucher.doctype, submitted_voucher.name), (submitted_voucher.doctype, submitted_voucher.name),
) )
) )
@@ -84,9 +82,8 @@ class TestJournalEntry(unittest.TestCase):
def advance_paid_testcase(self, base_jv, test_voucher, dr_or_cr): def advance_paid_testcase(self, base_jv, test_voucher, dr_or_cr):
# Test advance paid field # Test advance paid field
advance_paid = frappe.db.sql( advance_paid = frappe.db.sql(
"""select advance_paid from `tab%s` """select advance_paid from `tab{}`
where name=%s""" where name={}""".format(test_voucher.doctype, "%s"),
% (test_voucher.doctype, "%s"),
(test_voucher.name), (test_voucher.name),
) )
payment_against_order = base_jv.get("accounts")[0].get(dr_or_cr) payment_against_order = base_jv.get("accounts")[0].get(dr_or_cr)
@@ -159,9 +156,7 @@ class TestJournalEntry(unittest.TestCase):
jv.cancel() jv.cancel()
def test_multi_currency(self): def test_multi_currency(self):
jv = make_journal_entry( jv = make_journal_entry("_Test Bank USD - _TC", "_Test Bank - _TC", 100, exchange_rate=50, save=False)
"_Test Bank USD - _TC", "_Test Bank - _TC", 100, exchange_rate=50, save=False
)
jv.get("accounts")[1].credit_in_account_currency = 5000 jv.get("accounts")[1].credit_in_account_currency = 5000
jv.submit() jv.submit()
@@ -201,7 +196,7 @@ class TestJournalEntry(unittest.TestCase):
"credit", "credit",
"credit_in_account_currency", "credit_in_account_currency",
): ):
for i, gle in enumerate(gl_entries): for _i, gle in enumerate(gl_entries):
self.assertEqual(expected_values[gle.account][field], gle[field]) self.assertEqual(expected_values[gle.account][field], gle[field])
# cancel # cancel
@@ -263,7 +258,7 @@ class TestJournalEntry(unittest.TestCase):
"credit", "credit",
"credit_in_account_currency", "credit_in_account_currency",
): ):
for i, gle in enumerate(gl_entries): for _i, gle in enumerate(gl_entries):
self.assertEqual(expected_values[gle.account][field], gle[field]) self.assertEqual(expected_values[gle.account][field], gle[field])
def test_disallow_change_in_account_currency_for_a_party(self): def test_disallow_change_in_account_currency_for_a_party(self):

View File

@@ -83,7 +83,10 @@ class TestLedgerMerge(unittest.TestCase):
"account": "Indirect Income - _TC", "account": "Indirect Income - _TC",
"merge_accounts": [ "merge_accounts": [
{"account": "Indirect Test Income - _TC", "account_name": "Indirect Test Income"}, {"account": "Indirect Test Income - _TC", "account_name": "Indirect Test Income"},
{"account": "Administrative Test Income - _TC", "account_name": "Administrative Test Income"}, {
"account": "Administrative Test Income - _TC",
"account_name": "Administrative Test Income",
},
], ],
} }
).insert(ignore_permissions=True) ).insert(ignore_permissions=True)

View File

@@ -25,13 +25,11 @@ def get_loyalty_details(
condition += " and expiry_date>='%s' " % expiry_date condition += " and expiry_date>='%s' " % expiry_date
loyalty_point_details = frappe.db.sql( loyalty_point_details = frappe.db.sql(
"""select sum(loyalty_points) as loyalty_points, f"""select sum(loyalty_points) as loyalty_points,
sum(purchase_amount) as total_spent from `tabLoyalty Point Entry` sum(purchase_amount) as total_spent from `tabLoyalty Point Entry`
where customer=%s and loyalty_program=%s and posting_date <= %s where customer=%s and loyalty_program=%s and posting_date <= %s
{condition} {condition}
group by customer""".format( group by customer""",
condition=condition
),
(customer, loyalty_program, expiry_date), (customer, loyalty_program, expiry_date),
as_dict=1, as_dict=1,
) )
@@ -52,9 +50,7 @@ def get_loyalty_program_details_with_points(
include_expired_entry=False, include_expired_entry=False,
current_transaction_amount=0, current_transaction_amount=0,
): ):
lp_details = get_loyalty_program_details( lp_details = get_loyalty_program_details(customer, loyalty_program, company=company, silent=silent)
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)
lp_details.update( lp_details.update(
get_loyalty_details(customer, loyalty_program.name, expiry_date, company, include_expired_entry) get_loyalty_details(customer, loyalty_program.name, expiry_date, company, include_expired_entry)

View File

@@ -19,9 +19,7 @@ class TestLoyaltyProgram(unittest.TestCase):
create_records() create_records()
def test_loyalty_points_earned_single_tier(self): def test_loyalty_points_earned_single_tier(self):
frappe.db.set_value( frappe.db.set_value("Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty")
"Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty"
)
# create a new sales invoice # create a new sales invoice
si_original = create_sales_invoice_record() si_original = create_sales_invoice_record()
si_original.insert() si_original.insert()
@@ -69,9 +67,7 @@ class TestLoyaltyProgram(unittest.TestCase):
d.cancel() d.cancel()
def test_loyalty_points_earned_multiple_tier(self): def test_loyalty_points_earned_multiple_tier(self):
frappe.db.set_value( frappe.db.set_value("Customer", "Test Loyalty Customer", "loyalty_program", "Test Multiple Loyalty")
"Customer", "Test Loyalty Customer", "loyalty_program", "Test Multiple Loyalty"
)
# assign multiple tier program to the customer # assign multiple tier program to the customer
customer = frappe.get_doc("Customer", {"customer_name": "Test Loyalty Customer"}) customer = frappe.get_doc("Customer", {"customer_name": "Test Loyalty Customer"})
customer.loyalty_program = frappe.get_doc( customer.loyalty_program = frappe.get_doc(
@@ -128,9 +124,7 @@ class TestLoyaltyProgram(unittest.TestCase):
def test_cancel_sales_invoice(self): def test_cancel_sales_invoice(self):
"""cancelling the sales invoice should cancel the earned points""" """cancelling the sales invoice should cancel the earned points"""
frappe.db.set_value( frappe.db.set_value("Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty")
"Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty"
)
# create a new sales invoice # create a new sales invoice
si = create_sales_invoice_record() si = create_sales_invoice_record()
si.insert() si.insert()
@@ -140,7 +134,7 @@ class TestLoyaltyProgram(unittest.TestCase):
"Loyalty Point Entry", "Loyalty Point Entry",
{"invoice_type": "Sales Invoice", "invoice": si.name, "customer": si.customer}, {"invoice_type": "Sales Invoice", "invoice": si.name, "customer": si.customer},
) )
self.assertEqual(True, not (lpe is None)) self.assertEqual(True, lpe is not None)
# cancelling sales invoice # cancelling sales invoice
si.cancel() si.cancel()
@@ -148,9 +142,7 @@ class TestLoyaltyProgram(unittest.TestCase):
self.assertEqual(True, (lpe is None)) self.assertEqual(True, (lpe is None))
def test_sales_invoice_return(self): def test_sales_invoice_return(self):
frappe.db.set_value( frappe.db.set_value("Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty")
"Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty"
)
# create a new sales invoice # create a new sales invoice
si_original = create_sales_invoice_record(2) si_original = create_sales_invoice_record(2)
si_original.conversion_rate = flt(1) si_original.conversion_rate = flt(1)
@@ -346,9 +338,7 @@ def create_records():
).insert() ).insert()
# create item price # create item price
if not frappe.db.exists( if not frappe.db.exists("Item Price", {"price_list": "Standard Selling", "item_code": "Loyal Item"}):
"Item Price", {"price_list": "Standard Selling", "item_code": "Loyal Item"}
):
frappe.get_doc( frappe.get_doc(
{ {
"doctype": "Item Price", "doctype": "Item Price",

View File

@@ -37,9 +37,7 @@ class MonthlyDistribution(Document):
total = sum(flt(d.percentage_allocation) for d in self.get("percentages")) total = sum(flt(d.percentage_allocation) for d in self.get("percentages"))
if flt(total, 2) != 100.0: if flt(total, 2) != 100.0:
frappe.throw( frappe.throw(_("Percentage Allocation should be equal to 100%") + f" ({flt(total, 2)!s}%)")
_("Percentage Allocation should be equal to 100%") + " ({0}%)".format(str(flt(total, 2)))
)
def get_periodwise_distribution_data(distribution_id, period_list, periodicity): def get_periodwise_distribution_data(distribution_id, period_list, periodicity):

View File

@@ -83,9 +83,7 @@ class TestOpeningInvoiceCreationTool(FrappeTestCase):
company = "_Test Opening Invoice Company" company = "_Test Opening Invoice Company"
party_1, party_2 = make_customer("Customer A"), make_customer("Customer B") party_1, party_2 = make_customer("Customer A"), make_customer("Customer B")
old_default_receivable_account = frappe.db.get_value( old_default_receivable_account = frappe.db.get_value("Company", company, "default_receivable_account")
"Company", company, "default_receivable_account"
)
frappe.db.set_value("Company", company, "default_receivable_account", "") frappe.db.set_value("Company", company, "default_receivable_account", "")
if not frappe.db.exists("Cost Center", "_Test Opening Invoice Company - _TOIC"): if not frappe.db.exists("Cost Center", "_Test Opening Invoice Company - _TOIC"):
@@ -121,9 +119,7 @@ class TestOpeningInvoiceCreationTool(FrappeTestCase):
self.assertTrue(error_log) self.assertTrue(error_log)
# teardown # teardown
frappe.db.set_value( frappe.db.set_value("Company", company, "default_receivable_account", old_default_receivable_account)
"Company", company, "default_receivable_account", old_default_receivable_account
)
def test_renaming_of_invoice_using_invoice_number_field(self): def test_renaming_of_invoice_using_invoice_number_field(self):
company = "_Test Opening Invoice Company" company = "_Test Opening Invoice Company"
@@ -169,7 +165,7 @@ def get_opening_invoice_creation_dict(**args):
{ {
"qty": 1.0, "qty": 1.0,
"outstanding_amount": 300, "outstanding_amount": 300,
"party": args.get("party_1") or "_Test {0}".format(party), "party": args.get("party_1") or f"_Test {party}",
"item_name": "Opening Item", "item_name": "Opening Item",
"due_date": "2016-09-10", "due_date": "2016-09-10",
"posting_date": "2016-09-05", "posting_date": "2016-09-05",
@@ -179,7 +175,7 @@ def get_opening_invoice_creation_dict(**args):
{ {
"qty": 2.0, "qty": 2.0,
"outstanding_amount": 250, "outstanding_amount": 250,
"party": args.get("party_2") or "_Test {0} 1".format(party), "party": args.get("party_2") or f"_Test {party} 1",
"item_name": "Opening Item", "item_name": "Opening Item",
"due_date": "2016-09-10", "due_date": "2016-09-10",
"posting_date": "2016-09-05", "posting_date": "2016-09-05",

View File

@@ -24,7 +24,10 @@ class PartyLink(Document):
if existing_party_link: if existing_party_link:
frappe.throw( frappe.throw(
_("{} {} is already linked with {} {}").format( _("{} {} is already linked with {} {}").format(
self.primary_role, bold(self.primary_party), self.secondary_role, bold(self.secondary_party) self.primary_role,
bold(self.primary_party),
self.secondary_role,
bold(self.secondary_party),
) )
) )

View File

@@ -47,7 +47,7 @@ class InvalidPaymentEntry(ValidationError):
class PaymentEntry(AccountsController): class PaymentEntry(AccountsController):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(PaymentEntry, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
if not self.is_new(): if not self.is_new():
self.setup_party_account_field() self.setup_party_account_field()
@@ -113,7 +113,7 @@ class PaymentEntry(AccountsController):
"Unreconcile Payment", "Unreconcile Payment",
"Unreconcile Payment Entries", "Unreconcile Payment Entries",
) )
super(PaymentEntry, self).on_cancel() super().on_cancel()
self.make_gl_entries(cancel=1) self.make_gl_entries(cancel=1)
self.update_outstanding_amounts() self.update_outstanding_amounts()
self.update_advance_paid() self.update_advance_paid()
@@ -221,9 +221,7 @@ class PaymentEntry(AccountsController):
# If term based allocation is enabled, throw # If term based allocation is enabled, throw
if ( if (
d.payment_term is None or d.payment_term == "" d.payment_term is None or d.payment_term == ""
) and self.term_based_allocation_enabled_for_reference( ) and self.term_based_allocation_enabled_for_reference(d.reference_doctype, d.reference_name):
d.reference_doctype, d.reference_name
):
frappe.throw( frappe.throw(
_( _(
"{0} has Payment Term based allocation enabled. Select a Payment Term for Row #{1} in Payment References section" "{0} has Payment Term based allocation enabled. Select a Payment Term for Row #{1} in Payment References section"
@@ -235,7 +233,9 @@ class PaymentEntry(AccountsController):
# The reference has already been fully paid # The reference has already been fully paid
if not latest: if not latest:
frappe.throw( frappe.throw(
_("{0} {1} has already been fully paid.").format(_(d.reference_doctype), d.reference_name) _("{0} {1} has already been fully paid.").format(
_(d.reference_doctype), d.reference_name
)
) )
# The reference has already been partly paid # The reference has already been partly paid
elif ( elif (
@@ -259,14 +259,14 @@ class PaymentEntry(AccountsController):
and latest.payment_term_outstanding and latest.payment_term_outstanding
and (flt(d.allocated_amount) > flt(latest.payment_term_outstanding)) and (flt(d.allocated_amount) > flt(latest.payment_term_outstanding))
) )
and self.term_based_allocation_enabled_for_reference(d.reference_doctype, d.reference_name) and self.term_based_allocation_enabled_for_reference(
d.reference_doctype, d.reference_name
)
): ):
frappe.throw( frappe.throw(
_( _(
"Row #{0}: Allocated amount:{1} is greater than outstanding amount:{2} for Payment Term {3}" "Row #{0}: Allocated amount:{1} is greater than outstanding amount:{2} for Payment Term {3}"
).format( ).format(d.idx, d.allocated_amount, latest.payment_term_outstanding, d.payment_term)
d.idx, d.allocated_amount, latest.payment_term_outstanding, d.payment_term
)
) )
if (flt(d.allocated_amount)) > 0 and flt(d.allocated_amount) > flt(latest.outstanding_amount): if (flt(d.allocated_amount)) > 0 and flt(d.allocated_amount) > flt(latest.outstanding_amount):
@@ -343,7 +343,7 @@ class PaymentEntry(AccountsController):
for d in self.get("references"): for d in self.get("references"):
if d.allocated_amount: if d.allocated_amount:
if update_ref_details_only_for and ( if update_ref_details_only_for and (
not (d.reference_doctype, d.reference_name) in update_ref_details_only_for (d.reference_doctype, d.reference_name) not in update_ref_details_only_for
): ):
continue continue
@@ -390,7 +390,9 @@ class PaymentEntry(AccountsController):
else: else:
if ref_doc: if ref_doc:
if self.paid_from_account_currency == ref_doc.currency: if self.paid_from_account_currency == ref_doc.currency:
self.source_exchange_rate = ref_doc.get("exchange_rate") or ref_doc.get("conversion_rate") self.source_exchange_rate = ref_doc.get("exchange_rate") or ref_doc.get(
"conversion_rate"
)
if not self.source_exchange_rate: if not self.source_exchange_rate:
self.source_exchange_rate = get_exchange_rate( self.source_exchange_rate = get_exchange_rate(
@@ -427,7 +429,7 @@ class PaymentEntry(AccountsController):
if d.reference_doctype not in valid_reference_doctypes: if d.reference_doctype not in valid_reference_doctypes:
frappe.throw( frappe.throw(
_("Reference Doctype must be one of {0}").format( _("Reference Doctype must be one of {0}").format(
comma_or((_(d) for d in valid_reference_doctypes)) comma_or(_(d) for d in valid_reference_doctypes)
) )
) )
@@ -450,7 +452,8 @@ class PaymentEntry(AccountsController):
if d.reference_doctype in frappe.get_hooks("invoice_doctypes"): if d.reference_doctype in frappe.get_hooks("invoice_doctypes"):
if self.party_type == "Customer": if self.party_type == "Customer":
ref_party_account = ( ref_party_account = (
get_party_account_based_on_invoice_discounting(d.reference_name) or ref_doc.debit_to get_party_account_based_on_invoice_discounting(d.reference_name)
or ref_doc.debit_to
) )
elif self.party_type == "Supplier": elif self.party_type == "Supplier":
ref_party_account = ref_doc.credit_to ref_party_account = ref_doc.credit_to
@@ -460,7 +463,10 @@ class PaymentEntry(AccountsController):
if ref_party_account != self.party_account: if ref_party_account != self.party_account:
frappe.throw( frappe.throw(
_("{0} {1} is associated with {2}, but Party Account is {3}").format( _("{0} {1} is associated with {2}, but Party Account is {3}").format(
_(d.reference_doctype), d.reference_name, ref_party_account, self.party_account _(d.reference_doctype),
d.reference_name,
ref_party_account,
self.party_account,
) )
) )
@@ -471,7 +477,9 @@ class PaymentEntry(AccountsController):
) )
if ref_doc.docstatus != 1: if ref_doc.docstatus != 1:
frappe.throw(_("{0} {1} must be submitted").format(_(d.reference_doctype), d.reference_name)) frappe.throw(
_("{0} {1} must be submitted").format(_(d.reference_doctype), d.reference_name)
)
def get_valid_reference_doctypes(self): def get_valid_reference_doctypes(self):
if self.party_type == "Customer": if self.party_type == "Customer":
@@ -642,9 +650,7 @@ class PaymentEntry(AccountsController):
if not (is_single_currency and reference_is_multi_currency): if not (is_single_currency and reference_is_multi_currency):
return allocated_amount return allocated_amount
allocated_amount = flt( allocated_amount = flt(allocated_amount / ref_exchange_rate, self.precision("total_allocated_amount"))
allocated_amount / ref_exchange_rate, self.precision("total_allocated_amount")
)
return allocated_amount return allocated_amount
@@ -692,7 +698,6 @@ class PaymentEntry(AccountsController):
accounts = [] accounts = []
for d in self.taxes: for d in self.taxes:
if d.account_head == tax_withholding_details.get("account_head"): if d.account_head == tax_withholding_details.get("account_head"):
# Preserve user updated included in paid amount # Preserve user updated included in paid amount
if d.included_in_paid_amount: if d.included_in_paid_amount:
tax_withholding_details.update({"included_in_paid_amount": d.included_in_paid_amount}) tax_withholding_details.update({"included_in_paid_amount": d.included_in_paid_amount})
@@ -812,7 +817,6 @@ class PaymentEntry(AccountsController):
flt(d.allocated_amount) * flt(exchange_rate), self.precision("base_paid_amount") flt(d.allocated_amount) * flt(exchange_rate), self.precision("base_paid_amount")
) )
else: else:
# Use source/target exchange rate, so no difference amount is calculated. # Use source/target exchange rate, so no difference amount is calculated.
# then update exchange gain/loss amount in reference table # then update exchange gain/loss amount in reference table
# if there is an exchange gain/loss amount in reference table, submit a JE for that # if there is an exchange gain/loss amount in reference table, submit a JE for that
@@ -929,7 +933,9 @@ class PaymentEntry(AccountsController):
total_negative_outstanding = flt( total_negative_outstanding = flt(
sum( sum(
abs(flt(d.outstanding_amount)) for d in self.get("references") if flt(d.outstanding_amount) < 0 abs(flt(d.outstanding_amount))
for d in self.get("references")
if flt(d.outstanding_amount) < 0
), ),
self.references[0].precision("outstanding_amount") if self.references else None, self.references[0].precision("outstanding_amount") if self.references else None,
) )
@@ -982,7 +988,6 @@ class PaymentEntry(AccountsController):
) )
] ]
else: else:
remarks = [ remarks = [
_("Amount {0} {1} {2} {3}").format( _("Amount {0} {1} {2} {3}").format(
self.party_account_currency, self.party_account_currency,
@@ -1002,14 +1007,19 @@ class PaymentEntry(AccountsController):
if d.allocated_amount: if d.allocated_amount:
remarks.append( remarks.append(
_("Amount {0} {1} against {2} {3}").format( _("Amount {0} {1} against {2} {3}").format(
self.party_account_currency, d.allocated_amount, d.reference_doctype, d.reference_name self.party_account_currency,
d.allocated_amount,
d.reference_doctype,
d.reference_name,
) )
) )
for d in self.get("deductions"): for d in self.get("deductions"):
if d.amount: if d.amount:
remarks.append( remarks.append(
_("Amount {0} {1} deducted against {2}").format(self.company_currency, d.amount, d.account) _("Amount {0} {1} deducted against {2}").format(
self.company_currency, d.amount, d.account
)
) )
self.set("remarks", "\n".join(remarks)) self.set("remarks", "\n".join(remarks))
@@ -1444,7 +1454,8 @@ def get_outstanding_reference_documents(args):
return [] return []
elif supplier_status["hold_type"] == "Payments": elif supplier_status["hold_type"] == "Payments":
if ( if (
not supplier_status["release_date"] or getdate(nowdate()) <= supplier_status["release_date"] not supplier_status["release_date"]
or getdate(nowdate()) <= supplier_status["release_date"]
): ):
return [] return []
@@ -1454,7 +1465,7 @@ def get_outstanding_reference_documents(args):
# Get positive outstanding sales /purchase invoices # Get positive outstanding sales /purchase invoices
condition = "" condition = ""
if args.get("voucher_type") and args.get("voucher_no"): if args.get("voucher_type") and args.get("voucher_no"):
condition = " and voucher_type={0} and voucher_no={1}".format( condition = " and voucher_type={} and voucher_no={}".format(
frappe.db.escape(args["voucher_type"]), frappe.db.escape(args["voucher_no"]) frappe.db.escape(args["voucher_type"]), frappe.db.escape(args["voucher_no"])
) )
common_filter.append(ple.voucher_type == args["voucher_type"]) common_filter.append(ple.voucher_type == args["voucher_type"])
@@ -1469,7 +1480,7 @@ def get_outstanding_reference_documents(args):
active_dimensions = get_dimensions()[0] active_dimensions = get_dimensions()[0]
for dim in active_dimensions: for dim in active_dimensions:
if args.get(dim.fieldname): if args.get(dim.fieldname):
condition += " and {0}='{1}'".format(dim.fieldname, args.get(dim.fieldname)) condition += f" and {dim.fieldname}='{args.get(dim.fieldname)}'"
accounting_dimensions_filter.append(ple[dim.fieldname] == args.get(dim.fieldname)) accounting_dimensions_filter.append(ple[dim.fieldname] == args.get(dim.fieldname))
date_fields_dict = { date_fields_dict = {
@@ -1479,21 +1490,21 @@ def get_outstanding_reference_documents(args):
for fieldname, date_fields in date_fields_dict.items(): for fieldname, date_fields in date_fields_dict.items():
if args.get(date_fields[0]) and args.get(date_fields[1]): if args.get(date_fields[0]) and args.get(date_fields[1]):
condition += " and {0} between '{1}' and '{2}'".format( condition += " and {} between '{}' and '{}'".format(
fieldname, args.get(date_fields[0]), args.get(date_fields[1]) fieldname, args.get(date_fields[0]), args.get(date_fields[1])
) )
posting_and_due_date.append(ple[fieldname][args.get(date_fields[0]) : args.get(date_fields[1])]) posting_and_due_date.append(ple[fieldname][args.get(date_fields[0]) : args.get(date_fields[1])])
elif args.get(date_fields[0]): elif args.get(date_fields[0]):
# if only from date is supplied # if only from date is supplied
condition += " and {0} >= '{1}'".format(fieldname, args.get(date_fields[0])) condition += f" and {fieldname} >= '{args.get(date_fields[0])}'"
posting_and_due_date.append(ple[fieldname].gte(args.get(date_fields[0]))) posting_and_due_date.append(ple[fieldname].gte(args.get(date_fields[0])))
elif args.get(date_fields[1]): elif args.get(date_fields[1]):
# if only to date is supplied # if only to date is supplied
condition += " and {0} <= '{1}'".format(fieldname, args.get(date_fields[1])) condition += f" and {fieldname} <= '{args.get(date_fields[1])}'"
posting_and_due_date.append(ple[fieldname].lte(args.get(date_fields[1]))) posting_and_due_date.append(ple[fieldname].lte(args.get(date_fields[1])))
if args.get("company"): if args.get("company"):
condition += " and company = {0}".format(frappe.db.escape(args.get("company"))) condition += " and company = {}".format(frappe.db.escape(args.get("company")))
common_filter.append(ple.company == args.get("company")) common_filter.append(ple.company == args.get("company"))
outstanding_invoices = [] outstanding_invoices = []
@@ -1565,9 +1576,7 @@ def get_outstanding_reference_documents(args):
frappe.msgprint( frappe.msgprint(
_( _(
"No outstanding {0} found for the {1} {2} which qualify the filters you have specified." "No outstanding {0} found for the {1} {2} which qualify the filters you have specified."
).format( ).format(_(ref_document_type), _(args.get("party_type")).lower(), frappe.bold(args.get("party")))
_(ref_document_type), _(args.get("party_type")).lower(), frappe.bold(args.get("party"))
)
) )
return data return data
@@ -1603,12 +1612,10 @@ def split_invoices_based_on_payment_terms(outstanding_invoices, company) -> list
return outstanding_invoices_after_split return outstanding_invoices_after_split
def get_currency_data(outstanding_invoices: list, company: str = None) -> dict: def get_currency_data(outstanding_invoices: list, company: str | None = None) -> dict:
"""Get currency and conversion data for a list of invoices.""" """Get currency and conversion data for a list of invoices."""
exc_rates = frappe._dict() exc_rates = frappe._dict()
company_currency = ( company_currency = frappe.db.get_value("Company", company, "default_currency") if company else None
frappe.db.get_value("Company", company, "default_currency") if company else None
)
for doctype in ["Sales Invoice", "Purchase Invoice"]: for doctype in ["Sales Invoice", "Purchase Invoice"]:
invoices = [x.voucher_no for x in outstanding_invoices if x.voucher_type == doctype] invoices = [x.voucher_no for x in outstanding_invoices if x.voucher_type == doctype]
@@ -1703,7 +1710,7 @@ def get_orders_to_be_billed(
active_dimensions = get_dimensions()[0] active_dimensions = get_dimensions()[0]
for dim in active_dimensions: for dim in active_dimensions:
if filters.get(dim.fieldname): if filters.get(dim.fieldname):
condition += " and {0}='{1}'".format(dim.fieldname, filters.get(dim.fieldname)) condition += f" and {dim.fieldname}='{filters.get(dim.fieldname)}'"
if party_account_currency == company_currency: if party_account_currency == company_currency:
grand_total_field = "base_grand_total" grand_total_field = "base_grand_total"
@@ -1852,18 +1859,14 @@ def get_account_details(account, date, cost_center=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
account_list = frappe.get_list( account_list = frappe.get_list("Account", {"name": account}, reference_doctype="Payment Entry", limit=1)
"Account", {"name": account}, reference_doctype="Payment Entry", limit=1
)
# There might be some user permissions which will allow account under certain doctypes # There might be some user permissions which will allow account under certain doctypes
# except for Payment Entry, only in such case we should throw permission error # except for Payment Entry, only in such case we should throw permission error
if not account_list: if not account_list:
frappe.throw(_("Account: {0} is not permitted under Payment Entry").format(account)) frappe.throw(_("Account: {0} is not permitted under Payment Entry").format(account))
account_balance = get_balance_on( account_balance = get_balance_on(account, date, cost_center=cost_center, ignore_account_permission=True)
account, date, cost_center=cost_center, ignore_account_permission=True
)
return frappe._dict( return frappe._dict(
{ {
@@ -1910,9 +1913,7 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
total_amount = outstanding_amount = exchange_rate = None total_amount = outstanding_amount = exchange_rate = None
ref_doc = frappe.get_doc(reference_doctype, reference_name) ref_doc = frappe.get_doc(reference_doctype, reference_name)
company_currency = ref_doc.get("company_currency") or erpnext.get_company_currency( company_currency = ref_doc.get("company_currency") or erpnext.get_company_currency(ref_doc.company)
ref_doc.company
)
if reference_doctype == "Dunning": if reference_doctype == "Dunning":
total_amount = outstanding_amount = ref_doc.get("dunning_amount") total_amount = outstanding_amount = ref_doc.get("dunning_amount")
@@ -1921,9 +1922,7 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
elif reference_doctype == "Journal Entry" and ref_doc.docstatus == 1: elif reference_doctype == "Journal Entry" and ref_doc.docstatus == 1:
total_amount = ref_doc.get("total_amount") total_amount = ref_doc.get("total_amount")
if ref_doc.multi_currency: if ref_doc.multi_currency:
exchange_rate = get_exchange_rate( exchange_rate = get_exchange_rate(party_account_currency, company_currency, ref_doc.posting_date)
party_account_currency, company_currency, ref_doc.posting_date
)
else: else:
exchange_rate = 1 exchange_rate = 1
outstanding_amount = get_outstanding_on_journal_entry(reference_name) outstanding_amount = get_outstanding_on_journal_entry(reference_name)
@@ -1981,9 +1980,7 @@ def get_payment_entry(
): ):
doc = frappe.get_doc(dt, dn) doc = frappe.get_doc(dt, dn)
over_billing_allowance = frappe.db.get_single_value("Accounts Settings", "over_billing_allowance") over_billing_allowance = frappe.db.get_single_value("Accounts Settings", "over_billing_allowance")
if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) >= ( if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) >= (100.0 + over_billing_allowance):
100.0 + over_billing_allowance
):
frappe.throw(_("Can only make payment against unbilled {0}").format(_(dt))) frappe.throw(_("Can only make payment against unbilled {0}").format(_(dt)))
if not party_type: if not party_type:
@@ -2036,9 +2033,7 @@ def get_payment_entry(
pe.paid_from_account_currency = ( pe.paid_from_account_currency = (
party_account_currency if payment_type == "Receive" else bank.account_currency party_account_currency if payment_type == "Receive" else bank.account_currency
) )
pe.paid_to_account_currency = ( pe.paid_to_account_currency = party_account_currency if payment_type == "Pay" else bank.account_currency
party_account_currency if payment_type == "Pay" else bank.account_currency
)
pe.paid_amount = paid_amount pe.paid_amount = paid_amount
pe.received_amount = received_amount pe.received_amount = received_amount
pe.letter_head = doc.get("letter_head") pe.letter_head = doc.get("letter_head")
@@ -2067,7 +2062,6 @@ def get_payment_entry(
{"name": doc.payment_terms_template}, {"name": doc.payment_terms_template},
"allocate_payment_based_on_payment_terms", "allocate_payment_based_on_payment_terms",
): ):
for reference in get_reference_as_per_payment_terms( for reference in get_reference_as_per_payment_terms(
doc.payment_schedule, dt, dn, doc, grand_total, outstanding_amount, party_account_currency doc.payment_schedule, dt, dn, doc, grand_total, outstanding_amount, party_account_currency
): ):
@@ -2188,9 +2182,9 @@ def set_party_account_currency(dt, party_account, doc):
def set_payment_type(dt, doc): def set_payment_type(dt, doc):
if ( if (dt == "Sales Order" or (dt in ("Sales Invoice", "Dunning") and doc.outstanding_amount > 0)) or (
dt == "Sales Order" or (dt in ("Sales Invoice", "Dunning") and doc.outstanding_amount > 0) dt == "Purchase Invoice" and doc.outstanding_amount < 0
) or (dt == "Purchase Invoice" and doc.outstanding_amount < 0): ):
payment_type = "Receive" payment_type = "Receive"
else: else:
payment_type = "Pay" payment_type = "Pay"
@@ -2250,9 +2244,7 @@ def set_paid_amount_and_received_amount(
return paid_amount, received_amount return paid_amount, received_amount
def apply_early_payment_discount( def apply_early_payment_discount(paid_amount, received_amount, doc, party_account_currency, reference_date):
paid_amount, received_amount, doc, party_account_currency, reference_date
):
total_discount = 0 total_discount = 0
valid_discounts = [] valid_discounts = []
eligible_for_payments = ["Sales Order", "Sales Invoice", "Purchase Order", "Purchase Invoice"] eligible_for_payments = ["Sales Order", "Sales Invoice", "Purchase Order", "Purchase Invoice"]
@@ -2262,7 +2254,6 @@ def apply_early_payment_discount(
if doc.doctype in eligible_for_payments and has_payment_schedule: if doc.doctype in eligible_for_payments and has_payment_schedule:
for term in doc.payment_schedule: for term in doc.payment_schedule:
if not term.discounted_amount and term.discount and reference_date <= term.discount_date: if not term.discounted_amount and term.discount and reference_date <= term.discount_date:
if term.discount_type == "Percentage": if term.discount_type == "Percentage":
grand_total = doc.get("grand_total") if is_multi_currency else doc.get("base_grand_total") grand_total = doc.get("grand_total") if is_multi_currency else doc.get("base_grand_total")
discount_amount = flt(grand_total) * (term.discount / 100) discount_amount = flt(grand_total) * (term.discount / 100)
@@ -2291,9 +2282,7 @@ def apply_early_payment_discount(
return paid_amount, received_amount, total_discount, valid_discounts return paid_amount, received_amount, total_discount, valid_discounts
def set_pending_discount_loss( def set_pending_discount_loss(pe, doc, discount_amount, base_total_discount_loss, party_account_currency):
pe, doc, discount_amount, base_total_discount_loss, party_account_currency
):
# If multi-currency, get base discount amount to adjust with base currency deductions/losses # If multi-currency, get base discount amount to adjust with base currency deductions/losses
if party_account_currency != doc.company_currency: if party_account_currency != doc.company_currency:
discount_amount = discount_amount * doc.get("conversion_rate", 1) discount_amount = discount_amount * doc.get("conversion_rate", 1)
@@ -2313,7 +2302,8 @@ def set_pending_discount_loss(
pe.set_gain_or_loss( pe.set_gain_or_loss(
account_details={ account_details={
"account": frappe.get_cached_value("Company", pe.company, account_type), "account": frappe.get_cached_value("Company", pe.company, account_type),
"cost_center": pe.cost_center or frappe.get_cached_value("Company", pe.company, "cost_center"), "cost_center": pe.cost_center
or frappe.get_cached_value("Company", pe.company, "cost_center"),
"amount": discount_amount * positive_negative, "amount": discount_amount * positive_negative,
} }
) )
@@ -2336,9 +2326,7 @@ def split_early_payment_discount_loss(pe, doc, valid_discounts) -> float:
def get_total_discount_percent(doc, valid_discounts) -> float: def get_total_discount_percent(doc, valid_discounts) -> float:
"""Get total percentage and amount discount applied as a percentage.""" """Get total percentage and amount discount applied as a percentage."""
total_discount_percent = ( total_discount_percent = (
sum( sum(discount.get("discount") for discount in valid_discounts if discount.get("type") == "Percentage")
discount.get("discount") for discount in valid_discounts if discount.get("type") == "Percentage"
)
or 0.0 or 0.0
) )
@@ -2381,9 +2369,7 @@ def add_tax_discount_loss(pe, doc, total_discount_percentage) -> float:
# The same account head could be used more than once # The same account head could be used more than once
for tax in doc.get("taxes", []): for tax in doc.get("taxes", []):
base_tax_loss = tax.get("base_tax_amount_after_discount_amount") * ( base_tax_loss = tax.get("base_tax_amount_after_discount_amount") * (total_discount_percentage / 100)
total_discount_percentage / 100
)
account = tax.get("account_head") account = tax.get("account_head")
if not tax_discount_loss.get(account): if not tax_discount_loss.get(account):
@@ -2400,7 +2386,8 @@ def add_tax_discount_loss(pe, doc, total_discount_percentage) -> float:
"deductions", "deductions",
{ {
"account": account, "account": account,
"cost_center": pe.cost_center or frappe.get_cached_value("Company", pe.company, "cost_center"), "cost_center": pe.cost_center
or frappe.get_cached_value("Company", pe.company, "cost_center"),
"amount": flt(loss, precision), "amount": flt(loss, precision),
}, },
) )
@@ -2423,7 +2410,8 @@ def get_reference_as_per_payment_terms(
if not is_multi_currency_acc: if not is_multi_currency_acc:
# If accounting is done in company currency for multi-currency transaction # If accounting is done in company currency for multi-currency transaction
payment_term_outstanding = flt( payment_term_outstanding = flt(
payment_term_outstanding * doc.get("conversion_rate"), payment_term.precision("payment_amount") payment_term_outstanding * doc.get("conversion_rate"),
payment_term.precision("payment_amount"),
) )
if payment_term_outstanding: if payment_term_outstanding:
@@ -2451,7 +2439,7 @@ def get_paid_amount(dt, dn, party_type, party, account, due_date):
dr_or_cr = "debit_in_account_currency - credit_in_account_currency" dr_or_cr = "debit_in_account_currency - credit_in_account_currency"
paid_amount = frappe.db.sql( paid_amount = frappe.db.sql(
""" f"""
select ifnull(sum({dr_or_cr}), 0) as paid_amount select ifnull(sum({dr_or_cr}), 0) as paid_amount
from `tabGL Entry` from `tabGL Entry`
where against_voucher_type = %s where against_voucher_type = %s
@@ -2461,9 +2449,7 @@ def get_paid_amount(dt, dn, party_type, party, account, due_date):
and account = %s and account = %s
and due_date = %s and due_date = %s
and {dr_or_cr} > 0 and {dr_or_cr} > 0
""".format( """,
dr_or_cr=dr_or_cr
),
(dt, dn, party_type, party, account, due_date), (dt, dn, party_type, party, account, due_date),
) )

View File

@@ -2,7 +2,6 @@
# See license.txt # See license.txt
import json import json
import unittest
import frappe import frappe
from frappe import qb from frappe import qb
@@ -163,7 +162,7 @@ class TestPaymentEntry(FrappeTestCase):
supplier.on_hold = 0 supplier.on_hold = 0
supplier.save() supplier.save()
except: except Exception:
pass pass
else: else:
raise Exception raise Exception
@@ -470,9 +469,7 @@ class TestPaymentEntry(FrappeTestCase):
si.save() si.save()
si.submit() si.submit()
pe = get_payment_entry( pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC", bank_amount=4700)
"Sales Invoice", si.name, bank_account="_Test Bank - _TC", bank_amount=4700
)
pe.reference_no = si.name pe.reference_no = si.name
pe.reference_date = nowdate() pe.reference_date = nowdate()
@@ -639,9 +636,7 @@ class TestPaymentEntry(FrappeTestCase):
pe.set_exchange_rate() pe.set_exchange_rate()
pe.set_amounts() pe.set_amounts()
self.assertEqual( self.assertEqual(pe.source_exchange_rate, 65.1, f"{pe.source_exchange_rate} is not equal to {65.1}")
pe.source_exchange_rate, 65.1, "{0} is not equal to {1}".format(pe.source_exchange_rate, 65.1)
)
def test_internal_transfer_usd_to_inr(self): def test_internal_transfer_usd_to_inr(self):
pe = frappe.new_doc("Payment Entry") pe = frappe.new_doc("Payment Entry")
@@ -910,9 +905,7 @@ class TestPaymentEntry(FrappeTestCase):
cost_center = "_Test Cost Center for BS Account - _TC" cost_center = "_Test Cost Center for BS Account - _TC"
create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company") create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
pi = make_purchase_invoice_against_cost_center( pi = make_purchase_invoice_against_cost_center(cost_center=cost_center, credit_to="Creditors - _TC")
cost_center=cost_center, credit_to="Creditors - _TC"
)
pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC") pe = get_payment_entry("Purchase Invoice", pi.name, bank_account="_Test Bank - _TC")
self.assertEqual(pe.cost_center, pi.cost_center) self.assertEqual(pe.cost_center, pi.cost_center)
@@ -953,9 +946,7 @@ class TestPaymentEntry(FrappeTestCase):
si = create_sales_invoice_against_cost_center(cost_center=cost_center, debit_to="Debtors - _TC") si = create_sales_invoice_against_cost_center(cost_center=cost_center, debit_to="Debtors - _TC")
account_balance = get_balance_on(account="_Test Bank - _TC", cost_center=si.cost_center) account_balance = get_balance_on(account="_Test Bank - _TC", cost_center=si.cost_center)
party_balance = get_balance_on( party_balance = get_balance_on(party_type="Customer", party=si.customer, cost_center=si.cost_center)
party_type="Customer", party=si.customer, cost_center=si.cost_center
)
party_account_balance = get_balance_on(si.debit_to, cost_center=si.cost_center) party_account_balance = get_balance_on(si.debit_to, cost_center=si.cost_center)
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC") pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank - _TC")
@@ -1214,7 +1205,7 @@ class TestPaymentEntry(FrappeTestCase):
Overallocation validation shouldn't fire for Template without "Allocate Payment based on Payment Terms" enabled Overallocation validation shouldn't fire for Template without "Allocate Payment based on Payment Terms" enabled
""" """
customer = create_customer() create_customer()
create_payment_terms_template() create_payment_terms_template()
template = frappe.get_doc("Payment Terms Template", "Test Receivable Template") template = frappe.get_doc("Payment Terms Template", "Test Receivable Template")
@@ -1273,9 +1264,7 @@ class TestPaymentEntry(FrappeTestCase):
create_payment_terms_template() create_payment_terms_template()
# SI has an earlier due date and SI2 has a later due date # SI has an earlier due date and SI2 has a later due date
si = create_sales_invoice( si = create_sales_invoice(qty=1, rate=100, customer=customer, posting_date=add_days(nowdate(), -4))
qty=1, rate=100, customer=customer, posting_date=add_days(nowdate(), -4)
)
si2 = create_sales_invoice(do_not_save=1, qty=1, rate=100, customer=customer) si2 = create_sales_invoice(do_not_save=1, qty=1, rate=100, customer=customer)
si2.payment_terms_template = "Test Receivable Template" si2.payment_terms_template = "Test Receivable Template"
si2.submit() si2.submit()
@@ -1401,12 +1390,11 @@ def create_payment_entry(**args):
def create_payment_terms_template(): def create_payment_terms_template():
create_payment_term("Basic Amount Receivable") create_payment_term("Basic Amount Receivable")
create_payment_term("Tax Receivable") create_payment_term("Tax Receivable")
if not frappe.db.exists("Payment Terms Template", "Test Receivable Template"): if not frappe.db.exists("Payment Terms Template", "Test Receivable Template"):
payment_term_template = frappe.get_doc( frappe.get_doc(
{ {
"doctype": "Payment Terms Template", "doctype": "Payment Terms Template",
"template_name": "Test Receivable Template", "template_name": "Test Receivable Template",

View File

@@ -108,9 +108,9 @@ class PaymentLedgerEntry(Document):
): ):
if not self.get(dimension.fieldname): if not self.get(dimension.fieldname):
frappe.throw( frappe.throw(
_("Accounting Dimension <b>{0}</b> is required for 'Profit and Loss' account {1}.").format( _(
dimension.label, self.account "Accounting Dimension <b>{0}</b> is required for 'Profit and Loss' account {1}."
) ).format(dimension.label, self.account)
) )
if ( if (
@@ -121,9 +121,9 @@ class PaymentLedgerEntry(Document):
): ):
if not self.get(dimension.fieldname): if not self.get(dimension.fieldname):
frappe.throw( frappe.throw(
_("Accounting Dimension <b>{0}</b> is required for 'Balance Sheet' account {1}.").format( _(
dimension.label, self.account "Accounting Dimension <b>{0}</b> is required for 'Balance Sheet' account {1}."
) ).format(dimension.label, self.account)
) )
def validate(self): def validate(self):

View File

@@ -84,11 +84,14 @@ class TestPaymentLedgerEntry(FrappeTestCase):
self.customer = customer.name self.customer = customer.name
def create_sales_invoice( def create_sales_invoice(
self, qty=1, rate=100, posting_date=nowdate(), do_not_save=False, do_not_submit=False self, qty=1, rate=100, posting_date=None, do_not_save=False, do_not_submit=False
): ):
""" """
Helper function to populate default values in sales invoice Helper function to populate default values in sales invoice
""" """
if posting_date is None:
posting_date = nowdate()
sinv = create_sales_invoice( sinv = create_sales_invoice(
qty=qty, qty=qty,
rate=rate, rate=rate,
@@ -112,10 +115,12 @@ class TestPaymentLedgerEntry(FrappeTestCase):
) )
return sinv return sinv
def create_payment_entry(self, amount=100, posting_date=nowdate()): def create_payment_entry(self, amount=100, posting_date=None):
""" """
Helper function to populate default values in payment entry Helper function to populate default values in payment entry
""" """
if posting_date is None:
posting_date = nowdate()
payment = create_payment_entry( payment = create_payment_entry(
company=self.company, company=self.company,
payment_type="Receive", payment_type="Receive",
@@ -128,9 +133,10 @@ class TestPaymentLedgerEntry(FrappeTestCase):
payment.posting_date = posting_date payment.posting_date = posting_date
return payment return payment
def create_sales_order( def create_sales_order(self, qty=1, rate=100, posting_date=None, do_not_save=False, do_not_submit=False):
self, qty=1, rate=100, posting_date=nowdate(), do_not_save=False, do_not_submit=False if posting_date is None:
): posting_date = nowdate()
so = make_sales_order( so = make_sales_order(
company=self.company, company=self.company,
transaction_date=posting_date, transaction_date=posting_date,
@@ -159,9 +165,7 @@ class TestPaymentLedgerEntry(FrappeTestCase):
for doctype in doctype_list: for doctype in doctype_list:
qb.from_(qb.DocType(doctype)).delete().where(qb.DocType(doctype).company == self.company).run() qb.from_(qb.DocType(doctype)).delete().where(qb.DocType(doctype).company == self.company).run()
def create_journal_entry( def create_journal_entry(self, acc1=None, acc2=None, amount=0, posting_date=None, cost_center=None):
self, acc1=None, acc2=None, amount=0, posting_date=None, cost_center=None
):
je = frappe.new_doc("Journal Entry") je = frappe.new_doc("Journal Entry")
je.posting_date = posting_date or nowdate() je.posting_date = posting_date or nowdate()
je.company = self.company je.company = self.company
@@ -319,9 +323,7 @@ class TestPaymentLedgerEntry(FrappeTestCase):
ple.amount, ple.amount,
ple.delinked, ple.delinked,
) )
.where( .where((ple.against_voucher_type == cr_note1.doctype) & (ple.against_voucher_no == cr_note1.name))
(ple.against_voucher_type == cr_note1.doctype) & (ple.against_voucher_no == cr_note1.name)
)
.orderby(ple.creation) .orderby(ple.creation)
.run(as_dict=True) .run(as_dict=True)
) )
@@ -362,9 +364,7 @@ class TestPaymentLedgerEntry(FrappeTestCase):
) )
cr_note2.is_return = 1 cr_note2.is_return = 1
cr_note2 = cr_note2.save().submit() cr_note2 = cr_note2.save().submit()
je1 = self.create_journal_entry( je1 = self.create_journal_entry(self.debit_to, self.debit_to, amount, posting_date=transaction_date)
self.debit_to, self.debit_to, amount, posting_date=transaction_date
)
je1.get("accounts")[0].party_type = je1.get("accounts")[1].party_type = "Customer" je1.get("accounts")[0].party_type = je1.get("accounts")[1].party_type = "Customer"
je1.get("accounts")[0].party = je1.get("accounts")[1].party = self.customer je1.get("accounts")[0].party = je1.get("accounts")[1].party = self.customer
je1.get("accounts")[0].reference_type = cr_note2.doctype je1.get("accounts")[0].reference_type = cr_note2.doctype
@@ -419,9 +419,7 @@ class TestPaymentLedgerEntry(FrappeTestCase):
ple.amount, ple.amount,
ple.delinked, ple.delinked,
) )
.where( .where((ple.against_voucher_type == cr_note2.doctype) & (ple.against_voucher_no == cr_note2.name))
(ple.against_voucher_type == cr_note2.doctype) & (ple.against_voucher_no == cr_note2.name)
)
.orderby(ple.creation) .orderby(ple.creation)
.run(as_dict=True) .run(as_dict=True)
) )
@@ -518,7 +516,7 @@ class TestPaymentLedgerEntry(FrappeTestCase):
amount = 100 amount = 100
so = self.create_sales_order(qty=1, rate=amount, posting_date=transaction_date).save().submit() so = self.create_sales_order(qty=1, rate=amount, posting_date=transaction_date).save().submit()
pe = get_payment_entry(so.doctype, so.name).save().submit() get_payment_entry(so.doctype, so.name).save().submit()
so.reload() so.reload()
so.cancel() so.cancel()

View File

@@ -66,9 +66,7 @@ def make_journal_entry(doc, supplier, mode_of_payment=None):
je = frappe.new_doc("Journal Entry") je = frappe.new_doc("Journal Entry")
je.payment_order = doc.name je.payment_order = doc.name
je.posting_date = nowdate() je.posting_date = nowdate()
mode_of_payment_type = frappe._dict( mode_of_payment_type = frappe._dict(frappe.get_all("Mode of Payment", fields=["name", "type"], as_list=1))
frappe.get_all("Mode of Payment", fields=["name", "type"], as_list=1)
)
je.voucher_type = "Bank Entry" je.voucher_type = "Bank Entry"
if mode_of_payment and mode_of_payment_type.get(mode_of_payment) == "Cash": if mode_of_payment and mode_of_payment_type.get(mode_of_payment) == "Cash":

View File

@@ -1,7 +1,6 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt # See license.txt
import unittest
import frappe import frappe
from frappe.tests.utils import FrappeTestCase from frappe.tests.utils import FrappeTestCase
@@ -41,9 +40,7 @@ class TestPaymentOrder(FrappeTestCase):
payment_entry.insert() payment_entry.insert()
payment_entry.submit() payment_entry.submit()
doc = create_payment_order_against_payment_entry( doc = create_payment_order_against_payment_entry(payment_entry, "Payment Entry", self.bank_account)
payment_entry, "Payment Entry", self.bank_account
)
reference_doc = doc.get("references")[0] reference_doc = doc.get("references")[0]
self.assertEqual(reference_doc.reference_name, payment_entry.name) self.assertEqual(reference_doc.reference_name, payment_entry.name)
self.assertEqual(reference_doc.reference_doctype, "Payment Entry") self.assertEqual(reference_doc.reference_doctype, "Payment Entry")

View File

@@ -25,7 +25,7 @@ from erpnext.controllers.accounts_controller import get_advance_payment_entries_
class PaymentReconciliation(Document): class PaymentReconciliation(Document):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(PaymentReconciliation, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.common_filter_conditions = [] self.common_filter_conditions = []
self.accounting_dimension_filter_conditions = [] self.accounting_dimension_filter_conditions = []
self.ple_posting_date_filter = [] self.ple_posting_date_filter = []
@@ -219,7 +219,6 @@ class PaymentReconciliation(Document):
self.return_invoices = self.return_invoices_query.run(as_dict=True) self.return_invoices = self.return_invoices_query.run(as_dict=True)
def get_dr_or_cr_notes(self): def get_dr_or_cr_notes(self):
self.build_qb_filter_conditions(get_return_invoices=True) self.build_qb_filter_conditions(get_return_invoices=True)
ple = qb.DocType("Payment Ledger Entry") ple = qb.DocType("Payment Ledger Entry")
@@ -340,9 +339,7 @@ class PaymentReconciliation(Document):
payment_entry[0].get("reference_name") payment_entry[0].get("reference_name")
) )
new_difference_amount = self.get_difference_amount( new_difference_amount = self.get_difference_amount(payment_entry[0], invoice[0], allocated_amount)
payment_entry[0], invoice[0], allocated_amount
)
return new_difference_amount return new_difference_amount
@frappe.whitelist() @frappe.whitelist()
@@ -460,9 +457,9 @@ class PaymentReconciliation(Document):
if running_doc: if running_doc:
frappe.throw( frappe.throw(
_("A Reconciliation Job {0} is running for the same filters. Cannot reconcile now").format( _(
get_link_to_form("Auto Reconcile", running_doc) "A Reconciliation Job {0} is running for the same filters. Cannot reconcile now"
) ).format(get_link_to_form("Auto Reconcile", running_doc))
) )
return return
@@ -555,9 +552,7 @@ class PaymentReconciliation(Document):
invoice_exchange_map.update(purchase_invoice_map) invoice_exchange_map.update(purchase_invoice_map)
journals = [ journals = [d.get("invoice_number") for d in invoices if d.get("invoice_type") == "Journal Entry"]
d.get("invoice_number") for d in invoices if d.get("invoice_type") == "Journal Entry"
]
journals.extend( journals.extend(
[d.get("reference_name") for d in payments if d.get("reference_type") == "Journal Entry"] [d.get("reference_name") for d in payments if d.get("reference_type") == "Journal Entry"]
) )
@@ -677,7 +672,7 @@ class PaymentReconciliation(Document):
def get_journal_filter_conditions(self): def get_journal_filter_conditions(self):
conditions = [] conditions = []
je = qb.DocType("Journal Entry") je = qb.DocType("Journal Entry")
jea = qb.DocType("Journal Entry Account") qb.DocType("Journal Entry Account")
conditions.append(je.company == self.company) conditions.append(je.company == self.company)
if self.from_payment_date: if self.from_payment_date:
@@ -797,7 +792,7 @@ def adjust_allocations_for_taxes(doc):
@frappe.whitelist() @frappe.whitelist()
def get_queries_for_dimension_filters(company: str = None): def get_queries_for_dimension_filters(company: str | None = None):
dimensions_with_filters = [] dimensions_with_filters = []
for d in get_dimensions()[0]: for d in get_dimensions()[0]:
filters = {} filters = {}

View File

@@ -1,7 +1,6 @@
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt # See license.txt
import unittest
import frappe import frappe
from frappe import qb from frappe import qb
@@ -127,11 +126,14 @@ class TestPaymentReconciliation(FrappeTestCase):
setattr(self, x.attribute, acc.name) setattr(self, x.attribute, acc.name)
def create_sales_invoice( def create_sales_invoice(
self, qty=1, rate=100, posting_date=nowdate(), do_not_save=False, do_not_submit=False self, qty=1, rate=100, posting_date=None, do_not_save=False, do_not_submit=False
): ):
""" """
Helper function to populate default values in sales invoice Helper function to populate default values in sales invoice
""" """
if posting_date is None:
posting_date = nowdate()
sinv = create_sales_invoice( sinv = create_sales_invoice(
qty=qty, qty=qty,
rate=rate, rate=rate,
@@ -155,10 +157,13 @@ class TestPaymentReconciliation(FrappeTestCase):
) )
return sinv return sinv
def create_payment_entry(self, amount=100, posting_date=nowdate(), customer=None): def create_payment_entry(self, amount=100, posting_date=None, customer=None):
""" """
Helper function to populate default values in payment entry Helper function to populate default values in payment entry
""" """
if posting_date is None:
posting_date = nowdate()
payment = create_payment_entry( payment = create_payment_entry(
company=self.company, company=self.company,
payment_type="Receive", payment_type="Receive",
@@ -172,11 +177,14 @@ class TestPaymentReconciliation(FrappeTestCase):
return payment return payment
def create_purchase_invoice( def create_purchase_invoice(
self, qty=1, rate=100, posting_date=nowdate(), do_not_save=False, do_not_submit=False self, qty=1, rate=100, posting_date=None, do_not_save=False, do_not_submit=False
): ):
""" """
Helper function to populate default values in sales invoice Helper function to populate default values in sales invoice
""" """
if posting_date is None:
posting_date = nowdate()
pinv = make_purchase_invoice( pinv = make_purchase_invoice(
qty=qty, qty=qty,
rate=rate, rate=rate,
@@ -201,11 +209,14 @@ class TestPaymentReconciliation(FrappeTestCase):
return pinv return pinv
def create_purchase_order( def create_purchase_order(
self, qty=1, rate=100, posting_date=nowdate(), do_not_save=False, do_not_submit=False self, qty=1, rate=100, posting_date=None, do_not_save=False, do_not_submit=False
): ):
""" """
Helper function to populate default values in sales invoice Helper function to populate default values in sales invoice
""" """
if posting_date is None:
posting_date = nowdate()
pord = create_purchase_order( pord = create_purchase_order(
qty=qty, qty=qty,
rate=rate, rate=rate,
@@ -250,9 +261,7 @@ class TestPaymentReconciliation(FrappeTestCase):
pr.from_invoice_date = pr.to_invoice_date = pr.from_payment_date = pr.to_payment_date = nowdate() pr.from_invoice_date = pr.to_invoice_date = pr.from_payment_date = pr.to_payment_date = nowdate()
return pr return pr
def create_journal_entry( def create_journal_entry(self, acc1=None, acc2=None, amount=0, posting_date=None, cost_center=None):
self, acc1=None, acc2=None, amount=0, posting_date=None, cost_center=None
):
je = frappe.new_doc("Journal Entry") je = frappe.new_doc("Journal Entry")
je.posting_date = posting_date or nowdate() je.posting_date = posting_date or nowdate()
je.company = self.company je.company = self.company
@@ -402,7 +411,7 @@ class TestPaymentReconciliation(FrappeTestCase):
rate = 100 rate = 100
invoices = [] invoices = []
payments = [] payments = []
for i in range(5): for _i in range(5):
invoices.append(self.create_sales_invoice(qty=1, rate=rate, posting_date=transaction_date)) invoices.append(self.create_sales_invoice(qty=1, rate=rate, posting_date=transaction_date))
pe = self.create_payment_entry(amount=rate, posting_date=transaction_date).save().submit() pe = self.create_payment_entry(amount=rate, posting_date=transaction_date).save().submit()
payments.append(pe) payments.append(pe)
@@ -821,9 +830,7 @@ class TestPaymentReconciliation(FrappeTestCase):
cr_note.cancel() cr_note.cancel()
pay = self.create_payment_entry( pay = self.create_payment_entry(amount=amount, posting_date=transaction_date, customer=self.customer3)
amount=amount, posting_date=transaction_date, customer=self.customer3
)
pay.paid_from = self.debtors_eur pay.paid_from = self.debtors_eur
pay.paid_from_account_currency = "EUR" pay.paid_from_account_currency = "EUR"
pay.source_exchange_rate = exchange_rate pay.source_exchange_rate = exchange_rate
@@ -1025,9 +1032,7 @@ class TestPaymentReconciliation(FrappeTestCase):
rate = 100 rate = 100
# 'Main - PR' Cost Center # 'Main - PR' Cost Center
si1 = self.create_sales_invoice( si1 = self.create_sales_invoice(qty=1, rate=rate, posting_date=transaction_date, do_not_submit=True)
qty=1, rate=rate, posting_date=transaction_date, do_not_submit=True
)
si1.cost_center = self.main_cc.name si1.cost_center = self.main_cc.name
si1.submit() si1.submit()
@@ -1043,9 +1048,7 @@ class TestPaymentReconciliation(FrappeTestCase):
je1 = je1.save().submit() je1 = je1.save().submit()
# 'Sub - PR' Cost Center # 'Sub - PR' Cost Center
si2 = self.create_sales_invoice( si2 = self.create_sales_invoice(qty=1, rate=rate, posting_date=transaction_date, do_not_submit=True)
qty=1, rate=rate, posting_date=transaction_date, do_not_submit=True
)
si2.cost_center = self.sub_cc.name si2.cost_center = self.sub_cc.name
si2.submit() si2.submit()

View File

@@ -50,7 +50,7 @@ class PaymentRequest(Document):
) )
ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name) ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
if not hasattr(ref_doc, "order_type") or getattr(ref_doc, "order_type") != "Shopping Cart": if not hasattr(ref_doc, "order_type") or ref_doc.order_type != "Shopping Cart":
ref_amount = get_amount(ref_doc, self.payment_account) ref_amount = get_amount(ref_doc, self.payment_account)
if existing_payment_request_amount + flt(self.grand_total) > ref_amount: if existing_payment_request_amount + flt(self.grand_total) > ref_amount:
@@ -103,7 +103,7 @@ class PaymentRequest(Document):
ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name) ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
if ( if (
hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart" hasattr(ref_doc, "order_type") and ref_doc.order_type == "Shopping Cart"
) or self.flags.mute_email: ) or self.flags.mute_email:
send_mail = False send_mail = False
@@ -155,7 +155,7 @@ class PaymentRequest(Document):
def make_invoice(self): def make_invoice(self):
ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name) ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
if hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart": if hasattr(ref_doc, "order_type") and ref_doc.order_type == "Shopping Cart":
from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice
si = make_sales_invoice(self.reference_name, ignore_permissions=True) si = make_sales_invoice(self.reference_name, ignore_permissions=True)
@@ -241,14 +241,10 @@ class PaymentRequest(Document):
else: else:
party_account = get_party_account("Customer", ref_doc.get("customer"), ref_doc.company) party_account = get_party_account("Customer", ref_doc.get("customer"), ref_doc.company)
party_account_currency = ref_doc.get("party_account_currency") or get_account_currency( party_account_currency = ref_doc.get("party_account_currency") or get_account_currency(party_account)
party_account
)
bank_amount = self.grand_total bank_amount = self.grand_total
if ( if party_account_currency == ref_doc.company_currency and party_account_currency != self.currency:
party_account_currency == ref_doc.company_currency and party_account_currency != self.currency
):
party_amount = ref_doc.get("base_rounded_total") or ref_doc.get("base_grand_total") party_amount = ref_doc.get("base_rounded_total") or ref_doc.get("base_grand_total")
else: else:
party_amount = self.grand_total party_amount = self.grand_total
@@ -266,7 +262,7 @@ class PaymentRequest(Document):
"mode_of_payment": self.mode_of_payment, "mode_of_payment": self.mode_of_payment,
"reference_no": self.name, "reference_no": self.name,
"reference_date": nowdate(), "reference_date": nowdate(),
"remarks": "Payment Entry against {0} {1} via Payment Request {2}".format( "remarks": "Payment Entry against {} {} via Payment Request {}".format(
self.reference_doctype, self.reference_name, self.name self.reference_doctype, self.reference_name, self.name
), ),
} }
@@ -380,14 +376,13 @@ class PaymentRequest(Document):
and hasattr(frappe.local, "session") and hasattr(frappe.local, "session")
and frappe.local.session.user != "Guest" and frappe.local.session.user != "Guest"
) and self.payment_channel != "Phone": ) and self.payment_channel != "Phone":
success_url = shopping_cart_settings.payment_success_url success_url = shopping_cart_settings.payment_success_url
if success_url: if success_url:
redirect_to = ({"Orders": "/orders", "Invoices": "/invoices", "My Account": "/me"}).get( redirect_to = ({"Orders": "/orders", "Invoices": "/invoices", "My Account": "/me"}).get(
success_url, "/me" success_url, "/me"
) )
else: else:
redirect_to = get_url("/orders/{0}".format(self.reference_name)) redirect_to = get_url(f"/orders/{self.reference_name}")
return redirect_to return redirect_to
@@ -413,15 +408,11 @@ def make_payment_request(**args):
frappe.db.set_value( frappe.db.set_value(
"Sales Order", args.dn, "loyalty_points", int(args.loyalty_points), update_modified=False "Sales Order", args.dn, "loyalty_points", int(args.loyalty_points), update_modified=False
) )
frappe.db.set_value( frappe.db.set_value("Sales Order", args.dn, "loyalty_amount", loyalty_amount, update_modified=False)
"Sales Order", args.dn, "loyalty_amount", loyalty_amount, update_modified=False
)
grand_total = grand_total - loyalty_amount grand_total = grand_total - loyalty_amount
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")) if args.get("party_type") else ""
if args.get("party_type")
else ""
) )
draft_payment_request = frappe.db.get_value( draft_payment_request = frappe.db.get_value(

View File

@@ -93,7 +93,7 @@ class TestPaymentRequest(unittest.TestCase):
return_doc=1, return_doc=1,
) )
pe = pr.create_payment_entry() pr.create_payment_entry()
pr.load_from_db() pr.load_from_db()
self.assertEqual(pr.status, "Paid") self.assertEqual(pr.status, "Paid")
@@ -158,7 +158,7 @@ class TestPaymentRequest(unittest.TestCase):
self.assertTrue(gl_entries) self.assertTrue(gl_entries)
for i, gle in enumerate(gl_entries): for _i, gle in enumerate(gl_entries):
self.assertEqual(expected_gle[gle.account][0], gle.account) self.assertEqual(expected_gle[gle.account][0], gle.account)
self.assertEqual(expected_gle[gle.account][1], gle.debit) self.assertEqual(expected_gle[gle.account][1], gle.debit)
self.assertEqual(expected_gle[gle.account][2], gle.credit) self.assertEqual(expected_gle[gle.account][2], gle.credit)

View File

@@ -19,9 +19,7 @@ class PaymentTermsTemplate(Document):
total_portion += flt(term.get("invoice_portion", 0)) total_portion += flt(term.get("invoice_portion", 0))
if flt(total_portion, 2) != 100.00: if flt(total_portion, 2) != 100.00:
frappe.msgprint( frappe.msgprint(_("Combined invoice portion must equal 100%"), raise_exception=1, indicator="red")
_("Combined invoice portion must equal 100%"), raise_exception=1, indicator="red"
)
def validate_terms(self): def validate_terms(self):
terms = [] terms = []

View File

@@ -47,7 +47,8 @@ class PeriodClosingVoucher(AccountsController):
enqueue_after_commit=True, enqueue_after_commit=True,
) )
frappe.msgprint( frappe.msgprint(
_("The GL Entries will be cancelled in the background, it can take a few minutes."), alert=True _("The GL Entries will be cancelled in the background, it can take a few minutes."),
alert=True,
) )
else: else:
make_reverse_gl_entries(voucher_type="Period Closing Voucher", voucher_no=self.name) make_reverse_gl_entries(voucher_type="Period Closing Voucher", voucher_no=self.name)
@@ -89,9 +90,7 @@ class PeriodClosingVoucher(AccountsController):
self.posting_date, self.fiscal_year, self.company, label=_("Posting Date"), doc=self self.posting_date, self.fiscal_year, self.company, label=_("Posting Date"), doc=self
) )
self.year_start_date = get_fiscal_year( self.year_start_date = get_fiscal_year(self.posting_date, self.fiscal_year, company=self.company)[1]
self.posting_date, self.fiscal_year, company=self.company
)[1]
self.check_if_previous_year_closed() self.check_if_previous_year_closed()
@@ -205,7 +204,9 @@ class PeriodClosingVoucher(AccountsController):
"credit_in_account_currency": abs(flt(acc.bal_in_account_currency)) "credit_in_account_currency": abs(flt(acc.bal_in_account_currency))
if flt(acc.bal_in_account_currency) > 0 if flt(acc.bal_in_account_currency) > 0
else 0, else 0,
"credit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) > 0 else 0, "credit": abs(flt(acc.bal_in_company_currency))
if flt(acc.bal_in_company_currency) > 0
else 0,
"is_period_closing_voucher_entry": 1, "is_period_closing_voucher_entry": 1,
}, },
item=acc, item=acc,
@@ -229,7 +230,9 @@ class PeriodClosingVoucher(AccountsController):
"credit_in_account_currency": abs(flt(acc.bal_in_account_currency)) "credit_in_account_currency": abs(flt(acc.bal_in_account_currency))
if flt(acc.bal_in_account_currency) < 0 if flt(acc.bal_in_account_currency) < 0
else 0, else 0,
"credit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) < 0 else 0, "credit": abs(flt(acc.bal_in_company_currency))
if flt(acc.bal_in_company_currency) < 0
else 0,
"is_period_closing_voucher_entry": 1, "is_period_closing_voucher_entry": 1,
}, },
item=acc, item=acc,

View File

@@ -33,7 +33,7 @@ class POSClosingEntry(StatusUpdater):
for key, value in pos_occurences.items(): for key, value in pos_occurences.items():
if len(value) > 1: if len(value) > 1:
error_list.append( error_list.append(
_("{} is added multiple times on rows: {}".format(frappe.bold(key), frappe.bold(value))) _(f"{frappe.bold(key)} is added multiple times on rows: {frappe.bold(value)}")
) )
if error_list: if error_list:
@@ -128,9 +128,7 @@ def get_pos_invoices(start, end, pos_profile, user):
as_dict=1, as_dict=1,
) )
data = list( data = list(filter(lambda d: get_datetime(start) <= get_datetime(d.timestamp) <= get_datetime(end), data))
filter(lambda d: get_datetime(start) <= get_datetime(d.timestamp) <= get_datetime(end), data)
)
# need to get taxes and payments so can't avoid get_doc # need to get taxes and payments so can't avoid get_doc
data = [frappe.get_doc("POS Invoice", d.name).as_dict() for d in data] data = [frappe.get_doc("POS Invoice", d.name).as_dict() for d in data]
@@ -201,7 +199,11 @@ def make_closing_entry_from_opening(opening_entry):
else: else:
payments.append( payments.append(
frappe._dict( frappe._dict(
{"mode_of_payment": p.mode_of_payment, "opening_amount": 0, "expected_amount": p.amount} {
"mode_of_payment": p.mode_of_payment,
"opening_amount": 0,
"expected_amount": p.amount,
}
) )
) )

View File

@@ -27,7 +27,7 @@ from erpnext.stock.doctype.serial_no.serial_no import (
class POSInvoice(SalesInvoice): class POSInvoice(SalesInvoice):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(POSInvoice, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
def validate(self): def validate(self):
if not cint(self.is_pos): if not cint(self.is_pos):
@@ -129,7 +129,9 @@ class POSInvoice(SalesInvoice):
) )
if paid_amt and pay.amount != paid_amt: if paid_amt and pay.amount != paid_amt:
return frappe.throw(_("Payment related to {0} is not completed").format(pay.mode_of_payment)) return frappe.throw(
_("Payment related to {0} is not completed").format(pay.mode_of_payment)
)
def validate_pos_reserved_serial_nos(self, item): def validate_pos_reserved_serial_nos(self, item):
serial_nos = get_serial_nos(item.serial_no) serial_nos = get_serial_nos(item.serial_no)
@@ -164,7 +166,7 @@ class POSInvoice(SalesInvoice):
serial_nos = row.serial_no.split("\n") serial_nos = row.serial_no.split("\n")
if serial_nos: if serial_nos:
for key, value in collections.Counter(serial_nos).items(): for _key, value in collections.Counter(serial_nos).items():
if value > 1: if value > 1:
frappe.throw(_("Duplicate Serial No {0} found").format("key")) frappe.throw(_("Duplicate Serial No {0} found").format("key"))
@@ -191,9 +193,7 @@ class POSInvoice(SalesInvoice):
frappe.throw( frappe.throw(
_( _(
"Row #{}: Batch No. {} of item {} has less than required stock available, {} more required" "Row #{}: Batch No. {} of item {} has less than required stock available, {} more required"
).format( ).format(item.idx, bold_invalid_batch_no, bold_item_name, bold_extra_batch_qty_needed),
item.idx, bold_invalid_batch_no, bold_item_name, bold_extra_batch_qty_needed
),
title=_("Item Unavailable"), title=_("Item Unavailable"),
) )
@@ -249,7 +249,7 @@ class POSInvoice(SalesInvoice):
available_stock, is_stock_item = get_stock_availability(d.item_code, d.warehouse) available_stock, is_stock_item = get_stock_availability(d.item_code, d.warehouse)
item_code, warehouse, qty = ( item_code, warehouse, _qty = (
frappe.bold(d.item_code), frappe.bold(d.item_code),
frappe.bold(d.warehouse), frappe.bold(d.warehouse),
frappe.bold(d.qty), frappe.bold(d.qty),

View File

@@ -338,9 +338,7 @@ class TestPOSInvoice(unittest.TestCase):
) )
pos.set("payments", []) pos.set("payments", [])
pos.append( pos.append("payments", {"mode_of_payment": "Bank Draft", "account": "_Test Bank - _TC", "amount": 50})
"payments", {"mode_of_payment": "Bank Draft", "account": "_Test Bank - _TC", "amount": 50}
)
pos.append( pos.append(
"payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 60, "default": 1} "payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 60, "default": 1}
) )
@@ -594,9 +592,7 @@ class TestPOSInvoice(unittest.TestCase):
from erpnext.accounts.doctype.loyalty_program.test_loyalty_program import create_records from erpnext.accounts.doctype.loyalty_program.test_loyalty_program import create_records
create_records() create_records()
frappe.db.set_value( frappe.db.set_value("Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty")
"Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty"
)
before_lp_details = get_loyalty_program_details_with_points( before_lp_details = get_loyalty_program_details_with_points(
"Test Loyalty Customer", company="_Test Company", loyalty_program="Test Single Loyalty" "Test Loyalty Customer", company="_Test Company", loyalty_program="Test Single Loyalty"
) )
@@ -670,9 +666,7 @@ class TestPOSInvoice(unittest.TestCase):
consolidate_pos_invoices() consolidate_pos_invoices()
pos_inv.load_from_db() pos_inv.load_from_db()
rounded_total = frappe.db.get_value( rounded_total = frappe.db.get_value("Sales Invoice", pos_inv.consolidated_invoice, "rounded_total")
"Sales Invoice", pos_inv.consolidated_invoice, "rounded_total"
)
self.assertEqual(rounded_total, 3470) self.assertEqual(rounded_total, 3470)
def test_merging_into_sales_invoice_with_discount_and_inclusive_tax(self): def test_merging_into_sales_invoice_with_discount_and_inclusive_tax(self):
@@ -719,9 +713,7 @@ class TestPOSInvoice(unittest.TestCase):
consolidate_pos_invoices() consolidate_pos_invoices()
pos_inv.load_from_db() pos_inv.load_from_db()
rounded_total = frappe.db.get_value( rounded_total = frappe.db.get_value("Sales Invoice", pos_inv.consolidated_invoice, "rounded_total")
"Sales Invoice", pos_inv.consolidated_invoice, "rounded_total"
)
self.assertEqual(rounded_total, 840) self.assertEqual(rounded_total, 840)
def test_merging_with_validate_selling_price(self): def test_merging_with_validate_selling_price(self):
@@ -773,9 +765,7 @@ class TestPOSInvoice(unittest.TestCase):
consolidate_pos_invoices() consolidate_pos_invoices()
pos_inv2.load_from_db() pos_inv2.load_from_db()
rounded_total = frappe.db.get_value( rounded_total = frappe.db.get_value("Sales Invoice", pos_inv2.consolidated_invoice, "rounded_total")
"Sales Invoice", pos_inv2.consolidated_invoice, "rounded_total"
)
self.assertEqual(rounded_total, 400) self.assertEqual(rounded_total, 400)
def test_pos_batch_item_qty_validation(self): def test_pos_batch_item_qty_validation(self):
@@ -839,19 +829,19 @@ class TestPOSInvoice(unittest.TestCase):
pos_inv = create_pos_invoice(qty=1, do_not_submit=1) pos_inv = create_pos_invoice(qty=1, do_not_submit=1)
pos_inv.items[0].rate = 300 pos_inv.items[0].rate = 300
pos_inv.save() pos_inv.save()
self.assertEquals(pos_inv.items[0].discount_percentage, 10) self.assertEqual(pos_inv.items[0].discount_percentage, 10)
# rate shouldn't change # rate shouldn't change
self.assertEquals(pos_inv.items[0].rate, 405) self.assertEqual(pos_inv.items[0].rate, 405)
pos_inv.ignore_pricing_rule = 1 pos_inv.ignore_pricing_rule = 1
pos_inv.save() pos_inv.save()
self.assertEquals(pos_inv.ignore_pricing_rule, 1) self.assertEqual(pos_inv.ignore_pricing_rule, 1)
# rate should reset since pricing rules are ignored # rate should reset since pricing rules are ignored
self.assertEquals(pos_inv.items[0].rate, 450) self.assertEqual(pos_inv.items[0].rate, 450)
pos_inv.items[0].rate = 300 pos_inv.items[0].rate = 300
pos_inv.save() pos_inv.save()
self.assertEquals(pos_inv.items[0].rate, 300) self.assertEqual(pos_inv.items[0].rate, 300)
finally: finally:
item_price.delete() item_price.delete()
@@ -874,7 +864,7 @@ class TestPOSInvoice(unittest.TestCase):
dn = create_delivery_note(item_code="_Test Serialized Item With Series", serial_no=serial_no) dn = create_delivery_note(item_code="_Test Serialized Item With Series", serial_no=serial_no)
delivery_document_no = frappe.db.get_value("Serial No", serial_no, "delivery_document_no") delivery_document_no = frappe.db.get_value("Serial No", serial_no, "delivery_document_no")
self.assertEquals(delivery_document_no, dn.name) self.assertEqual(delivery_document_no, dn.name)
init_user_and_profile() init_user_and_profile()

View File

@@ -29,7 +29,7 @@ class POSInvoiceMergeLog(Document):
for key, value in pos_occurences.items(): for key, value in pos_occurences.items():
if len(value) > 1: if len(value) > 1:
error_list.append( error_list.append(
_("{} is added multiple times on rows: {}".format(frappe.bold(key), frappe.bold(value))) _(f"{frappe.bold(key)} is added multiple times on rows: {frappe.bold(value)}")
) )
if error_list: if error_list:
@@ -56,7 +56,9 @@ class POSInvoiceMergeLog(Document):
bold_pos_invoice = frappe.bold(d.pos_invoice) bold_pos_invoice = frappe.bold(d.pos_invoice)
bold_status = frappe.bold(status) bold_status = frappe.bold(status)
if docstatus != 1: if docstatus != 1:
frappe.throw(_("Row #{}: POS Invoice {} is not submitted yet").format(d.idx, bold_pos_invoice)) frappe.throw(
_("Row #{}: POS Invoice {} is not submitted yet").format(d.idx, bold_pos_invoice)
)
if status == "Consolidated": if status == "Consolidated":
frappe.throw( frappe.throw(
_("Row #{}: POS Invoice {} has been {}").format(d.idx, bold_pos_invoice, bold_status) _("Row #{}: POS Invoice {} has been {}").format(d.idx, bold_pos_invoice, bold_status)
@@ -75,15 +77,17 @@ class POSInvoiceMergeLog(Document):
d.idx, bold_return_against, bold_pos_invoice, bold_unconsolidated d.idx, bold_return_against, bold_pos_invoice, bold_unconsolidated
) )
msg += " " msg += " "
msg += _("Original invoice should be consolidated before or along with the return invoice.") msg += _(
"Original invoice should be consolidated before or along with the return invoice."
)
msg += "<br><br>" msg += "<br><br>"
msg += _("You can add original invoice {} manually to proceed.").format(bold_return_against) msg += _("You can add original invoice {} manually to proceed.").format(
bold_return_against
)
frappe.throw(msg) frappe.throw(msg)
def on_submit(self): def on_submit(self):
pos_invoice_docs = [ pos_invoice_docs = [frappe.get_cached_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices]
frappe.get_cached_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices
]
returns = [d for d in pos_invoice_docs if d.get("is_return") == 1] returns = [d for d in pos_invoice_docs if d.get("is_return") == 1]
sales = [d for d in pos_invoice_docs if d.get("is_return") == 0] sales = [d for d in pos_invoice_docs if d.get("is_return") == 0]
@@ -100,9 +104,7 @@ class POSInvoiceMergeLog(Document):
self.update_pos_invoices(pos_invoice_docs, sales_invoice, credit_note) self.update_pos_invoices(pos_invoice_docs, sales_invoice, credit_note)
def on_cancel(self): def on_cancel(self):
pos_invoice_docs = [ pos_invoice_docs = [frappe.get_cached_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices]
frappe.get_cached_doc("POS Invoice", d.pos_invoice) for d in self.pos_invoices
]
self.update_pos_invoices(pos_invoice_docs) self.update_pos_invoices(pos_invoice_docs)
self.cancel_linked_invoices() self.cancel_linked_invoices()
@@ -192,7 +194,9 @@ class POSInvoiceMergeLog(Document):
for t in taxes: for t in taxes:
if t.account_head == tax.account_head and t.cost_center == tax.cost_center: if t.account_head == tax.account_head and t.cost_center == tax.cost_center:
t.tax_amount = flt(t.tax_amount) + flt(tax.tax_amount_after_discount_amount) t.tax_amount = flt(t.tax_amount) + flt(tax.tax_amount_after_discount_amount)
t.base_tax_amount = flt(t.base_tax_amount) + flt(tax.base_tax_amount_after_discount_amount) t.base_tax_amount = flt(t.base_tax_amount) + flt(
tax.base_tax_amount_after_discount_amount
)
update_item_wise_tax_detail(t, tax) update_item_wise_tax_detail(t, tax)
found = True found = True
if not found: if not found:
@@ -292,9 +296,7 @@ def update_item_wise_tax_detail(consolidate_tax_row, tax_row):
else: else:
consolidated_tax_detail.update({item_code: [tax_data[0], tax_data[1]]}) consolidated_tax_detail.update({item_code: [tax_data[0], tax_data[1]]})
consolidate_tax_row.item_wise_tax_detail = json.dumps( consolidate_tax_row.item_wise_tax_detail = json.dumps(consolidated_tax_detail, separators=(",", ":"))
consolidated_tax_detail, separators=(",", ":")
)
def get_all_unconsolidated_invoices(): def get_all_unconsolidated_invoices():
@@ -339,9 +341,7 @@ def consolidate_pos_invoices(pos_invoices=None, closing_entry=None):
if len(invoices) >= 10 and closing_entry: if len(invoices) >= 10 and closing_entry:
closing_entry.set_status(update=True, status="Queued") closing_entry.set_status(update=True, status="Queued")
enqueue_job( enqueue_job(create_merge_logs, invoice_by_customer=invoice_by_customer, closing_entry=closing_entry)
create_merge_logs, invoice_by_customer=invoice_by_customer, closing_entry=closing_entry
)
else: else:
create_merge_logs(invoice_by_customer, closing_entry) create_merge_logs(invoice_by_customer, closing_entry)
@@ -389,9 +389,7 @@ def split_invoices(invoices):
if not item.serial_no: if not item.serial_no:
continue continue
return_against_is_added = any( return_against_is_added = any(d for d in _invoices if d.pos_invoice == pos_invoice.return_against)
d for d in _invoices if d.pos_invoice == pos_invoice.return_against
)
if return_against_is_added: if return_against_is_added:
break break
@@ -442,7 +440,7 @@ def create_merge_logs(invoice_by_customer, closing_entry=None):
if closing_entry: if closing_entry:
closing_entry.set_status(update=True, status="Failed") closing_entry.set_status(update=True, status="Failed")
if type(error_message) == list: if isinstance(error_message, list):
error_message = frappe.json.dumps(error_message) error_message = frappe.json.dumps(error_message)
closing_entry.db_set("error_message", error_message) closing_entry.db_set("error_message", error_message)
raise raise
@@ -493,7 +491,7 @@ def enqueue_job(job, **kwargs):
timeout=10000, timeout=10000,
event="processing_merge_logs", event="processing_merge_logs",
job_name=job_name, job_name=job_name,
now=frappe.conf.developer_mode or frappe.flags.in_test now=frappe.conf.developer_mode or frappe.flags.in_test,
) )
if job == create_merge_logs: if job == create_merge_logs:

View File

@@ -28,15 +28,11 @@ class TestPOSInvoiceMergeLog(unittest.TestCase):
pos_inv.submit() pos_inv.submit()
pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1) pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1)
pos_inv2.append( pos_inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3200})
"payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3200}
)
pos_inv2.submit() pos_inv2.submit()
pos_inv3 = create_pos_invoice(customer="_Test Customer 2", rate=2300, do_not_submit=1) pos_inv3 = create_pos_invoice(customer="_Test Customer 2", rate=2300, do_not_submit=1)
pos_inv3.append( pos_inv3.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 2300})
"payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 2300}
)
pos_inv3.submit() pos_inv3.submit()
consolidate_pos_invoices() consolidate_pos_invoices()
@@ -65,15 +61,11 @@ class TestPOSInvoiceMergeLog(unittest.TestCase):
pos_inv.submit() pos_inv.submit()
pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1) pos_inv2 = create_pos_invoice(rate=3200, do_not_submit=1)
pos_inv2.append( pos_inv2.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3200})
"payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 3200}
)
pos_inv2.submit() pos_inv2.submit()
pos_inv3 = create_pos_invoice(customer="_Test Customer 2", rate=2300, do_not_submit=1) pos_inv3 = create_pos_invoice(customer="_Test Customer 2", rate=2300, do_not_submit=1)
pos_inv3.append( pos_inv3.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 2300})
"payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 2300}
)
pos_inv3.submit() pos_inv3.submit()
pos_inv_cn = make_sales_return(pos_inv.name) pos_inv_cn = make_sales_return(pos_inv.name)
@@ -309,7 +301,7 @@ class TestPOSInvoiceMergeLog(unittest.TestCase):
init_user_and_profile() init_user_and_profile()
item_rates = [69, 59, 29] item_rates = [69, 59, 29]
for i in [1, 2]: for _i in [1, 2]:
inv = create_pos_invoice(is_return=1, do_not_save=1) inv = create_pos_invoice(is_return=1, do_not_save=1)
inv.items = [] inv.items = []
for rate in item_rates: for rate in item_rates:

View File

@@ -114,10 +114,8 @@ class POSProfile(Document):
condition = " where pfu.default = 1 " condition = " where pfu.default = 1 "
pos_view_users = frappe.db.sql_list( pos_view_users = frappe.db.sql_list(
"""select pfu.user f"""select pfu.user
from `tabPOS Profile User` as pfu {0}""".format( from `tabPOS Profile User` as pfu {condition}"""
condition
)
) )
for user in pos_view_users: for user in pos_view_users:
@@ -144,10 +142,8 @@ def get_item_groups(pos_profile):
def get_child_nodes(group_type, root): def get_child_nodes(group_type, root):
lft, rgt = frappe.db.get_value(group_type, root, ["lft", "rgt"]) lft, rgt = frappe.db.get_value(group_type, root, ["lft", "rgt"])
return frappe.db.sql( return frappe.db.sql(
""" Select name, lft, rgt from `tab{tab}` where f""" Select name, lft, rgt from `tab{group_type}` where
lft >= {lft} and rgt <= {rgt} order by lft""".format( lft >= {lft} and rgt <= {rgt} order by lft""",
tab=group_type, lft=lft, rgt=rgt
),
as_dict=1, as_dict=1,
) )

View File

@@ -52,11 +52,9 @@ def get_customers_list(pos_profile=None):
return ( return (
frappe.db.sql( frappe.db.sql(
""" select name, customer_name, customer_group, f""" select name, customer_name, customer_group,
territory, customer_pos_id from tabCustomer where disabled = 0 territory, customer_pos_id from tabCustomer where disabled = 0
and {cond}""".format( and {cond}""",
cond=cond
),
tuple(customer_groups), tuple(customer_groups),
as_dict=1, as_dict=1,
) )
@@ -75,7 +73,7 @@ def get_items_list(pos_profile, company):
cond = "and i.item_group in (%s)" % (", ".join(["%s"] * len(args_list))) cond = "and i.item_group in (%s)" % (", ".join(["%s"] * len(args_list)))
return frappe.db.sql( return frappe.db.sql(
""" f"""
select select
i.name, i.item_code, i.item_name, i.description, i.item_group, i.has_batch_no, i.name, i.item_code, i.item_name, i.description, i.item_group, i.has_batch_no,
i.has_serial_no, i.is_stock_item, i.brand, i.stock_uom, i.image, i.has_serial_no, i.is_stock_item, i.brand, i.stock_uom, i.image,
@@ -88,10 +86,8 @@ def get_items_list(pos_profile, company):
where where
i.disabled = 0 and i.has_variants = 0 and i.is_sales_item = 1 and i.is_fixed_asset = 0 i.disabled = 0 and i.has_variants = 0 and i.is_sales_item = 1 and i.is_fixed_asset = 0
{cond} {cond}
""".format( """,
cond=cond tuple([company, *args_list]),
),
tuple([company] + args_list),
as_dict=1, as_dict=1,
) )

View File

@@ -77,9 +77,9 @@ class PricingRule(Document):
if self.priority and cint(self.priority) == 1: if self.priority and cint(self.priority) == 1:
throw( throw(
_("As the field {0} is enabled, the value of the field {1} should be more than 1.").format( _(
frappe.bold("Apply Discount on Discounted Rate"), frappe.bold("Priority") "As the field {0} is enabled, the value of the field {1} should be more than 1."
) ).format(frappe.bold("Apply Discount on Discounted Rate"), frappe.bold("Priority"))
) )
def validate_applicable_for_selling_or_buying(self): def validate_applicable_for_selling_or_buying(self):
@@ -273,9 +273,7 @@ def apply_pricing_rule(args, doc=None):
def get_serial_no_for_item(args): def get_serial_no_for_item(args):
from erpnext.stock.get_item_details import get_serial_no from erpnext.stock.get_item_details import get_serial_no
item_details = frappe._dict( item_details = frappe._dict({"doctype": args.doctype, "name": args.name, "serial_no": args.serial_no})
{"doctype": args.doctype, "name": args.name, "serial_no": args.serial_no}
)
if args.get("parenttype") in ("Sales Invoice", "Delivery Note") and flt(args.stock_qty) > 0: if args.get("parenttype") in ("Sales Invoice", "Delivery Note") and flt(args.stock_qty) > 0:
item_details.serial_no = get_serial_no(args) item_details.serial_no = get_serial_no(args)
return item_details return item_details
@@ -373,9 +371,11 @@ def get_pricing_rule_for_item(args, doc=None, for_validate=False):
) )
if pricing_rule.apply_rule_on_other_items: if pricing_rule.apply_rule_on_other_items:
item_details["apply_rule_on_other_items"] = json.dumps(pricing_rule.apply_rule_on_other_items) item_details["apply_rule_on_other_items"] = json.dumps(
pricing_rule.apply_rule_on_other_items
)
if pricing_rule.coupon_code_based == 1 and args.coupon_code == None: if pricing_rule.coupon_code_based == 1 and args.coupon_code is None:
return item_details return item_details
if not pricing_rule.validate_applied_rule: if not pricing_rule.validate_applied_rule:
@@ -419,7 +419,6 @@ def update_args_for_pricing_rule(args):
if args.transaction_type == "selling": if args.transaction_type == "selling":
if args.customer and not (args.customer_group and args.territory): if args.customer and not (args.customer_group and args.territory):
if args.quotation_to and args.quotation_to != "Customer": if args.quotation_to and args.quotation_to != "Customer":
customer = frappe._dict() customer = frappe._dict()
else: else:
@@ -450,9 +449,9 @@ def get_pricing_rule_details(args, pricing_rule):
def apply_price_discount_rule(pricing_rule, item_details, args): def apply_price_discount_rule(pricing_rule, item_details, args):
item_details.pricing_rule_for = pricing_rule.rate_or_discount item_details.pricing_rule_for = pricing_rule.rate_or_discount
if ( if (pricing_rule.margin_type in ["Amount", "Percentage"] and pricing_rule.currency == args.currency) or (
pricing_rule.margin_type in ["Amount", "Percentage"] and pricing_rule.currency == args.currency pricing_rule.margin_type == "Percentage"
) or (pricing_rule.margin_type == "Percentage"): ):
item_details.margin_type = pricing_rule.margin_type item_details.margin_type = pricing_rule.margin_type
item_details.has_margin = True item_details.has_margin = True
@@ -595,7 +594,7 @@ def get_item_uoms(doctype, txt, searchfield, start, page_len, filters):
return frappe.get_all( return frappe.get_all(
"UOM Conversion Detail", "UOM Conversion Detail",
filters={"parent": ("in", items), "uom": ("like", "{0}%".format(txt))}, filters={"parent": ("in", items), "uom": ("like", f"{txt}%")},
fields=["distinct uom"], fields=["distinct uom"],
as_list=1, as_list=1,
) )

View File

@@ -103,8 +103,6 @@ class TestPricingRule(unittest.TestCase):
self.assertEqual(details.get("discount_percentage"), 15) self.assertEqual(details.get("discount_percentage"), 15)
def test_pricing_rule_for_margin(self): def test_pricing_rule_for_margin(self):
from frappe import MandatoryError
from erpnext.stock.get_item_details import get_item_details from erpnext.stock.get_item_details import get_item_details
test_record = { test_record = {
@@ -205,8 +203,6 @@ class TestPricingRule(unittest.TestCase):
self.assertEqual(details.get("discount_percentage"), 10) self.assertEqual(details.get("discount_percentage"), 10)
def test_pricing_rule_for_variants(self): def test_pricing_rule_for_variants(self):
from frappe import MandatoryError
from erpnext.stock.get_item_details import get_item_details from erpnext.stock.get_item_details import get_item_details
if not frappe.db.exists("Item", "Test Variant PRT"): if not frappe.db.exists("Item", "Test Variant PRT"):
@@ -1055,8 +1051,7 @@ def delete_existing_pricing_rules():
"Pricing Rule Item Group", "Pricing Rule Item Group",
"Pricing Rule Brand", "Pricing Rule Brand",
]: ]:
frappe.db.sql(f"delete from `tab{doctype}`")
frappe.db.sql("delete from `tab{0}`".format(doctype))
def make_item_price(item, price_list_name, item_price): def make_item_price(item, price_list_name, item_price):

View File

@@ -101,14 +101,12 @@ def _get_pricing_rules(apply_on, args, values):
if not args.get(apply_on_field): if not args.get(apply_on_field):
return [] return []
child_doc = "`tabPricing Rule {0}`".format(apply_on) child_doc = f"`tabPricing Rule {apply_on}`"
conditions = item_variant_condition = item_conditions = "" conditions = item_variant_condition = item_conditions = ""
values[apply_on_field] = args.get(apply_on_field) values[apply_on_field] = args.get(apply_on_field)
if apply_on_field in ["item_code", "brand"]: if apply_on_field in ["item_code", "brand"]:
item_conditions = "{child_doc}.{apply_on_field}= %({apply_on_field})s".format( item_conditions = f"{child_doc}.{apply_on_field}= %({apply_on_field})s"
child_doc=child_doc, apply_on_field=apply_on_field
)
if apply_on_field == "item_code": if apply_on_field == "item_code":
if args.get("uom", None): if args.get("uom", None):
@@ -121,9 +119,7 @@ def _get_pricing_rules(apply_on, args, values):
args.variant_of = frappe.get_cached_value("Item", args.item_code, "variant_of") args.variant_of = frappe.get_cached_value("Item", args.item_code, "variant_of")
if args.variant_of: if args.variant_of:
item_variant_condition = " or {child_doc}.item_code=%(variant_of)s ".format( item_variant_condition = f" or {child_doc}.item_code=%(variant_of)s "
child_doc=child_doc
)
values["variant_of"] = args.variant_of values["variant_of"] = args.variant_of
elif apply_on_field == "item_group": elif apply_on_field == "item_group":
item_conditions = _get_tree_conditions(args, "Item Group", child_doc, False) item_conditions = _get_tree_conditions(args, "Item Group", child_doc, False)
@@ -131,7 +127,7 @@ def _get_pricing_rules(apply_on, args, values):
conditions += get_other_conditions(conditions, values, args) conditions += get_other_conditions(conditions, values, args)
warehouse_conditions = _get_tree_conditions(args, "Warehouse", "`tabPricing Rule`") warehouse_conditions = _get_tree_conditions(args, "Warehouse", "`tabPricing Rule`")
if warehouse_conditions: if warehouse_conditions:
warehouse_conditions = " and {0}".format(warehouse_conditions) warehouse_conditions = f" and {warehouse_conditions}"
if not args.price_list: if not args.price_list:
args.price_list = None args.price_list = None
@@ -157,7 +153,7 @@ def _get_pricing_rules(apply_on, args, values):
item_variant_condition=item_variant_condition, item_variant_condition=item_variant_condition,
transaction_type=args.transaction_type, transaction_type=args.transaction_type,
warehouse_cond=warehouse_conditions, warehouse_cond=warehouse_conditions,
apply_on_other_field="other_{0}".format(apply_on_field), apply_on_other_field=f"other_{apply_on_field}",
conditions=conditions, conditions=conditions,
), ),
values, values,
@@ -196,14 +192,13 @@ def _get_tree_conditions(args, parenttype, table, allow_blank=True):
frappe.throw(_("Invalid {0}").format(args.get(field))) frappe.throw(_("Invalid {0}").format(args.get(field)))
parent_groups = frappe.db.sql_list( parent_groups = frappe.db.sql_list(
"""select name from `tab%s` """select name from `tab{}`
where lft<=%s and rgt>=%s""" where lft<={} and rgt>={}""".format(parenttype, "%s", "%s"),
% (parenttype, "%s", "%s"),
(lft, rgt), (lft, rgt),
) )
if parenttype in ["Customer Group", "Item Group", "Territory"]: if parenttype in ["Customer Group", "Item Group", "Territory"]:
parent_field = "parent_{0}".format(frappe.scrub(parenttype)) parent_field = f"parent_{frappe.scrub(parenttype)}"
root_name = frappe.db.get_list( root_name = frappe.db.get_list(
parenttype, parenttype,
{"is_group": 1, parent_field: ("is", "not set")}, {"is_group": 1, parent_field: ("is", "not set")},
@@ -229,10 +224,10 @@ def _get_tree_conditions(args, parenttype, table, allow_blank=True):
def get_other_conditions(conditions, values, args): def get_other_conditions(conditions, values, args):
for field in ["company", "customer", "supplier", "campaign", "sales_partner"]: for field in ["company", "customer", "supplier", "campaign", "sales_partner"]:
if args.get(field): if args.get(field):
conditions += " and ifnull(`tabPricing Rule`.{0}, '') in (%({1})s, '')".format(field, field) conditions += f" and ifnull(`tabPricing Rule`.{field}, '') in (%({field})s, '')"
values[field] = args.get(field) values[field] = args.get(field)
else: else:
conditions += " and ifnull(`tabPricing Rule`.{0}, '') = ''".format(field) conditions += f" and ifnull(`tabPricing Rule`.{field}, '') = ''"
for parenttype in ["Customer Group", "Territory", "Supplier Group"]: for parenttype in ["Customer Group", "Territory", "Supplier Group"]:
group_condition = _get_tree_conditions(args, parenttype, "`tabPricing Rule`") group_condition = _get_tree_conditions(args, parenttype, "`tabPricing Rule`")
@@ -504,7 +499,7 @@ def get_qty_amount_data_for_cumulative(pr_doc, doc, items=None):
"transaction_date" if frappe.get_meta(doctype).has_field("transaction_date") else "posting_date" "transaction_date" if frappe.get_meta(doctype).has_field("transaction_date") else "posting_date"
) )
child_doctype = "{0} Item".format(doctype) child_doctype = f"{doctype} Item"
apply_on = frappe.scrub(pr_doc.get("apply_on")) apply_on = frappe.scrub(pr_doc.get("apply_on"))
values = [pr_doc.valid_from, pr_doc.valid_upto] values = [pr_doc.valid_from, pr_doc.valid_upto]
@@ -514,9 +509,7 @@ def get_qty_amount_data_for_cumulative(pr_doc, doc, items=None):
warehouses = get_child_warehouses(pr_doc.warehouse) warehouses = get_child_warehouses(pr_doc.warehouse)
condition += """ and `tab{child_doc}`.warehouse in ({warehouses}) condition += """ and `tab{child_doc}`.warehouse in ({warehouses})
""".format( """.format(child_doc=child_doctype, warehouses=",".join(["%s"] * len(warehouses)))
child_doc=child_doctype, warehouses=",".join(["%s"] * len(warehouses))
)
values.extend(warehouses) values.extend(warehouses)
@@ -528,16 +521,14 @@ def get_qty_amount_data_for_cumulative(pr_doc, doc, items=None):
values.extend(items) values.extend(items)
data_set = frappe.db.sql( data_set = frappe.db.sql(
""" SELECT `tab{child_doc}`.stock_qty, f""" SELECT `tab{child_doctype}`.stock_qty,
`tab{child_doc}`.amount `tab{child_doctype}`.amount
FROM `tab{child_doc}`, `tab{parent_doc}` FROM `tab{child_doctype}`, `tab{doctype}`
WHERE WHERE
`tab{child_doc}`.parent = `tab{parent_doc}`.name and `tab{parent_doc}`.{date_field} `tab{child_doctype}`.parent = `tab{doctype}`.name and `tab{doctype}`.{date_field}
between %s and %s and `tab{parent_doc}`.docstatus = 1 between %s and %s and `tab{doctype}`.docstatus = 1
{condition} group by `tab{child_doc}`.name {condition} group by `tab{child_doctype}`.name
""".format( """,
parent_doc=doctype, child_doc=child_doctype, condition=condition, date_field=date_field
),
tuple(values), tuple(values),
as_dict=1, as_dict=1,
) )
@@ -556,11 +547,9 @@ def apply_pricing_rule_on_transaction(doc):
conditions = get_other_conditions(conditions, values, doc) conditions = get_other_conditions(conditions, values, doc)
pricing_rules = frappe.db.sql( pricing_rules = frappe.db.sql(
""" Select `tabPricing Rule`.* from `tabPricing Rule` f""" Select `tabPricing Rule`.* from `tabPricing Rule`
where {conditions} and `tabPricing Rule`.disable = 0 where {conditions} and `tabPricing Rule`.disable = 0
""".format( """,
conditions=conditions
),
values, values,
as_dict=1, as_dict=1,
) )
@@ -583,7 +572,9 @@ def apply_pricing_rule_on_transaction(doc):
continue continue
if ( if (
d.validate_applied_rule and doc.get(field) is not None and doc.get(field) < d.get(pr_field) d.validate_applied_rule
and doc.get(field) is not None
and doc.get(field) < d.get(pr_field)
): ):
frappe.msgprint(_("User has not applied rule on the invoice {0}").format(doc.name)) frappe.msgprint(_("User has not applied rule on the invoice {0}").format(doc.name))
else: else:
@@ -643,9 +634,7 @@ def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None):
qty = pricing_rule.free_qty or 1 qty = pricing_rule.free_qty or 1
if pricing_rule.is_recursive: if pricing_rule.is_recursive:
transaction_qty = ( transaction_qty = (args.get("qty") if args else doc.total_qty) - pricing_rule.apply_recursion_over
args.get("qty") if args else doc.total_qty
) - pricing_rule.apply_recursion_over
if transaction_qty: if transaction_qty:
qty = flt(transaction_qty) * qty / pricing_rule.recurse_for qty = flt(transaction_qty) * qty / pricing_rule.recurse_for
if pricing_rule.round_free_qty: if pricing_rule.round_free_qty:

View File

@@ -40,7 +40,7 @@ class TestProcessDeferredAccounting(unittest.TestCase):
si.save() si.save()
si.submit() si.submit()
process_deferred_accounting = doc = frappe.get_doc( process_deferred_accounting = frappe.get_doc(
dict( dict(
doctype="Process Deferred Accounting", doctype="Process Deferred Accounting",
posting_date="2023-07-01", posting_date="2023-07-01",

View File

@@ -41,9 +41,7 @@ class ProcessPaymentReconciliation(Document):
def on_cancel(self): def on_cancel(self):
self.db_set("status", "Cancelled") self.db_set("status", "Cancelled")
log = frappe.db.get_value( log = frappe.db.get_value("Process Payment Reconciliation Log", filters={"process_pr": self.name})
"Process Payment Reconciliation Log", filters={"process_pr": self.name}
)
if log: if log:
frappe.db.set_value("Process Payment Reconciliation Log", log, "status", "Cancelled") frappe.db.set_value("Process Payment Reconciliation Log", log, "status", "Cancelled")
@@ -129,7 +127,7 @@ def trigger_job_for_doc(docname: str | None = None):
frappe.db.set_value("Process Payment Reconciliation", docname, "status", "Running") frappe.db.set_value("Process Payment Reconciliation", docname, "status", "Running")
job_name = f"start_processing_{docname}" job_name = f"start_processing_{docname}"
if not is_job_running(job_name): if not is_job_running(job_name):
job = frappe.enqueue( frappe.enqueue(
method="erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.reconcile_based_on_filters", method="erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.reconcile_based_on_filters",
queue="long", queue="long",
is_async=True, is_async=True,
@@ -147,7 +145,7 @@ def trigger_job_for_doc(docname: str | None = None):
# Resume tasks for running doc # Resume tasks for running doc
job_name = f"start_processing_{docname}" job_name = f"start_processing_{docname}"
if not is_job_running(job_name): if not is_job_running(job_name):
job = frappe.enqueue( frappe.enqueue(
method="erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.reconcile_based_on_filters", method="erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.reconcile_based_on_filters",
queue="long", queue="long",
is_async=True, is_async=True,
@@ -224,7 +222,7 @@ def reconcile_based_on_filters(doc: None | str = None) -> None:
job_name = f"process_{doc}_fetch_and_allocate" job_name = f"process_{doc}_fetch_and_allocate"
if not is_job_running(job_name): if not is_job_running(job_name):
job = frappe.enqueue( frappe.enqueue(
method="erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.fetch_and_allocate", method="erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.fetch_and_allocate",
queue="long", queue="long",
timeout="3600", timeout="3600",
@@ -245,7 +243,7 @@ def reconcile_based_on_filters(doc: None | str = None) -> None:
if not allocated: if not allocated:
job_name = f"process__{doc}_fetch_and_allocate" job_name = f"process__{doc}_fetch_and_allocate"
if not is_job_running(job_name): if not is_job_running(job_name):
job = frappe.enqueue( frappe.enqueue(
method="erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.fetch_and_allocate", method="erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.fetch_and_allocate",
queue="long", queue="long",
timeout="3600", timeout="3600",
@@ -263,7 +261,7 @@ def reconcile_based_on_filters(doc: None | str = None) -> None:
else: else:
reconcile_job_name = f"process_{doc}_reconcile" reconcile_job_name = f"process_{doc}_reconcile"
if not is_job_running(reconcile_job_name): if not is_job_running(reconcile_job_name):
job = frappe.enqueue( frappe.enqueue(
method="erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.reconcile", method="erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.reconcile",
queue="long", queue="long",
timeout="3600", timeout="3600",
@@ -350,7 +348,7 @@ def fetch_and_allocate(doc: str) -> None:
reconcile_job_name = f"process_{doc}_reconcile" reconcile_job_name = f"process_{doc}_reconcile"
if not is_job_running(reconcile_job_name): if not is_job_running(reconcile_job_name):
job = frappe.enqueue( frappe.enqueue(
method="erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.reconcile", method="erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.reconcile",
queue="long", queue="long",
timeout="3600", timeout="3600",
@@ -391,7 +389,6 @@ def reconcile(doc: None | str = None) -> None:
# If Payment Entry, update details only for newly linked references # If Payment Entry, update details only for newly linked references
# This is for performance # This is for performance
if allocations[0].reference_type == "Payment Entry": if allocations[0].reference_type == "Payment Entry":
references = [(x.invoice_type, x.invoice_number) for x in allocations] references = [(x.invoice_type, x.invoice_number) for x in allocations]
pe = frappe.get_doc(allocations[0].reference_type, allocations[0].reference_name) pe = frappe.get_doc(allocations[0].reference_type, allocations[0].reference_name)
pe.flags.ignore_validate_update_after_submit = True pe.flags.ignore_validate_update_after_submit = True
@@ -405,13 +402,14 @@ def reconcile(doc: None | str = None) -> None:
# Update reconciled count # Update reconciled count
reconciled_count = frappe.db.count( reconciled_count = frappe.db.count(
"Process Payment Reconciliation Log Allocations", filters={"parent": log, "reconciled": True} "Process Payment Reconciliation Log Allocations",
filters={"parent": log, "reconciled": True},
) )
frappe.db.set_value( frappe.db.set_value(
"Process Payment Reconciliation Log", log, "reconciled_entries", reconciled_count "Process Payment Reconciliation Log", log, "reconciled_entries", reconciled_count
) )
except Exception as err: except Exception:
# Update the parent doc about the exception # Update the parent doc about the exception
frappe.db.rollback() frappe.db.rollback()
@@ -449,20 +447,19 @@ def reconcile(doc: None | str = None) -> None:
frappe.db.set_value("Process Payment Reconciliation Log", log, "reconciled", True) frappe.db.set_value("Process Payment Reconciliation Log", log, "reconciled", True)
frappe.db.set_value("Process Payment Reconciliation", doc, "status", "Completed") frappe.db.set_value("Process Payment Reconciliation", doc, "status", "Completed")
else: else:
if not (
if not (frappe.db.get_value("Process Payment Reconciliation", doc, "status") == "Paused"): frappe.db.get_value("Process Payment Reconciliation", doc, "status") == "Paused"
):
# trigger next batch in job # trigger next batch in job
# generate reconcile job name # generate reconcile job name
allocation = get_next_allocation(log) allocation = get_next_allocation(log)
if allocation: if allocation:
reconcile_job_name = ( reconcile_job_name = f"process_{doc}_reconcile_allocation_{allocation[0].idx}_{allocation[-1].idx}"
f"process_{doc}_reconcile_allocation_{allocation[0].idx}_{allocation[-1].idx}"
)
else: else:
reconcile_job_name = f"process_{doc}_reconcile" reconcile_job_name = f"process_{doc}_reconcile"
if not is_job_running(reconcile_job_name): if not is_job_running(reconcile_job_name):
job = frappe.enqueue( frappe.enqueue(
method="erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.reconcile", method="erpnext.accounts.doctype.process_payment_reconciliation.process_payment_reconciliation.reconcile",
queue="long", queue="long",
timeout="3600", timeout="3600",
@@ -481,7 +478,7 @@ def reconcile(doc: None | str = None) -> None:
def is_any_doc_running(for_filter: str | dict | None = None) -> str | None: def is_any_doc_running(for_filter: str | dict | None = None) -> str | None:
running_doc = None running_doc = None
if for_filter: if for_filter:
if type(for_filter) == str: if isinstance(for_filter, str):
for_filter = frappe.json.loads(for_filter) for_filter = frappe.json.loads(for_filter)
running_doc = frappe.db.get_value( running_doc = frappe.db.get_value(

View File

@@ -423,9 +423,7 @@ def send_emails(document_name, from_scheduler=False, posting_date=None):
else: else:
new_to_date = add_months(new_to_date, 1 if doc.frequency == "Monthly" else 3) new_to_date = add_months(new_to_date, 1 if doc.frequency == "Monthly" else 3)
new_from_date = add_months(new_to_date, -1 * doc.filter_duration) new_from_date = add_months(new_to_date, -1 * doc.filter_duration)
doc.add_comment( doc.add_comment("Comment", "Emails sent on: " + frappe.utils.format_datetime(frappe.utils.now()))
"Comment", "Emails sent on: " + frappe.utils.format_datetime(frappe.utils.now())
)
if doc.report == "General Ledger": if doc.report == "General Ledger":
doc.db_set("to_date", new_to_date, commit=True) doc.db_set("to_date", new_to_date, commit=True)
doc.db_set("from_date", new_from_date, commit=True) doc.db_set("from_date", new_from_date, commit=True)

View File

@@ -1,7 +1,6 @@
# Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2020, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt # See license.txt
import unittest
import frappe import frappe
from frappe.tests.utils import FrappeTestCase from frappe.tests.utils import FrappeTestCase

View File

@@ -100,9 +100,7 @@ class PromotionalScheme(Document):
docnames = frappe.get_all("Pricing Rule", filters={"promotional_scheme": self.name}) docnames = frappe.get_all("Pricing Rule", filters={"promotional_scheme": self.name})
for docname in docnames: for docname in docnames:
if frappe.db.exists( if frappe.db.exists("Pricing Rule Detail", {"pricing_rule": docname.name, "docstatus": ("<", 2)}):
"Pricing Rule Detail", {"pricing_rule": docname.name, "docstatus": ("<", 2)}
):
raise_for_transaction_exists(self.name) raise_for_transaction_exists(self.name)
if docnames and not transaction_exists: if docnames and not transaction_exists:
@@ -177,7 +175,7 @@ def _get_pricing_rules(doc, child_doc, discount_fields, rules=None):
args = get_args_for_pricing_rule(doc) args = get_args_for_pricing_rule(doc)
applicable_for = frappe.scrub(doc.get("applicable_for")) applicable_for = frappe.scrub(doc.get("applicable_for"))
for idx, d in enumerate(doc.get(child_doc)): for _idx, d in enumerate(doc.get(child_doc)):
if d.name in rules: if d.name in rules:
if not args.get(applicable_for): if not args.get(applicable_for):
docname = get_pricing_rule_docname(d) docname = get_pricing_rule_docname(d)
@@ -187,7 +185,14 @@ def _get_pricing_rules(doc, child_doc, discount_fields, rules=None):
for applicable_for_value in args.get(applicable_for): for applicable_for_value in args.get(applicable_for):
docname = get_pricing_rule_docname(d, applicable_for, applicable_for_value) docname = get_pricing_rule_docname(d, applicable_for, applicable_for_value)
pr = prepare_pricing_rule( pr = prepare_pricing_rule(
args, doc, child_doc, discount_fields, d, docname, applicable_for, applicable_for_value args,
doc,
child_doc,
discount_fields,
d,
docname,
applicable_for,
applicable_for_value,
) )
new_doc.append(pr) new_doc.append(pr)
@@ -213,7 +218,7 @@ def _get_pricing_rules(doc, child_doc, discount_fields, rules=None):
def get_pricing_rule_docname( def get_pricing_rule_docname(
row: dict, applicable_for: str = None, applicable_for_value: str = None row: dict, applicable_for: str | None = None, applicable_for_value: str | None = None
) -> str: ) -> str:
fields = ["promotional_scheme_id", "name"] fields = ["promotional_scheme_id", "name"]
filters = {"promotional_scheme_id": row.name} filters = {"promotional_scheme_id": row.name}

View File

@@ -55,7 +55,7 @@ form_grid_templates = {"items": "templates/form_grid/item_grid.html"}
class PurchaseInvoice(BuyingController): class PurchaseInvoice(BuyingController):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(PurchaseInvoice, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.status_updater = [ self.status_updater = [
{ {
"source_dt": "Purchase Invoice Item", "source_dt": "Purchase Invoice Item",
@@ -72,7 +72,7 @@ class PurchaseInvoice(BuyingController):
] ]
def onload(self): def onload(self):
super(PurchaseInvoice, self).onload() super().onload()
supplier_tds = frappe.db.get_value("Supplier", self.supplier, "tax_withholding_category") supplier_tds = frappe.db.get_value("Supplier", self.supplier, "tax_withholding_category")
self.set_onload("supplier_tds", supplier_tds) self.set_onload("supplier_tds", supplier_tds)
@@ -92,7 +92,7 @@ class PurchaseInvoice(BuyingController):
self.validate_posting_time() self.validate_posting_time()
super(PurchaseInvoice, self).validate() super().validate()
if not self.is_return: if not self.is_return:
self.po_required() self.po_required()
@@ -155,7 +155,6 @@ class PurchaseInvoice(BuyingController):
if flt(self.paid_amount) + flt(self.write_off_amount) - flt( if flt(self.paid_amount) + flt(self.write_off_amount) - flt(
self.get("rounded_total") or self.grand_total self.get("rounded_total") or self.grand_total
) > 1 / (10 ** (self.precision("base_grand_total") + 1)): ) > 1 / (10 ** (self.precision("base_grand_total") + 1)):
frappe.throw(_("""Paid amount + Write Off Amount can not be greater than Grand Total""")) frappe.throw(_("""Paid amount + Write Off Amount can not be greater than Grand Total"""))
def create_remarks(self): def create_remarks(self):
@@ -184,7 +183,7 @@ class PurchaseInvoice(BuyingController):
self.tax_withholding_category = tds_category self.tax_withholding_category = tds_category
self.set_onload("supplier_tds", tds_category) self.set_onload("supplier_tds", tds_category)
super(PurchaseInvoice, self).set_missing_values(for_validate) super().set_missing_values(for_validate)
def validate_credit_to_acc(self): def validate_credit_to_acc(self):
if not self.credit_to: if not self.credit_to:
@@ -218,12 +217,12 @@ class PurchaseInvoice(BuyingController):
check_list = [] check_list = []
for d in self.get("items"): for d in self.get("items"):
if d.purchase_order and not d.purchase_order in check_list and not d.purchase_receipt: if d.purchase_order and d.purchase_order not in check_list and not d.purchase_receipt:
check_list.append(d.purchase_order) check_list.append(d.purchase_order)
check_on_hold_or_closed_status("Purchase Order", d.purchase_order) check_on_hold_or_closed_status("Purchase Order", d.purchase_order)
def validate_with_previous_doc(self): def validate_with_previous_doc(self):
super(PurchaseInvoice, self).validate_with_previous_doc( super().validate_with_previous_doc(
{ {
"Purchase Order": { "Purchase Order": {
"ref_dn_field": "purchase_order", "ref_dn_field": "purchase_order",
@@ -271,7 +270,7 @@ class PurchaseInvoice(BuyingController):
exc=WarehouseMissingError, exc=WarehouseMissingError,
) )
super(PurchaseInvoice, self).validate_warehouse() super().validate_warehouse()
def validate_item_code(self): def validate_item_code(self):
for d in self.get("items"): for d in self.get("items"):
@@ -307,7 +306,6 @@ class PurchaseInvoice(BuyingController):
or not frappe.db.get_value("Purchase Order Item", item.po_detail, "delivered_by_supplier") or not frappe.db.get_value("Purchase Order Item", item.po_detail, "delivered_by_supplier")
) )
): ):
if self.update_stock and item.warehouse and (not item.from_warehouse): if self.update_stock and item.warehouse and (not item.from_warehouse):
if ( if (
for_validate for_validate
@@ -335,12 +333,16 @@ class PurchaseInvoice(BuyingController):
if negative_expense_booked_in_pr: if negative_expense_booked_in_pr:
if ( if (
for_validate and item.expense_account and item.expense_account != stock_not_billed_account for_validate
and item.expense_account
and item.expense_account != stock_not_billed_account
): ):
msg = _( msg = _(
"Row {0}: Expense Head changed to {1} because expense is booked against this account in Purchase Receipt {2}" "Row {0}: Expense Head changed to {1} because expense is booked against this account in Purchase Receipt {2}"
).format( ).format(
item.idx, frappe.bold(stock_not_billed_account), frappe.bold(item.purchase_receipt) item.idx,
frappe.bold(stock_not_billed_account),
frappe.bold(item.purchase_receipt),
) )
frappe.msgprint(msg, title=_("Expense Head Changed")) frappe.msgprint(msg, title=_("Expense Head Changed"))
@@ -349,7 +351,9 @@ class PurchaseInvoice(BuyingController):
# If no purchase receipt present then book expense in 'Stock Received But Not Billed' # If no purchase receipt present then book expense in 'Stock Received But Not Billed'
# This is done in cases when Purchase Invoice is created before Purchase Receipt # This is done in cases when Purchase Invoice is created before Purchase Receipt
if ( if (
for_validate and item.expense_account and item.expense_account != stock_not_billed_account for_validate
and item.expense_account
and item.expense_account != stock_not_billed_account
): ):
msg = _( msg = _(
"Row {0}: Expense Head changed to {1} as no Purchase Receipt is created against Item {2}." "Row {0}: Expense Head changed to {1} as no Purchase Receipt is created against Item {2}."
@@ -400,7 +404,6 @@ class PurchaseInvoice(BuyingController):
def po_required(self): def po_required(self):
if frappe.db.get_value("Buying Settings", None, "po_required") == "Yes": if frappe.db.get_value("Buying Settings", None, "po_required") == "Yes":
if frappe.get_value( if frappe.get_value(
"Supplier", self.supplier, "allow_purchase_invoice_creation_without_purchase_order" "Supplier", self.supplier, "allow_purchase_invoice_creation_without_purchase_order"
): ):
@@ -410,7 +413,9 @@ class PurchaseInvoice(BuyingController):
if not d.purchase_order: if not d.purchase_order:
msg = _("Purchase Order Required for item {}").format(frappe.bold(d.item_code)) msg = _("Purchase Order Required for item {}").format(frappe.bold(d.item_code))
msg += "<br><br>" msg += "<br><br>"
msg += _("To submit the invoice without purchase order please set {0} as {1} in {2}").format( msg += _(
"To submit the invoice without purchase order please set {0} as {1} in {2}"
).format(
frappe.bold(_("Purchase Order Required")), frappe.bold(_("Purchase Order Required")),
frappe.bold("No"), frappe.bold("No"),
get_link_to_form("Buying Settings", "Buying Settings", "Buying Settings"), get_link_to_form("Buying Settings", "Buying Settings", "Buying Settings"),
@@ -420,7 +425,6 @@ class PurchaseInvoice(BuyingController):
def pr_required(self): def pr_required(self):
stock_items = self.get_stock_items() stock_items = self.get_stock_items()
if frappe.db.get_value("Buying Settings", None, "pr_required") == "Yes": if frappe.db.get_value("Buying Settings", None, "pr_required") == "Yes":
if frappe.get_value( if frappe.get_value(
"Supplier", self.supplier, "allow_purchase_invoice_creation_without_purchase_receipt" "Supplier", self.supplier, "allow_purchase_invoice_creation_without_purchase_receipt"
): ):
@@ -453,7 +457,8 @@ class PurchaseInvoice(BuyingController):
frappe.throw(_("Purchase Order {0} is not submitted").format(d.purchase_order)) frappe.throw(_("Purchase Order {0} is not submitted").format(d.purchase_order))
if d.purchase_receipt: if d.purchase_receipt:
submitted = frappe.db.sql( submitted = frappe.db.sql(
"select name from `tabPurchase Receipt` where docstatus = 1 and name = %s", d.purchase_receipt "select name from `tabPurchase Receipt` where docstatus = 1 and name = %s",
d.purchase_receipt,
) )
if not submitted: if not submitted:
frappe.throw(_("Purchase Receipt {0} is not submitted").format(d.purchase_receipt)) frappe.throw(_("Purchase Receipt {0} is not submitted").format(d.purchase_receipt))
@@ -501,7 +506,9 @@ class PurchaseInvoice(BuyingController):
for item in self.get("items"): for item in self.get("items"):
if item.purchase_receipt: if item.purchase_receipt:
frappe.throw( frappe.throw(
_("Stock cannot be updated against Purchase Receipt {0}").format(item.purchase_receipt) _("Stock cannot be updated against Purchase Receipt {0}").format(
item.purchase_receipt
)
) )
def validate_for_repost(self): def validate_for_repost(self):
@@ -511,7 +518,7 @@ class PurchaseInvoice(BuyingController):
validate_docs_for_deferred_accounting([], [self.name]) validate_docs_for_deferred_accounting([], [self.name])
def on_submit(self): def on_submit(self):
super(PurchaseInvoice, self).on_submit() super().on_submit()
self.check_prev_docstatus() self.check_prev_docstatus()
@@ -551,9 +558,7 @@ class PurchaseInvoice(BuyingController):
if self.update_stock == 1: if self.update_stock == 1:
self.repost_future_sle_and_gle() self.repost_future_sle_and_gle()
if ( if frappe.db.get_single_value("Buying Settings", "project_update_frequency") == "Each Transaction":
frappe.db.get_single_value("Buying Settings", "project_update_frequency") == "Each Transaction"
):
self.update_project() self.update_project()
update_linked_doc(self.doctype, self.name, self.inter_company_invoice_reference) update_linked_doc(self.doctype, self.name, self.inter_company_invoice_reference)
@@ -733,9 +738,7 @@ class PurchaseInvoice(BuyingController):
exchange_rate_map, net_rate_map = get_purchase_document_details(self) exchange_rate_map, net_rate_map = get_purchase_document_details(self)
provisional_accounting_for_non_stock_items = cint( provisional_accounting_for_non_stock_items = cint(
frappe.db.get_value( frappe.db.get_value("Company", self.company, "enable_provisional_accounting_for_non_stock_items")
"Company", self.company, "enable_provisional_accounting_for_non_stock_items"
)
) )
if provisional_accounting_for_non_stock_items: if provisional_accounting_for_non_stock_items:
self.get_provisional_accounts() self.get_provisional_accounts()
@@ -744,7 +747,7 @@ class PurchaseInvoice(BuyingController):
if flt(item.base_net_amount): if flt(item.base_net_amount):
account_currency = get_account_currency(item.expense_account) account_currency = get_account_currency(item.expense_account)
if item.item_code: if item.item_code:
asset_category = frappe.get_cached_value("Item", item.item_code, "asset_category") frappe.get_cached_value("Item", item.item_code, "asset_category")
if ( if (
self.update_stock self.update_stock
@@ -849,7 +852,9 @@ class PurchaseInvoice(BuyingController):
if flt(item.rm_supp_cost): if flt(item.rm_supp_cost):
supplier_warehouse_account = warehouse_account[self.supplier_warehouse]["account"] supplier_warehouse_account = warehouse_account[self.supplier_warehouse]["account"]
if not supplier_warehouse_account: if not supplier_warehouse_account:
frappe.throw(_("Please set account in Warehouse {0}").format(self.supplier_warehouse)) frappe.throw(
_("Please set account in Warehouse {0}").format(self.supplier_warehouse)
)
gl_entries.append( gl_entries.append(
self.get_gl_dict( self.get_gl_dict(
{ {
@@ -899,10 +904,9 @@ class PurchaseInvoice(BuyingController):
and self.conversion_rate != exchange_rate_map[item.purchase_receipt] and self.conversion_rate != exchange_rate_map[item.purchase_receipt]
and item.net_rate == net_rate_map[item.pr_detail] and item.net_rate == net_rate_map[item.pr_detail]
): ):
discrepancy_caused_by_exchange_rate_difference = (
discrepancy_caused_by_exchange_rate_difference = (item.qty * item.net_rate) * ( item.qty * item.net_rate
exchange_rate_map[item.purchase_receipt] - self.conversion_rate ) * (exchange_rate_map[item.purchase_receipt] - self.conversion_rate)
)
gl_entries.append( gl_entries.append(
self.get_gl_dict( self.get_gl_dict(
@@ -983,7 +987,9 @@ class PurchaseInvoice(BuyingController):
default_provisional_account = self.get_company_default("default_provisional_account") default_provisional_account = self.get_company_default("default_provisional_account")
provisional_accounts = set( provisional_accounts = set(
[ [
d.provisional_expense_account if d.provisional_expense_account else default_provisional_account d.provisional_expense_account
if d.provisional_expense_account
else default_provisional_account
for d in pr_items for d in pr_items
] ]
) )
@@ -1040,9 +1046,7 @@ class PurchaseInvoice(BuyingController):
}, },
) )
def make_stock_adjustment_entry( def make_stock_adjustment_entry(self, gl_entries, item, voucher_wise_stock_value, account_currency):
self, gl_entries, item, voucher_wise_stock_value, account_currency
):
net_amt_precision = item.precision("base_net_amount") net_amt_precision = item.precision("base_net_amount")
val_rate_db_precision = 6 if cint(item.precision("valuation_rate")) <= 6 else 9 val_rate_db_precision = 6 if cint(item.precision("valuation_rate")) <= 6 else 9
@@ -1058,7 +1062,6 @@ class PurchaseInvoice(BuyingController):
and warehouse_debit_amount and warehouse_debit_amount
!= flt(voucher_wise_stock_value.get((item.name, item.warehouse)), net_amt_precision) != flt(voucher_wise_stock_value.get((item.name, item.warehouse)), net_amt_precision)
): ):
cost_of_goods_sold_account = self.get_company_default("default_expense_account") cost_of_goods_sold_account = self.get_company_default("default_expense_account")
stock_amount = flt(voucher_wise_stock_value.get((item.name, item.warehouse)), net_amt_precision) stock_amount = flt(voucher_wise_stock_value.get((item.name, item.warehouse)), net_amt_precision)
stock_adjustment_amt = warehouse_debit_amount - stock_amount stock_adjustment_amt = warehouse_debit_amount - stock_amount
@@ -1281,9 +1284,7 @@ class PurchaseInvoice(BuyingController):
# base_rounding_adjustment may become zero due to small precision # base_rounding_adjustment may become zero due to small precision
# eg: rounding_adjustment = 0.01 and exchange rate = 0.05 and precision of base_rounding_adjustment is 2 # eg: rounding_adjustment = 0.01 and exchange rate = 0.05 and precision of base_rounding_adjustment is 2
# then base_rounding_adjustment becomes zero and error is thrown in GL Entry # then base_rounding_adjustment becomes zero and error is thrown in GL Entry
if ( if not self.is_internal_transfer() and self.rounding_adjustment and self.base_rounding_adjustment:
not self.is_internal_transfer() and self.rounding_adjustment and self.base_rounding_adjustment
):
round_off_account, round_off_cost_center = get_round_off_account_and_cost_center( round_off_account, round_off_cost_center = get_round_off_account_and_cost_center(
self.company, "Purchase Invoice", self.name, self.use_company_roundoff_cost_center self.company, "Purchase Invoice", self.name, self.use_company_roundoff_cost_center
) )
@@ -1306,7 +1307,7 @@ class PurchaseInvoice(BuyingController):
def on_cancel(self): def on_cancel(self):
check_if_return_invoice_linked_with_payment_entry(self) check_if_return_invoice_linked_with_payment_entry(self)
super(PurchaseInvoice, self).on_cancel() super().on_cancel()
self.check_on_hold_or_closed_status() self.check_on_hold_or_closed_status()
@@ -1337,9 +1338,7 @@ class PurchaseInvoice(BuyingController):
if self.update_stock == 1: if self.update_stock == 1:
self.repost_future_sle_and_gle() self.repost_future_sle_and_gle()
if ( if frappe.db.get_single_value("Buying Settings", "project_update_frequency") == "Each Transaction":
frappe.db.get_single_value("Buying Settings", "project_update_frequency") == "Each Transaction"
):
self.update_project() self.update_project()
self.db_set("status", "Cancelled") self.db_set("status", "Cancelled")
@@ -1370,9 +1369,7 @@ class PurchaseInvoice(BuyingController):
pj = frappe.qb.DocType("Project") pj = frappe.qb.DocType("Project")
for proj, value in projects.items(): for proj, value in projects.items():
res = ( res = frappe.qb.from_(pj).select(pj.total_purchase_cost).where(pj.name == proj).for_update().run()
frappe.qb.from_(pj).select(pj.total_purchase_cost).where(pj.name == proj).for_update().run()
)
current_purchase_cost = res and res[0][0] or 0 current_purchase_cost = res and res[0][0] or 0
frappe.db.set_value("Project", proj, "total_purchase_cost", current_purchase_cost + value) frappe.db.set_value("Project", proj, "total_purchase_cost", current_purchase_cost + value)
@@ -1640,9 +1637,7 @@ def get_purchase_document_details(doc):
) )
net_rate_map = frappe._dict( net_rate_map = frappe._dict(
frappe.get_all( frappe.get_all(child_doctype, filters={"name": ("in", items)}, fields=["name", "net_rate"], as_list=1)
child_doctype, filters={"name": ("in", items)}, fields=["name", "net_rate"], as_list=1
)
) )
return exchange_rate_map, net_rate_map return exchange_rate_map, net_rate_map

View File

@@ -2,8 +2,6 @@
# License: GNU General Public License v3. See license.txt # License: GNU General Public License v3. See license.txt
import unittest
import frappe import frappe
from frappe.tests.utils import FrappeTestCase, change_settings from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import add_days, cint, flt, getdate, nowdate, today from frappe.utils import add_days, cint, flt, getdate, nowdate, today
@@ -218,7 +216,7 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
supplier.on_hold = 0 supplier.on_hold = 0
supplier.save() supplier.save()
except: except Exception:
pass pass
else: else:
raise Exception raise Exception
@@ -252,7 +250,6 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
self.assertEqual(pi.on_hold, 0) self.assertEqual(pi.on_hold, 0)
def test_gl_entries_with_perpetual_inventory_against_pr(self): def test_gl_entries_with_perpetual_inventory_against_pr(self):
pr = make_purchase_receipt( pr = make_purchase_receipt(
company="_Test Company with perpetual inventory", company="_Test Company with perpetual inventory",
supplier_warehouse="Work In Progress - TCP1", supplier_warehouse="Work In Progress - TCP1",
@@ -303,7 +300,7 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
] ]
) )
for i, gle in enumerate(gl_entries): for _i, gle in enumerate(gl_entries):
self.assertEqual(expected_values[gle.account][0], gle.account) self.assertEqual(expected_values[gle.account][0], gle.account)
self.assertEqual(expected_values[gle.account][1], gle.debit) self.assertEqual(expected_values[gle.account][1], gle.debit)
self.assertEqual(expected_values[gle.account][2], gle.credit) self.assertEqual(expected_values[gle.account][2], gle.credit)
@@ -327,9 +324,7 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
pi.submit() pi.submit()
# Get exchnage gain and loss account # Get exchnage gain and loss account
exchange_gain_loss_account = frappe.db.get_value( exchange_gain_loss_account = frappe.db.get_value("Company", pi.company, "exchange_gain_loss_account")
"Company", pi.company, "exchange_gain_loss_account"
)
# fetching the latest GL Entry with exchange gain and loss account account # fetching the latest GL Entry with exchange gain and loss account account
amount = frappe.db.get_value( amount = frappe.db.get_value(
@@ -545,12 +540,10 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
project = frappe.get_doc("Project", {"project_name": "_Test Project for Purchase"}) project = frappe.get_doc("Project", {"project_name": "_Test Project for Purchase"})
existing_purchase_cost = frappe.db.sql( existing_purchase_cost = frappe.db.sql(
"""select sum(base_net_amount) f"""select sum(base_net_amount)
from `tabPurchase Invoice Item` from `tabPurchase Invoice Item`
where project = '{0}' where project = '{project.name}'
and docstatus=1""".format( and docstatus=1"""
project.name
)
) )
existing_purchase_cost = existing_purchase_cost and existing_purchase_cost[0][0] or 0 existing_purchase_cost = existing_purchase_cost and existing_purchase_cost[0][0] or 0
@@ -725,7 +718,7 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
"credit", "credit",
"credit_in_account_currency", "credit_in_account_currency",
): ):
for i, gle in enumerate(gl_entries): for _i, gle in enumerate(gl_entries):
self.assertEqual(expected_values[gle.account][field], gle[field]) self.assertEqual(expected_values[gle.account][field], gle[field])
# Check for valid currency # Check for valid currency
@@ -747,7 +740,6 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
self.assertFalse(gle) self.assertFalse(gle)
def test_purchase_invoice_update_stock_gl_entry_with_perpetual_inventory(self): def test_purchase_invoice_update_stock_gl_entry_with_perpetual_inventory(self):
pi = make_purchase_invoice( pi = make_purchase_invoice(
update_stock=1, update_stock=1,
posting_date=frappe.utils.nowdate(), posting_date=frappe.utils.nowdate(),
@@ -776,13 +768,12 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
(d[0], d) for d in [[pi.credit_to, 0.0, 250.0], [stock_in_hand_account, 250.0, 0.0]] (d[0], d) for d in [[pi.credit_to, 0.0, 250.0], [stock_in_hand_account, 250.0, 0.0]]
) )
for i, gle in enumerate(gl_entries): for _i, gle in enumerate(gl_entries):
self.assertEqual(expected_gl_entries[gle.account][0], gle.account) self.assertEqual(expected_gl_entries[gle.account][0], gle.account)
self.assertEqual(expected_gl_entries[gle.account][1], gle.debit) self.assertEqual(expected_gl_entries[gle.account][1], gle.debit)
self.assertEqual(expected_gl_entries[gle.account][2], gle.credit) self.assertEqual(expected_gl_entries[gle.account][2], gle.credit)
def test_purchase_invoice_for_is_paid_and_update_stock_gl_entry_with_perpetual_inventory(self): def test_purchase_invoice_for_is_paid_and_update_stock_gl_entry_with_perpetual_inventory(self):
pi = make_purchase_invoice( pi = make_purchase_invoice(
update_stock=1, update_stock=1,
posting_date=frappe.utils.nowdate(), posting_date=frappe.utils.nowdate(),
@@ -817,7 +808,7 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
] ]
) )
for i, gle in enumerate(gl_entries): for _i, gle in enumerate(gl_entries):
self.assertEqual(expected_gl_entries[gle.account][0], gle.account) self.assertEqual(expected_gl_entries[gle.account][0], gle.account)
self.assertEqual(expected_gl_entries[gle.account][1], gle.debit) self.assertEqual(expected_gl_entries[gle.account][1], gle.debit)
self.assertEqual(expected_gl_entries[gle.account][2], gle.credit) self.assertEqual(expected_gl_entries[gle.account][2], gle.credit)
@@ -1015,12 +1006,8 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
def test_duplicate_due_date_in_terms(self): def test_duplicate_due_date_in_terms(self):
pi = make_purchase_invoice(do_not_save=1) pi = make_purchase_invoice(do_not_save=1)
pi.append( pi.append("payment_schedule", dict(due_date="2017-01-01", invoice_portion=50.00, payment_amount=50))
"payment_schedule", dict(due_date="2017-01-01", invoice_portion=50.00, payment_amount=50) pi.append("payment_schedule", dict(due_date="2017-01-01", invoice_portion=50.00, payment_amount=50))
)
pi.append(
"payment_schedule", dict(due_date="2017-01-01", invoice_portion=50.00, payment_amount=50)
)
self.assertRaises(frappe.ValidationError, pi.insert) self.assertRaises(frappe.ValidationError, pi.insert)
@@ -1058,9 +1045,7 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
cost_center = "_Test Cost Center for BS Account - _TC" cost_center = "_Test Cost Center for BS Account - _TC"
create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company") create_cost_center(cost_center_name="_Test Cost Center for BS Account", company="_Test Company")
pi = make_purchase_invoice_against_cost_center( pi = make_purchase_invoice_against_cost_center(cost_center=cost_center, credit_to="Creditors - _TC")
cost_center=cost_center, credit_to="Creditors - _TC"
)
self.assertEqual(pi.cost_center, cost_center) self.assertEqual(pi.cost_center, cost_center)
expected_values = { expected_values = {
@@ -1522,9 +1507,7 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
def test_provisional_accounting_entry(self): def test_provisional_accounting_entry(self):
setup_provisional_accounting() setup_provisional_accounting()
pr = make_purchase_receipt( pr = make_purchase_receipt(item_code="_Test Non Stock Item", posting_date=add_days(nowdate(), -2))
item_code="_Test Non Stock Item", posting_date=add_days(nowdate(), -2)
)
pi = create_purchase_invoice_from_receipt(pr.name) pi = create_purchase_invoice_from_receipt(pr.name)
pi.set_posting_time = 1 pi.set_posting_time = 1
@@ -1533,7 +1516,7 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
pi.save() pi.save()
pi.submit() pi.submit()
self.assertEquals(pr.items[0].provisional_expense_account, "Provision Account - _TC") self.assertEqual(pr.items[0].provisional_expense_account, "Provision Account - _TC")
# Check GLE for Purchase Invoice # Check GLE for Purchase Invoice
expected_gle = [ expected_gle = [
@@ -1560,9 +1543,7 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
["_Test Account Cost for Goods Sold - _TC", 250, 0, pi.posting_date], ["_Test Account Cost for Goods Sold - _TC", 250, 0, pi.posting_date],
] ]
check_gl_entries( check_gl_entries(self, pr.name, expected_gle_for_purchase_receipt_post_pi_cancel, pr.posting_date)
self, pr.name, expected_gle_for_purchase_receipt_post_pi_cancel, pr.posting_date
)
toggle_provisional_accounting_setting() toggle_provisional_accounting_setting()
@@ -1611,9 +1592,7 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
["_Test Account Cost for Goods Sold - _TC", 5000, 0, pi.posting_date], ["_Test Account Cost for Goods Sold - _TC", 5000, 0, pi.posting_date],
] ]
check_gl_entries( check_gl_entries(self, pr.name, expected_gle_for_purchase_receipt_post_pi_cancel, pr.posting_date)
self, pr.name, expected_gle_for_purchase_receipt_post_pi_cancel, pr.posting_date
)
toggle_provisional_accounting_setting() toggle_provisional_accounting_setting()
@@ -1659,9 +1638,7 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
def test_adjust_incoming_rate(self): def test_adjust_incoming_rate(self):
frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 0) frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 0)
frappe.db.set_single_value( frappe.db.set_single_value("Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate", 1)
"Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate", 1
)
# Increase the cost of the item # Increase the cost of the item
@@ -1713,9 +1690,7 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
) )
self.assertEqual(stock_value_difference, 50) self.assertEqual(stock_value_difference, 50)
frappe.db.set_single_value( frappe.db.set_single_value("Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate", 0)
"Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate", 0
)
# Don't adjust incoming rate # Don't adjust incoming rate
@@ -1745,7 +1720,6 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 1) frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 1)
def test_item_less_defaults(self): def test_item_less_defaults(self):
pi = frappe.new_doc("Purchase Invoice") pi = frappe.new_doc("Purchase Invoice")
pi.supplier = "_Test Supplier" pi.supplier = "_Test Supplier"
pi.company = "_Test Company" pi.company = "_Test Company"
@@ -2178,9 +2152,7 @@ def setup_provisional_accounting(**args):
parent_account=args.parent_account or "Current Liabilities - _TC", parent_account=args.parent_account or "Current Liabilities - _TC",
company=company, company=company,
) )
toggle_provisional_accounting_setting( toggle_provisional_accounting_setting(enable=1, company=company, provisional_account=provisional_account)
enable=1, company=company, provisional_account=provisional_account
)
def toggle_provisional_accounting_setting(**args): def toggle_provisional_accounting_setting(**args):

View File

@@ -17,4 +17,4 @@ class PurchaseTaxesandChargesTemplate(Document):
def autoname(self): def autoname(self):
if self.company and self.title: if self.company and self.title:
abbr = frappe.get_cached_value("Company", self.company, "abbr") abbr = frappe.get_cached_value("Company", self.company, "abbr")
self.name = "{0} - {1}".format(self.title, abbr) self.name = f"{self.title} - {abbr}"

View File

@@ -9,7 +9,7 @@ from frappe.utils.data import comma_and
class RepostAccountingLedger(Document): class RepostAccountingLedger(Document):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(RepostAccountingLedger, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self._allowed_types = get_allowed_types_from_settings() self._allowed_types = get_allowed_types_from_settings()
def validate(self): def validate(self):
@@ -136,7 +136,9 @@ def start_repost(account_repost_doc=str) -> None:
doc = frappe.get_doc(x.voucher_type, x.voucher_no) doc = frappe.get_doc(x.voucher_type, x.voucher_no)
if repost_doc.delete_cancelled_entries: if repost_doc.delete_cancelled_entries:
frappe.db.delete("GL Entry", filters={"voucher_type": doc.doctype, "voucher_no": doc.name}) frappe.db.delete(
"GL Entry", filters={"voucher_type": doc.doctype, "voucher_no": doc.name}
)
frappe.db.delete( frappe.db.delete(
"Payment Ledger Entry", filters={"voucher_type": doc.doctype, "voucher_no": doc.name} "Payment Ledger Entry", filters={"voucher_type": doc.doctype, "voucher_no": doc.name}
) )
@@ -182,7 +184,9 @@ def validate_docs_for_deferred_accounting(sales_docs, purchase_docs):
if docs_with_deferred_revenue or docs_with_deferred_expense: if docs_with_deferred_revenue or docs_with_deferred_expense:
frappe.throw( frappe.throw(
_("Documents: {0} have deferred revenue/expense enabled for them. Cannot repost.").format( _("Documents: {0} have deferred revenue/expense enabled for them. Cannot repost.").format(
frappe.bold(comma_and([x[0] for x in docs_with_deferred_expense + docs_with_deferred_revenue])) frappe.bold(
comma_and([x[0] for x in docs_with_deferred_expense + docs_with_deferred_revenue])
)
) )
) )

View File

@@ -9,7 +9,6 @@ from frappe.utils import add_days, nowdate, today
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_request.payment_request import make_payment_request from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request
from erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger import start_repost
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
from erpnext.accounts.utils import get_fiscal_year from erpnext.accounts.utils import get_fiscal_year

View File

@@ -40,7 +40,7 @@ def start_payment_ledger_repost(docname=None):
frappe.db.set_value(repost_doc.doctype, repost_doc.name, "repost_error_log", "") frappe.db.set_value(repost_doc.doctype, repost_doc.name, "repost_error_log", "")
frappe.db.set_value(repost_doc.doctype, repost_doc.name, "repost_status", "Completed") frappe.db.set_value(repost_doc.doctype, repost_doc.name, "repost_status", "Completed")
except Exception as e: except Exception:
frappe.db.rollback() frappe.db.rollback()
traceback = frappe.get_traceback(with_context=True) traceback = frappe.get_traceback(with_context=True)
@@ -53,7 +53,7 @@ def start_payment_ledger_repost(docname=None):
class RepostPaymentLedger(Document): class RepostPaymentLedger(Document):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(RepostPaymentLedger, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.vouchers = [] self.vouchers = []
def before_validate(self): def before_validate(self):

View File

@@ -50,7 +50,7 @@ form_grid_templates = {"items": "templates/form_grid/item_grid.html"}
class SalesInvoice(SellingController): class SalesInvoice(SellingController):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super(SalesInvoice, self).__init__(*args, **kwargs) super().__init__(*args, **kwargs)
self.status_updater = [ self.status_updater = [
{ {
"source_dt": "Sales Invoice Item", "source_dt": "Sales Invoice Item",
@@ -87,7 +87,7 @@ class SalesInvoice(SellingController):
self.indicator_title = _("Paid") self.indicator_title = _("Paid")
def validate(self): def validate(self):
super(SalesInvoice, self).validate() super().validate()
self.validate_auto_set_posting_time() self.validate_auto_set_posting_time()
if not (self.is_pos or self.is_debit_note): if not (self.is_pos or self.is_debit_note):
@@ -293,9 +293,7 @@ class SalesInvoice(SellingController):
self.update_time_sheet(self.name) self.update_time_sheet(self.name)
if ( if frappe.db.get_single_value("Selling Settings", "sales_update_frequency") == "Each Transaction":
frappe.db.get_single_value("Selling Settings", "sales_update_frequency") == "Each Transaction"
):
update_company_current_month_sales(self.company) update_company_current_month_sales(self.company)
self.update_project() self.update_project()
update_linked_doc(self.doctype, self.name, self.inter_company_invoice_reference) update_linked_doc(self.doctype, self.name, self.inter_company_invoice_reference)
@@ -308,9 +306,7 @@ class SalesInvoice(SellingController):
and not self.dont_create_loyalty_points and not self.dont_create_loyalty_points
): ):
self.make_loyalty_point_entry() self.make_loyalty_point_entry()
elif ( elif self.is_return and self.return_against and not self.is_consolidated and self.loyalty_program:
self.is_return and self.return_against and not self.is_consolidated and self.loyalty_program
):
against_si_doc = frappe.get_doc("Sales Invoice", self.return_against) against_si_doc = frappe.get_doc("Sales Invoice", self.return_against)
against_si_doc.delete_loyalty_point_entry() against_si_doc.delete_loyalty_point_entry()
against_si_doc.make_loyalty_point_entry() against_si_doc.make_loyalty_point_entry()
@@ -339,11 +335,11 @@ class SalesInvoice(SellingController):
def check_if_consolidated_invoice(self): def check_if_consolidated_invoice(self):
# since POS Invoice extends Sales Invoice, we explicitly check if doctype is Sales Invoice # since POS Invoice extends Sales Invoice, we explicitly check if doctype is Sales Invoice
if self.doctype == "Sales Invoice" and self.is_consolidated: if self.doctype == "Sales Invoice" and self.is_consolidated:
invoice_or_credit_note = ( invoice_or_credit_note = "consolidated_credit_note" if self.is_return else "consolidated_invoice"
"consolidated_credit_note" if self.is_return else "consolidated_invoice"
)
pos_closing_entry = frappe.get_all( pos_closing_entry = frappe.get_all(
"POS Invoice Merge Log", filters={invoice_or_credit_note: self.name}, pluck="pos_closing_entry" "POS Invoice Merge Log",
filters={invoice_or_credit_note: self.name},
pluck="pos_closing_entry",
) )
if pos_closing_entry and pos_closing_entry[0]: if pos_closing_entry and pos_closing_entry[0]:
msg = _("To cancel a {} you need to cancel the POS Closing Entry {}.").format( msg = _("To cancel a {} you need to cancel the POS Closing Entry {}.").format(
@@ -355,13 +351,13 @@ class SalesInvoice(SellingController):
def before_cancel(self): def before_cancel(self):
self.check_if_consolidated_invoice() self.check_if_consolidated_invoice()
super(SalesInvoice, self).before_cancel() super().before_cancel()
self.update_time_sheet(None) self.update_time_sheet(None)
def on_cancel(self): def on_cancel(self):
check_if_return_invoice_linked_with_payment_entry(self) check_if_return_invoice_linked_with_payment_entry(self)
super(SalesInvoice, self).on_cancel() super().on_cancel()
self.check_sales_order_on_hold_or_close("sales_order") self.check_sales_order_on_hold_or_close("sales_order")
@@ -391,16 +387,12 @@ class SalesInvoice(SellingController):
self.db_set("status", "Cancelled") self.db_set("status", "Cancelled")
self.db_set("repost_required", 0) self.db_set("repost_required", 0)
if ( if frappe.db.get_single_value("Selling Settings", "sales_update_frequency") == "Each Transaction":
frappe.db.get_single_value("Selling Settings", "sales_update_frequency") == "Each Transaction"
):
update_company_current_month_sales(self.company) update_company_current_month_sales(self.company)
self.update_project() self.update_project()
if not self.is_return and not self.is_consolidated and self.loyalty_program: if not self.is_return and not self.is_consolidated and self.loyalty_program:
self.delete_loyalty_point_entry() self.delete_loyalty_point_entry()
elif ( elif self.is_return and self.return_against and not self.is_consolidated and self.loyalty_program:
self.is_return and self.return_against and not self.is_consolidated and self.loyalty_program
):
against_si_doc = frappe.get_doc("Sales Invoice", self.return_against) against_si_doc = frappe.get_doc("Sales Invoice", self.return_against)
against_si_doc.delete_loyalty_point_entry() against_si_doc.delete_loyalty_point_entry()
against_si_doc.make_loyalty_point_entry() against_si_doc.make_loyalty_point_entry()
@@ -504,7 +496,7 @@ class SalesInvoice(SellingController):
if not self.due_date and self.customer: if not self.due_date and self.customer:
self.due_date = get_due_date(self.posting_date, "Customer", self.customer, self.company) self.due_date = get_due_date(self.posting_date, "Customer", self.customer, self.company)
super(SalesInvoice, self).set_missing_values(for_validate) super().set_missing_values(for_validate)
print_format = pos.get("print_format") if pos else None print_format = pos.get("print_format") if pos else None
if not print_format and not cint(frappe.db.get_value("Print Format", "POS Invoice", "disabled")): if not print_format and not cint(frappe.db.get_value("Print Format", "POS Invoice", "disabled")):
@@ -694,7 +686,8 @@ class SalesInvoice(SellingController):
if account.report_type != "Balance Sheet": if account.report_type != "Balance Sheet":
msg = ( msg = (
_("Please ensure {} account is a Balance Sheet account.").format(frappe.bold("Debit To")) + " " _("Please ensure {} account is a Balance Sheet account.").format(frappe.bold("Debit To"))
+ " "
) )
msg += _( msg += _(
"You can change the parent account to a Balance Sheet account or select a different account." "You can change the parent account to a Balance Sheet account or select a different account."
@@ -723,11 +716,16 @@ class SalesInvoice(SellingController):
) )
def validate_with_previous_doc(self): def validate_with_previous_doc(self):
super(SalesInvoice, self).validate_with_previous_doc( super().validate_with_previous_doc(
{ {
"Sales Order": { "Sales Order": {
"ref_dn_field": "sales_order", "ref_dn_field": "sales_order",
"compare_fields": [["customer", "="], ["company", "="], ["project", "="], ["currency", "="]], "compare_fields": [
["customer", "="],
["company", "="],
["project", "="],
["currency", "="],
],
}, },
"Sales Order Item": { "Sales Order Item": {
"ref_dn_field": "so_detail", "ref_dn_field": "so_detail",
@@ -737,7 +735,12 @@ class SalesInvoice(SellingController):
}, },
"Delivery Note": { "Delivery Note": {
"ref_dn_field": "delivery_note", "ref_dn_field": "delivery_note",
"compare_fields": [["customer", "="], ["company", "="], ["project", "="], ["currency", "="]], "compare_fields": [
["customer", "="],
["company", "="],
["project", "="],
["currency", "="],
],
}, },
"Delivery Note Item": { "Delivery Note Item": {
"ref_dn_field": "dn_detail", "ref_dn_field": "dn_detail",
@@ -792,13 +795,14 @@ class SalesInvoice(SellingController):
} }
for key, value in prev_doc_field_map.items(): for key, value in prev_doc_field_map.items():
if frappe.db.get_single_value("Selling Settings", value[0]) == "Yes": if frappe.db.get_single_value("Selling Settings", value[0]) == "Yes":
if frappe.get_value("Customer", self.customer, value[0]): if frappe.get_value("Customer", self.customer, value[0]):
continue continue
for d in self.get("items"): for d in self.get("items"):
if d.item_code and not d.get(key.lower().replace(" ", "_")) and not self.get(value[1]): if d.item_code and not d.get(key.lower().replace(" ", "_")) and not self.get(value[1]):
msgprint(_("{0} is mandatory for Item {1}").format(key, d.item_code), raise_exception=1) msgprint(
_("{0} is mandatory for Item {1}").format(key, d.item_code), raise_exception=1
)
def validate_proj_cust(self): def validate_proj_cust(self):
"""check for does customer belong to same project as entered..""" """check for does customer belong to same project as entered.."""
@@ -825,7 +829,7 @@ class SalesInvoice(SellingController):
msgprint(_("Item Code required at Row No {0}").format(d.idx), raise_exception=True) msgprint(_("Item Code required at Row No {0}").format(d.idx), raise_exception=True)
def validate_warehouse(self): def validate_warehouse(self):
super(SalesInvoice, self).validate_warehouse() super().validate_warehouse()
for d in self.get_item_list(): for d in self.get_item_list():
if ( if (
@@ -1154,7 +1158,9 @@ class SalesInvoice(SellingController):
asset.db_set("disposal_date", None) asset.db_set("disposal_date", None)
if asset.calculate_depreciation: if asset.calculate_depreciation:
posting_date = frappe.db.get_value("Sales Invoice", self.return_against, "posting_date") posting_date = frappe.db.get_value(
"Sales Invoice", self.return_against, "posting_date"
)
reverse_depreciation_entry_made_after_disposal(asset, posting_date) reverse_depreciation_entry_made_after_disposal(asset, posting_date)
reset_depreciation_schedule(asset, self.posting_date) reset_depreciation_schedule(asset, self.posting_date)
@@ -1188,7 +1194,9 @@ class SalesInvoice(SellingController):
else item.deferred_revenue_account else item.deferred_revenue_account
) )
amount, base_amount = self.get_amount_and_base_amount(item, enable_discount_accounting) amount, base_amount = self.get_amount_and_base_amount(
item, enable_discount_accounting
)
account_currency = get_account_currency(income_account) account_currency = get_account_currency(income_account)
gl_entries.append( gl_entries.append(
@@ -1212,7 +1220,7 @@ class SalesInvoice(SellingController):
# expense account gl entries # expense account gl entries
if cint(self.update_stock) and erpnext.is_perpetual_inventory_enabled(self.company): if cint(self.update_stock) and erpnext.is_perpetual_inventory_enabled(self.company):
gl_entries += super(SalesInvoice, self).get_gl_entries() gl_entries += super().get_gl_entries()
def get_asset(self, item): def get_asset(self, item):
if item.get("asset"): if item.get("asset"):
@@ -1275,7 +1283,6 @@ class SalesInvoice(SellingController):
def make_pos_gl_entries(self, gl_entries): def make_pos_gl_entries(self, gl_entries):
if cint(self.is_pos): if cint(self.is_pos):
skip_change_gl_entries = not cint( skip_change_gl_entries = not cint(
frappe.db.get_single_value("Accounts Settings", "post_change_gl_entries") frappe.db.get_single_value("Accounts Settings", "post_change_gl_entries")
) )
@@ -1434,7 +1441,9 @@ class SalesInvoice(SellingController):
"credit_in_account_currency": flt( "credit_in_account_currency": flt(
self.rounding_adjustment, self.precision("rounding_adjustment") self.rounding_adjustment, self.precision("rounding_adjustment")
), ),
"credit": flt(self.base_rounding_adjustment, self.precision("base_rounding_adjustment")), "credit": flt(
self.base_rounding_adjustment, self.precision("base_rounding_adjustment")
),
"cost_center": round_off_cost_center "cost_center": round_off_cost_center
if self.use_company_roundoff_cost_center if self.use_company_roundoff_cost_center
else (self.cost_center or round_off_cost_center), else (self.cost_center or round_off_cost_center),
@@ -1456,7 +1465,11 @@ class SalesInvoice(SellingController):
) )
billed_amt = billed_amt and billed_amt[0][0] or 0 billed_amt = billed_amt and billed_amt[0][0] or 0
frappe.db.set_value( frappe.db.set_value(
"Delivery Note Item", d.dn_detail, "billed_amt", billed_amt, update_modified=update_modified "Delivery Note Item",
d.dn_detail,
"billed_amt",
billed_amt,
update_modified=update_modified,
) )
updated_delivery_notes.append(d.delivery_note) updated_delivery_notes.append(d.delivery_note)
elif d.so_detail: elif d.so_detail:
@@ -1564,7 +1577,6 @@ class SalesInvoice(SellingController):
and getdate(lp_details.from_date) <= getdate(self.posting_date) and getdate(lp_details.from_date) <= getdate(self.posting_date)
and (not lp_details.to_date or getdate(lp_details.to_date) >= getdate(self.posting_date)) and (not lp_details.to_date or getdate(lp_details.to_date) >= getdate(self.posting_date))
): ):
collection_factor = lp_details.collection_factor if lp_details.collection_factor else 1.0 collection_factor = lp_details.collection_factor if lp_details.collection_factor else 1.0
points_earned = cint(eligible_amount / collection_factor) points_earned = cint(eligible_amount / collection_factor)
@@ -1815,7 +1827,7 @@ def validate_inter_company_party(doctype, party, company, inter_company_referenc
filters={"parenttype": partytype, "parent": party}, filters={"parenttype": partytype, "parent": party},
) )
companies = [d.company for d in companies] companies = [d.company for d in companies]
if not company in companies: if company not in companies:
frappe.throw( frappe.throw(
_( _(
"{0} not allowed to transact with {1}. Please change the Company or add the Company in the 'Allowed To Transact With'-Section in the Customer record." "{0} not allowed to transact with {1}. Please change the Company or add the Company in the 'Allowed To Transact With'-Section in the Customer record."
@@ -1824,7 +1836,6 @@ def validate_inter_company_party(doctype, party, company, inter_company_referenc
def update_linked_doc(doctype, name, inter_company_reference): def update_linked_doc(doctype, name, inter_company_reference):
if doctype in ["Sales Invoice", "Purchase Invoice"]: if doctype in ["Sales Invoice", "Purchase Invoice"]:
ref_field = "inter_company_invoice_reference" ref_field = "inter_company_invoice_reference"
else: else:
@@ -1835,7 +1846,6 @@ def update_linked_doc(doctype, name, inter_company_reference):
def unlink_inter_company_doc(doctype, name, inter_company_reference): def unlink_inter_company_doc(doctype, name, inter_company_reference):
if doctype in ["Sales Invoice", "Purchase Invoice"]: if doctype in ["Sales Invoice", "Purchase Invoice"]:
ref_doc = "Purchase Invoice" if doctype == "Sales Invoice" else "Sales Invoice" ref_doc = "Purchase Invoice" if doctype == "Sales Invoice" else "Sales Invoice"
ref_field = "inter_company_invoice_reference" ref_field = "inter_company_invoice_reference"
@@ -2010,16 +2020,13 @@ def get_internal_party(parties, link_doctype, doc):
def validate_inter_company_transaction(doc, doctype): def validate_inter_company_transaction(doc, doctype):
details = get_inter_company_details(doc, doctype) details = get_inter_company_details(doc, doctype)
price_list = ( price_list = (
doc.selling_price_list doc.selling_price_list
if doctype in ["Sales Invoice", "Sales Order", "Delivery Note"] if doctype in ["Sales Invoice", "Sales Order", "Delivery Note"]
else doc.buying_price_list else doc.buying_price_list
) )
valid_price_list = frappe.db.get_value( valid_price_list = frappe.db.get_value("Price List", {"name": price_list, "buying": 1, "selling": 1})
"Price List", {"name": price_list, "buying": 1, "selling": 1}
)
if not valid_price_list and not doc.is_internal_transfer(): if not valid_price_list and not doc.is_internal_transfer():
frappe.throw(_("Selected Price List should have buying and selling fields checked.")) frappe.throw(_("Selected Price List should have buying and selling fields checked."))
@@ -2280,9 +2287,7 @@ def update_pr_items(doc, sales_item_map, purchase_item_map, parent_child_map, wa
for item in doc.get("items"): for item in doc.get("items"):
item.warehouse = warehouse_map.get(sales_item_map.get(item.delivery_note_item)) item.warehouse = warehouse_map.get(sales_item_map.get(item.delivery_note_item))
if not item.warehouse and item.get("purchase_order") and item.get("purchase_order_item"): if not item.warehouse and item.get("purchase_order") and item.get("purchase_order_item"):
item.warehouse = frappe.db.get_value( item.warehouse = frappe.db.get_value("Purchase Order Item", item.purchase_order_item, "warehouse")
"Purchase Order Item", item.purchase_order_item, "warehouse"
)
def get_delivery_note_details(internal_reference): def get_delivery_note_details(internal_reference):
@@ -2534,9 +2539,7 @@ def check_if_return_invoice_linked_with_payment_entry(self):
# If a Return invoice is linked with payment entry along with other invoices, # If a Return invoice is linked with payment entry along with other invoices,
# the cancellation of the Return causes allocated amount to be greater than paid # the cancellation of the Return causes allocated amount to be greater than paid
if not frappe.db.get_single_value( if not frappe.db.get_single_value("Accounts Settings", "unlink_payment_on_cancellation_of_invoice"):
"Accounts Settings", "unlink_payment_on_cancellation_of_invoice"
):
return return
payment_entries = [] payment_entries = []

View File

@@ -2,7 +2,6 @@
# License: GNU General Public License v3. See license.txt # License: GNU General Public License v3. See license.txt
import copy import copy
import unittest
import frappe import frappe
from frappe.model.dynamic_links import get_dynamic_link_map from frappe.model.dynamic_links import get_dynamic_link_map
@@ -901,7 +900,7 @@ class TestSalesInvoice(FrappeTestCase):
] ]
) )
for i, gle in enumerate(gl_entries): for _i, gle in enumerate(gl_entries):
self.assertEqual(expected_values[gle.account][0], gle.account) self.assertEqual(expected_values[gle.account][0], gle.account)
self.assertEqual(expected_values[gle.account][1], gle.debit) self.assertEqual(expected_values[gle.account][1], gle.debit)
self.assertEqual(expected_values[gle.account][2], gle.credit) self.assertEqual(expected_values[gle.account][2], gle.credit)
@@ -927,7 +926,7 @@ class TestSalesInvoice(FrappeTestCase):
write_off_account="_Test Write Off - TCP1", write_off_account="_Test Write Off - TCP1",
) )
pr = make_purchase_receipt( make_purchase_receipt(
company="_Test Company with perpetual inventory", company="_Test Company with perpetual inventory",
item_code="_Test FG Item", item_code="_Test FG Item",
warehouse="Stores - TCP1", warehouse="Stores - TCP1",
@@ -1308,7 +1307,7 @@ class TestSalesInvoice(FrappeTestCase):
expected_values = dict( expected_values = dict(
(d[0], d) for d in [["Debtors - TCP1", 100.0, 0.0], ["Sales - TCP1", 0.0, 100.0]] (d[0], d) for d in [["Debtors - TCP1", 100.0, 0.0], ["Sales - TCP1", 0.0, 100.0]]
) )
for i, gle in enumerate(gl_entries): for _i, gle in enumerate(gl_entries):
self.assertEqual(expected_values[gle.account][0], gle.account) self.assertEqual(expected_values[gle.account][0], gle.account)
self.assertEqual(expected_values[gle.account][1], gle.debit) self.assertEqual(expected_values[gle.account][1], gle.debit)
self.assertEqual(expected_values[gle.account][2], gle.credit) self.assertEqual(expected_values[gle.account][2], gle.credit)
@@ -1332,7 +1331,7 @@ class TestSalesInvoice(FrappeTestCase):
[test_records[1]["items"][0]["income_account"], 0.0, 100.0], [test_records[1]["items"][0]["income_account"], 0.0, 100.0],
] ]
) )
for i, gle in enumerate(gl_entries): for _i, gle in enumerate(gl_entries):
self.assertEqual(expected_values[gle.account][0], gle.account) self.assertEqual(expected_values[gle.account][0], gle.account)
self.assertEqual(expected_values[gle.account][1], gle.debit) self.assertEqual(expected_values[gle.account][1], gle.debit)
self.assertEqual(expected_values[gle.account][2], gle.credit) self.assertEqual(expected_values[gle.account][2], gle.credit)
@@ -1422,9 +1421,7 @@ class TestSalesInvoice(FrappeTestCase):
si.submit() si.submit()
self.assertFalse(frappe.db.get_value("Serial No", serial_nos[0], "warehouse")) self.assertFalse(frappe.db.get_value("Serial No", serial_nos[0], "warehouse"))
self.assertEqual( self.assertEqual(frappe.db.get_value("Serial No", serial_nos[0], "delivery_document_no"), si.name)
frappe.db.get_value("Serial No", serial_nos[0], "delivery_document_no"), si.name
)
return si return si
@@ -1752,7 +1749,7 @@ class TestSalesInvoice(FrappeTestCase):
"credit", "credit",
"credit_in_account_currency", "credit_in_account_currency",
): ):
for i, gle in enumerate(gl_entries): for _i, gle in enumerate(gl_entries):
self.assertEqual(expected_values[gle.account][field], gle[field]) self.assertEqual(expected_values[gle.account][field], gle[field])
# cancel # cancel
@@ -2294,12 +2291,8 @@ class TestSalesInvoice(FrappeTestCase):
def test_duplicate_due_date_in_terms(self): def test_duplicate_due_date_in_terms(self):
si = create_sales_invoice(do_not_save=1) si = create_sales_invoice(do_not_save=1)
si.append( si.append("payment_schedule", dict(due_date="2017-01-01", invoice_portion=50.00, payment_amount=50))
"payment_schedule", dict(due_date="2017-01-01", invoice_portion=50.00, payment_amount=50) si.append("payment_schedule", dict(due_date="2017-01-01", invoice_portion=50.00, payment_amount=50))
)
si.append(
"payment_schedule", dict(due_date="2017-01-01", invoice_portion=50.00, payment_amount=50)
)
self.assertRaises(frappe.ValidationError, si.insert) self.assertRaises(frappe.ValidationError, si.insert)
@@ -2493,9 +2486,7 @@ class TestSalesInvoice(FrappeTestCase):
item.no_of_months = 12 item.no_of_months = 12
item.save() item.save()
si = create_sales_invoice( si = create_sales_invoice(item=item.name, posting_date="2019-01-16", rate=50000, do_not_submit=True)
item=item.name, posting_date="2019-01-16", rate=50000, do_not_submit=True
)
si.items[0].enable_deferred_revenue = 1 si.items[0].enable_deferred_revenue = 1
si.items[0].service_start_date = "2019-01-16" si.items[0].service_start_date = "2019-01-16"
si.items[0].service_end_date = "2019-03-31" si.items[0].service_end_date = "2019-03-31"
@@ -2815,21 +2806,16 @@ class TestSalesInvoice(FrappeTestCase):
item.save() item.save()
sales_invoice = create_sales_invoice(item="T Shirt", rate=700, do_not_submit=True) sales_invoice = create_sales_invoice(item="T Shirt", rate=700, do_not_submit=True)
self.assertEqual( self.assertEqual(sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 12 - _TC")
sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 12 - _TC"
)
# Apply discount # Apply discount
sales_invoice.apply_discount_on = "Net Total" sales_invoice.apply_discount_on = "Net Total"
sales_invoice.discount_amount = 300 sales_invoice.discount_amount = 300
sales_invoice.save() sales_invoice.save()
self.assertEqual( self.assertEqual(sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 10 - _TC")
sales_invoice.items[0].item_tax_template, "_Test Account Excise Duty @ 10 - _TC"
)
@change_settings("Selling Settings", {"enable_discount_accounting": 1}) @change_settings("Selling Settings", {"enable_discount_accounting": 1})
def test_sales_invoice_with_discount_accounting_enabled(self): def test_sales_invoice_with_discount_accounting_enabled(self):
discount_account = create_account( discount_account = create_account(
account_name="Discount Account", account_name="Discount Account",
parent_account="Indirect Expenses - _TC", parent_account="Indirect Expenses - _TC",
@@ -2847,7 +2833,6 @@ class TestSalesInvoice(FrappeTestCase):
@change_settings("Selling Settings", {"enable_discount_accounting": 1}) @change_settings("Selling Settings", {"enable_discount_accounting": 1})
def test_additional_discount_for_sales_invoice_with_discount_accounting_enabled(self): def test_additional_discount_for_sales_invoice_with_discount_accounting_enabled(self):
from erpnext.accounts.doctype.repost_accounting_ledger.test_repost_accounting_ledger import ( from erpnext.accounts.doctype.repost_accounting_ledger.test_repost_accounting_ledger import (
update_repost_settings, update_repost_settings,
) )
@@ -2860,7 +2845,7 @@ class TestSalesInvoice(FrappeTestCase):
company="_Test Company", company="_Test Company",
) )
tds_payable_account = create_account( create_account(
account_name="TDS Payable", account_name="TDS Payable",
account_type="Tax", account_type="Tax",
parent_account="Duties and Taxes - _TC", parent_account="Duties and Taxes - _TC",
@@ -3182,9 +3167,7 @@ class TestSalesInvoice(FrappeTestCase):
""" """
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
over_billing_allowance = frappe.db.get_single_value( over_billing_allowance = frappe.db.get_single_value("Accounts Settings", "over_billing_allowance")
"Accounts Settings", "over_billing_allowance"
)
frappe.db.set_value("Accounts Settings", None, "over_billing_allowance", 0) frappe.db.set_value("Accounts Settings", None, "over_billing_allowance", 0)
dn = create_delivery_note() dn = create_delivery_note()
@@ -3376,7 +3359,7 @@ class TestSalesInvoice(FrappeTestCase):
self.assertEqual(len(journals), 1) self.assertEqual(len(journals), 1)
je_type = frappe.get_cached_value("Journal Entry", journals[0], "voucher_type") je_type = frappe.get_cached_value("Journal Entry", journals[0], "voucher_type")
self.assertEqual(je_type, "Exchange Gain Or Loss") self.assertEqual(je_type, "Exchange Gain Or Loss")
ledger_outstanding = frappe.db.get_all( frappe.db.get_all(
"Payment Ledger Entry", "Payment Ledger Entry",
filters={"against_voucher_no": si.name, "delinked": 0}, filters={"against_voucher_no": si.name, "delinked": 0},
fields=["sum(amount), sum(amount_in_account_currency)"], fields=["sum(amount), sum(amount_in_account_currency)"],
@@ -3538,41 +3521,6 @@ class TestSalesInvoice(FrappeTestCase):
self.assertEqual(expected, actual) self.assertEqual(expected, actual)
def get_sales_invoice_for_e_invoice():
si = make_sales_invoice_for_ewaybill()
si.naming_series = "INV-2020-.#####"
si.items = []
si.append(
"items",
{
"item_code": "_Test Item",
"uom": "Nos",
"warehouse": "_Test Warehouse - _TC",
"qty": 2000,
"rate": 12,
"income_account": "Sales - _TC",
"expense_account": "Cost of Goods Sold - _TC",
"cost_center": "_Test Cost Center - _TC",
},
)
si.append(
"items",
{
"item_code": "_Test Item 2",
"uom": "Nos",
"warehouse": "_Test Warehouse - _TC",
"qty": 420,
"rate": 15,
"income_account": "Sales - _TC",
"expense_account": "Cost of Goods Sold - _TC",
"cost_center": "_Test Cost Center - _TC",
},
)
return si
def check_gl_entries(doc, voucher_no, expected_gle, posting_date): def check_gl_entries(doc, voucher_no, expected_gle, posting_date):
gl_entries = frappe.db.sql( gl_entries = frappe.db.sql(
"""select account, debit, credit, posting_date """select account, debit, credit, posting_date

View File

@@ -22,7 +22,7 @@ class SalesTaxesandChargesTemplate(Document):
def autoname(self): def autoname(self):
if self.company and self.title: if self.company and self.title:
abbr = frappe.get_cached_value("Company", self.company, "abbr") abbr = frappe.get_cached_value("Company", self.company, "abbr")
self.name = "{0} - {1}".format(self.title, abbr) self.name = f"{self.title} - {abbr}"
def set_missing_values(self): def set_missing_values(self):
for data in self.taxes: for data in self.taxes:
@@ -37,10 +37,8 @@ def valdiate_taxes_and_charges_template(doc):
if doc.is_default == 1: if doc.is_default == 1:
frappe.db.sql( frappe.db.sql(
"""update `tab{0}` set is_default = 0 f"""update `tab{doc.doctype}` set is_default = 0
where is_default = 1 and name != %s and company = %s""".format( where is_default = 1 and name != %s and company = %s""",
doc.doctype
),
(doc.name, doc.company), (doc.name, doc.company),
) )

View File

@@ -178,7 +178,9 @@ class ShareTransfer(Document):
doc = self.get_shareholder_doc(shareholder) doc = self.get_shareholder_doc(shareholder)
for entry in doc.share_balance: for entry in doc.share_balance:
if ( if (
entry.share_type != self.share_type or entry.from_no > self.to_no or entry.to_no < self.from_no entry.share_type != self.share_type
or entry.from_no > self.to_no
or entry.to_no < self.from_no
): ):
continue # since query lies outside bounds continue # since query lies outside bounds
elif entry.from_no <= self.from_no and entry.to_no >= self.to_no: # both inside elif entry.from_no <= self.from_no and entry.to_no >= self.to_no: # both inside
@@ -230,7 +232,9 @@ class ShareTransfer(Document):
for entry in current_entries: for entry in current_entries:
# use spaceage logic here # use spaceage logic here
if ( if (
entry.share_type != self.share_type or entry.from_no > self.to_no or entry.to_no < self.from_no entry.share_type != self.share_type
or entry.from_no > self.to_no
or entry.to_no < self.from_no
): ):
new_entries.append(entry) new_entries.append(entry)
continue # since query lies outside bounds continue # since query lies outside bounds
@@ -240,7 +244,9 @@ class ShareTransfer(Document):
if entry.to_no == self.to_no: if entry.to_no == self.to_no:
pass # nothing to append pass # nothing to append
else: else:
new_entries.append(self.return_share_balance_entry(self.to_no + 1, entry.to_no, entry.rate)) new_entries.append(
self.return_share_balance_entry(self.to_no + 1, entry.to_no, entry.rate)
)
else: else:
if entry.to_no == self.to_no: if entry.to_no == self.to_no:
new_entries.append( new_entries.append(
@@ -250,7 +256,9 @@ class ShareTransfer(Document):
new_entries.append( new_entries.append(
self.return_share_balance_entry(entry.from_no, self.from_no - 1, entry.rate) self.return_share_balance_entry(entry.from_no, self.from_no - 1, entry.rate)
) )
new_entries.append(self.return_share_balance_entry(self.to_no + 1, entry.to_no, entry.rate)) new_entries.append(
self.return_share_balance_entry(self.to_no + 1, entry.to_no, entry.rate)
)
elif entry.from_no >= self.from_no and entry.to_no <= self.to_no: elif entry.from_no >= self.from_no and entry.to_no <= self.to_no:
# split and check # split and check
pass # nothing to append pass # nothing to append
@@ -282,7 +290,7 @@ class ShareTransfer(Document):
def get_shareholder_doc(self, shareholder): def get_shareholder_doc(self, shareholder):
# Get Shareholder doc based on the Shareholder name # Get Shareholder doc based on the Shareholder name
if shareholder: if shareholder:
query_filters = {"name": shareholder} pass
name = frappe.db.get_value("Shareholder", {"name": shareholder}, "name") name = frappe.db.get_value("Shareholder", {"name": shareholder}, "name")

View File

@@ -44,7 +44,8 @@ class ShippingRule(Document):
zero_to_values.append(d) zero_to_values.append(d)
elif d.from_value >= d.to_value: elif d.from_value >= d.to_value:
throw( throw(
_("From value must be less than to value in row {0}").format(d.idx), FromGreaterThanToError _("From value must be less than to value in row {0}").format(d.idx),
FromGreaterThanToError,
) )
# check if more than two or more rows has To Value = 0 # check if more than two or more rows has To Value = 0
@@ -87,9 +88,7 @@ class ShippingRule(Document):
def get_shipping_amount_from_rules(self, value): def get_shipping_amount_from_rules(self, value):
for condition in self.get("conditions"): for condition in self.get("conditions"):
if not condition.to_value or ( if not condition.to_value or (flt(condition.from_value) <= flt(value) <= flt(condition.to_value)):
flt(condition.from_value) <= flt(value) <= flt(condition.to_value)
):
return condition.shipping_amount return condition.shipping_amount
return 0.0 return 0.0
@@ -104,7 +103,9 @@ class ShippingRule(Document):
) )
if shipping_country not in [d.country for d in self.countries]: if shipping_country not in [d.country for d in self.countries]:
frappe.throw( frappe.throw(
_("Shipping rule not applicable for country {0} in Shipping Address").format(shipping_country) _("Shipping rule not applicable for country {0} in Shipping Address").format(
shipping_country
)
) )
def add_shipping_rule_to_tax_table(self, doc, shipping_amount): def add_shipping_rule_to_tax_table(self, doc, shipping_amount):
@@ -172,11 +173,9 @@ class ShippingRule(Document):
messages = [] messages = []
for d1, d2 in overlaps: for d1, d2 in overlaps:
messages.append( messages.append(
"%s-%s = %s " f"{d1.from_value}-{d1.to_value} = {fmt_money(d1.shipping_amount, currency=company_currency)} "
% (d1.from_value, d1.to_value, fmt_money(d1.shipping_amount, currency=company_currency))
+ _("and") + _("and")
+ " %s-%s = %s" + f" {d2.from_value}-{d2.to_value} = {fmt_money(d2.shipping_amount, currency=company_currency)}"
% (d2.from_value, d2.to_value, fmt_money(d2.shipping_amount, currency=company_currency))
) )
msgprint("\n".join(messages), raise_exception=OverlappingConditionError) msgprint("\n".join(messages), raise_exception=OverlappingConditionError)

View File

@@ -45,7 +45,6 @@ class TestShippingRule(unittest.TestCase):
def create_shipping_rule(shipping_rule_type, shipping_rule_name): def create_shipping_rule(shipping_rule_type, shipping_rule_name):
if frappe.db.exists("Shipping Rule", shipping_rule_name): if frappe.db.exists("Shipping Rule", shipping_rule_name):
return frappe.get_doc("Shipping Rule", shipping_rule_name) return frappe.get_doc("Shipping Rule", shipping_rule_name)

View File

@@ -357,9 +357,7 @@ class Subscription(Document):
invoice.company = company invoice.company = company
invoice.set_posting_time = 1 invoice.set_posting_time = 1
invoice.posting_date = ( invoice.posting_date = (
self.current_invoice_start self.current_invoice_start if self.generate_invoice_at_period_start else self.current_invoice_end
if self.generate_invoice_at_period_start
else self.current_invoice_end
) )
invoice.cost_center = self.cost_center invoice.cost_center = self.cost_center
@@ -561,10 +559,9 @@ class Subscription(Document):
3. Change the `Subscription` status to 'Cancelled' 3. Change the `Subscription` status to 'Cancelled'
""" """
if not self.is_current_invoice_generated( if not self.is_current_invoice_generated(self.current_invoice_start, self.current_invoice_end) and (
self.current_invoice_start, self.current_invoice_end self.is_postpaid_to_invoice() or self.is_prepaid_to_invoice()
) and (self.is_postpaid_to_invoice() or self.is_prepaid_to_invoice()): ):
prorate = frappe.db.get_single_value("Subscription Settings", "prorate") prorate = frappe.db.get_single_value("Subscription Settings", "prorate")
self.generate_invoice(prorate) self.generate_invoice(prorate)
@@ -609,10 +606,11 @@ class Subscription(Document):
# Generate invoices periodically even if current invoice are unpaid # Generate invoices periodically even if current invoice are unpaid
if ( if (
self.generate_new_invoices_past_due_date self.generate_new_invoices_past_due_date
and not self.is_current_invoice_generated(self.current_invoice_start, self.current_invoice_end) and not self.is_current_invoice_generated(
self.current_invoice_start, self.current_invoice_end
)
and (self.is_postpaid_to_invoice() or self.is_prepaid_to_invoice()) and (self.is_postpaid_to_invoice() or self.is_prepaid_to_invoice())
): ):
prorate = frappe.db.get_single_value("Subscription Settings", "prorate") prorate = frappe.db.get_single_value("Subscription Settings", "prorate")
self.generate_invoice(prorate) self.generate_invoice(prorate)
@@ -628,7 +626,7 @@ class Subscription(Document):
Returns `True` if the most recent invoice for the `Subscription` is not paid Returns `True` if the most recent invoice for the `Subscription` is not paid
""" """
doctype = "Sales Invoice" if self.party_type == "Customer" else "Purchase Invoice" doctype = "Sales Invoice" if self.party_type == "Customer" else "Purchase Invoice"
current_invoice = self.get_current_invoice() self.get_current_invoice()
invoice_list = [d.invoice for d in self.invoices] invoice_list = [d.invoice for d in self.invoices]
outstanding_invoices = frappe.get_all( outstanding_invoices = frappe.get_all(

View File

@@ -1,7 +1,6 @@
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors # Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt # See license.txt
import unittest
import frappe import frappe
from frappe.tests.utils import FrappeTestCase from frappe.tests.utils import FrappeTestCase
@@ -85,9 +84,7 @@ def create_parties():
customer = frappe.new_doc("Customer") customer = frappe.new_doc("Customer")
customer.customer_name = "_Test Subscription Customer" customer.customer_name = "_Test Subscription Customer"
customer.billing_currency = "USD" customer.billing_currency = "USD"
customer.append( customer.append("accounts", {"company": "_Test Company", "account": "_Test Receivable USD - _TC"})
"accounts", {"company": "_Test Company", "account": "_Test Receivable USD - _TC"}
)
customer.insert() customer.insert()
@@ -358,9 +355,7 @@ class TestSubscription(FrappeTestCase):
invoice = subscription.get_current_invoice() invoice = subscription.get_current_invoice()
diff = flt(date_diff(nowdate(), subscription.current_invoice_start) + 1) diff = flt(date_diff(nowdate(), subscription.current_invoice_start) + 1)
plan_days = flt( plan_days = flt(date_diff(subscription.current_invoice_end, subscription.current_invoice_start) + 1)
date_diff(subscription.current_invoice_end, subscription.current_invoice_start) + 1
)
prorate_factor = flt(diff / plan_days) prorate_factor = flt(diff / plan_days)
self.assertEqual( self.assertEqual(
@@ -417,9 +412,7 @@ class TestSubscription(FrappeTestCase):
invoice = subscription.get_current_invoice() invoice = subscription.get_current_invoice()
diff = flt(date_diff(nowdate(), subscription.current_invoice_start) + 1) diff = flt(date_diff(nowdate(), subscription.current_invoice_start) + 1)
plan_days = flt( plan_days = flt(date_diff(subscription.current_invoice_end, subscription.current_invoice_start) + 1)
date_diff(subscription.current_invoice_end, subscription.current_invoice_start) + 1
)
prorate_factor = flt(diff / plan_days) prorate_factor = flt(diff / plan_days)
self.assertEqual(flt(invoice.grand_total, 2), flt(prorate_factor * 900, 2)) self.assertEqual(flt(invoice.grand_total, 2), flt(prorate_factor * 900, 2))

View File

@@ -21,9 +21,7 @@ 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):
plan, quantity=1, customer=None, start_date=None, end_date=None, prorate_factor=1
):
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":
return plan.cost * prorate_factor return plan.cost * prorate_factor

View File

@@ -82,27 +82,23 @@ class TaxRule(Document):
for d in filters: for d in filters:
if conds: if conds:
conds += " and " conds += " and "
conds += """ifnull({0}, '') = {1}""".format(d, frappe.db.escape(cstr(filters[d]))) conds += f"""ifnull({d}, '') = {frappe.db.escape(cstr(filters[d]))}"""
if self.from_date and self.to_date: if self.from_date and self.to_date:
conds += """ and ((from_date > '{from_date}' and from_date < '{to_date}') or conds += f""" and ((from_date > '{self.from_date}' and from_date < '{self.to_date}') or
(to_date > '{from_date}' and to_date < '{to_date}') or (to_date > '{self.from_date}' and to_date < '{self.to_date}') or
('{from_date}' > from_date and '{from_date}' < to_date) or ('{self.from_date}' > from_date and '{self.from_date}' < to_date) or
('{from_date}' = from_date and '{to_date}' = to_date))""".format( ('{self.from_date}' = from_date and '{self.to_date}' = to_date))"""
from_date=self.from_date, to_date=self.to_date
)
elif self.from_date and not self.to_date: elif self.from_date and not self.to_date:
conds += """ and to_date > '{from_date}'""".format(from_date=self.from_date) conds += f""" and to_date > '{self.from_date}'"""
elif self.to_date and not self.from_date: elif self.to_date and not self.from_date:
conds += """ and from_date < '{to_date}'""".format(to_date=self.to_date) conds += f""" and from_date < '{self.to_date}'"""
tax_rule = frappe.db.sql( tax_rule = frappe.db.sql(
"select name, priority \ f"select name, priority \
from `tabTax Rule` where {0} and name != '{1}'".format( from `tabTax Rule` where {conds} and name != '{self.name}'",
conds, self.name
),
as_dict=1, as_dict=1,
) )
@@ -117,7 +113,6 @@ class TaxRule(Document):
and cint(frappe.db.get_single_value("E Commerce Settings", "enabled")) and cint(frappe.db.get_single_value("E Commerce Settings", "enabled"))
and not frappe.db.get_value("Tax Rule", {"use_for_shopping_cart": 1, "name": ["!=", self.name]}) and not frappe.db.get_value("Tax Rule", {"use_for_shopping_cart": 1, "name": ["!=", self.name]})
): ):
self.use_for_shopping_cart = 1 self.use_for_shopping_cart = 1
frappe.msgprint( frappe.msgprint(
_( _(
@@ -174,27 +169,25 @@ def get_tax_template(posting_date, args):
conditions.append("(from_date is null) and (to_date is null)") conditions.append("(from_date is null) and (to_date is null)")
conditions.append( conditions.append(
"ifnull(tax_category, '') = {0}".format(frappe.db.escape(cstr(args.get("tax_category")))) "ifnull(tax_category, '') = {}".format(frappe.db.escape(cstr(args.get("tax_category"))))
) )
if "tax_category" in args.keys(): if "tax_category" in args.keys():
del args["tax_category"] del args["tax_category"]
for key, value in args.items(): for key, value in args.items():
if key == "use_for_shopping_cart": if key == "use_for_shopping_cart":
conditions.append("use_for_shopping_cart = {0}".format(1 if value else 0)) conditions.append(f"use_for_shopping_cart = {1 if value else 0}")
elif key == "customer_group": elif key == "customer_group":
if not value: if not value:
value = get_root_of("Customer Group") value = get_root_of("Customer Group")
customer_group_condition = get_customer_group_condition(value) customer_group_condition = get_customer_group_condition(value)
conditions.append("ifnull({0}, '') in ('', {1})".format(key, customer_group_condition)) conditions.append(f"ifnull({key}, '') in ('', {customer_group_condition})")
else: else:
conditions.append("ifnull({0}, '') in ('', {1})".format(key, frappe.db.escape(cstr(value)))) conditions.append(f"ifnull({key}, '') in ('', {frappe.db.escape(cstr(value))})")
tax_rule = frappe.db.sql( tax_rule = frappe.db.sql(
"""select * from `tabTax Rule` """select * from `tabTax Rule`
where {0}""".format( where {}""".format(" and ".join(conditions)),
" and ".join(conditions)
),
as_dict=True, as_dict=True,
) )
@@ -219,7 +212,7 @@ def get_tax_template(posting_date, args):
)[0] )[0]
tax_template = rule.sales_tax_template or rule.purchase_tax_template tax_template = rule.sales_tax_template or rule.purchase_tax_template
doctype = "{0} Taxes and Charges Template".format(rule.tax_type) doctype = f"{rule.tax_type} Taxes and Charges Template"
if frappe.db.get_value(doctype, tax_template, "disabled") == 1: if frappe.db.get_value(doctype, tax_template, "disabled") == 1:
return None return None
@@ -229,9 +222,7 @@ def get_tax_template(posting_date, args):
def get_customer_group_condition(customer_group): def get_customer_group_condition(customer_group):
condition = "" condition = ""
customer_groups = [ customer_groups = ["%s" % (frappe.db.escape(d.name)) for d in get_parent_customer_groups(customer_group)]
"%s" % (frappe.db.escape(d.name)) for d in get_parent_customer_groups(customer_group)
]
if customer_groups: if customer_groups:
condition = ",".join(["%s"] * len(customer_groups)) % (tuple(customer_groups)) condition = ",".join(["%s"] * len(customer_groups)) % (tuple(customer_groups))
return condition return condition

View File

@@ -36,13 +36,11 @@ class TaxWithholdingCategory(Document):
def validate_thresholds(self): def validate_thresholds(self):
for d in self.get("rates"): for d in self.get("rates"):
if ( if d.cumulative_threshold and d.single_threshold and d.cumulative_threshold < d.single_threshold:
d.cumulative_threshold and d.single_threshold and d.cumulative_threshold < d.single_threshold
):
frappe.throw( frappe.throw(
_("Row #{0}: Cumulative threshold cannot be less than Single Transaction threshold").format( _(
d.idx "Row #{0}: Cumulative threshold cannot be less than Single Transaction threshold"
) ).format(d.idx)
) )
@@ -295,9 +293,7 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N
def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"): def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"):
doctype = "Purchase Invoice" if party_type == "Supplier" else "Sales Invoice" doctype = "Purchase Invoice" if party_type == "Supplier" else "Sales Invoice"
field = ( field = (
"base_tax_withholding_net_total as base_net_total" "base_tax_withholding_net_total as base_net_total" if party_type == "Supplier" else "base_net_total"
if party_type == "Supplier"
else "base_net_total"
) )
voucher_wise_amount = {} voucher_wise_amount = {}
vouchers = [] vouchers = []
@@ -351,9 +347,7 @@ def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"):
return vouchers, voucher_wise_amount return vouchers, voucher_wise_amount
def get_advance_vouchers( def get_advance_vouchers(parties, company=None, from_date=None, to_date=None, party_type="Supplier"):
parties, company=None, from_date=None, to_date=None, party_type="Supplier"
):
""" """
Use Payment Ledger to fetch unallocated Advance Payments Use Payment Ledger to fetch unallocated Advance Payments
""" """
@@ -374,9 +368,7 @@ def get_advance_vouchers(
if from_date and to_date: if from_date and to_date:
conditions.append(ple.posting_date[from_date:to_date]) conditions.append(ple.posting_date[from_date:to_date])
advances = ( advances = qb.from_(ple).select(ple.voucher_no).distinct().where(Criterion.all(conditions)).run(as_list=1)
qb.from_(ple).select(ple.voucher_no).distinct().where(Criterion.all(conditions)).run(as_list=1)
)
if advances: if advances:
advances = [x[0] for x in advances] advances = [x[0] for x in advances]
@@ -542,9 +534,7 @@ def get_tcs_amount(parties, inv, tax_details, vouchers, adv_vouchers):
conditions.append(ple.voucher_no == ple.against_voucher_no) conditions.append(ple.voucher_no == ple.against_voucher_no)
conditions.append(ple.company == inv.company) conditions.append(ple.company == inv.company)
advances = ( (qb.from_(ple).select(Abs(Sum(ple.amount))).where(Criterion.all(conditions)).run(as_list=1))
qb.from_(ple).select(Abs(Sum(ple.amount))).where(Criterion.all(conditions)).run(as_list=1)
)
advance_amt = ( advance_amt = (
qb.from_(ple).select(Abs(Sum(ple.amount))).where(Criterion.all(conditions)).run()[0][0] or 0.0 qb.from_(ple).select(Abs(Sum(ple.amount))).where(Criterion.all(conditions)).run()[0][0] or 0.0
@@ -603,9 +593,7 @@ def get_limit_consumed(ldc, parties):
return limit_consumed return limit_consumed
def get_lower_deduction_amount( def get_lower_deduction_amount(current_amount, limit_consumed, certificate_limit, rate, tax_details):
current_amount, limit_consumed, certificate_limit, rate, tax_details
):
if certificate_limit - flt(limit_consumed) - flt(current_amount) >= 0: if certificate_limit - flt(limit_consumed) - flt(current_amount) >= 0:
return current_amount * rate / 100 return current_amount * rate / 100
else: else:
@@ -617,9 +605,7 @@ def get_lower_deduction_amount(
def is_valid_certificate(ldc, posting_date, limit_consumed): def is_valid_certificate(ldc, posting_date, limit_consumed):
available_amount = flt(ldc.certificate_limit) - flt(limit_consumed) available_amount = flt(ldc.certificate_limit) - flt(limit_consumed)
if ( if (getdate(ldc.valid_from) <= getdate(posting_date) <= getdate(ldc.valid_upto)) and available_amount > 0:
getdate(ldc.valid_from) <= getdate(posting_date) <= getdate(ldc.valid_upto)
) and available_amount > 0:
return True return True
return False return False

View File

@@ -96,9 +96,7 @@ class TestTaxWithholdingCategory(unittest.TestCase):
def test_tax_withholding_category_checks(self): def test_tax_withholding_category_checks(self):
invoices = [] invoices = []
frappe.db.set_value( frappe.db.set_value("Supplier", "Test TDS Supplier3", "tax_withholding_category", "New TDS Category")
"Supplier", "Test TDS Supplier3", "tax_withholding_category", "New TDS Category"
)
# First Invoice with no tds check # First Invoice with no tds check
pi = create_purchase_invoice(supplier="Test TDS Supplier3", rate=20000, do_not_save=True) pi = create_purchase_invoice(supplier="Test TDS Supplier3", rate=20000, do_not_save=True)
@@ -453,7 +451,7 @@ class TestTaxWithholdingCategory(unittest.TestCase):
pe3.save() pe3.save()
pe3.submit() pe3.submit()
self.assertEquals(pe3.get("taxes")[0].tax_amount, 1200) self.assertEqual(pe3.get("taxes")[0].tax_amount, 1200)
pe1.cancel() pe1.cancel()
pe2.cancel() pe2.cancel()
pe3.cancel() pe3.cancel()
@@ -850,9 +848,7 @@ def create_tax_withholding_category(
).insert() ).insert()
def create_lower_deduction_certificate( def create_lower_deduction_certificate(supplier, tax_withholding_category, tax_rate, certificate_no, limit):
supplier, tax_withholding_category, tax_rate, certificate_no, limit
):
fiscal_year = get_fiscal_year(today(), company="_Test Company") fiscal_year = get_fiscal_year(today(), company="_Test Company")
if not frappe.db.exists("Lower Deduction Certificate", certificate_no): if not frappe.db.exists("Lower Deduction Certificate", certificate_no):
frappe.get_doc( frappe.get_doc(

View File

@@ -93,7 +93,7 @@ class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase):
unreconcile.add_references() unreconcile.add_references()
self.assertEqual(len(unreconcile.allocations), 2) self.assertEqual(len(unreconcile.allocations), 2)
allocations = [x.reference_name for x in unreconcile.allocations] allocations = [x.reference_name for x in unreconcile.allocations]
self.assertEquals([si1.name, si2.name], allocations) self.assertEqual([si1.name, si2.name], allocations)
# unreconcile si1 # unreconcile si1
for x in unreconcile.allocations: for x in unreconcile.allocations:
if x.reference_name != si1.name: if x.reference_name != si1.name:
@@ -158,7 +158,7 @@ class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase):
unreconcile.add_references() unreconcile.add_references()
self.assertEqual(len(unreconcile.allocations), 2) self.assertEqual(len(unreconcile.allocations), 2)
allocations = [x.reference_name for x in unreconcile.allocations] allocations = [x.reference_name for x in unreconcile.allocations]
self.assertEquals([si1.name, si2.name], allocations) self.assertEqual([si1.name, si2.name], allocations)
# unreconcile si1 from pe2 # unreconcile si1 from pe2
for x in unreconcile.allocations: for x in unreconcile.allocations:
if x.reference_name != si1.name: if x.reference_name != si1.name:
@@ -216,7 +216,7 @@ class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase):
unreconcile.add_references() unreconcile.add_references()
self.assertEqual(len(unreconcile.allocations), 2) self.assertEqual(len(unreconcile.allocations), 2)
allocations = [x.reference_name for x in unreconcile.allocations] allocations = [x.reference_name for x in unreconcile.allocations]
self.assertEquals([si1.name, si2.name], allocations) self.assertEqual([si1.name, si2.name], allocations)
# unreconcile si1 from pe # unreconcile si1 from pe
for x in unreconcile.allocations: for x in unreconcile.allocations:
if x.reference_name != si1.name: if x.reference_name != si1.name:
@@ -301,7 +301,7 @@ class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase):
unreconcile.add_references() unreconcile.add_references()
self.assertEqual(len(unreconcile.allocations), 2) self.assertEqual(len(unreconcile.allocations), 2)
allocations = [x.reference_name for x in unreconcile.allocations] allocations = [x.reference_name for x in unreconcile.allocations]
self.assertEquals([si1.name, si2.name], allocations) self.assertEqual([si1.name, si2.name], allocations)
# unreconcile si1 from pe2 # unreconcile si1 from pe2
for x in unreconcile.allocations: for x in unreconcile.allocations:
if x.reference_name != si1.name: if x.reference_name != si1.name:
@@ -353,7 +353,7 @@ class TestUnreconcilePayment(AccountsTestMixin, FrappeTestCase):
unreconcile.add_references() unreconcile.add_references()
self.assertEqual(len(unreconcile.allocations), 1) self.assertEqual(len(unreconcile.allocations), 1)
allocations = [x.reference_name for x in unreconcile.allocations] allocations = [x.reference_name for x in unreconcile.allocations]
self.assertEquals([so.name], allocations) self.assertEqual([so.name], allocations)
# unreconcile so # unreconcile so
unreconcile.save().submit() unreconcile.save().submit()

View File

@@ -18,7 +18,7 @@ from erpnext.accounts.utils import (
class UnreconcilePayment(Document): class UnreconcilePayment(Document):
def validate(self): def validate(self):
self.supported_types = ["Payment Entry", "Journal Entry"] self.supported_types = ["Payment Entry", "Journal Entry"]
if not self.voucher_type in self.supported_types: if self.voucher_type not in self.supported_types:
frappe.throw(_("Only {0} are supported").format(comma_and(self.supported_types))) frappe.throw(_("Only {0} are supported").format(comma_and(self.supported_types)))
@frappe.whitelist() @frappe.whitelist()
@@ -70,7 +70,7 @@ class UnreconcilePayment(Document):
@frappe.whitelist() @frappe.whitelist()
def doc_has_references(doctype: str = None, docname: str = None): def doc_has_references(doctype: str | None = None, docname: str | None = None):
if doctype in ["Sales Invoice", "Purchase Invoice"]: if doctype in ["Sales Invoice", "Purchase Invoice"]:
return frappe.db.count( return frappe.db.count(
"Payment Ledger Entry", "Payment Ledger Entry",
@@ -85,7 +85,7 @@ def doc_has_references(doctype: str = None, docname: str = None):
@frappe.whitelist() @frappe.whitelist()
def get_linked_payments_for_doc( def get_linked_payments_for_doc(
company: str = None, doctype: str = None, docname: str = None company: str | None = None, doctype: str | None = None, docname: str | None = None
) -> list: ) -> list:
if company and doctype and docname: if company and doctype and docname:
_dt = doctype _dt = doctype

View File

@@ -78,7 +78,7 @@ def make_acc_dimensions_offsetting_entry(gl_map):
"credit": credit, "credit": credit,
"debit_in_account_currency": debit, "debit_in_account_currency": debit,
"credit_in_account_currency": credit, "credit_in_account_currency": credit,
"remarks": _("Offsetting for Accounting Dimension") + " - {0}".format(dimension.name), "remarks": _("Offsetting for Accounting Dimension") + f" - {dimension.name}",
"against_voucher": None, "against_voucher": None,
} }
) )
@@ -179,9 +179,7 @@ def process_gl_map(gl_map, merge_entries=True, precision=None):
def distribute_gl_based_on_cost_center_allocation(gl_map, precision=None): def distribute_gl_based_on_cost_center_allocation(gl_map, precision=None):
cost_center_allocation = get_cost_center_allocation_data( cost_center_allocation = get_cost_center_allocation_data(gl_map[0]["company"], gl_map[0]["posting_date"])
gl_map[0]["company"], gl_map[0]["posting_date"]
)
if not cost_center_allocation: if not cost_center_allocation:
return gl_map return gl_map
@@ -190,9 +188,7 @@ def distribute_gl_based_on_cost_center_allocation(gl_map, precision=None):
cost_center = d.get("cost_center") cost_center = d.get("cost_center")
# Validate budget against main cost center # Validate budget against main cost center
validate_expense_against_budget( validate_expense_against_budget(d, expense_amount=flt(d.debit, precision) - flt(d.credit, precision))
d, expense_amount=flt(d.debit, precision) - flt(d.credit, precision)
)
if cost_center and cost_center_allocation.get(cost_center): if cost_center and cost_center_allocation.get(cost_center):
for sub_cost_center, percentage in cost_center_allocation.get(cost_center, {}).items(): for sub_cost_center, percentage in cost_center_allocation.get(cost_center, {}).items():
@@ -224,9 +220,7 @@ def get_cost_center_allocation_data(company, posting_date):
cc_allocation = frappe._dict() cc_allocation = frappe._dict()
for d in records: for d in records:
cc_allocation.setdefault(d.main_cost_center, frappe._dict()).setdefault( cc_allocation.setdefault(d.main_cost_center, frappe._dict()).setdefault(d.cost_center, d.percentage)
d.cost_center, d.percentage
)
return cc_allocation return cc_allocation
@@ -540,9 +534,7 @@ def update_accounting_dimensions(round_off_gle):
round_off_gle[dimension] = dimension_values.get(dimension) round_off_gle[dimension] = dimension_values.get(dimension)
def get_round_off_account_and_cost_center( def get_round_off_account_and_cost_center(company, voucher_type, voucher_no, use_company_default=False):
company, voucher_type, voucher_no, use_company_default=False
):
round_off_account, round_off_cost_center = frappe.get_cached_value( round_off_account, round_off_cost_center = frappe.get_cached_value(
"Company", company, ["round_off_account", "round_off_cost_center"] "Company", company, ["round_off_account", "round_off_cost_center"]
) or [None, None] ) or [None, None]
@@ -650,9 +642,7 @@ def check_freezing_date(posting_date, adv_adj=False):
def validate_against_pcv(is_opening, posting_date, company): def validate_against_pcv(is_opening, posting_date, company):
if is_opening and frappe.db.exists( if is_opening and frappe.db.exists("Period Closing Voucher", {"docstatus": 1, "company": company}):
"Period Closing Voucher", {"docstatus": 1, "company": company}
):
frappe.throw( frappe.throw(
_("Opening Entry can not be created after Period Closing Voucher is created."), _("Opening Entry can not be created after Period Closing Voucher is created."),
title=_("Invalid Opening Entry"), title=_("Invalid Opening Entry"),
@@ -663,9 +653,7 @@ def validate_against_pcv(is_opening, posting_date, company):
) )
if last_pcv_date and getdate(posting_date) <= getdate(last_pcv_date): if last_pcv_date and getdate(posting_date) <= getdate(last_pcv_date):
message = _("Books have been closed till the period ending on {0}").format( message = _("Books have been closed till the period ending on {0}").format(formatdate(last_pcv_date))
formatdate(last_pcv_date)
)
message += "</br >" message += "</br >"
message += _("You cannot create/amend any accounting entries till this date.") message += _("You cannot create/amend any accounting entries till this date.")
frappe.throw(message, title=_("Period Closed")) frappe.throw(message, title=_("Period Closed"))

View File

@@ -2,8 +2,6 @@
# License: GNU General Public License v3. See license.txt # License: GNU General Public License v3. See license.txt
from typing import Optional
import frappe import frappe
from frappe import _, msgprint, qb, scrub from frappe import _, msgprint, qb, scrub
from frappe.contacts.doctype.address.address import get_company_address, get_default_address from frappe.contacts.doctype.address.address import get_company_address, get_default_address
@@ -69,7 +67,6 @@ def get_party_details(
shipping_address=None, shipping_address=None,
pos_profile=None, pos_profile=None,
): ):
if not party: if not party:
return {} return {}
if not frappe.db.exists(party_type, party): if not frappe.db.exists(party_type, party):
@@ -153,9 +150,7 @@ def _get_party_details(
party_details["taxes_and_charges"] = tax_template party_details["taxes_and_charges"] = tax_template
if cint(fetch_payment_terms_template): if cint(fetch_payment_terms_template):
party_details["payment_terms_template"] = get_payment_terms_template( party_details["payment_terms_template"] = get_payment_terms_template(party.name, party_type, company)
party.name, party_type, company
)
if not party_details.get("currency"): if not party_details.get("currency"):
party_details["currency"] = currency party_details["currency"] = currency
@@ -173,9 +168,7 @@ def _get_party_details(
# supplier tax withholding category # supplier tax withholding category
if party_type == "Supplier" and party: if party_type == "Supplier" and party:
party_details["supplier_tds"] = frappe.get_value( party_details["supplier_tds"] = frappe.get_value(party_type, party.name, "tax_withholding_category")
party_type, party.name, "tax_withholding_category"
)
if not party_details.get("tax_category") and pos_profile: if not party_details.get("tax_category") and pos_profile:
party_details["tax_category"] = frappe.get_value("POS Profile", pos_profile, "tax_category") party_details["tax_category"] = frappe.get_value("POS Profile", pos_profile, "tax_category")
@@ -195,12 +188,8 @@ def set_address_details(
*, *,
ignore_permissions=False, ignore_permissions=False,
): ):
billing_address_field = ( billing_address_field = "customer_address" if party_type == "Lead" else party_type.lower() + "_address"
"customer_address" if party_type == "Lead" else party_type.lower() + "_address" party_details[billing_address_field] = party_address or get_default_address(party_type, party.name)
)
party_details[billing_address_field] = party_address or get_default_address(
party_type, party.name
)
if doctype: if doctype:
party_details.update( party_details.update(
get_fetch_values(doctype, billing_address_field, party_details[billing_address_field]) get_fetch_values(doctype, billing_address_field, party_details[billing_address_field])
@@ -310,9 +299,7 @@ def set_contact_details(party_details, party, party_type):
"department as contact_department", "department as contact_department",
] ]
contact_details = frappe.db.get_value( contact_details = frappe.db.get_value("Contact", party_details.contact_person, fields, as_dict=True)
"Contact", party_details.contact_person, fields, as_dict=True
)
contact_details.contact_display = " ".join( contact_details.contact_display = " ".join(
filter( filter(
@@ -338,9 +325,7 @@ def set_other_values(party_details, party, party_type):
party_details[f] = party.get(f) party_details[f] = party.get(f)
# fields prepended with default in Customer doctype # fields prepended with default in Customer doctype
for f in ["currency"] + ( for f in ["currency"] + (["sales_partner", "commission_rate"] if party_type == "Customer" else []):
["sales_partner", "commission_rate"] if party_type == "Customer" else []
):
if party.get("default_" + f): if party.get("default_" + f):
party_details[f] = party.get("default_" + f) party_details[f] = party.get("default_" + f)
@@ -377,14 +362,10 @@ def set_price_list(party_details, party, party_type, given_price_list, pos=None)
"Price List", price_list, "currency", cache=True "Price List", price_list, "currency", cache=True
) )
party_details[ party_details["selling_price_list" if party.doctype == "Customer" else "buying_price_list"] = price_list
"selling_price_list" if party.doctype == "Customer" else "buying_price_list"
] = price_list
def set_account_and_due_date( def set_account_and_due_date(party, account, party_type, company, posting_date, bill_date, doctype):
party, account, party_type, company, posting_date, bill_date, doctype
):
if doctype not in ["POS Invoice", "Sales Invoice", "Purchase Invoice"]: if doctype not in ["POS Invoice", "Sales Invoice", "Purchase Invoice"]:
# not an invoice # not an invoice
return {party_type.lower(): party} return {party_type.lower(): party}
@@ -449,9 +430,7 @@ def get_party_account(party_type, party=None, company=None):
@frappe.whitelist() @frappe.whitelist()
def get_party_bank_account(party_type, party): def get_party_bank_account(party_type, party):
return frappe.db.get_value( return frappe.db.get_value("Bank Account", {"party_type": party_type, "party": party, "is_default": 1})
"Bank Account", {"party_type": party_type, "party": party, "is_default": 1}
)
def get_party_account_currency(party_type, party, company): def get_party_account_currency(party_type, party, company):
@@ -573,9 +552,7 @@ def get_due_date(posting_date, party_type, party, company=None, bill_date=None):
template_name = get_payment_terms_template(party, party_type, company) template_name = get_payment_terms_template(party, party_type, company)
if template_name: if template_name:
due_date = get_due_date_from_template(template_name, posting_date, bill_date).strftime( due_date = get_due_date_from_template(template_name, posting_date, bill_date).strftime("%Y-%m-%d")
"%Y-%m-%d"
)
else: else:
if party_type == "Supplier": if party_type == "Supplier":
supplier_group = frappe.get_cached_value(party_type, party, "supplier_group") supplier_group = frappe.get_cached_value(party_type, party, "supplier_group")
@@ -735,7 +712,6 @@ def get_payment_terms_template(party_name, party_type, company=None):
def validate_party_frozen_disabled(party_type, party_name): def validate_party_frozen_disabled(party_type, party_name):
if frappe.flags.ignore_party_validation: if frappe.flags.ignore_party_validation:
return return
@@ -748,7 +724,7 @@ def validate_party_frozen_disabled(party_type, party_name):
frozen_accounts_modifier = frappe.db.get_single_value( frozen_accounts_modifier = frappe.db.get_single_value(
"Accounts Settings", "frozen_accounts_modifier" "Accounts Settings", "frozen_accounts_modifier"
) )
if not frozen_accounts_modifier in frappe.get_roles(): if frozen_accounts_modifier not in frappe.get_roles():
frappe.throw(_("{0} {1} is frozen").format(party_type, party_name), PartyFrozen) frappe.throw(_("{0} {1} is frozen").format(party_type, party_name), PartyFrozen)
elif party_type == "Employee": elif party_type == "Employee":
@@ -871,9 +847,7 @@ def get_dashboard_info(party_type, party, loyalty_program=None):
party_account_currency = get_party_account_currency(party_type, party, d.company) party_account_currency = get_party_account_currency(party_type, party, d.company)
if party_account_currency == company_default_currency: if party_account_currency == company_default_currency:
billing_this_year = flt( billing_this_year = flt(company_wise_billing_this_year.get(d.company, {}).get("base_grand_total"))
company_wise_billing_this_year.get(d.company, {}).get("base_grand_total")
)
else: else:
billing_this_year = flt(company_wise_billing_this_year.get(d.company, {}).get("grand_total")) billing_this_year = flt(company_wise_billing_this_year.get(d.company, {}).get("grand_total"))
@@ -899,7 +873,7 @@ def get_dashboard_info(party_type, party, loyalty_program=None):
return company_wise_info return company_wise_info
def get_party_shipping_address(doctype: str, name: str) -> Optional[str]: def get_party_shipping_address(doctype: str, name: str) -> str | None:
""" """
Returns an Address name (best guess) for the given doctype and name for which `address_type == 'Shipping'` is true. Returns an Address name (best guess) for the given doctype and name for which `address_type == 'Shipping'` is true.
and/or `is_shipping_address = 1`. and/or `is_shipping_address = 1`.
@@ -965,7 +939,7 @@ def get_partywise_advanced_payment_amount(
return frappe._dict(data) return frappe._dict(data)
def get_default_contact(doctype: str, name: str) -> Optional[str]: def get_default_contact(doctype: str, name: str) -> str | None:
""" """
Returns contact name only if there is a primary contact for given doctype and name. Returns contact name only if there is a primary contact for given doctype and name.

View File

@@ -61,7 +61,6 @@ def get_conditions(filters):
def get_data(filters): def get_data(filters):
data = [] data = []
conditions = get_conditions(filters) conditions = get_conditions(filters)
accounts = frappe.db.get_all( accounts = frappe.db.get_all(

View File

@@ -1,16 +1,10 @@
import unittest
import frappe import frappe
from frappe.tests.utils import FrappeTestCase, change_settings from frappe.tests.utils import FrappeTestCase
from frappe.utils import add_days, flt, getdate, today from frappe.utils import today
from erpnext import get_default_cost_center
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.report.accounts_payable.accounts_payable import execute from erpnext.accounts.report.accounts_payable.accounts_payable import execute
from erpnext.accounts.test.accounts_mixin import AccountsTestMixin from erpnext.accounts.test.accounts_mixin import AccountsTestMixin
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):

View File

@@ -40,24 +40,20 @@ def execute(filters=None):
return ReceivablePayableReport(filters).run(args) return ReceivablePayableReport(filters).run(args)
class ReceivablePayableReport(object): class ReceivablePayableReport:
def __init__(self, filters=None): def __init__(self, filters=None):
self.filters = frappe._dict(filters or {}) self.filters = frappe._dict(filters or {})
self.qb_selection_filter = [] self.qb_selection_filter = []
self.ple = qb.DocType("Payment Ledger Entry") self.ple = qb.DocType("Payment Ledger Entry")
self.filters.report_date = getdate(self.filters.report_date or nowdate()) self.filters.report_date = getdate(self.filters.report_date or nowdate())
self.age_as_on = ( self.age_as_on = (
getdate(nowdate()) getdate(nowdate()) if self.filters.report_date > getdate(nowdate()) else self.filters.report_date
if self.filters.report_date > getdate(nowdate())
else self.filters.report_date
) )
def run(self, args): def run(self, args):
self.filters.update(args) self.filters.update(args)
self.set_defaults() self.set_defaults()
self.party_naming_by = frappe.db.get_value( self.party_naming_by = frappe.db.get_value(args.get("naming_by")[0], None, args.get("naming_by")[1])
args.get("naming_by")[0], None, args.get("naming_by")[1]
)
self.get_columns() self.get_columns()
self.get_data() self.get_data()
self.get_chart_data() self.get_chart_data()
@@ -72,9 +68,7 @@ class ReceivablePayableReport(object):
self.currency_precision = get_currency_precision() or 2 self.currency_precision = get_currency_precision() or 2
self.dr_or_cr = "debit" if self.filters.account_type == "Receivable" else "credit" self.dr_or_cr = "debit" if self.filters.account_type == "Receivable" else "credit"
self.account_type = self.filters.account_type self.account_type = self.filters.account_type
self.party_type = frappe.db.get_all( self.party_type = frappe.db.get_all("Party Type", {"account_type": self.account_type}, pluck="name")
"Party Type", {"account_type": self.account_type}, pluck="name"
)
self.party_details = {} self.party_details = {}
self.invoices = set() self.invoices = set()
self.skip_total_row = 0 self.skip_total_row = 0
@@ -128,7 +122,7 @@ class ReceivablePayableReport(object):
else: else:
key = (ple.account, ple.voucher_type, ple.voucher_no, ple.party) key = (ple.account, ple.voucher_type, ple.voucher_no, ple.party)
if not key in self.voucher_balance: if key not in self.voucher_balance:
self.voucher_balance[key] = frappe._dict( self.voucher_balance[key] = frappe._dict(
voucher_type=ple.voucher_type, voucher_type=ple.voucher_type,
voucher_no=ple.voucher_no, voucher_no=ple.voucher_no,
@@ -281,7 +275,7 @@ class ReceivablePayableReport(object):
def build_data(self): def build_data(self):
# set outstanding for all the accumulated balances # set outstanding for all the accumulated balances
# as we can use this to filter out invoices without outstanding # as we can use this to filter out invoices without outstanding
for key, row in self.voucher_balance.items(): for _key, row in self.voucher_balance.items():
row.outstanding = flt(row.invoiced - row.paid - row.credit_note, self.currency_precision) row.outstanding = flt(row.invoiced - row.paid - row.credit_note, self.currency_precision)
row.outstanding_in_account_currency = flt( row.outstanding_in_account_currency = flt(
row.invoiced_in_account_currency row.invoiced_in_account_currency
@@ -295,7 +289,7 @@ class ReceivablePayableReport(object):
must_consider = False must_consider = False
if self.filters.get("for_revaluation_journals"): if self.filters.get("for_revaluation_journals"):
if (abs(row.outstanding) > 0.0 / 10**self.currency_precision) or ( if (abs(row.outstanding) > 0.0 / 10**self.currency_precision) or (
(abs(row.outstanding_in_account_currency) > 0.0 / 10**self.currency_precision) abs(row.outstanding_in_account_currency) > 0.0 / 10**self.currency_precision
): ):
must_consider = True must_consider = True
else: else:
@@ -481,19 +475,17 @@ class ReceivablePayableReport(object):
def get_payment_terms(self, row): def get_payment_terms(self, row):
# build payment_terms for row # build payment_terms for row
payment_terms_details = frappe.db.sql( payment_terms_details = frappe.db.sql(
""" f"""
select select
si.name, si.party_account_currency, si.currency, si.conversion_rate, si.name, si.party_account_currency, si.currency, si.conversion_rate,
si.total_advance, ps.due_date, ps.payment_term, ps.payment_amount, ps.base_payment_amount, si.total_advance, ps.due_date, ps.payment_term, ps.payment_amount, ps.base_payment_amount,
ps.description, ps.paid_amount, ps.discounted_amount ps.description, ps.paid_amount, ps.discounted_amount
from `tab{0}` si, `tabPayment Schedule` ps from `tab{row.voucher_type}` si, `tabPayment Schedule` ps
where where
si.name = ps.parent and si.name = ps.parent and
si.name = %s si.name = %s
order by ps.paid_amount desc, due_date order by ps.paid_amount desc, due_date
""".format( """,
row.voucher_type
),
row.voucher_no, row.voucher_no,
as_dict=1, as_dict=1,
) )
@@ -737,9 +729,7 @@ class ReceivablePayableReport(object):
row.age = (getdate(self.age_as_on) - getdate(entry_date)).days or 0 row.age = (getdate(self.age_as_on) - getdate(entry_date)).days or 0
index = None index = None
if not ( if not (self.filters.range1 and self.filters.range2 and self.filters.range3 and self.filters.range4):
self.filters.range1 and self.filters.range2 and self.filters.range3 and self.filters.range4
):
self.filters.range1, self.filters.range2, self.filters.range3, self.filters.range4 = ( self.filters.range1, self.filters.range2, self.filters.range3, self.filters.range4 = (
30, 30,
60, 60,
@@ -765,12 +755,10 @@ class ReceivablePayableReport(object):
if self.filters.show_future_payments: if self.filters.show_future_payments:
self.qb_selection_filter.append( self.qb_selection_filter.append(
( self.ple.posting_date.lte(self.filters.report_date)
self.ple.posting_date.lte(self.filters.report_date) | (
| ( (self.ple.voucher_no == self.ple.against_voucher_no)
(self.ple.voucher_no == self.ple.against_voucher_no) & (Date(self.ple.creation).lte(self.filters.report_date))
& (Date(self.ple.creation).lte(self.filters.report_date))
)
) )
) )
else: else:
@@ -838,7 +826,7 @@ class ReceivablePayableReport(object):
self.qb_selection_filter = [] self.qb_selection_filter = []
self.or_filters = [] self.or_filters = []
for party_type in self.party_type: for _party_type in self.party_type:
self.add_common_filters() self.add_common_filters()
if self.account_type == "Receivable": if self.account_type == "Receivable":
@@ -975,7 +963,7 @@ class ReceivablePayableReport(object):
return True return True
def get_party_details(self, party): def get_party_details(self, party):
if not party in self.party_details: if party not in self.party_details:
if self.account_type == "Receivable": if self.account_type == "Receivable":
fields = ["customer_name", "territory", "customer_group", "customer_primary_contact"] fields = ["customer_name", "territory", "customer_group", "customer_primary_contact"]

Some files were not shown because too many files have changed in this diff Show More