Compare commits
178 Commits
skip_enque
...
v13.10.2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
210fc4481a | ||
|
|
4f3e2240b8 | ||
|
|
ede188d138 | ||
|
|
f28cb55d0f | ||
|
|
adb07ebe09 | ||
|
|
2565b1fb33 | ||
|
|
96aee284d2 | ||
|
|
6440e4f970 | ||
|
|
702eea3b54 | ||
|
|
e362e23941 | ||
|
|
68482b223f | ||
|
|
c31bf155f0 | ||
|
|
d641dd68d4 | ||
|
|
155df936cd | ||
|
|
0c4f29edcf | ||
|
|
f20913fb69 | ||
|
|
bafc9ddde4 | ||
|
|
d9b9888ad5 | ||
|
|
03dcecff67 | ||
|
|
f71ff830ef | ||
|
|
d88346c6cd | ||
|
|
0767d2dac2 | ||
|
|
64fab5b7d1 | ||
|
|
7c31e1f8bf | ||
|
|
409cc95b7b | ||
|
|
327be1cd9d | ||
|
|
6609321399 | ||
|
|
7f27586cbe | ||
|
|
4837d8872e | ||
|
|
014df08e7b | ||
|
|
09fb90b8ac | ||
|
|
7b9a23eb7a | ||
|
|
0fe6995816 | ||
|
|
f8ec0b6a86 | ||
|
|
4c3034ad79 | ||
|
|
1be810479c | ||
|
|
d97a87e28d | ||
|
|
7ac4916191 | ||
|
|
4eb7c2a011 | ||
|
|
fcb17f047d | ||
|
|
688fe4192c | ||
|
|
2b875bbf52 | ||
|
|
1810b73113 | ||
|
|
2ea108ae92 | ||
|
|
2f71b740fd | ||
|
|
5b411dc1f6 | ||
|
|
f13ae4de0b | ||
|
|
b3ffa0eb57 | ||
|
|
8220117500 | ||
|
|
74073ddc85 | ||
|
|
8474961b79 | ||
|
|
487952a04e | ||
|
|
c7508a034a | ||
|
|
38898d33c6 | ||
|
|
0bf9d1b29f | ||
|
|
a7cdba24bc | ||
|
|
f84740e6e4 | ||
|
|
24b2a31581 | ||
|
|
f7e0edecc9 | ||
|
|
d55d200b47 | ||
|
|
c8092b7e7a | ||
|
|
1604b6cc63 | ||
|
|
27fad29ad6 | ||
|
|
0476accf26 | ||
|
|
925a4a28e2 | ||
|
|
ac0800511d | ||
|
|
d360819384 | ||
|
|
ff6cda8547 | ||
|
|
60c06d3194 | ||
|
|
5320f3e5ea | ||
|
|
cc7ed1573a | ||
|
|
b932b3f252 | ||
|
|
1e3a6a8a98 | ||
|
|
0a23328151 | ||
|
|
f08d7410be | ||
|
|
352157c9fc | ||
|
|
85f582b145 | ||
|
|
3d047b83fd | ||
|
|
6814509f07 | ||
|
|
dbca11071e | ||
|
|
dd688db54c | ||
|
|
be57dee57a | ||
|
|
67dbb2bd7f | ||
|
|
571178ffbe | ||
|
|
9542da80c5 | ||
|
|
593ab98575 | ||
|
|
43813875ea | ||
|
|
d0e393a4cc | ||
|
|
05c7905fa3 | ||
|
|
3f05d928a3 | ||
|
|
2fd823ffb6 | ||
|
|
52570cc1f9 | ||
|
|
5c6f6c16d6 | ||
|
|
ee9b6d158a | ||
|
|
c192e9457e | ||
|
|
74af3be968 | ||
|
|
02a23bae58 | ||
|
|
df32fe3d49 | ||
|
|
92b3743c54 | ||
|
|
b42c23cad6 | ||
|
|
23c713cc9b | ||
|
|
f0d3a074e0 | ||
|
|
9bb69e711a | ||
|
|
e536f6d13f | ||
|
|
f37747da25 | ||
|
|
cada9b679a | ||
|
|
25b705e2ad | ||
|
|
ecbb59a1ce | ||
|
|
9f79415186 | ||
|
|
87326dd489 | ||
|
|
8ea5782c69 | ||
|
|
6a35d580e4 | ||
|
|
77ad668a6f | ||
|
|
5e1ed2d7eb | ||
|
|
36f18935d3 | ||
|
|
01a538123b | ||
|
|
44919ac807 | ||
|
|
e555e8cf05 | ||
|
|
0f15ded0cd | ||
|
|
46372fe5cd | ||
|
|
6950844a74 | ||
|
|
8cd3ffc84d | ||
|
|
9c1d739946 | ||
|
|
5e17b82779 | ||
|
|
9c401e75bb | ||
|
|
c8449702b4 | ||
|
|
90818d57f1 | ||
|
|
0a5dff1e1f | ||
|
|
333d962ac2 | ||
|
|
2730f51ca9 | ||
|
|
ace8cf965d | ||
|
|
e7143d8711 | ||
|
|
94f2c41475 | ||
|
|
48a11591cc | ||
|
|
6e921b1ccc | ||
|
|
133486a5c7 | ||
|
|
94030e08f1 | ||
|
|
663e550824 | ||
|
|
d076ba5c94 | ||
|
|
3c9b8dce21 | ||
|
|
64dfbfaecb | ||
|
|
1351d6e3be | ||
|
|
2aa0daf47b | ||
|
|
5fddd27cab | ||
|
|
321dd33015 | ||
|
|
560483eb98 | ||
|
|
6aed9e26ac | ||
|
|
20a5795d67 | ||
|
|
434692ad34 | ||
|
|
7881536e09 | ||
|
|
cb583a349f | ||
|
|
a2a5800b23 | ||
|
|
ffb174c489 | ||
|
|
adfa11d449 | ||
|
|
9209c1f91a | ||
|
|
262c1823a5 | ||
|
|
703b081172 | ||
|
|
f3ae956eae | ||
|
|
6691d2ca54 | ||
|
|
fd325a123c | ||
|
|
a16ab92e00 | ||
|
|
cf9734f98a | ||
|
|
42b340cc66 | ||
|
|
c26f95e3b2 | ||
|
|
8da3a5cdd4 | ||
|
|
c6c2773e02 | ||
|
|
950521299a | ||
|
|
c2b5b0edee | ||
|
|
75f23aed1c | ||
|
|
c02e42ff84 | ||
|
|
a21f76f2a1 | ||
|
|
449c58d809 | ||
|
|
a1a4e8d616 | ||
|
|
e60a349432 | ||
|
|
872cd1cac8 | ||
|
|
d160e73c03 | ||
|
|
905aebc310 | ||
|
|
47c2317b1a |
@@ -10,3 +10,6 @@
|
||||
|
||||
# This commit just changes spaces to tabs for indentation in some files
|
||||
5f473611bd6ed57703716244a054d3fb5ba9cd23
|
||||
|
||||
# Whitespace trimming throughout codebase
|
||||
9bb69e711a5da43aaf8c8ecb5601aeffd89dbe5a
|
||||
|
||||
8
.github/helper/documentation.py
vendored
8
.github/helper/documentation.py
vendored
@@ -32,11 +32,15 @@ if __name__ == "__main__":
|
||||
|
||||
if response.ok:
|
||||
payload = response.json()
|
||||
title = payload.get("title", "").lower()
|
||||
title = payload.get("title", "").lower().strip()
|
||||
head_sha = payload.get("head", {}).get("sha")
|
||||
body = payload.get("body", "").lower()
|
||||
|
||||
if title.startswith("feat") and head_sha and "no-docs" not in body:
|
||||
if (title.startswith("feat")
|
||||
and head_sha
|
||||
and "no-docs" not in body
|
||||
and "backport" not in body
|
||||
):
|
||||
if docs_link_exists(body):
|
||||
print("Documentation Link Found. You're Awesome! 🎉")
|
||||
|
||||
|
||||
7
.github/helper/install.sh
vendored
7
.github/helper/install.sh
vendored
@@ -4,11 +4,7 @@ set -e
|
||||
|
||||
cd ~ || exit
|
||||
|
||||
sudo apt-get install redis-server
|
||||
|
||||
sudo apt install nodejs
|
||||
|
||||
sudo apt install npm
|
||||
sudo apt-get install redis-server libcups2-dev
|
||||
|
||||
pip install frappe-bench
|
||||
|
||||
@@ -32,7 +28,6 @@ wget -O /tmp/wkhtmltox.tar.xz https://github.com/frappe/wkhtmltopdf/raw/master/w
|
||||
tar -xf /tmp/wkhtmltox.tar.xz -C /tmp
|
||||
sudo mv /tmp/wkhtmltox/bin/wkhtmltopdf /usr/local/bin/wkhtmltopdf
|
||||
sudo chmod o+x /usr/local/bin/wkhtmltopdf
|
||||
sudo apt-get install libcups2-dev
|
||||
|
||||
cd ~/frappe-bench || exit
|
||||
|
||||
|
||||
14
.github/workflows/patch.yml
vendored
14
.github/workflows/patch.yml
vendored
@@ -1,6 +1,12 @@
|
||||
name: Patch
|
||||
|
||||
on: [pull_request, workflow_dispatch]
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.js'
|
||||
- '**.md'
|
||||
workflow_dispatch:
|
||||
|
||||
|
||||
jobs:
|
||||
test:
|
||||
@@ -26,6 +32,12 @@ jobs:
|
||||
with:
|
||||
python-version: 3.6
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 12
|
||||
check-latest: true
|
||||
|
||||
- name: Add to Hosts
|
||||
run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts
|
||||
|
||||
|
||||
18
.github/workflows/server-tests.yml
vendored
18
.github/workflows/server-tests.yml
vendored
@@ -1,6 +1,16 @@
|
||||
name: Server
|
||||
|
||||
on: [pull_request, workflow_dispatch]
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.js'
|
||||
- '**.md'
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches: [ develop ]
|
||||
paths-ignore:
|
||||
- '**.js'
|
||||
- '**.md'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
@@ -32,6 +42,12 @@ jobs:
|
||||
with:
|
||||
python-version: 3.7
|
||||
|
||||
- name: Setup Node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 12
|
||||
check-latest: true
|
||||
|
||||
- name: Add to Hosts
|
||||
run: echo "127.0.0.1 test_site" | sudo tee -a /etc/hosts
|
||||
|
||||
|
||||
2
.github/workflows/ui-tests.yml
vendored
2
.github/workflows/ui-tests.yml
vendored
@@ -2,6 +2,8 @@ name: UI
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '**.md'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -5,7 +5,7 @@ import frappe
|
||||
from erpnext.hooks import regional_overrides
|
||||
from frappe.utils import getdate
|
||||
|
||||
__version__ = '13.9.0'
|
||||
__version__ = '13.10.2'
|
||||
|
||||
def get_default_company(user=None):
|
||||
'''Get default company for user'''
|
||||
|
||||
@@ -450,5 +450,3 @@ def get_deferred_booking_accounts(doctype, voucher_detail_no, dr_or_cr):
|
||||
return debit_account
|
||||
else:
|
||||
return credit_account
|
||||
|
||||
|
||||
|
||||
@@ -113,5 +113,3 @@ def disable_dimension():
|
||||
dimension2 = frappe.get_doc("Accounting Dimension", "Location")
|
||||
dimension2.disabled = 1
|
||||
dimension2.save()
|
||||
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
"delete_linked_ledger_entries",
|
||||
"book_asset_depreciation_entry_automatically",
|
||||
"unlink_advance_payment_on_cancelation_of_order",
|
||||
"enable_common_party_accounting",
|
||||
"post_change_gl_entries",
|
||||
"enable_discount_accounting",
|
||||
"tax_settings_section",
|
||||
@@ -269,6 +270,12 @@
|
||||
"fieldname": "enable_discount_accounting",
|
||||
"fieldtype": "Check",
|
||||
"label": "Enable Discount Accounting"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "enable_common_party_accounting",
|
||||
"fieldtype": "Check",
|
||||
"label": "Enable Common Party Accounting"
|
||||
}
|
||||
],
|
||||
"icon": "icon-cog",
|
||||
@@ -276,7 +283,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2021-07-12 18:54:29.084958",
|
||||
"modified": "2021-08-19 11:17:38.788054",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Accounts Settings",
|
||||
|
||||
@@ -22,6 +22,10 @@ class BankTransaction(StatusUpdater):
|
||||
self.clear_linked_payment_entries()
|
||||
self.set_status(update=True)
|
||||
|
||||
def on_cancel(self):
|
||||
self.clear_linked_payment_entries(for_cancel=True)
|
||||
self.set_status(update=True)
|
||||
|
||||
def update_allocations(self):
|
||||
if self.payment_entries:
|
||||
allocated_amount = reduce(lambda x, y: flt(x) + flt(y), [x.allocated_amount for x in self.payment_entries])
|
||||
@@ -42,20 +46,29 @@ class BankTransaction(StatusUpdater):
|
||||
|
||||
self.reload()
|
||||
|
||||
def clear_linked_payment_entries(self):
|
||||
def clear_linked_payment_entries(self, for_cancel=False):
|
||||
for payment_entry in self.payment_entries:
|
||||
if payment_entry.payment_document in ["Payment Entry", "Journal Entry", "Purchase Invoice", "Expense Claim"]:
|
||||
self.clear_simple_entry(payment_entry)
|
||||
self.clear_simple_entry(payment_entry, for_cancel=for_cancel)
|
||||
|
||||
elif payment_entry.payment_document == "Sales Invoice":
|
||||
self.clear_sales_invoice(payment_entry)
|
||||
self.clear_sales_invoice(payment_entry, for_cancel=for_cancel)
|
||||
|
||||
def clear_simple_entry(self, payment_entry):
|
||||
frappe.db.set_value(payment_entry.payment_document, payment_entry.payment_entry, "clearance_date", self.date)
|
||||
def clear_simple_entry(self, payment_entry, for_cancel=False):
|
||||
clearance_date = self.date if not for_cancel else None
|
||||
frappe.db.set_value(
|
||||
payment_entry.payment_document, payment_entry.payment_entry,
|
||||
"clearance_date", clearance_date)
|
||||
|
||||
def clear_sales_invoice(self, payment_entry):
|
||||
frappe.db.set_value("Sales Invoice Payment", dict(parenttype=payment_entry.payment_document,
|
||||
parent=payment_entry.payment_entry), "clearance_date", self.date)
|
||||
def clear_sales_invoice(self, payment_entry, for_cancel=False):
|
||||
clearance_date = self.date if not for_cancel else None
|
||||
frappe.db.set_value(
|
||||
"Sales Invoice Payment",
|
||||
dict(
|
||||
parenttype=payment_entry.payment_document,
|
||||
parent=payment_entry.payment_entry
|
||||
),
|
||||
"clearance_date", clearance_date)
|
||||
|
||||
def get_total_allocated_amount(payment_entry):
|
||||
return frappe.db.sql("""
|
||||
@@ -105,4 +118,3 @@ def unclear_reference_payment(doctype, docname):
|
||||
frappe.db.set_value(doc.payment_document, doc.payment_entry, "clearance_date", None)
|
||||
|
||||
return doc.payment_entry
|
||||
|
||||
|
||||
@@ -4,10 +4,12 @@
|
||||
frappe.listview_settings['Bank Transaction'] = {
|
||||
add_fields: ["unallocated_amount"],
|
||||
get_indicator: function(doc) {
|
||||
if(flt(doc.unallocated_amount)>0) {
|
||||
return [__("Unreconciled"), "orange", "unallocated_amount,>,0"];
|
||||
if(doc.docstatus == 2) {
|
||||
return [__("Cancelled"), "red", "docstatus,=,2"];
|
||||
} else if(flt(doc.unallocated_amount)<=0) {
|
||||
return [__("Reconciled"), "green", "unallocated_amount,=,0"];
|
||||
} else if(flt(doc.unallocated_amount)>0) {
|
||||
return [__("Unreconciled"), "orange", "unallocated_amount,>,0"];
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -25,6 +25,7 @@ class TestBankTransaction(unittest.TestCase):
|
||||
def tearDownClass(cls):
|
||||
for bt in frappe.get_all("Bank Transaction"):
|
||||
doc = frappe.get_doc("Bank Transaction", bt.name)
|
||||
if doc.docstatus == 1:
|
||||
doc.cancel()
|
||||
doc.delete()
|
||||
|
||||
@@ -57,6 +58,12 @@ class TestBankTransaction(unittest.TestCase):
|
||||
clearance_date = frappe.db.get_value("Payment Entry", payment.name, "clearance_date")
|
||||
self.assertTrue(clearance_date is not None)
|
||||
|
||||
bank_transaction.reload()
|
||||
bank_transaction.cancel()
|
||||
|
||||
clearance_date = frappe.db.get_value("Payment Entry", payment.name, "clearance_date")
|
||||
self.assertFalse(clearance_date)
|
||||
|
||||
# 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("Bank Transaction", dict(description="Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07"))
|
||||
|
||||
@@ -18,5 +18,3 @@ class CashFlowMapping(Document):
|
||||
frappe._('You can only select a maximum of one option from the list of check boxes.'),
|
||||
title='Error'
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -62,6 +62,3 @@ def create_cost_center(**args):
|
||||
cc.is_group = args.is_group or 0
|
||||
cc.parent_cost_center = args.parent_cost_center or "_Test Company - _TC"
|
||||
cc.insert()
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -124,6 +124,3 @@ class TestCouponCode(unittest.TestCase):
|
||||
|
||||
so.submit()
|
||||
self.assertEqual(frappe.db.get_value("Coupon Code", "SAVE30", "used"), 1)
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -9,19 +9,8 @@ import frappe
|
||||
import unittest
|
||||
|
||||
class TestFinanceBook(unittest.TestCase):
|
||||
def create_finance_book(self):
|
||||
if not frappe.db.exists("Finance Book", "_Test Finance Book"):
|
||||
finance_book = frappe.get_doc({
|
||||
"doctype": "Finance Book",
|
||||
"finance_book_name": "_Test Finance Book"
|
||||
}).insert()
|
||||
else:
|
||||
finance_book = frappe.get_doc("Finance Book", "_Test Finance Book")
|
||||
|
||||
return finance_book
|
||||
|
||||
def test_finance_book(self):
|
||||
finance_book = self.create_finance_book()
|
||||
finance_book = create_finance_book()
|
||||
|
||||
# create jv entry
|
||||
jv = make_journal_entry("_Test Bank - _TC",
|
||||
@@ -41,3 +30,14 @@ class TestFinanceBook(unittest.TestCase):
|
||||
|
||||
for gl_entry in gl_entries:
|
||||
self.assertEqual(gl_entry.finance_book, finance_book.name)
|
||||
|
||||
def create_finance_book():
|
||||
if not frappe.db.exists("Finance Book", "_Test Finance Book"):
|
||||
finance_book = frappe.get_doc({
|
||||
"doctype": "Finance Book",
|
||||
"finance_book_name": "_Test Finance Book"
|
||||
}).insert()
|
||||
else:
|
||||
finance_book = frappe.get_doc("Finance Book", "_Test Finance Book")
|
||||
|
||||
return finance_book
|
||||
@@ -58,8 +58,8 @@ class GLEntry(Document):
|
||||
if not self.get(k):
|
||||
frappe.throw(_("{0} is required").format(_(self.meta.get_label(k))))
|
||||
|
||||
account_type = frappe.get_cached_value("Account", self.account, "account_type")
|
||||
if not (self.party_type and self.party):
|
||||
account_type = frappe.get_cached_value("Account", self.account, "account_type")
|
||||
if account_type == "Receivable":
|
||||
frappe.throw(_("{0} {1}: Customer is required against Receivable account {2}")
|
||||
.format(self.voucher_type, self.voucher_no, self.account))
|
||||
@@ -73,8 +73,12 @@ class GLEntry(Document):
|
||||
.format(self.voucher_type, self.voucher_no, self.account))
|
||||
|
||||
def pl_must_have_cost_center(self):
|
||||
"""Validate that profit and loss type account GL entries have a cost center."""
|
||||
|
||||
if self.cost_center or self.voucher_type == 'Period Closing Voucher':
|
||||
return
|
||||
|
||||
if frappe.get_cached_value("Account", self.account, "report_type") == "Profit and Loss":
|
||||
if not self.cost_center and self.voucher_type != 'Period Closing Voucher':
|
||||
msg = _("{0} {1}: Cost Center is required for 'Profit and Loss' account {2}.").format(
|
||||
self.voucher_type, self.voucher_no, self.account)
|
||||
msg += " "
|
||||
|
||||
@@ -39,4 +39,3 @@ class ModeofPayment(Document):
|
||||
message = "POS Profile " + frappe.bold(", ".join(pos_profiles)) + " contains \
|
||||
Mode of Payment " + frappe.bold(str(self.name)) + ". Please remove them to disable this mode."
|
||||
frappe.throw(_(message), title="Not Allowed")
|
||||
|
||||
|
||||
@@ -240,5 +240,3 @@ def get_temporary_opening_account(company=None):
|
||||
frappe.throw(_("Please add a Temporary Opening account in Chart of Accounts"))
|
||||
|
||||
return accounts[0].name
|
||||
|
||||
|
||||
|
||||
0
erpnext/accounts/doctype/party_link/__init__.py
Normal file
0
erpnext/accounts/doctype/party_link/__init__.py
Normal file
33
erpnext/accounts/doctype/party_link/party_link.js
Normal file
33
erpnext/accounts/doctype/party_link/party_link.js
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Party Link', {
|
||||
refresh: function(frm) {
|
||||
frm.set_query('primary_role', () => {
|
||||
return {
|
||||
filters: {
|
||||
name: ['in', ['Customer', 'Supplier']]
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
frm.set_query('secondary_role', () => {
|
||||
let party_types = Object.keys(frappe.boot.party_account_types)
|
||||
.filter(p => p != frm.doc.primary_role);
|
||||
return {
|
||||
filters: {
|
||||
name: ['in', party_types]
|
||||
}
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
primary_role(frm) {
|
||||
frm.set_value('primary_party', '');
|
||||
frm.set_value('secondary_role', '');
|
||||
},
|
||||
|
||||
secondary_role(frm) {
|
||||
frm.set_value('secondary_party', '');
|
||||
}
|
||||
});
|
||||
102
erpnext/accounts/doctype/party_link/party_link.json
Normal file
102
erpnext/accounts/doctype/party_link/party_link.json
Normal file
@@ -0,0 +1,102 @@
|
||||
{
|
||||
"actions": [],
|
||||
"autoname": "ACC-PT-LNK-.###.",
|
||||
"creation": "2021-08-18 21:06:53.027695",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"primary_role",
|
||||
"secondary_role",
|
||||
"column_break_2",
|
||||
"primary_party",
|
||||
"secondary_party"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "primary_role",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Primary Role",
|
||||
"options": "DocType",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_2",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"depends_on": "primary_role",
|
||||
"fieldname": "secondary_role",
|
||||
"fieldtype": "Link",
|
||||
"label": "Secondary Role",
|
||||
"mandatory_depends_on": "primary_role",
|
||||
"options": "DocType"
|
||||
},
|
||||
{
|
||||
"depends_on": "primary_role",
|
||||
"fieldname": "primary_party",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"label": "Primary Party",
|
||||
"mandatory_depends_on": "primary_role",
|
||||
"options": "primary_role"
|
||||
},
|
||||
{
|
||||
"depends_on": "secondary_role",
|
||||
"fieldname": "secondary_party",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"label": "Secondary Party",
|
||||
"mandatory_depends_on": "secondary_role",
|
||||
"options": "secondary_role"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2021-08-25 20:08:56.761150",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Party Link",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Accounts Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Accounts User",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"title_field": "primary_party",
|
||||
"track_changes": 1
|
||||
}
|
||||
26
erpnext/accounts/doctype/party_link/party_link.py
Normal file
26
erpnext/accounts/doctype/party_link/party_link.py
Normal file
@@ -0,0 +1,26 @@
|
||||
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
|
||||
class PartyLink(Document):
|
||||
def validate(self):
|
||||
if self.primary_role not in ['Customer', 'Supplier']:
|
||||
frappe.throw(_("Allowed primary roles are 'Customer' and 'Supplier'. Please select one of these roles only."),
|
||||
title=_("Invalid Primary Role"))
|
||||
|
||||
existing_party_link = frappe.get_all('Party Link', {
|
||||
'primary_party': self.secondary_party
|
||||
}, pluck="primary_role")
|
||||
if existing_party_link:
|
||||
frappe.throw(_('{} {} is already linked with another {}')
|
||||
.format(self.secondary_role, self.secondary_party, existing_party_link[0]))
|
||||
|
||||
existing_party_link = frappe.get_all('Party Link', {
|
||||
'secondary_party': self.primary_party
|
||||
}, pluck="primary_role")
|
||||
if existing_party_link:
|
||||
frappe.throw(_('{} {} is already linked with another {}')
|
||||
.format(self.primary_role, self.primary_party, existing_party_link[0]))
|
||||
8
erpnext/accounts/doctype/party_link/test_party_link.py
Normal file
8
erpnext/accounts/doctype/party_link/test_party_link.py
Normal file
@@ -0,0 +1,8 @@
|
||||
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
# See license.txt
|
||||
|
||||
# import frappe
|
||||
import unittest
|
||||
|
||||
class TestPartyLink(unittest.TestCase):
|
||||
pass
|
||||
@@ -533,8 +533,8 @@ frappe.ui.form.on('Payment Entry', {
|
||||
source_exchange_rate: function(frm) {
|
||||
if (frm.doc.paid_amount) {
|
||||
frm.set_value("base_paid_amount", flt(frm.doc.paid_amount) * flt(frm.doc.source_exchange_rate));
|
||||
if(!frm.set_paid_amount_based_on_received_amount &&
|
||||
(frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency)) {
|
||||
// target exchange rate should always be same as source if both account currencies are same
|
||||
if(frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency) {
|
||||
frm.set_value("target_exchange_rate", frm.doc.source_exchange_rate);
|
||||
frm.set_value("base_received_amount", frm.doc.base_paid_amount);
|
||||
}
|
||||
@@ -872,7 +872,7 @@ frappe.ui.form.on('Payment Entry', {
|
||||
&& 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 + frm.doc.base_total_taxes_and_charges
|
||||
+ frm.doc.base_total_allocated_amount) / frm.doc.source_exchange_rate;
|
||||
- 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
|
||||
&& frm.doc.total_allocated_amount < frm.doc.received_amount + (total_deductions / frm.doc.target_exchange_rate)) {
|
||||
|
||||
@@ -55,14 +55,17 @@ class PaymentEntry(AccountsController):
|
||||
self.validate_mandatory()
|
||||
self.validate_reference_documents()
|
||||
self.set_tax_withholding()
|
||||
self.apply_taxes()
|
||||
self.set_amounts()
|
||||
self.validate_amounts()
|
||||
self.apply_taxes()
|
||||
self.set_amounts_after_tax()
|
||||
self.clear_unallocated_reference_document_rows()
|
||||
self.validate_payment_against_negative_invoice()
|
||||
self.validate_transaction_reference()
|
||||
self.set_title()
|
||||
self.set_remarks()
|
||||
self.validate_duplicate_entry()
|
||||
self.validate_payment_type_with_outstanding()
|
||||
self.validate_allocated_amount()
|
||||
self.validate_paid_invoices()
|
||||
self.ensure_supplier_is_not_blocked()
|
||||
@@ -118,6 +121,11 @@ class PaymentEntry(AccountsController):
|
||||
if not self.get(field):
|
||||
self.set(field, bank_data.account)
|
||||
|
||||
def validate_payment_type_with_outstanding(self):
|
||||
total_outstanding = sum(d.allocated_amount for d in self.get('references'))
|
||||
if total_outstanding < 0 and self.party_type == 'Customer' and self.payment_type == 'Receive':
|
||||
frappe.throw(_("Cannot receive from customer against negative outstanding"), title=_("Incorrect Payment Type"))
|
||||
|
||||
def validate_allocated_amount(self):
|
||||
for d in self.get("references"):
|
||||
if (flt(d.allocated_amount))> 0:
|
||||
@@ -236,7 +244,9 @@ class PaymentEntry(AccountsController):
|
||||
self.company_currency, self.posting_date)
|
||||
|
||||
def set_target_exchange_rate(self, ref_doc=None):
|
||||
if self.paid_to and not self.target_exchange_rate:
|
||||
if self.paid_from_account_currency == self.paid_to_account_currency:
|
||||
self.target_exchange_rate = self.source_exchange_rate
|
||||
elif self.paid_to and not self.target_exchange_rate:
|
||||
if ref_doc:
|
||||
if self.paid_to_account_currency == ref_doc.currency:
|
||||
self.target_exchange_rate = ref_doc.get("exchange_rate")
|
||||
@@ -468,13 +478,22 @@ class PaymentEntry(AccountsController):
|
||||
def set_amounts(self):
|
||||
self.set_received_amount()
|
||||
self.set_amounts_in_company_currency()
|
||||
self.set_amounts_after_tax()
|
||||
self.set_total_allocated_amount()
|
||||
self.set_unallocated_amount()
|
||||
self.set_difference_amount()
|
||||
|
||||
def validate_amounts(self):
|
||||
self.validate_received_amount()
|
||||
|
||||
def validate_received_amount(self):
|
||||
if self.paid_from_account_currency == self.paid_to_account_currency:
|
||||
if self.paid_amount != self.received_amount:
|
||||
frappe.throw(_("Received Amount should be same as Paid Amount"))
|
||||
|
||||
def set_received_amount(self):
|
||||
self.base_received_amount = self.base_paid_amount
|
||||
if self.paid_from_account_currency == self.paid_to_account_currency:
|
||||
self.received_amount = self.paid_amount
|
||||
|
||||
def set_amounts_after_tax(self):
|
||||
applicable_tax = 0
|
||||
@@ -529,7 +548,7 @@ class PaymentEntry(AccountsController):
|
||||
if self.payment_type == "Receive" \
|
||||
and self.base_total_allocated_amount < self.base_received_amount + total_deductions \
|
||||
and self.total_allocated_amount < self.paid_amount + (total_deductions / self.source_exchange_rate):
|
||||
self.unallocated_amount = (self.received_amount + total_deductions -
|
||||
self.unallocated_amount = (self.base_received_amount + total_deductions -
|
||||
self.base_total_allocated_amount) / self.source_exchange_rate
|
||||
self.unallocated_amount -= included_taxes
|
||||
elif self.payment_type == "Pay" \
|
||||
|
||||
@@ -107,7 +107,7 @@ class TestPaymentEntry(unittest.TestCase):
|
||||
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC")
|
||||
pe.reference_no = "1"
|
||||
pe.reference_date = "2016-01-01"
|
||||
pe.target_exchange_rate = 50
|
||||
pe.source_exchange_rate = 50
|
||||
pe.insert()
|
||||
pe.submit()
|
||||
|
||||
@@ -154,7 +154,7 @@ class TestPaymentEntry(unittest.TestCase):
|
||||
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC")
|
||||
pe.reference_no = "1"
|
||||
pe.reference_date = "2016-01-01"
|
||||
pe.target_exchange_rate = 50
|
||||
pe.source_exchange_rate = 50
|
||||
pe.insert()
|
||||
pe.submit()
|
||||
|
||||
@@ -295,6 +295,34 @@ class TestPaymentEntry(unittest.TestCase):
|
||||
outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"))
|
||||
self.assertEqual(outstanding_amount, 80)
|
||||
|
||||
def test_payment_entry_against_si_usd_to_usd_with_deduction_in_base_currency (self):
|
||||
si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
|
||||
currency="USD", conversion_rate=50, do_not_save=1)
|
||||
|
||||
si.plc_conversion_rate = 50
|
||||
si.save()
|
||||
si.submit()
|
||||
|
||||
pe = get_payment_entry("Sales Invoice", si.name, party_amount=20,
|
||||
bank_account="_Test Bank USD - _TC", bank_amount=900)
|
||||
|
||||
pe.source_exchange_rate = 45.263
|
||||
pe.target_exchange_rate = 45.263
|
||||
pe.reference_no = "1"
|
||||
pe.reference_date = "2016-01-01"
|
||||
|
||||
|
||||
pe.append("deductions", {
|
||||
"account": "_Test Exchange Gain/Loss - _TC",
|
||||
"cost_center": "_Test Cost Center - _TC",
|
||||
"amount": 94.80
|
||||
})
|
||||
|
||||
pe.save()
|
||||
|
||||
self.assertEqual(flt(pe.difference_amount, 2), 0.0)
|
||||
self.assertEqual(flt(pe.unallocated_amount, 2), 0.0)
|
||||
|
||||
def test_payment_entry_retrieves_last_exchange_rate(self):
|
||||
from erpnext.setup.doctype.currency_exchange.test_currency_exchange import test_records, save_new_records
|
||||
|
||||
@@ -463,7 +491,7 @@ class TestPaymentEntry(unittest.TestCase):
|
||||
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC")
|
||||
pe.reference_no = "1"
|
||||
pe.reference_date = "2016-01-01"
|
||||
pe.target_exchange_rate = 55
|
||||
pe.source_exchange_rate = 55
|
||||
|
||||
pe.append("deductions", {
|
||||
"account": "_Test Exchange Gain/Loss - _TC",
|
||||
|
||||
@@ -50,9 +50,13 @@ class PeriodClosingVoucher(AccountsController):
|
||||
.format(pce[0][0], self.posting_date))
|
||||
|
||||
def make_gl_entries(self):
|
||||
gl_entries = []
|
||||
net_pl_balance = 0
|
||||
gl_entries = self.get_gl_entries()
|
||||
if gl_entries:
|
||||
from erpnext.accounts.general_ledger import make_gl_entries
|
||||
make_gl_entries(gl_entries)
|
||||
|
||||
def get_gl_entries(self):
|
||||
gl_entries = []
|
||||
pl_accounts = self.get_pl_balances()
|
||||
|
||||
for acc in pl_accounts:
|
||||
@@ -60,6 +64,7 @@ class PeriodClosingVoucher(AccountsController):
|
||||
gl_entries.append(self.get_gl_dict({
|
||||
"account": acc.account,
|
||||
"cost_center": acc.cost_center,
|
||||
"finance_book": acc.finance_book,
|
||||
"account_currency": acc.account_currency,
|
||||
"debit_in_account_currency": abs(flt(acc.bal_in_account_currency)) if flt(acc.bal_in_account_currency) < 0 else 0,
|
||||
"debit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) < 0 else 0,
|
||||
@@ -67,35 +72,13 @@ class PeriodClosingVoucher(AccountsController):
|
||||
"credit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) > 0 else 0
|
||||
}, item=acc))
|
||||
|
||||
net_pl_balance += flt(acc.bal_in_company_currency)
|
||||
if gl_entries:
|
||||
gle_for_net_pl_bal = self.get_pnl_gl_entry(pl_accounts)
|
||||
gl_entries += gle_for_net_pl_bal
|
||||
|
||||
if net_pl_balance:
|
||||
if self.cost_center_wise_pnl:
|
||||
costcenter_wise_gl_entries = self.get_costcenter_wise_pnl_gl_entries(pl_accounts)
|
||||
gl_entries += costcenter_wise_gl_entries
|
||||
else:
|
||||
gl_entry = self.get_pnl_gl_entry(net_pl_balance)
|
||||
gl_entries.append(gl_entry)
|
||||
return gl_entries
|
||||
|
||||
from erpnext.accounts.general_ledger import make_gl_entries
|
||||
make_gl_entries(gl_entries)
|
||||
|
||||
def get_pnl_gl_entry(self, net_pl_balance):
|
||||
cost_center = frappe.db.get_value("Company", self.company, "cost_center")
|
||||
gl_entry = self.get_gl_dict({
|
||||
"account": self.closing_account_head,
|
||||
"debit_in_account_currency": abs(net_pl_balance) if net_pl_balance > 0 else 0,
|
||||
"debit": abs(net_pl_balance) if net_pl_balance > 0 else 0,
|
||||
"credit_in_account_currency": abs(net_pl_balance) if net_pl_balance < 0 else 0,
|
||||
"credit": abs(net_pl_balance) if net_pl_balance < 0 else 0,
|
||||
"cost_center": cost_center
|
||||
})
|
||||
|
||||
self.update_default_dimensions(gl_entry)
|
||||
|
||||
return gl_entry
|
||||
|
||||
def get_costcenter_wise_pnl_gl_entries(self, pl_accounts):
|
||||
def get_pnl_gl_entry(self, pl_accounts):
|
||||
company_cost_center = frappe.db.get_value("Company", self.company, "cost_center")
|
||||
gl_entries = []
|
||||
|
||||
@@ -104,6 +87,7 @@ class PeriodClosingVoucher(AccountsController):
|
||||
gl_entry = self.get_gl_dict({
|
||||
"account": self.closing_account_head,
|
||||
"cost_center": acc.cost_center or company_cost_center,
|
||||
"finance_book": acc.finance_book,
|
||||
"account_currency": acc.account_currency,
|
||||
"debit_in_account_currency": abs(flt(acc.bal_in_account_currency)) if flt(acc.bal_in_account_currency) > 0 else 0,
|
||||
"debit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) > 0 else 0,
|
||||
@@ -130,7 +114,7 @@ class PeriodClosingVoucher(AccountsController):
|
||||
def get_pl_balances(self):
|
||||
"""Get balance for dimension-wise pl accounts"""
|
||||
|
||||
dimension_fields = ['t1.cost_center']
|
||||
dimension_fields = ['t1.cost_center', 't1.finance_book']
|
||||
|
||||
self.accounting_dimensions = get_accounting_dimensions()
|
||||
for dimension in self.accounting_dimensions:
|
||||
|
||||
@@ -8,6 +8,7 @@ import frappe
|
||||
from frappe.utils import flt, today
|
||||
from erpnext.accounts.utils import get_fiscal_year, now
|
||||
from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry
|
||||
from erpnext.accounts.doctype.finance_book.test_finance_book import create_finance_book
|
||||
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||
|
||||
class TestPeriodClosingVoucher(unittest.TestCase):
|
||||
@@ -118,6 +119,58 @@ class TestPeriodClosingVoucher(unittest.TestCase):
|
||||
|
||||
self.assertTrue(pcv_gle, expected_gle)
|
||||
|
||||
def test_period_closing_with_finance_book_entries(self):
|
||||
frappe.db.sql("delete from `tabGL Entry` where company='Test PCV Company'")
|
||||
|
||||
company = create_company()
|
||||
surplus_account = create_account()
|
||||
cost_center = create_cost_center("Test Cost Center 1")
|
||||
|
||||
create_sales_invoice(
|
||||
company=company,
|
||||
income_account="Sales - TPC",
|
||||
expense_account="Cost of Goods Sold - TPC",
|
||||
cost_center=cost_center,
|
||||
rate=400,
|
||||
debit_to="Debtors - TPC"
|
||||
)
|
||||
jv = make_journal_entry(
|
||||
account1="Cash - TPC",
|
||||
account2="Sales - TPC",
|
||||
amount=400,
|
||||
cost_center=cost_center,
|
||||
posting_date=now()
|
||||
)
|
||||
jv.company = company
|
||||
jv.finance_book = create_finance_book().name
|
||||
jv.save()
|
||||
jv.submit()
|
||||
|
||||
pcv = frappe.get_doc({
|
||||
"transaction_date": today(),
|
||||
"posting_date": today(),
|
||||
"fiscal_year": get_fiscal_year(today())[0],
|
||||
"company": company,
|
||||
"closing_account_head": surplus_account,
|
||||
"remarks": "Test",
|
||||
"doctype": "Period Closing Voucher"
|
||||
})
|
||||
pcv.insert()
|
||||
pcv.submit()
|
||||
|
||||
expected_gle = (
|
||||
(surplus_account, 0.0, 400.0, ''),
|
||||
(surplus_account, 0.0, 400.0, jv.finance_book),
|
||||
('Sales - TPC', 400.0, 0.0, ''),
|
||||
('Sales - TPC', 400.0, 0.0, jv.finance_book)
|
||||
)
|
||||
|
||||
pcv_gle = frappe.db.sql("""
|
||||
select account, debit, credit, finance_book from `tabGL Entry` where voucher_no=%s
|
||||
""", (pcv.name))
|
||||
|
||||
self.assertTrue(pcv_gle, expected_gle)
|
||||
|
||||
def make_period_closing_voucher(self):
|
||||
pcv = frappe.get_doc({
|
||||
"doctype": "Period Closing Voucher",
|
||||
|
||||
@@ -110,17 +110,13 @@ erpnext.selling.POSInvoiceController = erpnext.selling.SellingController.extend(
|
||||
this.frm.refresh_field("base_paid_amount");
|
||||
},
|
||||
|
||||
write_off_outstanding_amount_automatically: function() {
|
||||
write_off_outstanding_amount_automatically() {
|
||||
if (cint(this.frm.doc.write_off_outstanding_amount_automatically)) {
|
||||
frappe.model.round_floats_in(this.frm.doc, ["grand_total", "paid_amount"]);
|
||||
// this will make outstanding amount 0
|
||||
this.frm.set_value("write_off_amount",
|
||||
flt(this.frm.doc.grand_total - this.frm.doc.paid_amount - this.frm.doc.total_advance, precision("write_off_amount"))
|
||||
);
|
||||
this.frm.toggle_enable("write_off_amount", false);
|
||||
|
||||
} else {
|
||||
this.frm.toggle_enable("write_off_amount", true);
|
||||
}
|
||||
|
||||
this.calculate_outstanding_amount(false);
|
||||
|
||||
@@ -99,6 +99,7 @@
|
||||
"loyalty_redemption_account",
|
||||
"loyalty_redemption_cost_center",
|
||||
"section_break_49",
|
||||
"coupon_code",
|
||||
"apply_discount_on",
|
||||
"base_discount_amount",
|
||||
"column_break_51",
|
||||
@@ -595,7 +596,8 @@
|
||||
{
|
||||
"fieldname": "scan_barcode",
|
||||
"fieldtype": "Data",
|
||||
"label": "Scan Barcode"
|
||||
"label": "Scan Barcode",
|
||||
"options": "Barcode"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 1,
|
||||
@@ -1182,7 +1184,8 @@
|
||||
"label": "Write Off Amount",
|
||||
"no_copy": 1,
|
||||
"options": "currency",
|
||||
"print_hide": 1
|
||||
"print_hide": 1,
|
||||
"read_only_depends_on": "eval: doc.write_off_outstanding_amount_automatically"
|
||||
},
|
||||
{
|
||||
"fieldname": "base_write_off_amount",
|
||||
@@ -1548,12 +1551,20 @@
|
||||
"no_copy": 1,
|
||||
"options": "Sales Invoice",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "coupon_code",
|
||||
"fieldname": "coupon_code",
|
||||
"fieldtype": "Link",
|
||||
"label": "Coupon Code",
|
||||
"options": "Coupon Code",
|
||||
"print_hide": 1
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-file-text",
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-07-29 13:37:20.636171",
|
||||
"modified": "2021-08-24 18:19:20.728433",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "POS Invoice",
|
||||
|
||||
@@ -44,6 +44,9 @@ class POSInvoice(SalesInvoice):
|
||||
self.validate_pos()
|
||||
self.validate_payment_amount()
|
||||
self.validate_loyalty_transaction()
|
||||
if self.coupon_code:
|
||||
from erpnext.accounts.doctype.pricing_rule.utils import validate_coupon_code
|
||||
validate_coupon_code(self.coupon_code)
|
||||
|
||||
def on_submit(self):
|
||||
# create the loyalty point ledger entry if the customer is enrolled in any loyalty program
|
||||
@@ -58,6 +61,10 @@ class POSInvoice(SalesInvoice):
|
||||
self.check_phone_payments()
|
||||
self.set_status(update=True)
|
||||
|
||||
if self.coupon_code:
|
||||
from erpnext.accounts.doctype.pricing_rule.utils import update_coupon_code_count
|
||||
update_coupon_code_count(self.coupon_code,'used')
|
||||
|
||||
def before_cancel(self):
|
||||
if self.consolidated_invoice and frappe.db.get_value('Sales Invoice', self.consolidated_invoice, 'docstatus') == 1:
|
||||
pos_closing_entry = frappe.get_all(
|
||||
@@ -84,6 +91,10 @@ class POSInvoice(SalesInvoice):
|
||||
against_psi_doc.delete_loyalty_point_entry()
|
||||
against_psi_doc.make_loyalty_point_entry()
|
||||
|
||||
if self.coupon_code:
|
||||
from erpnext.accounts.doctype.pricing_rule.utils import update_coupon_code_count
|
||||
update_coupon_code_count(self.coupon_code,'cancelled')
|
||||
|
||||
def check_phone_payments(self):
|
||||
for pay in self.payments:
|
||||
if pay.type == "Phone" and pay.amount >= 0:
|
||||
@@ -127,7 +138,7 @@ class POSInvoice(SalesInvoice):
|
||||
.format(item.idx, bold_delivered_serial_nos), title=_("Item Unavailable"))
|
||||
|
||||
def validate_stock_availablility(self):
|
||||
if self.is_return:
|
||||
if self.is_return or self.docstatus != 1:
|
||||
return
|
||||
|
||||
allow_negative_stock = frappe.db.get_single_value('Stock Settings', 'allow_negative_stock')
|
||||
|
||||
@@ -320,7 +320,8 @@ class TestPOSInvoice(unittest.TestCase):
|
||||
pos2.get("items")[0].serial_no = serial_nos[0]
|
||||
pos2.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 1000})
|
||||
|
||||
self.assertRaises(frappe.ValidationError, pos2.insert)
|
||||
pos2.insert()
|
||||
self.assertRaises(frappe.ValidationError, pos2.submit)
|
||||
|
||||
def test_delivered_serialized_item_transaction(self):
|
||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
|
||||
@@ -348,7 +349,8 @@ class TestPOSInvoice(unittest.TestCase):
|
||||
pos2.get("items")[0].serial_no = serial_nos[0]
|
||||
pos2.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 1000})
|
||||
|
||||
self.assertRaises(frappe.ValidationError, pos2.insert)
|
||||
pos2.insert()
|
||||
self.assertRaises(frappe.ValidationError, pos2.submit)
|
||||
|
||||
def test_loyalty_points(self):
|
||||
from erpnext.accounts.doctype.loyalty_program.test_loyalty_program import create_records
|
||||
|
||||
@@ -147,4 +147,3 @@ class TestPOSInvoiceMergeLog(unittest.TestCase):
|
||||
frappe.set_user("Administrator")
|
||||
frappe.db.sql("delete from `tabPOS Profile`")
|
||||
frappe.db.sql("delete from `tabPOS Invoice`")
|
||||
|
||||
|
||||
@@ -198,12 +198,19 @@ def apply_pricing_rule(args, doc=None):
|
||||
set_serial_nos_based_on_fifo = frappe.db.get_single_value("Stock Settings",
|
||||
"automatically_set_serial_nos_based_on_fifo")
|
||||
|
||||
item_code_list = tuple(item.get('item_code') for item in item_list)
|
||||
query_items = frappe.get_all('Item', fields=['item_code','has_serial_no'], filters=[['item_code','in',item_code_list]],as_list=1)
|
||||
serialized_items = dict()
|
||||
for item_code, val in query_items:
|
||||
serialized_items.setdefault(item_code, val)
|
||||
|
||||
for item in item_list:
|
||||
args_copy = copy.deepcopy(args)
|
||||
args_copy.update(item)
|
||||
data = get_pricing_rule_for_item(args_copy, item.get('price_list_rate'), doc=doc)
|
||||
out.append(data)
|
||||
if not item.get("serial_no") and set_serial_nos_based_on_fifo and not args.get('is_return'):
|
||||
|
||||
if serialized_items.get(item.get('item_code')) and not item.get("serial_no") and set_serial_nos_based_on_fifo and not args.get('is_return'):
|
||||
out[0].update(get_serial_no_for_item(args_copy))
|
||||
|
||||
return out
|
||||
|
||||
@@ -26,4 +26,3 @@ QUnit.test("test pricing rule", function(assert) {
|
||||
() => done()
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
@@ -106,7 +106,6 @@
|
||||
"depends_on": "eval:doc.rate_or_discount==\"Rate\"",
|
||||
"fieldname": "rate",
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Rate"
|
||||
},
|
||||
{
|
||||
@@ -170,7 +169,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-03-07 11:56:23.424137",
|
||||
"modified": "2021-08-19 15:49:29.598727",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Promotional Scheme Price Discount",
|
||||
|
||||
@@ -668,8 +668,7 @@
|
||||
"fieldname": "scan_barcode",
|
||||
"fieldtype": "Data",
|
||||
"label": "Scan Barcode",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
"options": "Barcode"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 1,
|
||||
@@ -1715,7 +1714,7 @@
|
||||
"idx": 204,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-08-07 17:53:14.351439",
|
||||
"modified": "2021-08-17 20:16:12.737743",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Invoice",
|
||||
|
||||
@@ -413,6 +413,8 @@ class PurchaseInvoice(BuyingController):
|
||||
self.update_project()
|
||||
update_linked_doc(self.doctype, self.name, self.inter_company_invoice_reference)
|
||||
|
||||
self.process_common_party_accounting()
|
||||
|
||||
def make_gl_entries(self, gl_entries=None, from_repost=False):
|
||||
if not gl_entries:
|
||||
gl_entries = self.get_gl_entries()
|
||||
@@ -519,8 +521,6 @@ class PurchaseInvoice(BuyingController):
|
||||
if d.category in ('Valuation', 'Total and Valuation')
|
||||
and flt(d.base_tax_amount_after_discount_amount)]
|
||||
|
||||
enable_discount_accounting = cint(frappe.db.get_single_value('Accounts Settings', 'enable_discount_accounting'))
|
||||
|
||||
for item in self.get("items"):
|
||||
if flt(item.base_net_amount):
|
||||
account_currency = get_account_currency(item.expense_account)
|
||||
@@ -611,7 +611,7 @@ class PurchaseInvoice(BuyingController):
|
||||
if (not item.enable_deferred_expense or self.is_return) else item.deferred_expense_account)
|
||||
|
||||
if not item.is_fixed_asset:
|
||||
dummy, amount = self.get_amount_and_base_amount(item, enable_discount_accounting)
|
||||
dummy, amount = self.get_amount_and_base_amount(item, self.enable_discount_accounting)
|
||||
else:
|
||||
amount = flt(item.base_net_amount + item.item_tax_amount, item.precision("base_net_amount"))
|
||||
|
||||
@@ -825,10 +825,9 @@ class PurchaseInvoice(BuyingController):
|
||||
def make_tax_gl_entries(self, gl_entries):
|
||||
# tax table gl entries
|
||||
valuation_tax = {}
|
||||
enable_discount_accounting = cint(frappe.db.get_single_value('Accounts Settings', 'enable_discount_accounting'))
|
||||
|
||||
for tax in self.get("taxes"):
|
||||
amount, base_amount = self.get_tax_amounts(tax, enable_discount_accounting)
|
||||
amount, base_amount = self.get_tax_amounts(tax, self.enable_discount_accounting)
|
||||
if tax.category in ("Total", "Valuation and Total") and flt(base_amount):
|
||||
account_currency = get_account_currency(tax.account_head)
|
||||
|
||||
@@ -893,6 +892,13 @@ class PurchaseInvoice(BuyingController):
|
||||
"remarks": self.remarks or "Accounting Entry for Stock"
|
||||
}, item=tax))
|
||||
|
||||
@property
|
||||
def enable_discount_accounting(self):
|
||||
if not hasattr(self, "_enable_discount_accounting"):
|
||||
self._enable_discount_accounting = cint(frappe.db.get_single_value('Accounts Settings', 'enable_discount_accounting'))
|
||||
|
||||
return self._enable_discount_accounting
|
||||
|
||||
def make_internal_transfer_gl_entries(self, gl_entries):
|
||||
if self.is_internal_transfer() and flt(self.base_total_taxes_and_charges):
|
||||
account_currency = get_account_currency(self.unrealized_profit_loss_account)
|
||||
|
||||
@@ -72,4 +72,3 @@ QUnit.test("test purchase invoice", function(assert) {
|
||||
() => done()
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
@@ -502,6 +502,7 @@
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "enable_deferred_expense",
|
||||
"fieldname": "deferred_expense_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Deferred Expense"
|
||||
@@ -861,7 +862,7 @@
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-07-13 02:04:37.787882",
|
||||
"modified": "2021-08-12 20:14:45.506639",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Invoice Item",
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
"cost_center",
|
||||
"dimension_col_break",
|
||||
"section_break_9",
|
||||
"currency",
|
||||
"account_currency",
|
||||
"tax_amount",
|
||||
"tax_amount_after_discount_amount",
|
||||
"total",
|
||||
@@ -208,14 +208,6 @@
|
||||
"fieldname": "dimension_col_break",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fetch_from": "account_head.account_currency",
|
||||
"fieldname": "currency",
|
||||
"fieldtype": "Link",
|
||||
"label": "Account Currency",
|
||||
"options": "Currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval:['Purchase Taxes and Charges Template', 'Payment Entry'].includes(parent.doctype)",
|
||||
@@ -223,12 +215,20 @@
|
||||
"fieldname": "included_in_paid_amount",
|
||||
"fieldtype": "Check",
|
||||
"label": "Considered In Paid Amount"
|
||||
},
|
||||
{
|
||||
"fetch_from": "account_head.account_currency",
|
||||
"fieldname": "account_currency",
|
||||
"fieldtype": "Link",
|
||||
"label": "Account Currency",
|
||||
"options": "Currency",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-06-14 01:43:50.750455",
|
||||
"modified": "2021-08-05 20:04:36.618240",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Taxes and Charges",
|
||||
|
||||
@@ -26,4 +26,3 @@ QUnit.test("test sales taxes and charges template", function(assert) {
|
||||
() => done()
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
@@ -323,17 +323,13 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
this.frm.refresh_fields();
|
||||
},
|
||||
|
||||
write_off_outstanding_amount_automatically: function() {
|
||||
write_off_outstanding_amount_automatically() {
|
||||
if (cint(this.frm.doc.write_off_outstanding_amount_automatically)) {
|
||||
frappe.model.round_floats_in(this.frm.doc, ["grand_total", "paid_amount"]);
|
||||
// this will make outstanding amount 0
|
||||
this.frm.set_value("write_off_amount",
|
||||
flt(this.frm.doc.grand_total - this.frm.doc.paid_amount - this.frm.doc.total_advance, precision("write_off_amount"))
|
||||
);
|
||||
this.frm.toggle_enable("write_off_amount", false);
|
||||
|
||||
} else {
|
||||
this.frm.toggle_enable("write_off_amount", true);
|
||||
}
|
||||
|
||||
this.calculate_outstanding_amount(false);
|
||||
@@ -787,8 +783,6 @@ frappe.ui.form.on('Sales Invoice', {
|
||||
if (frappe.boot.sysdefaults.country == 'India') unhide_field(['c_form_applicable', 'c_form_no']);
|
||||
else hide_field(['c_form_applicable', 'c_form_no']);
|
||||
|
||||
frm.toggle_enable("write_off_amount", !!!cint(doc.write_off_outstanding_amount_automatically));
|
||||
|
||||
frm.refresh_fields();
|
||||
},
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -253,6 +253,8 @@ class SalesInvoice(SellingController):
|
||||
if "Healthcare" in active_domains:
|
||||
manage_invoice_submit_cancel(self, "on_submit")
|
||||
|
||||
self.process_common_party_accounting()
|
||||
|
||||
def validate_pos_return(self):
|
||||
|
||||
if self.is_pos and self.is_return:
|
||||
@@ -478,6 +480,9 @@ class SalesInvoice(SellingController):
|
||||
if cint(self.is_pos) != 1:
|
||||
return
|
||||
|
||||
if not self.account_for_change_amount:
|
||||
self.account_for_change_amount = frappe.get_cached_value('Company', self.company, 'default_cash_account')
|
||||
|
||||
from erpnext.stock.get_item_details import get_pos_profile_item_details, get_pos_profile
|
||||
if not self.pos_profile:
|
||||
pos_profile = get_pos_profile(self.company) or {}
|
||||
@@ -492,9 +497,6 @@ class SalesInvoice(SellingController):
|
||||
if not self.get('payments') and not for_validate:
|
||||
update_multi_mode_option(self, pos)
|
||||
|
||||
if not self.account_for_change_amount:
|
||||
self.account_for_change_amount = frappe.get_cached_value('Company', self.company, 'default_cash_account')
|
||||
|
||||
if pos:
|
||||
if not for_validate:
|
||||
self.tax_category = pos.get("tax_category")
|
||||
@@ -888,10 +890,8 @@ class SalesInvoice(SellingController):
|
||||
)
|
||||
|
||||
def make_tax_gl_entries(self, gl_entries):
|
||||
enable_discount_accounting = cint(frappe.db.get_single_value('Accounts Settings', 'enable_discount_accounting'))
|
||||
|
||||
for tax in self.get("taxes"):
|
||||
amount, base_amount = self.get_tax_amounts(tax, enable_discount_accounting)
|
||||
amount, base_amount = self.get_tax_amounts(tax, self.enable_discount_accounting)
|
||||
|
||||
if flt(tax.base_tax_amount_after_discount_amount):
|
||||
account_currency = get_account_currency(tax.account_head)
|
||||
@@ -922,8 +922,6 @@ class SalesInvoice(SellingController):
|
||||
|
||||
def make_item_gl_entries(self, gl_entries):
|
||||
# income account gl entries
|
||||
enable_discount_accounting = cint(frappe.db.get_single_value('Accounts Settings', 'enable_discount_accounting'))
|
||||
|
||||
for item in self.get("items"):
|
||||
if flt(item.base_net_amount, item.precision("base_net_amount")):
|
||||
if item.is_fixed_asset:
|
||||
@@ -949,7 +947,7 @@ class SalesInvoice(SellingController):
|
||||
income_account = (item.income_account
|
||||
if (not item.enable_deferred_revenue or self.is_return) else item.deferred_revenue_account)
|
||||
|
||||
amount, base_amount = self.get_amount_and_base_amount(item, enable_discount_accounting)
|
||||
amount, base_amount = self.get_amount_and_base_amount(item, self.enable_discount_accounting)
|
||||
|
||||
account_currency = get_account_currency(income_account)
|
||||
gl_entries.append(
|
||||
@@ -970,6 +968,13 @@ class SalesInvoice(SellingController):
|
||||
erpnext.is_perpetual_inventory_enabled(self.company):
|
||||
gl_entries += super(SalesInvoice, self).get_gl_entries()
|
||||
|
||||
@property
|
||||
def enable_discount_accounting(self):
|
||||
if not hasattr(self, "_enable_discount_accounting"):
|
||||
self._enable_discount_accounting = cint(frappe.db.get_single_value('Accounts Settings', 'enable_discount_accounting'))
|
||||
|
||||
return self._enable_discount_accounting
|
||||
|
||||
def set_asset_status(self, asset):
|
||||
if self.is_return:
|
||||
asset.set_status()
|
||||
@@ -1365,7 +1370,7 @@ class SalesInvoice(SellingController):
|
||||
|
||||
discounting_status = None
|
||||
if self.is_discounted:
|
||||
discountng_status = get_discounting_status(self.name)
|
||||
discounting_status = get_discounting_status(self.name)
|
||||
|
||||
if not status:
|
||||
if self.docstatus == 2:
|
||||
@@ -1373,11 +1378,11 @@ class SalesInvoice(SellingController):
|
||||
elif self.docstatus == 1:
|
||||
if self.is_internal_transfer():
|
||||
self.status = 'Internal Transfer'
|
||||
elif outstanding_amount > 0 and due_date < nowdate and self.is_discounted and discountng_status=='Disbursed':
|
||||
elif outstanding_amount > 0 and due_date < nowdate and self.is_discounted and discounting_status=='Disbursed':
|
||||
self.status = "Overdue and Discounted"
|
||||
elif outstanding_amount > 0 and due_date < nowdate:
|
||||
self.status = "Overdue"
|
||||
elif outstanding_amount > 0 and due_date >= nowdate and self.is_discounted and discountng_status=='Disbursed':
|
||||
elif outstanding_amount > 0 and due_date >= nowdate and self.is_discounted and discounting_status=='Disbursed':
|
||||
self.status = "Unpaid and Discounted"
|
||||
elif outstanding_amount > 0 and due_date >= nowdate:
|
||||
self.status = "Unpaid"
|
||||
|
||||
@@ -23,6 +23,7 @@ from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
|
||||
from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
|
||||
from erpnext.stock.utils import get_incoming_rate
|
||||
from erpnext.accounts.utils import PaymentEntryUnlinkError
|
||||
|
||||
class TestSalesInvoice(unittest.TestCase):
|
||||
def make(self):
|
||||
@@ -133,7 +134,7 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
pe.paid_to_account_currency = si.currency
|
||||
pe.source_exchange_rate = 1
|
||||
pe.target_exchange_rate = 1
|
||||
pe.paid_amount = si.grand_total
|
||||
pe.paid_amount = si.outstanding_amount
|
||||
pe.insert()
|
||||
pe.submit()
|
||||
|
||||
@@ -142,6 +143,42 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
self.assertRaises(frappe.LinkExistsError, si.cancel)
|
||||
unlink_payment_on_cancel_of_invoice()
|
||||
|
||||
def test_payment_entry_unlink_against_standalone_credit_note(self):
|
||||
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
|
||||
si1 = create_sales_invoice(rate=1000)
|
||||
si2 = create_sales_invoice(rate=300)
|
||||
si3 = create_sales_invoice(qty=-1, rate=300, is_return=1)
|
||||
|
||||
|
||||
pe = get_payment_entry("Sales Invoice", si1.name, bank_account="_Test Bank - _TC")
|
||||
pe.append('references', {
|
||||
'reference_doctype': 'Sales Invoice',
|
||||
'reference_name': si2.name,
|
||||
'total_amount': si2.grand_total,
|
||||
'outstanding_amount': si2.outstanding_amount,
|
||||
'allocated_amount': si2.outstanding_amount
|
||||
})
|
||||
|
||||
pe.append('references', {
|
||||
'reference_doctype': 'Sales Invoice',
|
||||
'reference_name': si3.name,
|
||||
'total_amount': si3.grand_total,
|
||||
'outstanding_amount': si3.outstanding_amount,
|
||||
'allocated_amount': si3.outstanding_amount
|
||||
})
|
||||
|
||||
pe.reference_no = 'Test001'
|
||||
pe.reference_date = nowdate()
|
||||
pe.save()
|
||||
pe.submit()
|
||||
|
||||
si2.load_from_db()
|
||||
si2.cancel()
|
||||
|
||||
si1.load_from_db()
|
||||
self.assertRaises(PaymentEntryUnlinkError, si1.cancel)
|
||||
|
||||
|
||||
def test_sales_invoice_calculation_export_currency(self):
|
||||
si = frappe.copy_doc(test_records[2])
|
||||
si.currency = "USD"
|
||||
@@ -1070,6 +1107,18 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
self.assertEqual(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"), 1500)
|
||||
|
||||
|
||||
def test_incoming_rate_for_stand_alone_credit_note(self):
|
||||
return_si = create_sales_invoice(is_return=1, update_stock=1, qty=-1, rate=90000, incoming_rate=10,
|
||||
company='_Test Company with perpetual inventory', warehouse='Stores - TCP1', debit_to='Debtors - TCP1',
|
||||
income_account='Sales - TCP1', expense_account='Cost of Goods Sold - TCP1', cost_center='Main - TCP1')
|
||||
|
||||
incoming_rate = frappe.db.get_value('Stock Ledger Entry', {'voucher_no': return_si.name}, 'incoming_rate')
|
||||
debit_amount = frappe.db.get_value('GL Entry',
|
||||
{'voucher_no': return_si.name, 'account': 'Stock In Hand - TCP1'}, 'debit')
|
||||
|
||||
self.assertEqual(debit_amount, 10.0)
|
||||
self.assertEqual(incoming_rate, 10.0)
|
||||
|
||||
def test_discount_on_net_total(self):
|
||||
si = frappe.copy_doc(test_records[2])
|
||||
si.apply_discount_on = "Net Total"
|
||||
@@ -1746,23 +1795,13 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
acc_settings.save()
|
||||
|
||||
def test_inter_company_transaction(self):
|
||||
from erpnext.selling.doctype.customer.test_customer import create_internal_customer
|
||||
|
||||
if not frappe.db.exists("Customer", "_Test Internal Customer"):
|
||||
customer = frappe.get_doc({
|
||||
"customer_group": "_Test Customer Group",
|
||||
"customer_name": "_Test Internal Customer",
|
||||
"customer_type": "Individual",
|
||||
"doctype": "Customer",
|
||||
"territory": "_Test Territory",
|
||||
"is_internal_customer": 1,
|
||||
"represents_company": "_Test Company 1"
|
||||
})
|
||||
|
||||
customer.append("companies", {
|
||||
"company": "Wind Power LLC"
|
||||
})
|
||||
|
||||
customer.insert()
|
||||
create_internal_customer(
|
||||
customer_name="_Test Internal Customer",
|
||||
represents_company="_Test Company 1",
|
||||
allowed_to_interact_with="Wind Power LLC"
|
||||
)
|
||||
|
||||
if not frappe.db.exists("Supplier", "_Test Internal Supplier"):
|
||||
supplier = frappe.get_doc({
|
||||
@@ -1805,8 +1844,43 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
self.assertEqual(target_doc.company, "_Test Company 1")
|
||||
self.assertEqual(target_doc.supplier, "_Test Internal Supplier")
|
||||
|
||||
def test_sle_if_target_warehouse_exists_accidentally(self):
|
||||
"""
|
||||
Check if inward entry exists if Target Warehouse accidentally exists
|
||||
but Customer is not an internal customer.
|
||||
"""
|
||||
se = make_stock_entry(
|
||||
item_code="138-CMS Shoe",
|
||||
target="Finished Goods - _TC",
|
||||
company = "_Test Company",
|
||||
qty=1,
|
||||
basic_rate=500
|
||||
)
|
||||
|
||||
si = frappe.copy_doc(test_records[0])
|
||||
si.update_stock = 1
|
||||
si.set_warehouse = "Finished Goods - _TC"
|
||||
si.set_target_warehouse = "Stores - _TC"
|
||||
si.get("items")[0].warehouse = "Finished Goods - _TC"
|
||||
si.get("items")[0].target_warehouse = "Stores - _TC"
|
||||
si.insert()
|
||||
si.submit()
|
||||
|
||||
sles = frappe.get_all("Stock Ledger Entry", filters={"voucher_no": si.name},
|
||||
fields=["name", "actual_qty"])
|
||||
|
||||
# check if only one SLE for outward entry is created
|
||||
self.assertEqual(len(sles), 1)
|
||||
self.assertEqual(sles[0].actual_qty, -1)
|
||||
|
||||
# tear down
|
||||
si.cancel()
|
||||
se.cancel()
|
||||
|
||||
def test_internal_transfer_gl_entry(self):
|
||||
## Create internal transfer account
|
||||
from erpnext.selling.doctype.customer.test_customer import create_internal_customer
|
||||
|
||||
account = create_account(account_name="Unrealized Profit",
|
||||
parent_account="Current Liabilities - TCP1", company="_Test Company with perpetual inventory")
|
||||
|
||||
@@ -1898,7 +1972,7 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
|
||||
data = get_ewb_data("Sales Invoice", [si.name])
|
||||
|
||||
self.assertEqual(data['version'], '1.0.1118')
|
||||
self.assertEqual(data['version'], '1.0.0421')
|
||||
self.assertEqual(data['billLists'][0]['fromGstin'], '27AAECE4835E1ZR')
|
||||
self.assertEqual(data['billLists'][0]['fromTrdName'], '_Test Company')
|
||||
self.assertEqual(data['billLists'][0]['toTrdName'], '_Test Customer')
|
||||
@@ -2034,6 +2108,50 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
check_gl_entries(self, si.name, expected_gle, add_days(nowdate(), -1))
|
||||
enable_discount_accounting(enable=0)
|
||||
|
||||
def test_sales_invoice_against_supplier(self):
|
||||
from erpnext.accounts.doctype.opening_invoice_creation_tool.test_opening_invoice_creation_tool import make_customer
|
||||
from erpnext.buying.doctype.supplier.test_supplier import create_supplier
|
||||
|
||||
# create a customer
|
||||
customer = make_customer(customer="_Test Common Supplier")
|
||||
# create a supplier
|
||||
supplier = create_supplier(supplier_name="_Test Common Supplier").name
|
||||
|
||||
# create a party link between customer & supplier
|
||||
# set primary role as supplier
|
||||
party_link = frappe.new_doc("Party Link")
|
||||
party_link.primary_role = "Supplier"
|
||||
party_link.primary_party = supplier
|
||||
party_link.secondary_role = "Customer"
|
||||
party_link.secondary_party = customer
|
||||
party_link.save()
|
||||
|
||||
# enable common party accounting
|
||||
frappe.db.set_value('Accounts Settings', None, 'enable_common_party_accounting', 1)
|
||||
|
||||
# create a sales invoice
|
||||
si = create_sales_invoice(customer=customer, parent_cost_center="_Test Cost Center - _TC")
|
||||
|
||||
# check outstanding of sales invoice
|
||||
si.reload()
|
||||
self.assertEqual(si.status, 'Paid')
|
||||
self.assertEqual(flt(si.outstanding_amount), 0.0)
|
||||
|
||||
# check creation of journal entry
|
||||
jv = frappe.get_all('Journal Entry Account', {
|
||||
'account': si.debit_to,
|
||||
'party_type': 'Customer',
|
||||
'party': si.customer,
|
||||
'reference_type': si.doctype,
|
||||
'reference_name': si.name
|
||||
}, pluck='credit_in_account_currency')
|
||||
|
||||
self.assertTrue(jv)
|
||||
self.assertEqual(jv[0], si.grand_total)
|
||||
|
||||
party_link.delete()
|
||||
frappe.db.set_value('Accounts Settings', None, 'enable_common_party_accounting', 0)
|
||||
|
||||
def get_sales_invoice_for_e_invoice():
|
||||
si = make_sales_invoice_for_ewaybill()
|
||||
si.naming_series = 'INV-2020-.#####'
|
||||
@@ -2246,7 +2364,8 @@ def create_sales_invoice(**args):
|
||||
"discount_amount": args.discount_amount or 0,
|
||||
"cost_center": args.cost_center or "_Test Cost Center - _TC",
|
||||
"serial_no": args.serial_no,
|
||||
"conversion_factor": 1
|
||||
"conversion_factor": 1,
|
||||
"incoming_rate": args.incoming_rate or 0
|
||||
})
|
||||
|
||||
if not args.do_not_save:
|
||||
@@ -2343,29 +2462,6 @@ def get_taxes_and_charges():
|
||||
"row_id": 1
|
||||
}]
|
||||
|
||||
def create_internal_customer(customer_name, represents_company, allowed_to_interact_with):
|
||||
if not frappe.db.exists("Customer", customer_name):
|
||||
customer = frappe.get_doc({
|
||||
"customer_group": "_Test Customer Group",
|
||||
"customer_name": customer_name,
|
||||
"customer_type": "Individual",
|
||||
"doctype": "Customer",
|
||||
"territory": "_Test Territory",
|
||||
"is_internal_customer": 1,
|
||||
"represents_company": represents_company
|
||||
})
|
||||
|
||||
customer.append("companies", {
|
||||
"company": allowed_to_interact_with
|
||||
})
|
||||
|
||||
customer.insert()
|
||||
customer_name = customer.name
|
||||
else:
|
||||
customer_name = frappe.db.get_value("Customer", customer_name)
|
||||
|
||||
return customer_name
|
||||
|
||||
def create_internal_supplier(supplier_name, represents_company, allowed_to_interact_with):
|
||||
if not frappe.db.exists("Supplier", supplier_name):
|
||||
supplier = frappe.get_doc({
|
||||
|
||||
@@ -40,4 +40,3 @@ QUnit.test("test sales Invoice", function(assert) {
|
||||
() => done()
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
@@ -33,4 +33,3 @@ QUnit.test("test sales invoice with margin", function(assert) {
|
||||
() => done()
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
@@ -54,4 +54,3 @@ QUnit.test("test sales Invoice with payment", function(assert) {
|
||||
() => done()
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
@@ -49,4 +49,3 @@ QUnit.test("test sales Invoice with payment request", function(assert) {
|
||||
() => done()
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
@@ -42,4 +42,3 @@ QUnit.test("test sales Invoice with serialize item", function(assert) {
|
||||
() => done()
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
@@ -53,7 +53,6 @@
|
||||
"column_break_24",
|
||||
"base_net_rate",
|
||||
"base_net_amount",
|
||||
"incoming_rate",
|
||||
"drop_ship",
|
||||
"delivered_by_supplier",
|
||||
"accounting",
|
||||
@@ -81,6 +80,7 @@
|
||||
"target_warehouse",
|
||||
"quality_inspection",
|
||||
"batch_no",
|
||||
"incoming_rate",
|
||||
"col_break5",
|
||||
"allow_zero_valuation_rate",
|
||||
"serial_no",
|
||||
@@ -474,6 +474,7 @@
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "enable_deferred_revenue",
|
||||
"fieldname": "deferred_revenue",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Deferred Revenue"
|
||||
@@ -807,12 +808,12 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:parent.is_return && parent.update_stock && !parent.return_against",
|
||||
"fieldname": "incoming_rate",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Incoming Rate",
|
||||
"label": "Incoming Rate (Costing)",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.uom != doc.stock_uom",
|
||||
@@ -833,7 +834,7 @@
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-07-05 15:07:22.857128",
|
||||
"modified": "2021-08-19 13:41:53.435827",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice Item",
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
"section_break_8",
|
||||
"rate",
|
||||
"section_break_9",
|
||||
"currency",
|
||||
"account_currency",
|
||||
"tax_amount",
|
||||
"total",
|
||||
"tax_amount_after_discount_amount",
|
||||
@@ -186,14 +186,6 @@
|
||||
"fieldname": "dimension_col_break",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fetch_from": "account_head.account_currency",
|
||||
"fieldname": "currency",
|
||||
"fieldtype": "Link",
|
||||
"label": "Account Currency",
|
||||
"options": "Currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval:['Sales Taxes and Charges Template', 'Payment Entry'].includes(parent.doctype)",
|
||||
@@ -210,13 +202,21 @@
|
||||
"label": "Dont Recompute tax",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "account_head.account_currency",
|
||||
"fieldname": "account_currency",
|
||||
"fieldtype": "Link",
|
||||
"label": "Account Currency",
|
||||
"options": "Currency",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-07-27 12:40:59.051803",
|
||||
"modified": "2021-08-05 20:04:01.726867",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Taxes and Charges",
|
||||
|
||||
@@ -26,4 +26,3 @@ QUnit.test("test sales taxes and charges template", function(assert) {
|
||||
() => done()
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
@@ -34,4 +34,3 @@ QUnit.test("test Shipping Rule", function(assert) {
|
||||
() => done()
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
@@ -34,4 +34,3 @@ QUnit.test("test Shipping Rule", function(assert) {
|
||||
() => done()
|
||||
]);
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"actions": [],
|
||||
"autoname": "account",
|
||||
"creation": "2021-07-08 22:04:24.634967",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"account"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"allow_in_quick_entry": 1,
|
||||
"fieldname": "account",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"in_preview": 1,
|
||||
"label": "Account",
|
||||
"options": "Account"
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2021-07-08 22:35:33.202911",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "South Africa VAT Account",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class SouthAfricaVATAccount(Document):
|
||||
pass
|
||||
@@ -630,5 +630,3 @@ class TestSubscription(unittest.TestCase):
|
||||
|
||||
subscription.process()
|
||||
self.assertEqual(len(subscription.invoices), 1)
|
||||
|
||||
|
||||
|
||||
@@ -241,13 +241,14 @@ def get_tds_amount(ldc, parties, inv, tax_details, fiscal_year_details, tax_dedu
|
||||
tds_amount = 0
|
||||
invoice_filters = {
|
||||
'name': ('in', vouchers),
|
||||
'docstatus': 1
|
||||
'docstatus': 1,
|
||||
'apply_tds': 1
|
||||
}
|
||||
|
||||
field = 'sum(net_total)'
|
||||
|
||||
if not cint(tax_details.consider_party_ledger_amount):
|
||||
invoice_filters.update({'apply_tds': 1})
|
||||
if cint(tax_details.consider_party_ledger_amount):
|
||||
invoice_filters.pop('apply_tds', None)
|
||||
field = 'sum(grand_total)'
|
||||
|
||||
supp_credit_amt = frappe.db.get_value('Purchase Invoice', invoice_filters, field) or 0.0
|
||||
|
||||
@@ -145,6 +145,36 @@ class TestTaxWithholdingCategory(unittest.TestCase):
|
||||
for d in invoices:
|
||||
d.cancel()
|
||||
|
||||
def test_tds_calculation_on_net_total(self):
|
||||
frappe.db.set_value("Supplier", "Test TDS Supplier4", "tax_withholding_category", "Cumulative Threshold TDS")
|
||||
invoices = []
|
||||
|
||||
pi = create_purchase_invoice(supplier = "Test TDS Supplier4", rate = 20000, do_not_save=True)
|
||||
pi.append('taxes', {
|
||||
"category": "Total",
|
||||
"charge_type": "Actual",
|
||||
"account_head": '_Test Account VAT - _TC',
|
||||
"cost_center": 'Main - _TC',
|
||||
"tax_amount": 1000,
|
||||
"description": "Test",
|
||||
"add_deduct_tax": "Add"
|
||||
|
||||
})
|
||||
pi.save()
|
||||
pi.submit()
|
||||
invoices.append(pi)
|
||||
|
||||
# Second Invoice will apply TDS checked
|
||||
pi1 = create_purchase_invoice(supplier = "Test TDS Supplier4", rate = 20000)
|
||||
pi1.submit()
|
||||
invoices.append(pi1)
|
||||
|
||||
self.assertEqual(pi1.taxes[0].tax_amount, 4000)
|
||||
|
||||
#delete invoices to avoid clashing
|
||||
for d in invoices:
|
||||
d.cancel()
|
||||
|
||||
def cancel_invoices():
|
||||
purchase_invoices = frappe.get_all("Purchase Invoice", {
|
||||
'supplier': ['in', ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2']],
|
||||
@@ -220,7 +250,7 @@ def create_sales_invoice(**args):
|
||||
|
||||
def create_records():
|
||||
# create a new suppliers
|
||||
for name in ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2', 'Test TDS Supplier3']:
|
||||
for name in ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2', 'Test TDS Supplier3', 'Test TDS Supplier4']:
|
||||
if frappe.db.exists('Supplier', name):
|
||||
continue
|
||||
|
||||
|
||||
@@ -100,8 +100,8 @@ def merge_similar_entries(gl_map, precision=None):
|
||||
return merged_gl_map
|
||||
|
||||
def check_if_in_list(gle, gl_map, dimensions=None):
|
||||
account_head_fieldnames = ['party_type', 'party', 'against_voucher', 'against_voucher_type',
|
||||
'cost_center', 'project', 'voucher_detail_no']
|
||||
account_head_fieldnames = ['voucher_detail_no', 'party', 'against_voucher',
|
||||
'cost_center', 'against_voucher_type', 'party_type', 'project', 'finance_book']
|
||||
|
||||
if dimensions:
|
||||
account_head_fieldnames = account_head_fieldnames + dimensions
|
||||
@@ -110,10 +110,12 @@ def check_if_in_list(gle, gl_map, dimensions=None):
|
||||
same_head = True
|
||||
if e.account != gle.account:
|
||||
same_head = False
|
||||
continue
|
||||
|
||||
for fieldname in account_head_fieldnames:
|
||||
if cstr(e.get(fieldname)) != cstr(gle.get(fieldname)):
|
||||
same_head = False
|
||||
break
|
||||
|
||||
if same_head:
|
||||
return e
|
||||
@@ -143,9 +145,12 @@ def make_entry(args, adv_adj, update_outstanding, from_repost=False):
|
||||
validate_expense_against_budget(args)
|
||||
|
||||
def validate_cwip_accounts(gl_map):
|
||||
cwip_enabled = any(cint(ac.enable_cwip_accounting) for ac in frappe.db.get_all("Asset Category","enable_cwip_accounting"))
|
||||
"""Validate that CWIP account are not used in Journal Entry"""
|
||||
if gl_map and gl_map[0].voucher_type != "Journal Entry":
|
||||
return
|
||||
|
||||
if cwip_enabled and gl_map[0].voucher_type == "Journal Entry":
|
||||
cwip_enabled = any(cint(ac.enable_cwip_accounting) for ac in frappe.db.get_all("Asset Category", "enable_cwip_accounting"))
|
||||
if cwip_enabled:
|
||||
cwip_accounts = [d[0] for d in frappe.db.sql("""select name from tabAccount
|
||||
where account_type = 'Capital Work in Progress' and is_group=0""")]
|
||||
|
||||
|
||||
@@ -286,6 +286,7 @@ def validate_party_gle_currency(party_type, party, company, party_account_curren
|
||||
.format(frappe.bold(party_type), frappe.bold(party), frappe.bold(existing_gle_currency), frappe.bold(company)), InvalidAccountCurrency)
|
||||
|
||||
def validate_party_accounts(doc):
|
||||
|
||||
companies = []
|
||||
|
||||
for account in doc.get("accounts"):
|
||||
@@ -446,6 +447,10 @@ def get_payment_terms_template(party_name, party_type, company=None):
|
||||
return template
|
||||
|
||||
def validate_party_frozen_disabled(party_type, party_name):
|
||||
|
||||
if frappe.flags.ignore_party_validation:
|
||||
return
|
||||
|
||||
if party_type and party_name:
|
||||
if party_type in ("Customer", "Supplier"):
|
||||
party = frappe.get_cached_value(party_type, party_name, ["is_frozen", "disabled"], as_dict=True)
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
"name": "Bank and Cash Payment Voucher",
|
||||
"owner": "Administrator",
|
||||
"print_format_builder": 0,
|
||||
"print_format_type": "Server",
|
||||
"print_format_type": "Jinja",
|
||||
"show_section_headings": 0,
|
||||
"standard": "Yes"
|
||||
}
|
||||
@@ -10,6 +10,6 @@
|
||||
"modified_by": "Administrator",
|
||||
"name": "Cheque Printing Format",
|
||||
"owner": "Administrator",
|
||||
"print_format_type": "Server",
|
||||
"print_format_type": "Jinja",
|
||||
"standard": "Yes"
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
"owner": "Administrator",
|
||||
"parentfield": "__print_formats",
|
||||
"print_format_builder": 0,
|
||||
"print_format_type": "Server",
|
||||
"print_format_type": "Jinja",
|
||||
"show_section_headings": 0,
|
||||
"standard": "Yes"
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
"name": "GST Purchase Invoice",
|
||||
"owner": "Administrator",
|
||||
"print_format_builder": 1,
|
||||
"print_format_type": "Server",
|
||||
"print_format_type": "Jinja",
|
||||
"show_section_headings": 0,
|
||||
"standard": "Yes"
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
"name": "Journal Auditing Voucher",
|
||||
"owner": "Administrator",
|
||||
"print_format_builder": 0,
|
||||
"print_format_type": "Server",
|
||||
"print_format_type": "Jinja",
|
||||
"show_section_headings": 0,
|
||||
"standard": "Yes"
|
||||
}
|
||||
@@ -27,4 +27,3 @@
|
||||
{{ _("Authorized Signatory") }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -12,6 +12,6 @@
|
||||
"name": "Payment Receipt Voucher",
|
||||
"owner": "Administrator",
|
||||
"print_format_builder": 0,
|
||||
"print_format_type": "Server",
|
||||
"print_format_type": "Jinja",
|
||||
"standard": "Yes"
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
"name": "Purchase Auditing Voucher",
|
||||
"owner": "Administrator",
|
||||
"print_format_builder": 0,
|
||||
"print_format_type": "Server",
|
||||
"print_format_type": "Jinja",
|
||||
"show_section_headings": 0,
|
||||
"standard": "Yes"
|
||||
}
|
||||
@@ -16,7 +16,7 @@
|
||||
"name": "Sales Auditing Voucher",
|
||||
"owner": "Administrator",
|
||||
"print_format_builder": 0,
|
||||
"print_format_type": "Server",
|
||||
"print_format_type": "Jinja",
|
||||
"show_section_headings": 0,
|
||||
"standard": "Yes"
|
||||
}
|
||||
@@ -62,8 +62,3 @@ def make_sales_invoice():
|
||||
income_account = 'Sales - _TC2',
|
||||
expense_account = 'Cost of Goods Sold - _TC2',
|
||||
cost_center = 'Main - _TC2')
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -136,4 +136,3 @@ frappe.query_reports["Accounts Payable"] = {
|
||||
}
|
||||
|
||||
erpnext.utils.add_dimensions('Accounts Payable', 9);
|
||||
|
||||
|
||||
@@ -105,4 +105,3 @@ frappe.query_reports["Accounts Payable Summary"] = {
|
||||
}
|
||||
|
||||
erpnext.utils.add_dimensions('Accounts Payable Summary', 9);
|
||||
|
||||
|
||||
@@ -12,4 +12,3 @@ def execute(filters=None):
|
||||
"naming_by": ["Buying Settings", "supp_master_name"],
|
||||
}
|
||||
return AccountsReceivableSummary(filters).run(args)
|
||||
|
||||
|
||||
@@ -200,4 +200,3 @@ frappe.query_reports["Accounts Receivable"] = {
|
||||
}
|
||||
|
||||
erpnext.utils.add_dimensions('Accounts Receivable', 9);
|
||||
|
||||
|
||||
@@ -535,6 +535,8 @@ class ReceivablePayableReport(object):
|
||||
if getdate(entry_date) > getdate(self.filters.report_date):
|
||||
row.range1 = row.range2 = row.range3 = row.range4 = row.range5 = 0.0
|
||||
|
||||
row.total_due = row.range1 + row.range2 + row.range3 + row.range4 + row.range5
|
||||
|
||||
def get_ageing_data(self, entry_date, row):
|
||||
# [0-30, 30-60, 60-90, 90-120, 120-above]
|
||||
row.range1 = row.range2 = row.range3 = row.range4 = row.range5 = 0.0
|
||||
|
||||
@@ -93,4 +93,3 @@ def make_credit_note(docname):
|
||||
cost_center = 'Main - _TC2',
|
||||
is_return = 1,
|
||||
return_against = docname)
|
||||
|
||||
|
||||
@@ -82,6 +82,7 @@ class AccountsReceivableSummary(ReceivablePayableReport):
|
||||
"range3": 0.0,
|
||||
"range4": 0.0,
|
||||
"range5": 0.0,
|
||||
"total_due": 0.0,
|
||||
"sales_person": []
|
||||
}))
|
||||
|
||||
@@ -135,3 +136,6 @@ class AccountsReceivableSummary(ReceivablePayableReport):
|
||||
"{range3}-{range4}".format(range3=cint(self.filters["range3"])+ 1, range4=self.filters["range4"]),
|
||||
"{range4}-{above}".format(range4=cint(self.filters["range4"])+ 1, above=_("Above"))]):
|
||||
self.add_column(label=label, fieldname='range' + str(i+1))
|
||||
|
||||
# Add column for total due amount
|
||||
self.add_column(label="Total Amount Due", fieldname='total_due')
|
||||
|
||||
@@ -92,4 +92,3 @@ frappe.query_reports["Budget Variance Report"] = {
|
||||
erpnext.dimension_filters.forEach((dimension) => {
|
||||
frappe.query_reports["Budget Variance Report"].filters[4].options.push(dimension["document_type"]);
|
||||
});
|
||||
|
||||
|
||||
@@ -399,4 +399,3 @@ def get_chart_data(filters, columns, data):
|
||||
},
|
||||
'type' : 'bar'
|
||||
}
|
||||
|
||||
|
||||
@@ -210,10 +210,10 @@ def get_data(companies, root_type, balance_must_be, fiscal_year, filters=None, i
|
||||
company_currency = get_company_currency(filters)
|
||||
|
||||
if filters.filter_based_on == 'Fiscal Year':
|
||||
start_date = fiscal_year.year_start_date
|
||||
start_date = fiscal_year.year_start_date if filters.report != 'Balance Sheet' else None
|
||||
end_date = fiscal_year.year_end_date
|
||||
else:
|
||||
start_date = filters.period_start_date
|
||||
start_date = filters.period_start_date if filters.report != 'Balance Sheet' else None
|
||||
end_date = filters.period_end_date
|
||||
|
||||
gl_entries_by_account = {}
|
||||
|
||||
@@ -176,4 +176,3 @@ frappe.query_reports["General Ledger"] = {
|
||||
}
|
||||
|
||||
erpnext.utils.add_dimensions('General Ledger', 15)
|
||||
|
||||
|
||||
@@ -78,10 +78,7 @@ def validate_filters(filters, account_details):
|
||||
def validate_party(filters):
|
||||
party_type, party = filters.get("party_type"), filters.get("party")
|
||||
|
||||
if party:
|
||||
if not party_type:
|
||||
frappe.throw(_("To filter based on Party, select Party Type first"))
|
||||
else:
|
||||
if party and party_type:
|
||||
for d in party:
|
||||
if not frappe.db.exists(party_type, d):
|
||||
frappe.throw(_("Invalid {0}: {1}").format(party_type, d))
|
||||
|
||||
@@ -1,16 +1,20 @@
|
||||
{
|
||||
"add_total_row": 1,
|
||||
"add_total_row": 0,
|
||||
"columns": [],
|
||||
"creation": "2013-02-25 17:03:34",
|
||||
"disable_prepared_report": 0,
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"filters": [],
|
||||
"idx": 3,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2020-08-13 11:26:39.112352",
|
||||
"modified": "2021-08-19 18:57:07.468202",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Gross Profit",
|
||||
"owner": "Administrator",
|
||||
"prepared_report": 0,
|
||||
"ref_doctype": "Sales Invoice",
|
||||
"report_name": "Gross Profit",
|
||||
"report_type": "Script Report",
|
||||
|
||||
@@ -41,12 +41,14 @@ def execute(filters=None):
|
||||
|
||||
columns = get_columns(group_wise_columns, filters)
|
||||
|
||||
for src in gross_profit_data.grouped_data:
|
||||
for idx, src in enumerate(gross_profit_data.grouped_data):
|
||||
row = []
|
||||
for col in group_wise_columns.get(scrub(filters.group_by)):
|
||||
row.append(src.get(col))
|
||||
|
||||
row.append(filters.currency)
|
||||
if idx == len(gross_profit_data.grouped_data)-1:
|
||||
row[0] = frappe.bold("Total")
|
||||
data.append(row)
|
||||
|
||||
return columns, data
|
||||
@@ -154,6 +156,15 @@ class GrossProfitGenerator(object):
|
||||
|
||||
def get_average_rate_based_on_group_by(self):
|
||||
# sum buying / selling totals for group
|
||||
self.totals = frappe._dict(
|
||||
qty=0,
|
||||
base_amount=0,
|
||||
buying_amount=0,
|
||||
gross_profit=0,
|
||||
gross_profit_percent=0,
|
||||
base_rate=0,
|
||||
buying_rate=0
|
||||
)
|
||||
for key in list(self.grouped):
|
||||
if self.filters.get("group_by") != "Invoice":
|
||||
for i, row in enumerate(self.grouped[key]):
|
||||
@@ -165,6 +176,7 @@ class GrossProfitGenerator(object):
|
||||
new_row.base_amount += flt(row.base_amount, self.currency_precision)
|
||||
new_row = self.set_average_rate(new_row)
|
||||
self.grouped_data.append(new_row)
|
||||
self.add_to_totals(new_row)
|
||||
else:
|
||||
for i, row in enumerate(self.grouped[key]):
|
||||
if row.parent in self.returned_invoices \
|
||||
@@ -177,15 +189,25 @@ class GrossProfitGenerator(object):
|
||||
if row.qty or row.base_amount:
|
||||
row = self.set_average_rate(row)
|
||||
self.grouped_data.append(row)
|
||||
self.add_to_totals(row)
|
||||
self.set_average_gross_profit(self.totals)
|
||||
self.grouped_data.append(self.totals)
|
||||
|
||||
def set_average_rate(self, new_row):
|
||||
self.set_average_gross_profit(new_row)
|
||||
new_row.buying_rate = flt(new_row.buying_amount / new_row.qty, self.float_precision) if new_row.qty else 0
|
||||
new_row.base_rate = flt(new_row.base_amount / new_row.qty, self.float_precision) if new_row.qty else 0
|
||||
return new_row
|
||||
|
||||
def set_average_gross_profit(self, new_row):
|
||||
new_row.gross_profit = flt(new_row.base_amount - new_row.buying_amount, self.currency_precision)
|
||||
new_row.gross_profit_percent = flt(((new_row.gross_profit / new_row.base_amount) * 100.0), self.currency_precision) \
|
||||
if new_row.base_amount else 0
|
||||
new_row.buying_rate = flt(new_row.buying_amount / new_row.qty, self.float_precision) if new_row.qty else 0
|
||||
new_row.base_rate = flt(new_row.base_amount / new_row.qty, self.float_precision) if new_row.qty else 0
|
||||
|
||||
return new_row
|
||||
def add_to_totals(self, new_row):
|
||||
for key in self.totals:
|
||||
if new_row.get(key):
|
||||
self.totals[key] += new_row[key]
|
||||
|
||||
def get_returned_invoice_items(self):
|
||||
returned_invoices = frappe.db.sql("""
|
||||
|
||||
@@ -76,7 +76,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
||||
'company': d.company,
|
||||
'sales_order': d.sales_order,
|
||||
'delivery_note': d.delivery_note,
|
||||
'income_account': d.unrealized_profit_loss_account or d.income_account,
|
||||
'income_account': d.unrealized_profit_loss_account if d.is_internal_customer == 1 else d.income_account,
|
||||
'cost_center': d.cost_center,
|
||||
'stock_qty': d.stock_qty,
|
||||
'stock_uom': d.stock_uom
|
||||
@@ -380,6 +380,7 @@ def get_items(filters, additional_query_columns):
|
||||
`tabSales Invoice Item`.name, `tabSales Invoice Item`.parent,
|
||||
`tabSales Invoice`.posting_date, `tabSales Invoice`.debit_to,
|
||||
`tabSales Invoice`.unrealized_profit_loss_account,
|
||||
`tabSales Invoice`.is_internal_customer,
|
||||
`tabSales Invoice`.project, `tabSales Invoice`.customer, `tabSales Invoice`.remarks,
|
||||
`tabSales Invoice`.territory, `tabSales Invoice`.company, `tabSales Invoice`.base_net_total,
|
||||
`tabSales Invoice Item`.item_code, `tabSales Invoice Item`.description,
|
||||
@@ -625,7 +626,3 @@ def add_sub_total_row(item, total_row_map, group_by_value, tax_columns):
|
||||
for tax in tax_columns:
|
||||
total_row.setdefault(frappe.scrub(tax + ' Amount'), 0.0)
|
||||
total_row[frappe.scrub(tax + ' Amount')] += flt(item[frappe.scrub(tax + ' Amount')])
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -69,4 +69,3 @@ frappe.query_reports["Sales Register"] = {
|
||||
}
|
||||
|
||||
erpnext.utils.add_dimensions('Sales Register', 7);
|
||||
|
||||
|
||||
@@ -84,7 +84,7 @@ def _execute(filters, additional_table_columns=None, additional_query_columns=No
|
||||
# Add amount in unrealized account
|
||||
for account in unrealized_profit_loss_accounts:
|
||||
row.update({
|
||||
frappe.scrub(account): flt(internal_invoice_map.get((inv.name, account)))
|
||||
frappe.scrub(account+"_unrealized"): flt(internal_invoice_map.get((inv.name, account)))
|
||||
})
|
||||
|
||||
# net total
|
||||
@@ -258,6 +258,7 @@ def get_columns(invoice_list, additional_table_columns):
|
||||
|
||||
unrealized_profit_loss_accounts = frappe.db.sql_list("""SELECT distinct unrealized_profit_loss_account
|
||||
from `tabSales Invoice` where docstatus = 1 and name in (%s)
|
||||
and is_internal_customer = 1
|
||||
and ifnull(unrealized_profit_loss_account, '') != ''
|
||||
order by unrealized_profit_loss_account""" %
|
||||
', '.join(['%s']*len(invoice_list)), tuple(inv.name for inv in invoice_list))
|
||||
@@ -284,7 +285,7 @@ def get_columns(invoice_list, additional_table_columns):
|
||||
for account in unrealized_profit_loss_accounts:
|
||||
unrealized_profit_loss_account_columns.append({
|
||||
"label": account,
|
||||
"fieldname": frappe.scrub(account),
|
||||
"fieldname": frappe.scrub(account+"_unrealized"),
|
||||
"fieldtype": "Currency",
|
||||
"options": "currency",
|
||||
"width": 120
|
||||
|
||||
@@ -110,6 +110,3 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
|
||||
|
||||
erpnext.utils.add_dimensions('Trial Balance', 6);
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,22 +1,52 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import unittest
|
||||
from erpnext.accounts.party import get_party_shipping_address
|
||||
|
||||
from frappe.test_runner import make_test_objects
|
||||
|
||||
from erpnext.accounts.party import get_party_shipping_address
|
||||
from erpnext.accounts.utils import get_future_stock_vouchers, get_voucherwise_gl_entries
|
||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
|
||||
|
||||
|
||||
class TestUtils(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestUtils, cls).setUpClass()
|
||||
make_test_objects('Address', ADDRESS_RECORDS)
|
||||
make_test_objects("Address", ADDRESS_RECORDS)
|
||||
|
||||
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')
|
||||
address = get_party_shipping_address("Customer", "_Test Customer 1")
|
||||
self.assertEqual(address, "_Test Billing Address 2 Title-Billing")
|
||||
|
||||
def test_get_party_shipping_address2(self):
|
||||
address = get_party_shipping_address('Customer', '_Test Customer 2')
|
||||
self.assertEqual(address, '_Test Shipping Address 2 Title-Shipping')
|
||||
address = get_party_shipping_address("Customer", "_Test Customer 2")
|
||||
self.assertEqual(address, "_Test Shipping Address 2 Title-Shipping")
|
||||
|
||||
def test_get_voucher_wise_gl_entry(self):
|
||||
|
||||
pr = make_purchase_receipt(
|
||||
item_code="_Test Item",
|
||||
posting_date="2021-02-01",
|
||||
rate=100,
|
||||
qty=1,
|
||||
warehouse="Stores - TCP1",
|
||||
company="_Test Company with perpetual inventory",
|
||||
)
|
||||
|
||||
future_vouchers = get_future_stock_vouchers("2021-01-01", "00:00:00", for_items=["_Test Item"])
|
||||
|
||||
voucher_type_and_no = ("Purchase Receipt", pr.name)
|
||||
self.assertTrue(
|
||||
voucher_type_and_no in future_vouchers,
|
||||
msg="get_future_stock_vouchers not returning correct value",
|
||||
)
|
||||
|
||||
posting_date = "2021-01-01"
|
||||
gl_entries = get_voucherwise_gl_entries(future_vouchers, posting_date)
|
||||
self.assertTrue(
|
||||
voucher_type_and_no in gl_entries, msg="get_voucherwise_gl_entries not returning expected GLes",
|
||||
)
|
||||
|
||||
|
||||
ADDRESS_RECORDS = [
|
||||
@@ -28,12 +58,8 @@ ADDRESS_RECORDS = [
|
||||
"city": "Lagos",
|
||||
"country": "Nigeria",
|
||||
"links": [
|
||||
{
|
||||
"link_doctype": "Customer",
|
||||
"link_name": "_Test Customer 2",
|
||||
"doctype": "Dynamic Link"
|
||||
}
|
||||
]
|
||||
{"link_doctype": "Customer", "link_name": "_Test Customer 2", "doctype": "Dynamic Link"}
|
||||
],
|
||||
},
|
||||
{
|
||||
"doctype": "Address",
|
||||
@@ -43,12 +69,8 @@ ADDRESS_RECORDS = [
|
||||
"city": "Lagos",
|
||||
"country": "Nigeria",
|
||||
"links": [
|
||||
{
|
||||
"link_doctype": "Customer",
|
||||
"link_name": "_Test Customer 2",
|
||||
"doctype": "Dynamic Link"
|
||||
}
|
||||
]
|
||||
{"link_doctype": "Customer", "link_name": "_Test Customer 2", "doctype": "Dynamic Link"}
|
||||
],
|
||||
},
|
||||
{
|
||||
"doctype": "Address",
|
||||
@@ -59,12 +81,8 @@ ADDRESS_RECORDS = [
|
||||
"country": "Nigeria",
|
||||
"is_shipping_address": "1",
|
||||
"links": [
|
||||
{
|
||||
"link_doctype": "Customer",
|
||||
"link_name": "_Test Customer 2",
|
||||
"doctype": "Dynamic Link"
|
||||
}
|
||||
]
|
||||
{"link_doctype": "Customer", "link_name": "_Test Customer 2", "doctype": "Dynamic Link"}
|
||||
],
|
||||
},
|
||||
{
|
||||
"doctype": "Address",
|
||||
@@ -75,11 +93,7 @@ ADDRESS_RECORDS = [
|
||||
"country": "Nigeria",
|
||||
"is_shipping_address": "1",
|
||||
"links": [
|
||||
{
|
||||
"link_doctype": "Customer",
|
||||
"link_name": "_Test Customer 1",
|
||||
"doctype": "Dynamic Link"
|
||||
}
|
||||
]
|
||||
}
|
||||
{"link_doctype": "Customer", "link_name": "_Test Customer 1", "doctype": "Dynamic Link"}
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
@@ -19,6 +19,7 @@ from erpnext.stock import get_warehouse_account_map
|
||||
|
||||
class StockValueAndAccountBalanceOutOfSync(frappe.ValidationError): pass
|
||||
class FiscalYearError(frappe.ValidationError): pass
|
||||
class PaymentEntryUnlinkError(frappe.ValidationError): pass
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_fiscal_year(date=None, fiscal_year=None, label="Date", verbose=1, company=None, as_dict=False):
|
||||
@@ -350,6 +351,7 @@ def reconcile_against_document(args):
|
||||
# cancel advance entry
|
||||
doc = frappe.get_doc(d.voucher_type, d.voucher_no)
|
||||
|
||||
frappe.flags.ignore_party_validation = True
|
||||
doc.make_gl_entries(cancel=1, adv_adj=1)
|
||||
|
||||
# update ref in advance entry
|
||||
@@ -361,6 +363,7 @@ def reconcile_against_document(args):
|
||||
# re-submit advance entry
|
||||
doc = frappe.get_doc(d.voucher_type, d.voucher_no)
|
||||
doc.make_gl_entries(cancel = 0, adv_adj =1)
|
||||
frappe.flags.ignore_party_validation = False
|
||||
|
||||
if d.voucher_type in ('Payment Entry', 'Journal Entry'):
|
||||
doc.update_expense_claim()
|
||||
@@ -553,10 +556,16 @@ def remove_ref_doc_link_from_pe(ref_type, ref_no):
|
||||
and docstatus < 2""", (now(), frappe.session.user, ref_type, ref_no))
|
||||
|
||||
for pe in linked_pe:
|
||||
try:
|
||||
pe_doc = frappe.get_doc("Payment Entry", pe)
|
||||
pe_doc.set_total_allocated_amount()
|
||||
pe_doc.set_unallocated_amount()
|
||||
pe_doc.set_amounts()
|
||||
pe_doc.clear_unallocated_reference_document_rows()
|
||||
pe_doc.validate_payment_type_with_outstanding()
|
||||
except Exception as e:
|
||||
msg = _("There were issues unlinking payment entry {0}.").format(pe_doc.name)
|
||||
msg += '<br>'
|
||||
msg += _("Please cancel payment entry manually first")
|
||||
frappe.throw(msg, exc=PaymentEntryUnlinkError, title=_("Payment Unlink Error"))
|
||||
|
||||
frappe.db.sql("""update `tabPayment Entry` set total_allocated_amount=%s,
|
||||
base_total_allocated_amount=%s, unallocated_amount=%s, modified=%s, modified_by=%s
|
||||
@@ -920,7 +929,6 @@ def repost_gle_for_stock_vouchers(stock_vouchers, posting_date, company=None, wa
|
||||
_delete_gl_entries(voucher_type, voucher_no)
|
||||
|
||||
def get_future_stock_vouchers(posting_date, posting_time, for_warehouses=None, for_items=None, company=None):
|
||||
future_stock_vouchers = []
|
||||
|
||||
values = []
|
||||
condition = ""
|
||||
@@ -936,30 +944,49 @@ def get_future_stock_vouchers(posting_date, posting_time, for_warehouses=None, f
|
||||
condition += " and company = %s"
|
||||
values.append(company)
|
||||
|
||||
for d in frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no
|
||||
future_stock_vouchers = frappe.db.sql("""select distinct sle.voucher_type, sle.voucher_no
|
||||
from `tabStock Ledger Entry` sle
|
||||
where
|
||||
timestamp(sle.posting_date, sle.posting_time) >= timestamp(%s, %s)
|
||||
and is_cancelled = 0
|
||||
{condition}
|
||||
order by timestamp(sle.posting_date, sle.posting_time) asc, creation asc for update""".format(condition=condition),
|
||||
tuple([posting_date, posting_time] + values), as_dict=True):
|
||||
future_stock_vouchers.append([d.voucher_type, d.voucher_no])
|
||||
tuple([posting_date, posting_time] + values), as_dict=True)
|
||||
|
||||
return future_stock_vouchers
|
||||
return [(d.voucher_type, d.voucher_no) for d in future_stock_vouchers]
|
||||
|
||||
def get_voucherwise_gl_entries(future_stock_vouchers, posting_date):
|
||||
""" Get voucherwise list of GL entries.
|
||||
|
||||
Only fetches GLE fields required for comparing with new GLE.
|
||||
Check compare_existing_and_expected_gle function below.
|
||||
|
||||
returns:
|
||||
Dict[Tuple[voucher_type, voucher_no], List[GL Entries]]
|
||||
"""
|
||||
gl_entries = {}
|
||||
if future_stock_vouchers:
|
||||
for d in frappe.db.sql("""select * from `tabGL Entry`
|
||||
where posting_date >= %s and voucher_no in (%s)""" %
|
||||
('%s', ', '.join(['%s']*len(future_stock_vouchers))),
|
||||
tuple([posting_date] + [d[1] for d in future_stock_vouchers]), as_dict=1):
|
||||
if not future_stock_vouchers:
|
||||
return gl_entries
|
||||
|
||||
voucher_nos = [d[1] for d in future_stock_vouchers]
|
||||
|
||||
gles = frappe.db.sql("""
|
||||
select name, account, credit, debit, cost_center, project, voucher_type, voucher_no
|
||||
from `tabGL Entry`
|
||||
where
|
||||
posting_date >= %s and voucher_no in (%s)""" %
|
||||
('%s', ', '.join(['%s'] * len(voucher_nos))),
|
||||
tuple([posting_date] + voucher_nos), as_dict=1)
|
||||
|
||||
for d in gles:
|
||||
gl_entries.setdefault((d.voucher_type, d.voucher_no), []).append(d)
|
||||
|
||||
return gl_entries
|
||||
|
||||
def compare_existing_and_expected_gle(existing_gle, expected_gle, precision):
|
||||
if len(existing_gle) != len(expected_gle):
|
||||
return False
|
||||
|
||||
matched = True
|
||||
for entry in expected_gle:
|
||||
account_existed = False
|
||||
@@ -1062,3 +1089,14 @@ def get_journal_entry(account, stock_adjustment_account, amount):
|
||||
db_or_cr_stock_adjustment_account : abs(amount)
|
||||
}]
|
||||
}
|
||||
|
||||
def check_and_delete_linked_reports(report):
|
||||
""" Check if reports are referenced in Desktop Icon """
|
||||
icons = frappe.get_all("Desktop Icon",
|
||||
fields = ['name'],
|
||||
filters = {
|
||||
"_report": report
|
||||
})
|
||||
if icons:
|
||||
for icon in icons:
|
||||
frappe.delete_doc("Desktop Icon", icon)
|
||||
|
||||
@@ -588,7 +588,7 @@
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Bank Statement",
|
||||
"label": "Banking and Payments",
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
@@ -642,6 +642,24 @@
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Payment Entry",
|
||||
"link_to": "Payment Entry",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Payment Reconciliation",
|
||||
"link_to": "Payment Reconciliation",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
@@ -1064,7 +1082,7 @@
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2021-06-10 03:17:31.427945",
|
||||
"modified": "2021-08-23 16:06:34.167267",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Accounting",
|
||||
|
||||
@@ -36,4 +36,3 @@ QUnit.test("test: Disease", function (assert) {
|
||||
]);
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ def make_depreciation_entry(asset_name, date=None):
|
||||
"credit_in_account_currency": d.depreciation_amount,
|
||||
"reference_type": "Asset",
|
||||
"reference_name": asset.name,
|
||||
"cost_center": ""
|
||||
"cost_center": depreciation_cost_center
|
||||
}
|
||||
|
||||
debit_entry = {
|
||||
|
||||
@@ -15,6 +15,7 @@ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_pu
|
||||
|
||||
class TestAssetMovement(unittest.TestCase):
|
||||
def setUp(self):
|
||||
frappe.db.set_value("Company", "_Test Company", "capital_work_in_progress_account", "CWIP Account - _TC")
|
||||
create_asset_data()
|
||||
make_location()
|
||||
|
||||
@@ -50,7 +51,7 @@ class TestAssetMovement(unittest.TestCase):
|
||||
reference_doctype = 'Purchase Receipt', reference_name = pr.name)
|
||||
self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location 2")
|
||||
|
||||
movement2 = create_asset_movement(purpose = 'Transfer', company = asset.company,
|
||||
create_asset_movement(purpose = 'Transfer', company = asset.company,
|
||||
assets = [{ 'asset': asset.name , 'source_location': 'Test Location 2', 'target_location': 'Test Location'}],
|
||||
reference_doctype = 'Purchase Receipt', reference_name = pr.name)
|
||||
self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location")
|
||||
@@ -59,7 +60,7 @@ class TestAssetMovement(unittest.TestCase):
|
||||
self.assertEqual(frappe.db.get_value("Asset", asset.name, "location"), "Test Location")
|
||||
|
||||
employee = make_employee("testassetmovemp@example.com", company="_Test Company")
|
||||
movement3 = create_asset_movement(purpose = 'Issue', company = asset.company,
|
||||
create_asset_movement(purpose = 'Issue', company = asset.company,
|
||||
assets = [{ 'asset': asset.name , 'source_location': 'Test Location', 'to_employee': employee}],
|
||||
reference_doctype = 'Purchase Receipt', reference_name = pr.name)
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user