Compare commits
171 Commits
gle_indexi
...
v13.10.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
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
|
# This commit just changes spaces to tabs for indentation in some files
|
||||||
5f473611bd6ed57703716244a054d3fb5ba9cd23
|
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:
|
if response.ok:
|
||||||
payload = response.json()
|
payload = response.json()
|
||||||
title = payload.get("title", "").lower()
|
title = payload.get("title", "").lower().strip()
|
||||||
head_sha = payload.get("head", {}).get("sha")
|
head_sha = payload.get("head", {}).get("sha")
|
||||||
body = payload.get("body", "").lower()
|
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):
|
if docs_link_exists(body):
|
||||||
print("Documentation Link Found. You're Awesome! 🎉")
|
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
|
cd ~ || exit
|
||||||
|
|
||||||
sudo apt-get install redis-server
|
sudo apt-get install redis-server libcups2-dev
|
||||||
|
|
||||||
sudo apt install nodejs
|
|
||||||
|
|
||||||
sudo apt install npm
|
|
||||||
|
|
||||||
pip install frappe-bench
|
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
|
tar -xf /tmp/wkhtmltox.tar.xz -C /tmp
|
||||||
sudo mv /tmp/wkhtmltox/bin/wkhtmltopdf /usr/local/bin/wkhtmltopdf
|
sudo mv /tmp/wkhtmltox/bin/wkhtmltopdf /usr/local/bin/wkhtmltopdf
|
||||||
sudo chmod o+x /usr/local/bin/wkhtmltopdf
|
sudo chmod o+x /usr/local/bin/wkhtmltopdf
|
||||||
sudo apt-get install libcups2-dev
|
|
||||||
|
|
||||||
cd ~/frappe-bench || exit
|
cd ~/frappe-bench || exit
|
||||||
|
|
||||||
|
|||||||
14
.github/workflows/patch.yml
vendored
14
.github/workflows/patch.yml
vendored
@@ -1,6 +1,12 @@
|
|||||||
name: Patch
|
name: Patch
|
||||||
|
|
||||||
on: [pull_request, workflow_dispatch]
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- '**.js'
|
||||||
|
- '**.md'
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
@@ -26,6 +32,12 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
python-version: 3.6
|
python-version: 3.6
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: 12
|
||||||
|
check-latest: true
|
||||||
|
|
||||||
- name: Add to Hosts
|
- name: Add to Hosts
|
||||||
run: echo "127.0.0.1 test_site" | sudo tee -a /etc/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
|
name: Server
|
||||||
|
|
||||||
on: [pull_request, workflow_dispatch]
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- '**.js'
|
||||||
|
- '**.md'
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches: [ develop ]
|
||||||
|
paths-ignore:
|
||||||
|
- '**.js'
|
||||||
|
- '**.md'
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
@@ -32,6 +42,12 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
python-version: 3.7
|
python-version: 3.7
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@v2
|
||||||
|
with:
|
||||||
|
node-version: 12
|
||||||
|
check-latest: true
|
||||||
|
|
||||||
- name: Add to Hosts
|
- name: Add to Hosts
|
||||||
run: echo "127.0.0.1 test_site" | sudo tee -a /etc/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:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- '**.md'
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
|||||||
@@ -6,4 +6,4 @@
|
|||||||
"scss/at-rule-no-unknown": true,
|
"scss/at-rule-no-unknown": true,
|
||||||
"no-descending-specificity": null
|
"no-descending-specificity": null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import frappe
|
|||||||
from erpnext.hooks import regional_overrides
|
from erpnext.hooks import regional_overrides
|
||||||
from frappe.utils import getdate
|
from frappe.utils import getdate
|
||||||
|
|
||||||
__version__ = '13.9.0'
|
__version__ = '13.10.0'
|
||||||
|
|
||||||
def get_default_company(user=None):
|
def get_default_company(user=None):
|
||||||
'''Get default company for user'''
|
'''Get default company for user'''
|
||||||
|
|||||||
@@ -19,4 +19,4 @@ frappe.dashboards.chart_sources["Account Balance Timeline"] = {
|
|||||||
reqd: 1
|
reqd: 1
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -450,5 +450,3 @@ def get_deferred_booking_accounts(doctype, voucher_detail_no, dr_or_cr):
|
|||||||
return debit_account
|
return debit_account
|
||||||
else:
|
else:
|
||||||
return credit_account
|
return credit_account
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -60,4 +60,4 @@ frappe.ui.form.on('Accounting Dimension Detail', {
|
|||||||
let row = locals[cdt][cdn];
|
let row = locals[cdt][cdn];
|
||||||
row.reference_document = frm.doc.document_type;
|
row.reference_document = frm.doc.document_type;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -113,5 +113,3 @@ def disable_dimension():
|
|||||||
dimension2 = frappe.get_doc("Accounting Dimension", "Location")
|
dimension2 = frappe.get_doc("Accounting Dimension", "Location")
|
||||||
dimension2.disabled = 1
|
dimension2.disabled = 1
|
||||||
dimension2.save()
|
dimension2.save()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -79,4 +79,4 @@ frappe.ui.form.on('Allowed Dimension', {
|
|||||||
row.accounting_dimension = frm.doc.accounting_dimension;
|
row.accounting_dimension = frm.doc.accounting_dimension;
|
||||||
frm.refresh_field("dimensions");
|
frm.refresh_field("dimensions");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -56,4 +56,4 @@ class AccountingPeriod(Document):
|
|||||||
self.append('closed_documents', {
|
self.append('closed_documents', {
|
||||||
"document_type": doctype_for_closing.document_type,
|
"document_type": doctype_for_closing.document_type,
|
||||||
"closed": doctype_for_closing.closed
|
"closed": doctype_for_closing.closed
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -48,4 +48,4 @@ frappe.tour['Accounts Settings'] = [
|
|||||||
title: "Unlink Advance Payment on Cancellation of Order",
|
title: "Unlink Advance Payment on Cancellation of Order",
|
||||||
description: __("Similar to the previous option, this unlinks any advance payments made against Purchase/Sales Orders.")
|
description: __("Similar to the previous option, this unlinks any advance payments made against Purchase/Sales Orders.")
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
"delete_linked_ledger_entries",
|
"delete_linked_ledger_entries",
|
||||||
"book_asset_depreciation_entry_automatically",
|
"book_asset_depreciation_entry_automatically",
|
||||||
"unlink_advance_payment_on_cancelation_of_order",
|
"unlink_advance_payment_on_cancelation_of_order",
|
||||||
|
"enable_common_party_accounting",
|
||||||
"post_change_gl_entries",
|
"post_change_gl_entries",
|
||||||
"enable_discount_accounting",
|
"enable_discount_accounting",
|
||||||
"tax_settings_section",
|
"tax_settings_section",
|
||||||
@@ -269,6 +270,12 @@
|
|||||||
"fieldname": "enable_discount_accounting",
|
"fieldname": "enable_discount_accounting",
|
||||||
"fieldtype": "Check",
|
"fieldtype": "Check",
|
||||||
"label": "Enable Discount Accounting"
|
"label": "Enable Discount Accounting"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"default": "0",
|
||||||
|
"fieldname": "enable_common_party_accounting",
|
||||||
|
"fieldtype": "Check",
|
||||||
|
"label": "Enable Common Party Accounting"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"icon": "icon-cog",
|
"icon": "icon-cog",
|
||||||
@@ -276,7 +283,7 @@
|
|||||||
"index_web_pages_for_search": 1,
|
"index_web_pages_for_search": 1,
|
||||||
"issingle": 1,
|
"issingle": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-07-12 18:54:29.084958",
|
"modified": "2021-08-19 11:17:38.788054",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "Accounts Settings",
|
"name": "Accounts Settings",
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ class AccountsSettings(Document):
|
|||||||
|
|
||||||
def toggle_discount_accounting_fields(self):
|
def toggle_discount_accounting_fields(self):
|
||||||
enable_discount_accounting = cint(self.enable_discount_accounting)
|
enable_discount_accounting = cint(self.enable_discount_accounting)
|
||||||
|
|
||||||
for doctype in ["Sales Invoice Item", "Purchase Invoice Item"]:
|
for doctype in ["Sales Invoice Item", "Purchase Invoice Item"]:
|
||||||
make_property_setter(doctype, "discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False)
|
make_property_setter(doctype, "discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False)
|
||||||
if enable_discount_accounting:
|
if enable_discount_accounting:
|
||||||
@@ -52,4 +52,4 @@ class AccountsSettings(Document):
|
|||||||
else:
|
else:
|
||||||
make_property_setter(doctype, "additional_discount_account", "mandatory_depends_on", "", "Code", validate_fields_for_doctype=False)
|
make_property_setter(doctype, "additional_discount_account", "mandatory_depends_on", "", "Code", validate_fields_for_doctype=False)
|
||||||
|
|
||||||
make_property_setter("Item", "default_discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False)
|
make_property_setter("Item", "default_discount_account", "hidden", not(enable_discount_accounting), "Check", validate_fields_for_doctype=False)
|
||||||
|
|||||||
@@ -5,4 +5,4 @@ frappe.ui.form.on('Accounts Settings', {
|
|||||||
frm.set_df_property("frozen_accounts_modifier", "label", "Role Allowed to Close Books & Make Changes to Closed Periods");
|
frm.set_df_property("frozen_accounts_modifier", "label", "Role Allowed to Close Books & Make Changes to Closed Periods");
|
||||||
frm.set_df_property("credit_controller", "label", "Credit Manager");
|
frm.set_df_property("credit_controller", "label", "Credit Manager");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -120,4 +120,4 @@ erpnext.integrations.refreshPlaidLink = class refreshPlaidLink {
|
|||||||
plaid_success(token, response) {
|
plaid_success(token, response) {
|
||||||
frappe.show_alert({ message: __('Plaid Link Updated'), indicator: 'green' });
|
frappe.show_alert({ message: __('Plaid Link Updated'), indicator: 'green' });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,4 +13,4 @@ class Bank(Document):
|
|||||||
load_address_and_contact(self)
|
load_address_and_contact(self)
|
||||||
|
|
||||||
def on_trash(self):
|
def on_trash(self):
|
||||||
delete_contact_and_address('Bank', self.name)
|
delete_contact_and_address('Bank', self.name)
|
||||||
|
|||||||
@@ -26,4 +26,4 @@ def get_data():
|
|||||||
'items': ['Journal Entry']
|
'items': ['Journal Entry']
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ frappe.ui.form.on("Bank Clearance", {
|
|||||||
|
|
||||||
onload: function(frm) {
|
onload: function(frm) {
|
||||||
|
|
||||||
let default_bank_account = frappe.defaults.get_user_default("Company")?
|
let default_bank_account = frappe.defaults.get_user_default("Company")?
|
||||||
locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"]: "";
|
locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"]: "";
|
||||||
frm.set_value("account", default_bank_account);
|
frm.set_value("account", default_bank_account);
|
||||||
|
|
||||||
|
|||||||
@@ -6,4 +6,4 @@ import frappe
|
|||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
class BankClearanceDetail(Document):
|
class BankClearanceDetail(Document):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -25,6 +25,6 @@ class BankGuarantee(Document):
|
|||||||
def get_vouchar_detials(column_list, doctype, docname):
|
def get_vouchar_detials(column_list, doctype, docname):
|
||||||
column_list = json.loads(column_list)
|
column_list = json.loads(column_list)
|
||||||
for col in column_list:
|
for col in column_list:
|
||||||
sanitize_searchfield(col)
|
sanitize_searchfield(col)
|
||||||
return frappe.db.sql(''' select {columns} from `tab{doctype}` where name=%s'''
|
return frappe.db.sql(''' select {columns} from `tab{doctype}` where name=%s'''
|
||||||
.format(columns=", ".join(column_list), doctype=doctype), docname, as_dict=1)[0]
|
.format(columns=", ".join(column_list), doctype=doctype), docname, as_dict=1)[0]
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ class BankTransaction(StatusUpdater):
|
|||||||
self.update_allocations()
|
self.update_allocations()
|
||||||
self.clear_linked_payment_entries()
|
self.clear_linked_payment_entries()
|
||||||
self.set_status(update=True)
|
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):
|
def update_allocations(self):
|
||||||
if self.payment_entries:
|
if self.payment_entries:
|
||||||
@@ -41,21 +45,30 @@ class BankTransaction(StatusUpdater):
|
|||||||
frappe.db.set_value(self.doctype, self.name, "status", "Reconciled")
|
frappe.db.set_value(self.doctype, self.name, "status", "Reconciled")
|
||||||
|
|
||||||
self.reload()
|
self.reload()
|
||||||
|
|
||||||
def clear_linked_payment_entries(self):
|
def clear_linked_payment_entries(self, for_cancel=False):
|
||||||
for payment_entry in self.payment_entries:
|
for payment_entry in self.payment_entries:
|
||||||
if payment_entry.payment_document in ["Payment Entry", "Journal Entry", "Purchase Invoice", "Expense Claim"]:
|
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":
|
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):
|
def clear_simple_entry(self, payment_entry, for_cancel=False):
|
||||||
frappe.db.set_value(payment_entry.payment_document, payment_entry.payment_entry, "clearance_date", self.date)
|
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):
|
def clear_sales_invoice(self, payment_entry, for_cancel=False):
|
||||||
frappe.db.set_value("Sales Invoice Payment", dict(parenttype=payment_entry.payment_document,
|
clearance_date = self.date if not for_cancel else None
|
||||||
parent=payment_entry.payment_entry), "clearance_date", self.date)
|
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):
|
def get_total_allocated_amount(payment_entry):
|
||||||
return frappe.db.sql("""
|
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)
|
frappe.db.set_value(doc.payment_document, doc.payment_entry, "clearance_date", None)
|
||||||
|
|
||||||
return doc.payment_entry
|
return doc.payment_entry
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,12 @@
|
|||||||
frappe.listview_settings['Bank Transaction'] = {
|
frappe.listview_settings['Bank Transaction'] = {
|
||||||
add_fields: ["unallocated_amount"],
|
add_fields: ["unallocated_amount"],
|
||||||
get_indicator: function(doc) {
|
get_indicator: function(doc) {
|
||||||
if(flt(doc.unallocated_amount)>0) {
|
if(doc.docstatus == 2) {
|
||||||
return [__("Unreconciled"), "orange", "unallocated_amount,>,0"];
|
return [__("Cancelled"), "red", "docstatus,=,2"];
|
||||||
} else if(flt(doc.unallocated_amount)<=0) {
|
} else if(flt(doc.unallocated_amount)<=0) {
|
||||||
return [__("Reconciled"), "green", "unallocated_amount,=,0"];
|
return [__("Reconciled"), "green", "unallocated_amount,=,0"];
|
||||||
|
} else if(flt(doc.unallocated_amount)>0) {
|
||||||
|
return [__("Unreconciled"), "orange", "unallocated_amount,>,0"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -77,4 +77,4 @@ def get_bank_mapping(bank_account):
|
|||||||
|
|
||||||
mapping = {row.file_field:row.bank_transaction_field for row in bank.bank_transaction_mapping}
|
mapping = {row.file_field:row.bank_transaction_field for row in bank.bank_transaction_mapping}
|
||||||
|
|
||||||
return mapping
|
return mapping
|
||||||
|
|||||||
@@ -25,7 +25,8 @@ class TestBankTransaction(unittest.TestCase):
|
|||||||
def tearDownClass(cls):
|
def tearDownClass(cls):
|
||||||
for bt in frappe.get_all("Bank Transaction"):
|
for bt in frappe.get_all("Bank Transaction"):
|
||||||
doc = frappe.get_doc("Bank Transaction", bt.name)
|
doc = frappe.get_doc("Bank Transaction", bt.name)
|
||||||
doc.cancel()
|
if doc.docstatus == 1:
|
||||||
|
doc.cancel()
|
||||||
doc.delete()
|
doc.delete()
|
||||||
|
|
||||||
# Delete directly in DB to avoid validation errors for countries not allowing deletion
|
# Delete directly in DB to avoid validation errors for countries not allowing deletion
|
||||||
@@ -57,6 +58,12 @@ class TestBankTransaction(unittest.TestCase):
|
|||||||
clearance_date = frappe.db.get_value("Payment Entry", payment.name, "clearance_date")
|
clearance_date = frappe.db.get_value("Payment Entry", payment.name, "clearance_date")
|
||||||
self.assertTrue(clearance_date is not None)
|
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
|
# Check if ERPNext can correctly filter a linked payments based on the debit/credit amount
|
||||||
def test_debit_credit_output(self):
|
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"))
|
bank_transaction = frappe.get_doc("Bank Transaction", dict(description="Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07"))
|
||||||
|
|||||||
@@ -6,4 +6,4 @@ import frappe
|
|||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
class CFormInvoiceDetail(Document):
|
class CFormInvoiceDetail(Document):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -18,5 +18,3 @@ class CashFlowMapping(Document):
|
|||||||
frappe._('You can only select a maximum of one option from the list of check boxes.'),
|
frappe._('You can only select a maximum of one option from the list of check boxes.'),
|
||||||
title='Error'
|
title='Error'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -33,4 +33,4 @@ class CashierClosing(Document):
|
|||||||
|
|
||||||
def validate_time(self):
|
def validate_time(self):
|
||||||
if self.from_time >= self.time:
|
if self.from_time >= self.time:
|
||||||
frappe.throw(_("From Time Should Be Less Than To Time"))
|
frappe.throw(_("From Time Should Be Less Than To Time"))
|
||||||
|
|||||||
@@ -10,10 +10,10 @@ frappe.ui.form.on('Cheque Print Template', {
|
|||||||
function() {
|
function() {
|
||||||
erpnext.cheque_print.view_cheque_print(frm);
|
erpnext.cheque_print.view_cheque_print(frm);
|
||||||
}).addClass("btn-primary");
|
}).addClass("btn-primary");
|
||||||
|
|
||||||
$(frm.fields_dict.cheque_print_preview.wrapper).empty()
|
$(frm.fields_dict.cheque_print_preview.wrapper).empty()
|
||||||
|
|
||||||
|
|
||||||
var template = '<div style="position: relative; overflow-x: scroll;">\
|
var template = '<div style="position: relative; overflow-x: scroll;">\
|
||||||
<div id="cheque_preview" style="width: {{ cheque_width }}cm; \
|
<div id="cheque_preview" style="width: {{ cheque_width }}cm; \
|
||||||
height: {{ cheque_height }}cm;\
|
height: {{ cheque_height }}cm;\
|
||||||
@@ -47,9 +47,9 @@ frappe.ui.form.on('Cheque Print Template', {
|
|||||||
position: absolute;"> Signatory Name </span>\
|
position: absolute;"> Signatory Name </span>\
|
||||||
</div>\
|
</div>\
|
||||||
</div>';
|
</div>';
|
||||||
|
|
||||||
$(frappe.render(template, frm.doc)).appendTo(frm.fields_dict.cheque_print_preview.wrapper)
|
$(frappe.render(template, frm.doc)).appendTo(frm.fields_dict.cheque_print_preview.wrapper)
|
||||||
|
|
||||||
if (frm.doc.scanned_cheque) {
|
if (frm.doc.scanned_cheque) {
|
||||||
$(frm.fields_dict.cheque_print_preview.wrapper).find("#cheque_preview").css('background-image', 'url(' + frm.doc.scanned_cheque + ')');
|
$(frm.fields_dict.cheque_print_preview.wrapper).find("#cheque_preview").css('background-image', 'url(' + frm.doc.scanned_cheque + ')');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -129,4 +129,4 @@ def get_name_with_number(new_account, account_number):
|
|||||||
|
|
||||||
def check_if_distributed_cost_center_enabled(cost_center_list):
|
def check_if_distributed_cost_center_enabled(cost_center_list):
|
||||||
value_list = frappe.get_list("Cost Center", {"name": ["in", cost_center_list]}, "enable_distributed_cost_center", as_list=1)
|
value_list = frappe.get_list("Cost Center", {"name": ["in", cost_center_list]}, "enable_distributed_cost_center", as_list=1)
|
||||||
return next((True for x in value_list if x[0]), False)
|
return next((True for x in value_list if x[0]), False)
|
||||||
|
|||||||
@@ -12,4 +12,4 @@ def get_data():
|
|||||||
'items': ['Budget Variance Report', 'General Ledger']
|
'items': ['Budget Variance Report', 'General Ledger']
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,4 +51,4 @@ frappe.treeview_settings["Cost Center"] = {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -62,6 +62,3 @@ def create_cost_center(**args):
|
|||||||
cc.is_group = args.is_group or 0
|
cc.is_group = args.is_group or 0
|
||||||
cc.parent_cost_center = args.parent_cost_center or "_Test Company - _TC"
|
cc.parent_cost_center = args.parent_cost_center or "_Test Company - _TC"
|
||||||
cc.insert()
|
cc.insert()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ class CouponCode(Document):
|
|||||||
self.coupon_code =''.join(i for i in self.coupon_name if not i.isdigit())[0:8].upper()
|
self.coupon_code =''.join(i for i in self.coupon_name if not i.isdigit())[0:8].upper()
|
||||||
elif self.coupon_type == "Gift Card":
|
elif self.coupon_type == "Gift Card":
|
||||||
self.coupon_code = frappe.generate_hash()[:10].upper()
|
self.coupon_code = frappe.generate_hash()[:10].upper()
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
if self.coupon_type == "Gift Card":
|
if self.coupon_type == "Gift Card":
|
||||||
self.maximum_use = 1
|
self.maximum_use = 1
|
||||||
|
|||||||
@@ -124,6 +124,3 @@ class TestCouponCode(unittest.TestCase):
|
|||||||
|
|
||||||
so.submit()
|
so.submit()
|
||||||
self.assertEqual(frappe.db.get_value("Coupon Code", "SAVE30", "used"), 1)
|
self.assertEqual(frappe.db.get_value("Coupon Code", "SAVE30", "used"), 1)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -7,4 +7,4 @@ from __future__ import unicode_literals
|
|||||||
from frappe.model.document import Document
|
from frappe.model.document import Document
|
||||||
|
|
||||||
class DiscountedInvoice(Document):
|
class DiscountedInvoice(Document):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -14,4 +14,4 @@ def get_data():
|
|||||||
'items': ['Payment Entry', 'Journal Entry']
|
'items': ['Payment Entry', 'Journal Entry']
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -144,4 +144,4 @@ def create_dunning_type_with_zero_interest_rate():
|
|||||||
'closing_text': 'We kindly request that you pay the outstanding amount immediately, and late fees.'
|
'closing_text': 'We kindly request that you pay the outstanding amount immediately, and late fees.'
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
dunning_type.save()
|
dunning_type.save()
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ frappe.ui.form.on('Exchange Rate Revaluation', {
|
|||||||
}, __('Create'));
|
}, __('Create'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -128,4 +128,4 @@ var get_account_details = function(frm, cdt, cdn) {
|
|||||||
frm.events.get_total_gain_loss(frm);
|
frm.events.get_total_gain_loss(frm);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ class ExchangeRateRevaluation(Document):
|
|||||||
|
|
||||||
if total_amt != total_debit:
|
if total_amt != total_debit:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@frappe.whitelist()
|
@frappe.whitelist()
|
||||||
@@ -205,4 +205,4 @@ def get_account_details(account, company, posting_date, party_type=None, party=N
|
|||||||
"new_balance_in_base_currency": new_balance_in_base_currency
|
"new_balance_in_base_currency": new_balance_in_base_currency
|
||||||
}
|
}
|
||||||
|
|
||||||
return account_details
|
return account_details
|
||||||
|
|||||||
@@ -9,19 +9,8 @@ import frappe
|
|||||||
import unittest
|
import unittest
|
||||||
|
|
||||||
class TestFinanceBook(unittest.TestCase):
|
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):
|
def test_finance_book(self):
|
||||||
finance_book = self.create_finance_book()
|
finance_book = create_finance_book()
|
||||||
|
|
||||||
# create jv entry
|
# create jv entry
|
||||||
jv = make_journal_entry("_Test Bank - _TC",
|
jv = make_journal_entry("_Test Bank - _TC",
|
||||||
@@ -41,3 +30,14 @@ class TestFinanceBook(unittest.TestCase):
|
|||||||
|
|
||||||
for gl_entry in gl_entries:
|
for gl_entry in gl_entries:
|
||||||
self.assertEqual(gl_entry.finance_book, finance_book.name)
|
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):
|
if not self.get(k):
|
||||||
frappe.throw(_("{0} is required").format(_(self.meta.get_label(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):
|
if not (self.party_type and self.party):
|
||||||
|
account_type = frappe.get_cached_value("Account", self.account, "account_type")
|
||||||
if account_type == "Receivable":
|
if account_type == "Receivable":
|
||||||
frappe.throw(_("{0} {1}: Customer is required against Receivable account {2}")
|
frappe.throw(_("{0} {1}: Customer is required against Receivable account {2}")
|
||||||
.format(self.voucher_type, self.voucher_no, self.account))
|
.format(self.voucher_type, self.voucher_no, self.account))
|
||||||
@@ -73,15 +73,19 @@ class GLEntry(Document):
|
|||||||
.format(self.voucher_type, self.voucher_no, self.account))
|
.format(self.voucher_type, self.voucher_no, self.account))
|
||||||
|
|
||||||
def pl_must_have_cost_center(self):
|
def pl_must_have_cost_center(self):
|
||||||
if frappe.get_cached_value("Account", self.account, "report_type") == "Profit and Loss":
|
"""Validate that profit and loss type account GL entries have a cost center."""
|
||||||
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 += " "
|
|
||||||
msg += _("Please set the cost center field in {0} or setup a default Cost Center for the Company.").format(
|
|
||||||
self.voucher_type)
|
|
||||||
|
|
||||||
frappe.throw(msg, title=_("Missing 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":
|
||||||
|
msg = _("{0} {1}: Cost Center is required for 'Profit and Loss' account {2}.").format(
|
||||||
|
self.voucher_type, self.voucher_no, self.account)
|
||||||
|
msg += " "
|
||||||
|
msg += _("Please set the cost center field in {0} or setup a default Cost Center for the Company.").format(
|
||||||
|
self.voucher_type)
|
||||||
|
|
||||||
|
frappe.throw(msg, title=_("Missing Cost Center"))
|
||||||
|
|
||||||
def validate_dimensions_for_pl_and_bs(self):
|
def validate_dimensions_for_pl_and_bs(self):
|
||||||
account_type = frappe.db.get_value("Account", self.account, "report_type")
|
account_type = frappe.db.get_value("Account", self.account, "report_type")
|
||||||
|
|||||||
@@ -17,4 +17,4 @@ def get_data():
|
|||||||
'items': ['Payment Entry', 'Journal Entry']
|
'items': ['Payment Entry', 'Journal Entry']
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,4 +18,4 @@ frappe.listview_settings['Invoice Discounting'] = {
|
|||||||
return [__("Canceled"), "red", "status,=,Canceled"];
|
return [__("Canceled"), "red", "status,=,Canceled"];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,4 +14,4 @@ frappe.ui.form.on("Journal Entry", {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ class TestJournalEntry(unittest.TestCase):
|
|||||||
"debit_in_account_currency": 0 if diff > 0 else abs(diff),
|
"debit_in_account_currency": 0 if diff > 0 else abs(diff),
|
||||||
"credit_in_account_currency": diff if diff > 0 else 0
|
"credit_in_account_currency": diff if diff > 0 else 0
|
||||||
})
|
})
|
||||||
|
|
||||||
jv.append("accounts", {
|
jv.append("accounts", {
|
||||||
"account": "Stock Adjustment - TCP1",
|
"account": "Stock Adjustment - TCP1",
|
||||||
"cost_center": "Main - TCP1",
|
"cost_center": "Main - TCP1",
|
||||||
|
|||||||
@@ -88,4 +88,4 @@ frappe.ui.form.on("Journal Entry Template", {
|
|||||||
frappe.model.clear_table(frm.doc, "accounts");
|
frappe.model.clear_table(frm.doc, "accounts");
|
||||||
frm.refresh_field("accounts");
|
frm.refresh_field("accounts");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -14,4 +14,4 @@ frappe.ui.form.on('Mode of Payment', {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -39,4 +39,3 @@ class ModeofPayment(Document):
|
|||||||
message = "POS Profile " + frappe.bold(", ".join(pos_profiles)) + " contains \
|
message = "POS Profile " + frappe.bold(", ".join(pos_profiles)) + " contains \
|
||||||
Mode of Payment " + frappe.bold(str(self.name)) + ". Please remove them to disable this mode."
|
Mode of Payment " + frappe.bold(str(self.name)) + ". Please remove them to disable this mode."
|
||||||
frappe.throw(_(message), title="Not Allowed")
|
frappe.throw(_(message), title="Not Allowed")
|
||||||
|
|
||||||
|
|||||||
@@ -55,4 +55,4 @@ def get_percentage(doc, start_date, period):
|
|||||||
if d.month in months:
|
if d.month in months:
|
||||||
percentage += d.percentage_allocation
|
percentage += d.percentage_allocation
|
||||||
|
|
||||||
return percentage
|
return percentage
|
||||||
|
|||||||
@@ -20,4 +20,4 @@ def get_data():
|
|||||||
'items': ['Budget']
|
'items': ['Budget']
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -154,4 +154,4 @@ frappe.ui.form.on('Opening Invoice Creation Tool Item', {
|
|||||||
invoices_add: (frm) => {
|
invoices_add: (frm) => {
|
||||||
frm.trigger('update_invoice_table');
|
frm.trigger('update_invoice_table');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -240,5 +240,3 @@ def get_temporary_opening_account(company=None):
|
|||||||
frappe.throw(_("Please add a Temporary Opening account in Chart of Accounts"))
|
frappe.throw(_("Please add a Temporary Opening account in Chart of Accounts"))
|
||||||
|
|
||||||
return accounts[0].name
|
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) {
|
source_exchange_rate: function(frm) {
|
||||||
if (frm.doc.paid_amount) {
|
if (frm.doc.paid_amount) {
|
||||||
frm.set_value("base_paid_amount", flt(frm.doc.paid_amount) * flt(frm.doc.source_exchange_rate));
|
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 &&
|
// target exchange rate should always be same as source if both account currencies are same
|
||||||
(frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency)) {
|
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("target_exchange_rate", frm.doc.source_exchange_rate);
|
||||||
frm.set_value("base_received_amount", frm.doc.base_paid_amount);
|
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.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)) {
|
&& 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
|
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"
|
} else if (frm.doc.payment_type == "Pay"
|
||||||
&& frm.doc.base_total_allocated_amount < frm.doc.base_paid_amount - total_deductions
|
&& 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)) {
|
&& 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_mandatory()
|
||||||
self.validate_reference_documents()
|
self.validate_reference_documents()
|
||||||
self.set_tax_withholding()
|
self.set_tax_withholding()
|
||||||
self.apply_taxes()
|
|
||||||
self.set_amounts()
|
self.set_amounts()
|
||||||
|
self.validate_amounts()
|
||||||
|
self.apply_taxes()
|
||||||
|
self.set_amounts_after_tax()
|
||||||
self.clear_unallocated_reference_document_rows()
|
self.clear_unallocated_reference_document_rows()
|
||||||
self.validate_payment_against_negative_invoice()
|
self.validate_payment_against_negative_invoice()
|
||||||
self.validate_transaction_reference()
|
self.validate_transaction_reference()
|
||||||
self.set_title()
|
self.set_title()
|
||||||
self.set_remarks()
|
self.set_remarks()
|
||||||
self.validate_duplicate_entry()
|
self.validate_duplicate_entry()
|
||||||
|
self.validate_payment_type_with_outstanding()
|
||||||
self.validate_allocated_amount()
|
self.validate_allocated_amount()
|
||||||
self.validate_paid_invoices()
|
self.validate_paid_invoices()
|
||||||
self.ensure_supplier_is_not_blocked()
|
self.ensure_supplier_is_not_blocked()
|
||||||
@@ -118,6 +121,11 @@ class PaymentEntry(AccountsController):
|
|||||||
if not self.get(field):
|
if not self.get(field):
|
||||||
self.set(field, bank_data.account)
|
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):
|
def validate_allocated_amount(self):
|
||||||
for d in self.get("references"):
|
for d in self.get("references"):
|
||||||
if (flt(d.allocated_amount))> 0:
|
if (flt(d.allocated_amount))> 0:
|
||||||
@@ -185,7 +193,7 @@ class PaymentEntry(AccountsController):
|
|||||||
for field, value in iteritems(ref_details):
|
for field, value in iteritems(ref_details):
|
||||||
if d.exchange_gain_loss:
|
if d.exchange_gain_loss:
|
||||||
# for cases where gain/loss is booked into invoice
|
# for cases where gain/loss is booked into invoice
|
||||||
# exchange_gain_loss is calculated from invoice & populated
|
# exchange_gain_loss is calculated from invoice & populated
|
||||||
# and row.exchange_rate is already set to payment entry's exchange rate
|
# and row.exchange_rate is already set to payment entry's exchange rate
|
||||||
# refer -> `update_reference_in_payment_entry()` in utils.py
|
# refer -> `update_reference_in_payment_entry()` in utils.py
|
||||||
continue
|
continue
|
||||||
@@ -236,7 +244,9 @@ class PaymentEntry(AccountsController):
|
|||||||
self.company_currency, self.posting_date)
|
self.company_currency, self.posting_date)
|
||||||
|
|
||||||
def set_target_exchange_rate(self, ref_doc=None):
|
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 ref_doc:
|
||||||
if self.paid_to_account_currency == ref_doc.currency:
|
if self.paid_to_account_currency == ref_doc.currency:
|
||||||
self.target_exchange_rate = ref_doc.get("exchange_rate")
|
self.target_exchange_rate = ref_doc.get("exchange_rate")
|
||||||
@@ -417,7 +427,7 @@ class PaymentEntry(AccountsController):
|
|||||||
net_total_for_tds = 0
|
net_total_for_tds = 0
|
||||||
if reference.reference_doctype == 'Purchase Order':
|
if reference.reference_doctype == 'Purchase Order':
|
||||||
net_total_for_tds += flt(frappe.db.get_value('Purchase Order', reference.reference_name, 'net_total'))
|
net_total_for_tds += flt(frappe.db.get_value('Purchase Order', reference.reference_name, 'net_total'))
|
||||||
|
|
||||||
if net_total_for_tds:
|
if net_total_for_tds:
|
||||||
net_total = net_total_for_tds
|
net_total = net_total_for_tds
|
||||||
|
|
||||||
@@ -468,13 +478,22 @@ class PaymentEntry(AccountsController):
|
|||||||
def set_amounts(self):
|
def set_amounts(self):
|
||||||
self.set_received_amount()
|
self.set_received_amount()
|
||||||
self.set_amounts_in_company_currency()
|
self.set_amounts_in_company_currency()
|
||||||
self.set_amounts_after_tax()
|
|
||||||
self.set_total_allocated_amount()
|
self.set_total_allocated_amount()
|
||||||
self.set_unallocated_amount()
|
self.set_unallocated_amount()
|
||||||
self.set_difference_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):
|
def set_received_amount(self):
|
||||||
self.base_received_amount = self.base_paid_amount
|
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):
|
def set_amounts_after_tax(self):
|
||||||
applicable_tax = 0
|
applicable_tax = 0
|
||||||
@@ -529,7 +548,7 @@ class PaymentEntry(AccountsController):
|
|||||||
if self.payment_type == "Receive" \
|
if self.payment_type == "Receive" \
|
||||||
and self.base_total_allocated_amount < self.base_received_amount + total_deductions \
|
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):
|
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.base_total_allocated_amount) / self.source_exchange_rate
|
||||||
self.unallocated_amount -= included_taxes
|
self.unallocated_amount -= included_taxes
|
||||||
elif self.payment_type == "Pay" \
|
elif self.payment_type == "Pay" \
|
||||||
@@ -841,7 +860,7 @@ class PaymentEntry(AccountsController):
|
|||||||
|
|
||||||
if account_details:
|
if account_details:
|
||||||
row.update(account_details)
|
row.update(account_details)
|
||||||
|
|
||||||
if not row.get('amount'):
|
if not row.get('amount'):
|
||||||
# if no difference amount
|
# if no difference amount
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -11,4 +11,4 @@ frappe.listview_settings['Payment Entry'] = {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ class TestPaymentEntry(unittest.TestCase):
|
|||||||
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC")
|
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC")
|
||||||
pe.reference_no = "1"
|
pe.reference_no = "1"
|
||||||
pe.reference_date = "2016-01-01"
|
pe.reference_date = "2016-01-01"
|
||||||
pe.target_exchange_rate = 50
|
pe.source_exchange_rate = 50
|
||||||
pe.insert()
|
pe.insert()
|
||||||
pe.submit()
|
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 = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC")
|
||||||
pe.reference_no = "1"
|
pe.reference_no = "1"
|
||||||
pe.reference_date = "2016-01-01"
|
pe.reference_date = "2016-01-01"
|
||||||
pe.target_exchange_rate = 50
|
pe.source_exchange_rate = 50
|
||||||
pe.insert()
|
pe.insert()
|
||||||
pe.submit()
|
pe.submit()
|
||||||
|
|
||||||
@@ -295,6 +295,34 @@ class TestPaymentEntry(unittest.TestCase):
|
|||||||
outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"))
|
outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"))
|
||||||
self.assertEqual(outstanding_amount, 80)
|
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):
|
def test_payment_entry_retrieves_last_exchange_rate(self):
|
||||||
from erpnext.setup.doctype.currency_exchange.test_currency_exchange import test_records, save_new_records
|
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 = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC")
|
||||||
pe.reference_no = "1"
|
pe.reference_no = "1"
|
||||||
pe.reference_date = "2016-01-01"
|
pe.reference_date = "2016-01-01"
|
||||||
pe.target_exchange_rate = 55
|
pe.source_exchange_rate = 55
|
||||||
|
|
||||||
pe.append("deductions", {
|
pe.append("deductions", {
|
||||||
"account": "_Test Exchange Gain/Loss - _TC",
|
"account": "_Test Exchange Gain/Loss - _TC",
|
||||||
|
|||||||
@@ -57,4 +57,4 @@ QUnit.test("test payment entry", function(assert) {
|
|||||||
() => frappe.timeout(3),
|
() => frappe.timeout(3),
|
||||||
() => done()
|
() => done()
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -25,4 +25,4 @@ QUnit.test("test payment entry", function(assert) {
|
|||||||
() => frappe.timeout(0.3),
|
() => frappe.timeout(0.3),
|
||||||
() => done()
|
() => done()
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -64,4 +64,4 @@ QUnit.test("test payment entry", function(assert) {
|
|||||||
},
|
},
|
||||||
() => done()
|
() => done()
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,19 +9,19 @@ from frappe.model.document import Document
|
|||||||
class PaymentGatewayAccount(Document):
|
class PaymentGatewayAccount(Document):
|
||||||
def autoname(self):
|
def autoname(self):
|
||||||
self.name = self.payment_gateway + " - " + self.currency
|
self.name = self.payment_gateway + " - " + self.currency
|
||||||
|
|
||||||
def validate(self):
|
def validate(self):
|
||||||
self.currency = frappe.db.get_value("Account", self.payment_account, "account_currency")
|
self.currency = frappe.db.get_value("Account", self.payment_account, "account_currency")
|
||||||
|
|
||||||
self.update_default_payment_gateway()
|
self.update_default_payment_gateway()
|
||||||
self.set_as_default_if_not_set()
|
self.set_as_default_if_not_set()
|
||||||
|
|
||||||
def update_default_payment_gateway(self):
|
def update_default_payment_gateway(self):
|
||||||
if self.is_default:
|
if self.is_default:
|
||||||
frappe.db.sql("""update `tabPayment Gateway Account` set is_default = 0
|
frappe.db.sql("""update `tabPayment Gateway Account` set is_default = 0
|
||||||
where is_default = 1 """)
|
where is_default = 1 """)
|
||||||
|
|
||||||
def set_as_default_if_not_set(self):
|
def set_as_default_if_not_set(self):
|
||||||
if not frappe.db.get_value("Payment Gateway Account",
|
if not frappe.db.get_value("Payment Gateway Account",
|
||||||
{"is_default": 1, "name": ("!=", self.name)}, "name"):
|
{"is_default": 1, "name": ("!=", self.name)}, "name"):
|
||||||
self.is_default = 1
|
self.is_default = 1
|
||||||
|
|||||||
@@ -136,4 +136,4 @@ frappe.ui.form.on('Payment Order', {
|
|||||||
|
|
||||||
dialog.show();
|
dialog.show();
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -9,4 +9,4 @@ def get_data():
|
|||||||
'items': ['Payment Entry', 'Journal Entry']
|
'items': ['Payment Entry', 'Journal Entry']
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,4 +46,4 @@ def create_payment_order_against_payment_entry(ref_doc, order_type):
|
|||||||
doc = make_payment_order(ref_doc.name, payment_order)
|
doc = make_payment_order(ref_doc.name, payment_order)
|
||||||
doc.save()
|
doc.save()
|
||||||
doc.submit()
|
doc.submit()
|
||||||
return doc
|
return doc
|
||||||
|
|||||||
@@ -307,4 +307,4 @@ def reconcile_dr_cr_note(dr_cr_notes, company):
|
|||||||
]
|
]
|
||||||
})
|
})
|
||||||
jv.flags.ignore_mandatory = True
|
jv.flags.ignore_mandatory = True
|
||||||
jv.submit()
|
jv.submit()
|
||||||
|
|||||||
@@ -541,4 +541,4 @@ def make_payment_order(source_name, target_doc=None):
|
|||||||
}
|
}
|
||||||
}, target_doc, set_missing_values)
|
}, target_doc, set_missing_values)
|
||||||
|
|
||||||
return doclist
|
return doclist
|
||||||
|
|||||||
@@ -138,4 +138,4 @@ class TestPaymentRequest(unittest.TestCase):
|
|||||||
|
|
||||||
# Try to make Payment Request more than SO amount, should give validation
|
# Try to make Payment Request more than SO amount, should give validation
|
||||||
pr2.grand_total = 900
|
pr2.grand_total = 900
|
||||||
self.assertRaises(frappe.ValidationError, pr2.save)
|
self.assertRaises(frappe.ValidationError, pr2.save)
|
||||||
|
|||||||
@@ -19,4 +19,4 @@ frappe.ui.form.on('Payment Term', {
|
|||||||
frm.set_df_property("discount", "description", description);
|
frm.set_df_property("discount", "description", description);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -3,6 +3,6 @@
|
|||||||
|
|
||||||
frappe.ui.form.on('Payment Terms Template', {
|
frappe.ui.form.on('Payment Terms Template', {
|
||||||
setup: function(frm) {
|
setup: function(frm) {
|
||||||
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -30,4 +30,4 @@ def get_data():
|
|||||||
'items': ['Customer Group', 'Supplier Group']
|
'items': ['Customer Group', 'Supplier Group']
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,9 +50,13 @@ class PeriodClosingVoucher(AccountsController):
|
|||||||
.format(pce[0][0], self.posting_date))
|
.format(pce[0][0], self.posting_date))
|
||||||
|
|
||||||
def make_gl_entries(self):
|
def make_gl_entries(self):
|
||||||
|
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 = []
|
gl_entries = []
|
||||||
net_pl_balance = 0
|
|
||||||
|
|
||||||
pl_accounts = self.get_pl_balances()
|
pl_accounts = self.get_pl_balances()
|
||||||
|
|
||||||
for acc in pl_accounts:
|
for acc in pl_accounts:
|
||||||
@@ -60,6 +64,7 @@ class PeriodClosingVoucher(AccountsController):
|
|||||||
gl_entries.append(self.get_gl_dict({
|
gl_entries.append(self.get_gl_dict({
|
||||||
"account": acc.account,
|
"account": acc.account,
|
||||||
"cost_center": acc.cost_center,
|
"cost_center": acc.cost_center,
|
||||||
|
"finance_book": acc.finance_book,
|
||||||
"account_currency": acc.account_currency,
|
"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_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,
|
"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
|
"credit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) > 0 else 0
|
||||||
}, item=acc))
|
}, 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:
|
return gl_entries
|
||||||
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)
|
|
||||||
|
|
||||||
from erpnext.accounts.general_ledger import make_gl_entries
|
|
||||||
make_gl_entries(gl_entries)
|
|
||||||
|
|
||||||
def get_pnl_gl_entry(self, net_pl_balance):
|
def get_pnl_gl_entry(self, pl_accounts):
|
||||||
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):
|
|
||||||
company_cost_center = frappe.db.get_value("Company", self.company, "cost_center")
|
company_cost_center = frappe.db.get_value("Company", self.company, "cost_center")
|
||||||
gl_entries = []
|
gl_entries = []
|
||||||
|
|
||||||
@@ -104,6 +87,7 @@ class PeriodClosingVoucher(AccountsController):
|
|||||||
gl_entry = self.get_gl_dict({
|
gl_entry = self.get_gl_dict({
|
||||||
"account": self.closing_account_head,
|
"account": self.closing_account_head,
|
||||||
"cost_center": acc.cost_center or company_cost_center,
|
"cost_center": acc.cost_center or company_cost_center,
|
||||||
|
"finance_book": acc.finance_book,
|
||||||
"account_currency": acc.account_currency,
|
"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_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,
|
"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):
|
def get_pl_balances(self):
|
||||||
"""Get balance for dimension-wise pl accounts"""
|
"""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()
|
self.accounting_dimensions = get_accounting_dimensions()
|
||||||
for dimension in self.accounting_dimensions:
|
for dimension in self.accounting_dimensions:
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import frappe
|
|||||||
from frappe.utils import flt, today
|
from frappe.utils import flt, today
|
||||||
from erpnext.accounts.utils import get_fiscal_year, now
|
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.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
|
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
|
||||||
|
|
||||||
class TestPeriodClosingVoucher(unittest.TestCase):
|
class TestPeriodClosingVoucher(unittest.TestCase):
|
||||||
@@ -118,6 +119,58 @@ class TestPeriodClosingVoucher(unittest.TestCase):
|
|||||||
|
|
||||||
self.assertTrue(pcv_gle, expected_gle)
|
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):
|
def make_period_closing_voucher(self):
|
||||||
pcv = frappe.get_doc({
|
pcv = frappe.get_doc({
|
||||||
"doctype": "Period Closing Voucher",
|
"doctype": "Period Closing Voucher",
|
||||||
@@ -139,7 +192,7 @@ def create_company():
|
|||||||
'company_name': "Test PCV Company",
|
'company_name': "Test PCV Company",
|
||||||
'country': 'United States',
|
'country': 'United States',
|
||||||
'default_currency': 'USD'
|
'default_currency': 'USD'
|
||||||
})
|
})
|
||||||
company.insert(ignore_if_duplicate = True)
|
company.insert(ignore_if_duplicate = True)
|
||||||
return company.name
|
return company.name
|
||||||
|
|
||||||
|
|||||||
@@ -20,9 +20,9 @@ frappe.ui.form.on('POS Closing Entry', {
|
|||||||
frm.set_query("pos_opening_entry", function(doc) {
|
frm.set_query("pos_opening_entry", function(doc) {
|
||||||
return { filters: { 'status': 'Open', 'docstatus': 1 } };
|
return { filters: { 'status': 'Open', 'docstatus': 1 } };
|
||||||
});
|
});
|
||||||
|
|
||||||
if (frm.doc.docstatus === 0 && !frm.doc.amended_from) frm.set_value("period_end_date", frappe.datetime.now_datetime());
|
if (frm.doc.docstatus === 0 && !frm.doc.amended_from) frm.set_value("period_end_date", frappe.datetime.now_datetime());
|
||||||
|
|
||||||
frappe.realtime.on('closing_process_complete', async function(data) {
|
frappe.realtime.on('closing_process_complete', async function(data) {
|
||||||
await frm.reload_doc();
|
await frm.reload_doc();
|
||||||
if (frm.doc.status == 'Failed' && frm.doc.error_message && data.user == frappe.session.user) {
|
if (frm.doc.status == 'Failed' && frm.doc.error_message && data.user == frappe.session.user) {
|
||||||
@@ -43,7 +43,7 @@ frappe.ui.form.on('POS Closing Entry', {
|
|||||||
const issue = '<a id="jump_to_error" style="text-decoration: underline;">issue</a>';
|
const issue = '<a id="jump_to_error" style="text-decoration: underline;">issue</a>';
|
||||||
frm.dashboard.set_headline(
|
frm.dashboard.set_headline(
|
||||||
__('POS Closing failed while running in a background process. You can resolve the {0} and retry the process again.', [issue]));
|
__('POS Closing failed while running in a background process. You can resolve the {0} and retry the process again.', [issue]));
|
||||||
|
|
||||||
$('#jump_to_error').on('click', (e) => {
|
$('#jump_to_error').on('click', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
frappe.utils.scroll_to(
|
frappe.utils.scroll_to(
|
||||||
|
|||||||
@@ -110,17 +110,13 @@ erpnext.selling.POSInvoiceController = erpnext.selling.SellingController.extend(
|
|||||||
this.frm.refresh_field("base_paid_amount");
|
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)) {
|
if (cint(this.frm.doc.write_off_outstanding_amount_automatically)) {
|
||||||
frappe.model.round_floats_in(this.frm.doc, ["grand_total", "paid_amount"]);
|
frappe.model.round_floats_in(this.frm.doc, ["grand_total", "paid_amount"]);
|
||||||
// this will make outstanding amount 0
|
// this will make outstanding amount 0
|
||||||
this.frm.set_value("write_off_amount",
|
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"))
|
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);
|
this.calculate_outstanding_amount(false);
|
||||||
@@ -235,4 +231,4 @@ frappe.ui.form.on('POS Invoice', {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -99,6 +99,7 @@
|
|||||||
"loyalty_redemption_account",
|
"loyalty_redemption_account",
|
||||||
"loyalty_redemption_cost_center",
|
"loyalty_redemption_cost_center",
|
||||||
"section_break_49",
|
"section_break_49",
|
||||||
|
"coupon_code",
|
||||||
"apply_discount_on",
|
"apply_discount_on",
|
||||||
"base_discount_amount",
|
"base_discount_amount",
|
||||||
"column_break_51",
|
"column_break_51",
|
||||||
@@ -595,7 +596,8 @@
|
|||||||
{
|
{
|
||||||
"fieldname": "scan_barcode",
|
"fieldname": "scan_barcode",
|
||||||
"fieldtype": "Data",
|
"fieldtype": "Data",
|
||||||
"label": "Scan Barcode"
|
"label": "Scan Barcode",
|
||||||
|
"options": "Barcode"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"allow_bulk_edit": 1,
|
"allow_bulk_edit": 1,
|
||||||
@@ -1182,7 +1184,8 @@
|
|||||||
"label": "Write Off Amount",
|
"label": "Write Off Amount",
|
||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "currency",
|
"options": "currency",
|
||||||
"print_hide": 1
|
"print_hide": 1,
|
||||||
|
"read_only_depends_on": "eval: doc.write_off_outstanding_amount_automatically"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"fieldname": "base_write_off_amount",
|
"fieldname": "base_write_off_amount",
|
||||||
@@ -1548,12 +1551,20 @@
|
|||||||
"no_copy": 1,
|
"no_copy": 1,
|
||||||
"options": "Sales Invoice",
|
"options": "Sales Invoice",
|
||||||
"read_only": 1
|
"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",
|
"icon": "fa fa-file-text",
|
||||||
"is_submittable": 1,
|
"is_submittable": 1,
|
||||||
"links": [],
|
"links": [],
|
||||||
"modified": "2021-07-29 13:37:20.636171",
|
"modified": "2021-08-24 18:19:20.728433",
|
||||||
"modified_by": "Administrator",
|
"modified_by": "Administrator",
|
||||||
"module": "Accounts",
|
"module": "Accounts",
|
||||||
"name": "POS Invoice",
|
"name": "POS Invoice",
|
||||||
|
|||||||
@@ -44,6 +44,9 @@ class POSInvoice(SalesInvoice):
|
|||||||
self.validate_pos()
|
self.validate_pos()
|
||||||
self.validate_payment_amount()
|
self.validate_payment_amount()
|
||||||
self.validate_loyalty_transaction()
|
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):
|
def on_submit(self):
|
||||||
# create the loyalty point ledger entry if the customer is enrolled in any loyalty program
|
# 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.check_phone_payments()
|
||||||
self.set_status(update=True)
|
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):
|
def before_cancel(self):
|
||||||
if self.consolidated_invoice and frappe.db.get_value('Sales Invoice', self.consolidated_invoice, 'docstatus') == 1:
|
if self.consolidated_invoice and frappe.db.get_value('Sales Invoice', self.consolidated_invoice, 'docstatus') == 1:
|
||||||
pos_closing_entry = frappe.get_all(
|
pos_closing_entry = frappe.get_all(
|
||||||
@@ -84,6 +91,10 @@ class POSInvoice(SalesInvoice):
|
|||||||
against_psi_doc.delete_loyalty_point_entry()
|
against_psi_doc.delete_loyalty_point_entry()
|
||||||
against_psi_doc.make_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):
|
def check_phone_payments(self):
|
||||||
for pay in self.payments:
|
for pay in self.payments:
|
||||||
if pay.type == "Phone" and pay.amount >= 0:
|
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"))
|
.format(item.idx, bold_delivered_serial_nos), title=_("Item Unavailable"))
|
||||||
|
|
||||||
def validate_stock_availablility(self):
|
def validate_stock_availablility(self):
|
||||||
if self.is_return:
|
if self.is_return or self.docstatus != 1:
|
||||||
return
|
return
|
||||||
|
|
||||||
allow_negative_stock = frappe.db.get_single_value('Stock Settings', 'allow_negative_stock')
|
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.get("items")[0].serial_no = serial_nos[0]
|
||||||
pos2.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 1000})
|
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):
|
def test_delivered_serialized_item_transaction(self):
|
||||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item
|
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.get("items")[0].serial_no = serial_nos[0]
|
||||||
pos2.append("payments", {'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 1000})
|
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):
|
def test_loyalty_points(self):
|
||||||
from erpnext.accounts.doctype.loyalty_program.test_loyalty_program import create_records
|
from erpnext.accounts.doctype.loyalty_program.test_loyalty_program import create_records
|
||||||
|
|||||||
@@ -5,10 +5,10 @@ frappe.ui.form.on('POS Invoice Merge Log', {
|
|||||||
setup: function(frm) {
|
setup: function(frm) {
|
||||||
frm.set_query("pos_invoice", "pos_invoices", doc => {
|
frm.set_query("pos_invoice", "pos_invoices", doc => {
|
||||||
return{
|
return{
|
||||||
filters: {
|
filters: {
|
||||||
'docstatus': 1,
|
'docstatus': 1,
|
||||||
'customer': doc.customer,
|
'customer': doc.customer,
|
||||||
'consolidated_invoice': ''
|
'consolidated_invoice': ''
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -354,4 +354,4 @@ def safe_load_json(message):
|
|||||||
except Exception:
|
except Exception:
|
||||||
json_message = message
|
json_message = message
|
||||||
|
|
||||||
return json_message
|
return json_message
|
||||||
|
|||||||
@@ -147,4 +147,3 @@ class TestPOSInvoiceMergeLog(unittest.TestCase):
|
|||||||
frappe.set_user("Administrator")
|
frappe.set_user("Administrator")
|
||||||
frappe.db.sql("delete from `tabPOS Profile`")
|
frappe.db.sql("delete from `tabPOS Profile`")
|
||||||
frappe.db.sql("delete from `tabPOS Invoice`")
|
frappe.db.sql("delete from `tabPOS Invoice`")
|
||||||
|
|
||||||
|
|||||||
@@ -53,4 +53,4 @@ frappe.ui.form.on('POS Opening Entry', {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -38,4 +38,4 @@ class POSOpeningEntry(StatusUpdater):
|
|||||||
frappe.throw(msg.format(", ".join(invalid_modes)), title=_("Missing Account"))
|
frappe.throw(msg.format(", ".join(invalid_modes)), title=_("Missing Account"))
|
||||||
|
|
||||||
def on_submit(self):
|
def on_submit(self):
|
||||||
self.set_status(update=True)
|
self.set_status(update=True)
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ def create_opening_entry(pos_profile, user):
|
|||||||
balance_details.append(frappe._dict({
|
balance_details.append(frappe._dict({
|
||||||
'mode_of_payment': d.mode_of_payment
|
'mode_of_payment': d.mode_of_payment
|
||||||
}))
|
}))
|
||||||
|
|
||||||
entry.set("balance_details", balance_details)
|
entry.set("balance_details", balance_details)
|
||||||
entry.submit()
|
entry.submit()
|
||||||
|
|
||||||
return entry.as_dict()
|
return entry.as_dict()
|
||||||
|
|||||||
@@ -8,4 +8,4 @@ from frappe.model.document import Document
|
|||||||
|
|
||||||
class POSSettings(Document):
|
class POSSettings(Document):
|
||||||
def validate(self):
|
def validate(self):
|
||||||
pass
|
pass
|
||||||
|
|||||||
@@ -198,12 +198,19 @@ def apply_pricing_rule(args, doc=None):
|
|||||||
set_serial_nos_based_on_fifo = frappe.db.get_single_value("Stock Settings",
|
set_serial_nos_based_on_fifo = frappe.db.get_single_value("Stock Settings",
|
||||||
"automatically_set_serial_nos_based_on_fifo")
|
"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:
|
for item in item_list:
|
||||||
args_copy = copy.deepcopy(args)
|
args_copy = copy.deepcopy(args)
|
||||||
args_copy.update(item)
|
args_copy.update(item)
|
||||||
data = get_pricing_rule_for_item(args_copy, item.get('price_list_rate'), doc=doc)
|
data = get_pricing_rule_for_item(args_copy, item.get('price_list_rate'), doc=doc)
|
||||||
out.append(data)
|
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))
|
out[0].update(get_serial_no_for_item(args_copy))
|
||||||
|
|
||||||
return out
|
return out
|
||||||
|
|||||||
@@ -605,4 +605,4 @@ def delete_existing_pricing_rules():
|
|||||||
for doctype in ["Pricing Rule", "Pricing Rule Item Code",
|
for doctype in ["Pricing Rule", "Pricing Rule Item Code",
|
||||||
"Pricing Rule Item Group", "Pricing Rule Brand"]:
|
"Pricing Rule Item Group", "Pricing Rule Brand"]:
|
||||||
|
|
||||||
frappe.db.sql("delete from `tab{0}`".format(doctype))
|
frappe.db.sql("delete from `tab{0}`".format(doctype))
|
||||||
|
|||||||
@@ -26,4 +26,3 @@ QUnit.test("test pricing rule", function(assert) {
|
|||||||
() => done()
|
() => done()
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -31,4 +31,4 @@ class ProcessDeferredAccounting(Document):
|
|||||||
'against_voucher': self.name
|
'against_voucher': self.name
|
||||||
})
|
})
|
||||||
|
|
||||||
make_reverse_gl_entries(gl_entries=gl_entries)
|
make_reverse_gl_entries(gl_entries=gl_entries)
|
||||||
|
|||||||
@@ -45,4 +45,4 @@ class TestProcessDeferredAccounting(unittest.TestCase):
|
|||||||
["Sales - _TC", 0.0, 33.85, "2019-01-31"]
|
["Sales - _TC", 0.0, 33.85, "2019-01-31"]
|
||||||
]
|
]
|
||||||
|
|
||||||
check_gl_entries(self, si.name, expected_gle, "2019-01-10")
|
check_gl_entries(self, si.name, expected_gle, "2019-01-10")
|
||||||
|
|||||||
@@ -106,4 +106,4 @@
|
|||||||
{{ terms_and_conditions }}
|
{{ terms_and_conditions }}
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -284,4 +284,4 @@ def send_auto_email():
|
|||||||
selected = frappe.get_list('Process Statement Of Accounts', filters={'to_date': format_date(today()), 'enable_auto_email': 1})
|
selected = frappe.get_list('Process Statement Of Accounts', filters={'to_date': format_date(today()), 'enable_auto_email': 1})
|
||||||
for entry in selected:
|
for entry in selected:
|
||||||
send_emails(entry.name, from_scheduler=True)
|
send_emails(entry.name, from_scheduler=True)
|
||||||
return True
|
return True
|
||||||
|
|||||||
@@ -48,4 +48,4 @@ frappe.ui.form.on('Promotional Scheme', {
|
|||||||
frm.doc.apply_on === key ? 1 : 0);
|
frm.doc.apply_on === key ? 1 : 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user