mirror of
https://github.com/frappe/erpnext.git
synced 2026-04-12 19:35:09 +00:00
Merge remote-tracking branch 'origin/develop' into feat/so-po-advance-payment-status
This commit is contained in:
12
.github/workflows/linters.yml
vendored
12
.github/workflows/linters.yml
vendored
@@ -20,6 +20,18 @@ jobs:
|
||||
- name: Install and Run Pre-commit
|
||||
uses: pre-commit/action@v3.0.0
|
||||
|
||||
semgrep:
|
||||
name: semgrep
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
|
||||
- name: Set up Python 3.10
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
cache: pip
|
||||
|
||||
- name: Download Semgrep rules
|
||||
run: git clone --depth 1 https://github.com/frappe/semgrep-rules.git frappe-semgrep-rules
|
||||
|
||||
|
||||
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -16,7 +16,7 @@ jobs:
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 18
|
||||
node-version: 20
|
||||
- name: Setup dependencies
|
||||
run: |
|
||||
npm install @semantic-release/git @semantic-release/exec --no-save
|
||||
|
||||
3
crowdin.yml
Normal file
3
crowdin.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
files:
|
||||
- source: /erpnext/locale/main.pot
|
||||
translation: /erpnext/locale/%two_letters_code%.po
|
||||
@@ -358,11 +358,9 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
|
||||
|
||||
account_currency = get_account_currency(item.expense_account or item.income_account)
|
||||
if doc.doctype == "Sales Invoice":
|
||||
against_type = "Customer"
|
||||
against, project = doc.customer, doc.project
|
||||
credit_account, debit_account = item.income_account, item.deferred_revenue_account
|
||||
else:
|
||||
against_type = "Supplier"
|
||||
against, project = doc.supplier, item.project
|
||||
credit_account, debit_account = item.deferred_expense_account, item.expense_account
|
||||
|
||||
@@ -415,7 +413,6 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
|
||||
doc,
|
||||
credit_account,
|
||||
debit_account,
|
||||
against_type,
|
||||
against,
|
||||
amount,
|
||||
base_amount,
|
||||
@@ -497,7 +494,6 @@ def make_gl_entries(
|
||||
doc,
|
||||
credit_account,
|
||||
debit_account,
|
||||
against_type,
|
||||
against,
|
||||
amount,
|
||||
base_amount,
|
||||
@@ -519,9 +515,7 @@ def make_gl_entries(
|
||||
doc.get_gl_dict(
|
||||
{
|
||||
"account": credit_account,
|
||||
"against_type": against_type,
|
||||
"against": against,
|
||||
"against_link": against,
|
||||
"credit": base_amount,
|
||||
"credit_in_account_currency": amount,
|
||||
"cost_center": cost_center,
|
||||
@@ -540,9 +534,7 @@ def make_gl_entries(
|
||||
doc.get_gl_dict(
|
||||
{
|
||||
"account": debit_account,
|
||||
"against_type": against_type,
|
||||
"against": against,
|
||||
"against_link": against,
|
||||
"debit": base_amount,
|
||||
"debit_in_account_currency": amount,
|
||||
"cost_center": cost_center,
|
||||
|
||||
@@ -108,6 +108,7 @@
|
||||
"fieldname": "parent_account",
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
"in_preview": 1,
|
||||
"label": "Parent Account",
|
||||
"oldfieldname": "parent_account",
|
||||
"oldfieldtype": "Link",
|
||||
@@ -192,7 +193,7 @@
|
||||
"idx": 1,
|
||||
"is_tree": 1,
|
||||
"links": [],
|
||||
"modified": "2023-07-20 18:18:44.405723",
|
||||
"modified": "2024-01-10 04:57:33.681676",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Account",
|
||||
@@ -249,8 +250,9 @@
|
||||
],
|
||||
"search_fields": "account_number",
|
||||
"show_name_in_global_search": 1,
|
||||
"show_preview_popup": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "ASC",
|
||||
"states": [],
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,8 +91,8 @@ class Account(NestedSet):
|
||||
super(Account, self).on_update()
|
||||
|
||||
def onload(self):
|
||||
frozen_accounts_modifier = frappe.db.get_value(
|
||||
"Accounts Settings", "Accounts Settings", "frozen_accounts_modifier"
|
||||
frozen_accounts_modifier = frappe.db.get_single_value(
|
||||
"Accounts Settings", "frozen_accounts_modifier"
|
||||
)
|
||||
if not frozen_accounts_modifier or frozen_accounts_modifier in frappe.get_roles():
|
||||
self.set_onload("can_freeze_account", True)
|
||||
|
||||
@@ -231,6 +231,8 @@ def build_account_tree(tree, parent, all_accounts):
|
||||
tree[child.account_name]["account_type"] = child.account_type
|
||||
if child.tax_rate:
|
||||
tree[child.account_name]["tax_rate"] = child.tax_rate
|
||||
if child.account_currency:
|
||||
tree[child.account_name]["account_currency"] = child.account_currency
|
||||
if not parent:
|
||||
tree[child.account_name]["root_type"] = child.root_type
|
||||
|
||||
|
||||
@@ -444,6 +444,10 @@ 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)
|
||||
transaction.validate_duplicate_references()
|
||||
transaction.allocate_payment_entries()
|
||||
transaction.update_allocated_amount()
|
||||
transaction.set_status()
|
||||
transaction.save()
|
||||
|
||||
return transaction
|
||||
|
||||
@@ -76,6 +76,7 @@ class TestBankReconciliationTool(AccountsTestMixin, FrappeTestCase):
|
||||
"deposit": 100,
|
||||
"bank_account": self.bank_account,
|
||||
"reference_number": "123",
|
||||
"currency": "INR",
|
||||
}
|
||||
)
|
||||
.save()
|
||||
|
||||
@@ -3,12 +3,12 @@
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.docstatus import DocStatus
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import flt
|
||||
|
||||
from erpnext.controllers.status_updater import StatusUpdater
|
||||
|
||||
|
||||
class BankTransaction(StatusUpdater):
|
||||
class BankTransaction(Document):
|
||||
# begin: auto-generated types
|
||||
# This code is auto-generated. Do not modify anything in this block.
|
||||
|
||||
@@ -49,6 +49,33 @@ class BankTransaction(StatusUpdater):
|
||||
|
||||
def validate(self):
|
||||
self.validate_duplicate_references()
|
||||
self.validate_currency()
|
||||
|
||||
def validate_currency(self):
|
||||
"""
|
||||
Bank Transaction should be on the same currency as the Bank Account.
|
||||
"""
|
||||
if self.currency and self.bank_account:
|
||||
account = frappe.get_cached_value("Bank Account", self.bank_account, "account")
|
||||
account_currency = frappe.get_cached_value("Account", account, "account_currency")
|
||||
|
||||
if self.currency != account_currency:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Transaction currency: {0} cannot be different from Bank Account({1}) currency: {2}"
|
||||
).format(
|
||||
frappe.bold(self.currency), frappe.bold(self.bank_account), frappe.bold(account_currency)
|
||||
)
|
||||
)
|
||||
|
||||
def set_status(self):
|
||||
if self.docstatus == 2:
|
||||
self.db_set("status", "Cancelled")
|
||||
elif self.docstatus == 1:
|
||||
if self.unallocated_amount > 0:
|
||||
self.db_set("status", "Unreconciled")
|
||||
elif self.unallocated_amount <= 0:
|
||||
self.db_set("status", "Reconciled")
|
||||
|
||||
def validate_duplicate_references(self):
|
||||
"""Make sure the same voucher is not allocated twice within the same Bank Transaction"""
|
||||
@@ -83,12 +110,13 @@ class BankTransaction(StatusUpdater):
|
||||
self.validate_duplicate_references()
|
||||
self.allocate_payment_entries()
|
||||
self.update_allocated_amount()
|
||||
self.set_status()
|
||||
|
||||
def on_cancel(self):
|
||||
for payment_entry in self.payment_entries:
|
||||
self.clear_linked_payment_entry(payment_entry, for_cancel=True)
|
||||
|
||||
self.set_status(update=True)
|
||||
self.set_status()
|
||||
|
||||
def add_payment_entries(self, vouchers):
|
||||
"Add the vouchers with zero allocation. Save() will perform the allocations and clearance"
|
||||
@@ -366,15 +394,17 @@ def set_voucher_clearance(doctype, docname, clearance_date, self):
|
||||
and len(get_reconciled_bank_transactions(doctype, docname)) < 2
|
||||
):
|
||||
return
|
||||
frappe.db.set_value(doctype, docname, "clearance_date", clearance_date)
|
||||
|
||||
elif doctype == "Sales Invoice":
|
||||
frappe.db.set_value(
|
||||
"Sales Invoice Payment",
|
||||
dict(parenttype=doctype, parent=docname),
|
||||
"clearance_date",
|
||||
clearance_date,
|
||||
)
|
||||
if doctype == "Sales Invoice":
|
||||
frappe.db.set_value(
|
||||
"Sales Invoice Payment",
|
||||
dict(parenttype=doctype, parent=docname),
|
||||
"clearance_date",
|
||||
clearance_date,
|
||||
)
|
||||
return
|
||||
|
||||
frappe.db.set_value(doctype, docname, "clearance_date", clearance_date)
|
||||
|
||||
elif doctype == "Bank Transaction":
|
||||
# For when a second bank transaction has fixed another, e.g. refund
|
||||
@@ -404,3 +434,21 @@ def unclear_reference_payment(doctype, docname, bt_name):
|
||||
bt = frappe.get_doc("Bank Transaction", bt_name)
|
||||
set_voucher_clearance(doctype, docname, None, bt)
|
||||
return docname
|
||||
|
||||
|
||||
def remove_from_bank_transaction(doctype, docname):
|
||||
"""Remove a (cancelled) voucher from all Bank Transactions."""
|
||||
for bt_name in get_reconciled_bank_transactions(doctype, docname):
|
||||
bt = frappe.get_doc("Bank Transaction", bt_name)
|
||||
if bt.docstatus == DocStatus.cancelled():
|
||||
continue
|
||||
|
||||
modified = False
|
||||
|
||||
for pe in bt.payment_entries:
|
||||
if pe.payment_document == doctype and pe.payment_entry == docname:
|
||||
bt.remove(pe)
|
||||
modified = True
|
||||
|
||||
if modified:
|
||||
bt.save()
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
# See license.txt
|
||||
|
||||
import json
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
from frappe import utils
|
||||
from frappe.model.docstatus import DocStatus
|
||||
from frappe.tests.utils import FrappeTestCase
|
||||
|
||||
from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import (
|
||||
@@ -81,6 +81,29 @@ class TestBankTransaction(FrappeTestCase):
|
||||
clearance_date = frappe.db.get_value("Payment Entry", payment.name, "clearance_date")
|
||||
self.assertFalse(clearance_date)
|
||||
|
||||
def test_cancel_voucher(self):
|
||||
bank_transaction = frappe.get_doc(
|
||||
"Bank Transaction",
|
||||
dict(description="1512567 BG/000003025 OPSKATTUZWXXX AT776000000098709849 Herr G"),
|
||||
)
|
||||
payment = frappe.get_doc("Payment Entry", dict(party="Mr G", paid_amount=1700))
|
||||
vouchers = json.dumps(
|
||||
[
|
||||
{
|
||||
"payment_doctype": "Payment Entry",
|
||||
"payment_name": payment.name,
|
||||
"amount": bank_transaction.unallocated_amount,
|
||||
}
|
||||
]
|
||||
)
|
||||
reconcile_vouchers(bank_transaction.name, vouchers)
|
||||
payment.reload()
|
||||
payment.cancel()
|
||||
bank_transaction.reload()
|
||||
self.assertEqual(bank_transaction.docstatus, DocStatus.submitted())
|
||||
self.assertEqual(bank_transaction.unallocated_amount, 1700)
|
||||
self.assertEqual(bank_transaction.payment_entries, [])
|
||||
|
||||
# Check if ERPNext can correctly filter a linked payments based on the debit/credit amount
|
||||
def test_debit_credit_output(self):
|
||||
bank_transaction = frappe.get_doc(
|
||||
|
||||
@@ -82,11 +82,11 @@
|
||||
"icon": "fa fa-calendar",
|
||||
"idx": 1,
|
||||
"links": [],
|
||||
"modified": "2020-11-05 12:16:53.081573",
|
||||
"modified": "2024-01-17 13:06:01.608953",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Fiscal Year",
|
||||
"owner": "Administrator",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
@@ -118,6 +118,14 @@
|
||||
{
|
||||
"read": 1,
|
||||
"role": "Employee"
|
||||
},
|
||||
{
|
||||
"read": 1,
|
||||
"role": "Accounts Manager"
|
||||
},
|
||||
{
|
||||
"read": 1,
|
||||
"role": "Stock Manager"
|
||||
}
|
||||
],
|
||||
"show_name_in_global_search": 1,
|
||||
|
||||
@@ -39,7 +39,7 @@ def test_record_generator():
|
||||
]
|
||||
|
||||
start = 2012
|
||||
end = now_datetime().year + 5
|
||||
end = now_datetime().year + 25
|
||||
for year in range(start, end):
|
||||
test_records.append(
|
||||
{
|
||||
|
||||
@@ -17,9 +17,7 @@
|
||||
"account_currency",
|
||||
"debit_in_account_currency",
|
||||
"credit_in_account_currency",
|
||||
"against_type",
|
||||
"against",
|
||||
"against_link",
|
||||
"against_voucher_type",
|
||||
"against_voucher",
|
||||
"voucher_type",
|
||||
@@ -131,13 +129,6 @@
|
||||
"label": "Credit Amount in Account Currency",
|
||||
"options": "account_currency"
|
||||
},
|
||||
{
|
||||
"fieldname": "against_type",
|
||||
"fieldtype": "Link",
|
||||
"in_filter": 1,
|
||||
"label": "Against Type",
|
||||
"options": "DocType"
|
||||
},
|
||||
{
|
||||
"fieldname": "against",
|
||||
"fieldtype": "Text",
|
||||
@@ -146,13 +137,6 @@
|
||||
"oldfieldname": "against",
|
||||
"oldfieldtype": "Text"
|
||||
},
|
||||
{
|
||||
"fieldname": "against_link",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"in_filter": 1,
|
||||
"label": "Against",
|
||||
"options": "against_type"
|
||||
},
|
||||
{
|
||||
"fieldname": "against_voucher_type",
|
||||
"fieldtype": "Link",
|
||||
@@ -306,7 +290,7 @@
|
||||
"idx": 1,
|
||||
"in_create": 1,
|
||||
"links": [],
|
||||
"modified": "2023-12-18 15:38:14.006208",
|
||||
"modified": "2023-09-26 12:03:23.031733",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "GL Entry",
|
||||
|
||||
@@ -435,8 +435,8 @@ def update_outstanding_amt(
|
||||
def validate_frozen_account(account, adv_adj=None):
|
||||
frozen_account = frappe.get_cached_value("Account", account, "freeze_account")
|
||||
if frozen_account == "Yes" and not adv_adj:
|
||||
frozen_accounts_modifier = frappe.db.get_value(
|
||||
"Accounts Settings", None, "frozen_accounts_modifier"
|
||||
frozen_accounts_modifier = frappe.db.get_single_value(
|
||||
"Accounts Settings", "frozen_accounts_modifier"
|
||||
)
|
||||
|
||||
if not frozen_accounts_modifier:
|
||||
|
||||
@@ -153,9 +153,7 @@ class InvoiceDiscounting(AccountsController):
|
||||
"account": inv.debit_to,
|
||||
"party_type": "Customer",
|
||||
"party": d.customer,
|
||||
"against_type": "Account",
|
||||
"against": self.accounts_receivable_credit,
|
||||
"against_link": self.accounts_receivable_credit,
|
||||
"credit": outstanding_in_company_currency,
|
||||
"credit_in_account_currency": outstanding_in_company_currency
|
||||
if inv.party_account_currency == company_currency
|
||||
@@ -175,9 +173,7 @@ class InvoiceDiscounting(AccountsController):
|
||||
"account": self.accounts_receivable_credit,
|
||||
"party_type": "Customer",
|
||||
"party": d.customer,
|
||||
"against_type": "Account",
|
||||
"against": inv.debit_to,
|
||||
"against_link": inv.debit_to,
|
||||
"debit": outstanding_in_company_currency,
|
||||
"debit_in_account_currency": outstanding_in_company_currency
|
||||
if ar_credit_account_currency == company_currency
|
||||
|
||||
@@ -8,7 +8,7 @@ frappe.provide("erpnext.journal_entry");
|
||||
frappe.ui.form.on("Journal Entry", {
|
||||
setup: function(frm) {
|
||||
frm.add_fetch("bank_account", "account", "account");
|
||||
frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Journal Entry', "Repost Payment Ledger", 'Asset', 'Asset Movement', 'Asset Depreciation Schedule', "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries"];
|
||||
frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Journal Entry', "Repost Payment Ledger", 'Asset', 'Asset Movement', 'Asset Depreciation Schedule', "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries", "Bank Transaction"];
|
||||
},
|
||||
|
||||
refresh: function(frm) {
|
||||
@@ -220,16 +220,6 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro
|
||||
return erpnext.journal_entry.account_query(me.frm);
|
||||
});
|
||||
|
||||
me.frm.set_query("against_account_link", "accounts", function(doc, cdt, cdn) {
|
||||
return erpnext.journal_entry.against_account_query(me.frm);
|
||||
});
|
||||
|
||||
me.frm.set_query("against_type", "accounts", function(){
|
||||
return {
|
||||
query: "erpnext.accounts.doctype.journal_entry.journal_entry.get_against_type",
|
||||
}
|
||||
})
|
||||
|
||||
me.frm.set_query("party_type", "accounts", function(doc, cdt, cdn) {
|
||||
const row = locals[cdt][cdn];
|
||||
|
||||
@@ -601,21 +591,6 @@ $.extend(erpnext.journal_entry, {
|
||||
return { filters: filters };
|
||||
},
|
||||
|
||||
against_account_query: function(frm) {
|
||||
if (frm.doc.against_type != "Account"){
|
||||
return { filters: {} };
|
||||
}
|
||||
else {
|
||||
let filters = { company: frm.doc.company, is_group: 0 };
|
||||
if(!frm.doc.multi_currency) {
|
||||
$.extend(filters, {
|
||||
account_currency: ['in', [frappe.get_doc(":Company", frm.doc.company).default_currency, null]]
|
||||
});
|
||||
}
|
||||
return { filters: filters };
|
||||
}
|
||||
},
|
||||
|
||||
reverse_journal_entry: function() {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.accounts.doctype.journal_entry.journal_entry.make_reverse_journal_entry",
|
||||
|
||||
@@ -304,7 +304,6 @@ class JournalEntry(AccountsController):
|
||||
"account": tax_withholding_details.get("account_head"),
|
||||
rev_debit_or_credit: tax_withholding_details.get("tax_amount"),
|
||||
"against_account": parties[0],
|
||||
"against_account_link": parties[0],
|
||||
},
|
||||
)
|
||||
|
||||
@@ -751,90 +750,27 @@ class JournalEntry(AccountsController):
|
||||
)
|
||||
|
||||
def set_against_account(self):
|
||||
accounts_debited, accounts_credited = [], []
|
||||
if self.voucher_type in ("Deferred Revenue", "Deferred Expense"):
|
||||
for d in self.get("accounts"):
|
||||
if d.reference_type == "Sales Invoice":
|
||||
against_type = "Customer"
|
||||
field = "customer"
|
||||
else:
|
||||
against_type = "Supplier"
|
||||
field = "supplier"
|
||||
|
||||
against_account = frappe.db.get_value(d.reference_type, d.reference_name, against_type.lower())
|
||||
d.against_type = against_type
|
||||
d.against_account_link = against_account
|
||||
d.against_account = frappe.db.get_value(d.reference_type, d.reference_name, field)
|
||||
else:
|
||||
self.get_debited_credited_accounts()
|
||||
if len(self.accounts_credited) > 1 and len(self.accounts_debited) > 1:
|
||||
self.auto_set_against_accounts()
|
||||
return
|
||||
self.get_against_accounts()
|
||||
for d in self.get("accounts"):
|
||||
if flt(d.debit) > 0:
|
||||
accounts_debited.append(d.party or d.account)
|
||||
if flt(d.credit) > 0:
|
||||
accounts_credited.append(d.party or d.account)
|
||||
|
||||
def auto_set_against_accounts(self):
|
||||
for i in range(0, len(self.accounts), 2):
|
||||
acc = self.accounts[i]
|
||||
against_acc = self.accounts[i + 1]
|
||||
if acc.debit_in_account_currency > 0:
|
||||
current_val = acc.debit_in_account_currency * flt(acc.exchange_rate)
|
||||
against_val = against_acc.credit_in_account_currency * flt(against_acc.exchange_rate)
|
||||
else:
|
||||
current_val = acc.credit_in_account_currency * flt(acc.exchange_rate)
|
||||
against_val = against_acc.debit_in_account_currency * flt(against_acc.exchange_rate)
|
||||
|
||||
if current_val == against_val:
|
||||
acc.against_type = against_acc.party_type or "Account"
|
||||
against_acc.against_type = acc.party_type or "Account"
|
||||
|
||||
acc.against_account_link = against_acc.party or against_acc.account
|
||||
against_acc.against_account_link = acc.party or acc.account
|
||||
else:
|
||||
frappe.msgprint(
|
||||
_(
|
||||
"Unable to automatically determine {0} accounts. Set them up in the {1} table if needed."
|
||||
).format(frappe.bold("against"), frappe.bold("Accounting Entries")),
|
||||
alert=True,
|
||||
)
|
||||
break
|
||||
|
||||
def get_against_accounts(self):
|
||||
self.against_accounts = []
|
||||
self.split_account = {}
|
||||
self.get_debited_credited_accounts()
|
||||
|
||||
if self.separate_against_account_entries:
|
||||
no_of_credited_acc, no_of_debited_acc = len(self.accounts_credited), len(self.accounts_debited)
|
||||
if no_of_credited_acc <= 1 and no_of_debited_acc <= 1:
|
||||
self.set_against_accounts_for_single_dr_cr()
|
||||
self.separate_against_account_entries = 0
|
||||
elif no_of_credited_acc == 1:
|
||||
self.against_accounts = self.accounts_debited
|
||||
self.split_account = self.accounts_credited[0]
|
||||
elif no_of_debited_acc == 1:
|
||||
self.against_accounts = self.accounts_credited
|
||||
self.split_account = self.accounts_debited[0]
|
||||
|
||||
def get_debited_credited_accounts(self):
|
||||
self.accounts_debited, self.accounts_credited = [], []
|
||||
self.separate_against_account_entries = 1
|
||||
for d in self.get("accounts"):
|
||||
if flt(d.debit) > 0:
|
||||
self.accounts_debited.append(d)
|
||||
elif flt(d.credit) > 0:
|
||||
self.accounts_credited.append(d)
|
||||
|
||||
if d.against_account_link:
|
||||
self.separate_against_account_entries = 0
|
||||
break
|
||||
|
||||
def set_against_accounts_for_single_dr_cr(self):
|
||||
against_account = None
|
||||
for d in self.accounts:
|
||||
if flt(d.debit) > 0:
|
||||
against_account = self.accounts_credited[0]
|
||||
elif flt(d.credit) > 0:
|
||||
against_account = self.accounts_debited[0]
|
||||
if against_account:
|
||||
d.against_type = against_account.party_type or "Account"
|
||||
d.against_account = against_account.party or against_account.account
|
||||
d.against_account_link = against_account.party or against_account.account
|
||||
for d in self.get("accounts"):
|
||||
if flt(d.debit) > 0:
|
||||
d.against_account = ", ".join(list(set(accounts_credited)))
|
||||
if flt(d.credit) > 0:
|
||||
d.against_account = ", ".join(list(set(accounts_debited)))
|
||||
|
||||
def validate_debit_credit_amount(self):
|
||||
if not (self.voucher_type == "Exchange Gain Or Loss" and self.multi_currency):
|
||||
@@ -1031,108 +967,42 @@ class JournalEntry(AccountsController):
|
||||
|
||||
def build_gl_map(self):
|
||||
gl_map = []
|
||||
conversion_rate_map = self.get_conversion_rate_map()
|
||||
transaction_currency_map = self.get_transaction_currency_map()
|
||||
company_currency = erpnext.get_company_currency(self.company)
|
||||
|
||||
self.get_against_accounts()
|
||||
for d in self.get("accounts"):
|
||||
if d.debit or d.credit or (self.voucher_type == "Exchange Gain Or Loss"):
|
||||
r = [d.user_remark, self.remark]
|
||||
r = [x for x in r if x]
|
||||
remarks = "\n".join(r)
|
||||
|
||||
gl_dict = self.get_gl_dict(
|
||||
{
|
||||
"account": d.account,
|
||||
"party_type": d.party_type,
|
||||
"due_date": self.due_date,
|
||||
"party": d.party,
|
||||
"debit": flt(d.debit, d.precision("debit")),
|
||||
"credit": flt(d.credit, d.precision("credit")),
|
||||
"account_currency": d.account_currency,
|
||||
"debit_in_account_currency": flt(
|
||||
d.debit_in_account_currency, d.precision("debit_in_account_currency")
|
||||
),
|
||||
"credit_in_account_currency": flt(
|
||||
d.credit_in_account_currency, d.precision("credit_in_account_currency")
|
||||
),
|
||||
"against_voucher_type": d.reference_type,
|
||||
"against_voucher": d.reference_name,
|
||||
"remarks": remarks,
|
||||
"voucher_detail_no": d.reference_detail_no,
|
||||
"cost_center": d.cost_center,
|
||||
"project": d.project,
|
||||
"finance_book": self.finance_book,
|
||||
"conversion_rate": conversion_rate_map.get(d.against_account_link, 1)
|
||||
if d.account_currency == company_currency
|
||||
else 1,
|
||||
"currency": transaction_currency_map.get(d.against_account_link, d.account_currency)
|
||||
if d.account_currency == company_currency
|
||||
else d.account_currency,
|
||||
},
|
||||
item=d,
|
||||
gl_map.append(
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": d.account,
|
||||
"party_type": d.party_type,
|
||||
"due_date": self.due_date,
|
||||
"party": d.party,
|
||||
"against": d.against_account,
|
||||
"debit": flt(d.debit, d.precision("debit")),
|
||||
"credit": flt(d.credit, d.precision("credit")),
|
||||
"account_currency": d.account_currency,
|
||||
"debit_in_account_currency": flt(
|
||||
d.debit_in_account_currency, d.precision("debit_in_account_currency")
|
||||
),
|
||||
"credit_in_account_currency": flt(
|
||||
d.credit_in_account_currency, d.precision("credit_in_account_currency")
|
||||
),
|
||||
"against_voucher_type": d.reference_type,
|
||||
"against_voucher": d.reference_name,
|
||||
"remarks": remarks,
|
||||
"voucher_detail_no": d.reference_detail_no,
|
||||
"cost_center": d.cost_center,
|
||||
"project": d.project,
|
||||
"finance_book": self.finance_book,
|
||||
},
|
||||
item=d,
|
||||
)
|
||||
)
|
||||
|
||||
if not self.separate_against_account_entries:
|
||||
gl_dict.update(
|
||||
{
|
||||
"against_type": d.against_type,
|
||||
"against_link": d.against_account_link,
|
||||
}
|
||||
)
|
||||
gl_map.append(gl_dict)
|
||||
|
||||
elif d in self.against_accounts:
|
||||
gl_dict.update(
|
||||
{
|
||||
"against_type": self.split_account.get("party_type") or "Account",
|
||||
"against": self.split_account.get("party") or self.split_account.get("account"),
|
||||
"against_link": self.split_account.get("party") or self.split_account.get("account"),
|
||||
}
|
||||
)
|
||||
gl_map.append(gl_dict)
|
||||
|
||||
else:
|
||||
for against_account in self.against_accounts:
|
||||
against_account = against_account.as_dict()
|
||||
debit = against_account.credit or against_account.credit_in_account_currency
|
||||
credit = against_account.debit or against_account.debit_in_account_currency
|
||||
gl_dict = gl_dict.copy()
|
||||
gl_dict.update(
|
||||
{
|
||||
"against_type": against_account.party_type or "Account",
|
||||
"against": against_account.party or against_account.account,
|
||||
"against_link": against_account.party or against_account.account,
|
||||
"debit": flt(debit, d.precision("debit")),
|
||||
"credit": flt(credit, d.precision("credit")),
|
||||
"account_currency": d.account_currency,
|
||||
"debit_in_account_currency": flt(
|
||||
debit / d.exchange_rate, d.precision("debit_in_account_currency")
|
||||
),
|
||||
"credit_in_account_currency": flt(
|
||||
credit / d.exchange_rate, d.precision("credit_in_account_currency")
|
||||
),
|
||||
}
|
||||
)
|
||||
gl_map.append(gl_dict)
|
||||
|
||||
return gl_map
|
||||
|
||||
def get_transaction_currency_map(self):
|
||||
transaction_currency_map = {}
|
||||
for account in self.get("accounts"):
|
||||
transaction_currency_map.setdefault(account.party or account.account, account.account_currency)
|
||||
|
||||
return transaction_currency_map
|
||||
|
||||
def get_conversion_rate_map(self):
|
||||
conversion_rate_map = {}
|
||||
for account in self.get("accounts"):
|
||||
conversion_rate_map.setdefault(account.party or account.account, account.exchange_rate)
|
||||
|
||||
return conversion_rate_map
|
||||
|
||||
def make_gl_entries(self, cancel=0, adv_adj=0):
|
||||
from erpnext.accounts.general_ledger import make_gl_entries
|
||||
|
||||
@@ -1755,10 +1625,3 @@ def make_reverse_journal_entry(source_name, target_doc=None):
|
||||
)
|
||||
|
||||
return doclist
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_against_type(doctype, txt, searchfield, start, page_len, filters):
|
||||
against_types = frappe.db.get_list("Party Type", pluck="name") + ["Account"]
|
||||
doctype = frappe.qb.DocType("DocType")
|
||||
return frappe.qb.from_(doctype).select(doctype.name).where(doctype.name.isin(against_types)).run()
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
frappe.listview_settings['Journal Entry'] = {
|
||||
add_fields: ["voucher_type", "posting_date", "total_debit", "company", "user_remark"],
|
||||
get_indicator: function(doc) {
|
||||
if(doc.docstatus==0) {
|
||||
return [__("Draft", "red", "docstatus,=,0")]
|
||||
} else if(doc.docstatus==2) {
|
||||
return [__("Cancelled", "grey", "docstatus,=,2")]
|
||||
} else {
|
||||
return [__(doc.voucher_type), "blue", "voucher_type,=," + doc.voucher_type]
|
||||
frappe.listview_settings["Journal Entry"] = {
|
||||
add_fields: [
|
||||
"voucher_type",
|
||||
"posting_date",
|
||||
"total_debit",
|
||||
"company",
|
||||
"user_remark",
|
||||
],
|
||||
get_indicator: function (doc) {
|
||||
if (doc.docstatus === 1) {
|
||||
return [
|
||||
__(doc.voucher_type),
|
||||
"blue",
|
||||
`voucher_type,=,${doc.voucher_type}`,
|
||||
];
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
@@ -37,9 +37,7 @@
|
||||
"col_break3",
|
||||
"is_advance",
|
||||
"user_remark",
|
||||
"against_type",
|
||||
"against_account",
|
||||
"against_account_link"
|
||||
"against_account"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@@ -252,21 +250,14 @@
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "against_account",
|
||||
"fieldtype": "Text",
|
||||
"hidden": 1,
|
||||
"label": "Against Account",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "against_account",
|
||||
"oldfieldtype": "Text",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "against_account_link",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"fieldname": "against_account",
|
||||
"fieldtype": "Text",
|
||||
"hidden": 1,
|
||||
"label": "Against Account",
|
||||
"no_copy": 1,
|
||||
"options": "against_type"
|
||||
"oldfieldname": "against_account",
|
||||
"oldfieldtype": "Text",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
@@ -290,18 +281,12 @@
|
||||
"hidden": 1,
|
||||
"label": "Reference Detail No",
|
||||
"no_copy": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "against_type",
|
||||
"fieldtype": "Link",
|
||||
"label": "Against Type",
|
||||
"options": "DocType"
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2023-12-02 23:21:22.205409",
|
||||
"modified": "2023-12-03 23:21:22.205409",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Journal Entry Account",
|
||||
|
||||
@@ -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 Payment', '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', "Bank Transaction"];
|
||||
|
||||
if(frm.doc.__islocal) {
|
||||
if (!frm.doc.paid_from) frm.set_value("paid_from_account_currency", null);
|
||||
@@ -747,6 +747,10 @@ frappe.ui.form.on('Payment Entry', {
|
||||
args["get_orders_to_be_billed"] = true;
|
||||
}
|
||||
|
||||
if (frm.doc.book_advance_payments_in_separate_party_account) {
|
||||
args["book_advance_payments_in_separate_party_account"] = true;
|
||||
}
|
||||
|
||||
frappe.flags.allocate_payment_amount = filters['allocate_payment_amount'];
|
||||
|
||||
return frappe.call({
|
||||
@@ -929,7 +933,7 @@ frappe.ui.form.on('Payment Entry', {
|
||||
if(frm.doc.payment_type == "Receive"
|
||||
&& frm.doc.base_total_allocated_amount < frm.doc.base_received_amount + total_deductions
|
||||
&& frm.doc.total_allocated_amount < frm.doc.paid_amount + (total_deductions / frm.doc.source_exchange_rate)) {
|
||||
unallocated_amount = (frm.doc.base_received_amount + total_deductions + flt(frm.doc.base_total_taxes_and_charges)
|
||||
unallocated_amount = (frm.doc.base_received_amount + total_deductions - flt(frm.doc.base_total_taxes_and_charges)
|
||||
- frm.doc.base_total_allocated_amount) / frm.doc.source_exchange_rate;
|
||||
} else if (frm.doc.payment_type == "Pay"
|
||||
&& frm.doc.base_total_allocated_amount < frm.doc.base_paid_amount - total_deductions
|
||||
|
||||
@@ -223,6 +223,7 @@
|
||||
"fieldname": "party_balance",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Party Balance",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
@@ -759,7 +760,7 @@
|
||||
"table_fieldname": "payment_entries"
|
||||
}
|
||||
],
|
||||
"modified": "2023-11-23 12:07:20.887885",
|
||||
"modified": "2024-01-08 13:17:15.744754",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Entry",
|
||||
|
||||
@@ -50,6 +50,88 @@ class InvalidPaymentEntry(ValidationError):
|
||||
|
||||
|
||||
class PaymentEntry(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.advance_taxes_and_charges.advance_taxes_and_charges import (
|
||||
AdvanceTaxesandCharges,
|
||||
)
|
||||
from erpnext.accounts.doctype.payment_entry_deduction.payment_entry_deduction import (
|
||||
PaymentEntryDeduction,
|
||||
)
|
||||
from erpnext.accounts.doctype.payment_entry_reference.payment_entry_reference import (
|
||||
PaymentEntryReference,
|
||||
)
|
||||
|
||||
amended_from: DF.Link | None
|
||||
apply_tax_withholding_amount: DF.Check
|
||||
auto_repeat: DF.Link | None
|
||||
bank: DF.ReadOnly | None
|
||||
bank_account: DF.Link | None
|
||||
bank_account_no: DF.ReadOnly | None
|
||||
base_paid_amount: DF.Currency
|
||||
base_paid_amount_after_tax: DF.Currency
|
||||
base_received_amount: DF.Currency
|
||||
base_received_amount_after_tax: DF.Currency
|
||||
base_total_allocated_amount: DF.Currency
|
||||
base_total_taxes_and_charges: DF.Currency
|
||||
book_advance_payments_in_separate_party_account: DF.Check
|
||||
clearance_date: DF.Date | None
|
||||
company: DF.Link
|
||||
contact_email: DF.Data | None
|
||||
contact_person: DF.Link | None
|
||||
cost_center: DF.Link | None
|
||||
custom_remarks: DF.Check
|
||||
deductions: DF.Table[PaymentEntryDeduction]
|
||||
difference_amount: DF.Currency
|
||||
letter_head: DF.Link | None
|
||||
mode_of_payment: DF.Link | None
|
||||
naming_series: DF.Literal["ACC-PAY-.YYYY.-"]
|
||||
paid_amount: DF.Currency
|
||||
paid_amount_after_tax: DF.Currency
|
||||
paid_from: DF.Link
|
||||
paid_from_account_balance: DF.Currency
|
||||
paid_from_account_currency: DF.Link
|
||||
paid_from_account_type: DF.Data | None
|
||||
paid_to: DF.Link
|
||||
paid_to_account_balance: DF.Currency
|
||||
paid_to_account_currency: DF.Link
|
||||
paid_to_account_type: DF.Data | None
|
||||
party: DF.DynamicLink | None
|
||||
party_balance: DF.Currency
|
||||
party_bank_account: DF.Link | None
|
||||
party_name: DF.Data | None
|
||||
party_type: DF.Link | None
|
||||
payment_order: DF.Link | None
|
||||
payment_order_status: DF.Literal["Initiated", "Payment Ordered"]
|
||||
payment_type: DF.Literal["Receive", "Pay", "Internal Transfer"]
|
||||
posting_date: DF.Date
|
||||
print_heading: DF.Link | None
|
||||
project: DF.Link | None
|
||||
purchase_taxes_and_charges_template: DF.Link | None
|
||||
received_amount: DF.Currency
|
||||
received_amount_after_tax: DF.Currency
|
||||
reference_date: DF.Date | None
|
||||
reference_no: DF.Data | None
|
||||
references: DF.Table[PaymentEntryReference]
|
||||
remarks: DF.SmallText | None
|
||||
sales_taxes_and_charges_template: DF.Link | None
|
||||
source_exchange_rate: DF.Float
|
||||
status: DF.Literal["", "Draft", "Submitted", "Cancelled"]
|
||||
target_exchange_rate: DF.Float
|
||||
tax_withholding_category: DF.Link | None
|
||||
taxes: DF.Table[AdvanceTaxesandCharges]
|
||||
title: DF.Data | None
|
||||
total_allocated_amount: DF.Currency
|
||||
total_taxes_and_charges: DF.Currency
|
||||
unallocated_amount: DF.Currency
|
||||
# end: auto-generated types
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(PaymentEntry, self).__init__(*args, **kwargs)
|
||||
if not self.is_new():
|
||||
@@ -256,6 +338,7 @@ class PaymentEntry(AccountsController):
|
||||
"get_outstanding_invoices": True,
|
||||
"get_orders_to_be_billed": True,
|
||||
"vouchers": vouchers,
|
||||
"book_advance_payments_in_separate_party_account": self.book_advance_payments_in_separate_party_account,
|
||||
},
|
||||
validate=True,
|
||||
)
|
||||
@@ -1061,9 +1144,7 @@ class PaymentEntry(AccountsController):
|
||||
"account": self.party_account,
|
||||
"party_type": self.party_type,
|
||||
"party": self.party,
|
||||
"against_type": "Account",
|
||||
"against": against_account,
|
||||
"against_link": against_account,
|
||||
"account_currency": self.party_account_currency,
|
||||
"cost_center": self.cost_center,
|
||||
},
|
||||
@@ -1228,9 +1309,7 @@ class PaymentEntry(AccountsController):
|
||||
{
|
||||
"account": self.paid_from,
|
||||
"account_currency": self.paid_from_account_currency,
|
||||
"against_type": self.party_type if self.payment_type == "Pay" else "Account",
|
||||
"against": self.party if self.payment_type == "Pay" else self.paid_to,
|
||||
"against_link": self.party if self.payment_type == "Pay" else self.paid_to,
|
||||
"credit_in_account_currency": self.paid_amount,
|
||||
"credit": self.base_paid_amount,
|
||||
"cost_center": self.cost_center,
|
||||
@@ -1245,9 +1324,7 @@ class PaymentEntry(AccountsController):
|
||||
{
|
||||
"account": self.paid_to,
|
||||
"account_currency": self.paid_to_account_currency,
|
||||
"against_type": self.party_type if self.payment_type == "Receive" else "Account",
|
||||
"against": self.party if self.payment_type == "Receive" else self.paid_from,
|
||||
"against_link": self.party if self.payment_type == "Receive" else self.paid_from,
|
||||
"debit_in_account_currency": self.received_amount,
|
||||
"debit": self.base_received_amount,
|
||||
"cost_center": self.cost_center,
|
||||
@@ -1271,7 +1348,6 @@ class PaymentEntry(AccountsController):
|
||||
rev_dr_or_cr = "credit" if dr_or_cr == "debit" else "debit"
|
||||
against = self.party or self.paid_to
|
||||
|
||||
against_type = self.party_type or "Account"
|
||||
payment_account = self.get_party_account_for_taxes()
|
||||
tax_amount = d.tax_amount
|
||||
base_tax_amount = d.base_tax_amount
|
||||
@@ -1280,9 +1356,7 @@ class PaymentEntry(AccountsController):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": d.account_head,
|
||||
"against_type": against_type,
|
||||
"against": against,
|
||||
"against_link": against,
|
||||
dr_or_cr: tax_amount,
|
||||
dr_or_cr + "_in_account_currency": base_tax_amount
|
||||
if account_currency == self.company_currency
|
||||
@@ -1307,9 +1381,7 @@ class PaymentEntry(AccountsController):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": payment_account,
|
||||
"against_type": against_type,
|
||||
"against": against,
|
||||
"against_link": against,
|
||||
rev_dr_or_cr: tax_amount,
|
||||
rev_dr_or_cr + "_in_account_currency": base_tax_amount
|
||||
if account_currency == self.company_currency
|
||||
@@ -1334,9 +1406,7 @@ class PaymentEntry(AccountsController):
|
||||
{
|
||||
"account": d.account,
|
||||
"account_currency": account_currency,
|
||||
"against_type": self.party_type or "Account",
|
||||
"against": self.party or self.paid_from,
|
||||
"against_link": self.party or self.paid_from,
|
||||
"debit_in_account_currency": d.amount,
|
||||
"debit": d.amount,
|
||||
"cost_center": d.cost_center,
|
||||
@@ -1628,11 +1698,16 @@ def get_outstanding_reference_documents(args, validate=False):
|
||||
outstanding_invoices = []
|
||||
negative_outstanding_invoices = []
|
||||
|
||||
if args.get("book_advance_payments_in_separate_party_account"):
|
||||
party_account = get_party_account(args.get("party_type"), args.get("party"), args.get("company"))
|
||||
else:
|
||||
party_account = args.get("party_account")
|
||||
|
||||
if args.get("get_outstanding_invoices"):
|
||||
outstanding_invoices = get_outstanding_invoices(
|
||||
args.get("party_type"),
|
||||
args.get("party"),
|
||||
get_party_account(args.get("party_type"), args.get("party"), args.get("company")),
|
||||
party_account,
|
||||
common_filter=common_filter,
|
||||
posting_date=posting_and_due_date,
|
||||
min_outstanding=args.get("outstanding_amt_greater_than"),
|
||||
|
||||
@@ -472,6 +472,15 @@ def make_payment_request(**args):
|
||||
"""Make payment request"""
|
||||
|
||||
args = frappe._dict(args)
|
||||
if args.dt not in [
|
||||
"Sales Order",
|
||||
"Purchase Order",
|
||||
"Sales Invoice",
|
||||
"Purchase Invoice",
|
||||
"POS Invoice",
|
||||
"Fees",
|
||||
]:
|
||||
frappe.throw(_("Payment Requests cannot be created against: {0}").format(frappe.bold(args.dt)))
|
||||
|
||||
ref_doc = frappe.get_doc(args.dt, args.dn)
|
||||
gateway_account = get_gateway_details(args) or frappe._dict()
|
||||
|
||||
@@ -579,12 +579,17 @@ def apply_price_discount_rule(pricing_rule, item_details, args):
|
||||
item_details[field] += pricing_rule.get(field, 0) if pricing_rule else args.get(field, 0)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def remove_pricing_rule_for_item(pricing_rules, item_details, item_code=None, rate=None):
|
||||
from erpnext.accounts.doctype.pricing_rule.utils import (
|
||||
get_applied_pricing_rules,
|
||||
get_pricing_rule_items,
|
||||
)
|
||||
|
||||
if isinstance(item_details, str):
|
||||
item_details = json.loads(item_details)
|
||||
item_details = frappe._dict(item_details)
|
||||
|
||||
for d in get_applied_pricing_rules(pricing_rules):
|
||||
if not d or not frappe.db.exists("Pricing Rule", d):
|
||||
continue
|
||||
|
||||
@@ -527,7 +527,7 @@ def get_qty_amount_data_for_cumulative(pr_doc, doc, items=None):
|
||||
values.extend(warehouses)
|
||||
|
||||
if items:
|
||||
condition = " and `tab{child_doc}`.{apply_on} in ({items})".format(
|
||||
condition += " and `tab{child_doc}`.{apply_on} in ({items})".format(
|
||||
child_doc=child_doctype, apply_on=apply_on, items=",".join(["%s"] * len(items))
|
||||
)
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
|
||||
super.onload();
|
||||
|
||||
// Ignore linked advances
|
||||
this.frm.ignore_doctypes_on_cancel_all = ['Journal Entry', 'Payment Entry', 'Purchase Invoice', "Repost Payment Ledger", "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries", "Serial and Batch Bundle"];
|
||||
this.frm.ignore_doctypes_on_cancel_all = ['Journal Entry', 'Payment Entry', 'Purchase Invoice', "Repost Payment Ledger", "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries", "Serial and Batch Bundle", "Bank Transaction"];
|
||||
|
||||
if(!this.frm.doc.__islocal) {
|
||||
// show credit_to in print format
|
||||
|
||||
@@ -296,6 +296,18 @@ class PurchaseInvoice(BuyingController):
|
||||
self.reset_default_field_value("set_warehouse", "items", "warehouse")
|
||||
self.reset_default_field_value("rejected_warehouse", "items", "rejected_warehouse")
|
||||
self.reset_default_field_value("set_from_warehouse", "items", "from_warehouse")
|
||||
self.set_percentage_received()
|
||||
|
||||
def set_percentage_received(self):
|
||||
total_billed_qty = 0.0
|
||||
total_received_qty = 0.0
|
||||
for row in self.items:
|
||||
if row.purchase_receipt and row.pr_detail and row.received_qty:
|
||||
total_billed_qty += row.qty
|
||||
total_received_qty += row.received_qty
|
||||
|
||||
if total_billed_qty and total_received_qty:
|
||||
self.per_received = total_received_qty / total_billed_qty * 100
|
||||
|
||||
def validate_release_date(self):
|
||||
if self.release_date and getdate(nowdate()) >= getdate(self.release_date):
|
||||
@@ -552,7 +564,7 @@ class PurchaseInvoice(BuyingController):
|
||||
self.against_expense_account = ",".join(against_accounts)
|
||||
|
||||
def po_required(self):
|
||||
if frappe.db.get_value("Buying Settings", None, "po_required") == "Yes":
|
||||
if frappe.db.get_single_value("Buying Settings", "po_required") == "Yes":
|
||||
|
||||
if frappe.get_value(
|
||||
"Supplier", self.supplier, "allow_purchase_invoice_creation_without_purchase_order"
|
||||
@@ -572,7 +584,7 @@ class PurchaseInvoice(BuyingController):
|
||||
|
||||
def pr_required(self):
|
||||
stock_items = self.get_stock_items()
|
||||
if frappe.db.get_value("Buying Settings", None, "pr_required") == "Yes":
|
||||
if frappe.db.get_single_value("Buying Settings", "pr_required") == "Yes":
|
||||
|
||||
if frappe.get_value(
|
||||
"Supplier", self.supplier, "allow_purchase_invoice_creation_without_purchase_receipt"
|
||||
@@ -815,9 +827,7 @@ class PurchaseInvoice(BuyingController):
|
||||
"party_type": "Supplier",
|
||||
"party": self.supplier,
|
||||
"due_date": self.due_date,
|
||||
"against_type": "Account",
|
||||
"against": self.against_expense_account,
|
||||
"against_link": self.against_expense_account,
|
||||
"credit": base_grand_total,
|
||||
"credit_in_account_currency": base_grand_total
|
||||
if self.party_account_currency == self.company_currency
|
||||
@@ -890,9 +900,7 @@ class PurchaseInvoice(BuyingController):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": warehouse_account[item.warehouse]["account"],
|
||||
"against_type": "Account",
|
||||
"against": warehouse_account[item.from_warehouse]["account"],
|
||||
"against_link": warehouse_account[item.from_warehouse]["account"],
|
||||
"cost_center": item.cost_center,
|
||||
"project": item.project or self.project,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
@@ -912,9 +920,7 @@ class PurchaseInvoice(BuyingController):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": warehouse_account[item.from_warehouse]["account"],
|
||||
"against_type": "Account",
|
||||
"against": warehouse_account[item.warehouse]["account"],
|
||||
"against_link": warehouse_account[item.warehouse]["account"],
|
||||
"cost_center": item.cost_center,
|
||||
"project": item.project or self.project,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
@@ -931,9 +937,7 @@ class PurchaseInvoice(BuyingController):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": item.expense_account,
|
||||
"against_type": "Supplier",
|
||||
"against": self.supplier,
|
||||
"against_link": self.supplier,
|
||||
"debit": flt(item.base_net_amount, item.precision("base_net_amount")),
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
"cost_center": item.cost_center,
|
||||
@@ -950,9 +954,7 @@ class PurchaseInvoice(BuyingController):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": item.expense_account,
|
||||
"against_type": "Supplier",
|
||||
"against": self.supplier,
|
||||
"against_link": self.supplier,
|
||||
"debit": warehouse_debit_amount,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
"cost_center": item.cost_center,
|
||||
@@ -971,9 +973,7 @@ class PurchaseInvoice(BuyingController):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": account,
|
||||
"against_type": "Account",
|
||||
"against": item.expense_account,
|
||||
"against_link": item.expense_account,
|
||||
"cost_center": item.cost_center,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
"credit": flt(amount["base_amount"]),
|
||||
@@ -993,9 +993,7 @@ class PurchaseInvoice(BuyingController):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": supplier_warehouse_account,
|
||||
"against_type": "Account",
|
||||
"against": item.expense_account,
|
||||
"against_link": item.expense_account,
|
||||
"cost_center": item.cost_center,
|
||||
"project": item.project or self.project,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
@@ -1050,9 +1048,7 @@ class PurchaseInvoice(BuyingController):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": expense_account,
|
||||
"against_type": "Supplier",
|
||||
"against": self.supplier,
|
||||
"against_link": self.supplier,
|
||||
"debit": amount,
|
||||
"cost_center": item.cost_center,
|
||||
"project": item.project or self.project,
|
||||
@@ -1078,9 +1074,7 @@ class PurchaseInvoice(BuyingController):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": expense_account,
|
||||
"against_type": "Supplier",
|
||||
"against": self.supplier,
|
||||
"against_link": self.supplier,
|
||||
"debit": discrepancy_caused_by_exchange_rate_difference,
|
||||
"cost_center": item.cost_center,
|
||||
"project": item.project or self.project,
|
||||
@@ -1093,9 +1087,7 @@ class PurchaseInvoice(BuyingController):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": self.get_company_default("exchange_gain_loss_account"),
|
||||
"against_type": "Supplier",
|
||||
"against": self.supplier,
|
||||
"against_link": self.supplier,
|
||||
"credit": discrepancy_caused_by_exchange_rate_difference,
|
||||
"cost_center": item.cost_center,
|
||||
"project": item.project or self.project,
|
||||
@@ -1104,17 +1096,6 @@ class PurchaseInvoice(BuyingController):
|
||||
item=item,
|
||||
)
|
||||
)
|
||||
|
||||
# update gross amount of asset bought through this document
|
||||
assets = frappe.db.get_all(
|
||||
"Asset", filters={"purchase_invoice": self.name, "item_code": item.item_code}
|
||||
)
|
||||
for asset in assets:
|
||||
frappe.db.set_value("Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate))
|
||||
frappe.db.set_value(
|
||||
"Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate)
|
||||
)
|
||||
|
||||
if (
|
||||
self.auto_accounting_for_stock
|
||||
and self.is_opening == "No"
|
||||
@@ -1139,10 +1120,8 @@ class PurchaseInvoice(BuyingController):
|
||||
gl_entries.append(
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": stock_rbnb,
|
||||
"against_type": "Supplier",
|
||||
"account": self.stock_received_but_not_billed,
|
||||
"against": self.supplier,
|
||||
"against_link": self.supplier,
|
||||
"debit": flt(item.item_tax_amount, item.precision("item_tax_amount")),
|
||||
"remarks": self.remarks or _("Accounting Entry for Stock"),
|
||||
"cost_center": self.cost_center,
|
||||
@@ -1156,17 +1135,24 @@ class PurchaseInvoice(BuyingController):
|
||||
item.item_tax_amount, item.precision("item_tax_amount")
|
||||
)
|
||||
|
||||
if item.is_fixed_asset and item.landed_cost_voucher_amount:
|
||||
self.update_gross_purchase_amount_for_linked_assets(item)
|
||||
|
||||
def update_gross_purchase_amount_for_linked_assets(self, item):
|
||||
assets = frappe.db.get_all(
|
||||
"Asset",
|
||||
filters={"purchase_invoice": self.name, "item_code": item.item_code},
|
||||
fields=["name", "asset_quantity"],
|
||||
)
|
||||
for asset in assets:
|
||||
purchase_amount = flt(item.valuation_rate) * asset.asset_quantity
|
||||
frappe.db.set_value(
|
||||
"Asset", asset.name, "gross_purchase_amount", flt(item.valuation_rate) * asset.asset_quantity
|
||||
)
|
||||
frappe.db.set_value(
|
||||
"Asset", asset.name, "purchase_receipt_amount", flt(item.valuation_rate) * asset.asset_quantity
|
||||
"Asset",
|
||||
asset.name,
|
||||
{
|
||||
"gross_purchase_amount": purchase_amount,
|
||||
"purchase_receipt_amount": purchase_amount,
|
||||
},
|
||||
)
|
||||
|
||||
def make_stock_adjustment_entry(
|
||||
@@ -1196,9 +1182,7 @@ class PurchaseInvoice(BuyingController):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": cost_of_goods_sold_account,
|
||||
"against_type": "Account",
|
||||
"against": item.expense_account,
|
||||
"against_link": item.expense_account,
|
||||
"debit": stock_adjustment_amt,
|
||||
"remarks": self.get("remarks") or _("Stock Adjustment"),
|
||||
"cost_center": item.cost_center,
|
||||
@@ -1228,9 +1212,7 @@ class PurchaseInvoice(BuyingController):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": tax.account_head,
|
||||
"against_type": "Supplier",
|
||||
"against": self.supplier,
|
||||
"against_link": self.supplier,
|
||||
dr_or_cr: base_amount,
|
||||
dr_or_cr + "_in_account_currency": base_amount
|
||||
if account_currency == self.company_currency
|
||||
@@ -1278,10 +1260,8 @@ class PurchaseInvoice(BuyingController):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": tax.account_head,
|
||||
"against_type": "Supplier",
|
||||
"cost_center": tax.cost_center,
|
||||
"against": self.supplier,
|
||||
"against_link": self.supplier,
|
||||
"credit": applicable_amount,
|
||||
"remarks": self.remarks or _("Accounting Entry for Stock"),
|
||||
},
|
||||
@@ -1299,9 +1279,7 @@ class PurchaseInvoice(BuyingController):
|
||||
{
|
||||
"account": tax.account_head,
|
||||
"cost_center": tax.cost_center,
|
||||
"against_type": "Supplier",
|
||||
"against": self.supplier,
|
||||
"against_link": self.supplier,
|
||||
"credit": valuation_tax[tax.name],
|
||||
"remarks": self.remarks or _("Accounting Entry for Stock"),
|
||||
},
|
||||
@@ -1316,9 +1294,7 @@ class PurchaseInvoice(BuyingController):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": self.unrealized_profit_loss_account,
|
||||
"against_type": "Supplier",
|
||||
"against": self.supplier,
|
||||
"against_link": self.supplier,
|
||||
"credit": flt(self.total_taxes_and_charges),
|
||||
"credit_in_account_currency": flt(self.base_total_taxes_and_charges),
|
||||
"cost_center": self.cost_center,
|
||||
@@ -1339,9 +1315,7 @@ class PurchaseInvoice(BuyingController):
|
||||
"account": self.credit_to,
|
||||
"party_type": "Supplier",
|
||||
"party": self.supplier,
|
||||
"against_type": "Account",
|
||||
"against": self.cash_bank_account,
|
||||
"against_link": self.cash_bank_account,
|
||||
"debit": self.base_paid_amount,
|
||||
"debit_in_account_currency": self.base_paid_amount
|
||||
if self.party_account_currency == self.company_currency
|
||||
@@ -1362,9 +1336,7 @@ class PurchaseInvoice(BuyingController):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": self.cash_bank_account,
|
||||
"against_type": "Supplier",
|
||||
"against": self.supplier,
|
||||
"against_link": self.supplier,
|
||||
"credit": self.base_paid_amount,
|
||||
"credit_in_account_currency": self.base_paid_amount
|
||||
if bank_account_currency == self.company_currency
|
||||
@@ -1388,9 +1360,7 @@ class PurchaseInvoice(BuyingController):
|
||||
"account": self.credit_to,
|
||||
"party_type": "Supplier",
|
||||
"party": self.supplier,
|
||||
"against_type": "Account",
|
||||
"against": self.write_off_account,
|
||||
"against_link": self.write_off_account,
|
||||
"debit": self.base_write_off_amount,
|
||||
"debit_in_account_currency": self.base_write_off_amount
|
||||
if self.party_account_currency == self.company_currency
|
||||
@@ -1410,9 +1380,7 @@ class PurchaseInvoice(BuyingController):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": self.write_off_account,
|
||||
"against_type": "Supplier",
|
||||
"against": self.supplier,
|
||||
"against_link": self.supplier,
|
||||
"credit": flt(self.base_write_off_amount),
|
||||
"credit_in_account_currency": self.base_write_off_amount
|
||||
if write_off_account_currency == self.company_currency
|
||||
@@ -1439,9 +1407,7 @@ class PurchaseInvoice(BuyingController):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": round_off_account,
|
||||
"against_type": "Supplier",
|
||||
"against": self.supplier,
|
||||
"against_link": self.supplier,
|
||||
"debit_in_account_currency": self.rounding_adjustment,
|
||||
"debit": self.base_rounding_adjustment,
|
||||
"cost_center": round_off_cost_center
|
||||
|
||||
@@ -14,7 +14,7 @@ from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_ent
|
||||
from erpnext.buying.doctype.purchase_order.purchase_order import get_mapped_purchase_invoice
|
||||
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
|
||||
from erpnext.buying.doctype.supplier.test_supplier import create_supplier
|
||||
from erpnext.controllers.accounts_controller import get_payment_terms
|
||||
from erpnext.controllers.accounts_controller import InvalidQtyError, get_payment_terms
|
||||
from erpnext.controllers.buying_controller import QtyMismatchError
|
||||
from erpnext.exceptions import InvalidCurrency
|
||||
from erpnext.projects.doctype.project.test_project import make_project
|
||||
@@ -51,6 +51,16 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
|
||||
def tearDown(self):
|
||||
frappe.db.rollback()
|
||||
|
||||
def test_purchase_invoice_qty(self):
|
||||
pi = make_purchase_invoice(qty=0, do_not_save=True)
|
||||
with self.assertRaises(InvalidQtyError):
|
||||
pi.save()
|
||||
|
||||
# No error with qty=1
|
||||
pi.items[0].qty = 1
|
||||
pi.save()
|
||||
self.assertEqual(pi.items[0].qty, 1)
|
||||
|
||||
def test_purchase_invoice_received_qty(self):
|
||||
"""
|
||||
1. Test if received qty is validated against accepted + rejected
|
||||
@@ -1227,11 +1237,11 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
|
||||
|
||||
@change_settings("Accounts Settings", {"unlink_payment_on_cancellation_of_invoice": 1})
|
||||
def test_gain_loss_with_advance_entry(self):
|
||||
unlink_enabled = frappe.db.get_value(
|
||||
"Accounts Settings", "Accounts Settings", "unlink_payment_on_cancel_of_invoice"
|
||||
unlink_enabled = frappe.db.get_single_value(
|
||||
"Accounts Settings", "unlink_payment_on_cancellation_of_invoice"
|
||||
)
|
||||
|
||||
frappe.db.set_single_value("Accounts Settings", "unlink_payment_on_cancel_of_invoice", 1)
|
||||
frappe.db.set_single_value("Accounts Settings", "unlink_payment_on_cancellation_of_invoice", 1)
|
||||
|
||||
original_account = frappe.db.get_value("Company", "_Test Company", "exchange_gain_loss_account")
|
||||
frappe.db.set_value(
|
||||
@@ -1422,7 +1432,7 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
|
||||
pay.cancel()
|
||||
|
||||
frappe.db.set_single_value(
|
||||
"Accounts Settings", "unlink_payment_on_cancel_of_invoice", unlink_enabled
|
||||
"Accounts Settings", "unlink_payment_on_cancellation_of_invoice", unlink_enabled
|
||||
)
|
||||
frappe.db.set_value("Company", "_Test Company", "exchange_gain_loss_account", original_account)
|
||||
|
||||
@@ -2114,7 +2124,7 @@ def make_purchase_invoice(**args):
|
||||
bundle_id = None
|
||||
if args.get("batch_no") or args.get("serial_no"):
|
||||
batches = {}
|
||||
qty = args.qty or 5
|
||||
qty = args.qty if args.qty is not None else 5
|
||||
item_code = args.item or args.item_code or "_Test Item"
|
||||
if args.get("batch_no"):
|
||||
batches = frappe._dict({args.batch_no: qty})
|
||||
@@ -2143,7 +2153,7 @@ def make_purchase_invoice(**args):
|
||||
"item_code": args.item or args.item_code or "_Test Item",
|
||||
"item_name": args.item_name,
|
||||
"warehouse": args.warehouse or "_Test Warehouse - _TC",
|
||||
"qty": args.qty or 5,
|
||||
"qty": args.qty if args.qty is not None else 5,
|
||||
"received_qty": args.received_qty or 0,
|
||||
"rejected_qty": args.rejected_qty or 0,
|
||||
"rate": args.rate or 50,
|
||||
|
||||
@@ -126,7 +126,7 @@
|
||||
"fieldname": "rate",
|
||||
"fieldtype": "Float",
|
||||
"in_list_view": 1,
|
||||
"label": "Rate",
|
||||
"label": "Tax Rate",
|
||||
"oldfieldname": "rate",
|
||||
"oldfieldtype": "Currency"
|
||||
},
|
||||
@@ -230,7 +230,7 @@
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-08-05 20:04:36.618240",
|
||||
"modified": "2024-01-14 10:04:36.618240",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Taxes and Charges",
|
||||
@@ -239,4 +239,4 @@
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,8 +37,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e
|
||||
super.onload();
|
||||
|
||||
this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice', 'Timesheet', 'POS Invoice Merge Log',
|
||||
'POS Closing Entry', 'Journal Entry', 'Payment Entry', "Repost Payment Ledger", "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries",
|
||||
'Serial and Batch Bundle'
|
||||
'POS Closing Entry', 'Journal Entry', 'Payment Entry', "Repost Payment Ledger", "Repost Accounting Ledger", "Unreconcile Payment", "Unreconcile Payment Entries", "Serial and Batch Bundle", "Bank Transaction",
|
||||
];
|
||||
|
||||
if(!this.frm.doc.__islocal && !this.frm.doc.customer && this.frm.doc.debit_to) {
|
||||
@@ -898,8 +897,8 @@ frappe.ui.form.on('Sales Invoice', {
|
||||
frm.events.append_time_log(frm, timesheet, 1.0);
|
||||
}
|
||||
});
|
||||
frm.refresh_field("timesheets");
|
||||
frm.trigger("calculate_timesheet_totals");
|
||||
frm.refresh();
|
||||
},
|
||||
|
||||
async get_exchange_rate(frm, from_currency, to_currency) {
|
||||
|
||||
@@ -7,7 +7,7 @@ from frappe import _, msgprint, throw
|
||||
from frappe.contacts.doctype.address.address import get_address_display
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
from frappe.model.utils import get_fetch_values
|
||||
from frappe.utils import add_days, cint, flt, formatdate, get_link_to_form, getdate, nowdate
|
||||
from frappe.utils import add_days, cint, cstr, flt, formatdate, get_link_to_form, getdate, nowdate
|
||||
|
||||
import erpnext
|
||||
from erpnext.accounts.deferred_revenue import validate_service_stop_date
|
||||
@@ -1233,9 +1233,7 @@ class SalesInvoice(SellingController):
|
||||
"party_type": "Customer",
|
||||
"party": self.customer,
|
||||
"due_date": self.due_date,
|
||||
"against_type": "Account",
|
||||
"against": self.against_income_account,
|
||||
"against_link": self.against_income_account,
|
||||
"debit": base_grand_total,
|
||||
"debit_in_account_currency": base_grand_total
|
||||
if self.party_account_currency == self.company_currency
|
||||
@@ -1264,9 +1262,7 @@ class SalesInvoice(SellingController):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": tax.account_head,
|
||||
"against_type": "Customer",
|
||||
"against": self.customer,
|
||||
"against_link": self.customer,
|
||||
"credit": flt(base_amount, tax.precision("tax_amount_after_discount_amount")),
|
||||
"credit_in_account_currency": (
|
||||
flt(base_amount, tax.precision("base_tax_amount_after_discount_amount"))
|
||||
@@ -1287,9 +1283,7 @@ class SalesInvoice(SellingController):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": self.unrealized_profit_loss_account,
|
||||
"against_type": "Customer",
|
||||
"against": self.customer,
|
||||
"against_link": self.customer,
|
||||
"debit": flt(self.total_taxes_and_charges),
|
||||
"debit_in_account_currency": flt(self.base_total_taxes_and_charges),
|
||||
"cost_center": self.cost_center,
|
||||
@@ -1357,9 +1351,7 @@ class SalesInvoice(SellingController):
|
||||
add_asset_activity(asset.name, _("Asset sold"))
|
||||
|
||||
for gle in fixed_asset_gl_entries:
|
||||
gle["against_type"] = "Customer"
|
||||
gle["against"] = self.customer
|
||||
gle["against_link"] = self.customer
|
||||
gl_entries.append(self.get_gl_dict(gle, item=item))
|
||||
|
||||
self.set_asset_status(asset)
|
||||
@@ -1380,9 +1372,7 @@ class SalesInvoice(SellingController):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": income_account,
|
||||
"against_type": "Customer",
|
||||
"against": self.customer,
|
||||
"against_link": self.customer,
|
||||
"credit": flt(base_amount, item.precision("base_net_amount")),
|
||||
"credit_in_account_currency": (
|
||||
flt(base_amount, item.precision("base_net_amount"))
|
||||
@@ -1436,9 +1426,9 @@ class SalesInvoice(SellingController):
|
||||
"account": self.debit_to,
|
||||
"party_type": "Customer",
|
||||
"party": self.customer,
|
||||
"against_type": "Account",
|
||||
"against": self.loyalty_redemption_account,
|
||||
"against_link": self.loyalty_redemption_account,
|
||||
"against": "Expense account - "
|
||||
+ cstr(self.loyalty_redemption_account)
|
||||
+ " for the Loyalty Program",
|
||||
"credit": self.loyalty_amount,
|
||||
"against_voucher": self.return_against if cint(self.is_return) else self.name,
|
||||
"against_voucher_type": self.doctype,
|
||||
@@ -1452,9 +1442,7 @@ class SalesInvoice(SellingController):
|
||||
{
|
||||
"account": self.loyalty_redemption_account,
|
||||
"cost_center": self.cost_center or self.loyalty_redemption_cost_center,
|
||||
"against_type": "Customer",
|
||||
"against": self.customer,
|
||||
"against_link": self.customer,
|
||||
"debit": self.loyalty_amount,
|
||||
"remark": "Loyalty Points redeemed by the customer",
|
||||
},
|
||||
@@ -1481,9 +1469,7 @@ class SalesInvoice(SellingController):
|
||||
"account": self.debit_to,
|
||||
"party_type": "Customer",
|
||||
"party": self.customer,
|
||||
"against_type": "Account",
|
||||
"against": payment_mode.account,
|
||||
"against_link": payment_mode.account,
|
||||
"credit": payment_mode.base_amount,
|
||||
"credit_in_account_currency": payment_mode.base_amount
|
||||
if self.party_account_currency == self.company_currency
|
||||
@@ -1504,9 +1490,7 @@ class SalesInvoice(SellingController):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": payment_mode.account,
|
||||
"against_type": "Customer",
|
||||
"against": self.customer,
|
||||
"against_link": self.customer,
|
||||
"debit": payment_mode.base_amount,
|
||||
"debit_in_account_currency": payment_mode.base_amount
|
||||
if payment_mode_account_currency == self.company_currency
|
||||
@@ -1530,9 +1514,7 @@ class SalesInvoice(SellingController):
|
||||
"account": self.debit_to,
|
||||
"party_type": "Customer",
|
||||
"party": self.customer,
|
||||
"against_type": "Account",
|
||||
"against": self.account_for_change_amount,
|
||||
"against_link": self.account_for_change_amount,
|
||||
"debit": flt(self.base_change_amount),
|
||||
"debit_in_account_currency": flt(self.base_change_amount)
|
||||
if self.party_account_currency == self.company_currency
|
||||
@@ -1553,9 +1535,7 @@ class SalesInvoice(SellingController):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": self.account_for_change_amount,
|
||||
"against_type": "Customer",
|
||||
"against": self.customer,
|
||||
"against_link": self.customer,
|
||||
"credit": self.base_change_amount,
|
||||
"cost_center": self.cost_center,
|
||||
},
|
||||
@@ -1581,9 +1561,7 @@ class SalesInvoice(SellingController):
|
||||
"account": self.debit_to,
|
||||
"party_type": "Customer",
|
||||
"party": self.customer,
|
||||
"against_type": "Account",
|
||||
"against": self.write_off_account,
|
||||
"against_link": self.write_off_account,
|
||||
"credit": flt(self.base_write_off_amount, self.precision("base_write_off_amount")),
|
||||
"credit_in_account_currency": (
|
||||
flt(self.base_write_off_amount, self.precision("base_write_off_amount"))
|
||||
@@ -1603,9 +1581,7 @@ class SalesInvoice(SellingController):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": self.write_off_account,
|
||||
"against_type": "Customer",
|
||||
"against": self.customer,
|
||||
"against_link": self.customer,
|
||||
"debit": flt(self.base_write_off_amount, self.precision("base_write_off_amount")),
|
||||
"debit_in_account_currency": (
|
||||
flt(self.base_write_off_amount, self.precision("base_write_off_amount"))
|
||||
@@ -1633,9 +1609,7 @@ class SalesInvoice(SellingController):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": round_off_account,
|
||||
"against_type": "Customer",
|
||||
"against": self.customer,
|
||||
"against_link": self.customer,
|
||||
"credit_in_account_currency": flt(
|
||||
self.rounding_adjustment, self.precision("rounding_adjustment")
|
||||
),
|
||||
|
||||
@@ -23,7 +23,7 @@ from erpnext.assets.doctype.asset.test_asset import create_asset, create_asset_d
|
||||
from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_schedule import (
|
||||
get_depr_schedule,
|
||||
)
|
||||
from erpnext.controllers.accounts_controller import update_invoice_status
|
||||
from erpnext.controllers.accounts_controller import InvalidQtyError, update_invoice_status
|
||||
from erpnext.controllers.taxes_and_totals import get_itemised_tax_breakup_data
|
||||
from erpnext.exceptions import InvalidAccountCurrency, InvalidCurrency
|
||||
from erpnext.selling.doctype.customer.test_customer import get_customer_dict
|
||||
@@ -72,6 +72,16 @@ class TestSalesInvoice(FrappeTestCase):
|
||||
def tearDownClass(self):
|
||||
unlink_payment_on_cancel_of_invoice(0)
|
||||
|
||||
def test_sales_invoice_qty(self):
|
||||
si = create_sales_invoice(qty=0, do_not_save=True)
|
||||
with self.assertRaises(InvalidQtyError):
|
||||
si.save()
|
||||
|
||||
# No error with qty=1
|
||||
si.items[0].qty = 1
|
||||
si.save()
|
||||
self.assertEqual(si.items[0].qty, 1)
|
||||
|
||||
def test_timestamp_change(self):
|
||||
w = frappe.copy_doc(test_records[0])
|
||||
w.docstatus = 0
|
||||
@@ -3636,7 +3646,7 @@ def create_sales_invoice(**args):
|
||||
bundle_id = None
|
||||
if si.update_stock and (args.get("batch_no") or args.get("serial_no")):
|
||||
batches = {}
|
||||
qty = args.qty or 1
|
||||
qty = args.qty if args.qty is not None else 1
|
||||
item_code = args.item or args.item_code or "_Test Item"
|
||||
if args.get("batch_no"):
|
||||
batches = frappe._dict({args.batch_no: qty})
|
||||
@@ -3668,7 +3678,7 @@ def create_sales_invoice(**args):
|
||||
"description": args.description or "_Test Item",
|
||||
"warehouse": args.warehouse or "_Test Warehouse - _TC",
|
||||
"target_warehouse": args.target_warehouse,
|
||||
"qty": args.qty or 1,
|
||||
"qty": args.qty if args.qty is not None else 1,
|
||||
"uom": args.uom or "Nos",
|
||||
"stock_uom": args.uom or "Nos",
|
||||
"rate": args.rate if args.get("rate") is not None else 100,
|
||||
|
||||
@@ -108,7 +108,7 @@
|
||||
"fieldname": "rate",
|
||||
"fieldtype": "Float",
|
||||
"in_list_view": 1,
|
||||
"label": "Rate",
|
||||
"label": "Tax Rate",
|
||||
"oldfieldname": "rate",
|
||||
"oldfieldtype": "Currency"
|
||||
},
|
||||
@@ -218,7 +218,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2022-10-17 13:08:17.776528",
|
||||
"modified": "2022-10-18 13:08:17.776528",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Taxes and Charges",
|
||||
@@ -227,4 +227,4 @@
|
||||
"sort_field": "modified",
|
||||
"sort_order": "ASC",
|
||||
"states": []
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ from frappe.utils.data import (
|
||||
date_diff,
|
||||
flt,
|
||||
get_last_day,
|
||||
get_link_to_form,
|
||||
getdate,
|
||||
nowdate,
|
||||
)
|
||||
@@ -317,6 +318,37 @@ class Subscription(Document):
|
||||
if self.is_new():
|
||||
self.set_subscription_status()
|
||||
|
||||
self.validate_party_billing_currency()
|
||||
|
||||
def validate_party_billing_currency(self):
|
||||
"""
|
||||
Subscription should be of the same currency as the Party's default billing currency or company default.
|
||||
"""
|
||||
if self.party:
|
||||
party_billing_currency = frappe.get_cached_value(
|
||||
self.party_type, self.party, "default_currency"
|
||||
) or frappe.get_cached_value("Company", self.company, "default_currency")
|
||||
|
||||
plans = [x.plan for x in self.plans]
|
||||
subscription_plan_currencies = frappe.db.get_all(
|
||||
"Subscription Plan", filters={"name": ("in", plans)}, fields=["name", "currency"]
|
||||
)
|
||||
unsupported_plans = []
|
||||
for x in subscription_plan_currencies:
|
||||
if x.currency != party_billing_currency:
|
||||
unsupported_plans.append("{0}".format(get_link_to_form("Subscription Plan", x.name)))
|
||||
|
||||
if unsupported_plans:
|
||||
unsupported_plans = [
|
||||
_(
|
||||
"Below Subscription Plans are of different currency to the party default billing currency/Company currency: {0}"
|
||||
).format(frappe.bold(party_billing_currency))
|
||||
] + unsupported_plans
|
||||
|
||||
frappe.throw(
|
||||
unsupported_plans, frappe.ValidationError, "Unsupported Subscription Plans", as_list=True
|
||||
)
|
||||
|
||||
def validate_trial_period(self) -> None:
|
||||
"""
|
||||
Runs sanity checks on trial period dates for the `Subscription`
|
||||
@@ -563,6 +595,8 @@ class Subscription(Document):
|
||||
) and self.can_generate_new_invoice(posting_date):
|
||||
self.generate_invoice(posting_date=posting_date)
|
||||
self.update_subscription_period(add_days(self.current_invoice_end, 1))
|
||||
elif posting_date and getdate(posting_date) > getdate(self.current_invoice_end):
|
||||
self.update_subscription_period()
|
||||
|
||||
if self.cancel_at_period_end and (
|
||||
getdate(posting_date) >= getdate(self.current_invoice_end)
|
||||
|
||||
@@ -460,11 +460,13 @@ class TestSubscription(FrappeTestCase):
|
||||
self.assertEqual(len(subscription.invoices), 1)
|
||||
|
||||
def test_multi_currency_subscription(self):
|
||||
party = "_Test Subscription Customer"
|
||||
frappe.db.set_value("Customer", party, "default_currency", "USD")
|
||||
subscription = create_subscription(
|
||||
start_date="2018-01-01",
|
||||
generate_invoice_at="Beginning of the current subscription period",
|
||||
plans=[{"plan": "_Test Plan Multicurrency", "qty": 1}],
|
||||
party="_Test Subscription Customer",
|
||||
plans=[{"plan": "_Test Plan Multicurrency", "qty": 1, "currency": "USD"}],
|
||||
party=party,
|
||||
)
|
||||
|
||||
subscription.process()
|
||||
@@ -528,13 +530,21 @@ class TestSubscription(FrappeTestCase):
|
||||
|
||||
|
||||
def make_plans():
|
||||
create_plan(plan_name="_Test Plan Name", cost=900)
|
||||
create_plan(plan_name="_Test Plan Name 2", cost=1999)
|
||||
create_plan(plan_name="_Test Plan Name", cost=900, currency="INR")
|
||||
create_plan(plan_name="_Test Plan Name 2", cost=1999, currency="INR")
|
||||
create_plan(
|
||||
plan_name="_Test Plan Name 3", cost=1999, billing_interval="Day", billing_interval_count=14
|
||||
plan_name="_Test Plan Name 3",
|
||||
cost=1999,
|
||||
billing_interval="Day",
|
||||
billing_interval_count=14,
|
||||
currency="INR",
|
||||
)
|
||||
create_plan(
|
||||
plan_name="_Test Plan Name 4", cost=20000, billing_interval="Month", billing_interval_count=3
|
||||
plan_name="_Test Plan Name 4",
|
||||
cost=20000,
|
||||
billing_interval="Month",
|
||||
billing_interval_count=3,
|
||||
currency="INR",
|
||||
)
|
||||
create_plan(
|
||||
plan_name="_Test Plan Multicurrency", cost=50, billing_interval="Month", currency="USD"
|
||||
|
||||
@@ -41,7 +41,8 @@
|
||||
"fieldname": "currency",
|
||||
"fieldtype": "Link",
|
||||
"label": "Currency",
|
||||
"options": "Currency"
|
||||
"options": "Currency",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_3",
|
||||
@@ -148,10 +149,11 @@
|
||||
}
|
||||
],
|
||||
"links": [],
|
||||
"modified": "2021-12-10 15:24:15.794477",
|
||||
"modified": "2024-01-14 17:59:34.687977",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Subscription Plan",
|
||||
"naming_rule": "By fieldname",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
@@ -193,5 +195,6 @@
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -24,7 +24,7 @@ class SubscriptionPlan(Document):
|
||||
billing_interval_count: DF.Int
|
||||
cost: DF.Currency
|
||||
cost_center: DF.Link | None
|
||||
currency: DF.Link | None
|
||||
currency: DF.Link
|
||||
item: DF.Link
|
||||
payment_gateway: DF.Link | None
|
||||
plan_name: DF.Data
|
||||
|
||||
@@ -280,7 +280,6 @@ def check_if_in_list(gle, gl_map, dimensions=None):
|
||||
"project",
|
||||
"finance_book",
|
||||
"voucher_no",
|
||||
"against_link",
|
||||
]
|
||||
|
||||
if dimensions:
|
||||
@@ -654,10 +653,10 @@ def check_freezing_date(posting_date, adv_adj=False):
|
||||
Hence stop admin to bypass if accounts are freezed
|
||||
"""
|
||||
if not adv_adj:
|
||||
acc_frozen_upto = frappe.db.get_value("Accounts Settings", None, "acc_frozen_upto")
|
||||
acc_frozen_upto = frappe.db.get_single_value("Accounts Settings", "acc_frozen_upto")
|
||||
if acc_frozen_upto:
|
||||
frozen_accounts_modifier = frappe.db.get_value(
|
||||
"Accounts Settings", None, "frozen_accounts_modifier"
|
||||
frozen_accounts_modifier = frappe.db.get_single_value(
|
||||
"Accounts Settings", "frozen_accounts_modifier"
|
||||
)
|
||||
if getdate(posting_date) <= getdate(acc_frozen_upto) and (
|
||||
frozen_accounts_modifier not in frappe.get_roles() or frappe.session.user == "Administrator"
|
||||
|
||||
@@ -22,7 +22,7 @@ def get_columns(filters):
|
||||
"fieldtype": "Link",
|
||||
"fieldname": "account",
|
||||
"options": "Account",
|
||||
"width": 100,
|
||||
"width": 200,
|
||||
},
|
||||
{
|
||||
"label": _("Currency"),
|
||||
@@ -30,7 +30,7 @@ def get_columns(filters):
|
||||
"fieldname": "currency",
|
||||
"options": "Currency",
|
||||
"hidden": 1,
|
||||
"width": 50,
|
||||
"width": 100,
|
||||
},
|
||||
{
|
||||
"label": _("Balance"),
|
||||
|
||||
@@ -55,8 +55,8 @@ class ReceivablePayableReport(object):
|
||||
def run(self, args):
|
||||
self.filters.update(args)
|
||||
self.set_defaults()
|
||||
self.party_naming_by = frappe.db.get_value(
|
||||
args.get("naming_by")[0], None, args.get("naming_by")[1]
|
||||
self.party_naming_by = frappe.db.get_single_value(
|
||||
args.get("naming_by")[0], args.get("naming_by")[1]
|
||||
)
|
||||
self.get_columns()
|
||||
self.get_data()
|
||||
|
||||
@@ -24,8 +24,8 @@ class AccountsReceivableSummary(ReceivablePayableReport):
|
||||
def run(self, args):
|
||||
self.account_type = args.get("account_type")
|
||||
self.party_type = get_party_types_from_account_type(self.account_type)
|
||||
self.party_naming_by = frappe.db.get_value(
|
||||
args.get("naming_by")[0], None, args.get("naming_by")[1]
|
||||
self.party_naming_by = frappe.db.get_single_value(
|
||||
args.get("naming_by")[0], args.get("naming_by")[1]
|
||||
)
|
||||
self.get_columns()
|
||||
self.get_data(args)
|
||||
|
||||
@@ -124,11 +124,11 @@ def get_provisional_profit_loss(
|
||||
key = period if consolidated else period.key
|
||||
effective_liability = 0.0
|
||||
if liability:
|
||||
effective_liability += flt(liability[-2].get(key))
|
||||
effective_liability += flt(liability[0].get(key))
|
||||
if equity:
|
||||
effective_liability += flt(equity[-2].get(key))
|
||||
effective_liability += flt(equity[0].get(key))
|
||||
|
||||
provisional_profit_loss[key] = flt(asset[-2].get(key)) - effective_liability
|
||||
provisional_profit_loss[key] = flt(asset[0].get(key)) - effective_liability
|
||||
total_row[key] = effective_liability + provisional_profit_loss[key]
|
||||
|
||||
if provisional_profit_loss[key]:
|
||||
@@ -193,11 +193,11 @@ def get_report_summary(
|
||||
for period in period_list:
|
||||
key = period if consolidated else period.key
|
||||
if asset:
|
||||
net_asset += asset[-2].get(key)
|
||||
net_asset += asset[0].get(key)
|
||||
if liability:
|
||||
net_liability += liability[-2].get(key)
|
||||
net_liability += liability[0].get(key)
|
||||
if equity:
|
||||
net_equity += equity[-2].get(key)
|
||||
net_equity += equity[0].get(key)
|
||||
if provisional_profit_loss:
|
||||
net_provisional_profit_loss += provisional_profit_loss.get(key)
|
||||
|
||||
|
||||
@@ -3,49 +3,135 @@
|
||||
|
||||
import frappe
|
||||
from frappe.tests.utils import FrappeTestCase
|
||||
from frappe.utils import today
|
||||
from frappe.utils.data import today
|
||||
|
||||
from erpnext.accounts.report.balance_sheet.balance_sheet import execute
|
||||
|
||||
COMPANY = "_Test Company 6"
|
||||
COMPANY_SHORT_NAME = "_TC6"
|
||||
|
||||
test_dependencies = ["Company"]
|
||||
|
||||
|
||||
class TestBalanceSheet(FrappeTestCase):
|
||||
def test_balance_sheet(self):
|
||||
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import (
|
||||
create_sales_invoice,
|
||||
make_sales_invoice,
|
||||
)
|
||||
from erpnext.accounts.utils import get_fiscal_year
|
||||
frappe.db.sql(f"delete from `tabJournal Entry` where company='{COMPANY}'")
|
||||
frappe.db.sql(f"delete from `tabGL Entry` where company='{COMPANY}'")
|
||||
|
||||
frappe.db.sql("delete from `tabPurchase Invoice` where company='_Test Company 6'")
|
||||
frappe.db.sql("delete from `tabSales Invoice` where company='_Test Company 6'")
|
||||
frappe.db.sql("delete from `tabGL Entry` where company='_Test Company 6'")
|
||||
create_account("VAT Liabilities", f"Duties and Taxes - {COMPANY_SHORT_NAME}", COMPANY)
|
||||
create_account("Advance VAT Paid", f"Duties and Taxes - {COMPANY_SHORT_NAME}", COMPANY)
|
||||
create_account("My Bank", f"Bank Accounts - {COMPANY_SHORT_NAME}", COMPANY)
|
||||
|
||||
pi = make_purchase_invoice(
|
||||
company="_Test Company 6",
|
||||
warehouse="Finished Goods - _TC6",
|
||||
expense_account="Cost of Goods Sold - _TC6",
|
||||
cost_center="Main - _TC6",
|
||||
qty=10,
|
||||
rate=100,
|
||||
# 1000 equity paid to bank account
|
||||
make_journal_entry(
|
||||
[
|
||||
dict(
|
||||
account_name="My Bank",
|
||||
debit_in_account_currency=1000,
|
||||
credit_in_account_currency=0,
|
||||
),
|
||||
dict(
|
||||
account_name="Capital Stock",
|
||||
debit_in_account_currency=0,
|
||||
credit_in_account_currency=1000,
|
||||
),
|
||||
]
|
||||
)
|
||||
si = create_sales_invoice(
|
||||
company="_Test Company 6",
|
||||
debit_to="Debtors - _TC6",
|
||||
income_account="Sales - _TC6",
|
||||
cost_center="Main - _TC6",
|
||||
qty=5,
|
||||
rate=110,
|
||||
|
||||
# 110 income paid to bank account (100 revenue + 10 VAT)
|
||||
make_journal_entry(
|
||||
[
|
||||
dict(
|
||||
account_name="My Bank",
|
||||
debit_in_account_currency=110,
|
||||
credit_in_account_currency=0,
|
||||
),
|
||||
dict(
|
||||
account_name="Sales",
|
||||
debit_in_account_currency=0,
|
||||
credit_in_account_currency=100,
|
||||
),
|
||||
dict(
|
||||
account_name="VAT Liabilities",
|
||||
debit_in_account_currency=0,
|
||||
credit_in_account_currency=10,
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
# offset VAT Liabilities with intra-year advance payment
|
||||
make_journal_entry(
|
||||
[
|
||||
dict(
|
||||
account_name="My Bank",
|
||||
debit_in_account_currency=0,
|
||||
credit_in_account_currency=10,
|
||||
),
|
||||
dict(
|
||||
account_name="Advance VAT Paid",
|
||||
debit_in_account_currency=10,
|
||||
credit_in_account_currency=0,
|
||||
),
|
||||
]
|
||||
)
|
||||
|
||||
filters = frappe._dict(
|
||||
company="_Test Company 6",
|
||||
company=COMPANY,
|
||||
period_start_date=today(),
|
||||
period_end_date=today(),
|
||||
periodicity="Yearly",
|
||||
)
|
||||
result = execute(filters)[1]
|
||||
for account_dict in result:
|
||||
if account_dict.get("account") == "Current Liabilities - _TC6":
|
||||
self.assertEqual(account_dict.total, 1000)
|
||||
if account_dict.get("account") == "Current Assets - _TC6":
|
||||
self.assertEqual(account_dict.total, 550)
|
||||
results = execute(filters)
|
||||
name_and_total = {
|
||||
account_dict["account_name"]: account_dict["total"]
|
||||
for account_dict in results[1]
|
||||
if "total" in account_dict and "account_name" in account_dict
|
||||
}
|
||||
|
||||
self.assertNotIn("Sales", name_and_total)
|
||||
|
||||
self.assertIn("My Bank", name_and_total)
|
||||
self.assertEqual(name_and_total["My Bank"], 1100)
|
||||
|
||||
self.assertIn("VAT Liabilities", name_and_total)
|
||||
self.assertEqual(name_and_total["VAT Liabilities"], 10)
|
||||
|
||||
self.assertIn("Advance VAT Paid", name_and_total)
|
||||
self.assertEqual(name_and_total["Advance VAT Paid"], -10)
|
||||
|
||||
self.assertIn("Duties and Taxes", name_and_total)
|
||||
self.assertEqual(name_and_total["Duties and Taxes"], 0)
|
||||
|
||||
self.assertIn("Application of Funds (Assets)", name_and_total)
|
||||
self.assertEqual(name_and_total["Application of Funds (Assets)"], 1100)
|
||||
|
||||
self.assertIn("Equity", name_and_total)
|
||||
self.assertEqual(name_and_total["Equity"], 1000)
|
||||
|
||||
self.assertIn("'Provisional Profit / Loss (Credit)'", name_and_total)
|
||||
self.assertEqual(name_and_total["'Provisional Profit / Loss (Credit)'"], 100)
|
||||
|
||||
|
||||
def make_journal_entry(rows):
|
||||
jv = frappe.new_doc("Journal Entry")
|
||||
jv.posting_date = today()
|
||||
jv.company = COMPANY
|
||||
jv.user_remark = "test"
|
||||
|
||||
for row in rows:
|
||||
row["account"] = row.pop("account_name") + " - " + COMPANY_SHORT_NAME
|
||||
jv.append("accounts", row)
|
||||
|
||||
jv.insert()
|
||||
jv.submit()
|
||||
|
||||
|
||||
def create_account(account_name: str, parent_account: str, company: str):
|
||||
if frappe.db.exists("Account", {"account_name": account_name, "company": company}):
|
||||
return
|
||||
|
||||
acc = frappe.new_doc("Account")
|
||||
acc.account_name = account_name
|
||||
acc.company = COMPANY
|
||||
acc.parent_account = parent_account
|
||||
acc.insert()
|
||||
|
||||
@@ -84,10 +84,6 @@ function get_filters() {
|
||||
options: budget_against_options,
|
||||
default: "Cost Center",
|
||||
reqd: 1,
|
||||
get_data: function() {
|
||||
console.log(this.options);
|
||||
return ["Emacs", "Rocks"];
|
||||
},
|
||||
on_change: function() {
|
||||
frappe.query_report.set_filter_value("budget_against_filter", []);
|
||||
frappe.query_report.refresh();
|
||||
|
||||
@@ -128,7 +128,7 @@ frappe.query_reports["Consolidated Financial Statement"] = {
|
||||
}
|
||||
|
||||
value = default_formatter(value, row, column, data);
|
||||
if (!data.parent_account) {
|
||||
if (data && !data.parent_account) {
|
||||
value = $(`<span>${value}</span>`);
|
||||
|
||||
var $value = $(value).css("font-weight", "bold");
|
||||
|
||||
@@ -21,8 +21,8 @@ class PartyLedgerSummaryReport(object):
|
||||
frappe.throw(_("From Date must be before To Date"))
|
||||
|
||||
self.filters.party_type = args.get("party_type")
|
||||
self.party_naming_by = frappe.db.get_value(
|
||||
args.get("naming_by")[0], None, args.get("naming_by")[1]
|
||||
self.party_naming_by = frappe.db.get_single_value(
|
||||
args.get("naming_by")[0], args.get("naming_by")[1]
|
||||
)
|
||||
|
||||
self.get_gl_entries()
|
||||
@@ -376,6 +376,10 @@ class PartyLedgerSummaryReport(object):
|
||||
if not income_or_expense_accounts:
|
||||
# prevent empty 'in' condition
|
||||
income_or_expense_accounts.append("")
|
||||
else:
|
||||
# escape '%' in account name
|
||||
# ignoring frappe.db.escape as it replaces single quotes with double quotes
|
||||
income_or_expense_accounts = [x.replace("%", "%%") for x in income_or_expense_accounts]
|
||||
|
||||
accounts_query = (
|
||||
qb.from_(gl)
|
||||
|
||||
@@ -8,17 +8,7 @@ import re
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import (
|
||||
add_days,
|
||||
add_months,
|
||||
cint,
|
||||
cstr,
|
||||
flt,
|
||||
formatdate,
|
||||
get_first_day,
|
||||
getdate,
|
||||
today,
|
||||
)
|
||||
from frappe.utils import add_days, add_months, cint, cstr, flt, formatdate, get_first_day, getdate
|
||||
|
||||
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
|
||||
get_accounting_dimensions,
|
||||
@@ -53,8 +43,6 @@ def get_period_list(
|
||||
year_start_date = getdate(period_start_date)
|
||||
year_end_date = getdate(period_end_date)
|
||||
|
||||
year_end_date = getdate(today()) if year_end_date > getdate(today()) else year_end_date
|
||||
|
||||
months_to_add = {"Yearly": 12, "Half-Yearly": 6, "Quarterly": 3, "Monthly": 1}[periodicity]
|
||||
|
||||
period_list = []
|
||||
|
||||
@@ -203,7 +203,7 @@ def get_gl_entries(filters, accounting_dimensions):
|
||||
voucher_type, voucher_subtype, voucher_no, {dimension_fields}
|
||||
cost_center, project, {transaction_currency_fields}
|
||||
against_voucher_type, against_voucher, account_currency,
|
||||
against_link, against, is_opening, creation {select_fields}
|
||||
against, is_opening, creation {select_fields}
|
||||
from `tabGL Entry`
|
||||
where company=%(company)s {conditions}
|
||||
{order_by_statement}
|
||||
@@ -398,7 +398,6 @@ def initialize_gle_map(gl_entries, filters):
|
||||
group_by = group_by_field(filters.get("group_by"))
|
||||
|
||||
for gle in gl_entries:
|
||||
gle.against = gle.get("against_link") or gle.get("against")
|
||||
gle_map.setdefault(gle.get(group_by), _dict(totals=get_totals_dict(), entries=[]))
|
||||
return gle_map
|
||||
|
||||
@@ -449,6 +448,10 @@ def get_accountwise_gle(filters, accounting_dimensions, gl_entries, gle_map):
|
||||
for gle in gl_entries:
|
||||
group_by_value = gle.get(group_by)
|
||||
gle.voucher_type = _(gle.voucher_type)
|
||||
gle.voucher_subtype = _(gle.voucher_subtype)
|
||||
gle.against_voucher_type = _(gle.against_voucher_type)
|
||||
gle.remarks = _(gle.remarks)
|
||||
gle.party_type = _(gle.party_type)
|
||||
|
||||
if gle.posting_date < from_date or (cstr(gle.is_opening) == "Yes" and not show_opening_entries):
|
||||
if not group_by_voucher_consolidated:
|
||||
|
||||
@@ -134,7 +134,7 @@ def get_revenue(data, period_list, include_in_gross=1):
|
||||
|
||||
def remove_parent_with_no_child(data):
|
||||
data_to_be_removed = False
|
||||
for parent in data:
|
||||
for parent in list(data):
|
||||
if "is_group" in parent and parent.get("is_group") == 1:
|
||||
have_child = False
|
||||
for child in data:
|
||||
|
||||
@@ -59,7 +59,21 @@ frappe.query_reports["Item-wise Sales Register"] = {
|
||||
"fieldname": "group_by",
|
||||
"fieldtype": "Select",
|
||||
"options": ["Customer Group", "Customer", "Item Group", "Item", "Territory", "Invoice"]
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname": "income_account",
|
||||
"label": __("Income Account"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Account",
|
||||
get_query: () => {
|
||||
let company = frappe.query_report.get_filter_value('company');
|
||||
return {
|
||||
filters: {
|
||||
'company': company,
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
],
|
||||
"formatter": function(value, row, column, data, default_formatter) {
|
||||
value = default_formatter(value, row, column, data);
|
||||
|
||||
@@ -83,9 +83,7 @@ def _execute(filters=None, additional_table_columns=None, additional_conditions=
|
||||
"company": d.company,
|
||||
"sales_order": d.sales_order,
|
||||
"delivery_note": d.delivery_note,
|
||||
"income_account": d.unrealized_profit_loss_account
|
||||
if d.is_internal_customer == 1
|
||||
else d.income_account,
|
||||
"income_account": get_income_account(d),
|
||||
"cost_center": d.cost_center,
|
||||
"stock_qty": d.stock_qty,
|
||||
"stock_uom": d.stock_uom,
|
||||
@@ -150,6 +148,15 @@ def _execute(filters=None, additional_table_columns=None, additional_conditions=
|
||||
return columns, data, None, None, None, skip_total_row
|
||||
|
||||
|
||||
def get_income_account(row):
|
||||
if row.enable_deferred_revenue:
|
||||
return row.deferred_revenue_account
|
||||
elif row.is_internal_customer == 1:
|
||||
return row.unrealized_profit_loss_account
|
||||
else:
|
||||
return row.income_account
|
||||
|
||||
|
||||
def get_columns(additional_table_columns, filters):
|
||||
columns = []
|
||||
|
||||
@@ -358,6 +365,13 @@ def get_conditions(filters, additional_conditions=None):
|
||||
if filters.get("item_group"):
|
||||
conditions += """and ifnull(`tabSales Invoice Item`.item_group, '') = %(item_group)s"""
|
||||
|
||||
if filters.get("income_account"):
|
||||
conditions += """
|
||||
and (ifnull(`tabSales Invoice Item`.income_account, '') = %(income_account)s
|
||||
or ifnull(`tabSales Invoice Item`.deferred_revenue_account, '') = %(income_account)s
|
||||
or ifnull(`tabSales Invoice`.unrealized_profit_loss_account, '') = %(income_account)s)
|
||||
"""
|
||||
|
||||
if not filters.get("group_by"):
|
||||
conditions += (
|
||||
"ORDER BY `tabSales Invoice`.posting_date desc, `tabSales Invoice Item`.item_group desc"
|
||||
@@ -399,6 +413,7 @@ def get_items(filters, additional_query_columns, additional_conditions=None):
|
||||
`tabItem`.`item_name` as i_item_name, `tabItem`.`item_group` as i_item_group,
|
||||
`tabSales Invoice Item`.sales_order, `tabSales Invoice Item`.delivery_note,
|
||||
`tabSales Invoice Item`.income_account, `tabSales Invoice Item`.cost_center,
|
||||
`tabSales Invoice Item`.enable_deferred_revenue, `tabSales Invoice Item`.deferred_revenue_account,
|
||||
`tabSales Invoice Item`.stock_qty, `tabSales Invoice Item`.stock_uom,
|
||||
`tabSales Invoice Item`.base_net_rate, `tabSales Invoice Item`.base_net_amount,
|
||||
`tabSales Invoice`.customer_name, `tabSales Invoice`.customer_group, `tabSales Invoice Item`.so_detail,
|
||||
|
||||
@@ -46,12 +46,10 @@ def get_result(
|
||||
|
||||
out = []
|
||||
for name, details in gle_map.items():
|
||||
tax_amount, total_amount, grand_total, base_total = 0, 0, 0, 0
|
||||
bill_no, bill_date = "", ""
|
||||
tax_withholding_category = tax_category_map.get(name)
|
||||
rate = tax_rate_map.get(tax_withholding_category)
|
||||
|
||||
for entry in details:
|
||||
tax_amount, total_amount, grand_total, base_total = 0, 0, 0, 0
|
||||
tax_withholding_category, rate = None, None
|
||||
bill_no, bill_date = "", ""
|
||||
party = entry.party or entry.against
|
||||
posting_date = entry.posting_date
|
||||
voucher_type = entry.voucher_type
|
||||
@@ -61,12 +59,19 @@ def get_result(
|
||||
if party_list:
|
||||
party = party_list[0]
|
||||
|
||||
if not tax_withholding_category:
|
||||
tax_withholding_category = party_map.get(party, {}).get("tax_withholding_category")
|
||||
rate = tax_rate_map.get(tax_withholding_category)
|
||||
|
||||
if entry.account in tds_accounts:
|
||||
if entry.account in tds_accounts.keys():
|
||||
tax_amount += entry.credit - entry.debit
|
||||
# infer tax withholding category from the account if it's the single account for this category
|
||||
tax_withholding_category = tds_accounts.get(entry.account)
|
||||
rate = tax_rate_map.get(tax_withholding_category)
|
||||
# or else the consolidated value from the voucher document
|
||||
if not tax_withholding_category:
|
||||
# or else from the party default
|
||||
tax_withholding_category = tax_category_map.get(name)
|
||||
rate = tax_rate_map.get(tax_withholding_category)
|
||||
if not tax_withholding_category:
|
||||
tax_withholding_category = party_map.get(party, {}).get("tax_withholding_category")
|
||||
rate = tax_rate_map.get(tax_withholding_category)
|
||||
|
||||
if net_total_map.get(name):
|
||||
if voucher_type == "Journal Entry" and tax_amount and rate:
|
||||
@@ -80,41 +85,41 @@ def get_result(
|
||||
else:
|
||||
total_amount += entry.credit
|
||||
|
||||
if tax_amount:
|
||||
if party_map.get(party, {}).get("party_type") == "Supplier":
|
||||
party_name = "supplier_name"
|
||||
party_type = "supplier_type"
|
||||
else:
|
||||
party_name = "customer_name"
|
||||
party_type = "customer_type"
|
||||
if tax_amount:
|
||||
if party_map.get(party, {}).get("party_type") == "Supplier":
|
||||
party_name = "supplier_name"
|
||||
party_type = "supplier_type"
|
||||
else:
|
||||
party_name = "customer_name"
|
||||
party_type = "customer_type"
|
||||
|
||||
row = {
|
||||
"pan"
|
||||
if frappe.db.has_column(filters.party_type, "pan")
|
||||
else "tax_id": party_map.get(party, {}).get("pan"),
|
||||
"party": party_map.get(party, {}).get("name"),
|
||||
}
|
||||
|
||||
if filters.naming_series == "Naming Series":
|
||||
row.update({"party_name": party_map.get(party, {}).get(party_name)})
|
||||
|
||||
row.update(
|
||||
{
|
||||
"section_code": tax_withholding_category or "",
|
||||
"entity_type": party_map.get(party, {}).get(party_type),
|
||||
"rate": rate,
|
||||
"total_amount": total_amount,
|
||||
"grand_total": grand_total,
|
||||
"base_total": base_total,
|
||||
"tax_amount": tax_amount,
|
||||
"transaction_date": posting_date,
|
||||
"transaction_type": voucher_type,
|
||||
"ref_no": name,
|
||||
"supplier_invoice_no": bill_no,
|
||||
"supplier_invoice_date": bill_date,
|
||||
row = {
|
||||
"pan"
|
||||
if frappe.db.has_column(filters.party_type, "pan")
|
||||
else "tax_id": party_map.get(party, {}).get("pan"),
|
||||
"party": party_map.get(party, {}).get("name"),
|
||||
}
|
||||
)
|
||||
out.append(row)
|
||||
|
||||
if filters.naming_series == "Naming Series":
|
||||
row.update({"party_name": party_map.get(party, {}).get(party_name)})
|
||||
|
||||
row.update(
|
||||
{
|
||||
"section_code": tax_withholding_category or "",
|
||||
"entity_type": party_map.get(party, {}).get(party_type),
|
||||
"rate": rate,
|
||||
"total_amount": total_amount,
|
||||
"grand_total": grand_total,
|
||||
"base_total": base_total,
|
||||
"tax_amount": tax_amount,
|
||||
"transaction_date": posting_date,
|
||||
"transaction_type": voucher_type,
|
||||
"ref_no": name,
|
||||
"supplier_invoice_no": bill_no,
|
||||
"supplier_invoice_date": bill_date,
|
||||
}
|
||||
)
|
||||
out.append(row)
|
||||
|
||||
out.sort(key=lambda x: x["section_code"])
|
||||
|
||||
@@ -282,11 +287,20 @@ def get_tds_docs(filters):
|
||||
journal_entry_party_map = frappe._dict()
|
||||
bank_accounts = frappe.get_all("Account", {"is_group": 0, "account_type": "Bank"}, pluck="name")
|
||||
|
||||
tds_accounts = frappe.get_all(
|
||||
"Tax Withholding Account", {"company": filters.get("company")}, pluck="account"
|
||||
_tds_accounts = frappe.get_all(
|
||||
"Tax Withholding Account",
|
||||
{"company": filters.get("company")},
|
||||
["account", "parent"],
|
||||
)
|
||||
tds_accounts = {}
|
||||
for tds_acc in _tds_accounts:
|
||||
# if it turns out not to be the only tax withholding category, then don't include in the map
|
||||
if tds_accounts.get(tds_acc["account"]):
|
||||
tds_accounts[tds_acc["account"]] = None
|
||||
else:
|
||||
tds_accounts[tds_acc["account"]] = tds_acc["parent"]
|
||||
|
||||
tds_docs = get_tds_docs_query(filters, bank_accounts, tds_accounts).run(as_dict=True)
|
||||
tds_docs = get_tds_docs_query(filters, bank_accounts, list(tds_accounts.keys())).run(as_dict=True)
|
||||
|
||||
for d in tds_docs:
|
||||
if d.voucher_type == "Purchase Invoice":
|
||||
|
||||
@@ -368,7 +368,7 @@ def filter_invoices_based_on_dimensions(filters, query, parent_doc):
|
||||
dimension.document_type, filters.get(dimension.fieldname)
|
||||
)
|
||||
fieldname = dimension.fieldname
|
||||
query = query.where(parent_doc[fieldname] == filters.fieldname)
|
||||
query = query.where(parent_doc[fieldname].isin(filters[fieldname]))
|
||||
return query
|
||||
|
||||
|
||||
|
||||
@@ -23,6 +23,10 @@ class TestUtils(unittest.TestCase):
|
||||
super(TestUtils, cls).setUpClass()
|
||||
make_test_objects("Address", ADDRESS_RECORDS)
|
||||
|
||||
@classmethod
|
||||
def tearDownClass(cls):
|
||||
frappe.db.rollback()
|
||||
|
||||
def test_get_party_shipping_address(self):
|
||||
address = get_party_shipping_address("Customer", "_Test Customer 1")
|
||||
self.assertEqual(address, "_Test Billing Address 2 Title-Billing")
|
||||
@@ -126,6 +130,38 @@ class TestUtils(unittest.TestCase):
|
||||
self.assertEqual(len(payment_entry.references), 1)
|
||||
self.assertEqual(payment_entry.difference_amount, 0)
|
||||
|
||||
def test_naming_series_variable_parsing(self):
|
||||
"""
|
||||
Tests parsing utility used by Naming Series Variable hook for FY
|
||||
"""
|
||||
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
|
||||
from frappe.utils import nowdate
|
||||
|
||||
from erpnext.accounts.utils import get_fiscal_year
|
||||
from erpnext.buying.doctype.supplier.test_supplier import create_supplier
|
||||
|
||||
# Configure Supplier Naming in Buying Settings
|
||||
frappe.db.set_default("supp_master_name", "Auto Name")
|
||||
|
||||
# Configure Autoname in Supplier DocType
|
||||
make_property_setter(
|
||||
"Supplier", None, "naming_rule", "Expression", "Data", for_doctype="Doctype"
|
||||
)
|
||||
make_property_setter(
|
||||
"Supplier", None, "autoname", "SUP-.FY.-.#####", "Data", for_doctype="Doctype"
|
||||
)
|
||||
|
||||
fiscal_year = get_fiscal_year(nowdate())[0]
|
||||
|
||||
# Create Supplier
|
||||
supplier = create_supplier()
|
||||
|
||||
# Check Naming Series in generated Supplier ID
|
||||
doc_name = supplier.name.split("-")
|
||||
self.assertEqual(len(doc_name), 3)
|
||||
self.assertSequenceEqual(doc_name[0:2], ("SUP", fiscal_year))
|
||||
frappe.db.set_default("supp_master_name", "Supplier Name")
|
||||
|
||||
|
||||
ADDRESS_RECORDS = [
|
||||
{
|
||||
|
||||
@@ -642,7 +642,6 @@ def update_reference_in_journal_entry(d, journal_entry, do_not_save=False):
|
||||
new_row.set("reference_name", d["against_voucher"])
|
||||
|
||||
new_row.against_account = cstr(jv_detail.against_account)
|
||||
new_row.against_account_link = cstr(jv_detail.against_account)
|
||||
new_row.is_advance = cstr(jv_detail.is_advance)
|
||||
new_row.docstatus = 1
|
||||
|
||||
@@ -1275,7 +1274,7 @@ def get_autoname_with_number(number_value, doc_title, company):
|
||||
def parse_naming_series_variable(doc, variable):
|
||||
if variable == "FY":
|
||||
if doc:
|
||||
date = doc.get("posting_date") or doc.get("transaction_date")
|
||||
date = doc.get("posting_date") or doc.get("transaction_date") or getdate()
|
||||
company = doc.get("company")
|
||||
else:
|
||||
date = getdate()
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
"label": "Profit and Loss"
|
||||
}
|
||||
],
|
||||
"content": "[{\"id\":\"MmUf9abwxg\",\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Accounts\",\"col\":12}},{\"id\":\"VVvJ1lUcfc\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Total Outgoing Bills\",\"col\":3}},{\"id\":\"Vlj2FZtlHV\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Total Incoming Bills\",\"col\":3}},{\"id\":\"VVVjQVAhPf\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Total Incoming Payment\",\"col\":3}},{\"id\":\"DySNdlysIW\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Total Outgoing Payment\",\"col\":3}},{\"id\":\"i0EtSjDAXq\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Profit and Loss\",\"col\":12}},{\"id\":\"X78jcbq1u3\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"vikWSkNm6_\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"id\":\"pMywM0nhlj\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Chart of Accounts\",\"col\":3}},{\"id\":\"_pRdD6kqUG\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Invoice\",\"col\":3}},{\"id\":\"G984SgVRJN\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Purchase Invoice\",\"col\":3}},{\"id\":\"1ArNvt9qhz\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Journal Entry\",\"col\":3}},{\"id\":\"F9f4I1viNr\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Payment Entry\",\"col\":3}},{\"id\":\"4IBBOIxfqW\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Accounts Receivable\",\"col\":3}},{\"id\":\"El2anpPaFY\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"General Ledger\",\"col\":3}},{\"id\":\"1nwcM9upJo\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Trial Balance\",\"col\":3}},{\"id\":\"OF9WOi1Ppc\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"id\":\"iAwpe-Chra\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Learn Accounting\",\"col\":3}},{\"id\":\"B7-uxs8tkU\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"tHb3yxthkR\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"id\":\"DnNtsmxpty\",\"type\":\"card\",\"data\":{\"card_name\":\"Accounting Masters\",\"col\":4}},{\"id\":\"nKKr6fjgjb\",\"type\":\"card\",\"data\":{\"card_name\":\"General Ledger\",\"col\":4}},{\"id\":\"xOHTyD8b5l\",\"type\":\"card\",\"data\":{\"card_name\":\"Accounts Receivable\",\"col\":4}},{\"id\":\"_Cb7C8XdJJ\",\"type\":\"card\",\"data\":{\"card_name\":\"Accounts Payable\",\"col\":4}},{\"id\":\"p7NY6MHe2Y\",\"type\":\"card\",\"data\":{\"card_name\":\"Financial Statements\",\"col\":4}},{\"id\":\"KlqilF5R_V\",\"type\":\"card\",\"data\":{\"card_name\":\"Taxes\",\"col\":4}},{\"id\":\"jTUy8LB0uw\",\"type\":\"card\",\"data\":{\"card_name\":\"Cost Center and Budgeting\",\"col\":4}},{\"id\":\"Wn2lhs7WLn\",\"type\":\"card\",\"data\":{\"card_name\":\"Multi Currency\",\"col\":4}},{\"id\":\"PAQMqqNkBM\",\"type\":\"card\",\"data\":{\"card_name\":\"Banking\",\"col\":4}},{\"id\":\"Q_hBCnSeJY\",\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"id\":\"3AK1Zf0oew\",\"type\":\"card\",\"data\":{\"card_name\":\"Profitability\",\"col\":4}},{\"id\":\"kxhoaiqdLq\",\"type\":\"card\",\"data\":{\"card_name\":\"Opening and Closing\",\"col\":4}},{\"id\":\"q0MAlU2j_Z\",\"type\":\"card\",\"data\":{\"card_name\":\"Subscription Management\",\"col\":4}},{\"id\":\"ptm7T6Hwu-\",\"type\":\"card\",\"data\":{\"card_name\":\"Share Management\",\"col\":4}},{\"id\":\"OX7lZHbiTr\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]",
|
||||
"content": "[{\"id\":\"MmUf9abwxg\",\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Accounts\",\"col\":12}},{\"id\":\"nDhfcJYbKH\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Profit and Loss\",\"col\":12}},{\"id\":\"VVvJ1lUcfc\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Total Outgoing Bills\",\"col\":3}},{\"id\":\"Vlj2FZtlHV\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Total Incoming Bills\",\"col\":3}},{\"id\":\"VVVjQVAhPf\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Total Incoming Payment\",\"col\":3}},{\"id\":\"DySNdlysIW\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Total Outgoing Payment\",\"col\":3}},{\"id\":\"9k1rDm2C0l\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"vikWSkNm6_\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Shortcuts</b></span>\",\"col\":12}},{\"id\":\"pMywM0nhlj\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Chart of Accounts\",\"col\":3}},{\"id\":\"_pRdD6kqUG\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Invoice\",\"col\":3}},{\"id\":\"G984SgVRJN\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Purchase Invoice\",\"col\":3}},{\"id\":\"1ArNvt9qhz\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Journal Entry\",\"col\":3}},{\"id\":\"F9f4I1viNr\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Payment Entry\",\"col\":3}},{\"id\":\"4IBBOIxfqW\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Accounts Receivable\",\"col\":3}},{\"id\":\"El2anpPaFY\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"General Ledger\",\"col\":3}},{\"id\":\"1nwcM9upJo\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Trial Balance\",\"col\":3}},{\"id\":\"OF9WOi1Ppc\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"id\":\"iAwpe-Chra\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Learn Accounting\",\"col\":3}},{\"id\":\"B7-uxs8tkU\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"tHb3yxthkR\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"id\":\"DnNtsmxpty\",\"type\":\"card\",\"data\":{\"card_name\":\"Accounting Masters\",\"col\":4}},{\"id\":\"nKKr6fjgjb\",\"type\":\"card\",\"data\":{\"card_name\":\"Payments\",\"col\":4}},{\"id\":\"KlqilF5R_V\",\"type\":\"card\",\"data\":{\"card_name\":\"Tax Masters\",\"col\":4}},{\"id\":\"jTUy8LB0uw\",\"type\":\"card\",\"data\":{\"card_name\":\"Cost Center and Budgeting\",\"col\":4}},{\"id\":\"Wn2lhs7WLn\",\"type\":\"card\",\"data\":{\"card_name\":\"Multi Currency\",\"col\":4}},{\"id\":\"PAQMqqNkBM\",\"type\":\"card\",\"data\":{\"card_name\":\"Banking\",\"col\":4}},{\"id\":\"kxhoaiqdLq\",\"type\":\"card\",\"data\":{\"card_name\":\"Opening and Closing\",\"col\":4}},{\"id\":\"q0MAlU2j_Z\",\"type\":\"card\",\"data\":{\"card_name\":\"Subscription Management\",\"col\":4}},{\"id\":\"ptm7T6Hwu-\",\"type\":\"card\",\"data\":{\"card_name\":\"Share Management\",\"col\":4}}]",
|
||||
"creation": "2020-03-02 15:41:59.515192",
|
||||
"custom_blocks": [],
|
||||
"docstatus": 0,
|
||||
@@ -14,562 +14,10 @@
|
||||
"hide_custom": 0,
|
||||
"icon": "accounting",
|
||||
"idx": 0,
|
||||
"indicator_color": "",
|
||||
"is_hidden": 0,
|
||||
"label": "Accounting",
|
||||
"links": [
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Accounting Masters",
|
||||
"link_count": 0,
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Company",
|
||||
"link_count": 0,
|
||||
"link_to": "Company",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Chart of Accounts",
|
||||
"link_count": 0,
|
||||
"link_to": "Account",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Accounts Settings",
|
||||
"link_count": 0,
|
||||
"link_to": "Accounts Settings",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Fiscal Year",
|
||||
"link_count": 0,
|
||||
"link_to": "Fiscal Year",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Accounting Dimension",
|
||||
"link_count": 0,
|
||||
"link_to": "Accounting Dimension",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Finance Book",
|
||||
"link_count": 0,
|
||||
"link_to": "Finance Book",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Accounting Period",
|
||||
"link_count": 0,
|
||||
"link_to": "Accounting Period",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Payment Term",
|
||||
"link_count": 0,
|
||||
"link_to": "Payment Term",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "General Ledger",
|
||||
"link_count": 0,
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Journal Entry",
|
||||
"link_count": 0,
|
||||
"link_to": "Journal Entry",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Journal Entry Template",
|
||||
"link_count": 0,
|
||||
"link_to": "Journal Entry Template",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "GL Entry",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "General Ledger",
|
||||
"link_count": 0,
|
||||
"link_to": "General Ledger",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Sales Invoice",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Customer Ledger Summary",
|
||||
"link_count": 0,
|
||||
"link_to": "Customer Ledger Summary",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Sales Invoice",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Supplier Ledger Summary",
|
||||
"link_count": 0,
|
||||
"link_to": "Supplier Ledger Summary",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Accounts Receivable",
|
||||
"link_count": 0,
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Sales Invoice",
|
||||
"link_count": 0,
|
||||
"link_to": "Sales Invoice",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Customer",
|
||||
"link_count": 0,
|
||||
"link_to": "Customer",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Payment Entry",
|
||||
"link_count": 0,
|
||||
"link_to": "Payment Entry",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Payment Request",
|
||||
"link_count": 0,
|
||||
"link_to": "Payment Request",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Payment Reconciliation",
|
||||
"link_count": 0,
|
||||
"link_to": "Payment Reconciliation",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Sales Invoice",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Accounts Receivable",
|
||||
"link_count": 0,
|
||||
"link_to": "Accounts Receivable",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Sales Invoice",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Accounts Receivable Summary",
|
||||
"link_count": 0,
|
||||
"link_to": "Accounts Receivable Summary",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Sales Invoice",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Sales Register",
|
||||
"link_count": 0,
|
||||
"link_to": "Sales Register",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Sales Invoice",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Item-wise Sales Register",
|
||||
"link_count": 0,
|
||||
"link_to": "Item-wise Sales Register",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Sales Invoice",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Sales Order Analysis",
|
||||
"link_count": 0,
|
||||
"link_to": "Sales Order Analysis",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Sales Invoice",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Delivered Items To Be Billed",
|
||||
"link_count": 0,
|
||||
"link_to": "Delivered Items To Be Billed",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Accounts Payable",
|
||||
"link_count": 0,
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Purchase Invoice",
|
||||
"link_count": 0,
|
||||
"link_to": "Purchase Invoice",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Supplier",
|
||||
"link_count": 0,
|
||||
"link_to": "Supplier",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Payment Entry",
|
||||
"link_count": 0,
|
||||
"link_to": "Payment Entry",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Payment Reconciliation",
|
||||
"link_count": 0,
|
||||
"link_to": "Payment Reconciliation",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Purchase Invoice",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Accounts Payable",
|
||||
"link_count": 0,
|
||||
"link_to": "Accounts Payable",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Purchase Invoice",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Accounts Payable Summary",
|
||||
"link_count": 0,
|
||||
"link_to": "Accounts Payable Summary",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Purchase Invoice",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Purchase Register",
|
||||
"link_count": 0,
|
||||
"link_to": "Purchase Register",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Purchase Invoice",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Item-wise Purchase Register",
|
||||
"link_count": 0,
|
||||
"link_to": "Item-wise Purchase Register",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Purchase Order",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Purchase Order Analysis",
|
||||
"link_count": 0,
|
||||
"link_to": "Purchase Order Analysis",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Purchase Invoice",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Received Items To Be Billed",
|
||||
"link_count": 0,
|
||||
"link_to": "Received Items To Be Billed",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Reports",
|
||||
"link_count": 0,
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "GL Entry",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Trial Balance for Party",
|
||||
"link_count": 0,
|
||||
"link_to": "Trial Balance for Party",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Journal Entry",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Payment Period Based On Invoice Date",
|
||||
"link_count": 0,
|
||||
"link_to": "Payment Period Based On Invoice Date",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Sales Invoice",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Sales Partners Commission",
|
||||
"link_count": 0,
|
||||
"link_to": "Sales Partners Commission",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Customer",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Customer Credit Balance",
|
||||
"link_count": 0,
|
||||
"link_to": "Customer Credit Balance",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Sales Invoice",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Sales Payment Summary",
|
||||
"link_count": 0,
|
||||
"link_to": "Sales Payment Summary",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Address",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Address And Contacts",
|
||||
"link_count": 0,
|
||||
"link_to": "Address And Contacts",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "GL Entry",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "UAE VAT 201",
|
||||
"link_count": 0,
|
||||
"link_to": "UAE VAT 201",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"only_for": "United Arab Emirates",
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Financial Statements",
|
||||
"link_count": 0,
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "GL Entry",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Trial Balance",
|
||||
"link_count": 0,
|
||||
"link_to": "Trial Balance",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "GL Entry",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Profit and Loss Statement",
|
||||
"link_count": 0,
|
||||
"link_to": "Profit and Loss Statement",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "GL Entry",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Balance Sheet",
|
||||
"link_count": 0,
|
||||
"link_to": "Balance Sheet",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "GL Entry",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Cash Flow",
|
||||
"link_count": 0,
|
||||
"link_to": "Cash Flow",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "GL Entry",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Consolidated Financial Statement",
|
||||
"link_count": 0,
|
||||
"link_to": "Consolidated Financial Statement",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
@@ -611,102 +59,6 @@
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Settings",
|
||||
"link_count": 0,
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Payment Gateway Account",
|
||||
"link_count": 0,
|
||||
"link_to": "Payment Gateway Account",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Terms and Conditions Template",
|
||||
"link_count": 0,
|
||||
"link_to": "Terms and Conditions",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Mode of Payment",
|
||||
"link_count": 0,
|
||||
"link_to": "Mode of Payment",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Bank",
|
||||
"link_count": 0,
|
||||
"link_to": "Bank",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Bank Account",
|
||||
"link_count": 0,
|
||||
"link_to": "Bank Account",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Bank Clearance",
|
||||
"link_count": 0,
|
||||
"link_to": "Bank Clearance",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Bank Reconciliation Tool",
|
||||
"link_count": 0,
|
||||
"link_to": "Bank Reconciliation Tool",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "GL Entry",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Bank Reconciliation Statement",
|
||||
"link_count": 0,
|
||||
"link_to": "Bank Reconciliation Statement",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
@@ -918,8 +270,83 @@
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Taxes",
|
||||
"label": "Banking",
|
||||
"link_count": 6,
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Bank",
|
||||
"link_count": 0,
|
||||
"link_to": "Bank",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Bank Account",
|
||||
"link_count": 0,
|
||||
"link_to": "Bank Account",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Bank Clearance",
|
||||
"link_count": 0,
|
||||
"link_to": "Bank Clearance",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Bank Reconciliation Tool",
|
||||
"link_count": 0,
|
||||
"link_to": "Bank Reconciliation Tool",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "GL Entry",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Bank Reconciliation Statement",
|
||||
"link_count": 0,
|
||||
"link_to": "Bank Reconciliation Statement",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Plaid Settings",
|
||||
"link_count": 0,
|
||||
"link_to": "Plaid Settings",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Tax Masters",
|
||||
"link_count": 7,
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
@@ -1003,60 +430,8 @@
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Profitability",
|
||||
"link_count": 0,
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "Sales Invoice",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Gross Profit",
|
||||
"link_count": 0,
|
||||
"link_to": "Gross Profit",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "GL Entry",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Profitability Analysis",
|
||||
"link_count": 0,
|
||||
"link_to": "Profitability Analysis",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Sales Invoice",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Sales Invoice Trends",
|
||||
"link_count": 0,
|
||||
"link_to": "Sales Invoice Trends",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Purchase Invoice",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Purchase Invoice Trends",
|
||||
"link_count": 0,
|
||||
"link_to": "Purchase Invoice Trends",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Banking",
|
||||
"link_count": 6,
|
||||
"label": "Accounting Masters",
|
||||
"link_count": 8,
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
@@ -1065,9 +440,31 @@
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Bank",
|
||||
"label": "Company",
|
||||
"link_count": 0,
|
||||
"link_to": "Bank",
|
||||
"link_to": "Company",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Chart of Accounts",
|
||||
"link_count": 0,
|
||||
"link_to": "Account",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Accounts Settings",
|
||||
"link_count": 0,
|
||||
"link_to": "Accounts Settings",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
@@ -1076,9 +473,9 @@
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Bank Account",
|
||||
"label": "Fiscal Year",
|
||||
"link_count": 0,
|
||||
"link_to": "Bank Account",
|
||||
"link_to": "Fiscal Year",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
@@ -1087,9 +484,9 @@
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Bank Clearance",
|
||||
"label": "Accounting Dimension",
|
||||
"link_count": 0,
|
||||
"link_to": "Bank Clearance",
|
||||
"link_to": "Accounting Dimension",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
@@ -1098,36 +495,98 @@
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Bank Reconciliation Tool",
|
||||
"label": "Finance Book",
|
||||
"link_count": 0,
|
||||
"link_to": "Bank Reconciliation Tool",
|
||||
"link_to": "Finance Book",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "GL Entry",
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Bank Reconciliation Statement",
|
||||
"is_query_report": 0,
|
||||
"label": "Accounting Period",
|
||||
"link_count": 0,
|
||||
"link_to": "Bank Reconciliation Statement",
|
||||
"link_type": "Report",
|
||||
"link_to": "Accounting Period",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Payment Term",
|
||||
"link_count": 0,
|
||||
"link_to": "Payment Term",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Plaid Settings",
|
||||
"label": "Payments",
|
||||
"link_count": 5,
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Payment Entry",
|
||||
"link_count": 0,
|
||||
"link_to": "Plaid Settings",
|
||||
"link_to": "Payment Entry",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Journal Entry",
|
||||
"link_count": 0,
|
||||
"link_to": "Journal Entry",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Journal Entry Template",
|
||||
"link_count": 0,
|
||||
"link_to": "Journal Entry Template",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Terms and Conditions",
|
||||
"link_count": 0,
|
||||
"link_to": "Terms and Conditions",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Mode of Payment",
|
||||
"link_count": 0,
|
||||
"link_to": "Mode of Payment",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2024-01-02 15:21:09.895531",
|
||||
"modified": "2024-01-18 22:15:40.941711",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Accounting",
|
||||
|
||||
@@ -0,0 +1,277 @@
|
||||
{
|
||||
"charts": [],
|
||||
"content": "[{\"id\":\"nKKr6fjgjb\",\"type\":\"card\",\"data\":{\"card_name\":\"Ledgers\",\"col\":4}},{\"id\":\"p7NY6MHe2Y\",\"type\":\"card\",\"data\":{\"card_name\":\"Financial Statements\",\"col\":4}},{\"id\":\"3AK1Zf0oew\",\"type\":\"card\",\"data\":{\"card_name\":\"Profitability\",\"col\":4}},{\"id\":\"Q_hBCnSeJY\",\"type\":\"card\",\"data\":{\"card_name\":\"Other Reports\",\"col\":4}}]",
|
||||
"creation": "2024-01-05 16:09:16.766939",
|
||||
"custom_blocks": [],
|
||||
"docstatus": 0,
|
||||
"doctype": "Workspace",
|
||||
"for_user": "",
|
||||
"hide_custom": 0,
|
||||
"icon": "file",
|
||||
"idx": 0,
|
||||
"indicator_color": "",
|
||||
"is_hidden": 0,
|
||||
"label": "Financial Reports",
|
||||
"links": [
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Profitability",
|
||||
"link_count": 0,
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "Sales Invoice",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Gross Profit",
|
||||
"link_count": 0,
|
||||
"link_to": "Gross Profit",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "GL Entry",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Profitability Analysis",
|
||||
"link_count": 0,
|
||||
"link_to": "Profitability Analysis",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Sales Invoice",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Sales Invoice Trends",
|
||||
"link_count": 0,
|
||||
"link_to": "Sales Invoice Trends",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Purchase Invoice",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Purchase Invoice Trends",
|
||||
"link_count": 0,
|
||||
"link_to": "Purchase Invoice Trends",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Financial Statements",
|
||||
"link_count": 5,
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "GL Entry",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Trial Balance",
|
||||
"link_count": 0,
|
||||
"link_to": "Trial Balance",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "GL Entry",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Profit and Loss Statement",
|
||||
"link_count": 0,
|
||||
"link_to": "Profit and Loss Statement",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "GL Entry",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Balance Sheet",
|
||||
"link_count": 0,
|
||||
"link_to": "Balance Sheet",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "GL Entry",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Cash Flow",
|
||||
"link_count": 0,
|
||||
"link_to": "Cash Flow",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "GL Entry",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Consolidated Financial Statement",
|
||||
"link_count": 0,
|
||||
"link_to": "Consolidated Financial Statement",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Ledgers",
|
||||
"link_count": 3,
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "GL Entry",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "General Ledger",
|
||||
"link_count": 0,
|
||||
"link_to": "General Ledger",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Sales Invoice",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Customer Ledger Summary",
|
||||
"link_count": 0,
|
||||
"link_to": "Customer Ledger Summary",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Sales Invoice",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Supplier Ledger Summary",
|
||||
"link_count": 0,
|
||||
"link_to": "Supplier Ledger Summary",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Other Reports",
|
||||
"link_count": 7,
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "GL Entry",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Trial Balance for Party",
|
||||
"link_count": 0,
|
||||
"link_to": "Trial Balance for Party",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Journal Entry",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Payment Period Based On Invoice Date",
|
||||
"link_count": 0,
|
||||
"link_to": "Payment Period Based On Invoice Date",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Sales Invoice",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Sales Partners Commission",
|
||||
"link_count": 0,
|
||||
"link_to": "Sales Partners Commission",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Customer",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Customer Credit Balance",
|
||||
"link_count": 0,
|
||||
"link_to": "Customer Credit Balance",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Sales Invoice",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Sales Payment Summary",
|
||||
"link_count": 0,
|
||||
"link_to": "Sales Payment Summary",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Address",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Address And Contacts",
|
||||
"link_count": 0,
|
||||
"link_to": "Address And Contacts",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "GL Entry",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "UAE VAT 201",
|
||||
"link_count": 0,
|
||||
"link_to": "UAE VAT 201",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"only_for": "United Arab Emirates",
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2024-01-18 22:13:07.596844",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Financial Reports",
|
||||
"number_cards": [],
|
||||
"owner": "Administrator",
|
||||
"parent_page": "Accounting",
|
||||
"public": 1,
|
||||
"quick_lists": [],
|
||||
"restrict_to_domain": "",
|
||||
"roles": [],
|
||||
"sequence_id": 5.0,
|
||||
"shortcuts": [],
|
||||
"title": "Financial Reports"
|
||||
}
|
||||
204
erpnext/accounts/workspace/payables/payables.json
Normal file
204
erpnext/accounts/workspace/payables/payables.json
Normal file
@@ -0,0 +1,204 @@
|
||||
{
|
||||
"charts": [],
|
||||
"content": "[{\"id\":\"rMMsfn2eB4\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Shortcuts</b></span>\",\"col\":12}},{\"id\":\"G984SgVRJN\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Purchase Invoice\",\"col\":3}},{\"id\":\"F9f4I1viNr\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Payment Entry\",\"col\":3}},{\"id\":\"1ArNvt9qhz\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Journal Entry\",\"col\":3}},{\"id\":\"4IBBOIxfqW\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Accounts Payable\",\"col\":3}},{\"id\":\"B7-uxs8tkU\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"jAcOH-cC-Q\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"id\":\"7dj93PEUjW\",\"type\":\"card\",\"data\":{\"card_name\":\"Invoicing\",\"col\":4}},{\"id\":\"_Cb7C8XdJJ\",\"type\":\"card\",\"data\":{\"card_name\":\"Payments\",\"col\":4}},{\"id\":\"9yseIkdG50\",\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}}]",
|
||||
"creation": "2024-01-05 15:29:11.144373",
|
||||
"custom_blocks": [],
|
||||
"docstatus": 0,
|
||||
"doctype": "Workspace",
|
||||
"for_user": "",
|
||||
"hide_custom": 0,
|
||||
"icon": "arrow-left",
|
||||
"idx": 0,
|
||||
"indicator_color": "",
|
||||
"is_hidden": 0,
|
||||
"label": "Payables",
|
||||
"links": [
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Invoicing",
|
||||
"link_count": 2,
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Purchase Invoice",
|
||||
"link_count": 0,
|
||||
"link_to": "Purchase Invoice",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Supplier",
|
||||
"link_count": 0,
|
||||
"link_to": "Supplier",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Payments",
|
||||
"link_count": 3,
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Payment Entry",
|
||||
"link_count": 0,
|
||||
"link_to": "Payment Entry",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Journal Entry",
|
||||
"link_count": 0,
|
||||
"link_to": "Journal Entry",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Payment Reconciliation",
|
||||
"link_count": 0,
|
||||
"link_to": "Payment Reconciliation",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Reports",
|
||||
"link_count": 7,
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Accounts Payable",
|
||||
"link_count": 0,
|
||||
"link_to": "Accounts Payable",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Accounts Payable Summary",
|
||||
"link_count": 0,
|
||||
"link_to": "Accounts Payable Summary",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Purchase Register",
|
||||
"link_count": 0,
|
||||
"link_to": "Purchase Register",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Item-wise Purchase Register",
|
||||
"link_count": 0,
|
||||
"link_to": "Item-wise Purchase Register",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Purchase Order Analysis",
|
||||
"link_count": 0,
|
||||
"link_to": "Purchase Order Analysis",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Received Items To Be Billed",
|
||||
"link_count": 0,
|
||||
"link_to": "Received Items To Be Billed",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Supplier Ledger Summary",
|
||||
"link_count": 0,
|
||||
"link_to": "Supplier Ledger Summary",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2024-01-18 22:09:46.221549",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payables",
|
||||
"number_cards": [],
|
||||
"owner": "Administrator",
|
||||
"parent_page": "Accounting",
|
||||
"public": 1,
|
||||
"quick_lists": [],
|
||||
"restrict_to_domain": "",
|
||||
"roles": [],
|
||||
"sequence_id": 3.0,
|
||||
"shortcuts": [
|
||||
{
|
||||
"doc_view": "",
|
||||
"label": "Accounts Payable",
|
||||
"link_to": "Accounts Payable",
|
||||
"type": "Report"
|
||||
},
|
||||
{
|
||||
"doc_view": "",
|
||||
"label": "Purchase Invoice",
|
||||
"link_to": "Purchase Invoice",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"doc_view": "",
|
||||
"label": "Journal Entry",
|
||||
"link_to": "Journal Entry",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"doc_view": "",
|
||||
"label": "Payment Entry",
|
||||
"link_to": "Payment Entry",
|
||||
"type": "DocType"
|
||||
}
|
||||
],
|
||||
"title": "Payables"
|
||||
}
|
||||
254
erpnext/accounts/workspace/receivables/receivables.json
Normal file
254
erpnext/accounts/workspace/receivables/receivables.json
Normal file
@@ -0,0 +1,254 @@
|
||||
{
|
||||
"charts": [],
|
||||
"content": "[{\"id\":\"vikWSkNm6_\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Shortcuts</b></span>\",\"col\":12}},{\"id\":\"G984SgVRJN\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Invoice\",\"col\":3}},{\"id\":\"5yHldR0JNk\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"POS Invoice\",\"col\":3}},{\"id\":\"F9f4I1viNr\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Payment Entry\",\"col\":3}},{\"id\":\"1ArNvt9qhz\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Journal Entry\",\"col\":3}},{\"id\":\"4IBBOIxfqW\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Accounts Receivable\",\"col\":3}},{\"id\":\"ILlIxJuexy\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Cost Center\",\"col\":3}},{\"id\":\"B7-uxs8tkU\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"tHb3yxthkR\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"id\":\"jLgv00c6ek\",\"type\":\"card\",\"data\":{\"card_name\":\"Invoicing\",\"col\":4}},{\"id\":\"npwfXlz0u1\",\"type\":\"card\",\"data\":{\"card_name\":\"Payments\",\"col\":4}},{\"id\":\"am70C27Jrb\",\"type\":\"card\",\"data\":{\"card_name\":\"Dunning\",\"col\":4}},{\"id\":\"xOHTyD8b5l\",\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}}]",
|
||||
"creation": "2024-01-05 15:29:21.084241",
|
||||
"custom_blocks": [],
|
||||
"docstatus": 0,
|
||||
"doctype": "Workspace",
|
||||
"for_user": "",
|
||||
"hide_custom": 0,
|
||||
"icon": "arrow-right",
|
||||
"idx": 0,
|
||||
"indicator_color": "",
|
||||
"is_hidden": 0,
|
||||
"label": "Receivables",
|
||||
"links": [
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Invoicing",
|
||||
"link_count": 2,
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Sales Invoice",
|
||||
"link_count": 0,
|
||||
"link_to": "Sales Invoice",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Customer",
|
||||
"link_count": 0,
|
||||
"link_to": "Customer",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Payments",
|
||||
"link_count": 4,
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Payment Entry",
|
||||
"link_count": 0,
|
||||
"link_to": "Payment Entry",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Payment Request",
|
||||
"link_count": 0,
|
||||
"link_to": "Payment Request",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Payment Reconciliation",
|
||||
"link_count": 0,
|
||||
"link_to": "Payment Reconciliation",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Payment Gateway Account",
|
||||
"link_count": 0,
|
||||
"link_to": "Payment Gateway Account",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Dunning",
|
||||
"link_count": 2,
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Dunning",
|
||||
"link_count": 0,
|
||||
"link_to": "Dunning",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Dunning Type",
|
||||
"link_count": 0,
|
||||
"link_to": "Dunning Type",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Reports",
|
||||
"link_count": 6,
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "Sales Invoice",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Accounts Receivable",
|
||||
"link_count": 0,
|
||||
"link_to": "Accounts Receivable",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Sales Invoice",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Accounts Receivable Summary",
|
||||
"link_count": 0,
|
||||
"link_to": "Accounts Receivable Summary",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Sales Invoice",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Sales Register",
|
||||
"link_count": 0,
|
||||
"link_to": "Sales Register",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Sales Invoice",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Item-wise Sales Register",
|
||||
"link_count": 0,
|
||||
"link_to": "Item-wise Sales Register",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Sales Invoice",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Sales Order Analysis",
|
||||
"link_count": 0,
|
||||
"link_to": "Sales Order Analysis",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Sales Invoice",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Delivered Items To Be Billed",
|
||||
"link_count": 0,
|
||||
"link_to": "Delivered Items To Be Billed",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2024-01-18 22:11:51.474477",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Receivables",
|
||||
"number_cards": [],
|
||||
"owner": "Administrator",
|
||||
"parent_page": "Accounting",
|
||||
"public": 1,
|
||||
"quick_lists": [],
|
||||
"restrict_to_domain": "",
|
||||
"roles": [],
|
||||
"sequence_id": 4.0,
|
||||
"shortcuts": [
|
||||
{
|
||||
"color": "Grey",
|
||||
"doc_view": "List",
|
||||
"label": "POS Invoice",
|
||||
"link_to": "POS Invoice",
|
||||
"stats_filter": "[]",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"color": "Grey",
|
||||
"doc_view": "List",
|
||||
"label": "Cost Center",
|
||||
"link_to": "Cost Center",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"doc_view": "",
|
||||
"label": "Sales Invoice",
|
||||
"link_to": "Sales Invoice",
|
||||
"stats_filter": "[]",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"doc_view": "",
|
||||
"label": "Journal Entry",
|
||||
"link_to": "Journal Entry",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"doc_view": "",
|
||||
"label": "Payment Entry",
|
||||
"link_to": "Payment Entry",
|
||||
"type": "DocType"
|
||||
},
|
||||
{
|
||||
"doc_view": "",
|
||||
"label": "Accounts Receivable",
|
||||
"link_to": "Accounts Receivable",
|
||||
"type": "Report"
|
||||
}
|
||||
],
|
||||
"title": "Receivables"
|
||||
}
|
||||
@@ -571,10 +571,16 @@ frappe.ui.form.on('Asset', {
|
||||
indicator: 'red'
|
||||
});
|
||||
}
|
||||
frm.set_value('gross_purchase_amount', item.base_net_rate + item.item_tax_amount);
|
||||
frm.set_value('purchase_receipt_amount', item.base_net_rate + item.item_tax_amount);
|
||||
item.asset_location && frm.set_value('location', item.asset_location);
|
||||
var is_grouped_asset = frappe.db.get_value('Item', item.item_code, 'is_grouped_asset');
|
||||
var asset_quantity = is_grouped_asset ? item.qty : 1;
|
||||
var purchase_amount = flt(item.valuation_rate * asset_quantity, precision('gross_purchase_amount'));
|
||||
|
||||
frm.set_value('gross_purchase_amount', purchase_amount);
|
||||
frm.set_value('purchase_receipt_amount', purchase_amount);
|
||||
frm.set_value('asset_quantity', asset_quantity);
|
||||
frm.set_value('cost_center', item.cost_center || purchase_doc.cost_center);
|
||||
if(item.asset_location) { frm.set_value('location', item.asset_location); }
|
||||
|
||||
},
|
||||
|
||||
set_depreciation_rate: function(frm, row) {
|
||||
|
||||
@@ -202,9 +202,8 @@
|
||||
"fieldname": "purchase_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Purchase Date",
|
||||
"read_only": 1,
|
||||
"read_only_depends_on": "eval:!doc.is_existing_asset && !doc.is_composite_asset",
|
||||
"reqd": 1
|
||||
"mandatory_depends_on": "eval:!doc.is_existing_asset && !doc.is_composite_asset",
|
||||
"read_only_depends_on": "eval:!doc.is_existing_asset && !doc.is_composite_asset"
|
||||
},
|
||||
{
|
||||
"fieldname": "disposal_date",
|
||||
@@ -227,15 +226,15 @@
|
||||
"fieldname": "gross_purchase_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Gross Purchase Amount",
|
||||
"mandatory_depends_on": "eval:(!doc.is_composite_asset || doc.docstatus==1)",
|
||||
"options": "Company:company:default_currency",
|
||||
"read_only_depends_on": "eval:!doc.is_existing_asset",
|
||||
"reqd": 1
|
||||
"read_only_depends_on": "eval:!doc.is_existing_asset"
|
||||
},
|
||||
{
|
||||
"fieldname": "available_for_use_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Available-for-use Date",
|
||||
"reqd": 1
|
||||
"mandatory_depends_on": "eval:(!doc.is_composite_asset || doc.docstatus==1)"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
@@ -590,7 +589,7 @@
|
||||
"link_fieldname": "target_asset"
|
||||
}
|
||||
],
|
||||
"modified": "2023-12-21 16:46:20.732869",
|
||||
"modified": "2024-01-15 17:35:49.226603",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Assets",
|
||||
"name": "Asset",
|
||||
|
||||
@@ -57,7 +57,7 @@ class Asset(AccountsController):
|
||||
asset_owner: DF.Literal["", "Company", "Supplier", "Customer"]
|
||||
asset_owner_company: DF.Link | None
|
||||
asset_quantity: DF.Int
|
||||
available_for_use_date: DF.Date
|
||||
available_for_use_date: DF.Date | None
|
||||
booked_fixed_asset: DF.Check
|
||||
calculate_depreciation: DF.Check
|
||||
capitalized_in: DF.Link | None
|
||||
@@ -92,7 +92,7 @@ class Asset(AccountsController):
|
||||
number_of_depreciations_booked: DF.Int
|
||||
opening_accumulated_depreciation: DF.Currency
|
||||
policy_number: DF.Data | None
|
||||
purchase_date: DF.Date
|
||||
purchase_date: DF.Date | None
|
||||
purchase_invoice: DF.Link | None
|
||||
purchase_receipt: DF.Link | None
|
||||
purchase_receipt_amount: DF.Currency
|
||||
@@ -162,6 +162,7 @@ class Asset(AccountsController):
|
||||
def on_cancel(self):
|
||||
self.validate_cancellation()
|
||||
self.cancel_movement_entries()
|
||||
self.cancel_capitalization()
|
||||
self.delete_depreciation_entries()
|
||||
cancel_asset_depr_schedules(self)
|
||||
self.set_status()
|
||||
@@ -316,7 +317,12 @@ class Asset(AccountsController):
|
||||
frappe.throw(_("Gross Purchase Amount is mandatory"), frappe.MandatoryError)
|
||||
|
||||
if is_cwip_accounting_enabled(self.asset_category):
|
||||
if not self.is_existing_asset and not self.purchase_receipt and not self.purchase_invoice:
|
||||
if (
|
||||
not self.is_existing_asset
|
||||
and not self.is_composite_asset
|
||||
and not self.purchase_receipt
|
||||
and not self.purchase_invoice
|
||||
):
|
||||
frappe.throw(
|
||||
_("Please create purchase receipt or purchase invoice for the item {0}").format(
|
||||
self.item_code
|
||||
@@ -329,7 +335,7 @@ class Asset(AccountsController):
|
||||
and not frappe.db.get_value("Purchase Invoice", self.purchase_invoice, "update_stock")
|
||||
):
|
||||
frappe.throw(
|
||||
_("Update stock must be enable for the purchase invoice {0}").format(self.purchase_invoice)
|
||||
_("Update stock must be enabled for the purchase invoice {0}").format(self.purchase_invoice)
|
||||
)
|
||||
|
||||
if not self.calculate_depreciation:
|
||||
@@ -512,6 +518,16 @@ class Asset(AccountsController):
|
||||
movement = frappe.get_doc("Asset Movement", movement.get("name"))
|
||||
movement.cancel()
|
||||
|
||||
def cancel_capitalization(self):
|
||||
asset_capitalization = frappe.db.get_value(
|
||||
"Asset Capitalization",
|
||||
{"target_asset": self.name, "docstatus": 1, "entry_type": "Capitalization"},
|
||||
)
|
||||
|
||||
if asset_capitalization:
|
||||
asset_capitalization = frappe.get_doc("Asset Capitalization", asset_capitalization)
|
||||
asset_capitalization.cancel()
|
||||
|
||||
def delete_depreciation_entries(self):
|
||||
if self.calculate_depreciation:
|
||||
for row in self.get("finance_books"):
|
||||
@@ -692,9 +708,7 @@ class Asset(AccountsController):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": cwip_account,
|
||||
"against_type": "Account",
|
||||
"against": fixed_asset_account,
|
||||
"against_link": fixed_asset_account,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
|
||||
"posting_date": self.available_for_use_date,
|
||||
"credit": self.purchase_receipt_amount,
|
||||
@@ -709,9 +723,7 @@ class Asset(AccountsController):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": fixed_asset_account,
|
||||
"against_type": "Account",
|
||||
"against": cwip_account,
|
||||
"against_link": cwip_account,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
|
||||
"posting_date": self.available_for_use_date,
|
||||
"debit": self.purchase_receipt_amount,
|
||||
@@ -1026,6 +1038,8 @@ def is_cwip_accounting_enabled(asset_category):
|
||||
@frappe.whitelist()
|
||||
def get_asset_value_after_depreciation(asset_name, finance_book=None):
|
||||
asset = frappe.get_doc("Asset", asset_name)
|
||||
if not asset.calculate_depreciation:
|
||||
return flt(asset.value_after_depreciation)
|
||||
|
||||
return asset.get_value_after_depreciation(finance_book)
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ from frappe.utils import (
|
||||
)
|
||||
from frappe.utils.user import get_users_with_role
|
||||
|
||||
import erpnext
|
||||
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
|
||||
get_checks_for_pl_and_bs_accounts,
|
||||
)
|
||||
@@ -35,7 +36,7 @@ from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_sched
|
||||
def post_depreciation_entries(date=None):
|
||||
# Return if automatic booking of asset depreciation is disabled
|
||||
if not cint(
|
||||
frappe.db.get_value("Accounts Settings", None, "book_asset_depreciation_entry_automatically")
|
||||
frappe.db.get_single_value("Accounts Settings", "book_asset_depreciation_entry_automatically")
|
||||
):
|
||||
return
|
||||
|
||||
@@ -522,6 +523,13 @@ def depreciate_asset(asset_doc, date, notes):
|
||||
|
||||
make_depreciation_entry_for_all_asset_depr_schedules(asset_doc, date)
|
||||
|
||||
cancel_depreciation_entries(asset_doc, date)
|
||||
|
||||
|
||||
@erpnext.allow_regional
|
||||
def cancel_depreciation_entries(asset_doc, date):
|
||||
pass
|
||||
|
||||
|
||||
def reset_depreciation_schedule(asset_doc, date, notes):
|
||||
if not asset_doc.calculate_depreciation:
|
||||
|
||||
@@ -251,16 +251,7 @@ class TestAsset(AssetSetup):
|
||||
flt(18000.0 + pro_rata_amount, asset.precision("gross_purchase_amount")),
|
||||
0.0,
|
||||
),
|
||||
(
|
||||
"_Test Fixed Asset - _TC",
|
||||
0.0,
|
||||
flt(18000.0 + pro_rata_amount, asset.precision("gross_purchase_amount")),
|
||||
),
|
||||
(
|
||||
"_Test Fixed Asset - _TC",
|
||||
0.0,
|
||||
flt(82000.0 - pro_rata_amount, asset.precision("gross_purchase_amount")),
|
||||
),
|
||||
("_Test Fixed Asset - _TC", 0.0, 100000.0),
|
||||
(
|
||||
"_Test Gain/Loss on Asset Disposal - _TC",
|
||||
flt(82000.0 - pro_rata_amount, asset.precision("gross_purchase_amount")),
|
||||
@@ -900,7 +891,7 @@ class TestDepreciationMethods(AssetSetup):
|
||||
["2030-12-31", 28630.14, 28630.14],
|
||||
["2031-12-31", 35684.93, 64315.07],
|
||||
["2032-12-31", 17842.46, 82157.53],
|
||||
["2033-06-06", 5342.47, 87500.0],
|
||||
["2033-06-06", 5342.46, 87499.99],
|
||||
]
|
||||
|
||||
schedules = [
|
||||
@@ -1012,7 +1003,7 @@ class TestDepreciationBasics(AssetSetup):
|
||||
asset_depr_schedule_doc = get_asset_depr_schedule_doc(asset.name, "Active")
|
||||
|
||||
depreciation_amount = get_depreciation_amount(
|
||||
asset_depr_schedule_doc, asset, 100000, asset.finance_books[0]
|
||||
asset_depr_schedule_doc, asset, 100000, 100000, asset.finance_books[0]
|
||||
)
|
||||
self.assertEqual(depreciation_amount, 30000)
|
||||
|
||||
|
||||
@@ -21,10 +21,10 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s
|
||||
this.show_stock_ledger();
|
||||
}
|
||||
|
||||
if (this.frm.doc.stock_items && !this.frm.doc.stock_items.length && this.frm.doc.target_asset && this.frm.doc.capitalization_method === "Choose a WIP composite asset") {
|
||||
this.set_consumed_stock_items_tagged_to_wip_composite_asset(this.frm.doc.target_asset);
|
||||
this.get_target_asset_details();
|
||||
}
|
||||
// if (this.frm.doc.stock_items && !this.frm.doc.stock_items.length && this.frm.doc.target_asset && this.frm.doc.capitalization_method === "Choose a WIP composite asset") {
|
||||
// this.set_consumed_stock_items_tagged_to_wip_composite_asset(this.frm.doc.target_asset);
|
||||
// this.get_target_asset_details();
|
||||
// }
|
||||
}
|
||||
|
||||
setup_queries() {
|
||||
@@ -143,13 +143,20 @@ erpnext.assets.AssetCapitalization = class AssetCapitalization extends erpnext.s
|
||||
},
|
||||
callback: function (r) {
|
||||
if (!r.exc && r.message) {
|
||||
me.frm.clear_table("stock_items");
|
||||
|
||||
for (let item of r.message) {
|
||||
me.frm.add_child("stock_items", item);
|
||||
if(r.message[0] && r.message[0].length) {
|
||||
me.frm.clear_table("stock_items");
|
||||
for (let item of r.message[0]) {
|
||||
me.frm.add_child("stock_items", item);
|
||||
}
|
||||
refresh_field("stock_items");
|
||||
}
|
||||
if (r.message[1] && r.message[1].length) {
|
||||
me.frm.clear_table("asset_items");
|
||||
for (let item of r.message[1]) {
|
||||
me.frm.add_child("asset_items", item);
|
||||
}
|
||||
me.frm.refresh_field("asset_items");
|
||||
}
|
||||
|
||||
refresh_field("stock_items");
|
||||
|
||||
me.calculate_totals();
|
||||
}
|
||||
|
||||
@@ -136,11 +136,19 @@ class AssetCapitalization(StockController):
|
||||
"Stock Ledger Entry",
|
||||
"Repost Item Valuation",
|
||||
"Serial and Batch Bundle",
|
||||
"Asset",
|
||||
)
|
||||
self.cancel_target_asset()
|
||||
self.update_stock_ledger()
|
||||
self.make_gl_entries()
|
||||
self.restore_consumed_asset_items()
|
||||
|
||||
def cancel_target_asset(self):
|
||||
if self.entry_type == "Capitalization" and self.target_asset:
|
||||
asset_doc = frappe.get_doc("Asset", self.target_asset)
|
||||
if asset_doc.docstatus == 1:
|
||||
asset_doc.cancel()
|
||||
|
||||
def set_title(self):
|
||||
self.title = self.target_asset_name or self.target_item_name or self.target_item_code
|
||||
|
||||
@@ -485,9 +493,7 @@ class AssetCapitalization(StockController):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": account,
|
||||
"against_type": "Account",
|
||||
"against": target_account,
|
||||
"against_link": target_account,
|
||||
"cost_center": item_row.cost_center,
|
||||
"project": item_row.get("project") or self.get("project"),
|
||||
"remarks": self.get("remarks") or "Accounting Entry for Stock",
|
||||
@@ -528,9 +534,7 @@ class AssetCapitalization(StockController):
|
||||
self.set_consumed_asset_status(asset)
|
||||
|
||||
for gle in fixed_asset_gl_entries:
|
||||
gle["against_type"] = "Account"
|
||||
gle["against"] = target_account
|
||||
gle["against_link"] = target_account
|
||||
gl_entries.append(self.get_gl_dict(gle, item=item))
|
||||
target_against.add(gle["account"])
|
||||
|
||||
@@ -546,9 +550,7 @@ class AssetCapitalization(StockController):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": item_row.expense_account,
|
||||
"against_type": "Account",
|
||||
"against": target_account,
|
||||
"against_link": target_account,
|
||||
"cost_center": item_row.cost_center,
|
||||
"project": item_row.get("project") or self.get("project"),
|
||||
"remarks": self.get("remarks") or "Accounting Entry for Stock",
|
||||
@@ -559,46 +561,41 @@ class AssetCapitalization(StockController):
|
||||
)
|
||||
|
||||
def get_gl_entries_for_target_item(self, gl_entries, target_against, precision):
|
||||
for target_account in target_against:
|
||||
if self.target_is_fixed_asset:
|
||||
# Capitalization
|
||||
if self.target_is_fixed_asset:
|
||||
# Capitalization
|
||||
gl_entries.append(
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": self.target_fixed_asset_account,
|
||||
"against": ", ".join(target_against),
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
|
||||
"debit": flt(self.total_value, precision),
|
||||
"cost_center": self.get("cost_center"),
|
||||
},
|
||||
item=self,
|
||||
)
|
||||
)
|
||||
else:
|
||||
# Target Stock Item
|
||||
sle_list = self.sle_map.get(self.name)
|
||||
for sle in sle_list:
|
||||
stock_value_difference = flt(sle.stock_value_difference, precision)
|
||||
account = self.warehouse_account[sle.warehouse]["account"]
|
||||
|
||||
gl_entries.append(
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": self.target_fixed_asset_account,
|
||||
"against_type": "Account",
|
||||
"against": target_account,
|
||||
"against_link": target_account,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
|
||||
"debit": flt(self.total_value, precision) / len(target_against),
|
||||
"cost_center": self.get("cost_center"),
|
||||
"account": account,
|
||||
"against": ", ".join(target_against),
|
||||
"cost_center": self.cost_center,
|
||||
"project": self.get("project"),
|
||||
"remarks": self.get("remarks") or "Accounting Entry for Stock",
|
||||
"debit": stock_value_difference,
|
||||
},
|
||||
self.warehouse_account[sle.warehouse]["account_currency"],
|
||||
item=self,
|
||||
)
|
||||
)
|
||||
else:
|
||||
# Target Stock Item
|
||||
sle_list = self.sle_map.get(self.name)
|
||||
for sle in sle_list:
|
||||
stock_value_difference = flt(sle.stock_value_difference, precision)
|
||||
account = self.warehouse_account[sle.warehouse]["account"]
|
||||
|
||||
gl_entries.append(
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": account,
|
||||
"against_type": "Account",
|
||||
"against": target_account,
|
||||
"against_link": target_account,
|
||||
"cost_center": self.cost_center,
|
||||
"project": self.get("project"),
|
||||
"remarks": self.get("remarks") or "Accounting Entry for Stock",
|
||||
"debit": stock_value_difference / len(target_against),
|
||||
},
|
||||
self.warehouse_account[sle.warehouse]["account_currency"],
|
||||
item=self,
|
||||
)
|
||||
)
|
||||
|
||||
def create_target_asset(self):
|
||||
if (
|
||||
@@ -892,7 +889,6 @@ def get_consumed_asset_details(args):
|
||||
out.cost_center = get_default_cost_center(
|
||||
args, item_defaults, item_group_defaults, brand_defaults
|
||||
)
|
||||
|
||||
return out
|
||||
|
||||
|
||||
@@ -940,10 +936,27 @@ def get_items_tagged_to_wip_composite_asset(asset):
|
||||
"qty",
|
||||
"valuation_rate",
|
||||
"amount",
|
||||
"is_fixed_asset",
|
||||
"parent",
|
||||
]
|
||||
|
||||
pr_items = frappe.get_all(
|
||||
"Purchase Receipt Item", filters={"wip_composite_asset": asset}, fields=fields
|
||||
"Purchase Receipt Item", filters={"wip_composite_asset": asset, "docstatus": 1}, fields=fields
|
||||
)
|
||||
|
||||
return pr_items
|
||||
stock_items = []
|
||||
asset_items = []
|
||||
for d in pr_items:
|
||||
if not d.is_fixed_asset:
|
||||
stock_items.append(frappe._dict(d))
|
||||
else:
|
||||
asset_details = frappe.db.get_value(
|
||||
"Asset",
|
||||
{"item_code": d.item_code, "purchase_receipt": d.parent},
|
||||
["name as asset", "asset_name"],
|
||||
as_dict=1,
|
||||
)
|
||||
d.update(asset_details)
|
||||
asset_items.append(frappe._dict(d))
|
||||
|
||||
return stock_items, asset_items
|
||||
|
||||
@@ -98,12 +98,12 @@ class TestAssetCapitalization(unittest.TestCase):
|
||||
|
||||
# Test General Ledger Entries
|
||||
expected_gle = {
|
||||
"_Test Fixed Asset - TCP1": 2999.99,
|
||||
"_Test Fixed Asset - TCP1": 3000,
|
||||
"Expenses Included In Asset Valuation - TCP1": -1000,
|
||||
"_Test Warehouse - TCP1": -2000,
|
||||
"Round Off - TCP1": 0.01,
|
||||
}
|
||||
actual_gle = get_actual_gle_dict(asset_capitalization.name)
|
||||
|
||||
self.assertEqual(actual_gle, expected_gle)
|
||||
|
||||
# Test Stock Ledger Entries
|
||||
@@ -189,10 +189,9 @@ class TestAssetCapitalization(unittest.TestCase):
|
||||
# Test General Ledger Entries
|
||||
default_expense_account = frappe.db.get_value("Company", company, "default_expense_account")
|
||||
expected_gle = {
|
||||
"_Test Fixed Asset - _TC": 2999.99,
|
||||
"_Test Fixed Asset - _TC": 3000,
|
||||
"Expenses Included In Asset Valuation - _TC": -1000,
|
||||
default_expense_account: -2000,
|
||||
"Round Off - _TC": 0.01,
|
||||
}
|
||||
actual_gle = get_actual_gle_dict(asset_capitalization.name)
|
||||
|
||||
@@ -377,10 +376,9 @@ class TestAssetCapitalization(unittest.TestCase):
|
||||
|
||||
# Test General Ledger Entries
|
||||
expected_gle = {
|
||||
"_Test Warehouse - TCP1": consumed_asset_value_before_disposal,
|
||||
"_Test Accumulated Depreciations - TCP1": accumulated_depreciation,
|
||||
"_Test Fixed Asset - TCP1": -consumed_asset_purchase_value,
|
||||
"_Test Warehouse - TCP1": consumed_asset_value_before_disposal - 0.01,
|
||||
"Round Off - TCP1": 0.01,
|
||||
}
|
||||
actual_gle = get_actual_gle_dict(asset_capitalization.name)
|
||||
self.assertEqual(actual_gle, expected_gle)
|
||||
|
||||
@@ -86,12 +86,12 @@ class AssetCategory(Document):
|
||||
if selected_key_type not in expected_key_types:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Row #{}: {} of {} should be {}. Please modify the account or select a different account."
|
||||
"Row #{0}: {1} of {2} should be {3}. Please update the {1} or select a different account."
|
||||
).format(
|
||||
d.idx,
|
||||
frappe.unscrub(key_to_match),
|
||||
frappe.bold(selected_account),
|
||||
frappe.bold(expected_key_types),
|
||||
frappe.bold(" or ".join(expected_key_types)),
|
||||
),
|
||||
title=_("Invalid Account"),
|
||||
)
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
"field_order": [
|
||||
"asset",
|
||||
"naming_series",
|
||||
"company",
|
||||
"column_break_2",
|
||||
"gross_purchase_amount",
|
||||
"opening_accumulated_depreciation",
|
||||
@@ -193,12 +194,20 @@
|
||||
"fieldtype": "Check",
|
||||
"label": "Depreciate based on shifts",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "asset.company",
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"label": "Company",
|
||||
"options": "Company",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2023-11-29 00:57:00.461998",
|
||||
"modified": "2024-01-08 16:31:04.533928",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Assets",
|
||||
"name": "Asset Depreciation Schedule",
|
||||
|
||||
@@ -7,6 +7,7 @@ from frappe.model.document import Document
|
||||
from frappe.utils import (
|
||||
add_days,
|
||||
add_months,
|
||||
add_years,
|
||||
cint,
|
||||
date_diff,
|
||||
flt,
|
||||
@@ -18,6 +19,7 @@ from frappe.utils import (
|
||||
)
|
||||
|
||||
import erpnext
|
||||
from erpnext.accounts.utils import get_fiscal_year
|
||||
|
||||
|
||||
class AssetDepreciationSchedule(Document):
|
||||
@@ -35,6 +37,7 @@ class AssetDepreciationSchedule(Document):
|
||||
|
||||
amended_from: DF.Link | None
|
||||
asset: DF.Link
|
||||
company: DF.Link | None
|
||||
daily_prorata_based: DF.Check
|
||||
depreciation_method: DF.Literal[
|
||||
"", "Straight Line", "Double Declining Balance", "Written Down Value", "Manual"
|
||||
@@ -282,12 +285,20 @@ class AssetDepreciationSchedule(Document):
|
||||
depreciation_amount = 0
|
||||
|
||||
number_of_pending_depreciations = final_number_of_depreciations - start
|
||||
|
||||
yearly_opening_wdv = value_after_depreciation
|
||||
current_fiscal_year_end_date = None
|
||||
for n in range(start, final_number_of_depreciations):
|
||||
# If depreciation is already completed (for double declining balance)
|
||||
if skip_row:
|
||||
continue
|
||||
|
||||
schedule_date = add_months(row.depreciation_start_date, n * cint(row.frequency_of_depreciation))
|
||||
if not current_fiscal_year_end_date:
|
||||
current_fiscal_year_end_date = get_fiscal_year(row.depreciation_start_date)[2]
|
||||
elif getdate(schedule_date) > getdate(current_fiscal_year_end_date):
|
||||
current_fiscal_year_end_date = add_years(current_fiscal_year_end_date, 1)
|
||||
yearly_opening_wdv = value_after_depreciation
|
||||
|
||||
if n > 0 and len(self.get("depreciation_schedule")) > n - 1:
|
||||
prev_depreciation_amount = self.get("depreciation_schedule")[n - 1].depreciation_amount
|
||||
else:
|
||||
@@ -297,6 +308,7 @@ class AssetDepreciationSchedule(Document):
|
||||
self,
|
||||
asset_doc,
|
||||
value_after_depreciation,
|
||||
yearly_opening_wdv,
|
||||
row,
|
||||
n,
|
||||
prev_depreciation_amount,
|
||||
@@ -340,10 +352,7 @@ class AssetDepreciationSchedule(Document):
|
||||
n == 0
|
||||
and (has_pro_rata or has_wdv_or_dd_non_yearly_pro_rata)
|
||||
and not self.opening_accumulated_depreciation
|
||||
and get_updated_rate_of_depreciation_for_wdv_and_dd(
|
||||
asset_doc, value_after_depreciation, row, False
|
||||
)
|
||||
== row.rate_of_depreciation
|
||||
and not self.flags.wdv_it_act_applied
|
||||
):
|
||||
from_date = add_days(
|
||||
asset_doc.available_for_use_date, -1
|
||||
@@ -403,8 +412,9 @@ class AssetDepreciationSchedule(Document):
|
||||
|
||||
if not depreciation_amount:
|
||||
continue
|
||||
value_after_depreciation -= flt(
|
||||
depreciation_amount, asset_doc.precision("gross_purchase_amount")
|
||||
value_after_depreciation = flt(
|
||||
value_after_depreciation - flt(depreciation_amount),
|
||||
asset_doc.precision("gross_purchase_amount"),
|
||||
)
|
||||
|
||||
# Adjust depreciation amount in the last period based on the expected value after useful life
|
||||
@@ -584,6 +594,7 @@ def get_depreciation_amount(
|
||||
asset_depr_schedule,
|
||||
asset,
|
||||
depreciable_value,
|
||||
yearly_opening_wdv,
|
||||
fb_row,
|
||||
schedule_idx=0,
|
||||
prev_depreciation_amount=0,
|
||||
@@ -595,26 +606,18 @@ def get_depreciation_amount(
|
||||
asset_depr_schedule, asset, fb_row, schedule_idx, number_of_pending_depreciations
|
||||
)
|
||||
else:
|
||||
rate_of_depreciation = get_updated_rate_of_depreciation_for_wdv_and_dd(
|
||||
asset, depreciable_value, fb_row
|
||||
)
|
||||
return get_wdv_or_dd_depr_amount(
|
||||
asset,
|
||||
fb_row,
|
||||
depreciable_value,
|
||||
rate_of_depreciation,
|
||||
fb_row.frequency_of_depreciation,
|
||||
yearly_opening_wdv,
|
||||
schedule_idx,
|
||||
prev_depreciation_amount,
|
||||
has_wdv_or_dd_non_yearly_pro_rata,
|
||||
asset_depr_schedule,
|
||||
)
|
||||
|
||||
|
||||
@erpnext.allow_regional
|
||||
def get_updated_rate_of_depreciation_for_wdv_and_dd(
|
||||
asset, depreciable_value, fb_row, show_msg=True
|
||||
):
|
||||
return fb_row.rate_of_depreciation
|
||||
|
||||
|
||||
def get_straight_line_or_manual_depr_amount(
|
||||
asset_depr_schedule, asset, row, schedule_idx, number_of_pending_depreciations
|
||||
):
|
||||
@@ -750,30 +753,57 @@ def get_asset_shift_factors_map():
|
||||
return dict(frappe.db.get_all("Asset Shift Factor", ["shift_name", "shift_factor"], as_list=True))
|
||||
|
||||
|
||||
@erpnext.allow_regional
|
||||
def get_wdv_or_dd_depr_amount(
|
||||
asset,
|
||||
fb_row,
|
||||
depreciable_value,
|
||||
rate_of_depreciation,
|
||||
frequency_of_depreciation,
|
||||
yearly_opening_wdv,
|
||||
schedule_idx,
|
||||
prev_depreciation_amount,
|
||||
has_wdv_or_dd_non_yearly_pro_rata,
|
||||
asset_depr_schedule,
|
||||
):
|
||||
if cint(frequency_of_depreciation) == 12:
|
||||
return flt(depreciable_value) * (flt(rate_of_depreciation) / 100)
|
||||
return get_default_wdv_or_dd_depr_amount(
|
||||
asset,
|
||||
fb_row,
|
||||
depreciable_value,
|
||||
schedule_idx,
|
||||
prev_depreciation_amount,
|
||||
has_wdv_or_dd_non_yearly_pro_rata,
|
||||
asset_depr_schedule,
|
||||
)
|
||||
|
||||
|
||||
def get_default_wdv_or_dd_depr_amount(
|
||||
asset,
|
||||
fb_row,
|
||||
depreciable_value,
|
||||
schedule_idx,
|
||||
prev_depreciation_amount,
|
||||
has_wdv_or_dd_non_yearly_pro_rata,
|
||||
asset_depr_schedule,
|
||||
):
|
||||
if cint(fb_row.frequency_of_depreciation) == 12:
|
||||
return flt(depreciable_value) * (flt(fb_row.rate_of_depreciation) / 100)
|
||||
else:
|
||||
if has_wdv_or_dd_non_yearly_pro_rata:
|
||||
if schedule_idx == 0:
|
||||
return flt(depreciable_value) * (flt(rate_of_depreciation) / 100)
|
||||
elif schedule_idx % (12 / cint(frequency_of_depreciation)) == 1:
|
||||
return flt(depreciable_value) * (flt(fb_row.rate_of_depreciation) / 100)
|
||||
elif schedule_idx % (12 / cint(fb_row.frequency_of_depreciation)) == 1:
|
||||
return (
|
||||
flt(depreciable_value) * flt(frequency_of_depreciation) * (flt(rate_of_depreciation) / 1200)
|
||||
flt(depreciable_value)
|
||||
* flt(fb_row.frequency_of_depreciation)
|
||||
* (flt(fb_row.rate_of_depreciation) / 1200)
|
||||
)
|
||||
else:
|
||||
return prev_depreciation_amount
|
||||
else:
|
||||
if schedule_idx % (12 / cint(frequency_of_depreciation)) == 0:
|
||||
if schedule_idx % (12 / cint(fb_row.frequency_of_depreciation)) == 0:
|
||||
return (
|
||||
flt(depreciable_value) * flt(frequency_of_depreciation) * (flt(rate_of_depreciation) / 1200)
|
||||
flt(depreciable_value)
|
||||
* flt(fb_row.frequency_of_depreciation)
|
||||
* (flt(fb_row.rate_of_depreciation) / 1200)
|
||||
)
|
||||
else:
|
||||
return prev_depreciation_amount
|
||||
|
||||
@@ -94,7 +94,6 @@
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval:doc.depreciation_method == \"Straight Line\" || doc.depreciation_method == \"Manual\"",
|
||||
"fieldname": "daily_prorata_based",
|
||||
"fieldtype": "Check",
|
||||
"label": "Depreciate based on daily pro-rata"
|
||||
@@ -110,7 +109,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2023-11-29 00:57:07.579777",
|
||||
"modified": "2023-12-29 08:49:39.876439",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Assets",
|
||||
"name": "Asset Finance Book",
|
||||
|
||||
@@ -285,9 +285,7 @@ class AssetRepair(AccountsController):
|
||||
"account": fixed_asset_account,
|
||||
"debit": self.repair_cost,
|
||||
"debit_in_account_currency": self.repair_cost,
|
||||
"against_type": "Account",
|
||||
"against": pi_expense_account,
|
||||
"against_link": pi_expense_account,
|
||||
"voucher_type": self.doctype,
|
||||
"voucher_no": self.name,
|
||||
"cost_center": self.cost_center,
|
||||
@@ -306,9 +304,7 @@ class AssetRepair(AccountsController):
|
||||
"account": pi_expense_account,
|
||||
"credit": self.repair_cost,
|
||||
"credit_in_account_currency": self.repair_cost,
|
||||
"against_type": "Account",
|
||||
"against": fixed_asset_account,
|
||||
"against_link": fixed_asset_account,
|
||||
"voucher_type": self.doctype,
|
||||
"voucher_no": self.name,
|
||||
"cost_center": self.cost_center,
|
||||
@@ -342,9 +338,7 @@ class AssetRepair(AccountsController):
|
||||
"account": item.expense_account or default_expense_account,
|
||||
"credit": item.amount,
|
||||
"credit_in_account_currency": item.amount,
|
||||
"against_type": "Account",
|
||||
"against": fixed_asset_account,
|
||||
"against_link": fixed_asset_account,
|
||||
"voucher_type": self.doctype,
|
||||
"voucher_no": self.name,
|
||||
"cost_center": self.cost_center,
|
||||
@@ -361,9 +355,7 @@ class AssetRepair(AccountsController):
|
||||
"account": fixed_asset_account,
|
||||
"debit": item.amount,
|
||||
"debit_in_account_currency": item.amount,
|
||||
"against_type": "Account",
|
||||
"against": item.expense_account or default_expense_account,
|
||||
"against_link": item.expense_account or default_expense_account,
|
||||
"voucher_type": self.doctype,
|
||||
"voucher_no": self.name,
|
||||
"cost_center": self.cost_center,
|
||||
|
||||
@@ -196,18 +196,18 @@
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2023-05-24 14:47:20.243146",
|
||||
"modified": "2024-01-05 17:40:34.570041",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Assets",
|
||||
"name": "Assets",
|
||||
"number_cards": [],
|
||||
"owner": "Administrator",
|
||||
"parent_page": "Accounting",
|
||||
"parent_page": "",
|
||||
"public": 1,
|
||||
"quick_lists": [],
|
||||
"restrict_to_domain": "",
|
||||
"roles": [],
|
||||
"sequence_id": 4.0,
|
||||
"sequence_id": 7.0,
|
||||
"shortcuts": [
|
||||
{
|
||||
"label": "Asset",
|
||||
|
||||
@@ -214,7 +214,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2023-11-28 13:01:18.403492",
|
||||
"modified": "2024-01-12 16:42:01.894346",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Buying Settings",
|
||||
@@ -238,6 +238,26 @@
|
||||
"role": "Purchase Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"read": 1,
|
||||
"role": "Accounts User"
|
||||
},
|
||||
{
|
||||
"read": 1,
|
||||
"role": "Accounts Manager"
|
||||
},
|
||||
{
|
||||
"read": 1,
|
||||
"role": "Stock Manager"
|
||||
},
|
||||
{
|
||||
"read": 1,
|
||||
"role": "Stock User"
|
||||
},
|
||||
{
|
||||
"read": 1,
|
||||
"role": "Purchase User"
|
||||
}
|
||||
],
|
||||
"sort_field": "modified",
|
||||
|
||||
@@ -29,6 +29,8 @@ from erpnext.stock.doctype.purchase_receipt.purchase_receipt import (
|
||||
class TestPurchaseOrder(FrappeTestCase):
|
||||
def test_purchase_order_qty(self):
|
||||
po = create_purchase_order(qty=1, do_not_save=True)
|
||||
|
||||
# NonNegativeError with qty=-1
|
||||
po.append(
|
||||
"items",
|
||||
{
|
||||
@@ -39,9 +41,15 @@ class TestPurchaseOrder(FrappeTestCase):
|
||||
)
|
||||
self.assertRaises(frappe.NonNegativeError, po.save)
|
||||
|
||||
# InvalidQtyError with qty=0
|
||||
po.items[1].qty = 0
|
||||
self.assertRaises(InvalidQtyError, po.save)
|
||||
|
||||
# No error with qty=1
|
||||
po.items[1].qty = 1
|
||||
po.save()
|
||||
self.assertEqual(po.items[1].qty, 1)
|
||||
|
||||
def test_make_purchase_receipt(self):
|
||||
po = create_purchase_order(do_not_submit=True)
|
||||
self.assertRaises(frappe.ValidationError, make_purchase_receipt, po.name)
|
||||
@@ -1108,7 +1116,7 @@ def create_purchase_order(**args):
|
||||
"item_code": args.item or args.item_code or "_Test Item",
|
||||
"warehouse": args.warehouse or "_Test Warehouse - _TC",
|
||||
"from_warehouse": args.from_warehouse,
|
||||
"qty": args.qty or 10,
|
||||
"qty": args.qty if args.qty is not None else 10,
|
||||
"rate": args.rate or 500,
|
||||
"schedule_date": add_days(nowdate(), 1),
|
||||
"include_exploded_items": args.get("include_exploded_items", 1),
|
||||
|
||||
@@ -65,6 +65,7 @@ class RequestforQuotation(BuyingController):
|
||||
def validate(self):
|
||||
self.validate_duplicate_supplier()
|
||||
self.validate_supplier_list()
|
||||
super(RequestforQuotation, self).validate_qty_is_not_zero()
|
||||
validate_for_items(self)
|
||||
super(RequestforQuotation, self).set_qty_as_per_stock_uom()
|
||||
self.update_email_id()
|
||||
@@ -357,8 +358,8 @@ def make_supplier_quotation_from_rfq(source_name, target_doc=None, for_supplier=
|
||||
target_doc.currency = args.currency or get_party_account_currency(
|
||||
"Supplier", for_supplier, source.company
|
||||
)
|
||||
target_doc.buying_price_list = args.buying_price_list or frappe.db.get_value(
|
||||
"Buying Settings", None, "buying_price_list"
|
||||
target_doc.buying_price_list = args.buying_price_list or frappe.db.get_single_value(
|
||||
"Buying Settings", "buying_price_list"
|
||||
)
|
||||
set_missing_values(source, target_doc)
|
||||
|
||||
@@ -398,7 +399,7 @@ def create_supplier_quotation(doc):
|
||||
"currency": doc.get("currency")
|
||||
or get_party_account_currency("Supplier", doc.get("supplier"), doc.get("company")),
|
||||
"buying_price_list": doc.get("buying_price_list")
|
||||
or frappe.db.get_value("Buying Settings", None, "buying_price_list"),
|
||||
or frappe.db.get_single_value("Buying Settings", "buying_price_list"),
|
||||
}
|
||||
)
|
||||
add_items(sq_doc, doc.get("supplier"), doc.get("items"))
|
||||
|
||||
@@ -14,6 +14,7 @@ from erpnext.buying.doctype.request_for_quotation.request_for_quotation import (
|
||||
get_pdf,
|
||||
make_supplier_quotation_from_rfq,
|
||||
)
|
||||
from erpnext.controllers.accounts_controller import InvalidQtyError
|
||||
from erpnext.crm.doctype.opportunity.opportunity import make_request_for_quotation as make_rfq
|
||||
from erpnext.crm.doctype.opportunity.test_opportunity import make_opportunity
|
||||
from erpnext.stock.doctype.item.test_item import make_item
|
||||
@@ -21,6 +22,16 @@ from erpnext.templates.pages.rfq import check_supplier_has_docname_access
|
||||
|
||||
|
||||
class TestRequestforQuotation(FrappeTestCase):
|
||||
def test_rfq_qty(self):
|
||||
rfq = make_request_for_quotation(qty=0, do_not_save=True)
|
||||
with self.assertRaises(InvalidQtyError):
|
||||
rfq.save()
|
||||
|
||||
# No error with qty=1
|
||||
rfq.items[0].qty = 1
|
||||
rfq.save()
|
||||
self.assertEqual(rfq.items[0].qty, 1)
|
||||
|
||||
def test_quote_status(self):
|
||||
rfq = make_request_for_quotation()
|
||||
|
||||
@@ -161,14 +172,17 @@ def make_request_for_quotation(**args) -> "RequestforQuotation":
|
||||
"description": "_Test Item",
|
||||
"uom": args.uom or "_Test UOM",
|
||||
"stock_uom": args.stock_uom or "_Test UOM",
|
||||
"qty": args.qty or 5,
|
||||
"qty": args.qty if args.qty is not None else 5,
|
||||
"conversion_factor": args.conversion_factor or 1.0,
|
||||
"warehouse": args.warehouse or "_Test Warehouse - _TC",
|
||||
"schedule_date": nowdate(),
|
||||
},
|
||||
)
|
||||
|
||||
rfq.submit()
|
||||
if not args.do_not_save:
|
||||
rfq.insert()
|
||||
if not args.do_not_submit:
|
||||
rfq.submit()
|
||||
|
||||
return rfq
|
||||
|
||||
|
||||
@@ -5,8 +5,21 @@
|
||||
import frappe
|
||||
from frappe.tests.utils import FrappeTestCase
|
||||
|
||||
from erpnext.controllers.accounts_controller import InvalidQtyError
|
||||
|
||||
|
||||
class TestPurchaseOrder(FrappeTestCase):
|
||||
def test_supplier_quotation_qty(self):
|
||||
sq = frappe.copy_doc(test_records[0])
|
||||
sq.items[0].qty = 0
|
||||
with self.assertRaises(InvalidQtyError):
|
||||
sq.save()
|
||||
|
||||
# No error with qty=1
|
||||
sq.items[0].qty = 1
|
||||
sq.save()
|
||||
self.assertEqual(sq.items[0].qty, 1)
|
||||
|
||||
def test_make_purchase_order(self):
|
||||
from erpnext.buying.doctype.supplier_quotation.supplier_quotation import make_purchase_order
|
||||
|
||||
|
||||
@@ -44,11 +44,6 @@ def update_last_purchase_rate(doc, is_submit) -> None:
|
||||
def validate_for_items(doc) -> None:
|
||||
items = []
|
||||
for d in doc.get("items"):
|
||||
if not d.qty:
|
||||
if doc.doctype == "Purchase Receipt" and d.rejected_qty:
|
||||
continue
|
||||
frappe.throw(_("Please enter quantity for Item {0}").format(d.item_code))
|
||||
|
||||
set_stock_levels(row=d) # update with latest quantities
|
||||
item = validate_item_and_get_basic_data(row=d)
|
||||
validate_stock_item_warehouse(row=d, item=item)
|
||||
|
||||
@@ -930,7 +930,7 @@ class AccountsController(TransactionBase):
|
||||
# Update details in transaction currency
|
||||
gl_dict.update(
|
||||
{
|
||||
"transaction_currency": args.get("currency") or self.get("currency") or self.company_currency,
|
||||
"transaction_currency": self.get("currency") or self.company_currency,
|
||||
"transaction_exchange_rate": self.get("conversion_rate", 1),
|
||||
"debit_in_transaction_currency": self.get_value_in_transaction_currency(
|
||||
account_currency, args, "debit"
|
||||
@@ -969,19 +969,21 @@ class AccountsController(TransactionBase):
|
||||
return self.doctype
|
||||
|
||||
def get_value_in_transaction_currency(self, account_currency, args, field):
|
||||
if account_currency == args.get("currency") or self.get("currency"):
|
||||
if account_currency == self.get("currency"):
|
||||
return args.get(field + "_in_account_currency")
|
||||
else:
|
||||
return flt(args.get(field, 0) / (args.get("conversion_rate") or self.get("conversion_rate", 1)))
|
||||
return flt(args.get(field, 0) / self.get("conversion_rate", 1))
|
||||
|
||||
def validate_qty_is_not_zero(self):
|
||||
if self.doctype == "Purchase Receipt":
|
||||
return
|
||||
|
||||
for item in self.items:
|
||||
if self.doctype == "Purchase Receipt" and item.rejected_qty:
|
||||
continue
|
||||
|
||||
if not flt(item.qty):
|
||||
frappe.throw(
|
||||
msg=_("Row #{0}: Item quantity cannot be zero").format(item.idx),
|
||||
msg=_("Row #{0}: Quantity for Item {1} cannot be zero.").format(
|
||||
item.idx, frappe.bold(item.item_code)
|
||||
),
|
||||
title=_("Invalid Quantity"),
|
||||
exc=InvalidQtyError,
|
||||
)
|
||||
@@ -1159,7 +1161,6 @@ class AccountsController(TransactionBase):
|
||||
)
|
||||
|
||||
credit_or_debit = "credit" if self.doctype == "Purchase Invoice" else "debit"
|
||||
against_type = "Supplier" if self.doctype == "Purchase Invoice" else "Customer"
|
||||
against = self.supplier if self.doctype == "Purchase Invoice" else self.customer
|
||||
|
||||
if precision_loss:
|
||||
@@ -1167,9 +1168,7 @@ class AccountsController(TransactionBase):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": round_off_account,
|
||||
"against_type": against_type,
|
||||
"against": against,
|
||||
"against_link": against,
|
||||
credit_or_debit: precision_loss,
|
||||
"cost_center": round_off_cost_center
|
||||
if self.use_company_roundoff_cost_center
|
||||
@@ -1419,11 +1418,16 @@ class AccountsController(TransactionBase):
|
||||
reconcile_against_document(lst)
|
||||
|
||||
def on_cancel(self):
|
||||
from erpnext.accounts.doctype.bank_transaction.bank_transaction import (
|
||||
remove_from_bank_transaction,
|
||||
)
|
||||
from erpnext.accounts.utils import (
|
||||
cancel_exchange_gain_loss_journal,
|
||||
unlink_ref_doc_from_payment_entries,
|
||||
)
|
||||
|
||||
remove_from_bank_transaction(self.doctype, self.name)
|
||||
|
||||
if self.doctype in ["Sales Invoice", "Purchase Invoice", "Payment Entry", "Journal Entry"]:
|
||||
# Cancel Exchange Gain/Loss Journal before unlinking
|
||||
cancel_exchange_gain_loss_journal(self)
|
||||
@@ -1518,13 +1522,11 @@ class AccountsController(TransactionBase):
|
||||
if self.doctype == "Purchase Invoice":
|
||||
dr_or_cr = "credit"
|
||||
rev_dr_cr = "debit"
|
||||
against_type = "Supplier"
|
||||
supplier_or_customer = self.supplier
|
||||
|
||||
else:
|
||||
dr_or_cr = "debit"
|
||||
rev_dr_cr = "credit"
|
||||
against_type = "Customer"
|
||||
supplier_or_customer = self.customer
|
||||
|
||||
if enable_discount_accounting:
|
||||
@@ -1549,9 +1551,7 @@ class AccountsController(TransactionBase):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": item.discount_account,
|
||||
"against_type": against_type,
|
||||
"against": supplier_or_customer,
|
||||
"against_link": supplier_or_customer,
|
||||
dr_or_cr: flt(
|
||||
discount_amount * self.get("conversion_rate"), item.precision("discount_amount")
|
||||
),
|
||||
@@ -1569,9 +1569,7 @@ class AccountsController(TransactionBase):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": income_or_expense_account,
|
||||
"against_type": against_type,
|
||||
"against": supplier_or_customer,
|
||||
"against_link": supplier_or_customer,
|
||||
rev_dr_cr: flt(
|
||||
discount_amount * self.get("conversion_rate"), item.precision("discount_amount")
|
||||
),
|
||||
@@ -1594,9 +1592,7 @@ class AccountsController(TransactionBase):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": self.additional_discount_account,
|
||||
"against_type": against_type,
|
||||
"against": supplier_or_customer,
|
||||
"against_link": supplier_or_customer,
|
||||
dr_or_cr: self.base_discount_amount,
|
||||
"cost_center": self.cost_center or erpnext.get_default_cost_center(self.company),
|
||||
},
|
||||
@@ -1966,7 +1962,7 @@ class AccountsController(TransactionBase):
|
||||
self.remove(item)
|
||||
|
||||
def set_payment_schedule(self):
|
||||
if self.doctype == "Sales Invoice" and self.is_pos:
|
||||
if (self.doctype == "Sales Invoice" and self.is_pos) or self.get("is_opening") == "Yes":
|
||||
self.payment_terms_template = ""
|
||||
return
|
||||
|
||||
@@ -2149,7 +2145,7 @@ class AccountsController(TransactionBase):
|
||||
)
|
||||
|
||||
def validate_payment_schedule_amount(self):
|
||||
if self.doctype == "Sales Invoice" and self.is_pos:
|
||||
if (self.doctype == "Sales Invoice" and self.is_pos) or self.get("is_opening") == "Yes":
|
||||
return
|
||||
|
||||
party_account_currency = self.get("party_account_currency")
|
||||
@@ -3103,7 +3099,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
|
||||
def validate_quantity(child_item, new_data):
|
||||
if not flt(new_data.get("qty")):
|
||||
frappe.throw(
|
||||
_("Row # {0}: Quantity for Item {1} cannot be zero").format(
|
||||
_("Row #{0}: Quantity for Item {1} cannot be zero.").format(
|
||||
new_data.get("idx"), frappe.bold(new_data.get("item_code"))
|
||||
),
|
||||
title=_("Invalid Qty"),
|
||||
|
||||
@@ -744,11 +744,8 @@ class BuyingController(SubcontractingController):
|
||||
item_data = frappe.db.get_value(
|
||||
"Item", row.item_code, ["asset_naming_series", "asset_category"], as_dict=1
|
||||
)
|
||||
|
||||
if is_grouped_asset:
|
||||
purchase_amount = flt(row.base_amount + row.item_tax_amount)
|
||||
else:
|
||||
purchase_amount = flt(row.base_rate + row.item_tax_amount)
|
||||
asset_quantity = row.qty if is_grouped_asset else 1
|
||||
purchase_amount = flt(row.valuation_rate) * asset_quantity
|
||||
|
||||
asset = frappe.get_doc(
|
||||
{
|
||||
@@ -764,7 +761,7 @@ class BuyingController(SubcontractingController):
|
||||
"calculate_depreciation": 0,
|
||||
"purchase_receipt_amount": purchase_amount,
|
||||
"gross_purchase_amount": purchase_amount,
|
||||
"asset_quantity": row.qty if is_grouped_asset else 1,
|
||||
"asset_quantity": asset_quantity,
|
||||
"purchase_receipt": self.name if self.doctype == "Purchase Receipt" else None,
|
||||
"purchase_invoice": self.name if self.doctype == "Purchase Invoice" else None,
|
||||
}
|
||||
|
||||
@@ -56,7 +56,11 @@ def make_variant_based_on_manufacturer(template, manufacturer, manufacturer_part
|
||||
|
||||
copy_attributes_to_variant(template, variant)
|
||||
|
||||
variant.item_code = append_number_if_name_exists("Item", template.name)
|
||||
variant_name = f"{template.name} - {manufacturer}"
|
||||
if manufacturer_part_no:
|
||||
variant_name += f" - {manufacturer_part_no}"
|
||||
|
||||
variant.item_code = append_number_if_name_exists("Item", variant_name)
|
||||
variant.flags.ignore_mandatory = True
|
||||
variant.save()
|
||||
|
||||
|
||||
@@ -6,10 +6,12 @@ import json
|
||||
from collections import OrderedDict, defaultdict
|
||||
|
||||
import frappe
|
||||
from frappe import scrub
|
||||
from frappe import qb, scrub
|
||||
from frappe.desk.reportview import get_filters_cond, get_match_cond
|
||||
from frappe.query_builder.functions import Concat, Sum
|
||||
from frappe.query_builder import Criterion, CustomFunction
|
||||
from frappe.query_builder.functions import Concat, Locate, Sum
|
||||
from frappe.utils import nowdate, today, unique
|
||||
from pypika import Order
|
||||
|
||||
import erpnext
|
||||
from erpnext.stock.get_item_details import _get_item_tax_template
|
||||
@@ -344,37 +346,46 @@ def bom(doctype, txt, searchfield, start, page_len, filters):
|
||||
@frappe.whitelist()
|
||||
@frappe.validate_and_sanitize_search_inputs
|
||||
def get_project_name(doctype, txt, searchfield, start, page_len, filters):
|
||||
doctype = "Project"
|
||||
cond = ""
|
||||
proj = qb.DocType("Project")
|
||||
qb_filter_and_conditions = []
|
||||
qb_filter_or_conditions = []
|
||||
ifelse = CustomFunction("IF", ["condition", "then", "else"])
|
||||
|
||||
if filters and filters.get("customer"):
|
||||
cond = """(`tabProject`.customer = %s or
|
||||
ifnull(`tabProject`.customer,"")="") and""" % (
|
||||
frappe.db.escape(filters.get("customer"))
|
||||
)
|
||||
qb_filter_and_conditions.append(proj.customer == filters.get("customer"))
|
||||
|
||||
qb_filter_and_conditions.append(proj.status.notin(["Completed", "Cancelled"]))
|
||||
|
||||
q = qb.from_(proj)
|
||||
|
||||
fields = get_fields(doctype, ["name", "project_name"])
|
||||
searchfields = frappe.get_meta(doctype).get_search_fields()
|
||||
searchfields = " or ".join(["`tabProject`." + field + " like %(txt)s" for field in searchfields])
|
||||
for x in fields:
|
||||
q = q.select(proj[x])
|
||||
|
||||
return frappe.db.sql(
|
||||
"""select {fields} from `tabProject`
|
||||
where
|
||||
`tabProject`.status not in ('Completed', 'Cancelled')
|
||||
and {cond} {scond} {match_cond}
|
||||
order by
|
||||
(case when locate(%(_txt)s, `tabProject`.name) > 0 then locate(%(_txt)s, `tabProject`.name) else 99999 end),
|
||||
`tabProject`.idx desc,
|
||||
`tabProject`.name asc
|
||||
limit {page_len} offset {start}""".format(
|
||||
fields=", ".join(["`tabProject`.{0}".format(f) for f in fields]),
|
||||
cond=cond,
|
||||
scond=searchfields,
|
||||
match_cond=get_match_cond(doctype),
|
||||
start=start,
|
||||
page_len=page_len,
|
||||
),
|
||||
{"txt": "%{0}%".format(txt), "_txt": txt.replace("%", "")},
|
||||
)
|
||||
# don't consider 'customer' and 'status' fields for pattern search, as they must be exactly matched
|
||||
searchfields = [
|
||||
x for x in frappe.get_meta(doctype).get_search_fields() if x not in ["customer", "status"]
|
||||
]
|
||||
|
||||
# pattern search
|
||||
if txt:
|
||||
for x in searchfields:
|
||||
qb_filter_or_conditions.append(proj[x].like(f"%{txt}%"))
|
||||
|
||||
q = q.where(Criterion.all(qb_filter_and_conditions)).where(Criterion.any(qb_filter_or_conditions))
|
||||
|
||||
# ordering
|
||||
if txt:
|
||||
# project_name containing search string 'txt' will be given higher precedence
|
||||
q = q.orderby(ifelse(Locate(txt, proj.project_name) > 0, Locate(txt, proj.project_name), 99999))
|
||||
q = q.orderby(proj.idx, order=Order.desc).orderby(proj.name)
|
||||
|
||||
if page_len:
|
||||
q = q.limit(page_len)
|
||||
|
||||
if start:
|
||||
q = q.offset(start)
|
||||
return q.run()
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
@@ -421,23 +432,14 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters):
|
||||
meta = frappe.get_meta(doctype, cached=True)
|
||||
searchfields = meta.get_search_fields()
|
||||
|
||||
query = get_batches_from_stock_ledger_entries(searchfields, txt, filters)
|
||||
bundle_query = get_batches_from_serial_and_batch_bundle(searchfields, txt, filters)
|
||||
|
||||
data = (
|
||||
frappe.qb.from_((query) + (bundle_query))
|
||||
.select("batch_no", "qty", "manufacturing_date", "expiry_date")
|
||||
.offset(start)
|
||||
.limit(page_len)
|
||||
batches = get_batches_from_stock_ledger_entries(searchfields, txt, filters, start, page_len)
|
||||
batches.extend(
|
||||
get_batches_from_serial_and_batch_bundle(searchfields, txt, filters, start, page_len)
|
||||
)
|
||||
|
||||
for field in searchfields:
|
||||
data = data.select(field)
|
||||
filtered_batches = get_filterd_batches(batches)
|
||||
|
||||
data = data.run()
|
||||
data = get_filterd_batches(data)
|
||||
|
||||
return data
|
||||
return filtered_batches
|
||||
|
||||
|
||||
def get_filterd_batches(data):
|
||||
@@ -457,7 +459,7 @@ def get_filterd_batches(data):
|
||||
return filterd_batch
|
||||
|
||||
|
||||
def get_batches_from_stock_ledger_entries(searchfields, txt, filters):
|
||||
def get_batches_from_stock_ledger_entries(searchfields, txt, filters, start=0, page_len=100):
|
||||
stock_ledger_entry = frappe.qb.DocType("Stock Ledger Entry")
|
||||
batch_table = frappe.qb.DocType("Batch")
|
||||
|
||||
@@ -479,6 +481,8 @@ def get_batches_from_stock_ledger_entries(searchfields, txt, filters):
|
||||
& (stock_ledger_entry.batch_no.isnotnull())
|
||||
)
|
||||
.groupby(stock_ledger_entry.batch_no, stock_ledger_entry.warehouse)
|
||||
.offset(start)
|
||||
.limit(page_len)
|
||||
)
|
||||
|
||||
query = query.select(
|
||||
@@ -493,16 +497,16 @@ def get_batches_from_stock_ledger_entries(searchfields, txt, filters):
|
||||
query = query.select(batch_table[field])
|
||||
|
||||
if txt:
|
||||
txt_condition = batch_table.name.like(txt)
|
||||
txt_condition = batch_table.name.like("%{0}%".format(txt))
|
||||
for field in searchfields + ["name"]:
|
||||
txt_condition |= batch_table[field].like(txt)
|
||||
txt_condition |= batch_table[field].like("%{0}%".format(txt))
|
||||
|
||||
query = query.where(txt_condition)
|
||||
|
||||
return query
|
||||
return query.run(as_list=1) or []
|
||||
|
||||
|
||||
def get_batches_from_serial_and_batch_bundle(searchfields, txt, filters):
|
||||
def get_batches_from_serial_and_batch_bundle(searchfields, txt, filters, start=0, page_len=100):
|
||||
bundle = frappe.qb.DocType("Serial and Batch Entry")
|
||||
stock_ledger_entry = frappe.qb.DocType("Stock Ledger Entry")
|
||||
batch_table = frappe.qb.DocType("Batch")
|
||||
@@ -527,6 +531,8 @@ def get_batches_from_serial_and_batch_bundle(searchfields, txt, filters):
|
||||
& (stock_ledger_entry.serial_and_batch_bundle.isnotnull())
|
||||
)
|
||||
.groupby(bundle.batch_no, bundle.warehouse)
|
||||
.offset(start)
|
||||
.limit(page_len)
|
||||
)
|
||||
|
||||
bundle_query = bundle_query.select(
|
||||
@@ -541,13 +547,13 @@ def get_batches_from_serial_and_batch_bundle(searchfields, txt, filters):
|
||||
bundle_query = bundle_query.select(batch_table[field])
|
||||
|
||||
if txt:
|
||||
txt_condition = batch_table.name.like(txt)
|
||||
txt_condition = batch_table.name.like("%{0}%".format(txt))
|
||||
for field in searchfields + ["name"]:
|
||||
txt_condition |= batch_table[field].like(txt)
|
||||
txt_condition |= batch_table[field].like("%{0}%".format(txt))
|
||||
|
||||
bundle_query = bundle_query.where(txt_condition)
|
||||
|
||||
return bundle_query
|
||||
return bundle_query.run(as_list=1)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
|
||||
@@ -10,7 +10,7 @@ from frappe.utils import flt, format_datetime, get_datetime
|
||||
import erpnext
|
||||
from erpnext.stock.serial_batch_bundle import get_batches_from_bundle
|
||||
from erpnext.stock.serial_batch_bundle import get_serial_nos as get_serial_nos_from_bundle
|
||||
from erpnext.stock.utils import get_incoming_rate
|
||||
from erpnext.stock.utils import get_incoming_rate, get_valuation_method
|
||||
|
||||
|
||||
class StockOverReturnError(frappe.ValidationError):
|
||||
@@ -116,7 +116,12 @@ def validate_returned_items(doc):
|
||||
ref = valid_items.get(d.item_code, frappe._dict())
|
||||
validate_quantity(doc, d, ref, valid_items, already_returned_items)
|
||||
|
||||
if ref.rate and doc.doctype in ("Delivery Note", "Sales Invoice") and flt(d.rate) > ref.rate:
|
||||
if (
|
||||
ref.rate
|
||||
and flt(d.rate) > ref.rate
|
||||
and doc.doctype in ("Delivery Note", "Sales Invoice")
|
||||
and get_valuation_method(ref.item_code) != "Moving Average"
|
||||
):
|
||||
frappe.throw(
|
||||
_("Row # {0}: Rate cannot be greater than the rate used in {1} {2}").format(
|
||||
d.idx, doc.doctype, doc.return_against
|
||||
|
||||
@@ -295,9 +295,6 @@ class SellingController(StockController):
|
||||
def get_item_list(self):
|
||||
il = []
|
||||
for d in self.get("items"):
|
||||
if d.qty is None:
|
||||
frappe.throw(_("Row {0}: Qty is mandatory").format(d.idx))
|
||||
|
||||
if self.has_product_bundle(d.item_code):
|
||||
for p in self.get("packed_items"):
|
||||
if p.parent_detail_docname == d.name and p.parent_item == d.item_code:
|
||||
|
||||
@@ -139,11 +139,6 @@ status_map = {
|
||||
"eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Manufacture'",
|
||||
],
|
||||
],
|
||||
"Bank Transaction": [
|
||||
["Unreconciled", "eval:self.docstatus == 1 and self.unallocated_amount>0"],
|
||||
["Reconciled", "eval:self.docstatus == 1 and self.unallocated_amount<=0"],
|
||||
["Cancelled", "eval:self.docstatus == 2"],
|
||||
],
|
||||
"POS Opening Entry": [
|
||||
["Draft", None],
|
||||
["Open", "eval:self.docstatus == 1 and not self.pos_closing_entry"],
|
||||
|
||||
@@ -162,9 +162,7 @@ class StockController(AccountsController):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": warehouse_account[sle.warehouse]["account"],
|
||||
"against_type": "Account",
|
||||
"against": expense_account,
|
||||
"against_link": expense_account,
|
||||
"cost_center": item_row.cost_center,
|
||||
"project": item_row.project or self.get("project"),
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
@@ -180,9 +178,7 @@ class StockController(AccountsController):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": expense_account,
|
||||
"against_type": "Account",
|
||||
"against": warehouse_account[sle.warehouse]["account"],
|
||||
"against_link": warehouse_account[sle.warehouse]["account"],
|
||||
"cost_center": item_row.cost_center,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
"debit": -1 * flt(sle.stock_value_difference, precision),
|
||||
@@ -214,9 +210,7 @@ class StockController(AccountsController):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": expense_account,
|
||||
"against_type": "Account",
|
||||
"against": warehouse_asset_account,
|
||||
"against_link": warehouse_asset_account,
|
||||
"cost_center": item_row.cost_center,
|
||||
"project": item_row.project or self.get("project"),
|
||||
"remarks": _("Rounding gain/loss Entry for Stock Transfer"),
|
||||
@@ -232,9 +226,7 @@ class StockController(AccountsController):
|
||||
self.get_gl_dict(
|
||||
{
|
||||
"account": warehouse_asset_account,
|
||||
"against_type": "Account",
|
||||
"against": expense_account,
|
||||
"against_link": expense_account,
|
||||
"cost_center": item_row.cost_center,
|
||||
"remarks": _("Rounding gain/loss Entry for Stock Transfer"),
|
||||
"credit": sle_rounding_diff,
|
||||
@@ -395,11 +387,7 @@ class StockController(AccountsController):
|
||||
}
|
||||
|
||||
for row in self.get(table_name):
|
||||
for field in [
|
||||
"serial_and_batch_bundle",
|
||||
"current_serial_and_batch_bundle",
|
||||
"rejected_serial_and_batch_bundle",
|
||||
]:
|
||||
for field in QTY_FIELD.keys():
|
||||
if row.get(field):
|
||||
frappe.get_doc("Serial and Batch Bundle", row.get(field)).set_serial_and_batch_values(
|
||||
self, row, qty_field=QTY_FIELD[field]
|
||||
@@ -840,7 +828,6 @@ class StockController(AccountsController):
|
||||
credit,
|
||||
remarks,
|
||||
against_account,
|
||||
against_type="Account",
|
||||
debit_in_account_currency=None,
|
||||
credit_in_account_currency=None,
|
||||
account_currency=None,
|
||||
@@ -855,9 +842,7 @@ class StockController(AccountsController):
|
||||
"cost_center": cost_center,
|
||||
"debit": debit,
|
||||
"credit": credit,
|
||||
"against_type": against_type,
|
||||
"against": against_account,
|
||||
"against_link": against_account,
|
||||
"remarks": remarks,
|
||||
}
|
||||
|
||||
|
||||
@@ -68,7 +68,7 @@ class TestQueries(unittest.TestCase):
|
||||
self.assertGreaterEqual(len(query(txt="_Test Item Home Desktop Manufactured")), 1)
|
||||
|
||||
def test_project_query(self):
|
||||
query = add_default_params(queries.get_project_name, "BOM")
|
||||
query = add_default_params(queries.get_project_name, "Project")
|
||||
|
||||
self.assertGreaterEqual(len(query(txt="_Test Project")), 1)
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ class Appointment(Document):
|
||||
self.create_calendar_event()
|
||||
else:
|
||||
# Set status to unverified
|
||||
self.status = "Unverified"
|
||||
self.db_set("status", "Unverified")
|
||||
# Send email to confirm
|
||||
self.send_confirmation_email()
|
||||
|
||||
|
||||
@@ -129,9 +129,7 @@ class TallyMigration(Document):
|
||||
self.default_cost_center, self.default_round_off_account = frappe.db.get_value(
|
||||
"Company", self.erpnext_company, ["cost_center", "round_off_account"]
|
||||
)
|
||||
self.default_warehouse = frappe.db.get_value(
|
||||
"Stock Settings", "Stock Settings", "default_warehouse"
|
||||
)
|
||||
self.default_warehouse = frappe.db.get_single_value("Stock Settings", "default_warehouse")
|
||||
|
||||
def _process_master_data(self):
|
||||
def get_company_name(collection):
|
||||
|
||||
@@ -489,6 +489,7 @@ bank_reconciliation_doctypes = [
|
||||
"Payment Entry",
|
||||
"Journal Entry",
|
||||
"Purchase Invoice",
|
||||
"Sales Invoice",
|
||||
]
|
||||
|
||||
accounting_dimension_doctypes = [
|
||||
|
||||
81303
erpnext/locale/af.po
Normal file
81303
erpnext/locale/af.po
Normal file
File diff suppressed because it is too large
Load Diff
81303
erpnext/locale/ar.po
Normal file
81303
erpnext/locale/ar.po
Normal file
File diff suppressed because it is too large
Load Diff
81303
erpnext/locale/de.po
Normal file
81303
erpnext/locale/de.po
Normal file
File diff suppressed because it is too large
Load Diff
81303
erpnext/locale/es.po
Normal file
81303
erpnext/locale/es.po
Normal file
File diff suppressed because it is too large
Load Diff
81303
erpnext/locale/fi.po
Normal file
81303
erpnext/locale/fi.po
Normal file
File diff suppressed because it is too large
Load Diff
81303
erpnext/locale/fr.po
Normal file
81303
erpnext/locale/fr.po
Normal file
File diff suppressed because it is too large
Load Diff
81303
erpnext/locale/id.po
Normal file
81303
erpnext/locale/id.po
Normal file
File diff suppressed because it is too large
Load Diff
81303
erpnext/locale/it.po
Normal file
81303
erpnext/locale/it.po
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user