Merge remote-tracking branch 'upstream/develop' into feat/so-po-advance-payment-status

This commit is contained in:
David Arnold 2023-12-04 13:35:32 +01:00
commit e824cd012b
No known key found for this signature in database
GPG Key ID: AB15A6AF1101390D
654 changed files with 17389 additions and 2025 deletions

View File

@ -73,8 +73,6 @@ New passwords will be created for the ERPNext "Administrator" user, the MariaDB
1. [Issue Guidelines](https://github.com/frappe/erpnext/wiki/Issue-Guidelines)
1. [Report Security Vulnerabilities](https://erpnext.com/security)
1. [Pull Request Requirements](https://github.com/frappe/erpnext/wiki/Contribution-Guidelines)
1. [Translations](https://translate.erpnext.com)
## License

View File

@ -3,7 +3,7 @@ import inspect
import frappe
__version__ = "15.0.0-dev"
__version__ = "16.0.0-dev"
def get_default_company(user=None):

View File

@ -23,6 +23,65 @@ class InvalidAccountMergeError(frappe.ValidationError):
class Account(NestedSet):
# 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
account_currency: DF.Link | None
account_name: DF.Data
account_number: DF.Data | None
account_type: DF.Literal[
"",
"Accumulated Depreciation",
"Asset Received But Not Billed",
"Bank",
"Cash",
"Chargeable",
"Capital Work in Progress",
"Cost of Goods Sold",
"Current Asset",
"Current Liability",
"Depreciation",
"Direct Expense",
"Direct Income",
"Equity",
"Expense Account",
"Expenses Included In Asset Valuation",
"Expenses Included In Valuation",
"Fixed Asset",
"Income Account",
"Indirect Expense",
"Indirect Income",
"Liability",
"Payable",
"Receivable",
"Round Off",
"Stock",
"Stock Adjustment",
"Stock Received But Not Billed",
"Service Received But Not Billed",
"Tax",
"Temporary",
]
balance_must_be: DF.Literal["", "Debit", "Credit"]
company: DF.Link
disabled: DF.Check
freeze_account: DF.Literal["No", "Yes"]
include_in_gross: DF.Check
is_group: DF.Check
lft: DF.Int
old_parent: DF.Data | None
parent_account: DF.Link
report_type: DF.Literal["", "Balance Sheet", "Profit and Loss"]
rgt: DF.Int
root_type: DF.Literal["", "Asset", "Liability", "Income", "Expense", "Equity"]
tax_rate: DF.Float
# end: auto-generated types
nsm_parent_field = "parent_account"
def on_update(self):

View File

@ -1,4 +1,6 @@
{
"country_code": "hu",
"name": "Hungary - Chart of Accounts for Microenterprises",
"tree": {
"SZ\u00c1MLAOSZT\u00c1LY BEFEKTETETT ESZK\u00d6Z\u00d6K": {
"account_number": 1,
@ -1651,4 +1653,4 @@
}
}
}
}
}

View File

@ -11,6 +11,29 @@ from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
class AccountClosingBalance(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
account: DF.Link | None
account_currency: DF.Link | None
closing_date: DF.Date | None
company: DF.Link | None
cost_center: DF.Link | None
credit: DF.Currency
credit_in_account_currency: DF.Currency
debit: DF.Currency
debit_in_account_currency: DF.Currency
finance_book: DF.Link | None
is_period_closing_voucher_entry: DF.Check
period_closing_voucher: DF.Link | None
project: DF.Link | None
# end: auto-generated types
pass

View File

@ -13,6 +13,25 @@ from frappe.utils import cstr
class AccountingDimension(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
from erpnext.accounts.doctype.accounting_dimension_detail.accounting_dimension_detail import (
AccountingDimensionDetail,
)
dimension_defaults: DF.Table[AccountingDimensionDetail]
disabled: DF.Check
document_type: DF.Link
fieldname: DF.Data | None
label: DF.Data | None
# end: auto-generated types
def before_insert(self):
self.set_fieldname_and_label()

View File

@ -7,4 +7,24 @@ from frappe.model.document import Document
class AccountingDimensionDetail(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
automatically_post_balancing_accounting_entry: DF.Check
company: DF.Link | None
default_dimension: DF.DynamicLink | None
mandatory_for_bs: DF.Check
mandatory_for_pl: DF.Check
offsetting_account: DF.Link | None
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
reference_document: DF.Link | None
# end: auto-generated types
pass

View File

@ -8,6 +8,28 @@ from frappe.model.document import Document
class AccountingDimensionFilter(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
from erpnext.accounts.doctype.allowed_dimension.allowed_dimension import AllowedDimension
from erpnext.accounts.doctype.applicable_on_account.applicable_on_account import (
ApplicableOnAccount,
)
accounting_dimension: DF.Literal
accounts: DF.Table[ApplicableOnAccount]
allow_or_restrict: DF.Literal["Allow", "Restrict"]
apply_restriction_on_values: DF.Check
company: DF.Link
dimensions: DF.Table[AllowedDimension]
disabled: DF.Check
# end: auto-generated types
def before_save(self):
# If restriction is not applied on values, then remove all the dimensions and set allow_or_restrict to Restrict
if not self.apply_restriction_on_values:

View File

@ -16,6 +16,23 @@ class ClosedAccountingPeriod(frappe.ValidationError):
class AccountingPeriod(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
from erpnext.accounts.doctype.closed_document.closed_document import ClosedDocument
closed_documents: DF.Table[ClosedDocument]
company: DF.Link
end_date: DF.Date
period_name: DF.Data
start_date: DF.Date
# end: auto-generated types
def validate(self):
self.validate_overlap()

View File

@ -66,7 +66,12 @@
"show_balance_in_coa",
"banking_tab",
"enable_party_matching",
"enable_fuzzy_matching"
"enable_fuzzy_matching",
"reports_tab",
"remarks_section",
"general_ledger_remarks_length",
"column_break_lvjk",
"receivable_payable_remarks_length"
],
"fields": [
{
@ -422,6 +427,34 @@
"fieldname": "round_row_wise_tax",
"fieldtype": "Check",
"label": "Round Tax Amount Row-wise"
},
{
"fieldname": "reports_tab",
"fieldtype": "Tab Break",
"label": "Reports"
},
{
"default": "0",
"description": "Truncates 'Remarks' column to set character length",
"fieldname": "general_ledger_remarks_length",
"fieldtype": "Int",
"label": "General Ledger"
},
{
"default": "0",
"description": "Truncates 'Remarks' column to set character length",
"fieldname": "receivable_payable_remarks_length",
"fieldtype": "Int",
"label": "Accounts Receivable/Payable"
},
{
"fieldname": "column_break_lvjk",
"fieldtype": "Column Break"
},
{
"fieldname": "remarks_section",
"fieldtype": "Section Break",
"label": "Remarks Column Length"
}
],
"icon": "icon-cog",
@ -429,7 +462,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2023-08-28 00:12:02.740633",
"modified": "2023-11-20 09:37:47.650347",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Settings",

View File

@ -14,6 +14,52 @@ from erpnext.stock.utils import check_pending_reposting
class AccountsSettings(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
acc_frozen_upto: DF.Date | None
add_taxes_from_item_tax_template: DF.Check
allow_multi_currency_invoices_against_single_party_account: DF.Check
allow_stale: DF.Check
auto_reconcile_payments: DF.Check
automatically_fetch_payment_terms: DF.Check
automatically_process_deferred_accounting_entry: DF.Check
book_asset_depreciation_entry_automatically: DF.Check
book_deferred_entries_based_on: DF.Literal["Days", "Months"]
book_deferred_entries_via_journal_entry: DF.Check
book_tax_discount_loss: DF.Check
check_supplier_invoice_uniqueness: DF.Check
credit_controller: DF.Link | None
delete_linked_ledger_entries: DF.Check
determine_address_tax_category_from: DF.Literal["Billing Address", "Shipping Address"]
enable_common_party_accounting: DF.Check
enable_fuzzy_matching: DF.Check
enable_party_matching: DF.Check
frozen_accounts_modifier: DF.Link | None
general_ledger_remarks_length: DF.Int
ignore_account_closing_balance: DF.Check
make_payment_via_journal_entry: DF.Check
merge_similar_account_heads: DF.Check
over_billing_allowance: DF.Currency
post_change_gl_entries: DF.Check
receivable_payable_remarks_length: DF.Int
role_allowed_to_over_bill: DF.Link | None
round_row_wise_tax: DF.Check
show_balance_in_coa: DF.Check
show_inclusive_tax_in_print: DF.Check
show_payment_schedule_in_print: DF.Check
show_taxes_as_table_in_print: DF.Check
stale_days: DF.Int
submit_journal_entries: DF.Check
unlink_advance_payment_on_cancelation_of_order: DF.Check
unlink_payment_on_cancellation_of_invoice: DF.Check
# end: auto-generated types
def validate(self):
old_doc = self.get_doc_before_save()
clear_cache = False

View File

@ -6,4 +6,22 @@ from frappe.model.document import Document
class AdvanceTax(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
account_head: DF.Link | None
allocated_amount: DF.Currency
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
reference_detail: DF.Data | None
reference_name: DF.DynamicLink | None
reference_type: DF.Link | None
# end: auto-generated types
pass

View File

@ -7,4 +7,33 @@ from frappe.model.document import Document
class AdvanceTaxesandCharges(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
account_head: DF.Link
add_deduct_tax: DF.Literal["Add", "Deduct"]
allocated_amount: DF.Currency
base_tax_amount: DF.Currency
base_total: DF.Currency
charge_type: DF.Literal[
"", "Actual", "On Paid Amount", "On Previous Row Amount", "On Previous Row Total"
]
cost_center: DF.Link | None
currency: DF.Link | None
description: DF.SmallText
included_in_paid_amount: DF.Check
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
rate: DF.Float
row_id: DF.Data | None
tax_amount: DF.Currency
total: DF.Currency
# end: auto-generated types
pass

View File

@ -7,4 +7,19 @@ from frappe.model.document import Document
class AllowedDimension(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
accounting_dimension: DF.Link | None
dimension_value: DF.DynamicLink | None
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
# end: auto-generated types
pass

View File

@ -6,4 +6,18 @@ from frappe.model.document import Document
class AllowedToTransactWith(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
company: DF.Link
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
# end: auto-generated types
pass

View File

@ -7,4 +7,19 @@ from frappe.model.document import Document
class ApplicableOnAccount(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
applicable_on_account: DF.Link
is_mandatory: DF.Check
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
# end: auto-generated types
pass

View File

@ -10,6 +10,25 @@ from frappe.model.document import Document
class Bank(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
from erpnext.accounts.doctype.bank_transaction_mapping.bank_transaction_mapping import (
BankTransactionMapping,
)
bank_name: DF.Data
bank_transaction_mapping: DF.Table[BankTransactionMapping]
plaid_access_token: DF.Data | None
swift_number: DF.Data | None
website: DF.Data | None
# end: auto-generated types
def onload(self):
"""Load address and contacts in `__onload`"""
load_address_and_contact(self)

View File

@ -12,6 +12,33 @@ from frappe.model.document import Document
class BankAccount(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
account: DF.Link | None
account_name: DF.Data
account_subtype: DF.Link | None
account_type: DF.Link | None
bank: DF.Link
bank_account_no: DF.Data | None
branch_code: DF.Data | None
company: DF.Link | None
disabled: DF.Check
iban: DF.Data | None
integration_id: DF.Data | None
is_company_account: DF.Check
is_default: DF.Check
last_integration_date: DF.Date | None
mask: DF.Data | None
party: DF.DynamicLink | None
party_type: DF.Link | None
# end: auto-generated types
def onload(self):
"""Load address and contacts in `__onload`"""
load_address_and_contact(self)

View File

@ -6,4 +6,15 @@ from frappe.model.document import Document
class BankAccountSubtype(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
account_subtype: DF.Data | None
# end: auto-generated types
pass

View File

@ -7,4 +7,15 @@ from frappe.model.document import Document
class BankAccountType(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
account_type: DF.Data | None
# end: auto-generated types
pass

View File

@ -13,6 +13,28 @@ form_grid_templates = {"journal_entries": "templates/form_grid/bank_reconciliati
class BankClearance(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
from erpnext.accounts.doctype.bank_clearance_detail.bank_clearance_detail import (
BankClearanceDetail,
)
account: DF.Link
account_currency: DF.Link | None
bank_account: DF.Link | None
from_date: DF.Date
include_pos_transactions: DF.Check
include_reconciled_entries: DF.Check
payment_entries: DF.Table[BankClearanceDetail]
to_date: DF.Date
# end: auto-generated types
@frappe.whitelist()
def get_payment_entries(self):
if not (self.from_date and self.to_date):

View File

@ -6,4 +6,25 @@ from frappe.model.document import Document
class BankClearanceDetail(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
against_account: DF.Data | None
amount: DF.Data | None
cheque_date: DF.Date | None
cheque_number: DF.Data | None
clearance_date: DF.Date | None
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
payment_document: DF.Link | None
payment_entry: DF.DynamicLink | None
posting_date: DF.Date | None
# end: auto-generated types
pass

View File

@ -8,6 +8,40 @@ from frappe.model.document import Document
class BankGuarantee(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
account: DF.Link | None
amended_from: DF.Link | None
amount: DF.Currency
bank: DF.Link | None
bank_account: DF.Link | None
bank_account_no: DF.Data | None
bank_guarantee_number: DF.Data | None
bg_type: DF.Literal["", "Receiving", "Providing"]
branch_code: DF.Data | None
charges: DF.Currency
customer: DF.Link | None
end_date: DF.Date | None
fixed_deposit_number: DF.Data | None
iban: DF.Data | None
margin_money: DF.Currency
more_information: DF.TextEditor | None
name_of_beneficiary: DF.Data | None
project: DF.Link | None
reference_docname: DF.DynamicLink | None
reference_doctype: DF.Link | None
start_date: DF.Date
supplier: DF.Link | None
swift_number: DF.Data | None
validity: DF.Int
# end: auto-generated types
def validate(self):
if not (self.customer or self.supplier):
frappe.throw(_("Select the customer or supplier."))

View File

@ -22,6 +22,26 @@ from erpnext.setup.utils import get_exchange_rate
class BankReconciliationTool(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
account_currency: DF.Link | None
account_opening_balance: DF.Currency
bank_account: DF.Link | None
bank_statement_closing_balance: DF.Currency
bank_statement_from_date: DF.Date | None
bank_statement_to_date: DF.Date | None
company: DF.Link | None
filter_by_reference_date: DF.Check
from_reference_date: DF.Date | None
to_reference_date: DF.Date | None
# end: auto-generated types
pass
@ -424,7 +444,9 @@ def reconcile_vouchers(bank_transaction_name, vouchers):
vouchers = json.loads(vouchers)
transaction = frappe.get_doc("Bank Transaction", bank_transaction_name)
transaction.add_payment_entries(vouchers)
return frappe.get_doc("Bank Transaction", bank_transaction_name)
transaction.save()
return transaction
@frappe.whitelist()

View File

@ -20,6 +20,30 @@ INVALID_VALUES = ("", None)
class BankStatementImport(DataImport):
# 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
bank: DF.Link | None
bank_account: DF.Link
company: DF.Link
google_sheets_url: DF.Data | None
import_file: DF.Attach | None
import_type: DF.Literal["", "Insert New Records", "Update Existing Records"]
mute_emails: DF.Check
reference_doctype: DF.Link
show_failed_logs: DF.Check
statement_import_log: DF.Code | None
status: DF.Literal["Pending", "Success", "Partial Success", "Error"]
submit_after_import: DF.Check
template_options: DF.Code | None
template_warnings: DF.Code | None
# end: auto-generated types
def __init__(self, *args, **kwargs):
super(BankStatementImport, self).__init__(*args, **kwargs)

View File

@ -13,6 +13,7 @@
"status",
"bank_account",
"company",
"amended_from",
"section_break_4",
"deposit",
"withdrawal",
@ -25,10 +26,10 @@
"transaction_id",
"transaction_type",
"section_break_14",
"column_break_oufv",
"payment_entries",
"section_break_18",
"allocated_amount",
"amended_from",
"column_break_17",
"unallocated_amount",
"party_section",
@ -138,10 +139,12 @@
"fieldtype": "Section Break"
},
{
"allow_on_submit": 1,
"fieldname": "allocated_amount",
"fieldtype": "Currency",
"label": "Allocated Amount",
"options": "currency"
"options": "currency",
"read_only": 1
},
{
"fieldname": "amended_from",
@ -157,10 +160,12 @@
"fieldtype": "Column Break"
},
{
"allow_on_submit": 1,
"fieldname": "unallocated_amount",
"fieldtype": "Currency",
"label": "Unallocated Amount",
"options": "currency"
"options": "currency",
"read_only": 1
},
{
"fieldname": "party_section",
@ -225,11 +230,15 @@
"fieldname": "bank_party_account_number",
"fieldtype": "Data",
"label": "Party Account No. (Bank Statement)"
},
{
"fieldname": "column_break_oufv",
"fieldtype": "Column Break"
}
],
"is_submittable": 1,
"links": [],
"modified": "2023-06-06 13:58:12.821411",
"modified": "2023-11-18 18:32:47.203694",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Transaction",

View File

@ -2,78 +2,108 @@
# For license information, please see license.txt
import frappe
from frappe import _
from frappe.utils import flt
from erpnext.controllers.status_updater import StatusUpdater
class BankTransaction(StatusUpdater):
def after_insert(self):
self.unallocated_amount = abs(flt(self.withdrawal) - flt(self.deposit))
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.
def on_submit(self):
self.clear_linked_payment_entries()
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from frappe.types import DF
from erpnext.accounts.doctype.bank_transaction_payments.bank_transaction_payments import (
BankTransactionPayments,
)
allocated_amount: DF.Currency
amended_from: DF.Link | None
bank_account: DF.Link | None
bank_party_account_number: DF.Data | None
bank_party_iban: DF.Data | None
bank_party_name: DF.Data | None
company: DF.Link | None
currency: DF.Link | None
date: DF.Date | None
deposit: DF.Currency
description: DF.SmallText | None
naming_series: DF.Literal["ACC-BTN-.YYYY.-"]
party: DF.DynamicLink | None
party_type: DF.Link | None
payment_entries: DF.Table[BankTransactionPayments]
reference_number: DF.Data | None
status: DF.Literal["", "Pending", "Settled", "Unreconciled", "Reconciled", "Cancelled"]
transaction_id: DF.Data | None
transaction_type: DF.Data | None
unallocated_amount: DF.Currency
withdrawal: DF.Currency
# end: auto-generated types
def before_validate(self):
self.update_allocated_amount()
def validate(self):
self.validate_duplicate_references()
def validate_duplicate_references(self):
"""Make sure the same voucher is not allocated twice within the same Bank Transaction"""
if not self.payment_entries:
return
pe = []
for row in self.payment_entries:
reference = (row.payment_document, row.payment_entry)
if reference in pe:
frappe.throw(
_("{0} {1} is allocated twice in this Bank Transaction").format(
row.payment_document, row.payment_entry
)
)
pe.append(reference)
def update_allocated_amount(self):
self.allocated_amount = (
sum(p.allocated_amount for p in self.payment_entries) if self.payment_entries else 0.0
)
self.unallocated_amount = abs(flt(self.withdrawal) - flt(self.deposit)) - self.allocated_amount
def before_submit(self):
self.allocate_payment_entries()
self.set_status()
if frappe.db.get_single_value("Accounts Settings", "enable_party_matching"):
self.auto_set_party()
_saving_flag = False
# nosemgrep: frappe-semgrep-rules.rules.frappe-modifying-but-not-comitting
def on_update_after_submit(self):
"Run on save(). Avoid recursion caused by multiple saves"
if not self._saving_flag:
self._saving_flag = True
self.clear_linked_payment_entries()
self.update_allocations()
self._saving_flag = False
def before_update_after_submit(self):
self.validate_duplicate_references()
self.allocate_payment_entries()
self.update_allocated_amount()
def on_cancel(self):
self.clear_linked_payment_entries(for_cancel=True)
self.set_status(update=True)
for payment_entry in self.payment_entries:
self.clear_linked_payment_entry(payment_entry, for_cancel=True)
def update_allocations(self):
"The doctype does not allow modifications after submission, so write to the db direct"
if self.payment_entries:
allocated_amount = sum(p.allocated_amount for p in self.payment_entries)
else:
allocated_amount = 0.0
amount = abs(flt(self.withdrawal) - flt(self.deposit))
self.db_set("allocated_amount", flt(allocated_amount))
self.db_set("unallocated_amount", amount - flt(allocated_amount))
self.reload()
self.set_status(update=True)
def add_payment_entries(self, vouchers):
"Add the vouchers with zero allocation. Save() will perform the allocations and clearance"
if 0.0 >= self.unallocated_amount:
frappe.throw(frappe._("Bank Transaction {0} is already fully reconciled").format(self.name))
frappe.throw(_("Bank Transaction {0} is already fully reconciled").format(self.name))
added = False
for voucher in vouchers:
# Can't add same voucher twice
found = False
for pe in self.payment_entries:
if (
pe.payment_document == voucher["payment_doctype"]
and pe.payment_entry == voucher["payment_name"]
):
found = True
if not found:
pe = {
self.append(
"payment_entries",
{
"payment_document": voucher["payment_doctype"],
"payment_entry": voucher["payment_name"],
"allocated_amount": 0.0, # Temporary
}
child = self.append("payment_entries", pe)
added = True
# runs on_update_after_submit
if added:
self.save()
},
)
def allocate_payment_entries(self):
"""Refactored from bank reconciliation tool.
@ -90,6 +120,7 @@ class BankTransaction(StatusUpdater):
- clear means: set the latest transaction date as clearance date
"""
remaining_amount = self.unallocated_amount
to_remove = []
for payment_entry in self.payment_entries:
if payment_entry.allocated_amount == 0.0:
unallocated_amount, should_clear, latest_transaction = get_clearance_details(
@ -99,49 +130,39 @@ class BankTransaction(StatusUpdater):
if 0.0 == unallocated_amount:
if should_clear:
latest_transaction.clear_linked_payment_entry(payment_entry)
self.db_delete_payment_entry(payment_entry)
to_remove.append(payment_entry)
elif remaining_amount <= 0.0:
self.db_delete_payment_entry(payment_entry)
to_remove.append(payment_entry)
elif 0.0 < unallocated_amount and unallocated_amount <= remaining_amount:
payment_entry.db_set("allocated_amount", unallocated_amount)
elif 0.0 < unallocated_amount <= remaining_amount:
payment_entry.allocated_amount = unallocated_amount
remaining_amount -= unallocated_amount
if should_clear:
latest_transaction.clear_linked_payment_entry(payment_entry)
elif 0.0 < unallocated_amount and unallocated_amount > remaining_amount:
payment_entry.db_set("allocated_amount", remaining_amount)
elif 0.0 < unallocated_amount:
payment_entry.allocated_amount = remaining_amount
remaining_amount = 0.0
elif 0.0 > unallocated_amount:
self.db_delete_payment_entry(payment_entry)
frappe.throw(frappe._("Voucher {0} is over-allocated by {1}").format(unallocated_amount))
frappe.throw(_("Voucher {0} is over-allocated by {1}").format(unallocated_amount))
self.reload()
def db_delete_payment_entry(self, payment_entry):
frappe.db.delete("Bank Transaction Payments", {"name": payment_entry.name})
for payment_entry in to_remove:
self.remove(to_remove)
@frappe.whitelist()
def remove_payment_entries(self):
for payment_entry in self.payment_entries:
self.remove_payment_entry(payment_entry)
# runs on_update_after_submit
self.save()
self.save() # runs before_update_after_submit
def remove_payment_entry(self, payment_entry):
"Clear payment entry and clearance"
self.clear_linked_payment_entry(payment_entry, for_cancel=True)
self.remove(payment_entry)
def clear_linked_payment_entries(self, for_cancel=False):
if for_cancel:
for payment_entry in self.payment_entries:
self.clear_linked_payment_entry(payment_entry, for_cancel)
else:
self.allocate_payment_entries()
def clear_linked_payment_entry(self, payment_entry, for_cancel=False):
clearance_date = None if for_cancel else self.date
set_voucher_clearance(
@ -162,11 +183,10 @@ class BankTransaction(StatusUpdater):
deposit=self.deposit,
).match()
if result:
party_type, party = result
frappe.db.set_value(
"Bank Transaction", self.name, field={"party_type": party_type, "party": party}
)
if not result:
return
self.party_type, self.party = result
@frappe.whitelist()
@ -198,9 +218,7 @@ def get_clearance_details(transaction, payment_entry):
if gle["gl_account"] == gl_bank_account:
if gle["amount"] <= 0.0:
frappe.throw(
frappe._("Voucher {0} value is broken: {1}").format(
payment_entry.payment_entry, gle["amount"]
)
_("Voucher {0} value is broken: {1}").format(payment_entry.payment_entry, gle["amount"])
)
unmatched_gles -= 1
@ -221,7 +239,7 @@ def get_clearance_details(transaction, payment_entry):
def get_related_bank_gl_entries(doctype, docname):
# nosemgrep: frappe-semgrep-rules.rules.frappe-using-db-sql
result = frappe.db.sql(
return frappe.db.sql(
"""
SELECT
ABS(gle.credit_in_account_currency - gle.debit_in_account_currency) AS amount,
@ -239,7 +257,6 @@ def get_related_bank_gl_entries(doctype, docname):
dict(doctype=doctype, docname=docname),
as_dict=True,
)
return result
def get_total_allocated_amount(doctype, docname):
@ -365,6 +382,7 @@ def set_voucher_clearance(doctype, docname, clearance_date, self):
if clearance_date:
vouchers = [{"payment_doctype": "Bank Transaction", "payment_name": self.name}]
bt.add_payment_entries(vouchers)
bt.save()
else:
for pe in bt.payment_entries:
if pe.payment_document == self.doctype and pe.payment_entry == self.name:

View File

@ -6,4 +6,19 @@ from frappe.model.document import Document
class BankTransactionMapping(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
bank_transaction_field: DF.Literal
file_field: DF.Data
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
# end: auto-generated types
pass

View File

@ -6,4 +6,21 @@ from frappe.model.document import Document
class BankTransactionPayments(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
allocated_amount: DF.Currency
clearance_date: DF.Date | None
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
payment_document: DF.Link
payment_entry: DF.DynamicLink
# end: auto-generated types
pass

View File

@ -22,6 +22,36 @@ class DuplicateBudgetError(frappe.ValidationError):
class Budget(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
from erpnext.accounts.doctype.budget_account.budget_account import BudgetAccount
accounts: DF.Table[BudgetAccount]
action_if_accumulated_monthly_budget_exceeded: DF.Literal["", "Stop", "Warn", "Ignore"]
action_if_accumulated_monthly_budget_exceeded_on_mr: DF.Literal["", "Stop", "Warn", "Ignore"]
action_if_accumulated_monthly_budget_exceeded_on_po: DF.Literal["", "Stop", "Warn", "Ignore"]
action_if_annual_budget_exceeded: DF.Literal["", "Stop", "Warn", "Ignore"]
action_if_annual_budget_exceeded_on_mr: DF.Literal["", "Stop", "Warn", "Ignore"]
action_if_annual_budget_exceeded_on_po: DF.Literal["", "Stop", "Warn", "Ignore"]
amended_from: DF.Link | None
applicable_on_booking_actual_expenses: DF.Check
applicable_on_material_request: DF.Check
applicable_on_purchase_order: DF.Check
budget_against: DF.Literal["", "Cost Center", "Project"]
company: DF.Link
cost_center: DF.Link | None
fiscal_year: DF.Link
monthly_distribution: DF.Link | None
naming_series: DF.Data | None
project: DF.Link | None
# end: auto-generated types
def validate(self):
if not self.get(frappe.scrub(self.budget_against)):
frappe.throw(_("{0} is mandatory").format(self.budget_against))

View File

@ -6,4 +6,19 @@ from frappe.model.document import Document
class BudgetAccount(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
account: DF.Link
budget_amount: DF.Currency
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
# end: auto-generated types
pass

View File

@ -6,4 +6,18 @@ from frappe.model.document import Document
class CampaignItem(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
campaign: DF.Link | None
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
# end: auto-generated types
pass

View File

@ -9,6 +9,32 @@ from frappe.utils import flt
class CashierClosing(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
from erpnext.accounts.doctype.cashier_closing_payments.cashier_closing_payments import (
CashierClosingPayments,
)
amended_from: DF.Link | None
custody: DF.Float
date: DF.Date | None
expense: DF.Float
from_time: DF.Time
naming_series: DF.Literal["POS-CLO-"]
net_amount: DF.Float
outstanding_amount: DF.Float
payments: DF.Table[CashierClosingPayments]
returns: DF.Float
time: DF.Time
user: DF.Link
# end: auto-generated types
def validate(self):
self.validate_time()

View File

@ -6,4 +6,19 @@ from frappe.model.document import Document
class CashierClosingPayments(Document):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from frappe.types import DF
amount: DF.Float
mode_of_payment: DF.Link
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
# end: auto-generated types
pass

View File

@ -24,6 +24,18 @@ from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import
class ChartofAccountsImporter(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
company: DF.Link | None
import_file: DF.Attach | None
# end: auto-generated types
def validate(self):
if self.import_file:
get_coa(
@ -113,7 +125,7 @@ def generate_data_from_csv(file_doc, as_dict=False):
if as_dict:
data.append({frappe.scrub(header): row[index] for index, header in enumerate(headers)})
else:
if not row[1]:
if not row[1] and len(row) > 1:
row[1] = row[0]
row[3] = row[2]
data.append(row)

View File

@ -8,6 +8,41 @@ from frappe.model.document import Document
class ChequePrintTemplate(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
acc_no_dist_from_left_edge: DF.Float
acc_no_dist_from_top_edge: DF.Float
acc_pay_dist_from_left_edge: DF.Float
acc_pay_dist_from_top_edge: DF.Float
amt_in_figures_from_left_edge: DF.Float
amt_in_figures_from_top_edge: DF.Float
amt_in_word_width: DF.Float
amt_in_words_from_left_edge: DF.Float
amt_in_words_from_top_edge: DF.Float
amt_in_words_line_spacing: DF.Float
bank_name: DF.Data
cheque_height: DF.Float
cheque_size: DF.Literal["", "Regular", "A4"]
cheque_width: DF.Float
date_dist_from_left_edge: DF.Float
date_dist_from_top_edge: DF.Float
has_print_format: DF.Check
is_account_payable: DF.Check
message_to_show: DF.Data | None
payer_name_from_left_edge: DF.Float
payer_name_from_top_edge: DF.Float
scanned_cheque: DF.Attach | None
signatory_from_left_edge: DF.Float
signatory_from_top_edge: DF.Float
starting_position_from_top_edge: DF.Float
# end: auto-generated types
pass

View File

@ -6,4 +6,19 @@ from frappe.model.document import Document
class ClosedDocument(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
closed: DF.Check
document_type: DF.Link
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
# end: auto-generated types
pass

View File

@ -10,6 +10,25 @@ from erpnext.accounts.utils import validate_field_number
class CostCenter(NestedSet):
# 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
company: DF.Link
cost_center_name: DF.Data
cost_center_number: DF.Data | None
disabled: DF.Check
is_group: DF.Check
lft: DF.Int
old_parent: DF.Link | None
parent_cost_center: DF.Link
rgt: DF.Int
# end: auto-generated types
nsm_parent_field = "parent_cost_center"
def autoname(self):

View File

@ -28,6 +28,25 @@ class InvalidDateError(frappe.ValidationError):
class CostCenterAllocation(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
from erpnext.accounts.doctype.cost_center_allocation_percentage.cost_center_allocation_percentage import (
CostCenterAllocationPercentage,
)
allocation_percentages: DF.Table[CostCenterAllocationPercentage]
amended_from: DF.Link | None
company: DF.Link
main_cost_center: DF.Link
valid_from: DF.Date
# end: auto-generated types
def __init__(self, *args, **kwargs):
super(CostCenterAllocation, self).__init__(*args, **kwargs)
self._skip_from_date_validation = False

View File

@ -6,4 +6,19 @@ from frappe.model.document import Document
class CostCenterAllocationPercentage(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
cost_center: DF.Link
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
percentage: DF.Percent
# end: auto-generated types
pass

View File

@ -9,6 +9,27 @@ from frappe.utils import strip
class CouponCode(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
amended_from: DF.Link | None
coupon_code: DF.Data | None
coupon_name: DF.Data
coupon_type: DF.Literal["Promotional", "Gift Card"]
customer: DF.Link | None
description: DF.TextEditor | None
maximum_use: DF.Int
pricing_rule: DF.Link
used: DF.Int
valid_from: DF.Date | None
valid_upto: DF.Date | None
# end: auto-generated types
def autoname(self):
self.coupon_name = strip(self.coupon_name)
self.name = self.coupon_name

View File

@ -9,6 +9,30 @@ from frappe.utils import nowdate
class CurrencyExchangeSettings(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
from erpnext.accounts.doctype.currency_exchange_settings_details.currency_exchange_settings_details import (
CurrencyExchangeSettingsDetails,
)
from erpnext.accounts.doctype.currency_exchange_settings_result.currency_exchange_settings_result import (
CurrencyExchangeSettingsResult,
)
access_key: DF.Data | None
api_endpoint: DF.Data
disabled: DF.Check
req_params: DF.Table[CurrencyExchangeSettingsDetails]
result_key: DF.Table[CurrencyExchangeSettingsResult]
service_provider: DF.Literal["frankfurter.app", "exchangerate.host", "Custom"]
url: DF.Data | None
# end: auto-generated types
def validate(self):
self.set_parameters_and_result()
if frappe.flags.in_test or frappe.flags.in_install or frappe.flags.in_setup_wizard:

View File

@ -6,4 +6,19 @@ from frappe.model.document import Document
class CurrencyExchangeSettingsDetails(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
key: DF.Data
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
value: DF.Data
# end: auto-generated types
pass

View File

@ -6,4 +6,18 @@ from frappe.model.document import Document
class CurrencyExchangeSettingsResult(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
key: DF.Data
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
# end: auto-generated types
pass

View File

@ -6,4 +6,18 @@ from frappe.model.document import Document
class CustomerGroupItem(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
customer_group: DF.Link | None
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
# end: auto-generated types
pass

View File

@ -6,4 +6,18 @@ from frappe.model.document import Document
class CustomerItem(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
customer: DF.Link | None
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
# end: auto-generated types
pass

View File

@ -7,4 +7,22 @@ from frappe.model.document import Document
class DiscountedInvoice(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
customer: DF.Link | None
debit_to: DF.Link | None
outstanding_amount: DF.Currency
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
posting_date: DF.Date | None
sales_invoice: DF.Link
# end: auto-generated types
pass

View File

@ -22,6 +22,52 @@ from erpnext.controllers.accounts_controller import AccountsController
class Dunning(AccountsController):
# 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
from erpnext.accounts.doctype.overdue_payment.overdue_payment import OverduePayment
address_display: DF.SmallText | None
amended_from: DF.Link | None
base_dunning_amount: DF.Currency
body_text: DF.TextEditor | None
closing_text: DF.TextEditor | None
company: DF.Link
company_address: DF.Link | None
company_address_display: DF.SmallText | None
contact_display: DF.SmallText | None
contact_email: DF.Data | None
contact_mobile: DF.SmallText | None
contact_person: DF.Link | None
conversion_rate: DF.Float
cost_center: DF.Link | None
currency: DF.Link | None
customer: DF.Link
customer_address: DF.Link | None
customer_name: DF.Data | None
dunning_amount: DF.Currency
dunning_fee: DF.Currency
dunning_type: DF.Link | None
grand_total: DF.Currency
income_account: DF.Link | None
language: DF.Link | None
letter_head: DF.Link | None
naming_series: DF.Literal["DUNN-.MM.-.YY.-"]
overdue_payments: DF.Table[OverduePayment]
posting_date: DF.Date
posting_time: DF.Time | None
rate_of_interest: DF.Float
spacer: DF.Data | None
status: DF.Literal["Draft", "Resolved", "Unresolved", "Cancelled"]
total_interest: DF.Currency
total_outstanding: DF.Currency
# end: auto-generated types
def validate(self):
self.validate_same_currency()
self.validate_overdue_payments()

View File

@ -7,4 +7,21 @@ from frappe.model.document import Document
class DunningLetterText(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
body_text: DF.TextEditor | None
closing_text: DF.TextEditor | None
is_default_language: DF.Check
language: DF.Link | None
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
# end: auto-generated types
pass

View File

@ -7,6 +7,26 @@ from frappe.model.document import Document
class DunningType(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
from erpnext.accounts.doctype.dunning_letter_text.dunning_letter_text import DunningLetterText
company: DF.Link
cost_center: DF.Link | None
dunning_fee: DF.Currency
dunning_letter_text: DF.Table[DunningLetterText]
dunning_type: DF.Data
income_account: DF.Link | None
is_default: DF.Check
rate_of_interest: DF.Float
# end: auto-generated types
def autoname(self):
company_abbr = frappe.get_value("Company", self.company, "abbr")
self.name = f"{self.dunning_type} - {company_abbr}"

View File

@ -17,6 +17,28 @@ from erpnext.setup.utils import get_exchange_rate
class ExchangeRateRevaluation(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
from erpnext.accounts.doctype.exchange_rate_revaluation_account.exchange_rate_revaluation_account import (
ExchangeRateRevaluationAccount,
)
accounts: DF.Table[ExchangeRateRevaluationAccount]
amended_from: DF.Link | None
company: DF.Link
gain_loss_booked: DF.Currency
gain_loss_unbooked: DF.Currency
posting_date: DF.Date
rounding_loss_allowance: DF.Float
total_gain_loss: DF.Currency
# end: auto-generated types
def validate(self):
self.validate_rounding_loss_allowance()
self.set_total_gain_loss()
@ -192,7 +214,7 @@ class ExchangeRateRevaluation(Document):
# round off balance based on currency precision
# and consider debit-credit difference allowance
currency_precision = get_currency_precision()
rounding_loss_allowance = float(rounding_loss_allowance) or 0.05
rounding_loss_allowance = float(rounding_loss_allowance)
for acc in account_details:
acc.balance_in_account_currency = flt(acc.balance_in_account_currency, currency_precision)
if abs(acc.balance_in_account_currency) <= rounding_loss_allowance:

View File

@ -6,4 +6,29 @@ from frappe.model.document import Document
class ExchangeRateRevaluationAccount(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
account: DF.Link
account_currency: DF.Link | None
balance_in_account_currency: DF.Currency
balance_in_base_currency: DF.Currency
current_exchange_rate: DF.Float
gain_loss: DF.Currency
new_balance_in_account_currency: DF.Currency
new_balance_in_base_currency: DF.Currency
new_exchange_rate: DF.Float
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
party: DF.DynamicLink | None
party_type: DF.Link | None
zero_balance: DF.Check
# end: auto-generated types
pass

View File

@ -6,4 +6,15 @@ from frappe.model.document import Document
class FinanceBook(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
finance_book_name: DF.Data | None
# end: auto-generated types
pass

View File

@ -10,6 +10,25 @@ from frappe.utils import add_days, add_years, cstr, getdate
class FiscalYear(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
from erpnext.accounts.doctype.fiscal_year_company.fiscal_year_company import FiscalYearCompany
auto_created: DF.Check
companies: DF.Table[FiscalYearCompany]
disabled: DF.Check
is_short_year: DF.Check
year: DF.Data
year_end_date: DF.Date
year_start_date: DF.Date
# end: auto-generated types
def validate(self):
self.validate_dates()
self.validate_overlap()

View File

@ -6,4 +6,18 @@ from frappe.model.document import Document
class FiscalYearCompany(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
company: DF.Link | None
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
# end: auto-generated types
pass

View File

@ -28,6 +28,47 @@ exclude_from_linked_with = True
class GLEntry(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
account: DF.Link | None
account_currency: DF.Link | None
against: DF.Text | None
against_voucher: DF.DynamicLink | None
against_voucher_type: DF.Link | None
company: DF.Link | None
cost_center: DF.Link | None
credit: DF.Currency
credit_in_account_currency: DF.Currency
credit_in_transaction_currency: DF.Currency
debit: DF.Currency
debit_in_account_currency: DF.Currency
debit_in_transaction_currency: DF.Currency
due_date: DF.Date | None
finance_book: DF.Link | None
fiscal_year: DF.Link | None
is_advance: DF.Literal["No", "Yes"]
is_cancelled: DF.Check
is_opening: DF.Literal["No", "Yes"]
party: DF.DynamicLink | None
party_type: DF.Link | None
posting_date: DF.Date | None
project: DF.Link | None
remarks: DF.Text | None
to_rename: DF.Check
transaction_currency: DF.Link | None
transaction_date: DF.Date | None
transaction_exchange_rate: DF.Float
voucher_detail_no: DF.Data | None
voucher_no: DF.DynamicLink | None
voucher_type: DF.Link | None
# end: auto-generated types
def autoname(self):
"""
Temporarily name doc for fast insertion

View File

@ -17,6 +17,34 @@ from erpnext.controllers.accounts_controller import AccountsController
class InvoiceDiscounting(AccountsController):
# 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
from erpnext.accounts.doctype.discounted_invoice.discounted_invoice import DiscountedInvoice
accounts_receivable_credit: DF.Link
accounts_receivable_discounted: DF.Link
accounts_receivable_unpaid: DF.Link
amended_from: DF.Link | None
bank_account: DF.Link
bank_charges: DF.Currency
bank_charges_account: DF.Link
company: DF.Link
invoices: DF.Table[DiscountedInvoice]
loan_end_date: DF.Date | None
loan_period: DF.Int
loan_start_date: DF.Date | None
posting_date: DF.Date
short_term_loan: DF.Link
status: DF.Literal["Draft", "Sanctioned", "Disbursed", "Settled", "Cancelled"]
total_amount: DF.Currency
# end: auto-generated types
def validate(self):
self.validate_mandatory()
self.validate_invoices()

View File

@ -8,6 +8,24 @@ from frappe.model.document import Document
class ItemTaxTemplate(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
from erpnext.accounts.doctype.item_tax_template_detail.item_tax_template_detail import (
ItemTaxTemplateDetail,
)
company: DF.Link
disabled: DF.Check
taxes: DF.Table[ItemTaxTemplateDetail]
title: DF.Data
# end: auto-generated types
def validate(self):
self.validate_tax_accounts()

View File

@ -6,4 +6,19 @@ from frappe.model.document import Document
class ItemTaxTemplateDetail(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
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
tax_rate: DF.Float
tax_type: DF.Link
# end: auto-generated types
pass

View File

@ -51,7 +51,7 @@ frappe.ui.form.on("Journal Entry", {
}, __('Make'));
}
erpnext.accounts.unreconcile_payments.add_unreconcile_btn(frm);
erpnext.accounts.unreconcile_payment.add_unreconcile_btn(frm);
},
before_save: function(frm) {
if ((frm.doc.docstatus == 0) && (!frm.doc.is_system_generated)) {

View File

@ -548,8 +548,16 @@
"icon": "fa fa-file-text",
"idx": 176,
"is_submittable": 1,
"links": [],
"modified": "2023-08-10 14:32:22.366895",
"links": [
{
"is_child_table": 1,
"link_doctype": "Bank Transaction Payments",
"link_fieldname": "payment_entry",
"parent_doctype": "Bank Transaction",
"table_fieldname": "payment_entries"
}
],
"modified": "2023-11-23 12:11:04.128015",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry",

View File

@ -35,6 +35,78 @@ class StockAccountInvalidTransaction(frappe.ValidationError):
class JournalEntry(AccountsController):
# 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
from erpnext.accounts.doctype.journal_entry_account.journal_entry_account import (
JournalEntryAccount,
)
accounts: DF.Table[JournalEntryAccount]
amended_from: DF.Link | None
apply_tds: DF.Check
auto_repeat: DF.Link | None
bill_date: DF.Date | None
bill_no: DF.Data | None
cheque_date: DF.Date | None
cheque_no: DF.Data | None
clearance_date: DF.Date | None
company: DF.Link
difference: DF.Currency
due_date: DF.Date | None
finance_book: DF.Link | None
from_template: DF.Link | None
inter_company_journal_entry_reference: DF.Link | None
is_opening: DF.Literal["No", "Yes"]
is_system_generated: DF.Check
letter_head: DF.Link | None
mode_of_payment: DF.Link | None
multi_currency: DF.Check
naming_series: DF.Literal["ACC-JV-.YYYY.-"]
paid_loan: DF.Data | None
pay_to_recd_from: DF.Data | None
payment_order: DF.Link | None
posting_date: DF.Date
process_deferred_accounting: DF.Link | None
remark: DF.SmallText | None
reversal_of: DF.Link | None
select_print_heading: DF.Link | None
stock_entry: DF.Link | None
tax_withholding_category: DF.Link | None
title: DF.Data | None
total_amount: DF.Currency
total_amount_currency: DF.Link | None
total_amount_in_words: DF.Data | None
total_credit: DF.Currency
total_debit: DF.Currency
user_remark: DF.SmallText | None
voucher_type: DF.Literal[
"Journal Entry",
"Inter Company Journal Entry",
"Bank Entry",
"Cash Entry",
"Credit Card Entry",
"Debit Note",
"Credit Note",
"Contra Entry",
"Excise Entry",
"Write Off Entry",
"Opening Entry",
"Depreciation Entry",
"Exchange Rate Revaluation",
"Exchange Gain Or Loss",
"Deferred Revenue",
"Deferred Expense",
]
write_off_amount: DF.Currency
write_off_based_on: DF.Literal["Accounts Receivable", "Accounts Payable"]
# end: auto-generated types
def __init__(self, *args, **kwargs):
super(JournalEntry, self).__init__(*args, **kwargs)
@ -508,7 +580,7 @@ class JournalEntry(AccountsController):
).format(d.reference_name, d.account)
)
else:
dr_or_cr = "debit" if d.credit > 0 else "credit"
dr_or_cr = "debit" if flt(d.credit) > 0 else "credit"
valid = False
for jvd in against_entries:
if flt(jvd[dr_or_cr]) > 0:
@ -868,7 +940,7 @@ class JournalEntry(AccountsController):
party_account_currency = d.account_currency
elif frappe.get_cached_value("Account", d.account, "account_type") in ["Bank", "Cash"]:
bank_amount += d.debit_in_account_currency or d.credit_in_account_currency
bank_amount += flt(d.debit_in_account_currency) or flt(d.credit_in_account_currency)
bank_account_currency = d.account_currency
if party_type and pay_to_recd_from:

View File

@ -203,7 +203,8 @@
"fieldtype": "Select",
"label": "Reference Type",
"no_copy": 1,
"options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset\nLoan\nPayroll Entry\nEmployee Advance\nExchange Rate Revaluation\nInvoice Discounting\nFees\nFull and Final Statement\nPayment Entry"
"options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset\nLoan\nPayroll Entry\nEmployee Advance\nExchange Rate Revaluation\nInvoice Discounting\nFees\nFull and Final Statement\nPayment Entry",
"search_index": 1
},
{
"fieldname": "reference_name",
@ -211,7 +212,8 @@
"in_list_view": 1,
"label": "Reference Name",
"no_copy": 1,
"options": "reference_type"
"options": "reference_type",
"search_index": 1
},
{
"depends_on": "eval:doc.reference_type&&!in_list(doc.reference_type, ['Expense Claim', 'Asset', 'Employee Loan', 'Employee Advance'])",
@ -278,13 +280,14 @@
"fieldtype": "Data",
"hidden": 1,
"label": "Reference Detail No",
"no_copy": 1
"no_copy": 1,
"search_index": 1
}
],
"idx": 1,
"istable": 1,
"links": [],
"modified": "2023-06-16 14:11:13.507807",
"modified": "2023-11-23 11:44:25.841187",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry Account",

View File

@ -6,4 +6,56 @@ from frappe.model.document import Document
class JournalEntryAccount(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
account: DF.Link
account_currency: DF.Link | None
account_type: DF.Data | None
against_account: DF.Text | None
balance: DF.Currency
bank_account: DF.Link | None
cost_center: DF.Link | None
credit: DF.Currency
credit_in_account_currency: DF.Currency
debit: DF.Currency
debit_in_account_currency: DF.Currency
exchange_rate: DF.Float
is_advance: DF.Literal["No", "Yes"]
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
party: DF.DynamicLink | None
party_balance: DF.Currency
party_type: DF.Link | None
project: DF.Link | None
reference_detail_no: DF.Data | None
reference_due_date: DF.Date | None
reference_name: DF.DynamicLink | None
reference_type: DF.Literal[
"",
"Sales Invoice",
"Purchase Invoice",
"Journal Entry",
"Sales Order",
"Purchase Order",
"Expense Claim",
"Asset",
"Loan",
"Payroll Entry",
"Employee Advance",
"Exchange Rate Revaluation",
"Invoice Discounting",
"Fees",
"Full and Final Statement",
"Payment Entry",
]
user_remark: DF.SmallText | None
# end: auto-generated types
pass

View File

@ -7,6 +7,41 @@ from frappe.model.document import Document
class JournalEntryTemplate(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
from erpnext.accounts.doctype.journal_entry_template_account.journal_entry_template_account import (
JournalEntryTemplateAccount,
)
accounts: DF.Table[JournalEntryTemplateAccount]
company: DF.Link
is_opening: DF.Literal["No", "Yes"]
multi_currency: DF.Check
naming_series: DF.Literal
template_title: DF.Data
voucher_type: DF.Literal[
"Journal Entry",
"Inter Company Journal Entry",
"Bank Entry",
"Cash Entry",
"Credit Card Entry",
"Debit Note",
"Credit Note",
"Contra Entry",
"Excise Entry",
"Write Off Entry",
"Opening Entry",
"Depreciation Entry",
"Exchange Rate Revaluation",
]
# end: auto-generated types
pass

View File

@ -7,4 +7,18 @@ from frappe.model.document import Document
class JournalEntryTemplateAccount(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
account: DF.Link
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
# end: auto-generated types
pass

View File

@ -10,6 +10,27 @@ from erpnext.accounts.doctype.account.account import merge_account
class LedgerMerge(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
from erpnext.accounts.doctype.ledger_merge_accounts.ledger_merge_accounts import (
LedgerMergeAccounts,
)
account: DF.Link
account_name: DF.Data
company: DF.Link
is_group: DF.Check
merge_accounts: DF.Table[LedgerMergeAccounts]
root_type: DF.Literal["", "Asset", "Liability", "Income", "Expense", "Equity"]
status: DF.Literal["Pending", "Success", "Partial Success", "Error"]
# end: auto-generated types
def start_merge(self):
from frappe.utils.background_jobs import enqueue
from frappe.utils.scheduler import is_scheduler_inactive

View File

@ -6,4 +6,20 @@ from frappe.model.document import Document
class LedgerMergeAccounts(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
account: DF.Link
account_name: DF.Data
merged: DF.Check
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
# end: auto-generated types
pass

View File

@ -10,6 +10,27 @@ exclude_from_linked_with = True
class LoyaltyPointEntry(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
company: DF.Link | None
customer: DF.Link | None
expiry_date: DF.Date | None
invoice: DF.DynamicLink | None
invoice_type: DF.Link | None
loyalty_points: DF.Int
loyalty_program: DF.Link | None
loyalty_program_tier: DF.Data | None
posting_date: DF.Date | None
purchase_amount: DF.Currency
redeem_against: DF.Link | None
# end: auto-generated types
pass

View File

@ -6,4 +6,20 @@ from frappe.model.document import Document
class LoyaltyPointEntryRedemption(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
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
redeemed_points: DF.Int
redemption_date: DF.Date | None
sales_invoice: DF.Data | None
# end: auto-generated types
pass

View File

@ -9,6 +9,33 @@ from frappe.utils import flt, today
class LoyaltyProgram(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
from erpnext.accounts.doctype.loyalty_program_collection.loyalty_program_collection import (
LoyaltyProgramCollection,
)
auto_opt_in: DF.Check
collection_rules: DF.Table[LoyaltyProgramCollection]
company: DF.Link | None
conversion_factor: DF.Float
cost_center: DF.Link | None
customer_group: DF.Link | None
customer_territory: DF.Link | None
expense_account: DF.Link | None
expiry_duration: DF.Int
from_date: DF.Date
loyalty_program_name: DF.Data
loyalty_program_type: DF.Literal["Single Tier Program", "Multiple Tier Program"]
to_date: DF.Date | None
# end: auto-generated types
pass

View File

@ -6,4 +6,20 @@ from frappe.model.document import Document
class LoyaltyProgramCollection(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
collection_factor: DF.Currency
min_spent: DF.Currency
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
tier_name: DF.Data
# end: auto-generated types
pass

View File

@ -8,6 +8,24 @@ from frappe.model.document import Document
class ModeofPayment(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
from erpnext.accounts.doctype.mode_of_payment_account.mode_of_payment_account import (
ModeofPaymentAccount,
)
accounts: DF.Table[ModeofPaymentAccount]
enabled: DF.Check
mode_of_payment: DF.Data
type: DF.Literal["Cash", "Bank", "General", "Phone"]
# end: auto-generated types
def validate(self):
self.validate_accounts()
self.validate_repeating_companies()

View File

@ -6,4 +6,19 @@ from frappe.model.document import Document
class ModeofPaymentAccount(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
company: DF.Link | None
default_account: DF.Link | None
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
# end: auto-generated types
pass

View File

@ -9,6 +9,23 @@ from frappe.utils import add_months, flt
class MonthlyDistribution(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
from erpnext.accounts.doctype.monthly_distribution_percentage.monthly_distribution_percentage import (
MonthlyDistributionPercentage,
)
distribution_id: DF.Data
fiscal_year: DF.Link | None
percentages: DF.Table[MonthlyDistributionPercentage]
# end: auto-generated types
@frappe.whitelist()
def get_months(self):
month_list = [

View File

@ -6,4 +6,19 @@ from frappe.model.document import Document
class MonthlyDistributionPercentage(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
month: DF.Data
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
percentage_allocation: DF.Float
# end: auto-generated types
pass

View File

@ -14,6 +14,25 @@ from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
class OpeningInvoiceCreationTool(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
from erpnext.accounts.doctype.opening_invoice_creation_tool_item.opening_invoice_creation_tool_item import (
OpeningInvoiceCreationToolItem,
)
company: DF.Link
cost_center: DF.Link | None
create_missing_party: DF.Check
invoice_type: DF.Literal["Sales", "Purchase"]
invoices: DF.Table[OpeningInvoiceCreationToolItem]
# end: auto-generated types
def onload(self):
"""Load the Opening Invoice summary"""
summary, max_count = self.get_opening_invoice_summary()

View File

@ -6,4 +6,27 @@ from frappe.model.document import Document
class OpeningInvoiceCreationToolItem(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
cost_center: DF.Link | None
due_date: DF.Date | None
invoice_number: DF.Data | None
item_name: DF.Data | None
outstanding_amount: DF.Currency
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
party: DF.DynamicLink
party_type: DF.Link | None
posting_date: DF.Date | None
qty: DF.Data | None
temporary_opening_account: DF.Link | None
# end: auto-generated types
pass

View File

@ -6,4 +6,31 @@ from frappe.model.document import Document
class OverduePayment(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
description: DF.SmallText | None
discounted_amount: DF.Currency
due_date: DF.Date | None
dunning_level: DF.Int
interest: DF.Currency
invoice_portion: DF.Percent
mode_of_payment: DF.Link | None
outstanding: DF.Currency
overdue_days: DF.Data | None
paid_amount: DF.Currency
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
payment_amount: DF.Currency
payment_schedule: DF.Data | None
payment_term: DF.Link | None
sales_invoice: DF.Link
# end: auto-generated types
pass

View File

@ -6,4 +6,20 @@ from frappe.model.document import Document
class PartyAccount(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
account: DF.Link | None
advance_account: DF.Link | None
company: DF.Link
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
# end: auto-generated types
pass

View File

@ -7,6 +7,20 @@ from frappe.model.document import Document
class PartyLink(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
primary_party: DF.DynamicLink | None
primary_role: DF.Link
secondary_party: DF.DynamicLink | None
secondary_role: DF.Link | None
# end: auto-generated types
def validate(self):
if self.primary_role not in ["Customer", "Supplier"]:
frappe.throw(

View File

@ -9,7 +9,7 @@ erpnext.accounts.taxes.setup_tax_filters("Advance Taxes and Charges");
frappe.ui.form.on('Payment Entry', {
onload: function(frm) {
frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Journal Entry', 'Repost Payment Ledger','Repost Accounting Ledger', 'Unreconcile Payments', 'Unreconcile Payment Entries'];
frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Journal Entry', 'Repost Payment Ledger','Repost Accounting Ledger', 'Unreconcile Payment', 'Unreconcile Payment Entries'];
if(frm.doc.__islocal) {
if (!frm.doc.paid_from) frm.set_value("paid_from_account_currency", null);
@ -160,7 +160,7 @@ frappe.ui.form.on('Payment Entry', {
}, __('Actions'));
}
erpnext.accounts.unreconcile_payments.add_unreconcile_btn(frm);
erpnext.accounts.unreconcile_payment.add_unreconcile_btn(frm);
},
validate_company: (frm) => {
@ -853,6 +853,7 @@ frappe.ui.form.on('Payment Entry', {
var allocated_positive_outstanding = paid_amount + allocated_negative_outstanding;
} else if (in_list(["Customer", "Supplier"], frm.doc.party_type)) {
total_negative_outstanding = flt(total_negative_outstanding, precision("outstanding_amount"))
if(paid_amount > total_negative_outstanding) {
if(total_negative_outstanding == 0) {
frappe.msgprint(

View File

@ -750,8 +750,16 @@
],
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2023-11-08 21:51:03.482709",
"links": [
{
"is_child_table": 1,
"link_doctype": "Bank Transaction Payments",
"link_fieldname": "payment_entry",
"parent_doctype": "Bank Transaction",
"table_fieldname": "payment_entries"
}
],
"modified": "2023-11-23 12:07:20.887885",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Entry",

View File

@ -9,6 +9,8 @@ import frappe
from frappe import ValidationError, _, qb, scrub, throw
from frappe.utils import cint, comma_or, flt, getdate, nowdate
from frappe.utils.data import comma_and, fmt_money
from pypika import Case
from pypika.functions import Coalesce, Sum
import erpnext
from erpnext.accounts.doctype.bank_account.bank_account import (
@ -104,9 +106,17 @@ class PaymentEntry(AccountsController):
self.set_status()
def set_liability_account(self):
if not self.book_advance_payments_in_separate_party_account:
# Auto setting liability account should only be done during 'draft' status
if self.docstatus > 0:
return
if not frappe.db.get_value(
"Company", self.company, "book_advance_payments_in_separate_party_account"
):
return
# Important to set this flag for the gl building logic to work properly
self.book_advance_payments_in_separate_party_account = True
account_type = frappe.get_value(
"Account", {"name": self.party_account, "company": self.company}, "account_type"
)
@ -116,11 +126,13 @@ class PaymentEntry(AccountsController):
):
return
if self.unallocated_amount == 0:
for d in self.references:
if d.reference_doctype in ["Sales Order", "Purchase Order"]:
break
else:
if self.references:
allowed_types = frozenset(["Sales Order", "Purchase Order"])
reference_types = set([x.reference_doctype for x in self.references])
# If there are referencers other than `allowed_types`, treat this as a normal payment entry
if reference_types - allowed_types:
self.book_advance_payments_in_separate_party_account = False
return
liability_account = get_party_account(
@ -148,7 +160,7 @@ class PaymentEntry(AccountsController):
"Repost Payment Ledger Items",
"Repost Accounting Ledger",
"Repost Accounting Ledger Items",
"Unreconcile Payments",
"Unreconcile Payment",
"Unreconcile Payment Entries",
)
super(PaymentEntry, self).on_cancel()
@ -1022,6 +1034,7 @@ class PaymentEntry(AccountsController):
self.add_bank_gl_entries(gl_entries)
self.add_deductions_gl_entries(gl_entries)
self.add_tax_gl_entries(gl_entries)
add_regional_gl_entries(gl_entries, self)
return gl_entries
def make_gl_entries(self, cancel=0, adv_adj=0):
@ -1055,112 +1068,104 @@ class PaymentEntry(AccountsController):
)
dr_or_cr = "credit" if self.payment_type == "Receive" else "debit"
for d in self.get("references"):
cost_center = self.cost_center
if d.reference_doctype == "Sales Invoice" and not cost_center:
cost_center = frappe.db.get_value(d.reference_doctype, d.reference_name, "cost_center")
if self.book_advance_payments_in_separate_party_account:
gle = party_gl_dict.copy()
allocated_amount_in_company_currency = self.calculate_base_allocated_amount_for_reference(d)
if self.book_advance_payments_in_separate_party_account:
against_voucher_type = "Payment Entry"
against_voucher = self.name
if self.payment_type == "Receive":
amount = self.base_paid_amount
else:
against_voucher_type = d.reference_doctype
against_voucher = d.reference_name
amount = self.base_received_amount
reverse_dr_or_cr, standalone_note = 0, 0
if d.reference_doctype in ["Sales Invoice", "Purchase Invoice"]:
is_return, return_against = frappe.db.get_value(
d.reference_doctype, d.reference_name, ["is_return", "return_against"]
)
payable_party_types = get_party_types_from_account_type("Payable")
receivable_party_types = get_party_types_from_account_type("Receivable")
if is_return and self.party_type in receivable_party_types and (self.payment_type == "Pay"):
reverse_dr_or_cr = 1
elif (
is_return and self.party_type in payable_party_types and (self.payment_type == "Receive")
):
reverse_dr_or_cr = 1
if is_return and not return_against and not reverse_dr_or_cr:
dr_or_cr = "debit" if dr_or_cr == "credit" else "credit"
gle.update(
{
dr_or_cr: abs(allocated_amount_in_company_currency),
dr_or_cr + "_in_account_currency": abs(d.allocated_amount),
"against_voucher_type": against_voucher_type,
"against_voucher": against_voucher,
"cost_center": cost_center,
}
)
gl_entries.append(gle)
if self.unallocated_amount:
exchange_rate = self.get_exchange_rate()
base_unallocated_amount = self.unallocated_amount * exchange_rate
gle = party_gl_dict.copy()
amount_in_account_currency = amount * exchange_rate
gle.update(
{
dr_or_cr + "_in_account_currency": self.unallocated_amount,
dr_or_cr: base_unallocated_amount,
dr_or_cr: amount,
dr_or_cr + "_in_account_currency": amount_in_account_currency,
"against_voucher_type": "Payment Entry",
"against_voucher": self.name,
"cost_center": self.cost_center,
}
)
gl_entries.append(gle)
else:
for d in self.get("references"):
# re-defining dr_or_cr for every reference in order to avoid the last value affecting calculation of reverse
dr_or_cr = "credit" if self.payment_type == "Receive" else "debit"
cost_center = self.cost_center
if d.reference_doctype == "Sales Invoice" and not cost_center:
cost_center = frappe.db.get_value(d.reference_doctype, d.reference_name, "cost_center")
def make_advance_gl_entries(self, against_voucher_type=None, against_voucher=None, cancel=0):
if self.book_advance_payments_in_separate_party_account:
gl_entries = []
for d in self.get("references"):
if d.reference_doctype in ("Sales Invoice", "Purchase Invoice", "Journal Entry"):
if not (against_voucher_type and against_voucher) or (
d.reference_doctype == against_voucher_type and d.reference_name == against_voucher
):
self.make_invoice_liability_entry(gl_entries, d)
gle = party_gl_dict.copy()
if cancel:
for entry in gl_entries:
frappe.db.set_value(
"GL Entry",
allocated_amount_in_company_currency = self.calculate_base_allocated_amount_for_reference(d)
reverse_dr_or_cr = 0
if d.reference_doctype in ["Sales Invoice", "Purchase Invoice"]:
is_return = frappe.db.get_value(d.reference_doctype, d.reference_name, "is_return")
payable_party_types = get_party_types_from_account_type("Payable")
receivable_party_types = get_party_types_from_account_type("Receivable")
if is_return and self.party_type in receivable_party_types and (self.payment_type == "Pay"):
reverse_dr_or_cr = 1
elif (
is_return and self.party_type in payable_party_types and (self.payment_type == "Receive")
):
reverse_dr_or_cr = 1
if is_return and not reverse_dr_or_cr:
dr_or_cr = "debit" if dr_or_cr == "credit" else "credit"
gle.update(
{
"voucher_no": self.name,
"voucher_type": self.doctype,
"voucher_detail_no": entry.voucher_detail_no,
"against_voucher_type": entry.against_voucher_type,
"against_voucher": entry.against_voucher,
},
"is_cancelled",
1,
dr_or_cr: abs(allocated_amount_in_company_currency),
dr_or_cr + "_in_account_currency": abs(d.allocated_amount),
"against_voucher_type": d.reference_doctype,
"against_voucher": d.reference_name,
"cost_center": cost_center,
}
)
gl_entries.append(gle)
if self.unallocated_amount:
dr_or_cr = "credit" if self.payment_type == "Receive" else "debit"
exchange_rate = self.get_exchange_rate()
base_unallocated_amount = self.unallocated_amount * exchange_rate
gle = party_gl_dict.copy()
gle.update(
{
dr_or_cr + "_in_account_currency": self.unallocated_amount,
dr_or_cr: base_unallocated_amount,
}
)
make_reverse_gl_entries(gl_entries=gl_entries, partial_cancel=True)
return
gl_entries.append(gle)
# same reference added to payment entry
for gl_entry in gl_entries.copy():
if frappe.db.exists(
"GL Entry",
{
"account": gl_entry.account,
"voucher_type": gl_entry.voucher_type,
"voucher_no": gl_entry.voucher_no,
"voucher_detail_no": gl_entry.voucher_detail_no,
"debit": gl_entry.debit,
"credit": gl_entry.credit,
"is_cancelled": 0,
},
):
gl_entries.remove(gl_entry)
def make_advance_gl_entries(
self, entry: object | dict = None, cancel: bool = 0, update_outstanding: str = "Yes"
):
gl_entries = []
self.add_advance_gl_entries(gl_entries, entry)
make_gl_entries(gl_entries)
if cancel:
make_reverse_gl_entries(gl_entries, partial_cancel=True)
else:
make_gl_entries(gl_entries, update_outstanding=update_outstanding)
def make_invoice_liability_entry(self, gl_entries, invoice):
def add_advance_gl_entries(self, gl_entries: list, entry: object | dict | None):
"""
If 'entry' is passed, GL enties only for that reference is added.
"""
if self.book_advance_payments_in_separate_party_account:
references = [x for x in self.get("references")]
if entry:
references = [x for x in self.get("references") if x.name == entry.name]
for ref in references:
if ref.reference_doctype in ("Sales Invoice", "Purchase Invoice", "Journal Entry"):
self.add_advance_gl_for_reference(gl_entries, ref)
def add_advance_gl_for_reference(self, gl_entries, invoice):
args_dict = {
"party_type": self.party_type,
"party": self.party,
@ -1698,12 +1703,13 @@ def split_invoices_based_on_payment_terms(outstanding_invoices, company) -> list
if not split_rows:
continue
frappe.msgprint(
_("Splitting {0} {1} into {2} rows as per Payment Terms").format(
_(entry.voucher_type), frappe.bold(entry.voucher_no), len(split_rows)
),
alert=True,
)
if len(split_rows) > 1:
frappe.msgprint(
_("Splitting {0} {1} into {2} rows as per Payment Terms").format(
_(entry.voucher_type), frappe.bold(entry.voucher_no), len(split_rows)
),
alert=True,
)
outstanding_invoices_after_split += split_rows
continue
@ -1986,18 +1992,24 @@ def get_company_defaults(company):
def get_outstanding_on_journal_entry(name):
res = frappe.db.sql(
"SELECT "
'CASE WHEN party_type IN ("Customer") '
"THEN ifnull(sum(debit_in_account_currency - credit_in_account_currency), 0) "
"ELSE ifnull(sum(credit_in_account_currency - debit_in_account_currency), 0) "
"END as outstanding_amount "
"FROM `tabGL Entry` WHERE (voucher_no=%s OR against_voucher=%s) "
"AND party_type IS NOT NULL "
'AND party_type != ""',
(name, name),
as_dict=1,
)
gl = frappe.qb.DocType("GL Entry")
res = (
frappe.qb.from_(gl)
.select(
Case()
.when(
gl.party_type == "Customer",
Coalesce(Sum(gl.debit_in_account_currency - gl.credit_in_account_currency), 0),
)
.else_(Coalesce(Sum(gl.credit_in_account_currency - gl.debit_in_account_currency), 0))
.as_("outstanding_amount")
)
.where(
(Coalesce(gl.party_type, "") != "")
& (gl.is_cancelled == 0)
& ((gl.voucher_no == name) | (gl.against_voucher == name))
)
).run(as_dict=True)
outstanding_amount = res[0].get("outstanding_amount", 0) if res else 0
@ -2622,3 +2634,8 @@ def make_payment_order(source_name, target_doc=None):
)
return doclist
@erpnext.allow_regional
def add_regional_gl_entries(gl_entries, doc):
return

View File

@ -8,6 +8,7 @@ from frappe import qb
from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import add_days, flt, nowdate
from erpnext.accounts.doctype.account.test_account import create_account
from erpnext.accounts.doctype.payment_entry.payment_entry import (
InvalidPaymentEntry,
get_outstanding_reference_documents,
@ -1290,6 +1291,9 @@ class TestPaymentEntry(FrappeTestCase):
self.assertEqual(references[2].payment_term, "Tax Receivable")
def test_receive_payment_from_payable_party_type(self):
"""
Checks GL entries generated while receiving payments from a Payable Party Type.
"""
pe = create_payment_entry(
party_type="Supplier",
party="_Test Supplier",
@ -1301,11 +1305,196 @@ class TestPaymentEntry(FrappeTestCase):
)
self.voucher_no = pe.name
self.expected_gle = [
{"account": "_Test Cash - _TC", "debit": 1000.0, "credit": 0.0},
{"account": "Creditors - _TC", "debit": 0.0, "credit": 1000.0},
{"account": "_Test Cash - _TC", "debit": 1000.0, "credit": 0.0},
]
self.check_gl_entries()
def test_payment_against_partial_return_invoice(self):
"""
Checks GL entries generated for partial return invoice payments.
"""
si = create_sales_invoice(qty=10, rate=10, customer="_Test Customer")
credit_note = create_sales_invoice(
qty=-4, rate=10, customer="_Test Customer", is_return=1, return_against=si.name
)
pe = create_payment_entry(
party_type="Customer",
party="_Test Customer",
payment_type="Receive",
paid_from="Debtors - _TC",
paid_to="_Test Cash - _TC",
)
pe.set(
"references",
[
{
"reference_doctype": "Sales Invoice",
"reference_name": si.name,
"due_date": si.get("due_date"),
"total_amount": si.grand_total,
"outstanding_amount": si.outstanding_amount,
"allocated_amount": si.outstanding_amount,
},
{
"reference_doctype": "Sales Invoice",
"reference_name": credit_note.name,
"due_date": credit_note.get("due_date"),
"total_amount": credit_note.grand_total,
"outstanding_amount": credit_note.outstanding_amount,
"allocated_amount": credit_note.outstanding_amount,
},
],
)
pe.save()
pe.submit()
self.assertEqual(pe.total_allocated_amount, 60)
self.assertEqual(pe.unallocated_amount, 940)
self.voucher_no = pe.name
self.expected_gle = [
{"account": "Debtors - _TC", "debit": 40.0, "credit": 0.0},
{"account": "Debtors - _TC", "debit": 0.0, "credit": 940.0},
{"account": "Debtors - _TC", "debit": 0.0, "credit": 100.0},
{"account": "_Test Cash - _TC", "debit": 1000.0, "credit": 0.0},
]
self.check_gl_entries()
def test_ledger_entries_for_advance_as_liability(self):
from erpnext.accounts.doctype.account.test_account import create_account
company = "_Test Company"
advance_account = create_account(
parent_account="Current Assets - _TC",
account_name="Advances Received",
company=company,
account_type="Receivable",
)
frappe.db.set_value(
"Company",
company,
{
"book_advance_payments_in_separate_party_account": 1,
"default_advance_received_account": advance_account,
},
)
# Advance Payment
pe = create_payment_entry(
party_type="Customer",
party="_Test Customer",
payment_type="Receive",
paid_from="Debtors - _TC",
paid_to="_Test Cash - _TC",
)
pe.save() # use save() to trigger set_liability_account()
pe.submit()
# Normal Invoice
si = create_sales_invoice(qty=10, rate=100, customer="_Test Customer")
pre_reconciliation_gle = [
{"account": advance_account, "debit": 0.0, "credit": 1000.0},
{"account": "_Test Cash - _TC", "debit": 1000.0, "credit": 0.0},
]
pre_reconciliation_ple = [
{
"account": advance_account,
"voucher_no": pe.name,
"against_voucher_no": pe.name,
"amount": -1000.0,
}
]
self.voucher_no = pe.name
self.expected_gle = pre_reconciliation_gle
self.expected_ple = pre_reconciliation_ple
self.check_gl_entries()
self.check_pl_entries()
# Partially reconcile advance against invoice
pr = frappe.get_doc("Payment Reconciliation")
pr.company = company
pr.party_type = "Customer"
pr.party = "_Test Customer"
pr.receivable_payable_account = si.debit_to
pr.default_advance_account = advance_account
pr.payment_name = pe.name
pr.invoice_name = si.name
pr.get_unreconciled_entries()
self.assertEqual(len(pr.invoices), 1)
self.assertEqual(len(pr.payments), 1)
invoices = [x.as_dict() for x in pr.get("invoices")]
payments = [x.as_dict() for x in pr.get("payments")]
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
pr.allocation[0].allocated_amount = 400
pr.reconcile()
# assert General and Payment Ledger entries post partial reconciliation
self.expected_gle = [
{"account": si.debit_to, "debit": 0.0, "credit": 400.0},
{"account": advance_account, "debit": 400.0, "credit": 0.0},
{"account": advance_account, "debit": 0.0, "credit": 1000.0},
{"account": "_Test Cash - _TC", "debit": 1000.0, "credit": 0.0},
]
self.expected_ple = [
{
"account": advance_account,
"voucher_no": pe.name,
"against_voucher_no": pe.name,
"amount": -1000.0,
},
{
"account": si.debit_to,
"voucher_no": pe.name,
"against_voucher_no": si.name,
"amount": -400.0,
},
{
"account": advance_account,
"voucher_no": pe.name,
"against_voucher_no": pe.name,
"amount": 400.0,
},
]
self.check_gl_entries()
self.check_pl_entries()
# Unreconcile
unrecon = (
frappe.get_doc(
{
"doctype": "Unreconcile Payment",
"company": company,
"voucher_type": pe.doctype,
"voucher_no": pe.name,
"allocations": [{"reference_doctype": si.doctype, "reference_name": si.name}],
}
)
.save()
.submit()
)
self.voucher_no = pe.name
self.expected_gle = pre_reconciliation_gle
self.expected_ple = pre_reconciliation_ple
self.check_gl_entries()
self.check_pl_entries()
def check_pl_entries(self):
ple = frappe.qb.DocType("Payment Ledger Entry")
pl_entries = (
frappe.qb.from_(ple)
.select(ple.account, ple.voucher_no, ple.against_voucher_no, ple.amount)
.where((ple.voucher_no == self.voucher_no) & (ple.delinked == 0))
.orderby(ple.creation)
).run(as_dict=True)
for row in range(len(self.expected_ple)):
for field in ["account", "voucher_no", "against_voucher_no", "amount"]:
self.assertEqual(self.expected_ple[row][field], pl_entries[row][field])
def check_gl_entries(self):
gle = frappe.qb.DocType("GL Entry")
gl_entries = (
@ -1316,7 +1505,7 @@ class TestPaymentEntry(FrappeTestCase):
gle.credit,
)
.where((gle.voucher_no == self.voucher_no) & (gle.is_cancelled == 0))
.orderby(gle.account)
.orderby(gle.account, gle.debit, gle.credit, order=frappe.qb.desc)
).run(as_dict=True)
for row in range(len(self.expected_gle)):
for field in ["account", "debit", "credit"]:

View File

@ -6,4 +6,21 @@ from frappe.model.document import Document
class PaymentEntryDeduction(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
account: DF.Link
amount: DF.Currency
cost_center: DF.Link
description: DF.SmallText | None
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
# end: auto-generated types
pass

View File

@ -6,4 +6,28 @@ from frappe.model.document import Document
class PaymentEntryReference(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
account: DF.Link | None
allocated_amount: DF.Float
bill_no: DF.Data | None
due_date: DF.Date | None
exchange_gain_loss: DF.Currency
exchange_rate: DF.Float
outstanding_amount: DF.Float
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
payment_term: DF.Link | None
reference_doctype: DF.Link
reference_name: DF.DynamicLink
total_amount: DF.Float
# end: auto-generated types
pass

View File

@ -7,6 +7,22 @@ from frappe.model.document import Document
class PaymentGatewayAccount(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
currency: DF.ReadOnly | None
is_default: DF.Check
message: DF.SmallText | None
payment_account: DF.Link
payment_channel: DF.Literal["", "Email", "Phone"]
payment_gateway: DF.Link
# end: auto-generated types
def autoname(self):
self.name = self.payment_gateway + " - " + self.currency

View File

@ -21,6 +21,35 @@ from erpnext.exceptions import InvalidAccountDimensionError, MandatoryAccountDim
class PaymentLedgerEntry(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
account: DF.Link | None
account_currency: DF.Link | None
account_type: DF.Literal["Receivable", "Payable"]
against_voucher_no: DF.DynamicLink | None
against_voucher_type: DF.Link | None
amount: DF.Currency
amount_in_account_currency: DF.Currency
company: DF.Link | None
cost_center: DF.Link | None
delinked: DF.Check
due_date: DF.Date | None
finance_book: DF.Link | None
party: DF.DynamicLink | None
party_type: DF.Link | None
posting_date: DF.Date | None
remarks: DF.Text | None
voucher_detail_no: DF.Data | None
voucher_no: DF.DynamicLink | None
voucher_type: DF.Link | None
# end: auto-generated types
def validate_account(self):
valid_account = frappe.db.get_list(
"Account",

View File

@ -11,6 +11,30 @@ from erpnext.accounts.party import get_party_account
class PaymentOrder(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
from erpnext.accounts.doctype.payment_order_reference.payment_order_reference import (
PaymentOrderReference,
)
account: DF.Data | None
amended_from: DF.Link | None
company: DF.Link
company_bank: DF.Link | None
company_bank_account: DF.Link
naming_series: DF.Literal["PMO-"]
party: DF.Link | None
payment_order_type: DF.Literal["", "Payment Request", "Payment Entry"]
posting_date: DF.Date | None
references: DF.Table[PaymentOrderReference]
# end: auto-generated types
def on_submit(self):
self.update_payment_status()

View File

@ -6,4 +6,26 @@ from frappe.model.document import Document
class PaymentOrderReference(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
account: DF.Link | None
amount: DF.Currency
bank_account: DF.Link
mode_of_payment: DF.Link | None
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
payment_reference: DF.Data | None
payment_request: DF.Link | None
reference_doctype: DF.Link
reference_name: DF.DynamicLink
supplier: DF.Link | None
# end: auto-generated types
pass

View File

@ -212,9 +212,10 @@
],
"hide_toolbar": 1,
"icon": "icon-resize-horizontal",
"is_virtual": 1,
"issingle": 1,
"links": [],
"modified": "2023-08-15 05:35:50.109290",
"modified": "2023-11-17 17:33:55.701726",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Reconciliation",
@ -239,6 +240,5 @@
],
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
"states": []
}

View File

@ -23,12 +23,106 @@ from erpnext.controllers.accounts_controller import get_advance_payment_entries_
class PaymentReconciliation(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
from erpnext.accounts.doctype.payment_reconciliation_allocation.payment_reconciliation_allocation import (
PaymentReconciliationAllocation,
)
from erpnext.accounts.doctype.payment_reconciliation_invoice.payment_reconciliation_invoice import (
PaymentReconciliationInvoice,
)
from erpnext.accounts.doctype.payment_reconciliation_payment.payment_reconciliation_payment import (
PaymentReconciliationPayment,
)
allocation: DF.Table[PaymentReconciliationAllocation]
bank_cash_account: DF.Link | None
company: DF.Link
cost_center: DF.Link | None
default_advance_account: DF.Link | None
from_invoice_date: DF.Date | None
from_payment_date: DF.Date | None
invoice_limit: DF.Int
invoice_name: DF.Data | None
invoices: DF.Table[PaymentReconciliationInvoice]
maximum_invoice_amount: DF.Currency
maximum_payment_amount: DF.Currency
minimum_invoice_amount: DF.Currency
minimum_payment_amount: DF.Currency
party: DF.DynamicLink
party_type: DF.Link
payment_limit: DF.Int
payment_name: DF.Data | None
payments: DF.Table[PaymentReconciliationPayment]
receivable_payable_account: DF.Link
to_invoice_date: DF.Date | None
to_payment_date: DF.Date | None
# end: auto-generated types
def __init__(self, *args, **kwargs):
super(PaymentReconciliation, self).__init__(*args, **kwargs)
self.common_filter_conditions = []
self.accounting_dimension_filter_conditions = []
self.ple_posting_date_filter = []
def load_from_db(self):
# 'modified' attribute is required for `run_doc_method` to work properly.
doc_dict = frappe._dict(
{
"modified": None,
"company": None,
"party": None,
"party_type": None,
"receivable_payable_account": None,
"default_advance_account": None,
"from_invoice_date": None,
"to_invoice_date": None,
"invoice_limit": 50,
"from_payment_date": None,
"to_payment_date": None,
"payment_limit": 50,
"minimum_invoice_amount": None,
"minimum_payment_amount": None,
"maximum_invoice_amount": None,
"maximum_payment_amount": None,
"bank_cash_account": None,
"cost_center": None,
"payment_name": None,
"invoice_name": None,
}
)
super(Document, self).__init__(doc_dict)
def save(self):
return
@staticmethod
def get_list(args):
pass
@staticmethod
def get_count(args):
pass
@staticmethod
def get_stats(args):
pass
def db_insert(self, *args, **kwargs):
pass
def db_update(self, *args, **kwargs):
pass
def delete(self):
pass
@frappe.whitelist()
def get_unreconciled_entries(self):
self.get_nonreconciled_payment_entries()

View File

@ -1137,6 +1137,40 @@ class TestPaymentReconciliation(FrappeTestCase):
self.assertEqual(pay.unallocated_amount, 1000)
self.assertEqual(pay.difference_amount, 0)
def test_rounding_of_unallocated_amount(self):
self.supplier = "_Test Supplier USD"
pi = self.create_purchase_invoice(qty=1, rate=10, do_not_submit=True)
pi.supplier = self.supplier
pi.currency = "USD"
pi.conversion_rate = 80
pi.credit_to = self.creditors_usd
pi.save().submit()
pe = get_payment_entry(pi.doctype, pi.name)
pe.target_exchange_rate = 78.726500000
pe.received_amount = 26.75
pe.paid_amount = 2105.93
pe.references = []
pe.save().submit()
# unallocated_amount will have some rounding loss - 26.749950
self.assertNotEqual(pe.unallocated_amount, 26.75)
pr = frappe.get_doc("Payment Reconciliation")
pr.company = self.company
pr.party_type = "Supplier"
pr.party = self.supplier
pr.receivable_payable_account = self.creditors_usd
pr.from_invoice_date = pr.to_invoice_date = pr.from_payment_date = pr.to_payment_date = nowdate()
pr.get_unreconciled_entries()
invoices = [invoice.as_dict() for invoice in pr.invoices]
payments = [payment.as_dict() for payment in pr.payments]
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
# Should not raise frappe.exceptions.ValidationError: Payment Entry has been modified after you pulled it. Please pull it again.
pr.reconcile()
def make_customer(customer_name, currency=None):
if not frappe.db.exists("Customer", customer_name):

View File

@ -159,9 +159,10 @@
"label": "Difference Posting Date"
}
],
"is_virtual": 1,
"istable": 1,
"links": [],
"modified": "2023-10-23 10:44:56.066303",
"modified": "2023-11-17 17:33:38.612615",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Reconciliation Allocation",

View File

@ -6,4 +6,32 @@ from frappe.model.document import Document
class PaymentReconciliationAllocation(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
allocated_amount: DF.Currency
amount: DF.Currency
cost_center: DF.Link | None
currency: DF.Link | None
difference_account: DF.Link | None
difference_amount: DF.Currency
exchange_rate: DF.Float
gain_loss_posting_date: DF.Date | None
invoice_number: DF.DynamicLink
invoice_type: DF.Link
is_advance: DF.Data | None
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
reference_name: DF.DynamicLink
reference_row: DF.Data | None
reference_type: DF.Link
unreconciled_amount: DF.Currency
# end: auto-generated types
pass

View File

@ -71,9 +71,10 @@
"label": "Exchange Rate"
}
],
"is_virtual": 1,
"istable": 1,
"links": [],
"modified": "2022-11-08 18:18:02.502149",
"modified": "2023-11-17 17:33:45.455166",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Reconciliation Invoice",

View File

@ -6,4 +6,24 @@ from frappe.model.document import Document
class PaymentReconciliationInvoice(Document):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from frappe.types import DF
amount: DF.Currency
currency: DF.Link | None
exchange_rate: DF.Float
invoice_date: DF.Date | None
invoice_number: DF.DynamicLink | None
invoice_type: DF.Literal["Sales Invoice", "Purchase Invoice", "Journal Entry"]
outstanding_amount: DF.Currency
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
# end: auto-generated types
pass

View File

@ -107,9 +107,10 @@
"options": "Cost Center"
}
],
"is_virtual": 1,
"istable": 1,
"links": [],
"modified": "2023-09-03 07:43:29.965353",
"modified": "2023-11-17 17:33:34.818530",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Reconciliation Payment",

View File

@ -6,4 +6,28 @@ from frappe.model.document import Document
class PaymentReconciliationPayment(Document):
# begin: auto-generated types
# This code is auto-generated. Do not modify anything in this block.
from typing import TYPE_CHECKING
if TYPE_CHECKING:
from frappe.types import DF
amount: DF.Currency
cost_center: DF.Link | None
currency: DF.Link | None
difference_amount: DF.Currency
exchange_rate: DF.Float
is_advance: DF.Data | None
parent: DF.Data
parentfield: DF.Data
parenttype: DF.Data
posting_date: DF.Date | None
reference_name: DF.DynamicLink | None
reference_row: DF.Data | None
reference_type: DF.Link | None
remark: DF.SmallText | None
# end: auto-generated types
pass

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