Merge branch 'develop' into pos-refactor

This commit is contained in:
Faris Ansari 2017-08-30 12:37:05 +05:30 committed by GitHub
commit 491108a198
184 changed files with 3919 additions and 1024 deletions

View File

@ -55,4 +55,7 @@ script:
- set -e
- bench run-tests
- sleep 5
- bench reinstall --yes
- bench execute erpnext.setup.setup_wizard.utils.complete
- bench execute erpnext.setup.utils.enable_all_roles_and_domains
- bench run-ui-tests --app erpnext

View File

@ -4,7 +4,7 @@ import inspect
import frappe
from erpnext.hooks import regional_overrides
__version__ = '8.8.3'
__version__ = '8.10.0'
def get_default_company(user=None):
'''Get default company for user'''

View File

@ -4,7 +4,7 @@
"""
Import chart of accounts from OpenERP sources
"""
from __future__ import unicode_literals
from __future__ import print_function, unicode_literals
import os, json
import ast
@ -229,7 +229,7 @@ def make_charts():
filename = src["id"][5:] + "_" + chart_id
print "building " + filename
print("building " + filename)
chart = {}
chart["name"] = src["name"]
chart["country_code"] = src["id"][5:]

View File

@ -0,0 +1,722 @@
{
"country_code": "tw",
"name": "Taiwan - Chart of Accounts",
"tree": {
"1-\u8cc7\u7522": {
"11~12-\u6d41\u52d5\u8cc7\u7522": {
"111-\u73fe\u91d1\u53ca\u7d04\u7576\u73fe\u91d1": {
"1111-\u5eab\u5b58\u73fe\u91d1": {
"account_type": "Cash"
},
"1112-\u96f6\u7528\u91d1/\u9031\u8f49\u91d1": {
"account_type": "Cash"
},
"1113-\u9280\u884c\u5b58\u6b3e": {
"account_type": "Bank",
"\u4e2d\u570b\u4fe1\u8a17": {
"account_type": "Bank"
},
"\u53f0\u5317\u5bcc\u90a6": {
"account_type": "Bank"
}
},
"1116-\u5728\u9014\u73fe\u91d1": {
"account_type": "Cash"
},
"1117-\u7d04\u7576\u73fe\u91d1": {
"account_type": "Cash"
},
"1118-\u5176\u4ed6\u73fe\u91d1\u53ca\u7d04\u7576\u73fe\u91d1": {
"account_type": "Cash"
},
"account_type": "Cash"
},
"112-\u77ed\u671f\u6295\u8cc7": {
"1121-\u77ed\u671f\u6295\u8cc7 \u2014\u80a1\u7968": {}
},
"113-\u61c9\u6536\u7968\u64da": {
"1131-\u61c9\u6536\u7968\u64da": {
"account_type": "Receivable"
},
"1132-\u61c9\u6536\u7968\u64da\u8cbc\u73fe ": {
"account_type": "Receivable"
},
"1138-\u5176\u4ed6\u61c9\u6536\u7968\u64da ": {
"account_type": "Receivable"
},
"1139-\u5099\u62b5\u5446\u5e33 \uff0d\u61c9\u6536\u7968\u64da ": {
"account_type": "Receivable"
},
"account_type": "Receivable"
},
"114-\u61c9\u6536\u5e33\u6b3e": {
"1141-\u61c9\u6536\u5e33\u6b3e ": {
"account_type": "Receivable"
},
"1142-\u61c9\u6536\u5206\u671f\u5e33\u6b3e ": {
"account_type": "Receivable"
},
"1149-\u5099\u62b5\u5446\u5e33 \uff0d\u61c9\u6536\u5e33\u6b3e ": {
"account_type": "Receivable"
},
"account_type": "Receivable"
},
"118-\u5176\u4ed6\u61c9\u6536\u6b3e": {
"1184-\u61c9\u6536\u6536\u76ca": {
"account_type": "Receivable"
},
"1185-\u61c9\u6536\u9000\u7a05\u6b3e": {
"account_type": "Receivable"
},
"1189-\u5099\u62b5\u5446\u5e33 \u2014 \u5176\u4ed6\u61c9\u6536\u6b3e ": {
"account_type": "Receivable"
},
"account_type": "Receivable"
},
"121~122-\u5b58\u8ca8": {
"1219-\u5099\u62b5\u5b58\u8ca8\u8dcc\u50f9\u640d\u5931": {},
"1229-\u5099\u62b5\u5b58\u8ca8\u8dcc\u50f9\u640d\u5931": {},
"account_type": "Stock",
"is_group": 1
},
"125-\u9810\u4ed8\u8cbb\u7528": {
"1251-\u9810\u4ed8\u85aa\u8cc7": {},
"1252-\u9810\u4ed8\u79df\u91d1": {},
"1253-\u9810\u4ed8\u4fdd\u96aa\u8cbb": {},
"1254-\u7528\u54c1\u76e4\u5b58": {},
"1255-\u9810\u4ed8\u6240\u5f97\u7a05": {},
"1258-\u5176\u4ed6\u9810\u4ed8\u8cbb\u7528": {}
},
"126-\u9810\u4ed8\u6b3e\u9805": {
"1261-\u9810\u4ed8\u8ca8\u6b3e": {},
"1268-\u5176\u4ed6\u9810\u4ed8\u6b3e\u9805": {}
},
"128~129-\u5176\u4ed6\u6d41\u52d5\u8cc7\u7522": {
"1281-\u9032\u9805\u7a05\u984d": {},
"1282-\u7559\u62b5\u7a05\u984d": {},
"1283-\u66ab\u4ed8\u6b3e": {},
"1284-\u4ee3\u4ed8\u6b3e": {},
"1285-\u54e1\u5de5\u501f\u652f": {}
}
},
"13-\u57fa\u91d1\u53ca\u9577\u671f\u6295\u8cc7": {
"131-\u57fa\u91d1": {
"1311-\u511f\u50b5\u57fa\u91d1": {},
"1313-\u610f\u5916\u640d\u5931\u6e96\u5099\u57fa\u91d1": {},
"1314-\u9000\u4f11\u57fa\u91d1": {},
"1318-\u5176\u4ed6\u57fa\u91d1": {}
},
"132-\u9577\u671f\u6295\u8cc7": {
"1321-\u9577\u671f\u80a1\u6b0a\u6295\u8cc7": {},
"1322-\u9577\u671f\u50b5\u5238\u6295\u8cc7": {},
"1323-\u9577\u671f\u4e0d\u52d5\u7522\u6295\u8cc7": {},
"1328-\u5176\u4ed6\u9577\u671f\u6295\u8cc7": {}
}
},
"14~15-\u56fa\u5b9a\u8cc7\u7522": {
"141-\u571f\u5730": {
"1411-\u571f\u5730": {
"account_type": "Fixed Asset"
},
"account_type": "Fixed Asset"
},
"142-\u571f\u5730\u6539\u826f\u7269": {
"1421-\u571f\u5730\u6539\u826f\u7269": {
"account_type": "Fixed Asset"
},
"account_type": "Fixed Asset"
},
"143-\u623f\u5c4b\u53ca\u5efa\u7269": {
"1431-\u623f\u5c4b\u53ca\u5efa\u7269": {
"account_type": "Fixed Asset"
},
"account_type": "Fixed Asset"
},
"144~146-\u6a5f(\u5668)\u5177\u53ca\u8a2d\u5099": {
"1441-\u6a5f(\u5668)\u5177": {
"account_type": "Fixed Asset"
},
"account_type": "Fixed Asset"
},
"151-\u79df\u8cc3\u8cc7\u7522": {
"1511-\u79df\u8cc3\u8cc7\u7522": {
"account_type": "Fixed Asset"
},
"account_type": "Fixed Asset"
},
"152-\u79df\u8cc3\u6b0a\u76ca\u6539\u826f": {
"1521-\u79df\u8cc3\u6b0a\u76ca\u6539\u826f": {
"account_type": "Fixed Asset"
},
"account_type": "Fixed Asset"
},
"156-\u672a\u5b8c\u5de5\u7a0b\u53ca\u9810\u4ed8\u8cfc\u7f6e\u8a2d\u5099\u6b3e": {
"1561-\u672a\u5b8c\u5de5\u7a0b": {
"account_type": "Fixed Asset"
},
"account_type": "Fixed Asset"
},
"158-\u96dc\u9805\u56fa\u5b9a\u8cc7\u7522": {
"1581-\u96dc\u9805\u56fa\u5b9a\u8cc7\u7522": {
"account_type": "Fixed Asset"
},
"account_type": "Fixed Asset"
},
"account_type": "Fixed Asset"
},
"16-\u905e\u8017\u8cc7\u7522": {
"161-\u905e\u8017\u8cc7\u7522": {
"is_group": 1
}
},
"17-\u7121\u5f62\u8cc7\u7522": {
"171-\u5546\u6a19\u6b0a": {
"1711-\u5546\u6a19\u6b0a": {}
},
"172-\u5c08\u5229\u6b0a": {
"1721-\u5c08\u5229\u6b0a": {}
},
"176-\u5546\u8b7d": {
"1761-\u5546\u8b7d": {}
},
"177-\u958b\u8fa6\u8cbb": {
"1771-\u958b\u8fa6\u8cbb": {}
},
"178-\u5176\u4ed6\u7121\u5f62\u8cc7\u7522": {
"1781-\u905e\u5ef6\u9000\u4f11\u91d1\u6210\u672c": {}
}
},
"18-\u5176\u4ed6\u8cc7\u7522": {
"181-\u905e\u5ef6\u8cc7\u7522": {
"1811-\u50b5\u5238\u767c\u884c\u6210\u672c": {},
"1812-\u9577\u671f\u9810\u4ed8\u79df\u91d1": {},
"1813-\u9577\u671f\u9810\u4ed8\u4fdd\u96aa\u8cbb": {},
"1814-\u905e\u5ef6\u6240\u5f97\u7a05\u8cc7\u7522": {},
"1815-\u9810\u4ed8\u9000\u4f11\u91d1": {},
"1818-\u5176\u4ed6\u905e\u5ef6\u8cc7\u7522": {}
},
"182-\u9592\u7f6e\u8cc7\u7522": {
"1821-\u9592\u7f6e\u8cc7\u7522": {}
},
"184-\u9577\u671f\u61c9\u6536\u7968\u64da\u53ca\u6b3e\u9805\u8207\u50ac\u6536\u5e33\u6b3e": {
"1841-\u9577\u671f\u61c9\u6536\u7968\u64da": {
"account_type": "Receivable"
},
"1842-\u9577\u671f\u61c9\u6536\u5e33\u6b3e": {
"account_type": "Receivable"
},
"1843-\u50ac\u6536\u5e33\u6b3e": {
"account_type": "Receivable"
},
"1848-\u5176\u4ed6\u9577\u671f\u61c9\u6536\u6b3e\u9805": {
"account_type": "Receivable"
},
"1849-\u5099\u62b5\u5446\u5e33\u2014\u9577\u671f\u61c9\u6536\u7968\u64da\u53ca\u6b3e\u9805\u8207\u50ac\u6536\u5e33\u6b3e": {
"account_type": "Receivable"
},
"account_type": "Receivable"
},
"185-\u51fa\u79df\u8cc7\u7522": {
"1851-\u51fa\u79df\u8cc7\u7522": {},
"1858-\u51fa\u79df\u8cc7\u7522 \u2014\u91cd\u4f30\u589e\u503c": {},
"1859-\u7d2f\u7a4d\u6298\u820a \u2014\u51fa\u79df\u8cc7\u7522": {
"account_type": "Accumulated Depreciation"
}
},
"186-\u5b58\u51fa\u4fdd\u8b49\u91d1": {
"1861-\u5b58\u51fa\u4fdd\u8b49\u91d1": {}
},
"188-\u96dc\u9805\u8cc7\u7522": {
"1881-\u53d7\u9650\u5236\u5b58\u6b3e": {},
"1888-\u96dc\u9805\u8cc7\u7522 \u2014\u5176\u4ed6": {}
}
},
"Temporary Accounts": {
"Temporary Opening": {
"account_type": "Temporary"
},
"account_type": "Temporary"
},
"root_type": "Asset"
},
"2-\u8ca0\u50b5": {
"21~22-\u6d41\u52d5\u8ca0\u50b5": {
"211-\u77ed\u671f\u501f\u6b3e": {
"2111-\u9280\u884c\u900f\u652f": {},
"2112-\u9280\u884c\u501f\u6b3e": {}
},
"212-\u61c9\u4ed8\u77ed\u671f\u7968\u5238": {
"2121-\u61c9\u4ed8\u5546\u696d\u672c\u7968": {
"account_type": "Payable"
},
"2122-\u9280\u884c\u627f\u514c\u532f\u7968": {
"account_type": "Payable"
},
"account_type": "Payable"
},
"213-\u61c9\u4ed8\u7968\u64da": {
"2131-\u61c9\u4ed8\u7968\u64da": {
"account_type": "Payable"
},
"account_type": "Payable"
},
"214-\u61c9\u4ed8\u5e33\u6b3e": {
"2141-\u61c9\u4ed8\u5e33\u6b3e": {
"account_type": "Payable"
},
"account_type": "Payable"
},
"216-\u61c9\u4ed8\u6240\u5f97\u7a05": {
"2161-\u61c9\u4ed8\u6240\u5f97\u7a05": {
"account_type": "Tax",
"tax_rate": 5.0
},
"account_type": "Tax",
"tax_rate": 5.0
},
"217-\u61c9\u4ed8\u8cbb\u7528": {
"2171-\u61c9\u4ed8\u85aa\u5de5": {},
"2172-\u61c9\u4ed8\u79df\u91d1": {},
"2173-\u61c9\u4ed8\u5229\u606f": {},
"2174-\u61c9\u4ed8\u71df\u696d\u7a05": {},
"2175-\u61c9\u4ed8\u7a05\u6350 \u2014\u5176\u4ed6": {
"account_type": "Tax",
"tax_rate": 5.0
},
"2178-\u5176\u4ed6\u61c9\u4ed8\u8cbb\u7528": {}
},
"218~219-\u5176\u4ed6\u61c9\u4ed8\u6b3e": {
"2184-\u61c9\u4ed8\u571f\u5730\u623f\u5c4b\u6b3e": {},
"2185-\u61c9\u4ed8\u8a2d\u5099\u6b3e": {},
"2192-\u61c9\u4ed8\u80a1\u5229": {}
},
"226-\u9810\u6536\u6b3e\u9805": {
"2261-\u9810\u6536\u8ca8\u6b3e": {},
"2262-\u9810\u6536\u6536\u5165": {},
"2268-\u5176\u4ed6\u9810\u6536\u6b3e": {}
},
"227-\u4e00\u5e74\u6216\u4e00\u71df\u696d\u9031\u671f\u5167\u5230\u671f\u9577\u671f\u8ca0\u50b5": {
"is_group": 1
},
"228~229-\u5176\u4ed6\u6d41\u52d5\u8ca0\u50b5": {
"2281-\u92b7\u9805\u7a05\u984d": {},
"2283-\u66ab\u6536\u6b3e ": {},
"2284-\u4ee3\u6536\u6b3e": {},
"2285-\u4f30\u8a08\u552e\u5f8c\u670d\u52d9/\u4fdd\u56fa\u8ca0\u50b5": {},
"2291-\u905e\u5ef6\u6240\u5f97\u7a05\u8ca0\u50b5": {},
"2292-\u905e\u5ef6\u514c\u63db\u5229\u76ca": {}
}
},
"23-\u9577\u671f\u8ca0\u50b5": {
"231-\u61c9\u4ed8\u516c\u53f8\u50b5": {
"2311-\u61c9\u4ed8\u516c\u53f8\u50b5": {},
"2319-\u61c9\u4ed8\u516c\u53f8\u50b5\u6ea2(\u6298)\u50f9": {}
},
"232-\u9577\u671f\u501f\u6b3e": {
"2321-\u9577\u671f\u9280\u884c\u501f\u6b3e": {},
"2324-\u9577\u671f\u501f\u6b3e \u2014\u696d\u4e3b": {},
"2325-\u9577\u671f\u501f\u6b3e \u2014\u54e1\u5de5": {},
"2327-\u9577\u671f\u501f\u6b3e \u2014\u95dc\u4fc2\u4eba": {},
"2328-\u9577\u671f\u501f\u6b3e \u2014\u5176\u4ed6": {}
},
"233-\u9577\u671f\u61c9\u4ed8\u7968\u64da\u53ca\u6b3e\u9805": {
"2331-\u9577\u671f\u61c9\u4ed8\u7968\u64da": {
"account_type": "Payable"
},
"2332-\u9577\u671f\u61c9\u4ed8\u5e33\u6b3e": {
"account_type": "Payable"
},
"2333-\u9577\u671f\u61c9\u4ed8\u79df\u8cc3\u8ca0\u50b5": {
"account_type": "Payable"
},
"account_type": "Payable"
},
"234-\u4f30\u8a08\u61c9\u4ed8\u571f\u5730\u589e\u503c\u7a05": {
"2341-\u4f30\u8a08\u61c9\u4ed8\u571f\u5730\u589e\u503c\u7a05": {}
},
"235-\u61c9\u8a08\u9000\u4f11\u91d1\u8ca0\u50b5": {
"2351-\u61c9\u8a08\u9000\u4f11\u91d1\u8ca0\u50b5": {}
},
"238-\u5176\u4ed6\u9577\u671f\u8ca0\u50b5": {
"2388-\u5176\u4ed6\u9577\u671f\u8ca0\u50b5\u2014\u5176\u4ed6": {}
}
},
"28-\u5176\u4ed6\u8ca0\u50b5": {
"281-\u905e\u5ef6\u8ca0\u50b5": {
"2811-\u905e\u5ef6\u6536\u5165": {},
"2814-\u905e\u5ef6\u6240\u5f97\u7a05\u8ca0\u50b5": {},
"2818-\u5176\u4ed6\u905e\u5ef6\u8ca0\u50b5": {}
},
"286-\u5b58\u5165\u4fdd\u8b49\u91d1": {
"2861-\u5b58\u5165\u4fdd\u8b49\u91d1": {}
},
"288-\u96dc\u9805\u8ca0\u50b5": {
"2888-\u96dc\u9805\u8ca0\u50b5 \u2014\u5176\u4ed6": {}
}
},
"Stock Received But Not Billed": {
"account_type": "Stock Received But Not Billed"
},
"root_type": "Liability"
},
"3-\u696d\u4e3b\u6b0a\u76ca": {
"31-\u8cc7\u672c": {
"311-\u8cc7\u672c\uff08\u80a1\u672c\uff09 ": {
"3111-\u666e\u901a\u80a1\u80a1\u672c": {},
"3112-\u7279\u5225\u80a1\u80a1\u672c": {},
"3113-\u9810\u6536\u80a1\u672c": {},
"3114-\u5f85\u5206\u914d\u80a1\u7968\u80a1\u5229": {},
"3115-\u8cc7\u672c": {}
}
},
"32-\u8cc7\u672c\u516c\u7a4d": {
"321-\u80a1\u7968\u6ea2\u50f9": {
"3211-\u666e\u901a\u80a1\u80a1\u7968\u6ea2\u50f9": {},
"3212-\u7279\u5225\u80a1\u80a1\u7968\u6ea2\u50f9": {}
},
"323-\u8cc7\u7522\u91cd\u4f30\u589e\u503c\u6e96\u5099": {
"3231-\u8cc7\u7522\u91cd\u4f30\u589e\u503c\u6e96\u5099": {}
},
"324-\u8655\u5206\u8cc7\u7522\u6ea2\u50f9\u516c\u7a4d": {
"3241-\u8655\u5206\u8cc7\u7522\u6ea2\u50f9\u516c\u7a4d": {}
},
"325-\u5408\u4f75\u516c\u7a4d": {
"3251-\u5408\u4f75\u516c\u7a4d": {}
},
"326-\u53d7\u8d08\u516c\u7a4d": {
"3261-\u53d7\u8d08\u516c\u7a4d": {}
},
"328-\u5176\u4ed6\u8cc7\u672c\u516c\u7a4d": {
"3281-\u6b0a\u76ca\u6cd5\u9577\u671f\u80a1\u6b0a\u6295\u8cc7\u8cc7\u672c\u516c\u7a4d": {},
"3282-\u8cc7\u672c\u516c\u7a4d\u2014 \u5eab\u85cf\u80a1\u7968\u4ea4\u6613": {}
}
},
"33-\u4fdd\u7559\u76c8\u9918(\u7d2f\u7a4d\u8667\u640d)": {
"331-\u6cd5\u5b9a\u76c8\u9918\u516c\u7a4d": {
"3311-\u6cd5\u5b9a\u76c8\u9918\u516c\u7a4d": {}
},
"332-\u7279\u5225\u76c8\u9918\u516c\u7a4d": {
"3321-\u610f\u5916\u640d\u5931\u6e96\u5099": {},
"3322-\u6539\u826f\u64f4\u5145\u6e96\u5099": {},
"3323-\u511f\u50b5\u6e96\u5099": {},
"3328-\u5176\u4ed6\u7279\u5225\u76c8\u9918\u516c\u7a4d": {}
},
"335-\u672a\u5206\u914d\u76c8\u9918(\u7d2f\u7a4d\u8667\u640d) ": {
"is_group": 1
}
},
"34-\u6b0a\u76ca\u8abf\u6574": {
"341-\u9577\u671f\u80a1\u6b0a\u6295\u8cc7\u672a\u5be6\u73fe\u8dcc\u50f9\u640d\u5931": {
"3411-\u9577\u671f\u80a1\u6b0a\u6295\u8cc7\u672a\u5be6\u73fe\u8dcc\u50f9\u640d\u5931": {}
},
"342-\u7d2f\u7a4d\u63db\u7b97\u8abf\u6574\u6578": {
"3421-\u7d2f\u7a4d\u63db\u7b97\u8abf\u6574\u6578": {}
},
"343-\u672a\u8a8d\u5217\u70ba\u9000\u4f11\u91d1\u6210\u672c\u4e4b\u6de8\u640d\u5931": {
"3431-\u672a\u8a8d\u5217\u70ba\u9000\u4f11\u91d1\u6210\u672c\u4e4b\u6de8\u640d\u5931": {}
}
},
"35-\u5eab\u85cf\u80a1": {
"351-\u5eab\u85cf\u80a1": {
"3511-\u5eab\u85cf\u80a1": {}
}
},
"36-\u5c11\u6578\u80a1\u6b0a": {
"361-\u5c11\u6578\u80a1\u6b0a": {
"3611-\u5c11\u6578\u80a1\u6b0a": {}
}
},
"root_type": "Equity"
},
"4-\u71df\u696d\u6536\u5165": {
"41-\u92b7\u8ca8\u6536\u5165": {
"411-\u92b7\u8ca8\u6536\u5165": {
"4111-\u92b7\u8ca8\u6536\u5165": {},
"4112-\u5206\u671f\u4ed8\u6b3e\u92b7\u8ca8\u6536\u5165": {}
},
"417-\u92b7\u8ca8\u9000\u56de": {
"4171-\u92b7\u8ca8\u9000\u56de": {}
},
"419-\u92b7\u8ca8\u6298\u8b93": {
"4191-\u92b7\u8ca8\u6298\u8b93": {}
}
},
"46-\u52de\u52d9\u6536\u5165": {
"461-\u52de\u52d9\u6536\u5165": {
"4611-\u52de\u52d9\u6536\u5165": {}
}
},
"47-\u696d\u52d9\u6536\u5165": {
"471-\u696d\u52d9\u6536\u5165": {
"4711-\u696d\u52d9\u6536\u5165": {}
}
},
"48-\u5176\u4ed6\u71df\u696d\u6536\u5165": {
"488-\u5176\u4ed6\u71df\u696d\u6536\u5165\u2014\u5176\u4ed6": {
"4888-\u5176\u4ed6\u71df\u696d\u6536\u5165\u2014\u5176\u4ed6": {}
}
},
"root_type": "Income"
},
"5-\u71df\u696d\u6210\u672c": {
"51-\u92b7\u8ca8\u6210\u672c": {
"511-\u92b7\u8ca8\u6210\u672c": {
"5111-\u92b7\u8ca8\u6210\u672c": {
"account_type": "Cost of Goods Sold"
},
"5112-\u5206\u671f\u4ed8\u6b3e\u92b7\u8ca8\u6210\u672c": {
"account_type": "Cost of Goods Sold"
},
"account_type": "Cost of Goods Sold"
},
"512-\u9032\u8ca8": {
"5121-\u9032\u8ca8": {},
"5122-\u9032\u8ca8\u8cbb\u7528": {},
"5123-\u9032\u8ca8\u9000\u51fa": {},
"5124-\u9032\u8ca8\u6298\u8b93": {}
},
"513-\u9032\u6599": {
"5131-\u9032\u6599": {},
"5132-\u9032\u6599\u8cbb\u7528": {},
"5133-\u9032\u6599\u9000\u51fa": {},
"5134-\u9032\u6599\u6298\u8b93": {}
},
"514-\u76f4\u63a5\u4eba\u5de5": {
"5141-\u76f4\u63a5\u4eba\u5de5": {}
},
"515~518-\u88fd\u9020\u8cbb\u7528": {
"5151-\u9593\u63a5\u4eba\u5de5": {},
"5152-\u79df\u91d1\u652f\u51fa": {},
"5153-\u6587\u5177\u7528\u54c1": {},
"5154-\u65c5\u8cbb": {},
"5155-\u904b\u8cbb": {},
"5156-\u90f5\u96fb\u8cbb": {},
"5157-\u4fee\u7e55\u8cbb": {},
"5158-\u5305\u88dd\u8cbb": {},
"5161-\u6c34\u96fb\u74e6\u65af\u8cbb": {},
"5162-\u4fdd\u96aa\u8cbb": {},
"5163-\u52a0\u5de5\u8cbb": {},
"5166-\u7a05\u6350": {
"account_type": "Tax",
"tax_rate": 5.0
},
"5168-\u6298\u820a ": {
"account_type": "Depreciation"
},
"5169-\u5404\u9805\u8017\u7aed\u53ca\u6524\u63d0": {},
"5172-\u4f19\u98df\u8cbb": {},
"5173-\u8077\u5de5\u798f\u5229": {},
"5176-\u8a13\u7df4\u8cbb": {},
"5177-\u9593\u63a5\u6750\u6599": {},
"5188-\u5176\u4ed6\u88fd\u9020\u8cbb\u7528": {}
},
"Expenses Included In Valuation": {
"account_type": "Expenses Included In Valuation"
},
"account_type": "Cost of Goods Sold"
},
"56-\u52de\u52d9\u6210\u672c\u88fd": {
"561-\u52de\u52d9\u6210\u672c": {
"5611-\u52de\u52d9\u6210\u672c": {}
}
},
"57-\u696d\u52d9\u6210\u672c": {
"571-\u696d\u52d9\u6210\u672c": {
"5711-\u696d\u52d9\u6210\u672c": {}
}
},
"58-\u5176\u4ed6\u71df\u696d\u6210\u672c": {
"588-\u5176\u4ed6\u71df\u696d\u6210\u672c\u2014\u5176\u4ed6 ": {
"5888-\u5176\u4ed6\u71df\u696d\u6210\u672c\u2014\u5176\u4ed6": {}
}
},
"Stock Adjustment": {
"account_type": "Stock Adjustment"
},
"root_type": "Expense"
},
"6-\u71df\u696d\u8cbb\u7528": {
"61-\u63a8\u92b7\u8cbb\u7528": {
"615~618-\u63a8\u92b7\u8cbb\u7528": {
"6151-\u85aa\u8cc7\u652f\u51fa": {},
"6152-\u79df\u91d1\u652f\u51fa": {},
"6153-\u6587\u5177\u7528\u54c1": {},
"6154-\u65c5\u8cbb": {},
"6155-\u904b\u8cbb": {},
"6156-\u90f5\u96fb\u8cbb": {},
"6157-\u4fee\u7e55\u8cbb": {},
"6159-\u5ee3\u544a\u8cbb": {},
"6161-\u6c34\u96fb\u74e6\u65af\u8cbb": {},
"6162-\u4fdd\u96aa\u8cbb": {},
"6164-\u4ea4\u969b\u8cbb": {},
"6165-\u6350\u8d08": {},
"6166-\u7a05\u6350": {
"account_type": "Tax",
"tax_rate": 5.0
},
"6167-\u5446\u5e33\u640d\u5931": {},
"6168-\u6298\u820a ": {
"account_type": "Depreciation"
},
"6169-\u5404\u9805\u8017\u7aed\u53ca\u6524\u63d0": {},
"6172-\u4f19\u98df\u8cbb": {},
"6173-\u8077\u5de5\u798f\u5229": {},
"6175-\u4f63\u91d1\u652f\u51fa": {},
"6176-\u8a13\u7df4\u8cbb": {},
"6188-\u5176\u4ed6\u63a8\u92b7\u8cbb\u7528": {}
}
},
"62-\u7ba1\u7406\u53ca\u7e3d\u52d9\u8cbb\u7528": {
"625~628-\u7ba1\u7406\u53ca\u7e3d\u52d9\u8cbb\u7528": {
"6251-\u85aa\u8cc7\u652f\u51fa": {},
"6252-\u79df\u91d1\u652f\u51fa": {},
"6253-\u6587\u5177\u7528\u54c1": {},
"6254-\u65c5\u8cbb": {},
"6255-\u904b\u8cbb": {},
"6256-\u90f5\u96fb\u8cbb": {},
"6257-\u4fee\u7e55\u8cbb": {},
"6259-\u5ee3\u544a\u8cbb": {},
"6261-\u6c34\u96fb\u74e6\u65af\u8cbb": {},
"6262-\u4fdd\u96aa\u8cbb": {},
"6264-\u4ea4\u969b\u8cbb": {},
"6265-\u6350\u8d08": {},
"6266-\u7a05\u6350": {
"account_type": "Tax",
"tax_rate": 5.0
},
"6267-\u5446\u5e33\u640d\u5931": {},
"6268-\u6298\u820a": {
"account_type": "Depreciation"
},
"6269-\u5404\u9805\u8017\u7aed\u53ca\u6524\u63d0": {},
"6271-\u5916\u92b7\u640d\u5931": {},
"6272-\u4f19\u98df\u8cbb": {},
"6273-\u8077\u5de5\u798f\u5229": {},
"6274-\u7814\u7a76\u767c\u5c55\u8cbb\u7528": {},
"6275-\u4f63\u91d1\u652f\u51fa": {},
"6276-\u8a13\u7df4\u8cbb": {},
"6278-\u52de\u52d9\u8cbb": {},
"6288-\u5176\u4ed6\u7ba1\u7406\u53ca\u7e3d\u52d9\u8cbb\u7528": {}
}
},
"63-\u7814\u7a76\u767c\u5c55\u8cbb\u7528": {
"635~638-\u7814\u7a76\u767c\u5c55\u8cbb\u7528": {
"6351-\u85aa\u8cc7\u652f\u51fa": {},
"6352-\u79df\u91d1\u652f\u51fa": {},
"6353-\u6587\u5177\u7528\u54c1": {},
"6354-\u65c5\u8cbb": {},
"6355-\u904b\u8cbb": {},
"6356-\u90f5\u96fb\u8cbb": {},
"6357-\u4fee\u7e55\u8cbb": {},
"6361-\u6c34\u96fb\u74e6\u65af\u8cbb": {},
"6362-\u4fdd\u96aa\u8cbb": {},
"6364-\u4ea4\u969b\u8cbb": {},
"6366-\u7a05\u6350": {
"account_type": "Tax",
"tax_rate": 5.0
},
"6368-\u6298\u820a": {
"account_type": "Depreciation"
},
"6369-\u5404\u9805\u8017\u7aed\u53ca\u6524\u63d0": {},
"6372-\u4f19\u98df\u8cbb": {},
"6373-\u8077\u5de5\u798f\u5229": {},
"6376-\u8a13\u7df4\u8cbb": {},
"6378-\u5176\u4ed6\u7814\u7a76\u767c\u5c55\u8cbb\u7528": {}
}
},
"root_type": "Expense"
},
"7-\u71df\u696d\u5916\u6536\u5165\u53ca\u8cbb\u7528": {
"71~74-\u71df\u696d\u5916\u6536\u5165": {
"711-\u5229\u606f\u6536\u5165": {
"7111-\u5229\u606f\u6536\u5165": {}
},
"712-\u6295\u8cc7\u6536\u76ca": {
"7121-\u6b0a\u76ca\u6cd5\u8a8d\u5217\u4e4b\u6295\u8cc7\u6536\u76ca": {},
"7122-\u80a1\u5229\u6536\u5165": {},
"7123-\u77ed\u671f\u6295\u8cc7\u5e02\u50f9\u56de\u5347\u5229\u76ca": {}
},
"713-\u514c\u63db\u5229\u76ca": {
"7131-\u514c\u63db\u5229\u76ca": {}
},
"714-\u8655\u5206\u6295\u8cc7\u6536\u76ca": {
"7141-\u8655\u5206\u6295\u8cc7\u6536\u76ca": {}
},
"715-\u8655\u5206\u8cc7\u7522\u6ea2\u50f9\u6536\u5165": {
"7151-\u8655\u5206\u8cc7\u7522\u6ea2\u50f9\u6536\u5165": {}
},
"748-\u5176\u4ed6\u71df\u696d\u5916\u6536\u5165": {
"7481-\u6350\u8d08\u6536\u5165": {},
"7482-\u79df\u91d1\u6536\u5165": {},
"7483-\u4f63\u91d1\u6536\u5165": {},
"7484-\u51fa\u552e\u4e0b\u8173\u53ca\u5ee2\u6599\u6536\u5165": {},
"7485-\u5b58\u8ca8\u76e4\u76c8": {},
"7486-\u5b58\u8ca8\u8dcc\u50f9\u56de\u5347\u5229\u76ca": {},
"7487-\u58de\u5e33\u8f49\u56de\u5229\u76ca": {},
"7488-\u5176\u4ed6\u71df\u696d\u5916\u6536\u5165\u2014\u5176\u4ed6": {}
}
},
"75~78-\u71df\u696d\u5916\u8cbb\u7528": {
"751-\u5229\u606f\u8cbb\u7528": {
"7511-\u5229\u606f\u8cbb\u7528": {}
},
"752-\u6295\u8cc7\u640d\u5931": {
"7521-\u6b0a\u76ca\u6cd5\u8a8d\u5217\u4e4b\u6295\u8cc7\u640d\u5931": {},
"7523-\u77ed\u671f\u6295\u8cc7\u672a\u5be6\u73fe\u8dcc\u50f9\u640d\u5931": {}
},
"753-\u514c\u63db\u640d\u5931": {
"7531-\u514c\u63db\u640d\u5931": {}
},
"754-\u8655\u5206\u6295\u8cc7\u640d\u5931": {
"7541-\u8655\u5206\u6295\u8cc7\u640d\u5931": {}
},
"755-\u8655\u5206\u8cc7\u7522\u640d\u5931": {
"7551-\u8655\u5206\u8cc7\u7522\u640d\u5931 ": {}
},
"788-\u5176\u4ed6\u71df\u696d\u5916\u8cbb\u7528": {
"7881-\u505c\u5de5\u640d\u5931": {},
"7882-\u707d\u5bb3\u640d\u5931": {},
"7885-\u5b58\u8ca8\u76e4\u640d": {},
"7886-\u5b58\u8ca8\u8dcc\u50f9\u53ca\u5446\u6eef\u640d\u5931": {},
"7888-\u5176\u4ed6\u71df\u696d\u5916\u8cbb\u7528\u2014\u5176\u4ed6": {}
}
},
"root_type": "Income"
},
"8-\u6240\u5f97\u7a05\u8cbb\u7528(\u5229\u76ca)": {
"81-\u6240\u5f97\u7a05\u8cbb\u7528(\u5229\u76ca)": {
"811-\u6240\u5f97\u7a05\u8cbb\u7528(\u5229\u76ca) ": {
"8111-\u6240\u5f97\u7a05\u8cbb\u7528(\u5229\u76ca) ": {}
}
},
"root_type": "Expense"
},
"9-\u975e\u7d93\u5e38\u71df\u696d\u640d\u76ca": {
"91-\u505c\u696d\u90e8\u9580\u640d\u76ca": {
"911-\u505c\u696d\u90e8\u9580\u640d\u76ca\u2014\u505c\u696d\u524d\u71df\u696d\u640d\u76ca": {
"9111-\u505c\u696d\u90e8\u9580\u640d\u76ca\u2014\u505c\u696d\u524d\u71df\u696d\u640d\u76ca": {}
},
"912-\u505c\u696d\u90e8\u9580\u640d\u76ca\u2014\u8655\u5206\u640d\u76ca": {
"9121-\u505c\u696d\u90e8\u9580\u640d\u76ca\u2014\u8655\u5206\u640d\u76ca": {}
}
},
"92-\u975e\u5e38\u640d\u76ca": {
"921-\u975e\u5e38\u640d\u76ca": {
"9211-\u975e\u5e38\u640d\u76ca": {}
}
},
"93-\u6703\u8a08\u539f\u5247\u8b8a\u52d5\u7d2f\u7a4d\u5f71\u97ff\u6578": {
"931-\u6703\u8a08\u539f\u5247\u8b8a\u52d5\u7d2f\u7a4d\u5f71\u97ff\u6578": {
"9311-\u6703\u8a08\u539f\u5247\u8b8a\u52d5\u7d2f\u7a4d\u5f71\u97ff\u6578": {}
}
},
"94-\u5c11\u6578\u80a1\u6b0a\u6de8\u5229": {
"941-\u5c11\u6578\u80a1\u6b0a\u6de8\u5229": {
"9411-\u5c11\u6578\u80a1\u6b0a\u6de8\u5229": {}
}
},
"root_type": "Expense"
}
}
}

View File

@ -0,0 +1,22 @@
QUnit.module('Account');
QUnit.test("test Bank Reconciliation", function(assert) {
assert.expect(0);
let done = assert.async();
frappe.run_serially([
() => frappe.set_route('Form', 'Bank Reconciliation'),
() => cur_frm.set_value('bank_account','Cash - FT'),
() => frappe.click_button('Get Payment Entries'),
() => {
for(var i=0;i<=cur_frm.doc.payment_entries.length-1;i++){
cur_frm.doc.payment_entries[i].clearance_date = frappe.datetime.add_days(frappe.datetime.now_date(), 2);
}
},
() => {cur_frm.refresh_fields('payment_entries');},
() => frappe.click_button('Update Clearance Date'),
() => frappe.timeout(0.5),
() => frappe.click_button('Close'),
() => done()
]);
});

View File

@ -0,0 +1,39 @@
QUnit.module('Journal Entry');
QUnit.test("test journal entry", function(assert) {
assert.expect(2);
let done = assert.async();
frappe.run_serially([
() => {
return frappe.tests.make('Journal Entry', [
{posting_date:frappe.datetime.add_days(frappe.datetime.nowdate(), 0)},
{accounts: [
[
{'account':'Debtors - '+frappe.get_abbr(frappe.defaults.get_default('Company'))},
{'party_type':'Customer'},
{'party':'Test Customer 1'},
{'credit_in_account_currency':1000},
{'is_advance':'Yes'},
],
[
{'account':'HDFC - '+frappe.get_abbr(frappe.defaults.get_default('Company'))},
{'debit_in_account_currency':1000},
]
]},
{cheque_no:1234},
{cheque_date: frappe.datetime.add_days(frappe.datetime.nowdate(), -1)},
{user_remark: 'Test'},
]);
},
() => cur_frm.save(),
() => {
// get_item_details
assert.ok(cur_frm.doc.total_debit==1000, "total debit correct");
assert.ok(cur_frm.doc.total_credit==1000, "total credit correct");
},
() => frappe.tests.click_button('Submit'),
() => frappe.tests.click_button('Yes'),
() => frappe.timeout(0.3),
() => done()
]);
});

View File

@ -499,6 +499,7 @@ frappe.ui.form.on('Payment Entry', {
var c = frm.add_child("references");
c.reference_doctype = d.voucher_type;
c.reference_name = d.voucher_no;
c.due_date = d.due_date
c.total_amount = d.invoice_amount;
c.outstanding_amount = d.outstanding_amount;
if(!in_list(["Sales Order", "Purchase Order", "Expense Claim"], d.voucher_type)) {
@ -568,7 +569,7 @@ frappe.ui.form.on('Payment Entry', {
})
var allocated_negative_outstanding = 0;
if((frm.doc.payment_type=="Receive" && frm.doc.party_type=="Customer") ||
if ((frm.doc.payment_type=="Receive" && frm.doc.party_type=="Customer") ||
(frm.doc.payment_type=="Pay" && frm.doc.party_type=="Supplier") ||
(frm.doc.payment_type=="Pay" && frm.doc.party_type=="Employee")) {
if(total_positive_outstanding_including_order > paid_amount) {
@ -578,7 +579,7 @@ frappe.ui.form.on('Payment Entry', {
}
var allocated_positive_outstanding = paid_amount + allocated_negative_outstanding;
} else {
} else if (in_list(["Customer", "Supplier"], frm.doc.party_type)) {
if(paid_amount > total_negative_outstanding) {
if(total_negative_outstanding == 0) {
frappe.msgprint(__("Cannot {0} {1} {2} without any negative outstanding invoice",

View File

@ -393,7 +393,7 @@ class PaymentEntry(AccountsController):
if self.payment_type=="Receive":
against_account = self.paid_to
else:
against_account = self.paid_from
against_account = self.paid_from
party_gl_dict = self.get_gl_dict({

View File

@ -0,0 +1,29 @@
QUnit.module('Accounts');
QUnit.test("test payment entry", function(assert) {
assert.expect(1);
let done = assert.async();
frappe.run_serially([
() => {
return frappe.tests.make('Payment Entry', [
{payment_type:'Receive'},
{mode_of_payment:'Cash'},
{party_type:'Customer'},
{party:'Test Customer 3'},
{paid_amount:675},
{reference_no:123},
{reference_date: frappe.datetime.add_days(frappe.datetime.nowdate(), 0)},
]);
},
() => cur_frm.save(),
() => {
// get_item_details
assert.ok(cur_frm.doc.total_allocated_amount==675, "Allocated AmountCorrect");
},
() => frappe.tests.click_button('Submit'),
() => frappe.tests.click_button('Yes'),
() => frappe.timeout(0.3),
() => done()
]);
});

View File

@ -151,13 +151,8 @@ def get_items_list(pos_profile):
def get_item_groups(pos_profile):
item_group_dict = {}
if pos_profile.get('item_groups'):
item_groups = []
for d in pos_profile.get('item_groups'):
item_groups.extend(get_child_nodes('Item Group', d.item_group))
else:
item_groups = frappe.db.sql("""Select name,
lft, rgt from `tabItem Group` order by lft""", as_dict=1)
item_groups = frappe.db.sql("""Select name,
lft, rgt from `tabItem Group` order by lft""", as_dict=1)
for data in item_groups:
item_group_dict[data.name] = [data.lft, data.rgt]

View File

@ -325,23 +325,6 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
}
this.frm.refresh_fields();
},
company_address: function() {
var me = this;
if(this.frm.doc.company_address) {
frappe.call({
method: "frappe.contacts.doctype.address.address.get_address_display",
args: {"address_dict": this.frm.doc.company_address },
callback: function(r) {
if(r.message) {
me.frm.set_value("company_address_display", r.message)
}
}
})
} else {
this.frm.set_value("company_address_display", "");
}
}
});

View File

@ -19,6 +19,7 @@ from erpnext.accounts.doctype.asset.depreciation \
import get_disposal_account_and_cost_center, get_gl_entries_on_asset_disposal
from erpnext.stock.doctype.batch.batch import set_batch_nos
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos, get_delivery_note_serial_no
from erpnext.setup.doctype.company.company import update_company_current_month_sales
form_grid_templates = {
"items": "templates/form_grid/item_grid.html"
@ -108,7 +109,7 @@ class SalesInvoice(SellingController):
if not self.recurring_id:
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
self.company, self.base_grand_total, self)
self.company, self.base_grand_total, self)
self.check_prev_docstatus()
@ -140,7 +141,7 @@ class SalesInvoice(SellingController):
self.update_time_sheet(self.name)
frappe.enqueue('erpnext.setup.doctype.company.company.update_company_current_month_sales', company=self.company)
self.update_current_month_sales()
def validate_pos_paid_amount(self):
if len(self.payments) == 0 and self.is_pos:
@ -178,6 +179,15 @@ class SalesInvoice(SellingController):
self.make_gl_entries_on_cancel()
frappe.db.set(self, 'status', 'Cancelled')
self.update_current_month_sales()
def update_current_month_sales(self):
if frappe.flags.in_test:
update_company_current_month_sales(self.company)
else:
frappe.enqueue('erpnext.setup.doctype.company.company.update_company_current_month_sales',
company=self.company)
def update_status_updater_args(self):
if cint(self.update_stock):
self.status_updater.extend([{

View File

@ -1272,6 +1272,17 @@ class TestSalesInvoice(unittest.TestCase):
si.insert()
return si
def test_company_monthly_sales(self):
existing_current_month_sales = frappe.db.get_value("Company", "_Test Company", "total_monthly_sales")
si = create_sales_invoice()
current_month_sales = frappe.db.get_value("Company", "_Test Company", "total_monthly_sales")
self.assertEqual(current_month_sales, existing_current_month_sales + si.base_grand_total)
si.cancel()
current_month_sales = frappe.db.get_value("Company", "_Test Company", "total_monthly_sales")
self.assertEqual(current_month_sales, existing_current_month_sales)
def create_sales_invoice(**args):
si = frappe.new_doc("Sales Invoice")
args = frappe._dict(args)

View File

@ -0,0 +1,43 @@
QUnit.module('Sales Invoice');
QUnit.test("test sales Invoice", function(assert) {
assert.expect(4);
let done = assert.async();
frappe.run_serially([
() => {
return frappe.tests.make('Sales Invoice', [
{customer: 'Test Customer 1'},
{items: [
[
{'qty': 5},
{'item_code': 'Test Product 1'},
]
]},
{update_stock:1},
{customer_address: 'Test1-Billing'},
{shipping_address_name: 'Test1-Shipping'},
{contact_person: 'Contact 1-Test Customer 1'},
{taxes_and_charges: 'TEST In State GST'},
{tc_name: 'Test Term 1'},
{terms: 'This is Test'}
]);
},
() => cur_frm.save(),
() => {
// get_item_details
assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct");
// get tax details
assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST', "Tax details correct");
// get tax account head details
assert.ok(cur_frm.doc.taxes[0].account_head=='CGST - '+frappe.get_abbr(frappe.defaults.get_default('Company')), " Account Head abbr correct");
// grand_total Calculated
assert.ok(cur_frm.doc.grand_total==590, "Grad Total correct");
},
() => frappe.tests.click_button('Submit'),
() => frappe.tests.click_button('Yes'),
() => frappe.timeout(0.3),
() => done()
]);
});

View File

@ -0,0 +1,56 @@
QUnit.module('Sales Invoice');
QUnit.test("test sales Invoice with payment", function(assert) {
assert.expect(4);
let done = assert.async();
frappe.run_serially([
() => {
return frappe.tests.make('Sales Invoice', [
{customer: 'Test Customer 1'},
{items: [
[
{'qty': 5},
{'item_code': 'Test Product 1'},
]
]},
{update_stock:1},
{customer_address: 'Test1-Billing'},
{shipping_address_name: 'Test1-Shipping'},
{contact_person: 'Contact 1-Test Customer 1'},
{taxes_and_charges: 'TEST In State GST'},
{tc_name: 'Test Term 1'},
{terms: 'This is Test'}
]);
},
() => cur_frm.save(),
() => {
// get_item_details
assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct");
// get tax details
assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST', "Tax details correct");
// grand_total Calculated
assert.ok(cur_frm.doc.grand_total==590, "Grad Total correct");
},
() => frappe.tests.click_button('Submit'),
() => frappe.tests.click_button('Yes'),
() => frappe.timeout(2),
() => frappe.tests.click_button('Close'),
() => frappe.tests.click_button('Make'),
() => frappe.tests.click_link('Payment'),
() => frappe.timeout(0.2),
() => { cur_frm.set_value('mode_of_payment','Cash');},
() => { cur_frm.set_value('paid_to','Cash - '+frappe.get_abbr(frappe.defaults.get_default('Company')));},
() => {cur_frm.set_value('reference_no','TEST1234');},
() => {cur_frm.set_value('reference_date',frappe.datetime.add_days(frappe.datetime.nowdate(), 0));},
() => cur_frm.save(),
() => {
// get payment details
assert.ok(cur_frm.doc.paid_amount==590, "Paid Amount Correct");
},
() => frappe.tests.click_button('Submit'),
() => frappe.tests.click_button('Yes'),
() => done()
]);
});

View File

@ -0,0 +1,52 @@
QUnit.module('Sales Invoice');
QUnit.test("test sales Invoice with payment request", function(assert) {
assert.expect(4);
let done = assert.async();
frappe.run_serially([
() => {
return frappe.tests.make('Sales Invoice', [
{customer: 'Test Customer 1'},
{items: [
[
{'qty': 5},
{'item_code': 'Test Product 1'},
]
]},
{update_stock:1},
{customer_address: 'Test1-Billing'},
{shipping_address_name: 'Test1-Shipping'},
{contact_person: 'Contact 1-Test Customer 1'},
{taxes_and_charges: 'TEST In State GST'},
{tc_name: 'Test Term 1'},
{terms: 'This is Test'}
]);
},
() => cur_frm.save(),
() => {
// get_item_details
assert.ok(cur_frm.doc.items[0].item_name=='Test Product 1', "Item name correct");
// get tax details
assert.ok(cur_frm.doc.taxes_and_charges=='TEST In State GST', "Tax details correct");
// grand_total Calculated
assert.ok(cur_frm.doc.grand_total==590, "Grad Total correct");
},
() => frappe.tests.click_button('Submit'),
() => frappe.tests.click_button('Yes'),
() => frappe.timeout(2),
() => frappe.tests.click_button('Close'),
() => frappe.tests.click_button('Make'),
() => frappe.tests.click_link('Payment Request'),
() => frappe.timeout(0.2),
() => { cur_frm.set_value('print_format','GST Tax Invoice');},
() => { cur_frm.set_value('email_to','test@gmail.com');},
() => cur_frm.save(),
() => {
// get payment details
assert.ok(cur_frm.doc.grand_total==590, "grand total Correct");
},
() => done()
]);
});

View File

@ -54,13 +54,15 @@ class ShippingRule(Document):
d.idx = i + 1
def validate_overlapping_shipping_rule_conditions(self):
def overlap_exists_between((x1, x2), (y1, y2)):
def overlap_exists_between(num_range1, num_range2):
"""
(x1, x2) and (y1, y2) are two ranges
if condition x = 100 to 300
then condition y can only be like 50 to 99 or 301 to 400
num_range1 and num_range2 are two ranges
ranges are represented as a tuple e.g. range 100 to 300 is represented as (100, 300)
if condition num_range1 = 100 to 300
then condition num_range2 can only be like 50 to 99 or 301 to 400
hence, non-overlapping condition = (x1 <= x2 < y1 <= y2) or (y1 <= y2 < x1 <= x2)
"""
(x1, x2), (y1, y2) = num_range1, num_range2
separate = (x1 <= x2 <= y1 <= y2) or (y1 <= y2 <= x1 <= x2)
return (not separate)

View File

@ -8,6 +8,7 @@ from frappe import _
from frappe.model.document import Document
from frappe.utils import cstr, cint
from frappe.contacts.doctype.address.address import get_default_address
from erpnext.setup.doctype.customer_group.customer_group import get_parent_customer_groups
class IncorrectCustomerGroup(frappe.ValidationError): pass
class IncorrectSupplierType(frappe.ValidationError): pass
@ -134,6 +135,9 @@ def get_tax_template(posting_date, args):
for key, value in args.iteritems():
if key=="use_for_shopping_cart":
conditions.append("use_for_shopping_cart = {0}".format(1 if value else 0))
if key == 'customer_group' and value:
customer_group_condition = get_customer_group_condition(value)
conditions.append("ifnull({0}, '') in ('', {1})".format(key, customer_group_condition))
else:
conditions.append("ifnull({0}, '') in ('', '{1}')".format(key, frappe.db.escape(cstr(value))))
@ -157,3 +161,10 @@ def get_tax_template(posting_date, args):
return None
return tax_template
def get_customer_group_condition(customer_group):
condition = ""
customer_groups = ["'%s'"%(frappe.db.escape(d.name)) for d in get_parent_customer_groups(customer_group)]
if customer_groups:
condition = ",".join(['%s'] * len(customer_groups))%(tuple(customer_groups))
return condition

View File

@ -34,6 +34,14 @@ class TestTaxRule(unittest.TestCase):
tax_rule2.save()
self.assertTrue(tax_rule2.name)
def test_for_parent_customer_group(self):
tax_rule1 = make_tax_rule(customer_group= "All Customer Groups",
sales_tax_template = "_Test Sales Taxes and Charges Template", priority = 1, from_date = "2015-01-01")
tax_rule1.save()
self.assertEquals(get_tax_template("2015-01-01", {"customer_group" : "Commercial"}),
"_Test Sales Taxes and Charges Template")
def test_conflict_with_overlapping_dates(self):
tax_rule1 = make_tax_rule(customer= "_Test Customer",
sales_tax_template = "_Test Sales Taxes and Charges Template", priority = 1, from_date = "2015-01-01", to_date = "2015-01-05")

View File

@ -6,11 +6,11 @@ QUnit.test("test:POS Profile", function(assert) {
() => {
return frappe.tests.make("POS Profile", [
{naming_series: "SINV"},
{company: "_Test Company"},
{company: "Test Company"},
{country: "India"},
{currency: "INR"},
{write_off_account: "Write Off - _TC"},
{write_off_cost_center: "Main - _TC"},
{write_off_account: "Write Off - TC"},
{write_off_cost_center: "Main - TC"},
{payments: [
[
{"default": 1},
@ -35,15 +35,16 @@ QUnit.test("test:Sales Invoice", function(assert) {
frappe.run_serially([
() => {
return frappe.tests.make("Sales Invoice", [
{customer: "_Test Customer 2"},
{company: "_Test Company"},
{customer: "Test Customer 2"},
{company: "Test Company"},
{is_pos: 1},
{posting_date: frappe.datetime.get_today()},
{due_date: frappe.datetime.get_today()},
{items: [
[
{"item_code": "_Test Item"},
{"qty": 5}
{"item_code": "Test Product 1"},
{"qty": 5},
{"warehouse":'Stores - TC'}
]]
}
]);

View File

@ -79,7 +79,7 @@ def set_address_details(out, party, party_type, doctype=None, company=None):
out.shipping_address = get_address_display(out["shipping_address_name"])
out.update(get_fetch_values(doctype, 'shipping_address_name', out.shipping_address_name))
if doctype and doctype in ['Sales Invoice']:
if doctype and doctype in ['Delivery Note', 'Sales Invoice']:
out.update(get_company_address(company))
if out.company_address:
out.update(get_fetch_values(doctype, 'company_address', out.company_address))

View File

@ -10,15 +10,15 @@
<thead>
<tr>
{% if(report.report_name === "Accounts Receivable" || report.report_name === "Accounts Payable") { %}
<th style="width: 15%">{%= __("Date") %}</th>
<th style="width: 15%">{%= __("Ref") %}</th>
<th style="width: 40%">{%= __("Party") %}</th>
<th style="width: 15%">{%= __("Invoiced Amount") %}</th>
<th style="width: 15%">{%= __("Paid Amount") %}</th>
<th style="width: 15%">{%= report.report_name === "Accounts Receivable" ? __('Credit Note') : __('Debit Note') %}</th>
<th style="width: 15%">{%= __("Outstanding Amount") %}</th>
<th style="width: 14%">{%= __("Date") %}</th>
<th style="width: 16%">{%= __("Ref") %}</th>
<th style="width: 30%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
<th style="width: 10%">{%= __("Invoiced Amount") %}</th>
<th style="width: 10%">{%= __("Paid Amount") %}</th>
<th style="width: 10%">{%= report.report_name === "Accounts Receivable" ? __('Credit Note') : __('Debit Note') %}</th>
<th style="width: 10%">{%= __("Outstanding Amount") %}</th>
{% } else { %}
<th style="width: 40%">{%= __("Party") %}</th>
<th style="width: 40%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
<th style="width: 15%">{%= __("Total Invoiced Amount") %}</th>
<th style="width: 15%">{%= __("Total Paid Amount") %}</th>
<th style="width: 15%">{%= report.report_name === "Accounts Receivable Summary" ? __('Credit Note Amount') : __('Debit Note Amount') %}</th>
@ -34,8 +34,12 @@
<td>{%= dateutil.str_to_user(data[i][__("Posting Date")]) %}</td>
<td>{%= data[i][__("Voucher Type")] %}
<br>{%= data[i][__("Voucher No")] %}</td>
<td>{%= data[i][__("Customer Name")] || data[i][__("Customer")] || data[i][__("Supplier Name")] || data[i][__("Supplier")] %}
<br>{%= __("Remarks") %}: {%= data[i][__("Remarks")] %}</td>
<td>
{% if(!(filters.customer || filters.supplier)) { %}
{%= data[i][__("Customer Name")] || data[i][__("Customer")] || data[i][__("Supplier Name")] || data[i][__("Supplier")] %}<br>{%= __("Remarks") %}:
{% } %}
{%= data[i][__("Remarks")] %}
</td>
<td style="text-align: right">
{%= format_currency(data[i]["Invoiced Amount"], data[i]["currency"]) %}</td>
<td style="text-align: right">
@ -59,8 +63,13 @@
{% } else { %}
{% if(data[i][__("Customer")] || data[i][__("Supplier")]|| "&nbsp;") { %}
{% if((data[i][__("Customer")] || data[i][__("Supplier")]) != __("'Total'")) { %}
<td>{%= data[i][__("Customer")] || data[i][__("Supplier")] %}
<br>{%= __("Remarks") %}: {%= data[i][__("Remarks")] %}</td>
<td>
{% if(!(filters.customer || filters.supplier)) { %}
{%= data[i][__("Customer")] || data[i][__("Supplier")] %}
<br>{%= __("Remarks") %}:
{% } %}
{%= data[i][__("Remarks")] %}
</td>
{% } else { %}
<td><b>{%= __("Total") %}</b></td>
{% } %}

View File

@ -7,7 +7,7 @@ frappe.query_reports["Item-wise Purchase Register"] = {
"fieldname":"from_date",
"label": __("From Date"),
"fieldtype": "Date",
"default": frappe.defaults.get_user_default("year_start_date"),
"default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
"width": "80"
},
{

View File

@ -2,9 +2,10 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
import frappe, erpnext
from frappe import _
from frappe.utils import flt
from erpnext.accounts.report.item_wise_sales_register.item_wise_sales_register import get_tax_accounts
def execute(filters=None):
return _execute(filters)
@ -12,12 +13,14 @@ def execute(filters=None):
def _execute(filters=None, additional_table_columns=None, additional_query_columns=None):
if not filters: filters = {}
columns = get_columns(additional_table_columns)
last_col = len(columns)
company_currency = erpnext.get_company_currency(filters.company)
item_list = get_items(filters, additional_query_columns)
aii_account_map = get_aii_accounts()
if item_list:
item_row_tax, tax_accounts = get_tax_accounts(item_list, columns)
itemised_tax, tax_columns = get_tax_accounts(item_list, columns, company_currency,
doctype="Purchase Invoice", tax_doctype="Purchase Taxes and Charges")
columns.append({
"fieldname": "currency",
@ -25,7 +28,8 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
"fieldtype": "Data",
"width": 80
})
company_currency = frappe.db.get_value("Company", filters.company, "default_currency")
po_pr_map = get_purchase_receipts_against_purchase_order(item_list)
data = []
for d in item_list:
@ -33,8 +37,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
if d.purchase_receipt:
purchase_receipt = d.purchase_receipt
elif d.po_detail:
purchase_receipt = ", ".join(frappe.db.sql_list("""select distinct parent
from `tabPurchase Receipt Item` where docstatus=1 and purchase_order_item=%s""", d.po_detail))
purchase_receipt = ", ".join(po_pr_map.get(d.po_detail, []))
expense_account = d.expense_account or aii_account_map.get(d.company)
row = [d.item_code, d.item_name, d.item_group, d.parent, d.posting_date, d.supplier,
@ -46,13 +49,15 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
row += [
d.credit_to, d.mode_of_payment, d.project, d.company, d.purchase_order,
purchase_receipt, expense_account, d.qty, d.base_net_rate, d.base_net_amount
purchase_receipt, expense_account, d.qty, d.stock_uom, d.base_net_rate, d.base_net_amount
]
for tax in tax_accounts:
row.append(item_row_tax.get(d.name, {}).get(tax, 0))
total_tax = 0
for tax in tax_columns:
item_tax = itemised_tax.get(d.name, {}).get(tax, {})
row += [item_tax.get("tax_rate", 0), item_tax.get("tax_amount", 0)]
total_tax += flt(item_tax.get("tax_amount"))
total_tax = sum(row[last_col:])
row += [total_tax, d.base_net_amount + total_tax, company_currency]
data.append(row)
@ -76,7 +81,8 @@ def get_columns(additional_table_columns):
_("Mode of Payment") + ":Link/Mode of Payment:80", _("Project") + ":Link/Project:80",
_("Company") + ":Link/Company:100", _("Purchase Order") + ":Link/Purchase Order:100",
_("Purchase Receipt") + ":Link/Purchase Receipt:100", _("Expense Account") + ":Link/Account:140",
_("Qty") + ":Float:120", _("Rate") + ":Currency/currency:120", _("Amount") + ":Currency/currency:120"
_("Qty") + ":Float:120", _("Stock UOM") + "::100",
_("Rate") + ":Currency/currency:120", _("Amount") + ":Currency/currency:120"
]
return columns
@ -106,8 +112,9 @@ def get_items(filters, additional_query_columns):
pi_item.name, pi_item.parent, pi.posting_date, pi.credit_to, pi.company,
pi.supplier, pi.remarks, pi.base_net_total, pi_item.item_code, pi_item.item_name,
pi_item.item_group, pi_item.project, pi_item.purchase_order, pi_item.purchase_receipt,
pi_item.po_detail, pi_item.expense_account, pi_item.qty, pi_item.base_net_rate,
pi_item.base_net_amount, pi.supplier_name, pi.mode_of_payment {0}
pi_item.po_detail, pi_item.expense_account, pi_item.qty, pi_item.stock_uom,
pi_item.base_net_rate, pi_item.base_net_amount,
pi.supplier_name, pi.mode_of_payment {0}
from `tabPurchase Invoice` pi, `tabPurchase Invoice Item` pi_item
where pi.name = pi_item.parent and pi.docstatus = 1 %s %s
order by pi.posting_date desc, pi_item.item_code desc
@ -116,53 +123,19 @@ def get_items(filters, additional_query_columns):
def get_aii_accounts():
return dict(frappe.db.sql("select name, stock_received_but_not_billed from tabCompany"))
def get_tax_accounts(item_list, columns):
import json
item_row_tax = {}
tax_accounts = []
invoice_item_row = {}
item_row_map = {}
for d in item_list:
invoice_item_row.setdefault(d.parent, []).append(d)
item_row_map.setdefault(d.parent, {}).setdefault(d.item_code, []).append(d)
def get_purchase_receipts_against_purchase_order(item_list):
po_pr_map = frappe._dict()
po_item_rows = list(set([d.po_detail for d in item_list]))
tax_details = frappe.db.sql("""
select
parent, account_head, item_wise_tax_detail, charge_type, base_tax_amount_after_discount_amount
from `tabPurchase Taxes and Charges`
where parenttype = 'Purchase Invoice' and docstatus = 1
and (account_head is not null and account_head != '')
and category in ('Total', 'Valuation and Total')
and parent in (%s)
""" % ', '.join(['%s']*len(invoice_item_row)), tuple(invoice_item_row.keys()))
if po_item_rows:
purchase_receipts = frappe.db.sql("""
select parent, purchase_order_item
from `tabPurchase Receipt Item`
where docstatus=1 and purchase_order_item in (%s)
group by purchase_order_item, parent
""" % (', '.join(['%s']*len(po_item_rows))), tuple(po_item_rows), as_dict=1)
for parent, account_head, item_wise_tax_detail, charge_type, tax_amount in tax_details:
if account_head not in tax_accounts:
tax_accounts.append(account_head)
for pr in purchase_receipts:
po_pr_map.setdefault(pr.po_detail, []).append(pr.parent)
if item_wise_tax_detail:
try:
item_wise_tax_detail = json.loads(item_wise_tax_detail)
for item_code, tax_amount in item_wise_tax_detail.items():
tax_amount = flt(tax_amount[1]) if isinstance(tax_amount, list) else flt(tax_amount)
item_net_amount = sum([flt(d.base_net_amount)
for d in item_row_map.get(parent, {}).get(item_code, [])])
for d in item_row_map.get(parent, {}).get(item_code, []):
item_tax_amount = flt((tax_amount * d.base_net_amount) / item_net_amount) if item_net_amount else 0
item_row_tax.setdefault(d.name, {})[account_head] = item_tax_amount
except ValueError:
continue
elif charge_type == "Actual" and tax_amount:
for d in invoice_item_row.get(parent, []):
item_row_tax.setdefault(d.name, {})[account_head] = \
flt((tax_amount * d.base_net_amount) / d.base_net_total)
tax_accounts.sort()
columns += [account_head + ":Currency/currency:80" for account_head in tax_accounts]
columns += ["Total Tax:Currency/currency:80", "Total:Currency/currency:80"]
return item_row_tax, tax_accounts
return po_pr_map

View File

@ -7,7 +7,7 @@ frappe.query_reports["Item-wise Sales Register"] = frappe.query_reports["Sales R
"fieldname":"from_date",
"label": __("From Date"),
"fieldtype": "Date",
"default": frappe.defaults.get_default("year_start_date"),
"default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
"width": "80"
},
{

View File

@ -2,9 +2,10 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
import frappe, erpnext
from frappe import _
from frappe.utils import flt
from frappe.model.meta import get_field_precision
from erpnext.accounts.report.sales_register.sales_register import get_mode_of_payments
def execute(filters=None):
@ -13,19 +14,20 @@ def execute(filters=None):
def _execute(filters=None, additional_table_columns=None, additional_query_columns=None):
if not filters: filters = {}
columns = get_columns(additional_table_columns)
last_col = len(columns)
company_currency = erpnext.get_company_currency(filters.company)
item_list = get_items(filters, additional_query_columns)
if item_list:
item_row_tax, tax_accounts = get_tax_accounts(item_list, columns)
itemised_tax, tax_columns = get_tax_accounts(item_list, columns, company_currency)
columns.append({
"fieldname": "currency",
"label": _("Currency"),
"fieldtype": "Data",
"width": 80
})
company_currency = frappe.db.get_value("Company", filters.get("company"), "default_currency")
mode_of_payments = get_mode_of_payments(set([d.parent for d in item_list]))
so_dn_map = get_delivery_notes_against_sales_order(item_list)
data = []
for d in item_list:
@ -33,8 +35,8 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
if d.delivery_note:
delivery_note = d.delivery_note
elif d.so_detail:
delivery_note = ", ".join(frappe.db.sql_list("""select distinct parent
from `tabDelivery Note Item` where docstatus=1 and so_detail=%s""", d.so_detail))
delivery_note = ", ".join(so_dn_map.get(d.so_detail, []))
if not delivery_note and d.update_stock:
delivery_note = d.parent
@ -47,13 +49,16 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
row += [
d.customer_group, d.debit_to, ", ".join(mode_of_payments.get(d.parent, [])),
d.territory, d.project, d.company, d.sales_order,
delivery_note, d.income_account, d.cost_center, d.qty, d.base_net_rate, d.base_net_amount
delivery_note, d.income_account, d.cost_center, d.qty, d.stock_uom,
d.base_net_rate, d.base_net_amount
]
for tax in tax_accounts:
row.append(item_row_tax.get(d.name, {}).get(tax, 0))
total_tax = 0
for tax in tax_columns:
item_tax = itemised_tax.get(d.name, {}).get(tax, {})
row += [item_tax.get("tax_rate", 0), item_tax.get("tax_amount", 0)]
total_tax += flt(item_tax.get("tax_amount"))
total_tax = sum(row[last_col:])
row += [total_tax, d.base_net_amount + total_tax, company_currency]
data.append(row)
@ -77,7 +82,7 @@ def get_columns(additional_table_columns):
_("Project") + ":Link/Project:80", _("Company") + ":Link/Company:100",
_("Sales Order") + ":Link/Sales Order:100", _("Delivery Note") + ":Link/Delivery Note:100",
_("Income Account") + ":Link/Account:140", _("Cost Center") + ":Link/Cost Center:140",
_("Qty") + ":Float:120",
_("Qty") + ":Float:120", _("Stock UOM") + "::100",
_("Rate") + ":Currency/currency:120",
_("Amount") + ":Currency/currency:120"
]
@ -112,62 +117,111 @@ def get_items(filters, additional_query_columns):
si_item.name, si_item.parent, si.posting_date, si.debit_to, si.project,
si.customer, si.remarks, si.territory, si.company, si.base_net_total,
si_item.item_code, si_item.item_name, si_item.item_group, si_item.sales_order,
si_item.delivery_note, si_item.income_account, si_item.cost_center, si_item.qty,
si_item.base_net_rate, si_item.base_net_amount, si.customer_name,
si.customer_group, si_item.so_detail, si.update_stock {0}
si_item.delivery_note, si_item.income_account, si_item.cost_center,
si_item.qty, si_item.stock_uom, si_item.base_net_rate, si_item.base_net_amount,
si.customer_name, si.customer_group, si_item.so_detail, si.update_stock {0}
from `tabSales Invoice` si, `tabSales Invoice Item` si_item
where si.name = si_item.parent and si.docstatus = 1 %s
order by si.posting_date desc, si_item.item_code desc
""".format(additional_query_columns or '') % conditions, filters, as_dict=1)
def get_tax_accounts(item_list, columns):
def get_delivery_notes_against_sales_order(item_list):
so_dn_map = frappe._dict()
so_item_rows = list(set([d.so_detail for d in item_list]))
if so_item_rows:
delivery_notes = frappe.db.sql("""
select parent, so_detail
from `tabDelivery Note Item`
where docstatus=1 and so_detail in (%s)
group by so_detail, parent
""" % (', '.join(['%s']*len(so_item_rows))), tuple(so_item_rows), as_dict=1)
for dn in delivery_notes:
so_dn_map.setdefault(dn.so_detail, []).append(dn.parent)
return so_dn_map
def get_tax_accounts(item_list, columns, company_currency,
doctype="Sales Invoice", tax_doctype="Sales Taxes and Charges"):
import json
item_row_tax = {}
tax_accounts = []
invoice_item_row = {}
item_row_map = {}
tax_columns = []
invoice_item_row = {}
itemised_tax = {}
tax_amount_precision = get_field_precision(frappe.get_meta(tax_doctype).get_field("tax_amount"),
currency=company_currency) or 2
for d in item_list:
invoice_item_row.setdefault(d.parent, []).append(d)
item_row_map.setdefault(d.parent, {}).setdefault(d.item_code, []).append(d)
conditions = ""
if doctype == "Purchase Invoice":
conditions = " and category in ('Total', 'Valuation and Total')"
tax_details = frappe.db.sql("""
select
parent, account_head, item_wise_tax_detail,
parent, description, item_wise_tax_detail,
charge_type, base_tax_amount_after_discount_amount
from `tabSales Taxes and Charges`
from `tab%s`
where
parenttype = 'Sales Invoice' and docstatus = 1
and (account_head is not null and account_head != '')
parenttype = %s and docstatus = 1
and (description is not null and description != '')
and parent in (%s)
""" % ', '.join(['%s']*len(invoice_item_row)), tuple(invoice_item_row.keys()))
%s
order by description
""" % (tax_doctype, '%s', ', '.join(['%s']*len(invoice_item_row)), conditions),
tuple([doctype] + invoice_item_row.keys()))
for parent, account_head, item_wise_tax_detail, charge_type, tax_amount in tax_details:
if account_head not in tax_accounts:
tax_accounts.append(account_head)
for parent, description, item_wise_tax_detail, charge_type, tax_amount in tax_details:
if description not in tax_columns and tax_amount:
tax_columns.append(description)
if item_wise_tax_detail:
try:
item_wise_tax_detail = json.loads(item_wise_tax_detail)
for item_code, tax_amount in item_wise_tax_detail.items():
tax_amount = flt(tax_amount[1]) if isinstance(tax_amount, list) else flt(tax_amount)
for item_code, tax_data in item_wise_tax_detail.items():
itemised_tax.setdefault(item_code, frappe._dict())
if isinstance(tax_data, list):
tax_rate, tax_amount = tax_data
else:
tax_rate = tax_data
tax_amount = 0
if charge_type == "Actual" and not tax_rate:
tax_rate = "NA"
item_net_amount = sum([flt(d.base_net_amount)
for d in item_row_map.get(parent, {}).get(item_code, [])])
for d in item_row_map.get(parent, {}).get(item_code, []):
item_tax_amount = flt((tax_amount * d.base_net_amount) / item_net_amount) if item_net_amount else 0
item_row_tax.setdefault(d.name, {})[account_head] = item_tax_amount
item_tax_amount = flt((tax_amount * d.base_net_amount) / item_net_amount) \
if item_net_amount else 0
if item_tax_amount:
itemised_tax.setdefault(d.name, {})[description] = frappe._dict({
"tax_rate": tax_rate,
"tax_amount": flt(item_tax_amount, tax_amount_precision)
})
except ValueError:
continue
elif charge_type == "Actual" and tax_amount:
for d in invoice_item_row.get(parent, []):
item_row_tax.setdefault(d.name, {})[account_head] = \
flt((tax_amount * d.base_net_amount) / d.base_net_total)
itemised_tax.setdefault(d.name, {})[description] = frappe._dict({
"tax_rate": "NA",
"tax_amount": flt((tax_amount * d.base_net_amount) / d.base_net_total,
tax_amount_precision)
})
tax_accounts.sort()
columns += [account_head + ":Currency/currency:80" for account_head in tax_accounts]
columns += ["Total Tax:Currency/currency:80", "Total:Currency/currency:80"]
tax_columns.sort()
for desc in tax_columns:
columns.append(desc + " Rate:Data:80")
columns.append(desc + " Amount:Currency/currency:100")
return item_row_tax, tax_accounts
columns += ["Total Tax:Currency/currency:80", "Total:Currency/currency:100"]
return itemised_tax, tax_columns

View File

@ -7,7 +7,7 @@ frappe.query_reports["Purchase Register"] = {
"fieldname":"from_date",
"label": __("From Date"),
"fieldtype": "Date",
"default": frappe.defaults.get_user_default("year_start_date"),
"default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
"width": "80"
},
{

View File

@ -7,7 +7,7 @@ frappe.query_reports["Sales Register"] = {
"fieldname":"from_date",
"label": __("From Date"),
"fieldtype": "Date",
"default": frappe.defaults.get_default("year_start_date"),
"default": frappe.datetime.add_months(frappe.datetime.get_today(), -1),
"width": "80"
},
{

View File

@ -580,9 +580,15 @@ def get_outstanding_invoices(party_type, party, account, condition=None):
dr_or_cr = "credit_in_account_currency - debit_in_account_currency"
payment_dr_or_cr = "payment_gl_entry.debit_in_account_currency - payment_gl_entry.credit_in_account_currency"
invoice = 'Sales Invoice' if party_type == 'Customer' else 'Purchase Invoice'
invoice_list = frappe.db.sql("""
select
voucher_no, voucher_type, posting_date, ifnull(sum({dr_or_cr}), 0) as invoice_amount,
(
case when (voucher_type = 'Sales Invoice' or voucher_type = 'Purchase Invoice')
then (select due_date from `tab{invoice}` where name = voucher_no)
else posting_date end
) as due_date,
(
select ifnull(sum({payment_dr_or_cr}), 0)
from `tabGL Entry` payment_gl_entry
@ -606,6 +612,7 @@ def get_outstanding_invoices(party_type, party, account, condition=None):
having (invoice_amount - payment_amount) > 0.005
order by posting_date, name""".format(
dr_or_cr = dr_or_cr,
invoice = invoice,
payment_dr_or_cr = payment_dr_or_cr,
condition = condition or ""
), {
@ -618,6 +625,7 @@ def get_outstanding_invoices(party_type, party, account, condition=None):
outstanding_invoices.append(frappe._dict({
'voucher_no': d.voucher_no,
'voucher_type': d.voucher_type,
'due_date': d.due_date,
'posting_date': d.posting_date,
'invoice_amount': flt(d.invoice_amount),
'payment_amount': flt(d.payment_amount),

View File

@ -8,10 +8,8 @@ QUnit.test("test: purchase order", function(assert) {
() => {
return frappe.tests.make('Purchase Order', [
{supplier: 'Test Supplier'},
{company: 'Wind Power LLC'},
{is_subcontracted: 'No'},
{buying_price_list: 'Test-Buying-USD'},
{currency: 'USD'},
{currency: 'INR'},
{items: [
[
{"item_code": 'Test Product 4'},
@ -20,7 +18,7 @@ QUnit.test("test: purchase order", function(assert) {
{"qty": 5},
{"uom": 'Unit'},
{"rate": 100},
{"warehouse": 'Stores - WP'}
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
]
]},

View File

@ -8,7 +8,6 @@ QUnit.test("test: purchase order with get items", function(assert) {
() => {
return frappe.tests.make('Purchase Order', [
{supplier: 'Test Supplier'},
{company: 'Wind Power LLC'},
{is_subcontracted: 'No'},
{buying_price_list: 'Test-Buying-USD'},
{currency: 'USD'},
@ -18,7 +17,7 @@ QUnit.test("test: purchase order with get items", function(assert) {
{"qty": 5},
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
{"warehouse": 'Stores - WP'}
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
]
]}
]);
@ -46,9 +45,9 @@ QUnit.test("test: purchase order with get items", function(assert) {
assert.ok(cur_frm.doc.items[3].item_name == 'Keyboard', "Product bundle item 3 correct");
},
() => cur_frm.doc.items[1].warehouse = 'Stores - WP',
() => cur_frm.doc.items[2].warehouse = 'Stores - WP',
() => cur_frm.doc.items[3].warehouse = 'Stores - WP',
() => cur_frm.doc.items[1].warehouse = 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company")),
() => cur_frm.doc.items[2].warehouse = 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company")),
() => cur_frm.doc.items[3].warehouse = 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company")),
() => cur_frm.save(),
() => frappe.timeout(1),

View File

@ -8,7 +8,6 @@ QUnit.test("test: purchase order receipt", function(assert) {
() => {
return frappe.tests.make('Purchase Order', [
{supplier: 'Test Supplier'},
{company: 'Wind Power LLC'},
{is_subcontracted: 'No'},
{buying_price_list: 'Test-Buying-USD'},
{currency: 'USD'},
@ -20,7 +19,7 @@ QUnit.test("test: purchase order receipt", function(assert) {
{"qty": 5},
{"uom": 'Unit'},
{"rate": 100},
{"warehouse": 'Stores - WP'}
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
]
]},
]);
@ -68,10 +67,8 @@ QUnit.test("test: purchase order receipt", function(assert) {
() => frappe.timeout(2),
() => {
assert.ok($('div.slick-cell.l2.r2 > a').text().includes('Test Product 1')
&& $('div.slick-cell.l9.r9 > div').text().includes(5)
&& $('div.slick-cell.l12.r12 > div').text().includes(100), "Stock ledger entry correct");
&& $('div.slick-cell.l9.r9 > div').text().includes(5), "Stock ledger entry correct");
},
() => done()
]);
});
});

View File

@ -8,7 +8,6 @@ QUnit.test("test: purchase order with discount on grand total", function(assert)
() => {
return frappe.tests.make('Purchase Order', [
{supplier: 'Test Supplier'},
{company: 'Wind Power LLC'},
{is_subcontracted: 'No'},
{buying_price_list: 'Test-Buying-EUR'},
{currency: 'EUR'},
@ -20,7 +19,7 @@ QUnit.test("test: purchase order with discount on grand total", function(assert)
{"rate": 500 },
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
{"warehouse": 'Stores - WP'}
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
]
]},
{apply_discount_on: 'Grand Total'},

View File

@ -8,7 +8,6 @@ QUnit.test("test: purchase order with item wise discount", function(assert) {
() => {
return frappe.tests.make('Purchase Order', [
{supplier: 'Test Supplier'},
{company: 'Wind Power LLC'},
{is_subcontracted: 'No'},
{buying_price_list: 'Test-Buying-EUR'},
{currency: 'EUR'},
@ -19,7 +18,7 @@ QUnit.test("test: purchase order with item wise discount", function(assert) {
{"uom": 'Unit'},
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
{"warehouse": 'Stores - WP'},
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))},
{"discount_percentage": 20}
]
]}

View File

@ -8,7 +8,6 @@ QUnit.test("test: purchase order with multi UOM", function(assert) {
() => {
return frappe.tests.make('Purchase Order', [
{supplier: 'Test Supplier'},
{company: 'Wind Power LLC'},
{is_subcontracted: 'No'},
{items: [
[
@ -18,7 +17,7 @@ QUnit.test("test: purchase order with multi UOM", function(assert) {
{"rate": 100},
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
{"warehouse": 'Stores - WP'}
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
]
]}
]);

View File

@ -8,7 +8,6 @@ QUnit.test("test: purchase order with taxes and charges", function(assert) {
() => {
return frappe.tests.make('Purchase Order', [
{supplier: 'Test Supplier'},
{company: 'Wind Power LLC'},
{is_subcontracted: 'No'},
{buying_price_list: 'Test-Buying-USD'},
{currency: 'USD'},
@ -20,7 +19,7 @@ QUnit.test("test: purchase order with taxes and charges", function(assert) {
{"rate": 500 },
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
{"warehouse": 'Stores - WP'}
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
]
]},

View File

@ -195,7 +195,12 @@ def check_portal_enabled(reference_doctype):
def get_list_context(context=None):
from erpnext.controllers.website_list_for_contact import get_list_context
list_context = get_list_context(context)
list_context["show_sidebar"] = True
list_context.update({
'show_sidebar': True,
'show_search': True,
'no_breadcrumbs': True,
'title': _('Request for Quotation'),
})
return list_context
def get_supplier_contacts(doctype, txt, searchfield, start, page_len, filters):

View File

@ -9,7 +9,6 @@ QUnit.test("test: request_for_quotation", function(assert) {
date = frappe.datetime.add_days(frappe.datetime.now_date(), 10);
return frappe.tests.make('Request for Quotation', [
{transaction_date: date},
{company: 'Wind Power LLC'},
{suppliers: [
[
{"supplier": 'Test Supplier'},
@ -21,7 +20,7 @@ QUnit.test("test: request_for_quotation", function(assert) {
{"item_code": 'Test Product 4'},
{"qty": 5},
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(),20)},
{"warehouse": 'All Warehouses - WP'}
{"warehouse": 'All Warehouses - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
]
]},
{message_for_supplier: 'Please supply the specified items at the best possible rates'},
@ -30,12 +29,12 @@ QUnit.test("test: request_for_quotation", function(assert) {
},
() => {
assert.ok(cur_frm.doc.transaction_date == date, "Date correct");
assert.ok(cur_frm.doc.company == 'Wind Power LLC', "Company correct");
assert.ok(cur_frm.doc.company == cur_frm.doc.company, "Company correct");
assert.ok(cur_frm.doc.suppliers[0].supplier_name == 'Test Supplier', "Supplier name correct");
assert.ok(cur_frm.doc.suppliers[0].contact == 'Contact 3-Test Supplier', "Contact correct");
assert.ok(cur_frm.doc.suppliers[0].email_id == 'test@supplier.com', "Email id correct");
assert.ok(cur_frm.doc.items[0].item_name == 'Test Product 4', "Item Name correct");
assert.ok(cur_frm.doc.items[0].warehouse == 'All Warehouses - WP', "Warehouse correct");
assert.ok(cur_frm.doc.items[0].warehouse == 'All Warehouses - '+frappe.get_abbr(frappe.defaults.get_default("Company")), "Warehouse correct");
assert.ok(cur_frm.doc.message_for_supplier == 'Please supply the specified items at the best possible rates', "Reply correct");
assert.ok(cur_frm.doc.tc_name == 'Test Term 1', "Term name correct");
},

View File

@ -8,9 +8,8 @@ QUnit.test("test: supplier", function(assert) {
return frappe.tests.make('Supplier', [
{supplier_name: 'Test Supplier'},
{supplier_type: 'Hardware'},
{country: 'United States'},
{default_currency: 'USD'},
{default_price_list: 'Test-Buying-USD'},
{country: 'India'},
{default_currency: 'INR'},
{credit_days_based_on: 'Fixed Days'},
{accounts: [
[
@ -68,7 +67,7 @@ QUnit.test("test: supplier", function(assert) {
() => {
assert.ok(cur_frm.doc.supplier_name == 'Test Supplier', "Name correct");
assert.ok(cur_frm.doc.supplier_type == 'Hardware', "Type correct");
assert.ok(cur_frm.doc.default_currency == 'USD', "Currency correct");
assert.ok(cur_frm.doc.default_currency == 'INR', "Currency correct");
assert.ok(cur_frm.doc.accounts[0].account == 'Creditors - '+frappe.get_abbr('Test Company'), " Account Head abbr correct");
assert.ok($('.address-box:nth-child(3) p').text().includes('Shipping City 3'), "Address correct");
assert.ok($('.col-sm-6+ .col-sm-6 .h6').text().includes('Contact 3'), "Contact correct");

View File

@ -11,16 +11,14 @@ QUnit.test("test: supplier quotation", function(assert) {
return frappe.tests.make('Supplier Quotation', [
{supplier: 'Test Supplier'},
{transaction_date: date},
{company: 'Wind Power LLC'},
{buying_price_list: 'Test-Buying-USD'},
{currency: 'USD'},
{currency: 'INR'},
{items: [
[
{"item_code": 'Test Product 4'},
{"qty": 5},
{"uom": 'Unit'},
{"rate": 200},
{"warehouse": 'All Warehouses - WP'}
{"warehouse": 'All Warehouses - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
]
]},
{apply_discount_on: 'Grand Total'},
@ -32,7 +30,7 @@ QUnit.test("test: supplier quotation", function(assert) {
() => {
// Get Supplier details
assert.ok(cur_frm.doc.supplier == 'Test Supplier', "Supplier correct");
assert.ok(cur_frm.doc.company == 'Wind Power LLC', "Company correct");
assert.ok(cur_frm.doc.company == cur_frm.doc.company, "Company correct");
// Get Contact details
assert.ok(cur_frm.doc.contact_display == 'Contact 3', "Conatct correct");
assert.ok(cur_frm.doc.contact_email == 'test@supplier.com', "Email correct");

View File

@ -70,8 +70,8 @@ def get_data():
"items": [
{
"type": "doctype",
"name": "BOM Replace Tool",
"description": _("Replace Item / BOM in all BOMs"),
"name": "BOM Update Tool",
"description": _("Replace BOM and update latest price in all BOMs"),
},
]
},

View File

@ -232,7 +232,7 @@ def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len,
select `tabDelivery Note`.name, `tabDelivery Note`.customer, `tabDelivery Note`.posting_date
from `tabDelivery Note`
where `tabDelivery Note`.`%(key)s` like %(txt)s and
`tabDelivery Note`.docstatus = 1 and `tabDelivery Note`.is_return = 0
`tabDelivery Note`.docstatus = 1 and `tabDelivery Note`.is_return = 0
and status not in ("Stopped", "Closed") %(fcond)s
and (`tabDelivery Note`.per_billed < 100 or `tabDelivery Note`.grand_total = 0)
%(mcond)s order by `tabDelivery Note`.`%(key)s` asc
@ -367,31 +367,30 @@ def warehouse_query(doctype, txt, searchfield, start, page_len, filters):
sub_query = """ select round(`tabBin`.actual_qty, 2) from `tabBin`
where `tabBin`.warehouse = `tabWarehouse`.name
{bin_conditions} """.format(
bin_conditions=get_filters_cond(doctype, filter_dict.get("Bin"),
bin_conditions=get_filters_cond(doctype, filter_dict.get("Bin"),
bin_conditions, ignore_permissions=True))
response = frappe.db.sql("""select `tabWarehouse`.name,
query = """select `tabWarehouse`.name,
CONCAT_WS(" : ", "Actual Qty", ifnull( ({sub_query}), 0) ) as actual_qty
from `tabWarehouse`
where
`tabWarehouse`.`{key}` like %(txt)s
`tabWarehouse`.`{key}` like '{txt}'
{fcond} {mcond}
order by
`tabWarehouse`.name desc
limit
%(start)s, %(page_len)s
{start}, {page_len}
""".format(
sub_query=sub_query,
key=frappe.db.escape(searchfield),
fcond=get_filters_cond(doctype, filter_dict.get("Warehouse"), conditions),
mcond=get_match_cond(doctype)
),
{
"txt": "%%%s%%" % frappe.db.escape(txt),
"start": start,
"page_len": page_len
})
return response
mcond=get_match_cond(doctype),
start=start,
page_len=page_len,
txt=frappe.db.escape('%{0}%'.format(txt))
)
return frappe.db.sql(query)
def get_doctype_wise_filters(filters):

View File

@ -270,7 +270,7 @@ class calculate_taxes_and_totals(object):
if tax.item_wise_tax_detail.get(key):
item_wise_tax_amount += tax.item_wise_tax_detail[key][1]
tax.item_wise_tax_detail[key] = [tax_rate,flt(item_wise_tax_amount, tax.precision("base_tax_amount"))]
tax.item_wise_tax_detail[key] = [tax_rate,flt(item_wise_tax_amount)]
def round_off_totals(self, tax):
tax.tax_amount = flt(tax.tax_amount, tax.precision("tax_amount"))
@ -521,12 +521,20 @@ def get_itemised_tax_breakup_html(doc):
frappe.flags.company = doc.company
# get headers
tax_accounts = list(set([d.description for d in doc.taxes]))
tax_accounts = []
for tax in doc.taxes:
if getattr(tax, "category", None) and tax.category=="Valuation":
continue
if tax.description not in tax_accounts:
tax_accounts.append(tax.description)
headers = get_itemised_tax_breakup_header(doc.doctype + " Item", tax_accounts)
# get tax breakup data
itemised_tax, itemised_taxable_amount = get_itemised_tax_breakup_data(doc)
get_rounded_tax_amount(itemised_tax, doc.precision("tax_amount", "taxes"))
frappe.flags.company = None
return frappe.render_template(
@ -554,6 +562,9 @@ def get_itemised_tax_breakup_data(doc):
def get_itemised_tax(taxes):
itemised_tax = {}
for tax in taxes:
if getattr(tax, "category", None) and tax.category=="Valuation":
continue
tax_amount_precision = tax.precision("tax_amount")
tax_rate_precision = tax.precision("rate")
@ -562,16 +573,16 @@ def get_itemised_tax(taxes):
for item_code, tax_data in item_tax_map.items():
itemised_tax.setdefault(item_code, frappe._dict())
if isinstance(tax_data, list) and tax_data[0]:
if isinstance(tax_data, list):
precision = tax_amount_precision if tax.charge_type == "Actual" else tax_rate_precision
itemised_tax[item_code][tax.description] = frappe._dict(dict(
tax_rate=flt(tax_data[0], precision),
tax_amount=flt(tax_data[1], tax_amount_precision)
tax_rate=flt(tax_data[0]),
tax_amount=flt(tax_data[1])
))
else:
itemised_tax[item_code][tax.description] = frappe._dict(dict(
tax_rate=flt(tax_data, tax_rate_precision),
tax_rate=flt(tax_data),
tax_amount=0.0
))
@ -584,4 +595,10 @@ def get_itemised_taxable_amount(items):
itemised_taxable_amount.setdefault(item_code, 0)
itemised_taxable_amount[item_code] += item.net_amount
return itemised_taxable_amount
return itemised_taxable_amount
def get_rounded_tax_amount(itemised_tax, precision):
# Rounding based on tax_amount precision
for taxes in itemised_tax.values():
for tax_account in taxes:
taxes[tax_account]["tax_amount"] = flt(taxes[tax_account]["tax_amount"], precision)

View File

@ -68,8 +68,8 @@ def get_list_for_transactions(doctype, txt, filters, limit_start, limit_page_len
if txt:
if meta.get_field('items'):
if meta.get_field('items').options:
child_doctype = meta.get_field('items').options
for item in frappe.get_all(child_doctype, {"item_name": ['like', "%" + txt + "%"]}):
child_doctype = meta.get_field('items').options
for item in frappe.get_all(child_doctype, {"item_name": ['like', "%" + txt + "%"]}):
child = frappe.get_doc(child_doctype, item.name)
or_filters.append([doctype, "name", "=", child.parent])

View File

@ -76,36 +76,6 @@
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "salutation",
"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": 0,
"label": "Salutation",
"length": 0,
"no_copy": 0,
"options": "Salutation",
"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,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -141,21 +111,21 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "gender",
"fieldtype": "Link",
"fieldname": "company_name",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Gender",
"label": "Organization Name",
"length": 0,
"no_copy": 0,
"options": "Gender",
"oldfieldname": "company_name",
"oldfieldtype": "Data",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
@ -260,6 +230,37 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "gender",
"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": 0,
"label": "Gender",
"length": 0,
"no_copy": 0,
"options": "Gender",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@ -295,36 +296,6 @@
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "company_name",
"fieldtype": "Data",
"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,
"label": "Organization Name",
"length": 0,
"no_copy": 0,
"oldfieldname": "company_name",
"oldfieldtype": "Data",
"permlevel": 0,
"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,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@ -435,6 +406,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Follow Up",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@ -514,11 +486,12 @@
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"bold": 1,
"collapsible": 0,
"columns": 0,
"fieldname": "contact_by",
"fieldtype": "Link",
"description": "",
"fieldname": "contact_date",
"fieldtype": "Datetime",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@ -526,12 +499,11 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Next Contact By",
"label": "Next Contact Date",
"length": 0,
"no_copy": 0,
"oldfieldname": "contact_by",
"oldfieldtype": "Link",
"options": "User",
"no_copy": 1,
"oldfieldname": "contact_date",
"oldfieldtype": "Date",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
@ -550,9 +522,8 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "Add to calendar on this date",
"fieldname": "contact_date",
"fieldtype": "Datetime",
"fieldname": "contact_by",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@ -560,11 +531,12 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Next Contact Date",
"label": "Next Contact By",
"length": 0,
"no_copy": 1,
"oldfieldname": "contact_date",
"oldfieldtype": "Date",
"no_copy": 0,
"oldfieldname": "contact_by",
"oldfieldtype": "Link",
"options": "User",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
@ -726,6 +698,37 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "salutation",
"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": 0,
"label": "Salutation",
"length": 0,
"no_copy": 0,
"options": "Salutation",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@ -1144,7 +1147,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-06-22 14:29:12.700000",
"modified": "2017-08-21 02:28:21.581948",
"modified_by": "Administrator",
"module": "CRM",
"name": "Lead",

View File

@ -419,6 +419,164 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
"collapsible_depends_on": "contact_by",
"columns": 0,
"fieldname": "next_contact",
"fieldtype": "Section Break",
"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": "Follow Up",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "",
"fieldname": "contact_by",
"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": "Next Contact By",
"length": 0,
"no_copy": 0,
"oldfieldname": "contact_by",
"oldfieldtype": "Link",
"options": "User",
"permlevel": 0,
"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,
"unique": 0,
"width": "75px"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "",
"fieldname": "contact_date",
"fieldtype": "Datetime",
"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": "Next Contact Date",
"length": 0,
"no_copy": 0,
"oldfieldname": "contact_date",
"oldfieldtype": "Date",
"permlevel": 0,
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break2",
"fieldtype": "Column Break",
"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,
"length": 0,
"no_copy": 0,
"oldfieldtype": "Column Break",
"permlevel": 0,
"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,
"unique": 0,
"width": "50%"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "to_discuss",
"fieldtype": "Small Text",
"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": "To Discuss",
"length": 0,
"no_copy": 1,
"oldfieldname": "to_discuss",
"oldfieldtype": "Small Text",
"permlevel": 0,
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@ -435,7 +593,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "",
"label": "Items",
"length": 0,
"no_copy": 0,
"oldfieldtype": "Section Break",
@ -986,164 +1144,6 @@
"unique": 0,
"width": "50px"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
"collapsible_depends_on": "contact_by",
"columns": 0,
"fieldname": "next_contact",
"fieldtype": "Section Break",
"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": "Next Contact",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "Your sales person who will contact the customer in future",
"fieldname": "contact_by",
"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": "Next Contact By",
"length": 0,
"no_copy": 0,
"oldfieldname": "contact_by",
"oldfieldtype": "Link",
"options": "User",
"permlevel": 0,
"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,
"unique": 0,
"width": "75px"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "Your sales person will get a reminder on this date to contact the customer",
"fieldname": "contact_date",
"fieldtype": "Datetime",
"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": "Next Contact Date",
"length": 0,
"no_copy": 0,
"oldfieldname": "contact_date",
"oldfieldtype": "Date",
"permlevel": 0,
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break2",
"fieldtype": "Column Break",
"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,
"length": 0,
"no_copy": 0,
"oldfieldtype": "Column Break",
"permlevel": 0,
"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,
"unique": 0,
"width": "50%"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "to_discuss",
"fieldtype": "Small Text",
"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": "To Discuss",
"length": 0,
"no_copy": 1,
"oldfieldname": "to_discuss",
"oldfieldtype": "Small Text",
"permlevel": 0,
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@ -1189,7 +1189,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-08-07 21:25:10.836517",
"modified": "2017-08-21 02:07:46.486433",
"modified_by": "Administrator",
"module": "CRM",
"name": "Opportunity",

View File

@ -1,4 +1,4 @@
from __future__ import unicode_literals
from __future__ import print_function, unicode_literals
import random, json
import frappe, erpnext
@ -42,7 +42,7 @@ def setup(domain):
frappe.clear_cache()
def complete_setup(domain='Manufacturing'):
print "Complete Setup..."
print("Complete Setup...")
from frappe.desk.page.setup_wizard.setup_wizard import setup_complete
if not frappe.get_all('Company', limit=1):

View File

@ -1,7 +1,7 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
from __future__ import print_function, unicode_literals
import frappe, random
from frappe.desk import query_report
@ -36,7 +36,7 @@ def make_purchase_receipt():
try:
pr.submit()
except NegativeStockError:
print 'Negative stock for {0}'.format(po)
print('Negative stock for {0}'.format(po))
pass
frappe.db.commit()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 189 KiB

View File

@ -1,44 +0,0 @@
# BOM Replace Tool
Replace BOM is the utility to replace BOM of sub-assembly item, which is already updated in the BOM of Finished Good item.
To use the Production Planning Tool, go to:
> Manufacturing > Tools > BOM Replace Tool
Let's consider a scenario to understand this better.
If company manufactures computers, Bill of Material of its finished item will constitute of:
1. Monitor
1. Key Board
1. Mouse
1. CPU
Out of all the items above, CPU is asembled separately. Hence separate BOM will be created for the CPU. Following are the items from the BOM of CPU.
1. 250 GB Hard Disk
1. Mother Board
1. Processor
1. SMTP
1. DVD player
If we have more items to be added , or existing items to be edited in the BOM of CPU, then we should create new BOM for it.
1. _350 GB Hard Disk_
1. Mother Board
1. Processor
1. SMTP
1. DVD player
To update new BOM updated in the BOM of finished item, where CPU is selected as raw-material, you can use BOM Replace tool.
<img class="screenshot" alt="BOM replace Tool" src="/docs/assets/img/manufacturing/bom-replace-tool.png">
In this tool, you should select Current BOM, and New BOM. On clicking Replace button, current BOM of CPU will be replaced with New BOM in the BOM of finished Item (Computer).
**Will BOM Replace Tool work for replacing finsihed item in BOM?**
No. You should Cancel and Amend current BOM, or create a new BOM for finished item.
{next}

View File

@ -0,0 +1,54 @@
# BOM Update Tool
From BOM Update Tool, you can replace a sub-assembly BOM and update costs of all BOMs.
### Replace BOM
Using this utility, you can replace an existing BOM of sub-assembly item, with a new one. The system will update the new BOM in all the parent BOMs where it was used.
To use the BOM Update Tool, go to:
> Manufacturing > Tools > BOM Update Tool
Let's consider a scenario to understand this better.
Suppose a company manufactures computers, Bill of Material of of the computer will look like this:
1. Monitor
1. Key Board
1. Mouse
1. CPU
Out of all the items above, CPU is asembled separately. Hence separate BOM will be created for the CPU. Following are the items from the BOM of CPU.
1. 250 GB Hard Disk
1. Mother Board
1. Processor
1. SMTP
1. DVD player
If we have more items to be added , or existing items to be edited in the BOM of CPU, then we should create new BOM for it.
1. _350 GB Hard Disk_
1. Mother Board
1. Processor
1. SMTP
1. DVD player
To update new BOM in all the parent BOMs, where CPU is selected as raw-material, you can use Replace utility.
<img class="screenshot" alt="BOM Update Tool" src="{{docs_base_url}}/assets/img/manufacturing/bom-update-tool.png">
In this tool, you should select Current BOM, and New BOM. On clicking Replace button, current BOM of CPU will be replaced with New BOM in the BOM of finished Item (Computer).
**Will BOM Replace Tool work for replacing finsihed item in BOM?**
No. You should Cancel and Amend current BOM, or create a new BOM for finished item.
### Update BOM Cost
Using the button **Update latest price in all BOMs**, you can update cost of all Bill of Materials, based on latest purchase price / price list rate / valuation rate of raw materials.
On clicking of this buttom, system will create a background process to update all the BOM's cost. It is processed via background jobs because this process can take a few minutes (depending on the number of BOMs) to update all the BOMs.
This functionality can also be executed automatically on daily basis. For that, you need to enable "Update BOM Cost Automatically" from Manufacturing Settings.
{next}

View File

@ -1,2 +1,2 @@
production-planning-tool
bom-replace-tool
bom-update-tool

View File

@ -1,5 +1,6 @@
print-settings
print-format-builder
print-style
print-headings
letter-head
address-template

View File

@ -0,0 +1,15 @@
# Print Style
Frappe/ERPNext comes with pre-set styles for your printed documents. You can also create new styles using CSS that can be applied to all your print formats.
To create a new **Print Style** go to **Setup > Printing and Branding > Print Style**, or just type "print style" in the search bar.
Here you can define the CSS rules for your print formats. These apply to both standard and custom print formats. To find out the various classes available, you can make a standard print format, open in a new page and see the source.
To set a default style, you can go to [Print Settings](/docs/setup/print/print-settings)
All Print Format styles are based on Bootstrap (Version 3) CSS Framework.
<img class="screenshot" alt="Print Style" src="/docs/assets/img/setup/print/print-style.png">
{next}

View File

@ -196,7 +196,8 @@ scheduler_events = {
"erpnext.hr.doctype.daily_work_summary_settings.daily_work_summary_settings.send_summary",
"erpnext.stock.doctype.serial_no.serial_no.update_maintenance_status",
"erpnext.buying.doctype.supplier_scorecard.supplier_scorecard.refresh_scorecards",
"erpnext.setup.doctype.company.company.cache_companies_monthly_sales_history"
"erpnext.setup.doctype.company.company.cache_companies_monthly_sales_history",
"erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.update_latest_price_in_all_boms",
]
}

View File

@ -0,0 +1,58 @@
QUnit.module('hr');
QUnit.test("Test: Expense Claim [HR]", function (assert) {
assert.expect(3);
let done = assert.async();
let employee_name;
frappe.run_serially([
// Creating Appraisal
() => frappe.set_route('List','Appraisal','List'),
() => frappe.timeout(0.3),
() => frappe.click_button('Make a new Appraisal'),
() => {
cur_frm.set_value('kra_template','Test Appraisal 1'),
cur_frm.set_value('start_date','2017-08-21'),
cur_frm.set_value('end_date','2017-09-21');
},
() => frappe.timeout(1),
() => frappe.model.set_value('Appraisal Goal','New Appraisal Goal 1','score',4),
() => frappe.model.set_value('Appraisal Goal','New Appraisal Goal 1','score_earned',2),
() => frappe.model.set_value('Appraisal Goal','New Appraisal Goal 2','score',4),
() => frappe.model.set_value('Appraisal Goal','New Appraisal Goal 2','score_earned',2),
() => frappe.timeout(1),
() => frappe.db.get_value('Employee', {'employee_name': 'Test Employee 1'}, 'name'),
(r) => {
employee_name = r.message.name;
},
() => frappe.timeout(1),
() => cur_frm.set_value('employee',employee_name),
() => cur_frm.set_value('employee_name','Test Employee 1'),
() => cur_frm.set_value('company','Test Company'),
() => frappe.click_button('Calculate Total Score'),
() => frappe.timeout(1),
() => cur_frm.save(),
() => frappe.timeout(1),
() => cur_frm.save(),
// Submitting the Appraisal
() => frappe.click_button('Submit'),
() => frappe.click_button('Yes'),
() => frappe.timeout(3),
// Checking if the appraisal is correctly set for the employee
() => {
assert.equal('Submitted',cur_frm.get_field('status').value,
'Appraisal is submitted');
assert.equal('Test Employee 1',cur_frm.get_field('employee_name').value,
'Appraisal is created for correct employee');
assert.equal(4,cur_frm.get_field('total_score').value,
'Total score is correctly calculated');
},
() => done()
]);
});

View File

@ -0,0 +1,30 @@
QUnit.module('hr');
QUnit.test("Test: Appraisal Template [HR]", function (assert) {
assert.expect(1);
let done = assert.async();
frappe.run_serially([
// Job Opening creation
() => {
frappe.tests.make('Appraisal Template', [
{ kra_title: 'Test Appraisal 1'},
{ description: 'This is just a test'},
{ goals: [
[
{ kra: 'Design'},
{ per_weightage: 50}
],
[
{ kra: 'Code creation'},
{ per_weightage: 50}
]
]},
]);
},
() => frappe.timeout(5),
() => {
assert.equal('Test Appraisal 1',cur_frm.doc.kra_title, 'Appraisal name correctly set');
},
() => done()
]);
});

View File

@ -0,0 +1,79 @@
QUnit.test("Test Loan [HR]", function(assert) {
assert.expect(8);
let done = assert.async();
let employee_name;
// To create a loan and check principal,interest and balance amount
let loan_creation = (ename,lname) => {
return frappe.run_serially([
() => frappe.db.get_value('Employee', {'employee_name': ename}, 'name'),
(r) => {
employee_name = r.message.name;
},
() => frappe.db.get_value('Employee Loan Application', {'loan_type': lname}, 'name'),
(r) => {
// Creating loan for an employee
return frappe.tests.make('Employee Loan', [
{ company: 'Test Company'},
{ posting_date: '2017-08-26'},
{ employee: employee_name},
{ employee_loan_application: r.message.name},
{ disbursement_date: '2018-08-26'},
{ mode_of_payment: 'Cash'},
{ employee_loan_account: 'Temporary Opening - TC'},
{ interest_income_account: 'Service - TC'}
]);
},
() => frappe.timeout(3),
() => frappe.click_button('Submit'),
() => frappe.timeout(1),
() => frappe.click_button('Yes'),
() => frappe.timeout(3),
// Checking if all the amounts are correctly calculated
() => {
assert.ok(cur_frm.get_field('employee_name').value=='Test Employee 1'&&
(cur_frm.get_field('status').value=='Sanctioned'),
'Loan Sanctioned for correct employee');
assert.equal(7270,
cur_frm.get_doc('repayment_schedule').repayment_schedule[0].principal_amount,
'Principal amount for first instalment is correctly calculated');
assert.equal(2333,
cur_frm.get_doc('repayment_schedule').repayment_schedule[0].interest_amount,
'Interest amount for first instalment is correctly calculated');
assert.equal(192730,
cur_frm.get_doc('repayment_schedule').repayment_schedule[0].balance_loan_amount,
'Balance amount after first instalment is correctly calculated');
assert.equal(9479,
cur_frm.get_doc('repayment_schedule').repayment_schedule[23].principal_amount,
'Principal amount for last instalment is correctly calculated');
assert.equal(111,
cur_frm.get_doc('repayment_schedule').repayment_schedule[23].interest_amount,
'Interest amount for last instalment is correctly calculated');
assert.equal(0,
cur_frm.get_doc('repayment_schedule').repayment_schedule[23].balance_loan_amount,
'Balance amount after last instalment is correctly calculated');
},
() => frappe.set_route('List','Employee Loan','List'),
() => frappe.timeout(2),
// Checking the submission of Loan
() => {
assert.ok(cur_list.data[0].docstatus==1,'Loan sanctioned and submitted successfully');
},
]);
};
frappe.run_serially([
// Creating loan
() => loan_creation('Test Employee 1','Test Loan'),
() => done()
]);
});

View File

@ -0,0 +1,68 @@
QUnit.module('hr');
QUnit.test("Test: Employee Loan Application [HR]", function (assert) {
assert.expect(8);
let done = assert.async();
let employee_name;
frappe.run_serially([
// Creation of Loan Application
() => frappe.db.get_value('Employee', {'employee_name': 'Test Employee 1'}, 'name'),
(r) => {
employee_name = r.message.name;
},
() => {
frappe.tests.make('Employee Loan Application', [
{ company: 'Test Company'},
{ employee: employee_name},
{ employee_name: 'Test Employee 1'},
{ status: 'Approved'},
{ loan_type: 'Test Loan '},
{ loan_amount: 200000},
{ description: 'This is just a test'},
{ repayment_method: 'Repay Over Number of Periods'},
{ repayment_periods: 24},
{ rate_of_interest: 14}
]);
},
() => frappe.timeout(6),
() => frappe.click_button('Submit'),
() => frappe.timeout(1),
() => frappe.click_button('Yes'),
() => frappe.timeout(2),
() => {
// To check if all the amounts are correctly calculated
assert.ok(cur_frm.get_field('employee_name').value == 'Test Employee 1',
'Application created successfully');
assert.ok(cur_frm.get_field('status').value=='Approved',
'Status of application is correctly set');
assert.ok(cur_frm.get_field('loan_type').value=='Test Loan',
'Application is created for correct Loan Type');
assert.ok(cur_frm.get_field('status').value=='Approved',
'Status of application is correctly set');
assert.ok(cur_frm.get_field('repayment_amount').value==9603,
'Repayment amount is correctly calculated');
assert.ok(cur_frm.get_field('total_payable_interest').value==30459,
'Interest amount is correctly calculated');
assert.ok(cur_frm.get_field('total_payable_amount').value==230459,
'Total payable amount is correctly calculated');
},
() => frappe.set_route('List','Employee Loan Application','List'),
() => frappe.timeout(2),
// Checking the submission of Loan Application
() => {
assert.ok(cur_list.data[0].docstatus==1,'Loan Application submitted successfully');
},
() => frappe.timeout(1),
() => done()
]);
});

View File

@ -0,0 +1,59 @@
QUnit.module('hr');
QUnit.test("Test: Expense Claim [HR]", function (assert) {
assert.expect(3);
let done = assert.async();
let employee_name;
let d;
frappe.run_serially([
// Creating Expense Claim
() => frappe.set_route('List','Expense Claim','List'),
() => frappe.timeout(0.3),
() => frappe.click_button('Make a new Expense Claim'),
() => {
cur_frm.set_value('exp_approver','Administrator'),
cur_frm.set_value('is_paid',1),
cur_frm.set_value('expenses',[]),
d = frappe.model.add_child(cur_frm.doc,'Expense Claim Detail','expenses'),
d.expense_date = '2017-08-01',
d.expense_type = 'Test Expense Type 1',
d.description = 'This is just to test Expense Claim',
d.claim_amount = 2000,
d.sanctioned_amount=2000,
refresh_field('expenses');
},
() => frappe.timeout(2),
() => frappe.db.get_value('Employee', {'employee_name': 'Test Employee 1'}, 'name'),
(r) => {
employee_name = r.message.name;
},
() => frappe.timeout(1),
() => cur_frm.set_value('employee',employee_name),
() => cur_frm.set_value('employee_name','Test Employee 1'),
() => cur_frm.set_value('company','Test Company'),
() => cur_frm.set_value('payable_account','Creditors - TC'),
() => cur_frm.set_value('cost_center','Main - TC'),
() => cur_frm.set_value('mode_of_payment','Cash'),
() => cur_frm.save(),
() => frappe.timeout(1),
() => cur_frm.set_value('approval_status','Approved'),
() => frappe.timeout(1),
() => cur_frm.save(),
// Submitting the Expense Claim
() => frappe.click_button('Submit'),
() => frappe.click_button('Yes'),
() => frappe.timeout(3),
// Checking if the amount is correctly reimbursed for the employee
() => {
assert.equal(employee_name,cur_frm.get_field('employee').value,
'Expense Claim is created for correct employee');
assert.equal(1,cur_frm.get_field('is_paid').value,
'Expense is paid as required');
assert.equal(2000,cur_frm.get_field('total_amount_reimbursed').value,
'Amount is reimbursed correctly');
},
() => done()
]);
});

View File

@ -0,0 +1,30 @@
QUnit.module('hr');
QUnit.test("Test: Expense Claim Type [HR]", function (assert) {
assert.expect(1);
let done = assert.async();
frappe.run_serially([
// Creating a Expense Claim Type
() => {
frappe.tests.make('Expense Claim Type', [
{ expense_type: 'Test Expense Type 1'},
{ description:'This is just a test'},
{ accounts: [
[
{ company: 'Test Company'},
{ default_account: 'Round Off - TC'}
]
]},
]);
},
() => frappe.timeout(5),
// Checking if the created type is present in the list
() => {
assert.equal('Test Expense Type 1', cur_frm.doc.expense_type,
'Expense Claim Type created successfully');
},
() => done()
]);
});

View File

@ -0,0 +1,29 @@
QUnit.module('hr');
QUnit.test("Test: Job Opening [HR]", function (assert) {
assert.expect(2);
let done = assert.async();
frappe.run_serially([
// Job Applicant creation
() => {
frappe.tests.make('Job Applicant', [
{ applicant_name: 'Utkarsh Goswami'},
{ email_id: 'goswamiutkarsh0@gmail.com'},
{ job_title: 'software-developer'},
{ cover_letter: 'Highly skilled in designing, testing, and developing software.'+
' This is just a test.'}
]);
},
() => frappe.timeout(4),
() => frappe.set_route('List','Job Applicant'),
() => frappe.timeout(3),
() => {
assert.ok(cur_list.data.length==1, 'Job Applicant created successfully');
assert.ok(cur_list.data[0].name=='Utkarsh Goswami - goswamiutkarsh0@gmail.com - software-developer',
'Correct job applicant with valid job title');
},
() => done()
]);
});

View File

@ -0,0 +1,27 @@
QUnit.module('hr');
QUnit.test("Test: Job Opening [HR]", function (assert) {
assert.expect(2);
let done = assert.async();
frappe.run_serially([
// Job Opening creation
() => {
frappe.tests.make('Job Opening', [
{ job_title: 'Software Developer'},
{ description:
'You might be responsible for writing and coding individual'+
' programmes or providing an entirely new software resource.'}
]);
},
() => frappe.timeout(4),
() => frappe.set_route('List','Job Opening'),
() => frappe.timeout(3),
() => {
assert.ok(cur_list.data.length==1, 'Job Opening created successfully');
assert.ok(cur_list.data[0].job_title=='Software Developer', 'Job title Correctly set');
},
() => done()
]);
});

View File

@ -1,7 +1,7 @@
QUnit.module('hr');
QUnit.test("Test: Leave application [HR]", function (assert) {
assert.expect(5);
assert.expect(4);
let done = assert.async();
let today_date = frappe.datetime.nowdate();
let leave_date = frappe.datetime.add_days(today_date, 1); // leave for tomorrow
@ -22,8 +22,6 @@ QUnit.test("Test: Leave application [HR]", function (assert) {
},
() => frappe.timeout(1),
// check calculated total leave days
() => assert.equal("0.5", cur_frm.doc.total_leave_days,
"leave application for half day"),
() => assert.ok(!cur_frm.doc.docstatus,
"leave application not submitted with status as open"),
() => cur_frm.set_value("status", "Approved"), // approve the application [as administrator]

View File

@ -10,7 +10,7 @@ from erpnext.hr.doctype.leave_block_list.leave_block_list import get_applicable_
class TestLeaveBlockList(unittest.TestCase):
def tearDown(self):
frappe.set_user("Administrator")
frappe.set_user("Administrator")
def test_get_applicable_block_dates(self):
frappe.set_user("test@example.com")

View File

@ -0,0 +1,31 @@
QUnit.module('hr');
QUnit.test("Test: Loan Type [HR]", function (assert) {
assert.expect(3);
let done = assert.async();
frappe.run_serially([
// Loan Type creation
() => {
frappe.tests.make('Loan Type', [
{ loan_name: 'Test Loan'},
{ maximum_loan_amount: 400000},
{ rate_of_interest: 14},
{ description:
'This is just a test.'}
]);
},
() => frappe.timeout(3),
() => frappe.set_route('List','Loan Type','List'),
() => frappe.timeout(2),
// Checking if the fields are correctly set
() => {
assert.ok(cur_list.data.length==1, 'Loan Type created successfully');
assert.ok(cur_list.data[0].name=='Test Loan', 'Loan title Correctly set');
assert.ok(cur_list.data[0].disabled==0, 'Loan enabled');
},
() => done()
]);
});

View File

@ -0,0 +1,51 @@
QUnit.module('hr');
QUnit.test("Test: Offer Letter [HR]", function (assert) {
assert.expect(3);
let done = assert.async();
frappe.run_serially([
// Offer Letter Creation
() => {
frappe.tests.make('Offer Letter', [
{ job_applicant: 'Utkarsh Goswami - goswamiutkarsh0@gmail.com - software-developer'},
{ applicant_name: 'Utkarsh Goswami'},
{ status: 'Accepted'},
{ designation: 'Software Developer'},
{ offer_terms: [
[
{offer_term: 'Responsibilities'},
{value: 'Design, installation, testing and maintenance of software systems.'}
],
[
{offer_term: 'Department'},
{value: 'Research & Development'}
],
[
{offer_term: 'Probationary Period'},
{value: 'The Probation period is for 3 months.'}
]
]},
]);
},
() => frappe.timeout(8),
() => frappe.click_button('Submit'),
() => frappe.timeout(2),
() => frappe.click_button('Yes'),
() => frappe.timeout(8),
() => {
// To check if the fields are correctly set
assert.ok(cur_frm.get_field('status').value=='Accepted',
'Status of job offer is correct');
assert.ok(cur_frm.get_field('designation').value=='Software Developer',
'Designation of applicant is correct');
},
() => frappe.set_route('List','Offer Letter','List'),
() => frappe.timeout(2),
// Checking the submission of and offer letter
() => {
assert.ok(cur_list.data[0].docstatus==1,'Offer Letter Submitted successfully');
},
() => frappe.timeout(4),
() => done()
]);
});

View File

@ -0,0 +1,59 @@
QUnit.module('hr');
QUnit.test("Test: Process Payroll [HR]", function (assert) {
assert.expect(5);
let done = assert.async();
let net_pay;
let check_amounts = (employee_name,net_amt,gross_amt) => {
frappe.run_serially([
// Retrieving the actual amount from salary slip
() => frappe.db.get_value('Salary Slip', {'employee_name': employee_name}, 'net_pay'),
(r) => {
net_pay=r.message.net_pay;
},
() => frappe.db.get_value('Salary Slip', {'employee_name': employee_name}, 'gross_pay'),
// Checking if amounts are correctly calculated
(r) => {
assert.ok(net_pay==net_amt,
'Net Pay is correctly calculated for '+employee_name);
assert.ok(r.message.gross_pay==gross_amt,
'Gross Pay is correctly calculated for '+employee_name);
},
]);
};
frappe.run_serially([
// Deleting the already generated Salary Slips for employees
() => frappe.set_route('List','Salary Slip'),
() => frappe.timeout(2),
() => { $('input.list-row-checkbox').click();},
() => frappe.click_button('Delete'),
() => frappe.click_button('Yes'),
() => frappe.timeout(2),
() => assert.ok(cur_list.data.length==0,"Salary Slips successfully deleted"),
() => frappe.timeout(3),
// Creating Process Payroll for specific company
() => frappe.set_route('Form','Process Payroll'),
() => {
cur_frm.set_value('company','Test Company'),
frappe.timeout(1),
cur_frm.set_value('payroll_frequency','Monthly'),
cur_frm.set_value('start_date','2017-08-01'),
frappe.timeout(1),
cur_frm.set_value('end_date','2017-08-31'),
cur_frm.set_value('cost_center','Main-TC'),
frappe.timeout(1),
frappe.click_button('Create Salary Slip');
},
() => frappe.timeout(3),
() => check_amounts('Test Employee 1','19200','24000'),
() => frappe.timeout(3),
() => check_amounts('Test Employee 3','23040','28800'),
() => frappe.timeout(4),
() => done()
]);
});

View File

@ -231,12 +231,12 @@ class SalarySlip(TransactionBase):
holidays = self.get_holidays_for_employee(self.start_date, self.end_date)
working_days = date_diff(self.end_date, self.start_date) + 1
actual_lwp = self.calculate_lwp(holidays, working_days)
if not cint(frappe.db.get_value("HR Settings", None, "include_holidays_in_total_working_days")):
working_days -= len(holidays)
if working_days < 0:
frappe.throw(_("There are more holidays than working days this month."))
actual_lwp = self.calculate_lwp(holidays, working_days)
if not lwp:
lwp = actual_lwp
elif lwp != actual_lwp:

View File

@ -46,7 +46,7 @@ QUnit.test("test Salary Structure", function(assert) {
{ payment_account: 'CASH - TC'},
]);
},
() => frappe.timeout(9),
() => frappe.timeout(10),
() => cur_dialog.set_value('value','Test Salary Structure'),
() => frappe.timeout(2),
() => frappe.click_button('Create'),
@ -75,7 +75,7 @@ QUnit.test("test Salary Structure", function(assert) {
};
frappe.run_serially([
() => salary_structure('Test Employee 1','Test Employee 3'),
() => frappe.timeout(14),
() => frappe.timeout(16),
() => done()
]);
});

View File

@ -0,0 +1,55 @@
QUnit.module('hr');
QUnit.test("Test: Training Event [HR]", function (assert) {
assert.expect(4);
let done = assert.async();
let employee_name;
frappe.run_serially([
// Creation of Training Event
() => frappe.db.get_value('Employee', {'employee_name': 'Test Employee 1'}, 'name'),
(r) => {
employee_name = r.message.name;
},
() => {
frappe.tests.make('Training Event', [
{ event_name: 'Test Training Event 1'},
{ location: 'Mumbai'},
{ start_time: '2017-09-01 11:00:0'},
{ end_time: '2017-09-01 17:00:0'},
{ introduction: 'This is just a test'},
{ employees: [
[
{employee: employee_name},
{employee_name: 'Test Employee 1'}
]
]},
]);
},
() => frappe.timeout(7),
() => frappe.click_button('Submit'),
() => frappe.timeout(1),
() => frappe.click_button('Yes'),
() => frappe.timeout(8),
() => {
// To check if the fields are correctly set
assert.ok(cur_frm.get_field('event_name').value == 'Test Training Event 1',
'Event created successfully');
assert.ok(cur_frm.get_field('event_status').value=='Scheduled',
'Status of event is correctly set');
assert.ok(cur_frm.doc.employees[0].employee_name=='Test Employee 1',
'Attendee Employee is correctly set');
},
() => frappe.set_route('List','Training Event','List'),
() => frappe.timeout(2),
// Checking the submission of Training Event
() => {
assert.ok(cur_list.data[0].docstatus==1,'Training Event Submitted successfully');
},
() => frappe.timeout(2),
() => done()
]);
});

View File

@ -0,0 +1,52 @@
QUnit.module('hr');
QUnit.test("Test: Training Feedback [HR]", function (assert) {
assert.expect(3);
let done = assert.async();
let employee_name;
frappe.run_serially([
// Creating Training Feedback
() => frappe.set_route('List','Training Feedback','List'),
() => frappe.timeout(0.3),
() => frappe.click_button('Make a new Training Feedback'),
() => frappe.timeout(1),
() => frappe.db.get_value('Employee', {'employee_name': 'Test Employee 1'}, 'name'),
(r) => {
employee_name = r.message.name;
},
() => cur_frm.set_value('employee',employee_name),
() => cur_frm.set_value('employee_name','Test Employee 1'),
() => cur_frm.set_value('training_event','Test Training Event 1'),
() => cur_frm.set_value('event_name','Test Training Event 1'),
() => cur_frm.set_value('feedback','Great Experience. This is just a test.'),
() => frappe.timeout(1),
() => cur_frm.save(),
() => frappe.timeout(1),
() => cur_frm.save(),
// Submitting the feedback
() => frappe.click_button('Submit'),
() => frappe.click_button('Yes'),
() => frappe.timeout(3),
// Checking if the feedback is given by correct employee
() => {
assert.equal('Test Employee 1',cur_frm.get_field('employee_name').value,
'Feedback is given by correct employee');
assert.equal('Test Training Event 1',cur_frm.get_field('training_event').value,
'Feedback is given for correct event');
},
() => frappe.set_route('List','Training Feedback','List'),
() => frappe.timeout(2),
// Checking the submission of Training Result
() => {
assert.ok(cur_list.data[0].docstatus==1,'Training Feedback Submitted successfully');
},
() => done()
]);
});

View File

@ -0,0 +1,53 @@
QUnit.module('hr');
QUnit.test("Test: Training Result [HR]", function (assert) {
assert.expect(5);
let done = assert.async();
frappe.run_serially([
// Creating Training Result
() => frappe.set_route('List','Training Result','List'),
() => frappe.timeout(0.3),
() => frappe.click_button('Make a new Training Result'),
() => {
cur_frm.set_value('training_event','Test Training Event 1');
},
() => frappe.timeout(1),
() => frappe.model.set_value('Training Result Employee','New Training Result Employee 1','hours',4),
() => frappe.model.set_value('Training Result Employee','New Training Result Employee 1','grade','A'),
() => frappe.model.set_value('Training Result Employee','New Training Result Employee 1','comments','Nice Seminar'),
() => frappe.timeout(1),
() => cur_frm.save(),
() => frappe.timeout(1),
() => cur_frm.save(),
// Submitting the Training Result
() => frappe.click_button('Submit'),
() => frappe.click_button('Yes'),
() => frappe.timeout(4),
// Checking if the fields are correctly set
() => {
assert.equal('Test Training Event 1',cur_frm.get_field('training_event').value,
'Training Result is created');
assert.equal('Test Employee 1',cur_frm.doc.employees[0].employee_name,
'Training Result is created for correct employee');
assert.equal(4,cur_frm.doc.employees[0].hours,
'Hours field is correctly calculated');
assert.equal('A',cur_frm.doc.employees[0].grade,
'Grade field is correctly set');
},
() => frappe.set_route('List','Training Result','List'),
() => frappe.timeout(2),
// Checking the submission of Training Result
() => {
assert.ok(cur_list.data[0].docstatus==1,'Training Result Submitted successfully');
},
() => done()
]);
});

View File

@ -5,7 +5,7 @@ frappe.provide("erpnext.bom");
frappe.ui.form.on("BOM", {
setup: function(frm) {
frm.add_fetch('buying_price_list', 'currency', 'currency')
frm.add_fetch('buying_price_list', 'currency', 'currency');
frm.set_query("bom_no", "items", function() {
return {
@ -13,15 +13,15 @@ frappe.ui.form.on("BOM", {
'currency': frm.doc.currency,
'company': frm.doc.company
}
}
};
});
frm.set_query("source_warehouse", "items", function() {
return {
filters: {
'company': frm.doc.company,
}
}
};
});
},
@ -57,10 +57,14 @@ frappe.ui.form.on("BOM", {
doc: frm.doc,
method: "update_cost",
freeze: true,
args: {
update_parent: true,
from_child_bom:false
},
callback: function(r) {
if(!r.exc) frm.refresh_fields();
}
})
});
}
});

View File

@ -232,7 +232,7 @@
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
@ -262,7 +262,7 @@
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
@ -291,6 +291,37 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "1",
"fieldname": "set_rate_of_sub_assembly_item_based_on_bom",
"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": "Set rate of sub-assembly item based on BOM",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@ -1640,7 +1671,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-08-11 14:09:30.492628",
"modified": "2017-08-23 14:09:30.492628",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "BOM",

View File

@ -92,7 +92,7 @@ class BOM(WebsiteGenerator):
def validate_rm_item(self, item):
if (item[0]['name'] in [it.item_code for it in self.items]) and item[0]['name'] == self.item:
frappe.throw(_("Raw material cannot be same as main Item"))
frappe.throw(_("BOM #{0}: Raw material cannot be same as main Item").format(self.name))
def set_bom_material_details(self):
for item in self.get("items"):
@ -155,15 +155,17 @@ class BOM(WebsiteGenerator):
rate = frappe.db.get_value("Item Price", {"price_list": self.buying_price_list,
"item_code": arg["item_code"]}, "price_list_rate") or 0
if not rate and arg['bom_no']:
rate = self.get_bom_unitcost(arg['bom_no'])
if arg['bom_no'] and (not rate or self.set_rate_of_sub_assembly_item_based_on_bom):
rate = self.get_bom_unitcost(arg['bom_no'])
return rate
def update_cost(self):
def update_cost(self, update_parent=True, from_child_bom=False):
if self.docstatus == 2:
return
existing_bom_cost = self.total_cost
for d in self.get("items"):
rate = self.get_bom_material_detail({'item_code': d.item_code, 'bom_no': d.bom_no,
'stock_qty': d.stock_qty})["rate"]
@ -176,7 +178,16 @@ class BOM(WebsiteGenerator):
self.save()
self.update_exploded_items()
frappe.msgprint(_("Cost Updated"))
# update parent BOMs
if self.total_cost != existing_bom_cost and update_parent:
parent_boms = frappe.db.sql_list("""select distinct parent from `tabBOM Item`
where bom_no = %s and docstatus=1""", self.name)
for bom in parent_boms:
frappe.get_doc("BOM", bom).update_cost(from_child_bom=True)
if not from_child_bom:
frappe.msgprint(_("Cost Updated"))
def get_bom_unitcost(self, bom_no):
bom = frappe.db.sql("""select name, total_cost/quantity as unit_cost from `tabBOM`
@ -248,15 +259,15 @@ class BOM(WebsiteGenerator):
def update_stock_qty(self):
for m in self.get('items'):
for m in self.get('items'):
if not m.conversion_factor:
m.conversion_factor = flt(get_conversion_factor(m.item_code, m.uom)['conversion_factor'])
if m.uom and m.qty:
m.stock_qty = flt(m.conversion_factor)*flt(m.qty)
if not m.uom and m.stock_uom:
m.uom = m.stock_uom
m.qty = m.stock_qty
if m.uom and m.qty:
m.stock_qty = flt(m.conversion_factor)*flt(m.qty)
if not m.uom and m.stock_uom:
m.uom = m.stock_uom
m.qty = m.stock_qty
def set_conversion_rate(self):
@ -566,4 +577,27 @@ def get_children():
where bom_item.parent=%s
and bom_item.item_code = item.name
order by bom_item.idx
""", frappe.form_dict.parent, as_dict=True)
""", frappe.form_dict.parent, as_dict=True)
def get_boms_in_bottom_up_order(bom_no=None):
def _get_parent(bom_no):
return frappe.db.sql_list("""select distinct parent from `tabBOM Item`
where bom_no = %s and docstatus=1""", bom_no)
count = 0
bom_list = []
if bom_no:
bom_list.append(bom_no)
else:
# get all leaf BOMs
bom_list = frappe.db.sql_list("""select name from `tabBOM` bom where docstatus=1
and not exists(select bom_no from `tabBOM Item`
where parent=bom.name and ifnull(bom_no, '')!='')""")
while(count < len(bom_list)):
for child_bom in _get_parent(bom_list[count]):
if child_bom not in bom_list:
bom_list.append(child_bom)
count += 1
return bom_list

View File

@ -6,6 +6,8 @@ from __future__ import unicode_literals
import unittest
import frappe
from frappe.utils import cstr
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import create_stock_reconciliation
from erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool import update_cost
test_records = frappe.get_test_records('BOM')
@ -46,6 +48,32 @@ class TestBOM(unittest.TestCase):
bom.save()
self.assertTrue(_get_default_bom_in_item(), bom.name)
def test_update_bom_cost_in_all_boms(self):
# get current rate for '_Test Item 2'
rm_rate = frappe.db.sql("""select rate from `tabBOM Item`
where parent='BOM-_Test Item Home Desktop Manufactured-001'
and item_code='_Test Item 2' and docstatus=1""")
rm_rate = rm_rate[0][0] if rm_rate else 0
# update valuation rate of item '_Test Item 2'
warehouse_list = frappe.db.sql_list("""select warehouse from `tabBin`
where item_code='_Test Item 2' and actual_qty > 0""")
if not warehouse_list:
warehouse_list.append("_Test Warehouse - _TC")
for warehouse in warehouse_list:
create_stock_reconciliation(item_code="_Test Item 2", warehouse=warehouse,
qty=200, rate=rm_rate + 10)
# update cost of all BOMs based on latest valuation rate
update_cost()
# check if new valuation rate updated in all BOMs
for d in frappe.db.sql("""select rate from `tabBOM Item`
where item_code='_Test Item 2' and docstatus=1""", as_dict=1):
self.assertEqual(d.rate, rm_rate + 10)
def get_default_bom(item_code="_Test FG Item 2"):
return frappe.db.get_value("BOM", {"item": item_code, "is_active": 1, "is_default": 1})

View File

@ -1 +0,0 @@
Tool to replace one Item with another in all Bill of Material (BOM) trees.

View File

@ -1,22 +0,0 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
cur_frm.cscript.refresh = function(doc) {
cur_frm.disable_save();
}
cur_frm.set_query("current_bom", function(doc) {
return{
query: "erpnext.controllers.queries.bom",
filters: {name: "!" + doc.new_bom}
}
});
cur_frm.set_query("new_bom", function(doc) {
return{
query: "erpnext.controllers.queries.bom",
filters: {name: "!" + doc.current_bom}
}
});

View File

@ -1,119 +0,0 @@
{
"allow_copy": 1,
"allow_import": 0,
"allow_rename": 0,
"creation": "2012-12-06 12:10:10",
"custom": 0,
"description": "Replace a particular BOM in all other BOMs where it is used. It will replace the old BOM link, update cost and regenerate \"BOM Explosion Item\" table as per new BOM",
"docstatus": 0,
"doctype": "DocType",
"document_type": "Other",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"description": "The BOM which will be replaced",
"fieldname": "current_bom",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Current BOM",
"no_copy": 0,
"options": "BOM",
"permlevel": 0,
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"description": "The new BOM after replacement",
"fieldname": "new_bom",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "New BOM",
"no_copy": 0,
"options": "BOM",
"permlevel": 0,
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "replace",
"fieldtype": "Button",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Replace",
"no_copy": 0,
"options": "replace_bom",
"permlevel": 0,
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"hide_heading": 1,
"hide_toolbar": 1,
"icon": "fa fa-magic",
"idx": 1,
"in_create": 1,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 1,
"istable": 0,
"modified": "2015-08-12 08:52:46.035343",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "BOM Replace Tool",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 0,
"email": 0,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 0,
"read": 1,
"report": 0,
"role": "Manufacturing Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"read_only": 1,
"read_only_onload": 0
}

View File

@ -0,0 +1,34 @@
// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.ui.form.on('BOM Update Tool', {
setup: function(frm) {
frm.set_query("current_bom", function() {
return {
query: "erpnext.controllers.queries.bom",
filters: {name: "!" + frm.doc.new_bom}
};
});
frm.set_query("new_bom", function() {
return {
query: "erpnext.controllers.queries.bom",
filters: {name: "!" + frm.doc.current_bom}
};
});
},
refresh: function(frm) {
frm.disable_save();
},
update_latest_price_in_all_boms: function() {
frappe.call({
method: "erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.enqueue_update_cost",
freeze: true,
callback: function() {
frappe.msgprint(__("Latest price updated in all BOMs"));
}
});
}
});

View File

@ -0,0 +1,244 @@
{
"allow_copy": 1,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2012-12-06 12:10:10",
"custom": 0,
"description": "Replace a particular BOM in all other BOMs where it is used. It will replace the old BOM link, update cost and regenerate \"BOM Explosion Item\" table as per new BOM.\nIt also updates latest price in all the BOMs.",
"docstatus": 0,
"doctype": "DocType",
"document_type": "Other",
"editable_grid": 0,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "replace_bom_section",
"fieldtype": "Section Break",
"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": "Replace BOM",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "The BOM which will be replaced",
"fieldname": "current_bom",
"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,
"label": "Current BOM",
"length": 0,
"no_copy": 0,
"options": "BOM",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "The new BOM after replacement",
"fieldname": "new_bom",
"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,
"label": "New BOM",
"length": 0,
"no_copy": 0,
"options": "BOM",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "replace",
"fieldtype": "Button",
"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": "Replace",
"length": 0,
"no_copy": 0,
"options": "replace_bom",
"permlevel": 0,
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "update_cost_section",
"fieldtype": "Section Break",
"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": "Update Cost",
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "update_latest_price_in_all_boms",
"fieldtype": "Button",
"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": "Update latest price in all BOMs",
"length": 0,
"no_copy": 0,
"options": "",
"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,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 1,
"hide_toolbar": 1,
"icon": "icon-magic",
"idx": 1,
"image_view": 0,
"in_create": 1,
"is_submittable": 0,
"issingle": 1,
"istable": 0,
"max_attachments": 0,
"modified": "2017-07-31 18:08:05.919276",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "BOM Update Tool",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 0,
"email": 0,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 0,
"read": 1,
"report": 0,
"role": "Manufacturing Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 0,
"read_only": 1,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"track_changes": 0,
"track_seen": 0
}

View File

@ -1,14 +1,15 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
# -*- coding: utf-8 -*-
# Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.utils import cstr, flt
from frappe import _
from erpnext.manufacturing.doctype.bom.bom import get_boms_in_bottom_up_order
from frappe.model.document import Document
class BOMReplaceTool(Document):
class BOMUpdateTool(Document):
def replace_bom(self):
self.validate_bom()
self.update_new_bom()
@ -40,3 +41,17 @@ class BOMReplaceTool(Document):
return [d[0] for d in frappe.db.sql("""select distinct parent
from `tabBOM Item` where ifnull(bom_no, '') = %s and docstatus < 2""",
self.new_bom)]
@frappe.whitelist()
def enqueue_update_cost():
frappe.enqueue("erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.update_cost")
frappe.msgprint(_("Queued for updating latest price in all Bill of Materials. It may take a few minutes."))
def update_latest_price_in_all_boms():
if frappe.db.get_single_value("Manufacturing Settings", "update_bom_costs_automatically"):
update_cost()
def update_cost():
bom_list = get_boms_in_bottom_up_order()
for bom in bom_list:
frappe.get_doc("BOM", bom).update_cost(update_parent=False, from_child_bom=True)

View File

@ -0,0 +1,23 @@
/* eslint-disable */
// rename this file from _test_[name] to test_[name] to activate
// and remove above this line
QUnit.test("test: BOM Update Tool", function (assert) {
let done = assert.async();
// number of asserts
assert.expect(1);
frappe.run_serially('BOM Update Tool', [
// insert a new BOM Update Tool
() => frappe.tests.make([
// values to be set
{key: 'value'}
]),
() => {
assert.equal(cur_frm.doc.key, 'value');
},
() => done()
]);
});

View File

@ -0,0 +1,30 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import unittest
import frappe
test_records = frappe.get_test_records('BOM')
class TestBOMUpdateTool(unittest.TestCase):
def test_replace_bom(self):
current_bom = "BOM-_Test Item Home Desktop Manufactured-001"
bom_doc = frappe.copy_doc(test_records[0])
bom_doc.items[1].item_code = "_Test Item"
bom_doc.insert()
update_tool = frappe.get_doc("BOM Update Tool")
update_tool.current_bom = current_bom
update_tool.new_bom = bom_doc.name
update_tool.replace_bom()
self.assertFalse(frappe.db.sql("select name from `tabBOM Item` where bom_no=%s", current_bom))
self.assertTrue(frappe.db.sql("select name from `tabBOM Item` where bom_no=%s", bom_doc.name))
# reverse, as it affects other testcases
update_tool.current_bom = bom_doc.name
update_tool.new_bom = current_bom
update_tool.replace_bom()

View File

@ -1,23 +1,32 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2014-11-27 14:12:07.542534",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "Document",
"editable_grid": 0,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "capacity_planning",
"fieldtype": "Section Break",
"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": "Capacity Planning",
"length": 0,
"no_copy": 0,
@ -26,6 +35,7 @@
"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,
@ -33,16 +43,21 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "Disables creation of time logs against Production Orders. Operations shall not be tracked against Production Order",
"fieldname": "disable_capacity_planning",
"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": "Disable Capacity Planning and Time Tracking",
"length": 0,
"no_copy": 0,
@ -51,6 +66,7 @@
"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,
@ -58,16 +74,21 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "Plan time logs outside Workstation Working Hours.",
"fieldname": "allow_overtime",
"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": "Allow Overtime",
"length": 0,
"no_copy": 0,
@ -76,6 +97,7 @@
"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,
@ -83,16 +105,21 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "",
"fieldname": "allow_production_on_holidays",
"fieldtype": "Check",
"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,
"label": "Allow Production on Holidays",
"length": 0,
"no_copy": 0,
@ -102,6 +129,7 @@
"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,
@ -109,15 +137,20 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_3",
"fieldtype": "Column Break",
"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,
"length": 0,
"no_copy": 0,
"permlevel": 0,
@ -125,6 +158,7 @@
"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,
@ -132,17 +166,22 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "30",
"description": "Try planning operations for X days in advance.",
"fieldname": "capacity_planning_for_days",
"fieldtype": "Int",
"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": "Capacity Planning For (Days)",
"length": 0,
"no_copy": 0,
@ -151,6 +190,7 @@
"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,
@ -158,16 +198,21 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "Default 10 mins",
"fieldname": "mins_between_operations",
"fieldtype": "Int",
"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": "Time Between Operations (in mins)",
"length": 0,
"no_copy": 0,
@ -176,6 +221,7 @@
"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,
@ -183,15 +229,20 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_6",
"fieldtype": "Section Break",
"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,
"length": 0,
"no_copy": 0,
"permlevel": 0,
@ -199,6 +250,7 @@
"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,
@ -206,15 +258,20 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "over_production_allowance_percentage",
"fieldtype": "Percent",
"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": "Over Production Allowance Percentage",
"length": 0,
"no_copy": 0,
@ -223,6 +280,7 @@
"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,
@ -230,16 +288,21 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "BOM",
"fieldname": "backflush_raw_materials_based_on",
"fieldtype": "Select",
"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": "Backflush Raw Materials Based On",
"length": 0,
"no_copy": 0,
@ -249,6 +312,7 @@
"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,
@ -256,15 +320,22 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "column_break_11",
"fieldtype": "Column Break",
"columns": 0,
"description": "Update BOM cost automatically via Scheduler, based on latest valuation rate / price list rate / last purchase rate of raw materials.",
"fieldname": "update_bom_costs_automatically",
"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": "Update BOM Cost Automatically",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@ -272,6 +343,7 @@
"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,
@ -279,15 +351,49 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_11",
"fieldtype": "Column Break",
"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,
"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,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "default_wip_warehouse",
"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": 0,
"label": "Default Work In Progress Warehouse",
"length": 0,
"no_copy": 0,
@ -297,6 +403,7 @@
"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,
@ -304,15 +411,20 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "default_fg_warehouse",
"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": 0,
"label": "Default Finished Goods Warehouse",
"length": 0,
"no_copy": 0,
@ -322,6 +434,7 @@
"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,
@ -329,18 +442,19 @@
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "fa fa-wrench",
"icon": "icon-wrench",
"idx": 0,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 1,
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2015-12-10 00:03:20.895790",
"modified": "2017-07-31 19:25:04.242693",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Manufacturing Settings",
@ -368,8 +482,12 @@
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC"
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
}

View File

@ -0,0 +1,23 @@
/* eslint-disable */
// rename this file from _test_[name] to test_[name] to activate
// and remove above this line
QUnit.test("test: Manufacturing Settings", function (assert) {
let done = assert.async();
// number of asserts
assert.expect(1);
frappe.run_serially('Manufacturing Settings', [
// insert a new Manufacturing Settings
() => frappe.tests.make([
// values to be set
{key: 'value'}
]),
() => {
assert.equal(cur_frm.doc.key, 'value');
},
() => done()
]);
});

View File

@ -91,7 +91,7 @@ frappe.ui.form.on("Production Order", {
frm.set_indicator_formatter('operation',
function(doc) { return (frm.doc.qty==doc.completed_qty) ? "green" : "orange" });
},
refresh: function(frm) {
erpnext.toggle_naming_series();
erpnext.production_order.set_custom_buttons(frm);

View File

@ -523,7 +523,8 @@ def set_production_order_ops(name):
@frappe.whitelist()
def make_stock_entry(production_order_id, purpose, qty=None):
production_order = frappe.get_doc("Production Order", production_order_id)
if not frappe.db.get_value("Warehouse", production_order.wip_warehouse, "is_group"):
if not frappe.db.get_value("Warehouse", production_order.wip_warehouse, "is_group") \
and not production_order.skip_transfer:
wip_warehouse = production_order.wip_warehouse
else:
wip_warehouse = None

View File

@ -112,11 +112,11 @@ QUnit.test("test: production order", function (assert) {
() => click_make(),
() => {
assert.equal(cur_frm.doc.total_incoming_value, "105700",
"Incoming cost is correct"); // Price of each item x5, values are in USD
"Incoming cost is correct "+cur_frm.doc.total_incoming_value); // Price of each item x5, values are in INR
assert.equal(cur_frm.doc.total_outgoing_value, "99000",
"Outgoing cost is correct"); // Price of each item x5, values are in USD
"Outgoing cost is correct"); // Price of each item x5, values are in INR
assert.equal(cur_frm.doc.total_incoming_value - cur_frm.doc.total_outgoing_value, cur_frm.doc.value_difference,
"Value difference is correct"); // Price of each item x5, values are in USD
"Value difference is correct"); // Price of each item x5, values are in INR
},
() => frappe.click_button("Save"),
() => frappe.timeout(1),
@ -135,4 +135,4 @@ QUnit.test("test: production order", function (assert) {
() => done()
]);
});
});

View File

@ -229,6 +229,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:!parent.skip_transfer",
"fieldname": "transferred_qty",
"fieldtype": "Float",
"hidden": 0,
@ -353,7 +354,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-07-10 17:37:20.212361",
"modified": "2017-08-18 18:11:10.311736",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Production Order Item",

View File

@ -421,7 +421,7 @@ erpnext.patches.v8_1.removed_report_support_hours
erpnext.patches.v8_1.add_indexes_in_transaction_doctypes
erpnext.patches.v8_3.set_restrict_to_domain_for_module_def
erpnext.patches.v8_1.update_expense_claim_status
erpnext.patches.v8_3.update_company_total_sales
erpnext.patches.v8_3.update_company_total_sales #2017-08-16
erpnext.patches.v8_4.make_scorecard_records
erpnext.patches.v8_1.set_delivery_date_in_so_item #2017-07-28
erpnext.patches.v8_5.fix_tax_breakup_for_non_invoice_docs
@ -431,4 +431,8 @@ erpnext.patches.v8_5.set_default_mode_of_payment
erpnext.patches.v8_5.update_customer_group_in_POS_profile
erpnext.patches.v8_6.update_timesheet_company_from_PO
erpnext.patches.v8_6.set_write_permission_for_quotation_for_sales_manager
erpnext.patches.v8_5.remove_project_type_property_setter
erpnext.patches.v8_5.remove_project_type_property_setter
erpnext.patches.v8_7.add_more_gst_fields
erpnext.patches.v8_7.fix_purchase_receipt_status
erpnext.patches.v8_6.rename_bom_update_tool

View File

@ -1,4 +1,4 @@
from __future__ import unicode_literals
from __future__ import print_function, unicode_literals
import frappe
def execute():
@ -10,5 +10,5 @@ def execute():
frappe.db.get_value("Global Defaults", "Global Defaults", "country")})
d.insert()
except:
print frappe.get_traceback()
print(frappe.get_traceback())

View File

@ -1,7 +1,7 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
from __future__ import print_function, unicode_literals
from frappe.permissions import reset_perms
def execute():
@ -16,5 +16,5 @@ def execute():
try:
reset_perms(doctype)
except:
print "Error resetting perms for", doctype
print("Error resetting perms for", doctype)
raise

View File

@ -1,7 +1,7 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
from __future__ import print_function, unicode_literals
import frappe
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
@ -91,7 +91,7 @@ def get_default_series(doctype, new_series):
(new_series, new_series))
if not (default_series and default_series[0][0]):
print "[Skipping] Cannot guess which naming series to use for", doctype
print("[Skipping] Cannot guess which naming series to use for", doctype)
return
return default_series[0][0]

View File

@ -1,11 +1,11 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
from __future__ import print_function, unicode_literals
import frappe
def execute():
print "WARNING!!!! Email Settings not migrated. Please setup your email again."
print("WARNING!!!! Email Settings not migrated. Please setup your email again.")
# this will happen if you are migrating very old accounts
# comment out this line below and remember to create new Email Accounts

View File

@ -1,7 +1,7 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
from __future__ import print_function, unicode_literals
import frappe
def execute():
@ -31,4 +31,4 @@ def execute():
frappe.db.sql("""UPDATE tabAccount SET root_type=%s WHERE lft>%s and rgt<%s""",
(root.root_type, root.lft, root.rgt))
else:
print b"Root type not found for {0}".format(root.name.encode("utf-8"))
print(b"Root type not found for {0}".format(root.name.encode("utf-8")))

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