mirror of
https://github.com/frappe/erpnext.git
synced 2026-07-29 23:43:40 +00:00
Compare commits
440 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
dc35138356 | ||
|
|
08617f7700 | ||
|
|
cfecf3c5b1 | ||
|
|
831100b657 | ||
|
|
f296a2f58a | ||
|
|
aae557e8a1 | ||
|
|
0f92c4b85b | ||
|
|
217df1761d | ||
|
|
d355741462 | ||
|
|
7b6ed4f16e | ||
|
|
c5a682e9d9 | ||
|
|
b4fa105555 | ||
|
|
e1aef6f0db | ||
|
|
6f35d2b52d | ||
|
|
197f57c050 | ||
|
|
c74fa7546b | ||
|
|
233ef8752d | ||
|
|
afd9e7546f | ||
|
|
8b1532d691 | ||
|
|
a887c29e06 | ||
|
|
7114b72be8 | ||
|
|
92ab345daf | ||
|
|
79dd4d707b | ||
|
|
a3a733fa91 | ||
|
|
7a9b014f72 | ||
|
|
61b4a98356 | ||
|
|
46db69b304 | ||
|
|
a0be41322d | ||
|
|
05f29988f4 | ||
|
|
d7903de51c | ||
|
|
0e31cc9ff8 | ||
|
|
32280a11aa | ||
|
|
a2f9d2f89c | ||
|
|
5fc5355b96 | ||
|
|
30c73e6b35 | ||
|
|
7a6b88c791 | ||
|
|
9c42124fbc | ||
|
|
0378b15412 | ||
|
|
3f86369604 | ||
|
|
cc99d92116 | ||
|
|
f880975438 | ||
|
|
daa8db4d95 | ||
|
|
9ff39e2d1b | ||
|
|
9d70c5d13f | ||
|
|
a9a82919e7 | ||
|
|
723ed07642 | ||
|
|
86b531aea6 | ||
|
|
781a420593 | ||
|
|
08f709c2a2 | ||
|
|
a849f6e21e | ||
|
|
6ff8387d56 | ||
|
|
2af9ff9c33 | ||
|
|
01905cad2f | ||
|
|
58260e4f0a | ||
|
|
a01869bb20 | ||
|
|
3a92615f23 | ||
|
|
eca8db7405 | ||
|
|
58c4cfc0d7 | ||
|
|
ab52a4db75 | ||
|
|
c8d3a8c0f5 | ||
|
|
aab88eee3e | ||
|
|
d9f4c83567 | ||
|
|
b96dd366ce | ||
|
|
597ec83af7 | ||
|
|
e802bdd186 | ||
|
|
3a949bb298 | ||
|
|
1e920dd0d8 | ||
|
|
09536f402e | ||
|
|
581f26b7a0 | ||
|
|
0418a2f70a | ||
|
|
5e3338744c | ||
|
|
da3762700c | ||
|
|
2c9fccd8ba | ||
|
|
286c4fa640 | ||
|
|
a144e002db | ||
|
|
553dabaa08 | ||
|
|
55d0d32c77 | ||
|
|
1f93745eef | ||
|
|
e3a02dd5f6 | ||
|
|
153733414f | ||
|
|
ac535f0ce9 | ||
|
|
3886529787 | ||
|
|
d2cd713b89 | ||
|
|
6b378e1669 | ||
|
|
a4fc30bbe4 | ||
|
|
b4b0e4424d | ||
|
|
f3bdcc2a84 | ||
|
|
19901c14c9 | ||
|
|
714d686e50 | ||
|
|
094dc1dee6 | ||
|
|
cdcf424ba5 | ||
|
|
2a2b884e32 | ||
|
|
c1e00f4daa | ||
|
|
0a22aab6bb | ||
|
|
43c6d1a518 | ||
|
|
090219814e | ||
|
|
510dc60bf0 | ||
|
|
12d520a366 | ||
|
|
a06a527fe6 | ||
|
|
9a7681535f | ||
|
|
8e1a612b3b | ||
|
|
21085bf2be | ||
|
|
8e9413829d | ||
|
|
8208f878ff | ||
|
|
a7130649cd | ||
|
|
b2d9ffa4ca | ||
|
|
cc4f13a862 | ||
|
|
60681baf80 | ||
|
|
ea6049078a | ||
|
|
d83e8c56b2 | ||
|
|
e0ade62e38 | ||
|
|
c78b921412 | ||
|
|
9e9e415c5f | ||
|
|
0692e5eb78 | ||
|
|
3fa5eec07b | ||
|
|
ab1bf1af19 | ||
|
|
15e0861e82 | ||
|
|
aa493a25f8 | ||
|
|
1c2915f74b | ||
|
|
391b3b67cb | ||
|
|
0361c50503 | ||
|
|
517a3071cf | ||
|
|
0e30e705c5 | ||
|
|
940df7563b | ||
|
|
f390872944 | ||
|
|
cfd18d4e03 | ||
|
|
6c73439e7f | ||
|
|
2389e2c438 | ||
|
|
c2341ca8e3 | ||
|
|
3b810ea8da | ||
|
|
f85d6aeecd | ||
|
|
a13762b05f | ||
|
|
dd5a0a1f26 | ||
|
|
a6ad0b0ec8 | ||
|
|
077e20f7ae | ||
|
|
73c6d2e44c | ||
|
|
d7a13ceb10 | ||
|
|
6eadd6657d | ||
|
|
0c890a110d | ||
|
|
0f420baaba | ||
|
|
b32d96fc24 | ||
|
|
cfc9e18749 | ||
|
|
25d6d08329 | ||
|
|
9bf4c754c8 | ||
|
|
79b02db156 | ||
|
|
e55dd7233d | ||
|
|
3ba969ad58 | ||
|
|
f93cb0a6ba | ||
|
|
e23cfd22ca | ||
|
|
0d1b022ea0 | ||
|
|
aca3772f7d | ||
|
|
569815b5ad | ||
|
|
80b696ce25 | ||
|
|
ab7570b7e5 | ||
|
|
c87f8c6f00 | ||
|
|
c5c4de885b | ||
|
|
a970bcc56d | ||
|
|
5e2d822509 | ||
|
|
6be1475882 | ||
|
|
43f05d1de1 | ||
|
|
26bb028ec4 | ||
|
|
1637f0aeaf | ||
|
|
353f64caf8 | ||
|
|
5afb00a7f4 | ||
|
|
b0ec0f545e | ||
|
|
7088513da9 | ||
|
|
8908d4ee8f | ||
|
|
8133970944 | ||
|
|
dac7ede911 | ||
|
|
b052498348 | ||
|
|
d62de6c8e5 | ||
|
|
d37cc9a5d0 | ||
|
|
c7062d8be7 | ||
|
|
1435e30ad5 | ||
|
|
b2ff0bda32 | ||
|
|
ff42d7b5ed | ||
|
|
35a4cae2f6 | ||
|
|
a76cafecb0 | ||
|
|
715ecc31c8 | ||
|
|
09145674ec | ||
|
|
11eebddb79 | ||
|
|
7d7417af29 | ||
|
|
6edb6e0b09 | ||
|
|
e057575661 | ||
|
|
b7b5eeb92b | ||
|
|
85731053fd | ||
|
|
9f3eb9e077 | ||
|
|
5ac8dbfceb | ||
|
|
f4f2301e5b | ||
|
|
7888336524 | ||
|
|
b8c2e02c67 | ||
|
|
a9e9efbd23 | ||
|
|
2f175e6d85 | ||
|
|
df817a858b | ||
|
|
4a73059ed3 | ||
|
|
552615525c | ||
|
|
25e408fd2a | ||
|
|
375a6f67ae | ||
|
|
01095799e8 | ||
|
|
ead8d82a84 | ||
|
|
2c229c6403 | ||
|
|
5fb78a375d | ||
|
|
8fcad571f5 | ||
|
|
53d7e667dd | ||
|
|
7395716476 | ||
|
|
c068b6a885 | ||
|
|
02d28c5908 | ||
|
|
90d0d24d1a | ||
|
|
6aab14f9cf | ||
|
|
163dbdca34 | ||
|
|
dcc99a2644 | ||
|
|
f5297cf386 | ||
|
|
9f7fd16882 | ||
|
|
5296ab1f87 | ||
|
|
e42c14f2cb | ||
|
|
4ef10fd6c3 | ||
|
|
4c0d0e226b | ||
|
|
094caaa03e | ||
|
|
ce51553d29 | ||
|
|
f41fc21274 | ||
|
|
1f49b77529 | ||
|
|
a0012f8c48 | ||
|
|
4b1c3ad7ac | ||
|
|
a063803224 | ||
|
|
63c7fd90a5 | ||
|
|
c9c02c7c85 | ||
|
|
bc7ef1937e | ||
|
|
75b63c5b4c | ||
|
|
625191d20a | ||
|
|
1417c7e828 | ||
|
|
a4bbc68945 | ||
|
|
2d7a591c61 | ||
|
|
448a5e1c9c | ||
|
|
7be75adc3f | ||
|
|
fafee7cf61 | ||
|
|
16aa23e454 | ||
|
|
a4d5c5414d | ||
|
|
b104e3595c | ||
|
|
5a06dd1ed1 | ||
|
|
27fe55efe1 | ||
|
|
9089b242ba | ||
|
|
98e511d236 | ||
|
|
93784d3804 | ||
|
|
d29fde0bf3 | ||
|
|
8326925fe8 | ||
|
|
a924b636a4 | ||
|
|
3526ed975c | ||
|
|
d0faec3cc0 | ||
|
|
661a5ce332 | ||
|
|
b289aa3548 | ||
|
|
4ed521162e | ||
|
|
8865d05b4c | ||
|
|
4df46737ef | ||
|
|
09ac547a80 | ||
|
|
c1bbaf07a4 | ||
|
|
73a081d806 | ||
|
|
e1df414f8b | ||
|
|
cefef3b62d | ||
|
|
bd4ee60d1e | ||
|
|
f6a9eec23a | ||
|
|
5742d0836e | ||
|
|
0555223797 | ||
|
|
de93efb304 | ||
|
|
6856033d3c | ||
|
|
0735165ef3 | ||
|
|
1aa5462f36 | ||
|
|
f83514418e | ||
|
|
3c13e8e8b9 | ||
|
|
de0955b8ed | ||
|
|
82af75b853 | ||
|
|
d3ce4f815d | ||
|
|
85bf9203ed | ||
|
|
db12f75681 | ||
|
|
3763169978 | ||
|
|
36a8c431f5 | ||
|
|
5a67431daa | ||
|
|
c4e92b3004 | ||
|
|
2d90e8a2de | ||
|
|
4dacb89da6 | ||
|
|
c552d74746 | ||
|
|
2697c2d869 | ||
|
|
4a950abf2e | ||
|
|
7f10f4eea1 | ||
|
|
5c93260eec | ||
|
|
485d9c133a | ||
|
|
891d9aeee9 | ||
|
|
51f0d6d409 | ||
|
|
80374be724 | ||
|
|
619bf561da | ||
|
|
13c15d0222 | ||
|
|
3d31bccaf6 | ||
|
|
3959bf34ee | ||
|
|
1897ed38df | ||
|
|
126c4efb2c | ||
|
|
7d0bc2bd5a | ||
|
|
0c70ae44c3 | ||
|
|
024dc4a4c9 | ||
|
|
e3567ff31b | ||
|
|
97842ca804 | ||
|
|
d14799a9fa | ||
|
|
e534221245 | ||
|
|
4acfec901e | ||
|
|
a981a8a153 | ||
|
|
11e1c60cd3 | ||
|
|
fe7baae9f7 | ||
|
|
da64113b9a | ||
|
|
fb76cb7a78 | ||
|
|
1bd69b4490 | ||
|
|
62d51d82ea | ||
|
|
6ddc554965 | ||
|
|
5a6fc77751 | ||
|
|
733db826ec | ||
|
|
6643156df6 | ||
|
|
6022f2bcf8 | ||
|
|
b890492dc0 | ||
|
|
6ea108f01d | ||
|
|
628bed1f5a | ||
|
|
4c0e3aa097 | ||
|
|
631e334a3f | ||
|
|
8f4d92eba2 | ||
|
|
2f4193757e | ||
|
|
cba64988df | ||
|
|
17e6fce486 | ||
|
|
05fb3f2d75 | ||
|
|
af6360b273 | ||
|
|
043a47a9c0 | ||
|
|
23e424911c | ||
|
|
a5fbeaa3d3 | ||
|
|
8aeb4b04bd | ||
|
|
f3b07495f6 | ||
|
|
70f89462a8 | ||
|
|
bbc7f474c3 | ||
|
|
26fe30685a | ||
|
|
7bdd27f7e2 | ||
|
|
96ec4aeda0 | ||
|
|
cce65d41b8 | ||
|
|
a44d46e535 | ||
|
|
4f16d17d21 | ||
|
|
759bb0eb62 | ||
|
|
d7aa71aa70 | ||
|
|
32207ad722 | ||
|
|
8a32ad206a | ||
|
|
16bd2ed967 | ||
|
|
393b12a37f | ||
|
|
d1332f6c24 | ||
|
|
f36fa088f8 | ||
|
|
bb63103183 | ||
|
|
7760db7563 | ||
|
|
0ea1e67ce7 | ||
|
|
587b52dcd4 | ||
|
|
efcdf2fd42 | ||
|
|
010a05df48 | ||
|
|
58d565a882 | ||
|
|
b655b07f20 | ||
|
|
afb59fa5c0 | ||
|
|
cb4b86512d | ||
|
|
49f919a4fc | ||
|
|
4a9127f9a6 | ||
|
|
c4a670c8c8 | ||
|
|
a26e2c064a | ||
|
|
3889d0f0a0 | ||
|
|
4e81fb20b9 | ||
|
|
dfba52b834 | ||
|
|
f665e42e2a | ||
|
|
560cc66a36 | ||
|
|
d08953b72b | ||
|
|
34e4ac2398 | ||
|
|
3dbaa3c2d9 | ||
|
|
613d82e12f | ||
|
|
4f2fa173c9 | ||
|
|
d36e635e60 | ||
|
|
e86d21ea15 | ||
|
|
b6fda118b9 | ||
|
|
2c607e5562 | ||
|
|
95fce2395a | ||
|
|
0f0dcd9035 | ||
|
|
ce291c253b | ||
|
|
e85c6ad236 | ||
|
|
df16cdcf31 | ||
|
|
00303858df | ||
|
|
6f54a7b7d8 | ||
|
|
fba8bfc0d0 | ||
|
|
4db4f21d16 | ||
|
|
abf9ef0244 | ||
|
|
22ad81fb57 | ||
|
|
923c5462a2 | ||
|
|
753b3d1c28 | ||
|
|
b9046edb85 | ||
|
|
7932eba733 | ||
|
|
5ba9c82922 | ||
|
|
fc48ce7073 | ||
|
|
8b9a84b568 | ||
|
|
cc4e6a25f6 | ||
|
|
17923863de | ||
|
|
32efea5e38 | ||
|
|
87a2c1d27d | ||
|
|
c581d67bba | ||
|
|
1865e0df7c | ||
|
|
308ae1f155 | ||
|
|
2b54cee4aa | ||
|
|
ba47f89702 | ||
|
|
0ea32faf3d | ||
|
|
7be0736154 | ||
|
|
97bf12734a | ||
|
|
4bccd692e5 | ||
|
|
07f8e6bbfc | ||
|
|
15c7a05879 | ||
|
|
82e76b2c0c | ||
|
|
d6757b7af6 | ||
|
|
b380a02d09 | ||
|
|
64980fed59 | ||
|
|
7df8c0ef82 | ||
|
|
28fe73640b | ||
|
|
7cfe247b2e | ||
|
|
7cc972969f | ||
|
|
d333d2e6eb | ||
|
|
3b78a018aa | ||
|
|
f0ee3d26a7 | ||
|
|
2e87202b3b | ||
|
|
e8883e20cd | ||
|
|
af4d588f64 | ||
|
|
4f0a4a1a2d | ||
|
|
7f7a1b48ed | ||
|
|
f094662f5e | ||
|
|
e2fc03e561 | ||
|
|
3987b4b714 | ||
|
|
e889a58724 | ||
|
|
1638994436 | ||
|
|
ff99492481 | ||
|
|
a65af7a2f5 | ||
|
|
6ba48c58e8 | ||
|
|
3b4f481ca4 | ||
|
|
f62e789173 | ||
|
|
02181c017a | ||
|
|
9f2847e86c | ||
|
|
44d8224a3b | ||
|
|
5ba438af80 | ||
|
|
f817663f11 | ||
|
|
91578757ff | ||
|
|
88bd0674ed |
20
MANIFEST.in
20
MANIFEST.in
@@ -1,20 +0,0 @@
|
||||
include MANIFEST.in
|
||||
include requirements.txt
|
||||
include *.json
|
||||
include *.md
|
||||
include *.py
|
||||
include *.txt
|
||||
include .travis.yml
|
||||
recursive-include erpnext *.txt
|
||||
recursive-include erpnext *.css
|
||||
recursive-include erpnext *.csv
|
||||
recursive-include erpnext *.html
|
||||
recursive-include erpnext *.ico
|
||||
recursive-include erpnext *.js
|
||||
recursive-include erpnext *.json
|
||||
recursive-include erpnext *.md
|
||||
recursive-include erpnext *.png
|
||||
recursive-include erpnext *.py
|
||||
recursive-include erpnext *.svg
|
||||
recursive-include erpnext/public *
|
||||
recursive-exclude * *.pyc
|
||||
@@ -5,7 +5,7 @@ import frappe
|
||||
from erpnext.hooks import regional_overrides
|
||||
from frappe.utils import getdate
|
||||
|
||||
__version__ = '11.1.21'
|
||||
__version__ = '11.1.35'
|
||||
|
||||
def get_default_company(user=None):
|
||||
'''Get default company for user'''
|
||||
|
||||
@@ -42,15 +42,14 @@ frappe.ui.form.on('Account', {
|
||||
// show / hide convert buttons
|
||||
frm.trigger('add_toolbar_buttons');
|
||||
}
|
||||
frm.add_custom_button(__('Update Account Name / Number'), function () {
|
||||
frm.trigger("update_account_number");
|
||||
});
|
||||
}
|
||||
|
||||
if(!frm.doc.__islocal) {
|
||||
frm.add_custom_button(__('Merge Account'), function () {
|
||||
frm.trigger("merge_account");
|
||||
});
|
||||
if (frm.has_perm('write')) {
|
||||
frm.add_custom_button(__('Update Account Name / Number'), function () {
|
||||
frm.trigger("update_account_number");
|
||||
});
|
||||
frm.add_custom_button(__('Merge Account'), function () {
|
||||
frm.trigger("merge_account");
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
account_type: function (frm) {
|
||||
|
||||
@@ -105,20 +105,27 @@ class Account(NestedSet):
|
||||
descendants = get_descendants_of('Company', self.company)
|
||||
if not descendants: return
|
||||
|
||||
acc_name_map = {}
|
||||
acc_name = frappe.db.get_value('Account', self.parent_account, "account_name")
|
||||
parent_acc_name_map = {}
|
||||
parent_acc_name = frappe.db.get_value('Account', self.parent_account, "account_name")
|
||||
for d in frappe.db.get_values('Account',
|
||||
{"company": ["in", descendants], "account_name": acc_name},
|
||||
{"company": ["in", descendants], "account_name": parent_acc_name},
|
||||
["company", "name"], as_dict=True):
|
||||
acc_name_map[d["company"]] = d["name"]
|
||||
parent_acc_name_map[d["company"]] = d["name"]
|
||||
|
||||
if not acc_name_map: return
|
||||
if not parent_acc_name_map: return
|
||||
|
||||
for company in descendants:
|
||||
if not parent_acc_name_map.get(company):
|
||||
frappe.throw(_("While creating account for child Company {0}, parent account {1} not found. Please create the parent account in corresponding COA")
|
||||
.format(company, parent_acc_name))
|
||||
|
||||
doc = frappe.copy_doc(self)
|
||||
doc.flags.ignore_root_company_validation = True
|
||||
doc.update({"company": company, "account_currency": None,
|
||||
"parent": acc_name_map[company], "parent_account": acc_name_map[company]})
|
||||
doc.update({
|
||||
"company": company,
|
||||
"account_currency": None,
|
||||
"parent_account": parent_acc_name_map[company]
|
||||
})
|
||||
doc.save()
|
||||
frappe.msgprint(_("Account {0} is added in the child company {1}")
|
||||
.format(doc.name, company))
|
||||
@@ -261,7 +268,7 @@ def update_account_number(name, account_name, account_number=None):
|
||||
|
||||
new_name = get_account_autoname(account_number, account_name, account.company)
|
||||
if name != new_name:
|
||||
frappe.rename_doc("Account", name, new_name, ignore_permissions=1)
|
||||
frappe.rename_doc("Account", name, new_name, force=1)
|
||||
return new_name
|
||||
|
||||
@frappe.whitelist()
|
||||
@@ -280,7 +287,7 @@ def merge_account(old, new, is_group, root_type, company):
|
||||
frappe.db.set_value("Account", new, "parent_account",
|
||||
frappe.db.get_value("Account", old, "parent_account"))
|
||||
|
||||
frappe.rename_doc("Account", old, new, merge=1, ignore_permissions=1)
|
||||
frappe.rename_doc("Account", old, new, merge=1, force=1)
|
||||
|
||||
return new
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"country_code": "de",
|
||||
"name": "Germany - Kontenplan SKR04",
|
||||
"name": "SKR04 ohne Kontonummern",
|
||||
"tree": {
|
||||
"Bilanz - Aktiva": {
|
||||
"Anlageverm\u00f6gen": {
|
||||
@@ -1383,8 +1383,7 @@
|
||||
"Sonstige Zinsen und \u00e4hnliche Ertr\u00e4ge 1": {
|
||||
"Diskontertr\u00e4ge": {},
|
||||
"Diskontertr\u00e4ge aus verbundenen Unternehmen": {},
|
||||
"Laufende Ertr\u00e4ge aus Anteilen an Kapitalgesellschaften 100% / 50% steuerfrei": {},
|
||||
"Laufende Ertr\u00e4ge aus Anteilen an Kapitalgesellschaften 100% / 50% steuerfrei": {},
|
||||
"Laufende Ertr\u00e4ge aus Anteilen an Kapitalgesellschaften 100% / 50% steuerfrei": {},
|
||||
"Sonstige Zinsen und \u00e4hnliche Ertr\u00e4ge 2": {},
|
||||
"Sonstige Zinsen und \u00e4hnliche Ertr\u00e4ge aus verbundenen Unternehmen": {},
|
||||
"Sonstige Zinsertr\u00e4ge": {},
|
||||
@@ -1703,4 +1702,4 @@
|
||||
"root_type": "Asset"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -38,24 +38,24 @@
|
||||
"Kas": {
|
||||
"Kas Mata Uang Lain": {
|
||||
"Kas USD": {
|
||||
"account_number": "1112.0010",
|
||||
"account_number": "1112.001",
|
||||
"account_type": "Cash"
|
||||
},
|
||||
"account_number": "1112.000"
|
||||
},
|
||||
"Kas Rupiah": {
|
||||
"Kas Besar": {
|
||||
"account_number": "1111.0020",
|
||||
"account_number": "1111.002",
|
||||
"account_type": "Cash"
|
||||
},
|
||||
"Kas Kecil": {
|
||||
"account_number": "1111.0010",
|
||||
"account_number": "1111.001",
|
||||
"account_type": "Cash"
|
||||
},
|
||||
"account_number": "1111.000",
|
||||
"account_type": "Cash"
|
||||
},
|
||||
"account_number": "1110.0000"
|
||||
"account_number": "1110.000"
|
||||
},
|
||||
"Pendapatan Yang Akan di Terima": {
|
||||
"Pendapatan Yang di Terima": {
|
||||
@@ -98,7 +98,7 @@
|
||||
},
|
||||
"account_number": "1130.000"
|
||||
},
|
||||
"account_number": "1100.0000"
|
||||
"account_number": "1100.000"
|
||||
},
|
||||
"Aktiva Tetap": {
|
||||
"Aktiva": {
|
||||
@@ -121,20 +121,20 @@
|
||||
"Investasi": {
|
||||
"Investasi": {
|
||||
"Deposito": {
|
||||
"account_number": "1231.003",
|
||||
"account_number": "1231.300",
|
||||
"is_group": 1
|
||||
},
|
||||
"Investai Saham": {
|
||||
"Investasi Saham": {
|
||||
"Investasi Saham": {
|
||||
"account_number": "1231.0011"
|
||||
"account_number": "1231.101"
|
||||
},
|
||||
"account_number": "1231.001"
|
||||
"account_number": "1231.100"
|
||||
},
|
||||
"Investasi Perumahan": {
|
||||
"Investasi Perumahan": {
|
||||
"account_number": "1231.0021"
|
||||
"account_number": "1231.201"
|
||||
},
|
||||
"account_number": "1231.002"
|
||||
"account_number": "1231.200"
|
||||
},
|
||||
"account_number": "1231.000"
|
||||
},
|
||||
@@ -142,7 +142,7 @@
|
||||
},
|
||||
"account_number": "1200.000"
|
||||
},
|
||||
"account_number": "1000.0000",
|
||||
"account_number": "1000.000",
|
||||
"root_type": "Asset"
|
||||
},
|
||||
"Beban": {
|
||||
@@ -684,4 +684,4 @@
|
||||
"root_type": "Income"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -12,6 +12,11 @@ frappe.ui.form.on('Bank Account', {
|
||||
}
|
||||
};
|
||||
});
|
||||
frm.set_query("party_type", function() {
|
||||
return {
|
||||
query: "erpnext.setup.doctype.party_type.party_type.get_party_type",
|
||||
};
|
||||
});
|
||||
},
|
||||
refresh: function(frm) {
|
||||
frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Bank Account' }
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 0,
|
||||
"allow_import": 1,
|
||||
"allow_rename": 1,
|
||||
"autoname": "",
|
||||
"beta": 0,
|
||||
@@ -975,7 +975,7 @@
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"import": 1,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
|
||||
@@ -21,11 +21,39 @@ class BankAccount(Document):
|
||||
|
||||
def validate(self):
|
||||
self.validate_company()
|
||||
self.validate_iban()
|
||||
|
||||
def validate_company(self):
|
||||
if self.is_company_account and not self.company:
|
||||
frappe.throw(_("Company is manadatory for company account"))
|
||||
|
||||
def validate_iban(self):
|
||||
'''
|
||||
Algorithm: https://en.wikipedia.org/wiki/International_Bank_Account_Number#Validating_the_IBAN
|
||||
'''
|
||||
# IBAN field is optional
|
||||
if not self.iban:
|
||||
return
|
||||
|
||||
def encode_char(c):
|
||||
# Position in the alphabet (A=1, B=2, ...) plus nine
|
||||
return str(9 + ord(c) - 64)
|
||||
|
||||
# remove whitespaces, upper case to get the right number from ord()
|
||||
iban = ''.join(self.iban.split(' ')).upper()
|
||||
|
||||
# Move country code and checksum from the start to the end
|
||||
flipped = iban[4:] + iban[:4]
|
||||
|
||||
# Encode characters as numbers
|
||||
encoded = [encode_char(c) if ord(c) >= 65 and ord(c) <= 90 else c for c in flipped]
|
||||
|
||||
to_check = int(''.join(encoded))
|
||||
|
||||
if to_check % 97 != 1:
|
||||
frappe.throw(_('IBAN is not valid'))
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_bank_account(doctype, docname):
|
||||
doc = frappe.new_doc("Bank Account")
|
||||
|
||||
@@ -4,9 +4,46 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe import ValidationError
|
||||
import unittest
|
||||
|
||||
# test_records = frappe.get_test_records('Bank Account')
|
||||
|
||||
class TestBankAccount(unittest.TestCase):
|
||||
pass
|
||||
|
||||
def test_validate_iban(self):
|
||||
valid_ibans = [
|
||||
'GB82 WEST 1234 5698 7654 32',
|
||||
'DE91 1000 0000 0123 4567 89',
|
||||
'FR76 3000 6000 0112 3456 7890 189'
|
||||
]
|
||||
|
||||
invalid_ibans = [
|
||||
# wrong checksum (3rd place)
|
||||
'GB72 WEST 1234 5698 7654 32',
|
||||
'DE81 1000 0000 0123 4567 89',
|
||||
'FR66 3000 6000 0112 3456 7890 189'
|
||||
]
|
||||
|
||||
bank_account = frappe.get_doc({'doctype':'Bank Account'})
|
||||
|
||||
try:
|
||||
bank_account.validate_iban()
|
||||
except AttributeError:
|
||||
msg = _('BankAccount.validate_iban() failed for empty IBAN')
|
||||
self.fail(msg=msg)
|
||||
|
||||
for iban in valid_ibans:
|
||||
bank_account.iban = iban
|
||||
try:
|
||||
bank_account.validate_iban()
|
||||
except ValidationError:
|
||||
msg = _('BankAccount.validate_iban() failed for valid IBAN {}'.format(iban))
|
||||
self.fail(msg=msg)
|
||||
|
||||
for not_iban in invalid_ibans:
|
||||
bank_account.iban = not_iban
|
||||
msg = _('BankAccount.validate_iban() accepted invalid IBAN {}'.format(not_iban))
|
||||
with self.assertRaises(ValidationError, msg=msg):
|
||||
bank_account.validate_iban()
|
||||
|
||||
@@ -22,7 +22,5 @@ class BankGuarantee(Document):
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_vouchar_detials(column_list, doctype, docname):
|
||||
print (column_list, doctype, docname)
|
||||
return frappe.db.sql(''' select {columns} from `tab{doctype}` where name=%s'''
|
||||
.format(columns=", ".join(json.loads(column_list)), doctype=doctype), docname, as_dict=1)[0]
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Bank Transaction', {
|
||||
onload: function(frm) {
|
||||
onload(frm) {
|
||||
frm.set_query('payment_document', 'payment_entries', function() {
|
||||
return {
|
||||
"filters": {
|
||||
@@ -12,3 +12,21 @@ frappe.ui.form.on('Bank Transaction', {
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
frappe.ui.form.on('Bank Transaction Payments', {
|
||||
payment_entries_remove: function(frm, cdt, cdn) {
|
||||
update_clearance_date(frm, cdt, cdn);
|
||||
}
|
||||
});
|
||||
|
||||
const update_clearance_date = (frm, cdt, cdn) => {
|
||||
if (frm.doc.docstatus === 1) {
|
||||
frappe.xcall('erpnext.accounts.doctype.bank_transaction.bank_transaction.unclear_reference_payment',
|
||||
{doctype: cdt, docname: cdn})
|
||||
.then(e => {
|
||||
if (e == "success") {
|
||||
frappe.show_alert({message:__("Document {0} successfully uncleared", [e]), indicator:'green'});
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,18 +1,32 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
from erpnext.controllers.status_updater import StatusUpdater
|
||||
from frappe.utils import flt
|
||||
from six.moves import reduce
|
||||
from frappe import _
|
||||
|
||||
class BankTransaction(Document):
|
||||
class BankTransaction(StatusUpdater):
|
||||
def after_insert(self):
|
||||
self.unallocated_amount = abs(flt(self.credit) - flt(self.debit))
|
||||
|
||||
def on_submit(self):
|
||||
self.clear_linked_payment_entries()
|
||||
self.set_status()
|
||||
|
||||
def on_update_after_submit(self):
|
||||
allocated_amount = reduce(lambda x, y: flt(x) + flt(y), [x.allocated_amount for x in self.payment_entries])
|
||||
self.update_allocations()
|
||||
self.clear_linked_payment_entries()
|
||||
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])
|
||||
else:
|
||||
allocated_amount = 0
|
||||
|
||||
if allocated_amount:
|
||||
frappe.db.set_value(self.doctype, self.name, "allocated_amount", flt(allocated_amount))
|
||||
@@ -22,4 +36,71 @@ class BankTransaction(Document):
|
||||
frappe.db.set_value(self.doctype, self.name, "allocated_amount", 0)
|
||||
frappe.db.set_value(self.doctype, self.name, "unallocated_amount", abs(flt(self.credit) - flt(self.debit)))
|
||||
|
||||
self.reload()
|
||||
amount = self.debit or self.credit
|
||||
if amount == self.allocated_amount:
|
||||
frappe.db.set_value(self.doctype, self.name, "status", "Reconciled")
|
||||
|
||||
self.reload()
|
||||
|
||||
def clear_linked_payment_entries(self):
|
||||
for payment_entry in self.payment_entries:
|
||||
allocated_amount = get_total_allocated_amount(payment_entry)
|
||||
paid_amount = get_paid_amount(payment_entry)
|
||||
|
||||
if paid_amount and allocated_amount:
|
||||
if flt(allocated_amount[0]["allocated_amount"]) > flt(paid_amount):
|
||||
frappe.throw(_("The total allocated amount ({0}) is greated than the paid amount ({1}).".format(flt(allocated_amount[0]["allocated_amount"]), flt(paid_amount))))
|
||||
elif flt(allocated_amount[0]["allocated_amount"]) == flt(paid_amount):
|
||||
if payment_entry.payment_document in ["Payment Entry", "Journal Entry", "Purchase Invoice", "Expense Claim"]:
|
||||
self.clear_simple_entry(payment_entry)
|
||||
|
||||
elif payment_entry.payment_document == "Sales Invoice":
|
||||
self.clear_sales_invoice(payment_entry)
|
||||
|
||||
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_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 get_total_allocated_amount(payment_entry):
|
||||
return frappe.db.sql("""
|
||||
SELECT
|
||||
SUM(btp.allocated_amount) as allocated_amount,
|
||||
bt.name
|
||||
FROM
|
||||
`tabBank Transaction Payments` as btp
|
||||
LEFT JOIN
|
||||
`tabBank Transaction` bt ON bt.name=btp.parent
|
||||
WHERE
|
||||
btp.payment_document = %s
|
||||
AND
|
||||
btp.payment_entry = %s
|
||||
AND
|
||||
bt.docstatus = 1""", (payment_entry.payment_document, payment_entry.payment_entry), as_dict=True)
|
||||
|
||||
def get_paid_amount(payment_entry):
|
||||
if payment_entry.payment_document in ["Payment Entry", "Sales Invoice", "Purchase Invoice"]:
|
||||
return frappe.db.get_value(payment_entry.payment_document, payment_entry.payment_entry, "paid_amount")
|
||||
|
||||
elif payment_entry.payment_document == "Journal Entry":
|
||||
return frappe.db.get_value(payment_entry.payment_document, payment_entry.payment_entry, "total_credit")
|
||||
|
||||
elif payment_entry.payment_document == "Expense Claim":
|
||||
return frappe.db.get_value(payment_entry.payment_document, payment_entry.payment_entry, "total_amount_reimbursed")
|
||||
|
||||
else:
|
||||
frappe.throw("Please reconcile {0}: {1} manually".format(payment_entry.payment_document, payment_entry.payment_entry))
|
||||
|
||||
@frappe.whitelist()
|
||||
def unclear_reference_payment(doctype, docname):
|
||||
if frappe.db.exists(doctype, docname):
|
||||
doc = frappe.get_doc(doctype, docname)
|
||||
if doctype == "Sales Invoice":
|
||||
frappe.db.set_value("Sales Invoice Payment", dict(parenttype=doc.payment_document,
|
||||
parent=doc.payment_entry), "clearance_date", None)
|
||||
else:
|
||||
frappe.db.set_value(doc.payment_document, doc.payment_entry, "clearance_date", None)
|
||||
|
||||
return doc.payment_entry
|
||||
|
||||
@@ -6,7 +6,7 @@ frappe.listview_settings['Bank Transaction'] = {
|
||||
get_indicator: function(doc) {
|
||||
if(flt(doc.unallocated_amount)>0) {
|
||||
return [__("Unreconciled"), "orange", "unallocated_amount,>,0"];
|
||||
} else if(flt(doc.unallocated_amount)===0) {
|
||||
} else if(flt(doc.unallocated_amount)<=0) {
|
||||
return [__("Reconciled"), "green", "unallocated_amount,=,0"];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import frappe
|
||||
import json
|
||||
from frappe.utils import getdate
|
||||
from frappe.utils.dateutils import parse_date
|
||||
from six import iteritems
|
||||
|
||||
@frappe.whitelist()
|
||||
def upload_bank_statement():
|
||||
@@ -17,11 +18,11 @@ def upload_bank_statement():
|
||||
from frappe.utils.file_manager import get_uploaded_content
|
||||
fname, fcontent = get_uploaded_content()
|
||||
|
||||
if frappe.safe_encode(fname).lower().endswith("csv"):
|
||||
if frappe.safe_encode(fname).lower().endswith("csv".encode('utf-8')):
|
||||
from frappe.utils.csvutils import read_csv_content
|
||||
rows = read_csv_content(fcontent, False)
|
||||
|
||||
elif frappe.safe_encode(fname).lower().endswith("xlsx"):
|
||||
elif frappe.safe_encode(fname).lower().endswith("xlsx".encode('utf-8')):
|
||||
from frappe.utils.xlsxutils import read_xlsx_file_from_attached_file
|
||||
rows = read_xlsx_file_from_attached_file(fcontent=fcontent)
|
||||
|
||||
@@ -35,26 +36,30 @@ def upload_bank_statement():
|
||||
def create_bank_entries(columns, data, bank_account):
|
||||
header_map = get_header_mapping(columns, bank_account)
|
||||
|
||||
count = 0
|
||||
success = 0
|
||||
errors = 0
|
||||
for d in json.loads(data):
|
||||
if all(item is None for item in d) is True:
|
||||
continue
|
||||
fields = {}
|
||||
for key, value in header_map.iteritems():
|
||||
for key, value in iteritems(header_map):
|
||||
fields.update({key: d[int(value)-1]})
|
||||
|
||||
try:
|
||||
bank_transaction = frappe.get_doc({
|
||||
"doctype": "Bank Transaction"
|
||||
})
|
||||
bank_transaction.update(fields)
|
||||
bank_transaction.date = getdate(parse_date(bank_transaction.date))
|
||||
bank_transaction.bank_account = bank_account
|
||||
bank_transaction.insert()
|
||||
bank_transaction.submit()
|
||||
success += 1
|
||||
except Exception:
|
||||
frappe.log_error(frappe.get_traceback())
|
||||
errors += 1
|
||||
|
||||
bank_transaction = frappe.get_doc({
|
||||
"doctype": "Bank Transaction"
|
||||
})
|
||||
bank_transaction.update(fields)
|
||||
bank_transaction.date = getdate(parse_date(bank_transaction.date))
|
||||
bank_transaction.bank_account = bank_account
|
||||
bank_transaction.insert()
|
||||
bank_transaction.submit()
|
||||
count = count + 1
|
||||
|
||||
return count
|
||||
return {"success": success, "errors": errors}
|
||||
|
||||
def get_header_mapping(columns, bank_account):
|
||||
mapping = get_bank_mapping(bank_account)
|
||||
|
||||
@@ -160,7 +160,7 @@ class PaymentEntry(AccountsController):
|
||||
d.reference_name, self.party_account_currency)
|
||||
|
||||
for field, value in iteritems(ref_details):
|
||||
if not d.get(field) or force:
|
||||
if field == 'exchange_rate' or not d.get(field) or force:
|
||||
d.set(field, value)
|
||||
|
||||
def validate_payment_type(self):
|
||||
|
||||
@@ -167,7 +167,12 @@ def get_pricing_rule_for_item(args):
|
||||
|
||||
if args.transaction_type=="selling":
|
||||
if args.customer and not (args.customer_group and args.territory):
|
||||
customer = frappe.get_cached_value("Customer", args.customer, ["customer_group", "territory"])
|
||||
|
||||
if args.quotation_to and args.quotation_to != 'Customer':
|
||||
customer = frappe._dict()
|
||||
else:
|
||||
customer = frappe.get_cached_value("Customer", args.customer, ["customer_group", "territory"])
|
||||
|
||||
if customer:
|
||||
args.customer_group, args.territory = customer
|
||||
|
||||
|
||||
@@ -285,6 +285,7 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
||||
is_paid: function() {
|
||||
hide_fields(this.frm.doc);
|
||||
if(cint(this.frm.doc.is_paid)) {
|
||||
this.frm.set_value("allocate_advances_automatically", 0);
|
||||
if(!this.frm.doc.company) {
|
||||
this.frm.set_value("is_paid", 0)
|
||||
frappe.msgprint(__("Please specify Company to proceed"));
|
||||
|
||||
@@ -3511,6 +3511,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "clearance_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 1,
|
||||
@@ -3543,6 +3544,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "col_br_payments",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
@@ -3880,7 +3882,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "1",
|
||||
"default": "",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "allocate_advances_automatically",
|
||||
"fieldtype": "Check",
|
||||
@@ -4901,7 +4903,7 @@
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2019-04-11 16:08:22.288425",
|
||||
"modified": "2019-04-22 12:45:49.728359",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Invoice",
|
||||
@@ -5016,4 +5018,4 @@
|
||||
"track_changes": 1,
|
||||
"track_seen": 0,
|
||||
"track_views": 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,6 +100,7 @@ class PurchaseInvoice(BuyingController):
|
||||
self.validate_fixed_asset()
|
||||
self.create_remarks()
|
||||
self.set_status()
|
||||
self.validate_purchase_receipt_if_update_stock()
|
||||
validate_inter_company_party(self.doctype, self.supplier, self.company, self.inter_company_invoice_reference)
|
||||
|
||||
def validate_release_date(self):
|
||||
@@ -284,7 +285,7 @@ class PurchaseInvoice(BuyingController):
|
||||
|
||||
def update_status_updater_args(self):
|
||||
if cint(self.update_stock):
|
||||
self.status_updater.extend([{
|
||||
self.status_updater.append({
|
||||
'source_dt': 'Purchase Invoice Item',
|
||||
'target_dt': 'Purchase Order Item',
|
||||
'join_field': 'po_detail',
|
||||
@@ -292,28 +293,29 @@ class PurchaseInvoice(BuyingController):
|
||||
'target_parent_dt': 'Purchase Order',
|
||||
'target_parent_field': 'per_received',
|
||||
'target_ref_field': 'qty',
|
||||
'source_field': 'qty',
|
||||
'source_field': 'received_qty',
|
||||
'second_source_dt': 'Purchase Receipt Item',
|
||||
'second_source_field': 'received_qty',
|
||||
'second_join_field': 'purchase_order_item',
|
||||
'percent_join_field':'purchase_order',
|
||||
# 'percent_join_field': 'prevdoc_docname',
|
||||
'overflow_type': 'receipt',
|
||||
'extra_cond': """ and exists(select name from `tabPurchase Invoice`
|
||||
where name=`tabPurchase Invoice Item`.parent and update_stock = 1)"""
|
||||
},
|
||||
{
|
||||
'source_dt': 'Purchase Invoice Item',
|
||||
'target_dt': 'Purchase Order Item',
|
||||
'join_field': 'po_detail',
|
||||
'target_field': 'returned_qty',
|
||||
'target_parent_dt': 'Purchase Order',
|
||||
# 'target_parent_field': 'per_received',
|
||||
# 'target_ref_field': 'qty',
|
||||
'source_field': '-1 * qty',
|
||||
# 'percent_join_field': 'prevdoc_docname',
|
||||
# 'overflow_type': 'receipt',
|
||||
'extra_cond': """ and exists (select name from `tabPurchase Invoice`
|
||||
where name=`tabPurchase Invoice Item`.parent and update_stock=1 and is_return=1)"""
|
||||
}
|
||||
])
|
||||
})
|
||||
if cint(self.is_return):
|
||||
self.status_updater.append({
|
||||
'source_dt': 'Purchase Invoice Item',
|
||||
'target_dt': 'Purchase Order Item',
|
||||
'join_field': 'po_detail',
|
||||
'target_field': 'returned_qty',
|
||||
'source_field': '-1 * qty',
|
||||
'second_source_dt': 'Purchase Receipt Item',
|
||||
'second_source_field': '-1 * qty',
|
||||
'second_join_field': 'purchase_order_item',
|
||||
'overflow_type': 'receipt',
|
||||
'extra_cond': """ and exists (select name from `tabPurchase Invoice`
|
||||
where name=`tabPurchase Invoice Item`.parent and update_stock=1 and is_return=1)"""
|
||||
})
|
||||
|
||||
def validate_purchase_receipt_if_update_stock(self):
|
||||
if self.update_stock:
|
||||
@@ -327,13 +329,13 @@ class PurchaseInvoice(BuyingController):
|
||||
|
||||
self.check_prev_docstatus()
|
||||
self.update_status_updater_args()
|
||||
self.update_prevdoc_status()
|
||||
|
||||
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
|
||||
self.company, self.base_grand_total)
|
||||
|
||||
if not self.is_return:
|
||||
self.update_against_document_in_jv()
|
||||
self.update_prevdoc_status()
|
||||
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
|
||||
self.update_billing_status_in_pr()
|
||||
|
||||
@@ -763,13 +765,13 @@ class PurchaseInvoice(BuyingController):
|
||||
self.check_for_closed_status()
|
||||
|
||||
self.update_status_updater_args()
|
||||
self.update_prevdoc_status()
|
||||
|
||||
if not self.is_return:
|
||||
from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries
|
||||
if frappe.db.get_single_value('Accounts Settings', 'unlink_payment_on_cancellation_of_invoice'):
|
||||
unlink_ref_doc_from_payment_entries(self)
|
||||
|
||||
self.update_prevdoc_status()
|
||||
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
|
||||
self.update_billing_status_in_pr()
|
||||
|
||||
|
||||
@@ -6,8 +6,8 @@ frappe.listview_settings['Purchase Invoice'] = {
|
||||
add_fields: ["supplier", "supplier_name", "base_grand_total", "outstanding_amount", "due_date", "company",
|
||||
"currency", "is_return", "release_date", "on_hold"],
|
||||
get_indicator: function(doc) {
|
||||
if(cint(doc.is_return)==1) {
|
||||
return [__("Return"), "darkgrey", "is_return,=,Yes"];
|
||||
if(flt(doc.outstanding_amount) < 0 && doc.docstatus == 1) {
|
||||
return [__("Debit Note Issued"), "darkgrey", "outstanding_amount,<,0"]
|
||||
} else if(flt(doc.outstanding_amount) > 0 && doc.docstatus==1) {
|
||||
if(cint(doc.on_hold) && !doc.release_date) {
|
||||
return [__("On Hold"), "darkgrey"];
|
||||
@@ -18,9 +18,9 @@ frappe.listview_settings['Purchase Invoice'] = {
|
||||
} else {
|
||||
return [__("Unpaid"), "orange", "outstanding_amount,>,0|due,>=,Today"];
|
||||
}
|
||||
} else if(flt(doc.outstanding_amount) < 0 && doc.docstatus == 1) {
|
||||
return [__("Debit Note Issued"), "darkgrey", "outstanding_amount,<,0"]
|
||||
}else if(flt(doc.outstanding_amount)==0 && doc.docstatus==1) {
|
||||
} else if(cint(doc.is_return)) {
|
||||
return [__("Return"), "darkgrey", "is_return,=,Yes"];
|
||||
} else if(flt(doc.outstanding_amount)==0 && doc.docstatus==1) {
|
||||
return [__("Paid"), "green", "outstanding_amount,=,0"];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -402,9 +402,9 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
|
||||
pi.save()
|
||||
pi.submit()
|
||||
self.assertEqual(pi.payment_schedule[0].payment_amount, 756.15)
|
||||
self.assertEqual(pi.payment_schedule[0].payment_amount, 606.15)
|
||||
self.assertEqual(pi.payment_schedule[0].due_date, pi.posting_date)
|
||||
self.assertEqual(pi.payment_schedule[1].payment_amount, 756.15)
|
||||
self.assertEqual(pi.payment_schedule[1].payment_amount, 606.15)
|
||||
self.assertEqual(pi.payment_schedule[1].due_date, add_days(pi.posting_date, 30))
|
||||
|
||||
pi.load_from_db()
|
||||
|
||||
38
erpnext/accounts/doctype/sales_invoice/regional/india.js
Normal file
38
erpnext/accounts/doctype/sales_invoice/regional/india.js
Normal file
@@ -0,0 +1,38 @@
|
||||
frappe.ui.form.on("Sales Invoice", {
|
||||
setup: function(frm) {
|
||||
frm.set_query('transporter', function() {
|
||||
return {
|
||||
filters: {
|
||||
'is_transporter': 1
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
frm.set_query('driver', function(doc) {
|
||||
return {
|
||||
filters: {
|
||||
'transporter': doc.transporter
|
||||
}
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
refresh: function(frm) {
|
||||
if(frm.doc.docstatus == 1 && !frm.is_dirty()
|
||||
&& !frm.doc.is_return && !frm.doc.ewaybill) {
|
||||
|
||||
frm.add_custom_button('Generate e-Way Bill JSON', () => {
|
||||
var w = window.open(
|
||||
frappe.urllib.get_full_url(
|
||||
"/api/method/erpnext.regional.india.utils.generate_ewb_json?"
|
||||
+ "dt=" + encodeURIComponent(frm.doc.doctype)
|
||||
+ "&dn=" + encodeURIComponent(frm.doc.name)
|
||||
)
|
||||
);
|
||||
if (!w) {
|
||||
frappe.msgprint(__("Please enable pop-ups")); return;
|
||||
}
|
||||
}, __("Make"));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,33 @@
|
||||
var globalOnload = frappe.listview_settings['Sales Invoice'].onload;
|
||||
frappe.listview_settings['Sales Invoice'].onload = function (doclist) {
|
||||
|
||||
// Provision in case onload event is added to sales_invoice.js in future
|
||||
if (globalOnload) {
|
||||
globalOnload(doclist);
|
||||
}
|
||||
|
||||
const action = () => {
|
||||
const selected_docs = doclist.get_checked_items();
|
||||
const docnames = doclist.get_checked_items(true);
|
||||
|
||||
for (let doc of selected_docs) {
|
||||
if (doc.docstatus !== 1) {
|
||||
frappe.throw(__("e-Way Bill JSON can only be generated from a submitted document"));
|
||||
}
|
||||
}
|
||||
|
||||
var w = window.open(
|
||||
frappe.urllib.get_full_url(
|
||||
"/api/method/erpnext.regional.india.utils.generate_ewb_json?"
|
||||
+ "dt=" + encodeURIComponent(doclist.doctype)
|
||||
+ "&dn=" + encodeURIComponent(docnames)
|
||||
)
|
||||
);
|
||||
if (!w) {
|
||||
frappe.msgprint(__("Please enable pop-ups")); return;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
doclist.page.add_actions_menu_item(__('Generate e-Way Bill JSON'), action, false);
|
||||
};
|
||||
@@ -356,6 +356,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
|
||||
set_pos_data: function() {
|
||||
if(this.frm.doc.is_pos) {
|
||||
this.frm.set_value("allocate_advances_automatically", 0);
|
||||
if(!this.frm.doc.company) {
|
||||
this.frm.set_value("is_pos", 0);
|
||||
frappe.msgprint(__("Please specify Company to proceed"));
|
||||
|
||||
@@ -3630,7 +3630,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "1",
|
||||
"default": "",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "allocate_advances_automatically",
|
||||
"fieldtype": "Check",
|
||||
@@ -5816,7 +5816,7 @@
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2019-04-10 16:10:34.266458",
|
||||
"modified": "2019-04-22 12:45:41.109345",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice",
|
||||
|
||||
@@ -54,8 +54,8 @@ class SalesInvoice(SellingController):
|
||||
|
||||
def set_indicator(self):
|
||||
"""Set indicator for portal"""
|
||||
if cint(self.is_return) == 1:
|
||||
self.indicator_title = _("Return")
|
||||
if self.outstanding_amount < 0:
|
||||
self.indicator_title = _("Credit Note Issued")
|
||||
self.indicator_color = "darkgrey"
|
||||
elif self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()):
|
||||
self.indicator_color = "orange"
|
||||
@@ -63,8 +63,8 @@ class SalesInvoice(SellingController):
|
||||
elif self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()):
|
||||
self.indicator_color = "red"
|
||||
self.indicator_title = _("Overdue")
|
||||
elif self.outstanding_amount < 0:
|
||||
self.indicator_title = _("Credit Note Issued")
|
||||
elif cint(self.is_return) == 1:
|
||||
self.indicator_title = _("Return")
|
||||
self.indicator_color = "darkgrey"
|
||||
else:
|
||||
self.indicator_color = "green"
|
||||
@@ -256,7 +256,7 @@ class SalesInvoice(SellingController):
|
||||
|
||||
def update_status_updater_args(self):
|
||||
if cint(self.update_stock):
|
||||
self.status_updater.extend([{
|
||||
self.status_updater.append({
|
||||
'source_dt':'Sales Invoice Item',
|
||||
'target_dt':'Sales Order Item',
|
||||
'target_parent_dt':'Sales Order',
|
||||
@@ -274,21 +274,20 @@ class SalesInvoice(SellingController):
|
||||
'overflow_type': 'delivery',
|
||||
'extra_cond': """ and exists(select name from `tabSales Invoice`
|
||||
where name=`tabSales Invoice Item`.parent and update_stock = 1)"""
|
||||
},
|
||||
{
|
||||
'source_dt': 'Sales Invoice Item',
|
||||
'target_dt': 'Sales Order Item',
|
||||
'join_field': 'so_detail',
|
||||
'target_field': 'returned_qty',
|
||||
'target_parent_dt': 'Sales Order',
|
||||
# 'target_parent_field': 'per_delivered',
|
||||
# 'target_ref_field': 'qty',
|
||||
'source_field': '-1 * qty',
|
||||
# 'percent_join_field': 'sales_order',
|
||||
# 'overflow_type': 'delivery',
|
||||
'extra_cond': """ and exists (select name from `tabSales Invoice` where name=`tabSales Invoice Item`.parent and update_stock=1 and is_return=1)"""
|
||||
}
|
||||
])
|
||||
})
|
||||
if cint(self.is_return):
|
||||
self.status_updater.append({
|
||||
'source_dt': 'Sales Invoice Item',
|
||||
'target_dt': 'Sales Order Item',
|
||||
'join_field': 'so_detail',
|
||||
'target_field': 'returned_qty',
|
||||
'target_parent_dt': 'Sales Order',
|
||||
'source_field': '-1 * qty',
|
||||
'second_source_dt': 'Delivery Note Item',
|
||||
'second_source_field': '-1 * qty',
|
||||
'second_join_field': 'so_detail',
|
||||
'extra_cond': """ and exists (select name from `tabSales Invoice` where name=`tabSales Invoice Item`.parent and update_stock=1 and is_return=1)"""
|
||||
})
|
||||
|
||||
def check_credit_limit(self):
|
||||
from erpnext.selling.doctype.customer.customer import check_credit_limit
|
||||
@@ -504,12 +503,15 @@ class SalesInvoice(SellingController):
|
||||
|
||||
def so_dn_required(self):
|
||||
"""check in manage account if sales order / delivery note required or not."""
|
||||
if self.is_return:
|
||||
return
|
||||
dic = {'Sales Order':['so_required', 'is_pos'],'Delivery Note':['dn_required', 'update_stock']}
|
||||
for i in dic:
|
||||
if frappe.db.get_single_value('Selling Settings', dic[i][0]) == 'Yes':
|
||||
for d in self.get('items'):
|
||||
if frappe.get_cached_value('Item', d.item_code, 'is_stock_item') == 1 \
|
||||
and not d.get(i.lower().replace(' ','_')) and not self.get(dic[i][1]):
|
||||
is_stock_item = frappe.get_cached_value('Item', d.item_code, 'is_stock_item')
|
||||
if (d.item_code and is_stock_item == 1\
|
||||
and not d.get(i.lower().replace(' ','_')) and not self.get(dic[i][1])):
|
||||
msgprint(_("{0} is mandatory for Item {1}").format(i,d.item_code), raise_exception=1)
|
||||
|
||||
|
||||
@@ -1167,6 +1169,8 @@ class SalesInvoice(SellingController):
|
||||
self.set_missing_values(for_validate = True)
|
||||
|
||||
def validate_inter_company_party(doctype, party, company, inter_company_invoice_reference):
|
||||
if not party:
|
||||
return
|
||||
if doctype == "Sales Invoice":
|
||||
partytype, ref_partytype, internal = "Customer", "Supplier", "is_internal_customer"
|
||||
ref_doc = "Purchase Invoice"
|
||||
|
||||
@@ -6,16 +6,16 @@ frappe.listview_settings['Sales Invoice'] = {
|
||||
add_fields: ["customer", "customer_name", "base_grand_total", "outstanding_amount", "due_date", "company",
|
||||
"currency", "is_return"],
|
||||
get_indicator: function(doc) {
|
||||
if(cint(doc.is_return)==1) {
|
||||
return [__("Return"), "darkgrey", "is_return,=,Yes"];
|
||||
} else if(flt(doc.outstanding_amount)==0) {
|
||||
return [__("Paid"), "green", "outstanding_amount,=,0"]
|
||||
} else if(flt(doc.outstanding_amount) < 0) {
|
||||
if(flt(doc.outstanding_amount) < 0) {
|
||||
return [__("Credit Note Issued"), "darkgrey", "outstanding_amount,<,0"]
|
||||
}else if (flt(doc.outstanding_amount) > 0 && doc.due_date >= frappe.datetime.get_today()) {
|
||||
} else if (flt(doc.outstanding_amount) > 0 && doc.due_date >= frappe.datetime.get_today()) {
|
||||
return [__("Unpaid"), "orange", "outstanding_amount,>,0|due_date,>,Today"]
|
||||
} else if (flt(doc.outstanding_amount) > 0 && doc.due_date < frappe.datetime.get_today()) {
|
||||
return [__("Overdue"), "red", "outstanding_amount,>,0|due_date,<=,Today"]
|
||||
} else if(cint(doc.is_return)) {
|
||||
return [__("Return"), "darkgrey", "is_return,=,Yes"];
|
||||
} else if(flt(doc.outstanding_amount)==0) {
|
||||
return [__("Paid"), "green", "outstanding_amount,=,0"]
|
||||
}
|
||||
},
|
||||
right_column: "grand_total"
|
||||
|
||||
@@ -18,6 +18,8 @@ from erpnext.accounts.doctype.account.test_account import get_inventory_account,
|
||||
from erpnext.controllers.taxes_and_totals import get_itemised_tax_breakup_data
|
||||
from erpnext.stock.doctype.item.test_item import create_item
|
||||
from six import iteritems
|
||||
from erpnext.regional.india.utils import get_ewb_data
|
||||
|
||||
class TestSalesInvoice(unittest.TestCase):
|
||||
def make(self):
|
||||
w = frappe.copy_doc(test_records[0])
|
||||
@@ -1611,6 +1613,110 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
self.assertEqual(expected_gle[i][2], gle.credit)
|
||||
self.assertEqual(getdate(expected_gle[i][3]), gle.posting_date)
|
||||
|
||||
def test_eway_bill_json(self):
|
||||
if not frappe.db.exists('Address', '_Test Address for Eway bill-Billing'):
|
||||
address = frappe.get_doc({
|
||||
"address_line1": "_Test Address Line 1",
|
||||
"address_title": "_Test Address for Eway bill",
|
||||
"address_type": "Billing",
|
||||
"city": "_Test City",
|
||||
"state": "Test State",
|
||||
"country": "India",
|
||||
"doctype": "Address",
|
||||
"is_primary_address": 1,
|
||||
"phone": "+91 0000000000",
|
||||
"gstin": "27AAECE4835E1ZR",
|
||||
"gst_state": "Maharashtra",
|
||||
"gst_state_number": "27",
|
||||
"pincode": "401108"
|
||||
}).insert()
|
||||
|
||||
address.append("links", {
|
||||
"link_doctype": "Company",
|
||||
"link_name": "_Test Company"
|
||||
})
|
||||
|
||||
address.save()
|
||||
|
||||
if not frappe.db.exists('Address', '_Test Customer-Address for Eway bill-Shipping'):
|
||||
address = frappe.get_doc({
|
||||
"address_line1": "_Test Address Line 1",
|
||||
"address_title": "_Test Customer-Address for Eway bill",
|
||||
"address_type": "Shipping",
|
||||
"city": "_Test City",
|
||||
"state": "Test State",
|
||||
"country": "India",
|
||||
"doctype": "Address",
|
||||
"is_primary_address": 1,
|
||||
"phone": "+91 0000000000",
|
||||
"gst_state": "Maharashtra",
|
||||
"gst_state_number": "27",
|
||||
"pincode": "410038"
|
||||
}).insert()
|
||||
|
||||
address.append("links", {
|
||||
"link_doctype": "Customer",
|
||||
"link_name": "_Test Customer"
|
||||
})
|
||||
|
||||
address.save()
|
||||
|
||||
gst_settings = frappe.get_doc("GST Settings")
|
||||
|
||||
gst_account = frappe.get_all(
|
||||
"GST Account",
|
||||
fields=["cgst_account", "sgst_account", "igst_account"],
|
||||
filters = {"company": "_Test Company"})
|
||||
|
||||
if not gst_account:
|
||||
gst_settings.append("gst_accounts", {
|
||||
"company": "_Test Company",
|
||||
"cgst_account": "CGST - _TC",
|
||||
"sgst_account": "SGST - _TC",
|
||||
"igst_account": "IGST - _TC",
|
||||
})
|
||||
|
||||
gst_settings.save()
|
||||
|
||||
si = create_sales_invoice(do_not_save =1, rate = '60000')
|
||||
|
||||
si.distance = 2000
|
||||
si.company_address = "_Test Address for Eway bill-Billing"
|
||||
si.customer_address = "_Test Customer-Address for Eway bill-Shipping"
|
||||
si.vehicle_no = "KA12KA1234"
|
||||
|
||||
si.append("taxes", {
|
||||
"charge_type": "On Net Total",
|
||||
"account_head": "CGST - _TC",
|
||||
"cost_center": "Main - _TC",
|
||||
"description": "CGST @ 9.0",
|
||||
"rate": 9
|
||||
})
|
||||
|
||||
si.append("taxes", {
|
||||
"charge_type": "On Net Total",
|
||||
"account_head": "SGST - _TC",
|
||||
"cost_center": "Main - _TC",
|
||||
"description": "SGST @ 9.0",
|
||||
"rate": 9
|
||||
})
|
||||
|
||||
si.submit()
|
||||
|
||||
data = get_ewb_data("Sales Invoice", si.name)
|
||||
|
||||
self.assertEqual(data['version'], '1.0.1118')
|
||||
self.assertEqual(data['billLists'][0]['fromGstin'], '27AAECE4835E1ZR')
|
||||
self.assertEqual(data['billLists'][0]['fromTrdName'], '_Test Company')
|
||||
self.assertEqual(data['billLists'][0]['toTrdName'], '_Test Customer')
|
||||
self.assertEqual(data['billLists'][0]['vehicleType'], 'R')
|
||||
self.assertEqual(data['billLists'][0]['totalValue'], 60000)
|
||||
self.assertEqual(data['billLists'][0]['cgstValue'], 5400)
|
||||
self.assertEqual(data['billLists'][0]['sgstValue'], 5400)
|
||||
self.assertEqual(data['billLists'][0]['vehicleNo'], 'KA12KA1234')
|
||||
self.assertEqual(data['billLists'][0]['itemList'][0]['taxableAmount'], 60000)
|
||||
|
||||
|
||||
def create_sales_invoice(**args):
|
||||
si = frappe.new_doc("Sales Invoice")
|
||||
args = frappe._dict(args)
|
||||
|
||||
@@ -314,13 +314,11 @@ class Subscription(Document):
|
||||
|
||||
self.save()
|
||||
|
||||
@property
|
||||
def is_postpaid_to_invoice(self):
|
||||
return getdate(nowdate()) > getdate(self.current_invoice_end) or \
|
||||
(getdate(nowdate()) >= getdate(self.current_invoice_end) and getdate(self.current_invoice_end) == getdate(self.current_invoice_start)) and \
|
||||
not self.has_outstanding_invoice()
|
||||
|
||||
@property
|
||||
def is_prepaid_to_invoice(self):
|
||||
if not self.generate_invoice_at_period_start:
|
||||
return False
|
||||
@@ -340,7 +338,7 @@ class Subscription(Document):
|
||||
2. Change the `Subscription` status to 'Past Due Date'
|
||||
3. Change the `Subscription` status to 'Cancelled'
|
||||
"""
|
||||
if self.is_postpaid_to_invoice or self.is_prepaid_to_invoice:
|
||||
if self.is_postpaid_to_invoice() or self.is_prepaid_to_invoice():
|
||||
self.generate_invoice()
|
||||
if self.current_invoice_is_past_due():
|
||||
self.status = 'Past Due Date'
|
||||
|
||||
@@ -32,7 +32,6 @@ erpnext.accounts.bankReconciliation = class BankReconciliation {
|
||||
fieldname: 'company',
|
||||
options: "Company",
|
||||
onchange: function() {
|
||||
console.log(this.value)
|
||||
if (this.value) {
|
||||
me.company = this.value;
|
||||
} else {
|
||||
@@ -192,7 +191,7 @@ erpnext.accounts.bankTransactionUpload = class bankTransactionUpload {
|
||||
frappe.xcall('erpnext.accounts.doctype.bank_transaction.bank_transaction_upload.create_bank_entries',
|
||||
{columns: this.datatable.datamanager.columns, data: this.datatable.datamanager.data, bank_account: me.parent.bank_account}
|
||||
).then((result) => {
|
||||
let result_title = __("{0} bank transaction(s) created", [result])
|
||||
let result_title = result.errors == 0 ? __("{0} bank transaction(s) created", [result.success]) : __("{0} bank transaction(s) created and {1} errors", [result.success, result.errors])
|
||||
let result_msg = `
|
||||
<div class="flex justify-center align-center text-muted" style="height: 50vh; display: flex;">
|
||||
<h5 class="text-muted">${result_title}</h5>
|
||||
@@ -200,7 +199,11 @@ erpnext.accounts.bankTransactionUpload = class bankTransactionUpload {
|
||||
me.parent.page.clear_primary_action();
|
||||
me.parent.$main_section.empty();
|
||||
me.parent.$main_section.append(result_msg);
|
||||
frappe.show_alert({message:__("All bank transactions have been created"), indicator:'green'});
|
||||
if (result.errors == 0) {
|
||||
frappe.show_alert({message:__("All bank transactions have been created"), indicator:'green'});
|
||||
} else {
|
||||
frappe.show_alert({message:__("Please check the error log for details about the import errors"), indicator:'red'});
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -531,11 +534,13 @@ erpnext.accounts.ReconciliationRow = class ReconciliationRow {
|
||||
.then(doc => {
|
||||
let displayed_docs = []
|
||||
if (dt === "Payment Entry") {
|
||||
doc.currency = doc.payment_type == "Receive" ? doc.paid_to_account_currency : doc.paid_from_account_currency;
|
||||
displayed_docs.push(doc);
|
||||
payment.currency = doc.payment_type == "Receive" ? doc.paid_to_account_currency : doc.paid_from_account_currency;
|
||||
payment.doctype = dt
|
||||
displayed_docs.push(payment);
|
||||
} else if (dt === "Journal Entry") {
|
||||
doc.accounts.forEach(payment => {
|
||||
if (payment.account === me.gl_account) {
|
||||
payment.doctype = dt;
|
||||
payment.posting_date = doc.posting_date;
|
||||
payment.party = doc.pay_to_recd_from;
|
||||
payment.reference_no = doc.cheque_no;
|
||||
@@ -549,6 +554,7 @@ erpnext.accounts.ReconciliationRow = class ReconciliationRow {
|
||||
} else if (dt === "Sales Invoice") {
|
||||
doc.payments.forEach(payment => {
|
||||
if (payment.clearance_date === null || payment.clearance_date === "") {
|
||||
payment.doctype = dt;
|
||||
payment.posting_date = doc.posting_date;
|
||||
payment.party = doc.customer;
|
||||
payment.reference_no = doc.remarks;
|
||||
|
||||
@@ -28,51 +28,20 @@ def reconcile(bank_transaction, payment_doctype, payment_name):
|
||||
frappe.throw(_("The selected payment entry should be linked with a creditor bank transaction"))
|
||||
|
||||
add_payment_to_transaction(transaction, payment_entry, gl_entry)
|
||||
clear_payment_entry(transaction, payment_entry, gl_entry)
|
||||
|
||||
return 'reconciled'
|
||||
|
||||
def add_payment_to_transaction(transaction, payment_entry, gl_entry):
|
||||
gl_amount, transaction_amount = (gl_entry.credit, transaction.debit) if gl_entry.credit > 0 else (gl_entry.debit, transaction.credit)
|
||||
allocated_amount = gl_amount if gl_amount <= transaction_amount else transaction_amount
|
||||
transaction.append("payment_entries", {
|
||||
"payment_document": payment_entry.doctype,
|
||||
"payment_entry": payment_entry.name,
|
||||
"allocated_amount": gl_entry.credit if gl_entry.credit > 0 else gl_entry.debit
|
||||
"allocated_amount": allocated_amount
|
||||
})
|
||||
|
||||
transaction.save()
|
||||
|
||||
def clear_payment_entry(transaction, payment_entry, gl_entry):
|
||||
linked_bank_transactions = frappe.db.sql("""
|
||||
SELECT
|
||||
bt.credit, bt.debit
|
||||
FROM
|
||||
`tabBank Transaction Payments` as btp
|
||||
LEFT JOIN
|
||||
`tabBank Transaction` as bt on btp.parent=bt.name
|
||||
WHERE
|
||||
btp.payment_document = %s
|
||||
AND
|
||||
btp.payment_entry = %s
|
||||
AND
|
||||
bt.docstatus = 1
|
||||
""", (payment_entry.doctype, payment_entry.name), as_dict=True)
|
||||
|
||||
amount_cleared = (flt(linked_bank_transactions[0].credit) - flt(linked_bank_transactions[0].debit))
|
||||
amount_to_be_cleared = (flt(gl_entry.debit) - flt(gl_entry.credit))
|
||||
|
||||
if payment_entry.doctype in ("Payment Entry", "Journal Entry", "Purchase Invoice", "Expense Claim"):
|
||||
clear_simple_entry(amount_cleared, amount_to_be_cleared, payment_entry, transaction)
|
||||
|
||||
elif payment_entry.doctype == "Sales Invoice":
|
||||
clear_sales_invoice(amount_cleared, amount_to_be_cleared, payment_entry, transaction)
|
||||
|
||||
def clear_simple_entry(amount_cleared, amount_to_be_cleared, payment_entry, transaction):
|
||||
if amount_cleared >= amount_to_be_cleared:
|
||||
frappe.db.set_value(payment_entry.doctype, payment_entry.name, "clearance_date", transaction.date)
|
||||
|
||||
def clear_sales_invoice(amount_cleared, amount_to_be_cleared, payment_entry, transaction):
|
||||
if amount_cleared >= amount_to_be_cleared:
|
||||
frappe.db.set_value("Sales Invoice Payment", dict(parenttype=payment_entry.doctype,
|
||||
parent=payment_entry.name), "clearance_date", transaction.date)
|
||||
transaction.update_allocations()
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_linked_payments(bank_transaction):
|
||||
@@ -83,13 +52,17 @@ def get_linked_payments(bank_transaction):
|
||||
amount_matching = check_matching_amount(bank_account[0].account, bank_account[0].company, transaction)
|
||||
|
||||
# Get some data from payment entries linked to a corresponding bank transaction
|
||||
description_matching = get_matching_descriptions_data(bank_account[0].account, transaction)
|
||||
description_matching = get_matching_descriptions_data(bank_account[0].company, transaction)
|
||||
|
||||
if amount_matching:
|
||||
return check_amount_vs_description(amount_matching, description_matching)
|
||||
|
||||
elif description_matching:
|
||||
return sorted(description_matching, key = lambda x: x["posting_date"], reverse=True)
|
||||
description_matching = filter(lambda x: not x.get('clearance_date'), description_matching)
|
||||
if not description_matching:
|
||||
return []
|
||||
|
||||
return sorted(list(description_matching), key = lambda x: x["posting_date"], reverse=True)
|
||||
|
||||
else:
|
||||
return []
|
||||
@@ -104,7 +77,7 @@ def check_matching_amount(bank_account, company, transaction):
|
||||
|
||||
payment_entries = frappe.get_all("Payment Entry", fields=["'Payment Entry' as doctype", "name", "paid_amount", "payment_type", "reference_no", "reference_date",
|
||||
"party", "party_type", "posting_date", "{0}".format(currency_field)], filters=[["paid_amount", "like", "{0}%".format(amount)],
|
||||
["docstatus", "=", "1"], ["payment_type", "=", payment_type], ["ifnull(clearance_date, '')", "=", ""], ["{0}".format(account_from_to), "=", "{0}".format(bank_account)]])
|
||||
["docstatus", "=", "1"], ["payment_type", "=", [payment_type, "Internal Transfer"]], ["ifnull(clearance_date, '')", "=", ""], ["{0}".format(account_from_to), "=", "{0}".format(bank_account)]])
|
||||
|
||||
if transaction.credit > 0:
|
||||
journal_entries = frappe.db.sql("""
|
||||
@@ -130,7 +103,8 @@ def check_matching_amount(bank_account, company, transaction):
|
||||
journal_entries = frappe.db.sql("""
|
||||
SELECT
|
||||
'Journal Entry' as doctype, je.name, je.posting_date, je.cheque_no as reference_no,
|
||||
je.pay_to_recd_from as party, je.cheque_date as reference_date, jea.credit_in_account_currency as paid_amount
|
||||
jea.account_currency as currency, je.pay_to_recd_from as party, je.cheque_date as reference_date,
|
||||
jea.credit_in_account_currency as paid_amount
|
||||
FROM
|
||||
`tabJournal Entry Account` as jea
|
||||
JOIN
|
||||
@@ -140,12 +114,17 @@ def check_matching_amount(bank_account, company, transaction):
|
||||
WHERE
|
||||
(je.clearance_date is null or je.clearance_date='0000-00-00')
|
||||
AND
|
||||
jea.account = %s
|
||||
jea.account = %(bank_account)s
|
||||
AND
|
||||
jea.credit_in_account_currency like %s
|
||||
jea.credit_in_account_currency like %(txt)s
|
||||
AND
|
||||
je.docstatus = 1
|
||||
""", (bank_account, amount), as_dict=True)
|
||||
""", {
|
||||
'bank_account': bank_account,
|
||||
'txt': '%%%s%%' % amount
|
||||
}, as_dict=True)
|
||||
|
||||
frappe.errprint(journal_entries)
|
||||
|
||||
if transaction.credit > 0:
|
||||
sales_invoices = frappe.db.sql("""
|
||||
@@ -207,7 +186,7 @@ def check_matching_amount(bank_account, company, transaction):
|
||||
|
||||
return payments
|
||||
|
||||
def get_matching_descriptions_data(bank_account, transaction):
|
||||
def get_matching_descriptions_data(company, transaction):
|
||||
if not transaction.description :
|
||||
return []
|
||||
|
||||
@@ -243,17 +222,28 @@ def get_matching_descriptions_data(bank_account, transaction):
|
||||
|
||||
|
||||
data = []
|
||||
company_currency = get_company_currency(company)
|
||||
for key, value in iteritems(links):
|
||||
if key == "Payment Entry":
|
||||
data.extend(frappe.get_all("Payment Entry", filters=[["name", "in", value]], fields=["'Payment Entry' as doctype", "posting_date", "party", "reference_no", "reference_date", "paid_amount"]))
|
||||
data.extend(frappe.get_all("Payment Entry", filters=[["name", "in", value]],
|
||||
fields=["'Payment Entry' as doctype", "posting_date", "party", "reference_no",
|
||||
"reference_date", "paid_amount", "paid_to_account_currency as currency", "clearance_date"]))
|
||||
if key == "Journal Entry":
|
||||
data.extend(frappe.get_all("Journal Entry", filters=[["name", "in", value]], fields=["'Journal Entry' as doctype", "posting_date", "paid_to_recd_from as party", "cheque_no as reference_no", "cheque_date as reference_date"]))
|
||||
journal_entries = frappe.get_all("Journal Entry", filters=[["name", "in", value]],
|
||||
fields=["name", "'Journal Entry' as doctype", "posting_date",
|
||||
"pay_to_recd_from as party", "cheque_no as reference_no", "cheque_date as reference_date",
|
||||
"total_credit as paid_amount", "clearance_date"])
|
||||
for journal_entry in journal_entries:
|
||||
journal_entry_accounts = frappe.get_all("Journal Entry Account", filters={"parenttype": journal_entry["doctype"], "parent": journal_entry["name"]}, fields=["account_currency"])
|
||||
journal_entry["currency"] = journal_entry_accounts[0]["account_currency"] if journal_entry_accounts else company_currency
|
||||
data.extend(journal_entries)
|
||||
if key == "Sales Invoice":
|
||||
data.extend(frappe.get_all("Sales Invoice", filters=[["name", "in", value]], fields=["'Sales Invoice' as doctype", "posting_date", "customer_name as party"]))
|
||||
data.extend(frappe.get_all("Sales Invoice", filters=[["name", "in", value]], fields=["'Sales Invoice' as doctype", "posting_date", "customer_name as party", "paid_amount", "currency"]))
|
||||
if key == "Purchase Invoice":
|
||||
data.append(frappe.get_all("Purchase Invoice", filters=[["name", "in", value]], fields=["'Purchase Invoice' as doctype", "posting_date", "supplier_name as party"]))
|
||||
if key == "Purchase Invoice":
|
||||
data.append(frappe.get_all("Expense Claim", filters=[["name", "in", value]], fields=["'Expense Claim' as doctype", "posting_date", "employee_name as party"]))
|
||||
data.extend(frappe.get_all("Purchase Invoice", filters=[["name", "in", value]], fields=["'Purchase Invoice' as doctype", "posting_date", "supplier_name as party", "paid_amount", "currency"]))
|
||||
if key == "Expense Claim":
|
||||
expense_claims = frappe.get_all("Expense Claim", filters=[["name", "in", value]], fields=["'Expense Claim' as doctype", "posting_date", "employee_name as party", "total_amount_reimbursed as paid_amount"])
|
||||
data.extend([dict(x,**{"currency": company_currency}) for x in expense_claims])
|
||||
|
||||
return data
|
||||
|
||||
@@ -263,13 +253,16 @@ def check_amount_vs_description(amount_matching, description_matching):
|
||||
if description_matching:
|
||||
for am_match in amount_matching:
|
||||
for des_match in description_matching:
|
||||
if des_match.get("clearance_date"):
|
||||
continue
|
||||
|
||||
if am_match["party"] == des_match["party"]:
|
||||
if am_match not in result:
|
||||
result.append(am_match)
|
||||
continue
|
||||
|
||||
if hasattr(am_match, "reference_no") and hasattr(des_match, "reference_no"):
|
||||
if difflib.SequenceMatcher(lambda x: x == " ", am_match["reference_no"], des_match["reference_no"]) > 70:
|
||||
if "reference_no" in am_match and "reference_no" in des_match:
|
||||
if difflib.SequenceMatcher(lambda x: x == " ", am_match["reference_no"], des_match["reference_no"]).ratio() > 70:
|
||||
if am_match not in result:
|
||||
result.append(am_match)
|
||||
if result:
|
||||
|
||||
@@ -1957,6 +1957,12 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
||||
}],
|
||||
function(values){
|
||||
me.item_batch_no[me.items[0].item_code] = values.batch;
|
||||
const item = me.frm.doc.items.find(
|
||||
({ item_code }) => item_code === me.items[0].item_code
|
||||
);
|
||||
if (item) {
|
||||
item.batch_no = values.batch;
|
||||
}
|
||||
},
|
||||
__('Select Batch No'))
|
||||
}
|
||||
|
||||
@@ -44,7 +44,7 @@ def _get_party_details(party=None, account=None, party_type="Customer", company=
|
||||
frappe.throw(_("Not permitted for {0}").format(party), frappe.PermissionError)
|
||||
|
||||
party = frappe.get_doc(party_type, party)
|
||||
currency = party.default_currency if party.default_currency else get_company_currency(company)
|
||||
currency = party.default_currency if party.get("default_currency") else get_company_currency(company)
|
||||
|
||||
out["taxes_and_charges"] = set_taxes(party.name, party_type, posting_date, company, out.customer_group, out.supplier_group)
|
||||
out["payment_terms_template"] = get_pyt_term_template(party.name, party_type, company)
|
||||
@@ -140,7 +140,7 @@ def set_other_values(out, party, party_type):
|
||||
|
||||
def get_default_price_list(party):
|
||||
"""Return default price list for party (Document object)"""
|
||||
if party.default_price_list:
|
||||
if party.get("default_price_list"):
|
||||
return party.default_price_list
|
||||
|
||||
if party.doctype == "Customer":
|
||||
|
||||
@@ -1,19 +1,23 @@
|
||||
{
|
||||
"creation": "2014-08-28 11:11:39.796473",
|
||||
"custom_format": 0,
|
||||
"disabled": 0,
|
||||
"doc_type": "Journal Entry",
|
||||
"docstatus": 0,
|
||||
"doctype": "Print Format",
|
||||
"html": "{%- from \"templates/print_formats/standard_macros.html\" import add_header -%}\n\n<div class=\"page-break\">\n {%- if not doc.get(\"print_heading\") and not doc.get(\"select_print_heading\") \n and doc.set(\"select_print_heading\", _(\"Credit Note\")) -%}{%- endif -%}\n {{ add_header(0, 1, doc, letter_head, no_letterhead) }}\n\n {%- for label, value in (\n (_(\"Credit To\"), doc.pay_to_recd_from),\n (_(\"Date\"), frappe.utils.formatdate(doc.voucher_date)),\n (_(\"Amount\"), \"<strong>\" + doc.get_formatted(\"total_amount\") + \"</strong><br>\" + (doc.total_amount_in_words or \"\") + \"<br>\"),\n (_(\"Remarks\"), doc.remark)\n ) -%}\n\n <div class=\"row\">\n <div class=\"col-xs-3\"><label class=\"text-right\">{{ label }}</label></div>\n <div class=\"col-xs-9\">{{ value }}</div>\n </div>\n\n {%- endfor -%}\n\n <hr>\n <br>\n <p class=\"strong\">\n {{ _(\"For\") }} {{ doc.company }},<br>\n <br>\n <br>\n <br>\n {{ _(\"Authorized Signatory\") }}\n </p>\n</div>\n\n\n",
|
||||
"idx": 2,
|
||||
"modified": "2015-07-22 17:42:01.560817",
|
||||
"modified_by": "Administrator",
|
||||
"name": "Credit Note",
|
||||
"owner": "Administrator",
|
||||
"parent": "Journal Entry",
|
||||
"parentfield": "__print_formats",
|
||||
"parenttype": "DocType",
|
||||
"print_format_type": "Server",
|
||||
"align_labels_right": 0,
|
||||
"creation": "2014-08-28 11:11:39.796473",
|
||||
"custom_format": 0,
|
||||
"disabled": 0,
|
||||
"doc_type": "Journal Entry",
|
||||
"docstatus": 0,
|
||||
"doctype": "Print Format",
|
||||
"format_data": "[{\"fieldname\": \"print_heading_template\", \"fieldtype\": \"Custom HTML\", \"options\": \"<div class=\\\"print-heading\\\">\\t\\t\\t\\t<h2>Journal Entry<br><small>{{ doc.name }}</small>\\t\\t\\t\\t</h2></div>\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"voucher_type\", \"print_hide\": 0, \"label\": \"Entry Type\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"posting_date\", \"print_hide\": 0, \"label\": \"Posting Date\"}, {\"fieldname\": \"finance_book\", \"print_hide\": 0, \"label\": \"Finance Book\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"accounts\", \"print_hide\": 0, \"label\": \"Accounting Entries\", \"visible_columns\": [{\"fieldname\": \"account\", \"print_width\": \"250px\", \"print_hide\": 0}, {\"fieldname\": \"bank_account_no\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"party_type\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"party\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"debit_in_account_currency\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"credit_in_account_currency\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"reference_type\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"reference_name\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"reference_due_date\", \"print_width\": \"\", \"print_hide\": 0}, {\"fieldname\": \"project\", \"print_width\": \"\", \"print_hide\": 0}]}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"cheque_no\", \"print_hide\": 0, \"label\": \"Reference Number\"}, {\"fieldname\": \"cheque_date\", \"print_hide\": 0, \"label\": \"Reference Date\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"get_balance\", \"print_hide\": 0, \"label\": \"Make Difference Entry\"}, {\"fieldname\": \"total_amount\", \"print_hide\": 0, \"label\": \"Total Amount\"}, {\"fieldtype\": \"Section Break\", \"label\": \"Reference\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"clearance_date\", \"print_hide\": 0, \"label\": \"Clearance Date\"}, {\"fieldname\": \"remark\", \"print_hide\": 0, \"label\": \"Remark\"}, {\"fieldname\": \"inter_company_journal_entry_reference\", \"print_hide\": 0, \"label\": \"Inter Company Journal Entry Reference\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"due_date\", \"print_hide\": 0, \"label\": \"Due Date\"}, {\"fieldtype\": \"Section Break\", \"label\": \"Printing Settings\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"pay_to_recd_from\", \"print_hide\": 0, \"label\": \"Pay To / Recd From\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"letter_head\", \"print_hide\": 0, \"label\": \"Letter Head\"}, {\"fieldtype\": \"Section Break\", \"label\": \"More Information\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"mode_of_payment\", \"print_hide\": 0, \"label\": \"Mode of Payment\"}, {\"fieldtype\": \"Column Break\"}, {\"fieldname\": \"stock_entry\", \"print_hide\": 0, \"label\": \"Stock Entry\"}]",
|
||||
"html": "{%- from \"templates/print_formats/standard_macros.html\" import add_header -%}\n\n<div class=\"page-break\">\n {%- if not doc.get(\"print_heading\") and not doc.get(\"select_print_heading\") \n and doc.set(\"select_print_heading\", _(\"Credit Note\")) -%}{%- endif -%}\n {{ add_header(0, 1, doc, letter_head, no_letterhead) }}\n\n {%- for label, value in (\n (_(\"Credit To\"), doc.pay_to_recd_from),\n (_(\"Date\"), frappe.utils.formatdate(doc.voucher_date)),\n (_(\"Amount\"), \"<strong>\" + doc.get_formatted(\"total_amount\") + \"</strong><br>\" + (doc.total_amount_in_words or \"\") + \"<br>\"),\n (_(\"Remarks\"), doc.remark)\n ) -%}\n\n <div class=\"row\">\n <div class=\"col-xs-3\"><label class=\"text-right\">{{ label }}</label></div>\n <div class=\"col-xs-9\">{{ value }}</div>\n </div>\n\n {%- endfor -%}\n\n <hr>\n <br>\n <p class=\"strong\">\n {{ _(\"For\") }} {{ doc.company }},<br>\n <br>\n <br>\n <br>\n {{ _(\"Authorized Signatory\") }}\n </p>\n</div>\n\n\n",
|
||||
"idx": 2,
|
||||
"line_breaks": 0,
|
||||
"modified": "2019-04-18 12:10:14.732269",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Credit Note",
|
||||
"owner": "Administrator",
|
||||
"parentfield": "__print_formats",
|
||||
"print_format_builder": 0,
|
||||
"print_format_type": "Server",
|
||||
"show_section_headings": 0,
|
||||
"standard": "Yes"
|
||||
}
|
||||
@@ -44,6 +44,13 @@ frappe.query_reports["Accounts Payable"] = {
|
||||
"default": "90",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"range4",
|
||||
"label": __("Ageing Range 4"),
|
||||
"fieldtype": "Int",
|
||||
"default": "120",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"finance_book",
|
||||
"label": __("Finance Book"),
|
||||
|
||||
@@ -44,6 +44,13 @@ frappe.query_reports["Accounts Payable Summary"] = {
|
||||
"default": "90",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"range4",
|
||||
"label": __("Ageing Range 4"),
|
||||
"fieldtype": "Int",
|
||||
"default": "120",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"finance_book",
|
||||
"label": __("Finance Book"),
|
||||
|
||||
@@ -107,14 +107,14 @@
|
||||
<thead>
|
||||
<tr>
|
||||
{% if(report.report_name === "Accounts Receivable" || report.report_name === "Accounts Payable") { %}
|
||||
<th style="width: 9%">{%= __("Date") %}</th>
|
||||
<th style="width: 5%">{%= __("Age (Days)") %}</th>
|
||||
<th style="width: 10%">{%= __("Date") %}</th>
|
||||
<th style="width: 4%">{%= __("Age (Days)") %}</th>
|
||||
|
||||
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person_in_print) { %}
|
||||
<th style="width: 16%">{%= __("Reference") %}</th>
|
||||
<th style="width: 14%">{%= __("Reference") %}</th>
|
||||
<th style="width: 10%">{%= __("Sales Person") %}</th>
|
||||
{% } else { %}
|
||||
<th style="width: 26%">{%= __("Reference") %}</th>
|
||||
<th style="width: 24%">{%= __("Reference") %}</th>
|
||||
{% } %}
|
||||
{% if(!filters.show_pdc_in_print) { %}
|
||||
<th style="width: 20%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
|
||||
@@ -127,7 +127,7 @@
|
||||
<th style="width: 10%; text-align: right">{%= __("Outstanding Amount") %}</th>
|
||||
{% if(filters.show_pdc_in_print) { %}
|
||||
{% if(report.report_name === "Accounts Receivable") { %}
|
||||
<th style="width: 10%">{%= __("Customer LPO No.") %}</th>
|
||||
<th style="width: 12%">{%= __("Customer LPO No.") %}</th>
|
||||
{% } %}
|
||||
<th style="width: 10%">{%= __("PDC/LC Ref") %}</th>
|
||||
<th style="width: 10%">{%= __("PDC/LC Amount") %}</th>
|
||||
@@ -206,7 +206,7 @@
|
||||
{% if(!filters.show_pdc_in_print) { %}
|
||||
<td></td>
|
||||
{% } %}
|
||||
{% if(report.report_name === "Accounts Receivable") { %}
|
||||
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person_in_print) { %}
|
||||
<td></td>
|
||||
{% } %}
|
||||
<td></td>
|
||||
|
||||
@@ -44,6 +44,13 @@ frappe.query_reports["Accounts Receivable"] = {
|
||||
"default": "90",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"range4",
|
||||
"label": __("Ageing Range 4"),
|
||||
"fieldtype": "Int",
|
||||
"default": "120",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"finance_book",
|
||||
"label": __("Finance Book"),
|
||||
|
||||
@@ -100,11 +100,14 @@ class ReceivablePayableReport(object):
|
||||
self.filters["range2"] = "60"
|
||||
if not "range3" in self.filters:
|
||||
self.filters["range3"] = "90"
|
||||
if not "range4" in self.filters:
|
||||
self.filters["range4"] = "120"
|
||||
|
||||
for label in ("0-{range1}".format(range1=self.filters["range1"]),
|
||||
"{range1}-{range2}".format(range1=cint(self.filters["range1"])+ 1, range2=self.filters["range2"]),
|
||||
"{range2}-{range3}".format(range2=cint(self.filters["range2"])+ 1, range3=self.filters["range3"]),
|
||||
"{range3}-{above}".format(range3=cint(self.filters["range3"])+ 1, above=_("Above"))):
|
||||
"{range3}-{range4}".format(range3=cint(self.filters["range3"])+ 1, range4=self.filters["range4"]),
|
||||
"{range4}-{above}".format(range4=cint(self.filters["range4"])+ 1, above=_("Above"))):
|
||||
columns.append({
|
||||
"label": label,
|
||||
"fieldname":label,
|
||||
@@ -328,18 +331,17 @@ class ReceivablePayableReport(object):
|
||||
entry_date = gle.posting_date
|
||||
|
||||
row += get_ageing_data(cint(self.filters.range1), cint(self.filters.range2),
|
||||
cint(self.filters.range3), self.age_as_on, entry_date, outstanding_amount)
|
||||
|
||||
cint(self.filters.range3), cint(self.filters.range4), self.age_as_on, entry_date, outstanding_amount)
|
||||
|
||||
# issue 6371-Ageing buckets should not have amounts if due date is not reached
|
||||
if self.filters.ageing_based_on == "Due Date" \
|
||||
and getdate(due_date) > getdate(self.filters.report_date):
|
||||
row[-1]=row[-2]=row[-3]=row[-4]=0
|
||||
row[-1]=row[-2]=row[-3]=row[-4]=row[-5]=0
|
||||
|
||||
if self.filters.ageing_based_on == "Supplier Invoice Date" \
|
||||
and getdate(bill_date) > getdate(self.filters.report_date):
|
||||
|
||||
row[-1]=row[-2]=row[-3]=row[-4]=0
|
||||
row[-1]=row[-2]=row[-3]=row[-4]=row[-5]=0
|
||||
|
||||
if self.filters.get(scrub(args.get("party_type"))):
|
||||
row.append(gle.account_currency)
|
||||
@@ -487,13 +489,8 @@ class ReceivablePayableReport(object):
|
||||
conditions.append("company=%s")
|
||||
values.append(self.filters.company)
|
||||
|
||||
company_finance_book = erpnext.get_default_finance_book(self.filters.company)
|
||||
|
||||
if not self.filters.finance_book or (self.filters.finance_book == company_finance_book):
|
||||
if self.filters.finance_book:
|
||||
conditions.append("ifnull(finance_book,'') in (%s, '')")
|
||||
values.append(company_finance_book)
|
||||
elif self.filters.finance_book:
|
||||
conditions.append("ifnull(finance_book,'') = %s")
|
||||
values.append(self.filters.finance_book)
|
||||
|
||||
if self.filters.get(party_type_field):
|
||||
@@ -590,13 +587,13 @@ class ReceivablePayableReport(object):
|
||||
return payment_term_map
|
||||
|
||||
def get_chart_data(self, columns, data):
|
||||
ageing_columns = columns[self.ageing_col_idx_start : self.ageing_col_idx_start+4]
|
||||
ageing_columns = columns[self.ageing_col_idx_start : self.ageing_col_idx_start+5]
|
||||
|
||||
rows = []
|
||||
for d in data:
|
||||
rows.append(
|
||||
{
|
||||
'values': d[self.ageing_col_idx_start : self.ageing_col_idx_start+4]
|
||||
'values': d[self.ageing_col_idx_start : self.ageing_col_idx_start+5]
|
||||
}
|
||||
)
|
||||
|
||||
@@ -615,21 +612,22 @@ def execute(filters=None):
|
||||
}
|
||||
return ReceivablePayableReport(filters).run(args)
|
||||
|
||||
def get_ageing_data(first_range, second_range, third_range, age_as_on, entry_date, outstanding_amount):
|
||||
# [0-30, 30-60, 60-90, 90-above]
|
||||
outstanding_range = [0.0, 0.0, 0.0, 0.0]
|
||||
def get_ageing_data(first_range, second_range, third_range,
|
||||
fourth_range, age_as_on, entry_date, outstanding_amount):
|
||||
# [0-30, 30-60, 60-90, 90-120, 120-above]
|
||||
outstanding_range = [0.0, 0.0, 0.0, 0.0, 0.0]
|
||||
|
||||
if not (age_as_on and entry_date):
|
||||
return [0] + outstanding_range
|
||||
|
||||
age = (getdate(age_as_on) - getdate(entry_date)).days or 0
|
||||
index = None
|
||||
for i, days in enumerate([first_range, second_range, third_range]):
|
||||
for i, days in enumerate([first_range, second_range, third_range, fourth_range]):
|
||||
if age <= days:
|
||||
index = i
|
||||
break
|
||||
|
||||
if index is None: index = 3
|
||||
if index is None: index = 4
|
||||
outstanding_range[index] = outstanding_amount
|
||||
|
||||
return [age] + outstanding_range
|
||||
|
||||
@@ -44,6 +44,13 @@ frappe.query_reports["Accounts Receivable Summary"] = {
|
||||
"default": "90",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"range4",
|
||||
"label": __("Ageing Range 4"),
|
||||
"fieldtype": "Int",
|
||||
"default": "120",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname":"finance_book",
|
||||
"label": __("Finance Book"),
|
||||
|
||||
@@ -82,8 +82,15 @@ class AccountsReceivableSummary(ReceivablePayableReport):
|
||||
"width": 160
|
||||
},
|
||||
{
|
||||
"label": _(str(self.filters.range3) + _("-Above")),
|
||||
"fieldname": scrub(str(self.filters.range3) + _("-Above")),
|
||||
"label": _(str(self.filters.range3) + "-" + str(self.filters.range4)),
|
||||
"fieldname": scrub(str(self.filters.range3) + "-" + str(self.filters.range4)),
|
||||
"fieldtype": "Currency",
|
||||
"options": "currency",
|
||||
"width": 160
|
||||
},
|
||||
{
|
||||
"label": _(str(self.filters.range4) + _("-Above")),
|
||||
"fieldname": scrub(str(self.filters.range4) + _("-Above")),
|
||||
"fieldtype": "Currency",
|
||||
"options": "currency",
|
||||
"width": 160
|
||||
@@ -152,7 +159,7 @@ class AccountsReceivableSummary(ReceivablePayableReport):
|
||||
|
||||
row += [
|
||||
party_dict.invoiced_amt, paid_amt, party_dict.credit_amt, party_dict.outstanding_amt,
|
||||
party_dict.range1, party_dict.range2, party_dict.range3, party_dict.range4,
|
||||
party_dict.range1, party_dict.range2, party_dict.range3, party_dict.range4, party_dict.range5
|
||||
]
|
||||
|
||||
if args.get("party_type") == "Customer":
|
||||
@@ -178,6 +185,7 @@ class AccountsReceivableSummary(ReceivablePayableReport):
|
||||
"range2": 0,
|
||||
"range3": 0,
|
||||
"range4": 0,
|
||||
"range5": 0,
|
||||
"sales_person": []
|
||||
})
|
||||
)
|
||||
@@ -209,7 +217,7 @@ class AccountsReceivableSummary(ReceivablePayableReport):
|
||||
cols += ["bill_no", "bill_date"]
|
||||
|
||||
cols += ["invoiced_amt", "paid_amt", "credit_amt",
|
||||
"outstanding_amt", "age", "range1", "range2", "range3", "range4", "currency", "pdc/lc_date", "pdc/lc_ref",
|
||||
"outstanding_amt", "age", "range1", "range2", "range3", "range4", "range5", "currency", "pdc/lc_date", "pdc/lc_ref",
|
||||
"pdc/lc_amount"]
|
||||
|
||||
if args.get("party_type") == "Supplier":
|
||||
|
||||
@@ -31,11 +31,8 @@ def get_data(filters):
|
||||
|
||||
filters_data.append(["against_voucher", "in", assets])
|
||||
|
||||
company_finance_book = erpnext.get_default_finance_book(filters.get("company"))
|
||||
if (not filters.get('finance_book') or (filters.get('finance_book') == company_finance_book)):
|
||||
if filters.get("finance_book"):
|
||||
filters_data.append(["finance_book", "in", ['', filters.get('finance_book')]])
|
||||
elif filters.get("finance_book"):
|
||||
filters_data.append(["finance_book", "=", filters.get('finance_book')])
|
||||
|
||||
gl_entries = frappe.get_all('GL Entry',
|
||||
filters= filters_data,
|
||||
|
||||
@@ -101,7 +101,7 @@ def get_income_expense_data(companies, fiscal_year, filters):
|
||||
net_profit_loss = get_net_profit_loss(income, expense, companies, filters.company, company_currency, True)
|
||||
|
||||
return income, expense, net_profit_loss
|
||||
|
||||
|
||||
def get_cash_flow_data(fiscal_year, companies, filters):
|
||||
cash_flow_accounts = get_cash_flow_accounts()
|
||||
|
||||
@@ -123,7 +123,7 @@ def get_cash_flow_data(fiscal_year, companies, filters):
|
||||
# add first net income in operations section
|
||||
if net_profit_loss:
|
||||
net_profit_loss.update({
|
||||
"indent": 1,
|
||||
"indent": 1,
|
||||
"parent_account": cash_flow_accounts[0]['section_header']
|
||||
})
|
||||
data.append(net_profit_loss)
|
||||
@@ -327,7 +327,7 @@ def set_gl_entries_by_account(from_date, to_date, root_lft, root_rgt, filters, g
|
||||
accounts_by_name, ignore_closing_entries=False):
|
||||
"""Returns a dict like { "account": [gl entries], ... }"""
|
||||
|
||||
company_lft, company_rgt = frappe.get_cached_value('Company',
|
||||
company_lft, company_rgt = frappe.get_cached_value('Company',
|
||||
filters.get('company'), ["lft", "rgt"])
|
||||
|
||||
additional_conditions = get_additional_conditions(from_date, ignore_closing_entries, filters)
|
||||
@@ -354,7 +354,8 @@ def set_gl_entries_by_account(from_date, to_date, root_lft, root_rgt, filters, g
|
||||
"to_date": to_date,
|
||||
"lft": root_lft,
|
||||
"rgt": root_rgt,
|
||||
"company": d.name
|
||||
"company": d.name,
|
||||
"finance_book": filters.get("finance_book")
|
||||
},
|
||||
as_dict=True)
|
||||
|
||||
@@ -384,14 +385,8 @@ def get_additional_conditions(from_date, ignore_closing_entries, filters):
|
||||
if from_date:
|
||||
additional_conditions.append("gl.posting_date >= %(from_date)s")
|
||||
|
||||
company_finance_book = erpnext.get_default_finance_book(filters.get("company"))
|
||||
|
||||
if not filters.get('finance_book') or (filters.get('finance_book') == company_finance_book):
|
||||
additional_conditions.append("ifnull(finance_book, '') in ('%s', '')" %
|
||||
frappe.db.escape(company_finance_book))
|
||||
elif filters.get("finance_book"):
|
||||
additional_conditions.append("ifnull(finance_book, '') = '%s' " %
|
||||
frappe.db.escape(filters.get("finance_book")))
|
||||
if filters.get("finance_book"):
|
||||
additional_conditions.append("ifnull(finance_book, '') in (%(finance_book)s, '')")
|
||||
|
||||
return " and {}".format(" and ".join(additional_conditions)) if additional_conditions else ""
|
||||
|
||||
|
||||
@@ -184,12 +184,8 @@ class PartyLedgerSummaryReport(object):
|
||||
if self.filters.company:
|
||||
conditions.append("gle.company=%(company)s")
|
||||
|
||||
self.filters.company_finance_book = erpnext.get_default_finance_book(self.filters.company)
|
||||
|
||||
if not self.filters.finance_book or (self.filters.finance_book == self.filters.company_finance_book):
|
||||
conditions.append("ifnull(finance_book,'') in (%(company_finance_book)s, '')")
|
||||
elif self.filters.finance_book:
|
||||
conditions.append("ifnull(finance_book,'') = %(finance_book)s")
|
||||
if self.filters.finance_book:
|
||||
conditions.append("ifnull(finance_book,'') in (%(finance_book)s, '')")
|
||||
|
||||
if self.filters.get("party"):
|
||||
conditions.append("party=%(party)s")
|
||||
|
||||
@@ -321,7 +321,10 @@ def sort_accounts(accounts, is_root=False, key="name"):
|
||||
"""Sort root types as Asset, Liability, Equity, Income, Expense"""
|
||||
|
||||
def compare_accounts(a, b):
|
||||
if is_root:
|
||||
if re.split('\W+', a[key])[0].isdigit():
|
||||
# if chart of accounts is numbered, then sort by number
|
||||
return cmp(a[key], b[key])
|
||||
elif is_root:
|
||||
if a.report_type != b.report_type and a.report_type == "Balance Sheet":
|
||||
return -1
|
||||
if a.root_type != b.root_type and a.root_type == "Asset":
|
||||
@@ -330,10 +333,6 @@ def sort_accounts(accounts, is_root=False, key="name"):
|
||||
return -1
|
||||
if a.root_type == "Income" and b.root_type == "Expense":
|
||||
return -1
|
||||
else:
|
||||
if re.split('\W+', a[key])[0].isdigit():
|
||||
# if chart of accounts is numbered, then sort by number
|
||||
return cmp(a[key], b[key])
|
||||
return 1
|
||||
|
||||
accounts.sort(key = functools.cmp_to_key(compare_accounts))
|
||||
@@ -359,7 +358,8 @@ def set_gl_entries_by_account(
|
||||
"from_date": from_date,
|
||||
"to_date": to_date,
|
||||
"cost_center": filters.cost_center,
|
||||
"project": filters.project
|
||||
"project": filters.project,
|
||||
"finance_book": filters.get("finance_book")
|
||||
},
|
||||
as_dict=True)
|
||||
|
||||
@@ -392,14 +392,8 @@ def get_additional_conditions(from_date, ignore_closing_entries, filters):
|
||||
filters.cost_center = get_cost_centers_with_children(filters.cost_center)
|
||||
additional_conditions.append("cost_center in %(cost_center)s")
|
||||
|
||||
company_finance_book = erpnext.get_default_finance_book(filters.get("company"))
|
||||
|
||||
if not filters.get('finance_book') or (filters.get('finance_book') == company_finance_book):
|
||||
additional_conditions.append("ifnull(finance_book, '') in ('%s', '')" %
|
||||
frappe.db.escape(company_finance_book))
|
||||
elif filters.get("finance_book"):
|
||||
additional_conditions.append("ifnull(finance_book, '') = '%s' " %
|
||||
frappe.db.escape(filters.get("finance_book")))
|
||||
if filters.get("finance_book"):
|
||||
additional_conditions.append("ifnull(finance_book, '') in (%(finance_book)s, '')")
|
||||
|
||||
return " and {}".format(" and ".join(additional_conditions)) if additional_conditions else ""
|
||||
|
||||
|
||||
@@ -211,6 +211,11 @@ frappe.query_reports["General Ledger"] = {
|
||||
"label": __("Currency"),
|
||||
"fieldtype": "Select",
|
||||
"options": erpnext.get_presentation_currency_list()
|
||||
},
|
||||
{
|
||||
"fieldname": "show_opening_entries",
|
||||
"label": __("Show Opening Entries"),
|
||||
"fieldtype": "Check"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -186,12 +186,8 @@ def get_conditions(filters):
|
||||
if filters.get("project"):
|
||||
conditions.append("project in %(project)s")
|
||||
|
||||
company_finance_book = erpnext.get_default_finance_book(filters.get("company"))
|
||||
if not filters.get("finance_book") or (filters.get("finance_book") == company_finance_book):
|
||||
filters['finance_book'] = company_finance_book
|
||||
if filters.get("finance_book"):
|
||||
conditions.append("ifnull(finance_book, '') in (%(finance_book)s, '')")
|
||||
elif filters.get("finance_book"):
|
||||
conditions.append("ifnull(finance_book, '') = %(finance_book)s")
|
||||
|
||||
from frappe.desk.reportview import build_match_conditions
|
||||
match_conditions = build_match_conditions("GL Entry")
|
||||
@@ -287,7 +283,8 @@ def get_accountwise_gle(filters, gl_entries, gle_map):
|
||||
|
||||
from_date, to_date = getdate(filters.from_date), getdate(filters.to_date)
|
||||
for gle in gl_entries:
|
||||
if gle.posting_date < from_date or cstr(gle.is_opening) == "Yes":
|
||||
if (gle.posting_date < from_date or
|
||||
(cstr(gle.is_opening) == "Yes" and not filters.get("show_opening_entries"))):
|
||||
update_value_in_dict(gle_map[gle.get(group_by)].totals, 'opening', gle)
|
||||
update_value_in_dict(totals, 'opening', gle)
|
||||
|
||||
|
||||
@@ -23,6 +23,12 @@ frappe.query_reports["Gross Profit"] = {
|
||||
"fieldtype": "Date",
|
||||
"default": frappe.defaults.get_user_default("year_end_date")
|
||||
},
|
||||
{
|
||||
"fieldname":"sales_invoice",
|
||||
"label": __("Sales Invoice"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Sales Invoice"
|
||||
},
|
||||
{
|
||||
"fieldname":"group_by",
|
||||
"label": __("Group By"),
|
||||
|
||||
@@ -65,7 +65,7 @@ def get_columns(group_wise_columns, filters):
|
||||
"warehouse": _("Warehouse") + ":Link/Warehouse",
|
||||
"qty": _("Qty") + ":Float",
|
||||
"base_rate": _("Avg. Selling Rate") + ":Currency/currency",
|
||||
"buying_rate": _("Avg. Buying Rate") + ":Currency/currency",
|
||||
"buying_rate": _("Valuation Rate") + ":Currency/currency",
|
||||
"base_amount": _("Selling Amount") + ":Currency/currency",
|
||||
"buying_amount": _("Buying Amount") + ":Currency/currency",
|
||||
"gross_profit": _("Gross Profit") + ":Currency/currency",
|
||||
@@ -302,6 +302,12 @@ class GrossProfitGenerator(object):
|
||||
sales_person_cols = ""
|
||||
sales_team_table = ""
|
||||
|
||||
if self.filters.get("sales_invoice"):
|
||||
conditions += " and `tabSales Invoice`.name = %(sales_invoice)s"
|
||||
|
||||
if self.filters.get("item_code"):
|
||||
conditions += " and `tabSales Invoice Item`.item_code = %(item_code)s"
|
||||
|
||||
self.si_list = frappe.db.sql("""
|
||||
select
|
||||
`tabSales Invoice Item`.parenttype, `tabSales Invoice Item`.parent,
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
|
||||
// For license information, please see license.txt
|
||||
/* eslint-disable */
|
||||
|
||||
frappe.query_reports["Inactive Sales Items"] = {
|
||||
"filters": [
|
||||
{
|
||||
fieldname: "territory",
|
||||
label: __("Territory"),
|
||||
fieldtype: "Link",
|
||||
options: "Territory",
|
||||
reqd: 1,
|
||||
},
|
||||
{
|
||||
fieldname: "item",
|
||||
label: __("Item"),
|
||||
fieldtype: "Link",
|
||||
options: "Item"
|
||||
},
|
||||
{
|
||||
fieldname: "item_group",
|
||||
label: __("Item Group"),
|
||||
fieldtype: "Link",
|
||||
options: "Item Group"
|
||||
},
|
||||
{
|
||||
fieldname: "based_on",
|
||||
label: __("Based On"),
|
||||
fieldtype: "Select",
|
||||
options: "Sales Order\nSales Invoice",
|
||||
default: "Sales Order"
|
||||
},
|
||||
{
|
||||
fieldname: "days",
|
||||
label: __("Days Since Last order"),
|
||||
fieldtype: "Select",
|
||||
options: [30, 60, 90],
|
||||
default: 30
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"creation": "2019-05-01 12:59:52.018850",
|
||||
"disable_prepared_report": 0,
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2019-05-01 13:00:26.545278",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Inactive Sales Items",
|
||||
"owner": "Administrator",
|
||||
"prepared_report": 0,
|
||||
"ref_doctype": "Sales Invoice",
|
||||
"report_name": "Inactive Sales Items",
|
||||
"report_type": "Script Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Accounts User"
|
||||
},
|
||||
{
|
||||
"role": "Accounts Manager"
|
||||
},
|
||||
{
|
||||
"role": "Auditor"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.utils import getdate, add_days, today, cint
|
||||
from frappe import _
|
||||
|
||||
def execute(filters=None):
|
||||
columns = get_columns()
|
||||
data = get_data(filters)
|
||||
return columns, data
|
||||
|
||||
def get_columns():
|
||||
columns = [
|
||||
{
|
||||
"fieldname": "territory",
|
||||
"fieldtype": "Link",
|
||||
"label": _("Territory"),
|
||||
"options": "Territory",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"fieldname": "item_group",
|
||||
"fieldtype": "Link",
|
||||
"label": _("Item Group"),
|
||||
"options": "Item Group",
|
||||
"width": 150
|
||||
},
|
||||
{
|
||||
"fieldname": "item",
|
||||
"fieldtype": "Link",
|
||||
"options": "Item",
|
||||
"label": "Item",
|
||||
"width": 150
|
||||
},
|
||||
{
|
||||
"fieldname": "item_name",
|
||||
"fieldtype": "Data",
|
||||
"label": _("Item Name"),
|
||||
"width": 150
|
||||
},
|
||||
|
||||
{
|
||||
"fieldname": "customer",
|
||||
"fieldtype": "Link",
|
||||
"label": _("Customer"),
|
||||
"options": "Customer",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"fieldname": "last_order_date",
|
||||
"fieldtype": "Date",
|
||||
"label": _("Last Order Date"),
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"fieldname": "qty",
|
||||
"fieldtype": "Float",
|
||||
"label": _("Quantity"),
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"fieldname": "days_since_last_order",
|
||||
"fieldtype": "Int",
|
||||
"label": _("Days Since Last Order"),
|
||||
"width": 100
|
||||
},
|
||||
]
|
||||
|
||||
return columns
|
||||
|
||||
|
||||
def get_data(filters):
|
||||
data = []
|
||||
items = get_items(filters)
|
||||
territories = get_territories(filters)
|
||||
sales_invoice_data = get_sales_details(filters)
|
||||
|
||||
for territory in territories:
|
||||
for item in items:
|
||||
row = {
|
||||
"territory": territory.name,
|
||||
"item_group": item.item_group,
|
||||
"item": item.item_code,
|
||||
"item_name": item.item_name
|
||||
}
|
||||
|
||||
if sales_invoice_data.get((territory.name,item.item_code)):
|
||||
item_obj = sales_invoice_data[(territory.name,item.item_code)]
|
||||
if item_obj.days_since_last_order > cint(filters['days']):
|
||||
row.update({
|
||||
"territory": item_obj.territory,
|
||||
"customer": item_obj.customer,
|
||||
"last_order_date": item_obj.last_order_date,
|
||||
"qty": item_obj.qty,
|
||||
"days_since_last_order": item_obj.days_since_last_order
|
||||
})
|
||||
else:
|
||||
continue
|
||||
|
||||
data.append(row)
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def get_sales_details(filters):
|
||||
data = []
|
||||
item_details_map = {}
|
||||
|
||||
date_field = "s.transaction_date" if filters["based_on"] == "Sales Order" else "s.posting_date"
|
||||
|
||||
sales_data = frappe.db.sql("""
|
||||
select s.territory, s.customer, si.item_group, si.item_code, si.qty, {date_field} as last_order_date,
|
||||
DATEDIFF(CURDATE(), {date_field}) as days_since_last_order
|
||||
from `tab{doctype}` s, `tab{doctype} Item` si
|
||||
where s.name = si.parent and s.docstatus = 1
|
||||
order by days_since_last_order """ #nosec
|
||||
.format(date_field = date_field, doctype = filters['based_on']), as_dict=1)
|
||||
|
||||
for d in sales_data:
|
||||
item_details_map.setdefault((d.territory,d.item_code), d)
|
||||
|
||||
return item_details_map
|
||||
|
||||
def get_territories(filters):
|
||||
|
||||
filter_dict = {}
|
||||
if filters.get("territory"):
|
||||
filter_dict.update({'name': filters['territory']})
|
||||
|
||||
territories = frappe.get_all("Territory", fields=["name"], filters=filter_dict)
|
||||
|
||||
return territories
|
||||
|
||||
def get_items(filters):
|
||||
filters_dict = {
|
||||
"disabled": 0,
|
||||
"is_stock_item": 1
|
||||
}
|
||||
|
||||
if filters.get("item_group"):
|
||||
filters_dict.update({
|
||||
"item_group": filters["item_group"]
|
||||
})
|
||||
|
||||
if filters.get("item"):
|
||||
filters_dict.update({
|
||||
"name": filters["item"]
|
||||
})
|
||||
|
||||
items = frappe.get_all("Item", fields=["name", "item_group", "item_name", "item_code"], filters=filters_dict, order_by="name")
|
||||
|
||||
return items
|
||||
@@ -1,23 +1,27 @@
|
||||
{
|
||||
"add_total_row": 0,
|
||||
"apply_user_permissions": 1,
|
||||
"creation": "2017-12-27 16:15:52.615453",
|
||||
"disable_prepared_report": 0,
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 0,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2017-12-27 16:46:54.422356",
|
||||
"modified": "2019-04-19 10:50:36.061588",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Share Ledger",
|
||||
"owner": "Administrator",
|
||||
"prepared_report": 0,
|
||||
"ref_doctype": "Share Transfer",
|
||||
"report_name": "Share Ledger",
|
||||
"report_type": "Script Report",
|
||||
"roles": [
|
||||
{
|
||||
"role": "Administrator"
|
||||
},
|
||||
{
|
||||
"role": "System Manager"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -112,13 +112,15 @@ def convert_to_presentation_currency(gl_entries, currency_info):
|
||||
|
||||
if entry.get('debit'):
|
||||
entry['debit'] = converted_value
|
||||
else:
|
||||
|
||||
if entry.get('credit'):
|
||||
entry['credit'] = converted_value
|
||||
|
||||
elif account_currency == presentation_currency:
|
||||
if entry.get('debit'):
|
||||
entry['debit'] = debit_in_account_currency
|
||||
else:
|
||||
|
||||
if entry.get('credit'):
|
||||
entry['credit'] = credit_in_account_currency
|
||||
|
||||
converted_gl_list.append(entry)
|
||||
@@ -133,3 +135,25 @@ def get_appropriate_company(filters):
|
||||
company = get_default_company()
|
||||
|
||||
return company
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_invoiced_item_gross_margin(sales_invoice=None, item_code=None, company=None, with_item_data=False):
|
||||
from erpnext.accounts.report.gross_profit.gross_profit import GrossProfitGenerator
|
||||
|
||||
sales_invoice = sales_invoice or frappe.form_dict.get('sales_invoice')
|
||||
item_code = item_code or frappe.form_dict.get('item_code')
|
||||
company = company or frappe.get_cached_value("Sales Invoice", sales_invoice, 'company')
|
||||
|
||||
filters = {
|
||||
'sales_invoice': sales_invoice,
|
||||
'item_code': item_code,
|
||||
'company': company,
|
||||
'group_by': 'Invoice'
|
||||
}
|
||||
|
||||
gross_profit_data = GrossProfitGenerator(filters)
|
||||
result = gross_profit_data.grouped_data
|
||||
if not with_item_data:
|
||||
result = sum([d.gross_profit for d in result])
|
||||
|
||||
return result
|
||||
|
||||
@@ -333,6 +333,9 @@ def reconcile_against_document(args):
|
||||
doc = frappe.get_doc(d.voucher_type, d.voucher_no)
|
||||
doc.make_gl_entries(cancel = 0, adv_adj =1)
|
||||
|
||||
if d.voucher_type in ('Payment Entry', 'Journal Entry'):
|
||||
doc.update_expense_claim()
|
||||
|
||||
def check_if_advance_entry_modified(args):
|
||||
"""
|
||||
check if there is already a voucher reference
|
||||
@@ -375,9 +378,9 @@ def check_if_advance_entry_modified(args):
|
||||
|
||||
def validate_allocated_amount(args):
|
||||
if args.get("allocated_amount") < 0:
|
||||
throw(_("Allocated amount can not be negative"))
|
||||
throw(_("Allocated amount cannot be negative"))
|
||||
elif args.get("allocated_amount") > args.get("unadjusted_amount"):
|
||||
throw(_("Allocated amount can not greater than unadjusted amount"))
|
||||
throw(_("Allocated amount cannot be greater than unadjusted amount"))
|
||||
|
||||
def update_reference_in_journal_entry(d, jv_obj):
|
||||
"""
|
||||
|
||||
@@ -296,6 +296,12 @@ frappe.ui.form.on('Asset', {
|
||||
frm.toggle_reqd("finance_books", frm.doc.calculate_depreciation);
|
||||
},
|
||||
|
||||
gross_purchase_amount: function(frm) {
|
||||
frm.doc.finance_books.forEach(d => {
|
||||
frm.events.set_depreciation_rate(frm, d);
|
||||
})
|
||||
},
|
||||
|
||||
set_depreciation_rate: function(frm, row) {
|
||||
if (row.total_number_of_depreciations && row.frequency_of_depreciation) {
|
||||
frappe.call({
|
||||
|
||||
@@ -101,7 +101,7 @@ class Asset(AccountsController):
|
||||
|
||||
def set_depreciation_rate(self):
|
||||
for d in self.get("finance_books"):
|
||||
d.rate_of_depreciation = self.get_depreciation_rate(d)
|
||||
d.rate_of_depreciation = self.get_depreciation_rate(d, on_validate=True)
|
||||
|
||||
def make_depreciation_schedule(self):
|
||||
depreciation_method = [d.depreciation_method for d in self.finance_books]
|
||||
@@ -125,7 +125,7 @@ class Asset(AccountsController):
|
||||
no_of_depreciations * cint(d.frequency_of_depreciation))
|
||||
|
||||
total_days = date_diff(end_date, self.available_for_use_date)
|
||||
rate_per_day = value_after_depreciation / total_days
|
||||
rate_per_day = (value_after_depreciation - d.get("expected_value_after_useful_life")) / total_days
|
||||
|
||||
number_of_pending_depreciations = cint(d.total_number_of_depreciations) - \
|
||||
cint(self.number_of_depreciations_booked)
|
||||
@@ -291,8 +291,8 @@ class Asset(AccountsController):
|
||||
|
||||
def validate_expected_value_after_useful_life(self):
|
||||
for row in self.get('finance_books'):
|
||||
accumulated_depreciation_after_full_schedule = \
|
||||
max([d.accumulated_depreciation_amount for d in self.get("schedules") if d.finance_book_id == row.idx])
|
||||
accumulated_depreciation_after_full_schedule = max([d.accumulated_depreciation_amount
|
||||
for d in self.get("schedules") if cint(d.finance_book_id) == row.idx])
|
||||
|
||||
asset_value_after_full_schedule = flt(flt(self.gross_purchase_amount) -
|
||||
flt(accumulated_depreciation_after_full_schedule),
|
||||
@@ -403,7 +403,7 @@ class Asset(AccountsController):
|
||||
make_gl_entries(gl_entries)
|
||||
self.db_set('booked_fixed_asset', 1)
|
||||
|
||||
def get_depreciation_rate(self, args):
|
||||
def get_depreciation_rate(self, args, on_validate=False):
|
||||
if isinstance(args, string_types):
|
||||
args = json.loads(args)
|
||||
|
||||
@@ -420,7 +420,10 @@ class Asset(AccountsController):
|
||||
if args.get("depreciation_method") == 'Double Declining Balance':
|
||||
return 200.0 / args.get("total_number_of_depreciations")
|
||||
|
||||
if args.get("depreciation_method") == "Written Down Value" and not args.get("rate_of_depreciation"):
|
||||
if args.get("depreciation_method") == "Written Down Value":
|
||||
if args.get("rate_of_depreciation") and on_validate:
|
||||
return args.get("rate_of_depreciation")
|
||||
|
||||
no_of_years = flt(args.get("total_number_of_depreciations") * flt(args.get("frequency_of_depreciation"))) / 12
|
||||
value = flt(args.get("expected_value_after_useful_life")) / flt(self.gross_purchase_amount)
|
||||
|
||||
|
||||
@@ -102,9 +102,9 @@ class TestAsset(unittest.TestCase):
|
||||
asset.save()
|
||||
self.assertEqual(asset.status, "Draft")
|
||||
expected_schedules = [
|
||||
["2020-06-06", 163.93, 163.93],
|
||||
["2021-04-06", 49836.07, 50000.0],
|
||||
["2022-02-06", 40000.0, 90000.00]
|
||||
["2020-06-06", 147.54, 147.54],
|
||||
["2021-04-06", 44852.46, 45000.0],
|
||||
["2022-02-06", 45000.0, 90000.00]
|
||||
]
|
||||
|
||||
schedules = [[cstr(d.schedule_date), d.depreciation_amount, d.accumulated_depreciation_amount]
|
||||
@@ -130,8 +130,8 @@ class TestAsset(unittest.TestCase):
|
||||
self.assertEqual(asset.status, "Draft")
|
||||
asset.save()
|
||||
expected_schedules = [
|
||||
["2020-06-06", 197.37, 40197.37],
|
||||
["2021-04-06", 49802.63, 90000.00]
|
||||
["2020-06-06", 164.47, 40164.47],
|
||||
["2021-04-06", 49835.53, 90000.00]
|
||||
]
|
||||
schedules = [[cstr(d.schedule_date), flt(d.depreciation_amount, 2), d.accumulated_depreciation_amount]
|
||||
for d in asset.get("schedules")]
|
||||
@@ -266,8 +266,8 @@ class TestAsset(unittest.TestCase):
|
||||
self.assertEqual(asset.get("schedules")[0].journal_entry[:4], "DEPR")
|
||||
|
||||
expected_gle = (
|
||||
("_Test Accumulated Depreciations - _TC", 0.0, 35699.15),
|
||||
("_Test Depreciations - _TC", 35699.15, 0.0)
|
||||
("_Test Accumulated Depreciations - _TC", 0.0, 32129.24),
|
||||
("_Test Depreciations - _TC", 32129.24, 0.0)
|
||||
)
|
||||
|
||||
gle = frappe.db.sql("""select account, debit, credit from `tabGL Entry`
|
||||
|
||||
@@ -395,7 +395,7 @@ def make_purchase_invoice(source_name, target_doc=None):
|
||||
or item.get("buying_cost_center")
|
||||
or item_group.get("buying_cost_center"))
|
||||
|
||||
doc = get_mapped_doc("Purchase Order", source_name, {
|
||||
fields = {
|
||||
"Purchase Order": {
|
||||
"doctype": "Purchase Invoice",
|
||||
"field_map": {
|
||||
@@ -419,7 +419,15 @@ def make_purchase_invoice(source_name, target_doc=None):
|
||||
"doctype": "Purchase Taxes and Charges",
|
||||
"add_if_empty": True
|
||||
}
|
||||
}, target_doc, postprocess)
|
||||
}
|
||||
|
||||
if frappe.get_single("Accounts Settings").automatically_fetch_payment_terms == 1:
|
||||
fields["Payment Schedule"] = {
|
||||
"doctype": "Payment Schedule",
|
||||
"add_if_empty": True
|
||||
}
|
||||
|
||||
doc = get_mapped_doc("Purchase Order", source_name, fields, target_doc, postprocess)
|
||||
|
||||
return doc
|
||||
|
||||
|
||||
@@ -6,9 +6,11 @@ import unittest
|
||||
import frappe
|
||||
import frappe.defaults
|
||||
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
|
||||
from frappe.utils import flt, add_days, nowdate
|
||||
from frappe.utils import flt, add_days, nowdate, getdate
|
||||
from erpnext.stock.doctype.item.test_item import make_item
|
||||
from erpnext.buying.doctype.purchase_order.purchase_order import (make_purchase_receipt, make_purchase_invoice, make_rm_stock_entry as make_subcontract_transfer_entry)
|
||||
from erpnext.buying.doctype.purchase_order.purchase_order \
|
||||
import (make_purchase_receipt, make_purchase_invoice as make_pi_from_po, make_rm_stock_entry as make_subcontract_transfer_entry)
|
||||
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice as make_pi_from_pr
|
||||
from erpnext.stock.doctype.material_request.test_material_request import make_material_request
|
||||
from erpnext.stock.doctype.material_request.material_request import make_purchase_order
|
||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
|
||||
@@ -62,7 +64,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
|
||||
frappe.db.set_value('Item', '_Test Item', 'tolerance', 50)
|
||||
|
||||
pi = make_purchase_invoice(po.name)
|
||||
pi = make_pi_from_po(po.name)
|
||||
pi.update_stock = 1
|
||||
pi.items[0].qty = 12
|
||||
pi.insert()
|
||||
@@ -89,7 +91,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
|
||||
create_pr_against_po(po.name)
|
||||
|
||||
make_purchase_invoice(po.name)
|
||||
make_pi_from_po(po.name)
|
||||
|
||||
existing_ordered_qty = get_ordered_qty()
|
||||
existing_requested_qty = get_requested_qty()
|
||||
@@ -108,14 +110,85 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
self.assertEqual(po.get("items")[0].amount, 1400)
|
||||
self.assertEqual(get_ordered_qty(), existing_ordered_qty + 3)
|
||||
|
||||
def test_update_qty(self):
|
||||
po = create_purchase_order()
|
||||
|
||||
pr = make_pr_against_po(po.name, 2)
|
||||
|
||||
po.load_from_db()
|
||||
self.assertEqual(po.get("items")[0].received_qty, 2)
|
||||
|
||||
# Check received_qty after making PI from PR without update_stock checked
|
||||
pi1 = make_pi_from_pr(pr.name)
|
||||
pi1.get("items")[0].qty = 2
|
||||
pi1.insert()
|
||||
pi1.submit()
|
||||
|
||||
po.load_from_db()
|
||||
self.assertEqual(po.get("items")[0].received_qty, 2)
|
||||
|
||||
# Check received_qty after making PI from PO with update_stock checked
|
||||
pi2 = make_pi_from_po(po.name)
|
||||
pi2.set("update_stock", 1)
|
||||
pi2.get("items")[0].qty = 3
|
||||
pi2.insert()
|
||||
pi2.submit()
|
||||
|
||||
po.load_from_db()
|
||||
self.assertEqual(po.get("items")[0].received_qty, 5)
|
||||
|
||||
# Check received_qty after making PR from PO
|
||||
pr = make_pr_against_po(po.name, 1)
|
||||
|
||||
po.load_from_db()
|
||||
self.assertEqual(po.get("items")[0].received_qty, 6)
|
||||
|
||||
|
||||
|
||||
def test_return_against_purchase_order(self):
|
||||
po = create_purchase_order()
|
||||
|
||||
pr = make_pr_against_po(po.name, 6)
|
||||
|
||||
po.load_from_db()
|
||||
self.assertEqual(po.get("items")[0].received_qty, 6)
|
||||
|
||||
pi2 = make_pi_from_po(po.name)
|
||||
pi2.set("update_stock", 1)
|
||||
pi2.get("items")[0].qty = 3
|
||||
pi2.insert()
|
||||
pi2.submit()
|
||||
|
||||
po.load_from_db()
|
||||
self.assertEqual(po.get("items")[0].received_qty, 9)
|
||||
|
||||
# Make return purchase receipt, purchase invoice and check quantity
|
||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt \
|
||||
import make_purchase_receipt as make_purchase_receipt_return
|
||||
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice \
|
||||
import make_purchase_invoice as make_purchase_invoice_return
|
||||
|
||||
pr1 = make_purchase_receipt_return(is_return=1, return_against=pr.name, qty=-3, do_not_submit=True)
|
||||
pr1.items[0].purchase_order = po.name
|
||||
pr1.items[0].purchase_order_item = po.items[0].name
|
||||
pr1.submit()
|
||||
|
||||
pi1= make_purchase_invoice_return(is_return=1, return_against=pi2.name, qty=-1, update_stock=1, do_not_submit=True)
|
||||
pi1.items[0].purchase_order = po.name
|
||||
pi1.items[0].po_detail = po.items[0].name
|
||||
pi1.submit()
|
||||
|
||||
|
||||
po.load_from_db()
|
||||
self.assertEqual(po.get("items")[0].received_qty, 5)
|
||||
|
||||
def test_make_purchase_invoice(self):
|
||||
po = create_purchase_order(do_not_submit=True)
|
||||
|
||||
self.assertRaises(frappe.ValidationError, make_purchase_invoice, po.name)
|
||||
self.assertRaises(frappe.ValidationError, make_pi_from_po, po.name)
|
||||
|
||||
po.submit()
|
||||
pi = make_purchase_invoice(po.name)
|
||||
pi = make_pi_from_po(po.name)
|
||||
|
||||
self.assertEqual(pi.doctype, "Purchase Invoice")
|
||||
self.assertEqual(len(pi.get("items", [])), 1)
|
||||
@@ -123,7 +196,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
def test_make_purchase_invoice_with_terms(self):
|
||||
po = create_purchase_order(do_not_save=True)
|
||||
|
||||
self.assertRaises(frappe.ValidationError, make_purchase_invoice, po.name)
|
||||
self.assertRaises(frappe.ValidationError, make_pi_from_po, po.name)
|
||||
|
||||
po.update(
|
||||
{"payment_terms_template": "_Test Payment Term Template"}
|
||||
@@ -133,19 +206,19 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
po.submit()
|
||||
|
||||
self.assertEqual(po.payment_schedule[0].payment_amount, 2500.0)
|
||||
self.assertEqual(po.payment_schedule[0].due_date, po.transaction_date)
|
||||
self.assertEqual(getdate(po.payment_schedule[0].due_date), getdate(po.transaction_date))
|
||||
self.assertEqual(po.payment_schedule[1].payment_amount, 2500.0)
|
||||
self.assertEqual(po.payment_schedule[1].due_date, add_days(po.transaction_date, 30))
|
||||
pi = make_purchase_invoice(po.name)
|
||||
self.assertEqual(getdate(po.payment_schedule[1].due_date), add_days(getdate(po.transaction_date), 30))
|
||||
pi = make_pi_from_po(po.name)
|
||||
pi.save()
|
||||
|
||||
self.assertEqual(pi.doctype, "Purchase Invoice")
|
||||
self.assertEqual(len(pi.get("items", [])), 1)
|
||||
|
||||
self.assertEqual(pi.payment_schedule[0].payment_amount, 2500.0)
|
||||
self.assertEqual(pi.payment_schedule[0].due_date, po.transaction_date)
|
||||
self.assertEqual(getdate(pi.payment_schedule[0].due_date), getdate(po.transaction_date))
|
||||
self.assertEqual(pi.payment_schedule[1].payment_amount, 2500.0)
|
||||
self.assertEqual(pi.payment_schedule[1].due_date, add_days(po.transaction_date, 30))
|
||||
self.assertEqual(getdate(pi.payment_schedule[1].due_date), add_days(getdate(po.transaction_date), 30))
|
||||
|
||||
def test_subcontracting(self):
|
||||
po = create_purchase_order(item_code="_Test FG Item", is_subcontracted="Yes")
|
||||
@@ -274,7 +347,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
|
||||
self.assertTrue(po.get('payment_schedule'))
|
||||
|
||||
pi = make_purchase_invoice(po.name)
|
||||
pi = make_pi_from_po(po.name)
|
||||
|
||||
self.assertFalse(pi.get('payment_schedule'))
|
||||
|
||||
@@ -285,7 +358,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
po.submit()
|
||||
self.assertTrue(po.get('payment_schedule'))
|
||||
|
||||
pi = make_purchase_invoice(po.name)
|
||||
pi = make_pi_from_po(po.name)
|
||||
pi.insert()
|
||||
self.assertTrue(pi.get('payment_schedule'))
|
||||
|
||||
@@ -294,6 +367,10 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
make_stock_entry(target="_Test Warehouse - _TC", qty=10, basic_rate=100)
|
||||
make_stock_entry(target="_Test Warehouse - _TC", item_code="_Test Item Home Desktop 100",
|
||||
qty=20, basic_rate=100)
|
||||
make_stock_entry(target="_Test Warehouse 1 - _TC", item_code="_Test Item",
|
||||
qty=30, basic_rate=100)
|
||||
make_stock_entry(target="_Test Warehouse 1 - _TC", item_code="_Test Item Home Desktop 100",
|
||||
qty=30, basic_rate=100)
|
||||
|
||||
bin1 = frappe.db.get_value("Bin",
|
||||
filters={"warehouse": "_Test Warehouse - _TC", "item_code": "_Test Item"},
|
||||
@@ -361,7 +438,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
self.assertEquals(bin7.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6)
|
||||
|
||||
# Make Purchase Invoice
|
||||
pi = make_purchase_invoice(po.name)
|
||||
pi = make_pi_from_po(po.name)
|
||||
pi.update_stock = 1
|
||||
pi.supplier_warehouse = "_Test Warehouse 1 - _TC"
|
||||
pi.insert()
|
||||
@@ -465,6 +542,13 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
self.assertEquals(se_items, supplied_items)
|
||||
update_backflush_based_on("BOM")
|
||||
|
||||
def make_pr_against_po(po, received_qty=0):
|
||||
pr = make_purchase_receipt(po)
|
||||
pr.get("items")[0].qty = received_qty or 5
|
||||
pr.insert()
|
||||
pr.submit()
|
||||
return pr
|
||||
|
||||
def make_subcontracted_item(item_code):
|
||||
from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
|
||||
|
||||
|
||||
@@ -1,18 +1,19 @@
|
||||
{
|
||||
"add_total_row": 1,
|
||||
"apply_user_permissions": 1,
|
||||
"creation": "2013-05-13 16:10:02",
|
||||
"disable_prepared_report": 0,
|
||||
"disabled": 0,
|
||||
"docstatus": 0,
|
||||
"doctype": "Report",
|
||||
"idx": 3,
|
||||
"is_standard": "Yes",
|
||||
"modified": "2017-02-24 20:10:53.005589",
|
||||
"modified": "2019-04-19 14:54:49.123836",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Requested Items To Be Ordered",
|
||||
"owner": "Administrator",
|
||||
"query": "select \n mr.name as \"Material Request:Link/Material Request:120\",\n\tmr.transaction_date as \"Date:Date:100\",\n\tmr_item.item_code as \"Item Code:Link/Item:120\",\n\tsum(ifnull(mr_item.qty, 0)) as \"Qty:Float:100\",\n\tsum(ifnull(mr_item.ordered_qty, 0)) as \"Ordered Qty:Float:100\", \n\t(sum(mr_item.qty) - sum(ifnull(mr_item.ordered_qty, 0))) as \"Qty to Order:Float:100\",\n\tmr_item.item_name as \"Item Name::150\",\n\tmr_item.description as \"Description::200\",\n\tmr.company as \"Company:Link/Company:\"\nfrom\n\t`tabMaterial Request` mr, `tabMaterial Request Item` mr_item\nwhere\n\tmr_item.parent = mr.name\n\tand mr.material_request_type = \"Purchase\"\n\tand mr.docstatus = 1\n\tand mr.status != \"Stopped\"\ngroup by mr.name, mr_item.item_code\nhaving\n\tsum(ifnull(mr_item.ordered_qty, 0)) < sum(ifnull(mr_item.qty, 0))\norder by mr.transaction_date asc",
|
||||
"prepared_report": 0,
|
||||
"query": "select \n mr.name as \"Material Request:Link/Material Request:120\",\n\tmr.transaction_date as \"Date:Date:100\",\n\tmr_item.item_code as \"Item Code:Link/Item:120\",\n\tsum(ifnull(mr_item.stock_qty, 0)) as \"Qty:Float:100\",\n\tifnull(mr_item.stock_uom, '') as \"UOM:Link/UOM:100\",\n\tsum(ifnull(mr_item.ordered_qty, 0)) as \"Ordered Qty:Float:100\", \n\t(sum(mr_item.stock_qty) - sum(ifnull(mr_item.ordered_qty, 0))) as \"Qty to Order:Float:100\",\n\tmr_item.item_name as \"Item Name::150\",\n\tmr_item.description as \"Description::200\",\n\tmr.company as \"Company:Link/Company:\"\nfrom\n\t`tabMaterial Request` mr, `tabMaterial Request Item` mr_item\nwhere\n\tmr_item.parent = mr.name\n\tand mr.material_request_type = \"Purchase\"\n\tand mr.docstatus = 1\n\tand mr.status != \"Stopped\"\ngroup by mr.name, mr_item.item_code\nhaving\n\tsum(ifnull(mr_item.ordered_qty, 0)) < sum(ifnull(mr_item.stock_qty, 0))\norder by mr.transaction_date asc",
|
||||
"ref_doctype": "Purchase Order",
|
||||
"report_name": "Requested Items To Be Ordered",
|
||||
"report_type": "Query Report",
|
||||
|
||||
@@ -27,6 +27,11 @@ def get_data():
|
||||
"type": "doctype",
|
||||
"name": "Payment Entry",
|
||||
"description": _("Bank/Cash transactions against party or for internal transfer")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Payment Term",
|
||||
"description": _("Payment Terms based on conditions")
|
||||
}
|
||||
]
|
||||
|
||||
@@ -284,6 +289,11 @@ def get_data():
|
||||
"name": "Currency Exchange",
|
||||
"description": _("Currency exchange rate master.")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Exchange Rate Revaluation",
|
||||
"description": _("Exchange Rate Revaluation master.")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Payment Gateway Account",
|
||||
|
||||
@@ -291,6 +291,12 @@ def get_data():
|
||||
"name": "Customers Without Any Sales Transactions",
|
||||
"doctype": "Customer"
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"is_query_report": True,
|
||||
"name": "Sales Partners Commission",
|
||||
"doctype": "Customer"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -30,9 +30,8 @@ class AccountsController(TransactionBase):
|
||||
return self.__company_currency
|
||||
|
||||
def onload(self):
|
||||
if self.get("__onload"):
|
||||
self.get("__onload").make_payment_via_journal_entry \
|
||||
= frappe.db.get_single_value('Accounts Settings', 'make_payment_via_journal_entry')
|
||||
self.set_onload("make_payment_via_journal_entry",
|
||||
frappe.db.get_single_value('Accounts Settings', 'make_payment_via_journal_entry'))
|
||||
|
||||
if self.is_new():
|
||||
relevant_docs = ("Quotation", "Purchase Order", "Sales Order",
|
||||
@@ -89,7 +88,8 @@ class AccountsController(TransactionBase):
|
||||
self.validate_paid_amount()
|
||||
|
||||
if self.doctype in ['Purchase Invoice', 'Sales Invoice']:
|
||||
if cint(self.allocate_advances_automatically):
|
||||
pos_check_field = "is_pos" if self.doctype=="Sales Invoice" else "is_paid"
|
||||
if cint(self.allocate_advances_automatically) and not cint(self.get(pos_check_field)):
|
||||
self.set_advances()
|
||||
|
||||
if self.is_return:
|
||||
@@ -237,6 +237,10 @@ class AccountsController(TransactionBase):
|
||||
document_type = "{} Item".format(self.doctype)
|
||||
parent_dict.update({"document_type": document_type})
|
||||
|
||||
# party_name field used for customer in quotation
|
||||
if self.doctype == "Quotation" and self.quotation_to == "Customer" and parent_dict.get("party_name"):
|
||||
parent_dict.update({"customer": parent_dict.get("party_name")})
|
||||
|
||||
for item in self.get("items"):
|
||||
if item.get("item_code"):
|
||||
args = parent_dict.copy()
|
||||
@@ -344,7 +348,7 @@ class AccountsController(TransactionBase):
|
||||
'fiscal_year': fiscal_year,
|
||||
'voucher_type': self.doctype,
|
||||
'voucher_no': self.name,
|
||||
'remarks': self.get("remarks"),
|
||||
'remarks': self.get("remarks") or self.get("remark"),
|
||||
'debit': 0,
|
||||
'credit': 0,
|
||||
'debit_in_account_currency': 0,
|
||||
@@ -763,6 +767,9 @@ class AccountsController(TransactionBase):
|
||||
if self.doctype in ("Sales Invoice", "Purchase Invoice"):
|
||||
grand_total = grand_total - flt(self.write_off_amount)
|
||||
|
||||
if self.get("total_advance"):
|
||||
grand_total -= self.get("total_advance")
|
||||
|
||||
if not self.get("payment_schedule"):
|
||||
if self.get("payment_terms_template"):
|
||||
data = get_payment_terms(self.payment_terms_template, posting_date, grand_total)
|
||||
@@ -808,6 +815,9 @@ class AccountsController(TransactionBase):
|
||||
total = flt(total, self.precision("grand_total"))
|
||||
|
||||
grand_total = flt(self.get("rounded_total") or self.grand_total, self.precision('grand_total'))
|
||||
if self.get("total_advance"):
|
||||
grand_total -= self.get("total_advance")
|
||||
|
||||
if self.doctype in ("Sales Invoice", "Purchase Invoice"):
|
||||
grand_total = grand_total - flt(self.write_off_amount)
|
||||
if total != grand_total:
|
||||
|
||||
@@ -287,14 +287,14 @@ def copy_attributes_to_variant(item, variant):
|
||||
variant.set(field.fieldname, item.get(field.fieldname))
|
||||
|
||||
variant.variant_of = item.name
|
||||
if 'description' in allow_fields:
|
||||
variant.has_variants = 0
|
||||
|
||||
if 'description' not in allow_fields:
|
||||
if not variant.description:
|
||||
variant.description = ""
|
||||
variant.description = ""
|
||||
|
||||
if item.variant_based_on=='Item Attribute':
|
||||
if variant.attributes:
|
||||
attributes_description = ""
|
||||
attributes_description = item.description + " "
|
||||
for d in variant.attributes:
|
||||
attributes_description += "<div>" + d.attribute + ": " + cstr(d.attribute_value) + "</div>"
|
||||
|
||||
|
||||
@@ -254,11 +254,13 @@ def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len,
|
||||
and return_against in (select name from `tabDelivery Note` where per_billed < 100)
|
||||
)
|
||||
)
|
||||
%(mcond)s order by `tabDelivery Note`.`%(key)s` asc
|
||||
%(mcond)s order by `tabDelivery Note`.`%(key)s` asc limit %(start)s, %(page_len)s
|
||||
""" % {
|
||||
"key": searchfield,
|
||||
"fcond": get_filters_cond(doctype, filters, []),
|
||||
"mcond": get_match_cond(doctype),
|
||||
"start": start,
|
||||
"page_len": page_len,
|
||||
"txt": "%(txt)s"
|
||||
}, {"txt": ("%%%s%%" % txt)}, as_dict=as_dict)
|
||||
|
||||
|
||||
@@ -205,11 +205,14 @@ def get_already_returned_items(doc):
|
||||
|
||||
def make_return_doc(doctype, source_name, target_doc=None):
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
company = frappe.db.get_value("Delivery Note", source_name, "company")
|
||||
default_warehouse_for_sales_return = frappe.db.get_value("Company", company, "default_warehouse_for_sales_return")
|
||||
def set_missing_values(source, target):
|
||||
doc = frappe.get_doc(target)
|
||||
doc.is_return = 1
|
||||
doc.return_against = source.name
|
||||
doc.ignore_pricing_rule = 1
|
||||
doc.set_warehouse = ""
|
||||
if doctype == "Sales Invoice":
|
||||
doc.is_pos = source.is_pos
|
||||
|
||||
@@ -277,12 +280,16 @@ def make_return_doc(doctype, source_name, target_doc=None):
|
||||
target_doc.so_detail = source_doc.so_detail
|
||||
target_doc.si_detail = source_doc.si_detail
|
||||
target_doc.expense_account = source_doc.expense_account
|
||||
if default_warehouse_for_sales_return:
|
||||
target_doc.warehouse = default_warehouse_for_sales_return
|
||||
elif doctype == "Sales Invoice":
|
||||
target_doc.sales_order = source_doc.sales_order
|
||||
target_doc.delivery_note = source_doc.delivery_note
|
||||
target_doc.so_detail = source_doc.so_detail
|
||||
target_doc.dn_detail = source_doc.dn_detail
|
||||
target_doc.expense_account = source_doc.expense_account
|
||||
if default_warehouse_for_sales_return:
|
||||
target_doc.warehouse = default_warehouse_for_sales_return
|
||||
|
||||
def update_terms(source_doc, target_doc, source_parent):
|
||||
target_doc.payment_amount = -source_doc.payment_amount
|
||||
|
||||
@@ -45,9 +45,9 @@ status_map = {
|
||||
"Sales Invoice": [
|
||||
["Draft", None],
|
||||
["Submitted", "eval:self.docstatus==1"],
|
||||
["Paid", "eval:self.outstanding_amount==0 and self.docstatus==1"],
|
||||
["Return", "eval:self.is_return==1 and self.docstatus==1"],
|
||||
["Paid", "eval:self.outstanding_amount<=0 and self.docstatus==1 and self.is_return==0"],
|
||||
["Credit Note Issued", "eval:self.outstanding_amount < 0 and self.docstatus==1 and self.is_return==0 and get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1})"],
|
||||
["Credit Note Issued", "eval:self.outstanding_amount < 0 and self.docstatus==1"],
|
||||
["Unpaid", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1"],
|
||||
["Overdue", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1"],
|
||||
["Cancelled", "eval:self.docstatus==2"],
|
||||
@@ -55,9 +55,9 @@ status_map = {
|
||||
"Purchase Invoice": [
|
||||
["Draft", None],
|
||||
["Submitted", "eval:self.docstatus==1"],
|
||||
["Paid", "eval:self.outstanding_amount==0 and self.docstatus==1"],
|
||||
["Return", "eval:self.is_return==1 and self.docstatus==1"],
|
||||
["Paid", "eval:self.outstanding_amount<=0 and self.docstatus==1 and self.is_return==0"],
|
||||
["Debit Note Issued", "eval:self.outstanding_amount < 0 and self.docstatus==1 and self.is_return==0 and get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1})"],
|
||||
["Debit Note Issued", "eval:self.outstanding_amount < 0 and self.docstatus==1"],
|
||||
["Unpaid", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1"],
|
||||
["Overdue", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1"],
|
||||
["Cancelled", "eval:self.docstatus==2"],
|
||||
@@ -95,6 +95,10 @@ status_map = {
|
||||
["Ordered", "eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Purchase'"],
|
||||
["Transferred", "eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Material Transfer'"],
|
||||
["Issued", "eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Material Issue'"]
|
||||
],
|
||||
"Bank Transaction": [
|
||||
["Unreconciled", "eval:self.docstatus == 1 and self.unallocated_amount>0"],
|
||||
["Reconciled", "eval:self.docstatus == 1 and self.unallocated_amount<=0"]
|
||||
]
|
||||
}
|
||||
|
||||
@@ -312,19 +316,27 @@ class StatusUpdater(Document):
|
||||
.format(frappe.db.escape(frappe.session.user))
|
||||
|
||||
def update_billing_status_for_zero_amount_refdoc(self, ref_dt):
|
||||
ref_fieldname = ref_dt.lower().replace(" ", "_")
|
||||
zero_amount_refdoc = []
|
||||
all_zero_amount_refdoc = frappe.db.sql_list("""select name from `tab%s`
|
||||
where docstatus=1 and base_net_total = 0""" % ref_dt)
|
||||
ref_fieldname = frappe.scrub(ref_dt)
|
||||
|
||||
for item in self.get("items"):
|
||||
if item.get(ref_fieldname) \
|
||||
and item.get(ref_fieldname) in all_zero_amount_refdoc \
|
||||
and item.get(ref_fieldname) not in zero_amount_refdoc:
|
||||
zero_amount_refdoc.append(item.get(ref_fieldname))
|
||||
ref_docs = [item.get(ref_fieldname) for item in (self.get('items') or []) if item.get(ref_fieldname)]
|
||||
if not ref_docs:
|
||||
return
|
||||
|
||||
if zero_amount_refdoc:
|
||||
self.update_billing_status(zero_amount_refdoc, ref_dt, ref_fieldname)
|
||||
zero_amount_refdocs = frappe.db.sql_list("""
|
||||
SELECT
|
||||
name
|
||||
from
|
||||
`tab{ref_dt}`
|
||||
where
|
||||
docstatus = 1
|
||||
and base_net_total = 0
|
||||
and name in %(ref_docs)s
|
||||
""".format(ref_dt=ref_dt), {
|
||||
'ref_docs': ref_docs
|
||||
})
|
||||
|
||||
if zero_amount_refdocs:
|
||||
self.update_billing_status(zero_amount_refdocs, ref_dt, ref_fieldname)
|
||||
|
||||
def update_billing_status(self, zero_amount_refdoc, ref_dt, ref_fieldname):
|
||||
for ref_dn in zero_amount_refdoc:
|
||||
|
||||
@@ -80,6 +80,7 @@ class StockController(AccountsController):
|
||||
"cost_center": item_row.cost_center,
|
||||
"remarks": self.get("remarks") or "Accounting Entry for Stock",
|
||||
"debit": flt(sle.stock_value_difference, 2),
|
||||
"is_opening": item_row.get("is_opening") or self.get("is_opening") or "No",
|
||||
}, warehouse_account[sle.warehouse]["account_currency"]))
|
||||
|
||||
# to target warehouse / expense account
|
||||
@@ -89,7 +90,8 @@ class StockController(AccountsController):
|
||||
"cost_center": item_row.cost_center,
|
||||
"remarks": self.get("remarks") or "Accounting Entry for Stock",
|
||||
"credit": flt(sle.stock_value_difference, 2),
|
||||
"project": item_row.get("project") or self.get("project")
|
||||
"project": item_row.get("project") or self.get("project"),
|
||||
"is_opening": item_row.get("is_opening") or self.get("is_opening") or "No"
|
||||
}))
|
||||
elif sle.warehouse not in warehouse_with_no_account:
|
||||
warehouse_with_no_account.append(sle.warehouse)
|
||||
@@ -123,8 +125,17 @@ class StockController(AccountsController):
|
||||
|
||||
def get_voucher_details(self, default_expense_account, default_cost_center, sle_map):
|
||||
if self.doctype == "Stock Reconciliation":
|
||||
return [frappe._dict({ "name": voucher_detail_no, "expense_account": default_expense_account,
|
||||
"cost_center": default_cost_center }) for voucher_detail_no, sle in sle_map.items()]
|
||||
reconciliation_purpose = frappe.db.get_value(self.doctype, self.name, "purpose")
|
||||
is_opening = "Yes" if reconciliation_purpose == "Opening Stock" else "No"
|
||||
details = []
|
||||
for voucher_detail_no in sle_map:
|
||||
details.append(frappe._dict({
|
||||
"name": voucher_detail_no,
|
||||
"expense_account": default_expense_account,
|
||||
"cost_center": default_cost_center,
|
||||
"is_opening": is_opening
|
||||
}))
|
||||
return details
|
||||
else:
|
||||
details = self.get("items")
|
||||
|
||||
@@ -351,10 +362,12 @@ class StockController(AccountsController):
|
||||
frappe.throw(_("Row {0}: Quality Inspection rejected for item {1}")
|
||||
.format(d.idx, d.item_code), QualityInspectionRejectedError)
|
||||
elif qa_required :
|
||||
frappe.msgprint(_("Quality Inspection required for Item {0}").format(d.item_code))
|
||||
if self.docstatus==1:
|
||||
raise QualityInspectionRequiredError
|
||||
|
||||
action = frappe.get_doc('Stock Settings').action_if_quality_inspection_is_not_submitted
|
||||
if self.docstatus==1 and action == 'Stop':
|
||||
frappe.throw(_("Quality Inspection required for Item {0} to submit").format(frappe.bold(d.item_code)),
|
||||
exc=QualityInspectionRequiredError)
|
||||
else:
|
||||
frappe.msgprint(_("Create Quality Inspection for Item {0}").format(frappe.bold(d.item_code)))
|
||||
|
||||
def update_blanket_order(self):
|
||||
blanket_orders = list(set([d.blanket_order for d in self.items if d.blanket_order]))
|
||||
|
||||
@@ -608,7 +608,7 @@ def get_itemised_tax_breakup_data(doc):
|
||||
|
||||
return itemised_tax, itemised_taxable_amount
|
||||
|
||||
def get_itemised_tax(taxes):
|
||||
def get_itemised_tax(taxes, with_tax_account=False):
|
||||
itemised_tax = {}
|
||||
for tax in taxes:
|
||||
if getattr(tax, "category", None) and tax.category=="Valuation":
|
||||
@@ -633,6 +633,9 @@ def get_itemised_tax(taxes):
|
||||
tax_amount = tax_amount
|
||||
))
|
||||
|
||||
if with_tax_account:
|
||||
itemised_tax[item_code][tax.description].tax_account = tax.account_head
|
||||
|
||||
return itemised_tax
|
||||
|
||||
def get_itemised_taxable_amount(items):
|
||||
|
||||
@@ -5,79 +5,79 @@ frappe.provide("erpnext");
|
||||
cur_frm.email_field = "email_id";
|
||||
|
||||
erpnext.LeadController = frappe.ui.form.Controller.extend({
|
||||
setup: function() {
|
||||
this.frm.fields_dict.customer.get_query = function(doc, cdt, cdn) {
|
||||
return { query: "erpnext.controllers.queries.customer_query" } }
|
||||
setup: function () {
|
||||
this.frm.fields_dict.customer.get_query = function (doc, cdt, cdn) {
|
||||
return { query: "erpnext.controllers.queries.customer_query" }
|
||||
}
|
||||
|
||||
this.frm.toggle_reqd("lead_name", !this.frm.doc.organization_lead);
|
||||
},
|
||||
|
||||
onload: function() {
|
||||
|
||||
if(cur_frm.fields_dict.lead_owner.df.options.match(/^User/)) {
|
||||
cur_frm.fields_dict.lead_owner.get_query = function(doc, cdt, cdn) {
|
||||
onload: function () {
|
||||
if (cur_frm.fields_dict.lead_owner.df.options.match(/^User/)) {
|
||||
cur_frm.fields_dict.lead_owner.get_query = function (doc, cdt, cdn) {
|
||||
return { query: "frappe.core.doctype.user.user.user_query" }
|
||||
}
|
||||
}
|
||||
|
||||
if(cur_frm.fields_dict.contact_by.df.options.match(/^User/)) {
|
||||
cur_frm.fields_dict.contact_by.get_query = function(doc, cdt, cdn) {
|
||||
return { query: "frappe.core.doctype.user.user.user_query" } }
|
||||
if (cur_frm.fields_dict.contact_by.df.options.match(/^User/)) {
|
||||
cur_frm.fields_dict.contact_by.get_query = function (doc, cdt, cdn) {
|
||||
return { query: "frappe.core.doctype.user.user.user_query" }
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
refresh: function() {
|
||||
refresh: function () {
|
||||
var doc = this.frm.doc;
|
||||
erpnext.toggle_naming_series();
|
||||
frappe.dynamic_link = {doc: doc, fieldname: 'name', doctype: 'Lead'}
|
||||
frappe.dynamic_link = { doc: doc, fieldname: 'name', doctype: 'Lead' }
|
||||
|
||||
if(!doc.__islocal && doc.__onload && !doc.__onload.is_customer) {
|
||||
if (!doc.__islocal && doc.__onload && !doc.__onload.is_customer) {
|
||||
this.frm.add_custom_button(__("Customer"), this.create_customer, __("Make"));
|
||||
this.frm.add_custom_button(__("Opportunity"), this.create_opportunity, __("Make"));
|
||||
this.frm.add_custom_button(__("Quotation"), this.make_quotation, __("Make"));
|
||||
}
|
||||
|
||||
if(!this.frm.doc.__islocal) {
|
||||
if (!this.frm.doc.__islocal) {
|
||||
frappe.contacts.render_address_and_contact(cur_frm);
|
||||
} else {
|
||||
frappe.contacts.clear_address_and_contact(cur_frm);
|
||||
}
|
||||
},
|
||||
|
||||
create_customer: function() {
|
||||
create_customer: function () {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.crm.doctype.lead.lead.make_customer",
|
||||
frm: cur_frm
|
||||
})
|
||||
},
|
||||
|
||||
create_opportunity: function() {
|
||||
create_opportunity: function () {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.crm.doctype.lead.lead.make_opportunity",
|
||||
frm: cur_frm
|
||||
})
|
||||
},
|
||||
|
||||
make_quotation: function() {
|
||||
make_quotation: function () {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: "erpnext.crm.doctype.lead.lead.make_quotation",
|
||||
frm: cur_frm
|
||||
})
|
||||
},
|
||||
|
||||
organization_lead: function() {
|
||||
if (this.frm.doc.organization_lead == 1) {
|
||||
this.frm.set_df_property('company_name', 'reqd', 1);
|
||||
} else {
|
||||
this.frm.set_df_property('company_name', 'reqd', 0);
|
||||
}
|
||||
organization_lead: function () {
|
||||
this.frm.toggle_reqd("lead_name", !this.frm.doc.organization_lead);
|
||||
this.frm.toggle_reqd("company_name", this.frm.doc.organization_lead);
|
||||
},
|
||||
|
||||
company_name: function() {
|
||||
company_name: function () {
|
||||
if (this.frm.doc.organization_lead == 1) {
|
||||
this.frm.set_value("lead_name", this.frm.doc.company_name);
|
||||
}
|
||||
},
|
||||
|
||||
contact_date: function() {
|
||||
contact_date: function () {
|
||||
if (this.frm.doc.contact_date) {
|
||||
let d = moment(this.frm.doc.contact_date);
|
||||
d.add(1, "hours");
|
||||
@@ -86,4 +86,4 @@ erpnext.LeadController = frappe.ui.form.Controller.extend({
|
||||
}
|
||||
});
|
||||
|
||||
$.extend(cur_frm.cscript, new erpnext.LeadController({frm: cur_frm}));
|
||||
$.extend(cur_frm.cscript, new erpnext.LeadController({ frm: cur_frm }));
|
||||
|
||||
@@ -141,7 +141,7 @@
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 1,
|
||||
"reqd": 0,
|
||||
"search_index": 1,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
@@ -499,7 +499,7 @@
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Lead Owner",
|
||||
"length": 0,
|
||||
@@ -1389,7 +1389,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-10-02 09:12:23.415379",
|
||||
"modified": "2019-05-10 03:22:57.283628",
|
||||
"modified_by": "Administrator",
|
||||
"module": "CRM",
|
||||
"name": "Lead",
|
||||
|
||||
@@ -10,6 +10,7 @@ from frappe.model.mapper import get_mapped_doc
|
||||
from erpnext.controllers.selling_controller import SellingController
|
||||
from frappe.contacts.address_and_contact import load_address_and_contact
|
||||
from erpnext.accounts.party import set_taxes
|
||||
from frappe.email.inbox import link_communication_to_document
|
||||
|
||||
sender_field = "email_id"
|
||||
|
||||
@@ -89,11 +90,11 @@ class Lead(SellingController):
|
||||
return frappe.db.get_value("Customer", {"lead_name": self.name})
|
||||
|
||||
def has_opportunity(self):
|
||||
return frappe.db.get_value("Opportunity", {"lead": self.name, "status": ["!=", "Lost"]})
|
||||
return frappe.db.get_value("Opportunity", {"party_name": self.name, "status": ["!=", "Lost"]})
|
||||
|
||||
def has_quotation(self):
|
||||
return frappe.db.get_value("Quotation", {
|
||||
"lead": self.name,
|
||||
"party_name": self.name,
|
||||
"docstatus": 1,
|
||||
"status": ["!=", "Lost"]
|
||||
|
||||
@@ -101,14 +102,18 @@ class Lead(SellingController):
|
||||
|
||||
def has_lost_quotation(self):
|
||||
return frappe.db.get_value("Quotation", {
|
||||
"lead": self.name,
|
||||
"party_name": self.name,
|
||||
"docstatus": 1,
|
||||
"status": "Lost"
|
||||
})
|
||||
|
||||
def set_lead_name(self):
|
||||
if not self.lead_name:
|
||||
frappe.db.set_value("Lead", self.name, "lead_name", self.company_name)
|
||||
# Check for leads being created through data import
|
||||
if not self.company_name and not self.flags.ignore_mandatory:
|
||||
frappe.throw(_("A Lead requires either a person's name or an organization's name"))
|
||||
|
||||
self.lead_name = self.company_name
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_customer(source_name, target_doc=None):
|
||||
@@ -145,8 +150,8 @@ def make_opportunity(source_name, target_doc=None):
|
||||
"doctype": "Opportunity",
|
||||
"field_map": {
|
||||
"campaign_name": "campaign",
|
||||
"doctype": "enquiry_from",
|
||||
"name": "lead",
|
||||
"doctype": "opportunity_from",
|
||||
"name": "party_name",
|
||||
"lead_name": "contact_display",
|
||||
"company_name": "customer_name",
|
||||
"email_id": "contact_email",
|
||||
@@ -162,7 +167,7 @@ def make_quotation(source_name, target_doc=None):
|
||||
{"Lead": {
|
||||
"doctype": "Quotation",
|
||||
"field_map": {
|
||||
"name": "lead"
|
||||
"name": "party_name"
|
||||
}
|
||||
}}, target_doc)
|
||||
target_doc.quotation_to = "Lead"
|
||||
@@ -185,7 +190,7 @@ def get_lead_details(lead, posting_date=None, company=None):
|
||||
out.update({
|
||||
"territory": lead.territory,
|
||||
"customer_name": lead.company_name or lead.lead_name,
|
||||
"contact_display": lead.lead_name,
|
||||
"contact_display": " ".join(filter(None, [lead.salutation, lead.lead_name])),
|
||||
"contact_email": lead.email_id,
|
||||
"contact_mobile": lead.mobile_no,
|
||||
"contact_phone": lead.phone,
|
||||
@@ -199,3 +204,29 @@ def get_lead_details(lead, posting_date=None, company=None):
|
||||
out['taxes_and_charges'] = taxes_and_charges
|
||||
|
||||
return out
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_lead_from_communication(communication, ignore_communication_links=False):
|
||||
""" raise a issue from email """
|
||||
|
||||
doc = frappe.get_doc("Communication", communication)
|
||||
lead_name = None
|
||||
if doc.sender:
|
||||
lead_name = frappe.db.get_value("Lead", {"email_id": doc.sender})
|
||||
if not lead_name and doc.phone_no:
|
||||
lead_name = frappe.db.get_value("Lead", {"mobile_no": doc.phone_no})
|
||||
if not lead_name:
|
||||
lead = frappe.get_doc({
|
||||
"doctype": "Lead",
|
||||
"lead_name": doc.sender_full_name,
|
||||
"email_id": doc.sender,
|
||||
"mobile_no": doc.phone_no
|
||||
})
|
||||
lead.flags.ignore_mandatory = True
|
||||
lead.flags.ignore_permissions = True
|
||||
lead.insert()
|
||||
|
||||
lead_name = lead.name
|
||||
|
||||
link_communication_to_document(doc, "Lead", lead_name, ignore_communication_links)
|
||||
return lead_name
|
||||
|
||||
@@ -4,6 +4,13 @@ from frappe import _
|
||||
def get_data():
|
||||
return {
|
||||
'fieldname': 'lead',
|
||||
'non_standard_fieldnames': {
|
||||
'Quotation': 'party_name',
|
||||
'Opportunity': 'party_name'
|
||||
},
|
||||
'dynamic_links': {
|
||||
'party_name': ['Lead', 'quotation_to']
|
||||
},
|
||||
'transactions': [
|
||||
{
|
||||
'items': ['Opportunity', 'Quotation']
|
||||
|
||||
@@ -9,15 +9,34 @@ frappe.ui.form.on("Opportunity", {
|
||||
frm.custom_make_buttons = {
|
||||
'Quotation': 'Quotation',
|
||||
'Supplier Quotation': 'Supplier Quotation'
|
||||
}
|
||||
},
|
||||
customer: function(frm) {
|
||||
frm.trigger('set_contact_link');
|
||||
erpnext.utils.get_party_details(frm);
|
||||
},
|
||||
|
||||
frm.set_query("opportunity_from", function() {
|
||||
return{
|
||||
"filters": {
|
||||
"name": ["in", ["Customer", "Lead"]],
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
lead: function(frm) {
|
||||
frm.trigger('set_contact_link');
|
||||
onload_post_render: function(frm) {
|
||||
frm.get_field("items").grid.set_multiple_add("item_code", "qty");
|
||||
},
|
||||
|
||||
party_name: function(frm) {
|
||||
frm.toggle_display("contact_info", frm.doc.party_name);
|
||||
|
||||
if (frm.doc.opportunity_from == "Customer") {
|
||||
frm.trigger('set_contact_link');
|
||||
erpnext.utils.get_party_details(frm);
|
||||
} else if (frm.doc.opportunity_from == "Lead") {
|
||||
erpnext.utils.map_current_doc({
|
||||
method: "erpnext.crm.doctype.lead.lead.make_opportunity",
|
||||
source_name: frm.doc.party_name,
|
||||
frm: frm
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
with_items: function(frm) {
|
||||
@@ -30,15 +49,14 @@ frappe.ui.form.on("Opportunity", {
|
||||
|
||||
contact_person: erpnext.utils.get_contact_details,
|
||||
|
||||
enquiry_from: function(frm) {
|
||||
frm.toggle_reqd("lead", frm.doc.enquiry_from==="Lead");
|
||||
frm.toggle_reqd("customer", frm.doc.enquiry_from==="Customer");
|
||||
opportunity_from: function(frm) {
|
||||
frm.toggle_reqd("party_name", frm.doc.opportunity_from);
|
||||
frm.trigger("set_dynamic_field_label");
|
||||
},
|
||||
|
||||
refresh: function(frm) {
|
||||
var doc = frm.doc;
|
||||
frm.events.enquiry_from(frm);
|
||||
frm.trigger('set_contact_link');
|
||||
frm.events.opportunity_from(frm);
|
||||
frm.trigger('toggle_mandatory');
|
||||
erpnext.toggle_naming_series();
|
||||
|
||||
@@ -75,10 +93,17 @@ frappe.ui.form.on("Opportunity", {
|
||||
},
|
||||
|
||||
set_contact_link: function(frm) {
|
||||
if(frm.doc.customer) {
|
||||
frappe.dynamic_link = {doc: frm.doc, fieldname: 'customer', doctype: 'Customer'}
|
||||
} else if(frm.doc.lead) {
|
||||
frappe.dynamic_link = {doc: frm.doc, fieldname: 'lead', doctype: 'Lead'}
|
||||
if(frm.doc.opportunity_from == "Customer" && frm.doc.party_name) {
|
||||
frappe.dynamic_link = {doc: frm.doc, fieldname: 'party_name', doctype: 'Customer'}
|
||||
} else if(frm.doc.opportunity_from == "Lead" && frm.doc.party_name) {
|
||||
frappe.dynamic_link = {doc: frm.doc, fieldname: 'party_name', doctype: 'Lead'}
|
||||
}
|
||||
},
|
||||
|
||||
set_dynamic_field_label: function(frm){
|
||||
|
||||
if (frm.doc.opportunity_from) {
|
||||
frm.set_df_property("party_name", "label", frm.doc.opportunity_from);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -97,10 +122,6 @@ frappe.ui.form.on("Opportunity", {
|
||||
// TODO commonify this code
|
||||
erpnext.crm.Opportunity = frappe.ui.form.Controller.extend({
|
||||
onload: function() {
|
||||
if(!this.frm.doc.enquiry_from && this.frm.doc.customer)
|
||||
this.frm.doc.enquiry_from = "Customer";
|
||||
if(!this.frm.doc.enquiry_from && this.frm.doc.lead)
|
||||
this.frm.doc.enquiry_from = "Lead";
|
||||
|
||||
if(!this.frm.doc.status)
|
||||
set_multiple(this.frm.doc.doctype, this.frm.doc.name, { status:'Open' });
|
||||
@@ -129,12 +150,14 @@ erpnext.crm.Opportunity = frappe.ui.form.Controller.extend({
|
||||
};
|
||||
});
|
||||
|
||||
$.each([["lead", "lead"],
|
||||
["customer", "customer"],
|
||||
["contact_person", "contact_query"]],
|
||||
function(i, opts) {
|
||||
me.frm.set_query(opts[0], erpnext.queries[opts[1]]);
|
||||
});
|
||||
me.frm.set_query('contact_person', erpnext.queries['contact_query'])
|
||||
|
||||
if (me.frm.doc.opportunity_from == "Lead") {
|
||||
me.frm.set_query('party_name', erpnext.queries['lead']);
|
||||
}
|
||||
else if (me.frm.doc.opportunity_from == "Cuatomer") {
|
||||
me.frm.set_query('party_name', erpnext.queries['customer']);
|
||||
}
|
||||
},
|
||||
|
||||
create_quotation: function() {
|
||||
@@ -147,11 +170,6 @@ erpnext.crm.Opportunity = frappe.ui.form.Controller.extend({
|
||||
|
||||
$.extend(cur_frm.cscript, new erpnext.crm.Opportunity({frm: cur_frm}));
|
||||
|
||||
cur_frm.cscript.onload_post_render = function(doc, cdt, cdn) {
|
||||
if(doc.enquiry_from == 'Lead' && doc.lead)
|
||||
cur_frm.cscript.lead(doc, cdt, cdn);
|
||||
}
|
||||
|
||||
cur_frm.cscript.item_code = function(doc, cdt, cdn) {
|
||||
var d = locals[cdt][cdn];
|
||||
if (d.item_code) {
|
||||
@@ -170,15 +188,6 @@ cur_frm.cscript.item_code = function(doc, cdt, cdn) {
|
||||
}
|
||||
}
|
||||
|
||||
cur_frm.cscript.lead = function(doc, cdt, cdn) {
|
||||
cur_frm.toggle_display("contact_info", doc.customer || doc.lead);
|
||||
erpnext.utils.map_current_doc({
|
||||
method: "erpnext.crm.doctype.lead.lead.make_opportunity",
|
||||
source_name: cur_frm.doc.lead,
|
||||
frm: cur_frm
|
||||
});
|
||||
}
|
||||
|
||||
cur_frm.cscript['Declare Opportunity Lost'] = function() {
|
||||
var dialog = new frappe.ui.Dialog({
|
||||
title: __("Set as Lost"),
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "from_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
@@ -54,6 +55,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "naming_series",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
@@ -88,21 +90,22 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "enquiry_from",
|
||||
"fieldtype": "Select",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "opportunity_from",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Opportunity From",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "enquiry_from",
|
||||
"oldfieldtype": "Select",
|
||||
"options": "\nLead\nCustomer",
|
||||
"options": "DocType",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
@@ -122,9 +125,10 @@
|
||||
"bold": 1,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.enquiry_from===\"Customer\"",
|
||||
"fieldname": "customer",
|
||||
"fieldtype": "Link",
|
||||
"depends_on": "",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "party_name",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
@@ -132,54 +136,19 @@
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Customer",
|
||||
"label": "Party",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "customer",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Customer",
|
||||
"options": "opportunity_from",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 1,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.enquiry_from===\"Lead\"",
|
||||
"fieldname": "lead",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 1,
|
||||
"label": "Lead",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"oldfieldname": "lead",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Lead",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"reqd": 1,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
@@ -193,6 +162,8 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "",
|
||||
"fetch_from": "",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "customer_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
@@ -224,6 +195,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "column_break0",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
@@ -256,6 +228,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "title",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
@@ -289,6 +262,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "Sales",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "opportunity_type",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@@ -324,6 +298,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "Open",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "status",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
@@ -359,6 +334,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.status===\"Lost\"",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "order_lost_reason",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 0,
|
||||
@@ -390,6 +366,7 @@
|
||||
"bold": 1,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "mins_to_first_response",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
@@ -423,6 +400,7 @@
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "contact_by",
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "next_contact",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
@@ -456,6 +434,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "contact_by",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@@ -492,6 +471,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "contact_date",
|
||||
"fieldtype": "Datetime",
|
||||
"hidden": 0,
|
||||
@@ -525,6 +505,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "column_break2",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
@@ -557,6 +538,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "to_discuss",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 0,
|
||||
@@ -590,6 +572,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "section_break_14",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
@@ -622,6 +605,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "currency",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@@ -655,6 +639,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "opportunity_amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
@@ -687,6 +672,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "with_items",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
@@ -719,6 +705,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "column_break_17",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
@@ -751,6 +738,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "Prospecting",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "sales_stage",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@@ -785,6 +773,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "100",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "probability",
|
||||
"fieldtype": "Percent",
|
||||
"hidden": 0,
|
||||
@@ -818,6 +807,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "with_items",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "items_section",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
@@ -852,6 +842,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"description": "",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "items",
|
||||
"fieldtype": "Table",
|
||||
"hidden": 0,
|
||||
@@ -887,7 +878,8 @@
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "next_contact_by",
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.lead || doc.customer",
|
||||
"depends_on": "eval:doc.party_name",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "contact_info",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
@@ -920,7 +912,8 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.customer || doc.lead",
|
||||
"depends_on": "eval:doc.party_name",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "customer_address",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@@ -953,6 +946,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "address_display",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 1,
|
||||
@@ -988,6 +982,7 @@
|
||||
"columns": 0,
|
||||
"depends_on": "customer",
|
||||
"description": "",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "territory",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@@ -1022,6 +1017,7 @@
|
||||
"columns": 0,
|
||||
"depends_on": "customer",
|
||||
"description": "",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "customer_group",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@@ -1056,6 +1052,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "column_break3",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
@@ -1086,7 +1083,8 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.lead || doc.customer",
|
||||
"depends_on": "eval:doc.party_name",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "contact_person",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@@ -1120,6 +1118,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "customer",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "contact_display",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 0,
|
||||
@@ -1151,7 +1150,8 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.lead || doc.customer",
|
||||
"depends_on": "eval:doc.party_name",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "contact_email",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
@@ -1183,7 +1183,8 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.lead || doc.customer",
|
||||
"depends_on": "eval:doc.party_name",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "contact_mobile",
|
||||
"fieldtype": "Small Text",
|
||||
"hidden": 0,
|
||||
@@ -1216,6 +1217,7 @@
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "",
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "more_info",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
@@ -1249,6 +1251,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "source",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@@ -1285,6 +1288,7 @@
|
||||
"columns": 0,
|
||||
"depends_on": "eval: doc.source==\"Campaign\"",
|
||||
"description": "Enter name of campaign if source of enquiry is campaign",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "campaign",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@@ -1319,6 +1323,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "column_break1",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
@@ -1351,6 +1356,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@@ -1386,6 +1392,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "Today",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "transaction_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
@@ -1420,6 +1427,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "amended_from",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@@ -1460,7 +1468,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-10-01 09:28:43.990999",
|
||||
"modified": "2019-05-17 19:03:32.740910",
|
||||
"modified_by": "Administrator",
|
||||
"module": "CRM",
|
||||
"name": "Opportunity",
|
||||
@@ -1508,11 +1516,11 @@
|
||||
"quick_entry": 0,
|
||||
"read_only": 0,
|
||||
"read_only_onload": 0,
|
||||
"search_fields": "status,transaction_date,customer,lead,opportunity_type,territory,company",
|
||||
"search_fields": "status,transaction_date,party_name,opportunity_type,territory,company",
|
||||
"show_name_in_global_search": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"timeline_field": "customer",
|
||||
"timeline_field": "party_name",
|
||||
"title_field": "title",
|
||||
"track_changes": 0,
|
||||
"track_seen": 1,
|
||||
|
||||
@@ -9,14 +9,15 @@ from frappe.model.mapper import get_mapped_doc
|
||||
from erpnext.setup.utils import get_exchange_rate
|
||||
from erpnext.utilities.transaction_base import TransactionBase
|
||||
from erpnext.accounts.party import get_party_account_currency
|
||||
from frappe.email.inbox import link_communication_to_document
|
||||
|
||||
subject_field = "title"
|
||||
sender_field = "contact_email"
|
||||
|
||||
class Opportunity(TransactionBase):
|
||||
def after_insert(self):
|
||||
if self.lead:
|
||||
frappe.get_doc("Lead", self.lead).set_status(update=True)
|
||||
if self.opportunity_from == "Lead":
|
||||
frappe.get_doc("Lead", self.party_name).set_status(update=True)
|
||||
|
||||
def validate(self):
|
||||
self._prev = frappe._dict({
|
||||
@@ -28,12 +29,8 @@ class Opportunity(TransactionBase):
|
||||
|
||||
self.make_new_lead_if_required()
|
||||
|
||||
if not self.enquiry_from:
|
||||
frappe.throw(_("Opportunity From field is mandatory"))
|
||||
|
||||
self.validate_item_details()
|
||||
self.validate_uom_is_integer("uom", "qty")
|
||||
self.validate_lead_cust()
|
||||
self.validate_cust_name()
|
||||
|
||||
if not self.title:
|
||||
@@ -44,7 +41,7 @@ class Opportunity(TransactionBase):
|
||||
|
||||
def make_new_lead_if_required(self):
|
||||
"""Set lead against new opportunity"""
|
||||
if not (self.lead or self.customer) and self.contact_email:
|
||||
if (not self.get("party_name")) and self.contact_email:
|
||||
# check if customer is already created agains the self.contact_email
|
||||
customer = frappe.db.sql("""select
|
||||
distinct `tabDynamic Link`.link_name as customer
|
||||
@@ -60,8 +57,8 @@ class Opportunity(TransactionBase):
|
||||
`tabDynamic Link`.link_doctype='Customer'
|
||||
""".format(self.contact_email), as_dict=True)
|
||||
if customer and customer[0].customer:
|
||||
self.customer = customer[0].customer
|
||||
self.enquiry_from = "Customer"
|
||||
self.party_name = customer[0].customer
|
||||
self.opportunity_from = "Customer"
|
||||
return
|
||||
|
||||
lead_name = frappe.db.get_value("Lead", {"email_id": self.contact_email})
|
||||
@@ -88,8 +85,8 @@ class Opportunity(TransactionBase):
|
||||
lead.insert(ignore_permissions=True)
|
||||
lead_name = lead.name
|
||||
|
||||
self.enquiry_from = "Lead"
|
||||
self.lead = lead_name
|
||||
self.opportunity_from = "Lead"
|
||||
self.party_name = lead_name
|
||||
|
||||
def declare_enquiry_lost(self,arg):
|
||||
if not self.has_active_quotation():
|
||||
@@ -136,10 +133,10 @@ class Opportunity(TransactionBase):
|
||||
return True
|
||||
|
||||
def validate_cust_name(self):
|
||||
if self.customer:
|
||||
self.customer_name = frappe.db.get_value("Customer", self.customer, "customer_name")
|
||||
elif self.lead:
|
||||
lead_name, company_name = frappe.db.get_value("Lead", self.lead, ["lead_name", "company_name"])
|
||||
if self.party_name and self.opportunity_from == 'Customer':
|
||||
self.customer_name = frappe.db.get_value("Customer", self.party_name, "customer_name")
|
||||
elif self.party_name and self.opportunity_from == 'Lead':
|
||||
lead_name, company_name = frappe.db.get_value("Lead", self.party_name, ["lead_name", "company_name"])
|
||||
self.customer_name = company_name or lead_name
|
||||
|
||||
def on_update(self):
|
||||
@@ -152,16 +149,16 @@ class Opportunity(TransactionBase):
|
||||
opts.description = ""
|
||||
opts.contact_date = self.contact_date
|
||||
|
||||
if self.customer:
|
||||
if self.party_name and self.opportunity_from == 'Customer':
|
||||
if self.contact_person:
|
||||
opts.description = 'Contact '+cstr(self.contact_person)
|
||||
else:
|
||||
opts.description = 'Contact customer '+cstr(self.customer)
|
||||
elif self.lead:
|
||||
opts.description = 'Contact customer '+cstr(self.party_name)
|
||||
elif self.party_name and self.opportunity_from == 'Lead':
|
||||
if self.contact_display:
|
||||
opts.description = 'Contact '+cstr(self.contact_display)
|
||||
else:
|
||||
opts.description = 'Contact lead '+cstr(self.lead)
|
||||
opts.description = 'Contact lead '+cstr(self.party_name)
|
||||
|
||||
opts.subject = opts.description
|
||||
opts.description += '. By : ' + cstr(self.contact_by)
|
||||
@@ -186,17 +183,6 @@ class Opportunity(TransactionBase):
|
||||
for key in item_fields:
|
||||
if not d.get(key): d.set(key, item.get(key))
|
||||
|
||||
def validate_lead_cust(self):
|
||||
if self.enquiry_from == 'Lead':
|
||||
if not self.lead:
|
||||
frappe.throw(_("Lead must be set if Opportunity is made from Lead"))
|
||||
else:
|
||||
self.customer = None
|
||||
elif self.enquiry_from == 'Customer':
|
||||
if not self.customer:
|
||||
msgprint(_("Customer is mandatory if 'Opportunity From' is selected as Customer"), raise_exception=1)
|
||||
else:
|
||||
self.lead = None
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_item_details(item_code):
|
||||
@@ -218,8 +204,11 @@ def make_quotation(source_name, target_doc=None):
|
||||
quotation = frappe.get_doc(target)
|
||||
|
||||
company_currency = frappe.get_cached_value('Company', quotation.company, "default_currency")
|
||||
party_account_currency = get_party_account_currency("Customer", quotation.customer,
|
||||
quotation.company) if quotation.customer else company_currency
|
||||
|
||||
if quotation.quotation_to == 'Customer' and quotation.party_name:
|
||||
party_account_currency = get_party_account_currency("Customer", quotation.party_name, quotation.company)
|
||||
else:
|
||||
party_account_currency = company_currency
|
||||
|
||||
quotation.currency = party_account_currency or company_currency
|
||||
|
||||
@@ -245,7 +234,7 @@ def make_quotation(source_name, target_doc=None):
|
||||
"Opportunity": {
|
||||
"doctype": "Quotation",
|
||||
"field_map": {
|
||||
"enquiry_from": "quotation_to",
|
||||
"opportunity_from": "quotation_to",
|
||||
"opportunity_type": "order_type",
|
||||
"name": "enq_no",
|
||||
}
|
||||
@@ -321,3 +310,24 @@ def auto_close_opportunity():
|
||||
doc.flags.ignore_permissions = True
|
||||
doc.flags.ignore_mandatory = True
|
||||
doc.save()
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_opportunity_from_communication(communication, ignore_communication_links=False):
|
||||
from erpnext.crm.doctype.lead.lead import make_lead_from_communication
|
||||
doc = frappe.get_doc("Communication", communication)
|
||||
|
||||
lead = doc.reference_name if doc.reference_doctype == "Lead" else None
|
||||
if not lead:
|
||||
lead = make_lead_from_communication(communication, ignore_communication_links=True)
|
||||
|
||||
enquiry_from = "Lead"
|
||||
|
||||
opportunity = frappe.get_doc({
|
||||
"doctype": "Opportunity",
|
||||
"enquiry_from": enquiry_from,
|
||||
"lead": lead
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
link_communication_to_document(doc, "Opportunity", opportunity.name, ignore_communication_links)
|
||||
|
||||
return opportunity.name
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
frappe.listview_settings['Opportunity'] = {
|
||||
add_fields: ["customer_name", "opportunity_type", "enquiry_from", "status"],
|
||||
add_fields: ["customer_name", "opportunity_type", "opportunity_from", "status"],
|
||||
get_indicator: function(doc) {
|
||||
var indicator = [__(doc.status), frappe.utils.guess_colour(doc.status), "status,=," + doc.status];
|
||||
if(doc.status=="Quotation") {
|
||||
@@ -17,5 +17,13 @@ frappe.listview_settings['Opportunity'] = {
|
||||
listview.page.add_menu_item(__("Set as Closed"), function() {
|
||||
listview.call_for_selected_items(method, {"status": "Closed"});
|
||||
});
|
||||
|
||||
listview.page.fields_dict.opportunity_from.get_query = function() {
|
||||
return {
|
||||
"filters": {
|
||||
"name": ["in", ["Customer", "Lead"]],
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@ QUnit.test("test: opportunity", function (assert) {
|
||||
() => frappe.timeout(1),
|
||||
() => frappe.click_button('New'),
|
||||
() => frappe.timeout(1),
|
||||
() => cur_frm.set_value('enquiry_from', 'Customer'),
|
||||
() => cur_frm.set_value('opportunity_from', 'Customer'),
|
||||
() => cur_frm.set_value('customer', 'Test Customer 1'),
|
||||
|
||||
// check items
|
||||
|
||||
@@ -37,13 +37,13 @@ class TestOpportunity(unittest.TestCase):
|
||||
# new lead should be created against the new.opportunity@example.com
|
||||
opp_doc = frappe.get_doc(args).insert(ignore_permissions=True)
|
||||
|
||||
self.assertTrue(opp_doc.lead)
|
||||
self.assertEqual(opp_doc.enquiry_from, "Lead")
|
||||
self.assertEqual(frappe.db.get_value("Lead", opp_doc.lead, "email_id"),
|
||||
self.assertTrue(opp_doc.party_name)
|
||||
self.assertEqual(opp_doc.opportunity_from, "Lead")
|
||||
self.assertEqual(frappe.db.get_value("Lead", opp_doc.party_name, "email_id"),
|
||||
'new.opportunity@example.com')
|
||||
|
||||
# create new customer and create new contact against 'new.opportunity@example.com'
|
||||
customer = make_customer(opp_doc.lead).insert(ignore_permissions=True)
|
||||
customer = make_customer(opp_doc.party_name).insert(ignore_permissions=True)
|
||||
frappe.get_doc({
|
||||
"doctype": "Contact",
|
||||
"email_id": "new.opportunity@example.com",
|
||||
@@ -55,9 +55,9 @@ class TestOpportunity(unittest.TestCase):
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
opp_doc = frappe.get_doc(args).insert(ignore_permissions=True)
|
||||
self.assertTrue(opp_doc.customer)
|
||||
self.assertEqual(opp_doc.enquiry_from, "Customer")
|
||||
self.assertEqual(opp_doc.customer, customer.name)
|
||||
self.assertTrue(opp_doc.party_name)
|
||||
self.assertEqual(opp_doc.opportunity_from, "Customer")
|
||||
self.assertEqual(opp_doc.party_name, customer.name)
|
||||
|
||||
def make_opportunity(**args):
|
||||
args = frappe._dict(args)
|
||||
@@ -65,17 +65,17 @@ def make_opportunity(**args):
|
||||
opp_doc = frappe.get_doc({
|
||||
"doctype": "Opportunity",
|
||||
"company": args.company or "_Test Company",
|
||||
"enquiry_from": args.enquiry_from or "Customer",
|
||||
"opportunity_from": args.opportunity_from or "Customer",
|
||||
"opportunity_type": "Sales",
|
||||
"with_items": args.with_items or 0,
|
||||
"transaction_date": today()
|
||||
})
|
||||
|
||||
if opp_doc.enquiry_from == 'Customer':
|
||||
opp_doc.customer = args.customer or "_Test Customer"
|
||||
if opp_doc.opportunity_from == 'Customer':
|
||||
opp_doc.party_name= args.customer or "_Test Customer"
|
||||
|
||||
if opp_doc.enquiry_from == 'Lead':
|
||||
opp_doc.customer = args.lead or "_T-Lead-00001"
|
||||
if opp_doc.opportunity_from == 'Lead':
|
||||
opp_doc.party_name = args.lead or "_T-Lead-00001"
|
||||
|
||||
if args.with_items:
|
||||
opp_doc.append('items', {
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
{
|
||||
"doctype": "Opportunity",
|
||||
"name": "_Test Opportunity 1",
|
||||
"enquiry_from": "Lead",
|
||||
"opportunity_from": "Lead",
|
||||
"enquiry_type": "Sales",
|
||||
"lead": "_T-Lead-00001",
|
||||
"party_name": "_T-Lead-00001",
|
||||
"transaction_date": "2013-12-12",
|
||||
"items": [{
|
||||
"item_name": "Test Item",
|
||||
|
||||
@@ -4,86 +4,132 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import flt
|
||||
|
||||
def execute(filters=None):
|
||||
columns, data = [], []
|
||||
columns=get_columns()
|
||||
data=get_lead_data(filters, "Campaign Name")
|
||||
columns=get_columns("Campaign Name")
|
||||
data=get_lead_data(filters or {}, "Campaign Name")
|
||||
return columns, data
|
||||
|
||||
def get_columns():
|
||||
|
||||
def get_columns(based_on):
|
||||
return [
|
||||
_("Campaign Name") + ":data:130",
|
||||
_("Lead Count") + ":Int:80",
|
||||
_("Opp Count") + ":Int:80",
|
||||
_("Quot Count") + ":Int:80",
|
||||
_("Order Count") + ":Int:100",
|
||||
_("Order Value") + ":Float:100",
|
||||
_("Opp/Lead %") + ":Float:100",
|
||||
_("Quot/Lead %") + ":Float:100",
|
||||
_("Order/Quot %") + ":Float:100"
|
||||
{
|
||||
"fieldname": frappe.scrub(based_on),
|
||||
"label": _(based_on),
|
||||
"fieldtype": "Data",
|
||||
"width": 150
|
||||
},
|
||||
{
|
||||
"fieldname": "lead_count",
|
||||
"label": _("Lead Count"),
|
||||
"fieldtype": "Int",
|
||||
"width": 80
|
||||
},
|
||||
{
|
||||
"fieldname": "opp_count",
|
||||
"label": _("Opp Count"),
|
||||
"fieldtype": "Int",
|
||||
"width": 80
|
||||
},
|
||||
{
|
||||
"fieldname": "quot_count",
|
||||
"label": _("Quot Count"),
|
||||
"fieldtype": "Int",
|
||||
"width": 80
|
||||
},
|
||||
{
|
||||
"fieldname": "order_count",
|
||||
"label": _("Order Count"),
|
||||
"fieldtype": "Int",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"fieldname": "order_value",
|
||||
"label": _("Order Value"),
|
||||
"fieldtype": "Float",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"fieldname": "opp_lead",
|
||||
"label": _("Opp/Lead %"),
|
||||
"fieldtype": "Float",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"fieldname": "quot_lead",
|
||||
"label": _("Quot/Lead %"),
|
||||
"fieldtype": "Float",
|
||||
"width": 100
|
||||
},
|
||||
{
|
||||
"fieldname": "order_quot",
|
||||
"label": _("Order/Quot %"),
|
||||
"fieldtype": "Float",
|
||||
"width": 100
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
def get_lead_data(filters, based_on):
|
||||
based_on_field = frappe.scrub(based_on)
|
||||
conditions = get_filter_conditions(filters)
|
||||
|
||||
|
||||
lead_details = frappe.db.sql("""
|
||||
select {based_on_field}, name
|
||||
from `tabLead`
|
||||
where {based_on_field} is not null and {based_on_field} != '' {conditions}
|
||||
from `tabLead`
|
||||
where {based_on_field} is not null and {based_on_field} != '' {conditions}
|
||||
""".format(based_on_field=based_on_field, conditions=conditions), filters, as_dict=1)
|
||||
|
||||
|
||||
lead_map = frappe._dict()
|
||||
for d in lead_details:
|
||||
lead_map.setdefault(d.get(based_on_field), []).append(d.name)
|
||||
|
||||
|
||||
data = []
|
||||
for based_on_value, leads in lead_map.items():
|
||||
row = {
|
||||
based_on: based_on_value,
|
||||
"Lead Count": len(leads)
|
||||
based_on_field: based_on_value,
|
||||
"lead_count": len(leads)
|
||||
}
|
||||
row["Quot Count"]= get_lead_quotation_count(leads)
|
||||
row["Opp Count"] = get_lead_opp_count(leads)
|
||||
row["Order Count"] = get_quotation_ordered_count(leads)
|
||||
row["Order Value"] = get_order_amount(leads)
|
||||
|
||||
row["Opp/Lead %"] = row["Opp Count"] / row["Lead Count"] * 100
|
||||
row["Quot/Lead %"] = row["Quot Count"] / row["Lead Count"] * 100
|
||||
|
||||
row["Order/Quot %"] = row["Order Count"] / (row["Quot Count"] or 1) * 100
|
||||
|
||||
row["quot_count"]= get_lead_quotation_count(leads)
|
||||
row["opp_count"] = get_lead_opp_count(leads)
|
||||
row["order_count"] = get_quotation_ordered_count(leads)
|
||||
row["order_value"] = get_order_amount(leads) or 0
|
||||
|
||||
row["opp_lead"] = flt(row["opp_count"]) / flt(row["lead_count"] or 1.0) * 100.0
|
||||
row["quot_lead"] = flt(row["quot_count"]) / flt(row["lead_count"] or 1.0) * 100.0
|
||||
|
||||
row["order_quot"] = flt(row["order_count"]) / flt(row["quot_count"] or 1.0) * 100.0
|
||||
|
||||
data.append(row)
|
||||
|
||||
|
||||
return data
|
||||
|
||||
|
||||
def get_filter_conditions(filters):
|
||||
conditions=""
|
||||
if filters.from_date:
|
||||
conditions += " and date(creation) >= %(from_date)s"
|
||||
if filters.to_date:
|
||||
conditions += " and date(creation) <= %(to_date)s"
|
||||
|
||||
|
||||
return conditions
|
||||
|
||||
|
||||
def get_lead_quotation_count(leads):
|
||||
return frappe.db.sql("""select count(name) from `tabQuotation`
|
||||
where lead in (%s)""" % ', '.join(["%s"]*len(leads)), tuple(leads))[0][0]
|
||||
|
||||
return frappe.db.sql("""select count(name) from `tabQuotation`
|
||||
where quotation_to = 'Lead' and party_name in (%s)""" % ', '.join(["%s"]*len(leads)), tuple(leads))[0][0] #nosec
|
||||
|
||||
def get_lead_opp_count(leads):
|
||||
return frappe.db.sql("""select count(name) from `tabOpportunity`
|
||||
where lead in (%s)""" % ', '.join(["%s"]*len(leads)), tuple(leads))[0][0]
|
||||
|
||||
return frappe.db.sql("""select count(name) from `tabOpportunity`
|
||||
where opportunity_from = 'Lead' and party_name in (%s)""" % ', '.join(["%s"]*len(leads)), tuple(leads))[0][0]
|
||||
|
||||
def get_quotation_ordered_count(leads):
|
||||
return frappe.db.sql("""select count(name)
|
||||
from `tabQuotation` where status = 'Ordered'
|
||||
and lead in (%s)""" % ', '.join(["%s"]*len(leads)), tuple(leads))[0][0]
|
||||
|
||||
return frappe.db.sql("""select count(name)
|
||||
from `tabQuotation` where status = 'Ordered' and quotation_to = 'Lead'
|
||||
and party_name in (%s)""" % ', '.join(["%s"]*len(leads)), tuple(leads))[0][0]
|
||||
|
||||
def get_order_amount(leads):
|
||||
return frappe.db.sql("""select sum(base_net_amount)
|
||||
return frappe.db.sql("""select sum(base_net_amount)
|
||||
from `tabSales Order Item`
|
||||
where prevdoc_docname in (
|
||||
select name from `tabQuotation` where status = 'Ordered'
|
||||
and lead in (%s)
|
||||
select name from `tabQuotation` where status = 'Ordered'
|
||||
and quotation_to = 'Lead' and party_name in (%s)
|
||||
)""" % ', '.join(["%s"]*len(leads)), tuple(leads))[0][0]
|
||||
@@ -66,7 +66,7 @@ def get_columns():
|
||||
def get_communication_details(filters):
|
||||
communication_count = None
|
||||
communication_list = []
|
||||
opportunities = frappe.db.get_values('Opportunity', {'enquiry_from': 'Lead'},\
|
||||
opportunities = frappe.db.get_values('Opportunity', {'opportunity_from': 'Lead'},\
|
||||
['name', 'customer_name', 'lead', 'contact_email'], as_dict=1)
|
||||
|
||||
for d in opportunities:
|
||||
|
||||
@@ -11,16 +11,61 @@ def execute(filters=None):
|
||||
columns=get_columns()
|
||||
data=get_lead_data(filters, "Lead Owner")
|
||||
return columns, data
|
||||
|
||||
|
||||
def get_columns():
|
||||
return [
|
||||
_("Lead Owner") + ":Data:130",
|
||||
_("Lead Count") + ":Int:80",
|
||||
_("Opp Count") + ":Int:80",
|
||||
_("Quot Count") + ":Int:80",
|
||||
_("Order Count") + ":Int:100",
|
||||
_("Order Value") + ":Float:100",
|
||||
_("Opp/Lead %") + ":Float:100",
|
||||
_("Quot/Lead %") + ":Float:100",
|
||||
_("Order/Quot %") + ":Float:100"
|
||||
{
|
||||
"fieldname": "lead_owner",
|
||||
"label": _("Lead Owner"),
|
||||
"fieldtype": "Data",
|
||||
"width": "130"
|
||||
},
|
||||
{
|
||||
"fieldname": "lead_count",
|
||||
"label": _("Lead Count"),
|
||||
"fieldtype": "Int",
|
||||
"width": "80"
|
||||
},
|
||||
{
|
||||
"fieldname": "opp_count",
|
||||
"label": _("Opp Count"),
|
||||
"fieldtype": "Int",
|
||||
"width": "80"
|
||||
},
|
||||
{
|
||||
"fieldname": "quot_count",
|
||||
"label": _("Quot Count"),
|
||||
"fieldtype": "Int",
|
||||
"width": "80"
|
||||
},
|
||||
{
|
||||
"fieldname": "order_count",
|
||||
"label": _("Order Count"),
|
||||
"fieldtype": "Int",
|
||||
"width": "100"
|
||||
},
|
||||
{
|
||||
"fieldname": "order_value",
|
||||
"label": _("Order Value"),
|
||||
"fieldtype": "Float",
|
||||
"width": "100"
|
||||
},
|
||||
{
|
||||
"fieldname": "opp_lead",
|
||||
"label": _("Opp/Lead %"),
|
||||
"fieldtype": "Float",
|
||||
"width": "100"
|
||||
},
|
||||
{
|
||||
"fieldname": "quot_lead",
|
||||
"label": _("Quot/Lead %"),
|
||||
"fieldtype": "Float",
|
||||
"width": "100"
|
||||
},
|
||||
{
|
||||
"fieldname": "order_quot",
|
||||
"label": _("Order/Quot %"),
|
||||
"fieldtype": "Float",
|
||||
"width": "100"
|
||||
}
|
||||
]
|
||||
@@ -35,14 +35,14 @@ def get_data(filters):
|
||||
|
||||
for lead in frappe.get_all('Lead', fields = ['name', 'lead_name', 'company_name'], filters=lead_filters):
|
||||
data = frappe.db.sql("""
|
||||
select
|
||||
`tabCommunication`.reference_doctype, `tabCommunication`.reference_name,
|
||||
select
|
||||
`tabCommunication`.reference_doctype, `tabCommunication`.reference_name,
|
||||
`tabCommunication`.content, `tabCommunication`.communication_date
|
||||
from
|
||||
from
|
||||
(
|
||||
(select name, lead from `tabOpportunity` where lead = %(lead)s)
|
||||
union
|
||||
(select name, lead from `tabQuotation` where lead = %(lead)s)
|
||||
(select name, party_name as lead from `tabOpportunity` where opportunity_from='Lead' and party_name = %(lead)s)
|
||||
union
|
||||
(select name, party_name as lead from `tabQuotation` where quotation_to = 'Lead' and party_name = %(lead)s)
|
||||
union
|
||||
(select name, lead from `tabIssue` where lead = %(lead)s and status!='Closed')
|
||||
union
|
||||
|
||||
@@ -56,7 +56,7 @@ def work(domain="Manufacturing"):
|
||||
def make_opportunity(domain):
|
||||
b = frappe.get_doc({
|
||||
"doctype": "Opportunity",
|
||||
"enquiry_from": "Customer",
|
||||
"opportunity_from": "Customer",
|
||||
"customer": get_random("Customer"),
|
||||
"opportunity_type": "Sales",
|
||||
"with_items": 1,
|
||||
|
||||
@@ -186,7 +186,7 @@ def link_item(item_data,item_status):
|
||||
item.item_name = str(item_data.get("name"))
|
||||
item.item_code = "woocommerce - " + str(item_data.get("product_id"))
|
||||
item.woocommerce_id = str(item_data.get("product_id"))
|
||||
item.item_group = "WooCommerce Products"
|
||||
item.item_group = _("WooCommerce Products")
|
||||
item.stock_uom = woocommerce_settings.uom or _("Nos")
|
||||
item.save()
|
||||
frappe.db.commit()
|
||||
|
||||
@@ -22,6 +22,9 @@ erpnext.integrations.plaidLink = class plaidLink {
|
||||
frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.plaid_configuration')
|
||||
.then(result => {
|
||||
if (result !== "disabled") {
|
||||
if (result.plaid_env == undefined || result.plaid_public_key == undefined) {
|
||||
frappe.throw(__("Please add valid Plaid api keys in site_config.json first"));
|
||||
}
|
||||
me.plaid_env = result.plaid_env;
|
||||
me.plaid_public_key = result.plaid_public_key;
|
||||
me.client_name = result.client_name;
|
||||
|
||||
@@ -107,10 +107,18 @@ def get_series():
|
||||
|
||||
def setup_custom_fields():
|
||||
custom_fields = {
|
||||
"Customer": [dict(fieldname='shopify_customer_id', label='Shopify Customer Id',
|
||||
fieldtype='Data', insert_after='series', read_only=1, print_hide=1)],
|
||||
"Address": [dict(fieldname='shopify_address_id', label='Shopify Address Id',
|
||||
fieldtype='Data', insert_after='fax', read_only=1, print_hide=1)],
|
||||
"Customer": [
|
||||
dict(fieldname='shopify_customer_id', label='Shopify Customer Id',
|
||||
fieldtype='Data', insert_after='series', read_only=1, print_hide=1)
|
||||
],
|
||||
"Supplier": [
|
||||
dict(fieldname='shopify_supplier_id', label='Shopify Supplier Id',
|
||||
fieldtype='Data', insert_after='supplier_name', read_only=1, print_hide=1)
|
||||
],
|
||||
"Address": [
|
||||
dict(fieldname='shopify_address_id', label='Shopify Address Id',
|
||||
fieldtype='Data', insert_after='fax', read_only=1, print_hide=1)
|
||||
],
|
||||
"Item": [
|
||||
dict(fieldname='shopify_variant_id', label='Shopify Variant Id',
|
||||
fieldtype='Data', insert_after='item_code', read_only=1, print_hide=1),
|
||||
@@ -119,16 +127,20 @@ def setup_custom_fields():
|
||||
dict(fieldname='shopify_description', label='Shopify Description',
|
||||
fieldtype='Text Editor', insert_after='description', read_only=1, print_hide=1)
|
||||
],
|
||||
"Sales Order": [dict(fieldname='shopify_order_id', label='Shopify Order Id',
|
||||
fieldtype='Data', insert_after='title', read_only=1, print_hide=1)],
|
||||
"Sales Order": [
|
||||
dict(fieldname='shopify_order_id', label='Shopify Order Id',
|
||||
fieldtype='Data', insert_after='title', read_only=1, print_hide=1)
|
||||
],
|
||||
"Delivery Note":[
|
||||
dict(fieldname='shopify_order_id', label='Shopify Order Id',
|
||||
fieldtype='Data', insert_after='title', read_only=1, print_hide=1),
|
||||
dict(fieldname='shopify_fulfillment_id', label='Shopify Fulfillment Id',
|
||||
fieldtype='Data', insert_after='title', read_only=1, print_hide=1)
|
||||
],
|
||||
"Sales Invoice": [dict(fieldname='shopify_order_id', label='Shopify Order Id',
|
||||
fieldtype='Data', insert_after='title', read_only=1, print_hide=1)]
|
||||
"Sales Invoice": [
|
||||
dict(fieldname='shopify_order_id', label='Shopify Order Id',
|
||||
fieldtype='Data', insert_after='title', read_only=1, print_hide=1)
|
||||
]
|
||||
}
|
||||
|
||||
create_custom_fields(custom_fields)
|
||||
|
||||
@@ -4,8 +4,8 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
from frappe import _
|
||||
from frappe.utils.nestedset import get_root_of
|
||||
from frappe.model.document import Document
|
||||
from six.moves.urllib.parse import urlparse
|
||||
|
||||
@@ -62,10 +62,10 @@ class WoocommerceSettings(Document):
|
||||
custom.read_only = 1
|
||||
custom.save()
|
||||
|
||||
if not frappe.get_value("Item Group",{"name": "WooCommerce Products"}):
|
||||
if not frappe.get_value("Item Group",{"name": _("WooCommerce Products")}):
|
||||
item_group = frappe.new_doc("Item Group")
|
||||
item_group.item_group_name = "WooCommerce Products"
|
||||
item_group.parent_item_group = _("All Item Groups")
|
||||
item_group.item_group_name = _("WooCommerce Products")
|
||||
item_group.parent_item_group = get_root_of("Item Group")
|
||||
item_group.save()
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ class WoocommerceSettings(Document):
|
||||
for name in email_names:
|
||||
frappe.delete_doc("Custom Field",name)
|
||||
|
||||
frappe.delete_doc("Item Group","WooCommerce Products")
|
||||
frappe.delete_doc("Item Group", _("WooCommerce Products"))
|
||||
|
||||
frappe.db.commit()
|
||||
|
||||
|
||||
@@ -82,7 +82,7 @@ def get_healthcare_services_to_invoice(patient):
|
||||
'service': service_item, 'rate': practitioner_charge,
|
||||
'income_account': income_account})
|
||||
|
||||
lab_tests = frappe.get_list("Lab Test", {'patient': patient.name, 'invoiced': False})
|
||||
lab_tests = frappe.get_list("Lab Test", {'patient': patient.name, 'invoiced': False, 'docstatus': 1})
|
||||
if lab_tests:
|
||||
for lab_test in lab_tests:
|
||||
lab_test_obj = frappe.get_doc("Lab Test", lab_test['name'])
|
||||
|
||||
@@ -256,7 +256,7 @@ scheduler_events = {
|
||||
"daily_long": [
|
||||
"erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.update_latest_price_in_all_boms"
|
||||
],
|
||||
"monthly": [
|
||||
"monthly_long": [
|
||||
"erpnext.accounts.deferred_revenue.convert_deferred_revenue_to_income",
|
||||
"erpnext.accounts.deferred_revenue.convert_deferred_expense_to_expense",
|
||||
"erpnext.hr.utils.allocate_earned_leaves"
|
||||
|
||||
@@ -3,13 +3,8 @@
|
||||
|
||||
frappe.ui.form.on('Additional Salary', {
|
||||
setup: function(frm) {
|
||||
frm.set_query("salary_component", function() {
|
||||
return {
|
||||
filters: {
|
||||
is_additional_component: true
|
||||
}
|
||||
};
|
||||
});
|
||||
frm.add_fetch("salary_component", "deduct_full_tax_on_selected_payroll_date", "deduct_full_tax_on_selected_payroll_date");
|
||||
|
||||
frm.set_query("employee", function() {
|
||||
return {
|
||||
filters: {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"allow_copy": 0,
|
||||
"allow_events_in_timeline": 0,
|
||||
"allow_guest_to_view": 0,
|
||||
"allow_import": 1,
|
||||
"allow_rename": 0,
|
||||
@@ -21,6 +22,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "naming_series",
|
||||
"fieldtype": "Select",
|
||||
"hidden": 0,
|
||||
@@ -54,6 +56,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "employee",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@@ -87,6 +90,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "salary_component",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@@ -120,6 +124,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "amount",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
@@ -153,6 +158,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "1",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "overwrite_salary_structure_amount",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
@@ -185,6 +191,40 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "deduct_full_tax_on_selected_payroll_date",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Deduct Full Tax on Selected Payroll Date",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"translatable": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_in_quick_entry": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "column_break_5",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
@@ -217,6 +257,8 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "",
|
||||
"description": "Date on which this component is applied",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "payroll_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
@@ -250,6 +292,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_from": "employee.employee_name",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "employee_name",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
@@ -284,6 +327,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_from": "employee.department",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "department",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@@ -317,6 +361,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "company",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@@ -350,6 +395,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "salary_slip",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@@ -384,6 +430,7 @@
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_from": "salary_component.type",
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "type",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 0,
|
||||
@@ -417,6 +464,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fetch_if_empty": 0,
|
||||
"fieldname": "amended_from",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
@@ -453,7 +501,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2018-08-21 14:44:22.921926",
|
||||
"modified": "2019-05-09 19:53:37.475839",
|
||||
"modified_by": "Administrator",
|
||||
"module": "HR",
|
||||
"name": "Additional Salary",
|
||||
|
||||
@@ -9,6 +9,12 @@ from frappe import _
|
||||
from frappe.utils import getdate, date_diff
|
||||
|
||||
class AdditionalSalary(Document):
|
||||
def before_insert(self):
|
||||
if frappe.db.exists("Additional Salary", {"employee": self.employee, "salary_component": self.salary_component,
|
||||
"amount": self.amount, "payroll_date": self.payroll_date, "company": self.company}):
|
||||
|
||||
frappe.throw(_("Additional Salary Component Exists."))
|
||||
|
||||
def validate(self):
|
||||
self.validate_dates()
|
||||
if self.amount < 0:
|
||||
@@ -35,7 +41,8 @@ class AdditionalSalary(Document):
|
||||
@frappe.whitelist()
|
||||
def get_additional_salary_component(employee, start_date, end_date):
|
||||
additional_components = frappe.db.sql("""
|
||||
select salary_component, sum(amount) as amount, overwrite_salary_structure_amount from `tabAdditional Salary`
|
||||
select salary_component, sum(amount) as amount, overwrite_salary_structure_amount, deduct_full_tax_on_selected_payroll_date
|
||||
from `tabAdditional Salary`
|
||||
where employee=%(employee)s
|
||||
and docstatus = 1
|
||||
and payroll_date between %(from_date)s and %(to_date)s
|
||||
@@ -48,16 +55,20 @@ def get_additional_salary_component(employee, start_date, end_date):
|
||||
}, as_dict=1)
|
||||
|
||||
additional_components_list = []
|
||||
component_fields = ["depends_on_payment_days", "salary_component_abbr", "is_tax_applicable", "variable_based_on_taxable_salary", 'type']
|
||||
for d in additional_components:
|
||||
component = frappe.get_doc("Salary Component", d.salary_component)
|
||||
struct_row = {'salary_component': d.salary_component}
|
||||
for field in ["depends_on_lwp", "abbr", "is_tax_applicable", "variable_based_on_taxable_salary", "is_additional_component"]:
|
||||
struct_row[field] = component.get(field)
|
||||
struct_row = frappe._dict({'salary_component': d.salary_component})
|
||||
component = frappe.get_all("Salary Component", filters={'name': d.salary_component}, fields=component_fields)
|
||||
if component:
|
||||
struct_row.update(component[0])
|
||||
|
||||
additional_components_list.append({
|
||||
struct_row['deduct_full_tax_on_selected_payroll_date'] = d.deduct_full_tax_on_selected_payroll_date
|
||||
struct_row['is_additional_component'] = 1
|
||||
|
||||
additional_components_list.append(frappe._dict({
|
||||
'amount': d.amount,
|
||||
'type': component.type,
|
||||
'type': component[0].type,
|
||||
'struct_row': struct_row,
|
||||
'overwrite': d.overwrite_salary_structure_amount
|
||||
})
|
||||
'overwrite': d.overwrite_salary_structure_amount,
|
||||
}))
|
||||
return additional_components_list
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user