Compare commits
325 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
75f3f81a9b | ||
|
|
870ce3cfea | ||
|
|
0ad2cc3def | ||
|
|
00f6c2d61d | ||
|
|
38008f8daa | ||
|
|
afb63af3b6 | ||
|
|
816ce33daf | ||
|
|
99748dbacf | ||
|
|
31287b00a6 | ||
|
|
5ec9f6930b | ||
|
|
40611e4f69 | ||
|
|
dd0bdc7fa6 | ||
|
|
bbce7b7e5d | ||
|
|
92e09c0b7b | ||
|
|
819c50c042 | ||
|
|
6887cedaea | ||
|
|
aa7896f1e1 | ||
|
|
52909b73bb | ||
|
|
5235aa833c | ||
|
|
d0131762cc | ||
|
|
97b98b6c01 | ||
|
|
92a6155bb0 | ||
|
|
d6e67ce123 | ||
|
|
6a4e230cde | ||
|
|
c65f1d7745 | ||
|
|
e02ee898e9 | ||
|
|
2e6f6d6749 | ||
|
|
014b138074 | ||
|
|
fb734976eb | ||
|
|
8e7ba95b78 | ||
|
|
e146143bd1 | ||
|
|
4d22b6db30 | ||
|
|
bdfc074a7d | ||
|
|
c7bd77111e | ||
|
|
4a60554b91 | ||
|
|
3dc21b099d | ||
|
|
40a02769c5 | ||
|
|
e6712c129c | ||
|
|
464289b726 | ||
|
|
1c1237537b | ||
|
|
b7483f6dfd | ||
|
|
cba7a11d75 | ||
|
|
2672c331af | ||
|
|
f4283a3ebf | ||
|
|
8a0943ead7 | ||
|
|
82c9352d53 | ||
|
|
311823aca1 | ||
|
|
6c06e700d6 | ||
|
|
f76bc27db5 | ||
|
|
b88b7eaf5b | ||
|
|
7533101d4b | ||
|
|
e7eda65968 | ||
|
|
5fdd26f1e7 | ||
|
|
c44290955e | ||
|
|
4825eccad5 | ||
|
|
5a4d5bfaf2 | ||
|
|
b4f5f14438 | ||
|
|
bb60a59b96 | ||
|
|
5fa2adcca9 | ||
|
|
12e817bad6 | ||
|
|
47e405516b | ||
|
|
4272483f2d | ||
|
|
3173be9b17 | ||
|
|
9cb817874d | ||
|
|
8227422124 | ||
|
|
a7d5f94d4a | ||
|
|
7624e7bf85 | ||
|
|
dd7c1b87c5 | ||
|
|
3e4ca4219f | ||
|
|
7ec7a45f05 | ||
|
|
3e02d80636 | ||
|
|
5ab6ff2470 | ||
|
|
11e964d442 | ||
|
|
a516856c32 | ||
|
|
84f7ba8699 | ||
|
|
a3fe5b8528 | ||
|
|
bad5e90e46 | ||
|
|
a9feddbb79 | ||
|
|
44734049f5 | ||
|
|
bc4e2cd9c1 | ||
|
|
bfb108d722 | ||
|
|
45a640df08 | ||
|
|
53659cf0bd | ||
|
|
80f333950b | ||
|
|
cd4202c2f7 | ||
|
|
d3214fed57 | ||
|
|
bbbce58884 | ||
|
|
8bf7230fcc | ||
|
|
496174bbdf | ||
|
|
4072d71769 | ||
|
|
5ffed60916 | ||
|
|
8854438830 | ||
|
|
15411fcf70 | ||
|
|
76a9cefc9c | ||
|
|
4f7873b9df | ||
|
|
ad9afe68f3 | ||
|
|
41e2b98b63 | ||
|
|
4eb5286c4b | ||
|
|
3f309e1c20 | ||
|
|
9bb81ee1cb | ||
|
|
4cdb9dee09 | ||
|
|
ee9da67173 | ||
|
|
9893a2d608 | ||
|
|
e78601b040 | ||
|
|
47b2a5f7d1 | ||
|
|
5ba4c4c49c | ||
|
|
172f28fa06 | ||
|
|
d635a2646b | ||
|
|
f347e23556 | ||
|
|
966b3c156a | ||
|
|
fbbb8695f3 | ||
|
|
f9577652a0 | ||
|
|
d270748eea | ||
|
|
5ba39f3f83 | ||
|
|
2622d370c6 | ||
|
|
e181dd4c24 | ||
|
|
7f9d75521e | ||
|
|
eaf0abedd4 | ||
|
|
f91ad75b5a | ||
|
|
5cb6c38a34 | ||
|
|
6f5853b97a | ||
|
|
62ce218fc2 | ||
|
|
1b0f3ec666 | ||
|
|
beeba8b37a | ||
|
|
d7636b2b19 | ||
|
|
b870d0081b | ||
|
|
7eaeec951d | ||
|
|
5a834209d0 | ||
|
|
e3a5899980 | ||
|
|
455c3ebb27 | ||
|
|
58b587834e | ||
|
|
e31757b001 | ||
|
|
e0dfd1608e | ||
|
|
bf3b54f658 | ||
|
|
395cf4689b | ||
|
|
925e9776e8 | ||
|
|
9c0d30a087 | ||
|
|
4cb4d2f155 | ||
|
|
975d28307a | ||
|
|
607b5d4985 | ||
|
|
ea390effa5 | ||
|
|
31e6f75dbf | ||
|
|
34ad2e6a60 | ||
|
|
f8ad0cbe70 | ||
|
|
0518001bdc | ||
|
|
25db832be1 | ||
|
|
f23788bb7d | ||
|
|
74ce391303 | ||
|
|
671c6610de | ||
|
|
8c3d19e2ab | ||
|
|
4a5ac7cea6 | ||
|
|
f7a856b913 | ||
|
|
26507d7867 | ||
|
|
d6d99e0d59 | ||
|
|
729aa2025a | ||
|
|
7d4fd35aa3 | ||
|
|
e87a076f1d | ||
|
|
fd345f8e90 | ||
|
|
ff689a658f | ||
|
|
b290e3a4e7 | ||
|
|
77f0822abe | ||
|
|
f83bc51e81 | ||
|
|
fc712aea32 | ||
|
|
7105c4b76c | ||
|
|
54c725dcd1 | ||
|
|
1e2c554e61 | ||
|
|
65dfd09947 | ||
|
|
61287e3c53 | ||
|
|
1b67d71139 | ||
|
|
32456b0f14 | ||
|
|
3d0d4b2157 | ||
|
|
7e5a9f5c0e | ||
|
|
d3e21fff66 | ||
|
|
5171956646 | ||
|
|
0cc93538ed | ||
|
|
b779644493 | ||
|
|
c83e793ce8 | ||
|
|
4d68e03a97 | ||
|
|
eaec4695f7 | ||
|
|
5049edb494 | ||
|
|
d17bea0a31 | ||
|
|
444bfff1ff | ||
|
|
ee4a2dd26f | ||
|
|
f6580268e6 | ||
|
|
e3a468ed1b | ||
|
|
9e6f2a49e8 | ||
|
|
445e8a2e57 | ||
|
|
7b6eaee05b | ||
|
|
c26e3f1569 | ||
|
|
c4e52e5f95 | ||
|
|
7eba1a35d3 | ||
|
|
1f10d693e9 | ||
|
|
53e8989699 | ||
|
|
8919669ac2 | ||
|
|
d977333a99 | ||
|
|
3a2834c7ad | ||
|
|
88491715e0 | ||
|
|
0bdf1e5ef1 | ||
|
|
f059e7be35 | ||
|
|
00a48ad4e5 | ||
|
|
def308a433 | ||
|
|
cb38e599e5 | ||
|
|
9b98d7fa14 | ||
|
|
b1bf502119 | ||
|
|
cb48404bd2 | ||
|
|
5b58e489a8 | ||
|
|
38647ed832 | ||
|
|
42c74a6365 | ||
|
|
c6f25ba996 | ||
|
|
b387b3cca0 | ||
|
|
a456901e9f | ||
|
|
0f2b21f88b | ||
|
|
73f4eef40f | ||
|
|
9ded8a7290 | ||
|
|
c2befc59d1 | ||
|
|
34a8fb4100 | ||
|
|
47eac28f0a | ||
|
|
869b878286 | ||
|
|
f2e2e23b6b | ||
|
|
3a22fd1535 | ||
|
|
f07e53b7a8 | ||
|
|
bd121486a8 | ||
|
|
122981d465 | ||
|
|
962cef470a | ||
|
|
0f73f90fa2 | ||
|
|
69e88ffa39 | ||
|
|
f29969a5cc | ||
|
|
e3ed8b7cb3 | ||
|
|
9626ed79fa | ||
|
|
c429a6e688 | ||
|
|
2315a79b5f | ||
|
|
9da25f568d | ||
|
|
9d497a984b | ||
|
|
bb03ec103b | ||
|
|
4c58588d25 | ||
|
|
4b99fe15cc | ||
|
|
b9fe14631d | ||
|
|
01b96a0e31 | ||
|
|
79a1d2a3b0 | ||
|
|
6488645d42 | ||
|
|
b46900a4cb | ||
|
|
912ae24ca2 | ||
|
|
e292c83114 | ||
|
|
e1a4b3e4bc | ||
|
|
d3a48a83fd | ||
|
|
80d24f83f8 | ||
|
|
5d8fd477bd | ||
|
|
bf37995745 | ||
|
|
5510d0751d | ||
|
|
b79c4a9ff6 | ||
|
|
1b61dfd9ea | ||
|
|
3f7d96ecba | ||
|
|
367b90e3ae | ||
|
|
945f502748 | ||
|
|
9c339145b2 | ||
|
|
3c14c5a16c | ||
|
|
bdb4c542e7 | ||
|
|
6d61a45f42 | ||
|
|
c314485d55 | ||
|
|
3b04cfc812 | ||
|
|
79ba422273 | ||
|
|
0dc3c1b114 | ||
|
|
0f6fff6f0c | ||
|
|
2b87d100fa | ||
|
|
c4ee77a3cc | ||
|
|
3ec6960478 | ||
|
|
33670bba47 | ||
|
|
73f1c93cd2 | ||
|
|
bf68611567 | ||
|
|
0d91d3f572 | ||
|
|
892ec599d4 | ||
|
|
65cd9f2284 | ||
|
|
799d69baba | ||
|
|
71866e06f3 | ||
|
|
284ceb7abe | ||
|
|
65c7a6947a | ||
|
|
f7a8277d12 | ||
|
|
12cb07ce3c | ||
|
|
23a4b09d43 | ||
|
|
c9f9e5235b | ||
|
|
edd4fd4692 | ||
|
|
ca916a73de | ||
|
|
53e19075d1 | ||
|
|
a0ba5594f9 | ||
|
|
16645803f9 | ||
|
|
94799a8b93 | ||
|
|
fce14fdcf0 | ||
|
|
7baacb7f74 | ||
|
|
76615c8001 | ||
|
|
283922daa2 | ||
|
|
da8de2f0c7 | ||
|
|
0060993eab | ||
|
|
566a0a05c8 | ||
|
|
f702d72c35 | ||
|
|
76e1ca35ad | ||
|
|
e0a845c356 | ||
|
|
eb686f8b1a | ||
|
|
d3fa19143d | ||
|
|
70eaf2da95 | ||
|
|
103b239a31 | ||
|
|
87e994e0a2 | ||
|
|
45d45f4247 | ||
|
|
946e182564 | ||
|
|
9aff73d156 | ||
|
|
df8fbd7d72 | ||
|
|
51a07d19c8 | ||
|
|
afe9eabd3c | ||
|
|
cb5e1e550f | ||
|
|
3b61552836 | ||
|
|
4ebac3380d | ||
|
|
cdd6ded790 | ||
|
|
fb142f5283 | ||
|
|
6d78f7b862 | ||
|
|
5478a7fa67 | ||
|
|
2851dfad99 | ||
|
|
5b05335e89 | ||
|
|
0e28fccb34 | ||
|
|
ab5b03011d | ||
|
|
cb6774e373 | ||
|
|
1276893550 | ||
|
|
27bbb561d2 | ||
|
|
2c1f44ecfa | ||
|
|
c73383c34c | ||
|
|
764bb30d2d | ||
|
|
6544a85f1e |
@@ -4,7 +4,7 @@ import inspect
|
||||
import frappe
|
||||
from erpnext.hooks import regional_overrides
|
||||
|
||||
__version__ = '9.0.0'
|
||||
__version__ = '9.2.3'
|
||||
|
||||
def get_default_company(user=None):
|
||||
'''Get default company for user'''
|
||||
|
||||
@@ -286,6 +286,99 @@
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "currency_exchange_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": "Currency Exchange Settings",
|
||||
"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,
|
||||
"default": "1",
|
||||
"fieldname": "allow_stale",
|
||||
"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 Stale Exchange Rates",
|
||||
"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,
|
||||
"default": "1",
|
||||
"depends_on": "eval:doc.allow_stale==0",
|
||||
"fieldname": "stale_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": "Stale Days",
|
||||
"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
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
@@ -299,7 +392,7 @@
|
||||
"issingle": 1,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-06-16 17:39:50.614522",
|
||||
"modified": "2017-09-05 10:10:03.117505",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Accounts Settings",
|
||||
@@ -324,6 +417,46 @@
|
||||
"share": 1,
|
||||
"submit": 0,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"delete": 0,
|
||||
"email": 0,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 0,
|
||||
"read": 1,
|
||||
"report": 0,
|
||||
"role": "Sales User",
|
||||
"set_user_permissions": 0,
|
||||
"share": 0,
|
||||
"submit": 0,
|
||||
"write": 0
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"delete": 0,
|
||||
"email": 0,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 0,
|
||||
"read": 1,
|
||||
"report": 0,
|
||||
"role": "Purchase User",
|
||||
"set_user_permissions": 0,
|
||||
"share": 0,
|
||||
"submit": 0,
|
||||
"write": 0
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
@@ -333,4 +466,4 @@
|
||||
"sort_order": "ASC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,10 +5,20 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import cint, comma_and
|
||||
from frappe.utils import cint
|
||||
from frappe.model.document import Document
|
||||
|
||||
|
||||
class AccountsSettings(Document):
|
||||
def on_update(self):
|
||||
pass
|
||||
pass
|
||||
|
||||
def validate(self):
|
||||
self.validate_stale_days()
|
||||
|
||||
def validate_stale_days(self):
|
||||
if not self.allow_stale and cint(self.stale_days) <= 0:
|
||||
frappe.msgprint(
|
||||
"Stale Days should start from 1.", title='Error', indicator='red',
|
||||
raise_exception=1)
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
QUnit.module('accounts');
|
||||
|
||||
QUnit.test("test: Accounts Settings doesn't allow negatives", function (assert) {
|
||||
let done = assert.async();
|
||||
|
||||
assert.expect(2);
|
||||
|
||||
frappe.run_serially([
|
||||
() => frappe.set_route('Form', 'Accounts Settings', 'Accounts Settings'),
|
||||
() => frappe.timeout(2),
|
||||
() => unchecked_if_checked(cur_frm, 'Allow Stale Exchange Rates', frappe.click_check),
|
||||
() => cur_frm.set_value('stale_days', 0),
|
||||
() => frappe.click_button('Save'),
|
||||
() => frappe.timeout(2),
|
||||
() => {
|
||||
assert.ok(cur_dialog);
|
||||
},
|
||||
() => frappe.click_button('Close'),
|
||||
() => cur_frm.set_value('stale_days', -1),
|
||||
() => frappe.click_button('Save'),
|
||||
() => frappe.timeout(2),
|
||||
() => {
|
||||
assert.ok(cur_dialog);
|
||||
},
|
||||
() => frappe.click_button('Close'),
|
||||
() => done()
|
||||
]);
|
||||
|
||||
});
|
||||
|
||||
const unchecked_if_checked = function(frm, field_name, fn){
|
||||
if (frm.doc.allow_stale) {
|
||||
return fn(field_name);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,22 @@
|
||||
import unittest
|
||||
|
||||
import frappe
|
||||
|
||||
|
||||
class TestAccountsSettings(unittest.TestCase):
|
||||
def tearDown(self):
|
||||
# Just in case `save` method succeeds, we need to take things back to default so that other tests
|
||||
# don't break
|
||||
cur_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
|
||||
cur_settings.allow_stale = 1
|
||||
cur_settings.save()
|
||||
|
||||
def test_stale_days(self):
|
||||
cur_settings = frappe.get_doc('Accounts Settings', 'Accounts Settings')
|
||||
cur_settings.allow_stale = 0
|
||||
cur_settings.stale_days = 0
|
||||
|
||||
self.assertRaises(frappe.ValidationError, cur_settings.save)
|
||||
|
||||
cur_settings.stale_days = -1
|
||||
self.assertRaises(frappe.ValidationError, cur_settings.save)
|
||||
@@ -55,13 +55,13 @@ frappe.ui.form.on('Asset', {
|
||||
});
|
||||
}
|
||||
|
||||
frm.trigger("show_graph");
|
||||
frm.trigger("setup_chart");
|
||||
}
|
||||
},
|
||||
|
||||
show_graph: function(frm) {
|
||||
var x_intervals = ["x", frm.doc.purchase_date];
|
||||
var asset_values = ["Asset Value", frm.doc.gross_purchase_amount];
|
||||
setup_chart: function(frm) {
|
||||
var x_intervals = [frm.doc.purchase_date];
|
||||
var asset_values = [frm.doc.gross_purchase_amount];
|
||||
var last_depreciation_date = frm.doc.purchase_date;
|
||||
|
||||
if(frm.doc.opening_accumulated_depreciation) {
|
||||
@@ -94,32 +94,21 @@ frappe.ui.form.on('Asset', {
|
||||
last_depreciation_date = frm.doc.disposal_date;
|
||||
}
|
||||
|
||||
frm.dashboard.setup_chart({
|
||||
frm.dashboard.render_graph({
|
||||
title: "Asset Value",
|
||||
data: {
|
||||
x: 'x',
|
||||
columns: [x_intervals, asset_values],
|
||||
regions: {
|
||||
'Asset Value': [{'start': last_depreciation_date, 'style':'dashed'}]
|
||||
}
|
||||
labels: x_intervals,
|
||||
datasets: [{
|
||||
color: 'green',
|
||||
values: asset_values,
|
||||
formatted: asset_values.map(d => d.toFixed(2))
|
||||
}]
|
||||
},
|
||||
legend: {
|
||||
show: false
|
||||
},
|
||||
axis: {
|
||||
x: {
|
||||
type: 'timeseries',
|
||||
tick: {
|
||||
format: "%d-%m-%Y"
|
||||
}
|
||||
},
|
||||
y: {
|
||||
min: 0,
|
||||
padding: {bottom: 10}
|
||||
}
|
||||
}
|
||||
type: 'line'
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
item_code: function(frm) {
|
||||
if(frm.doc.item_code) {
|
||||
frappe.call({
|
||||
|
||||
@@ -13,6 +13,7 @@ class TestAsset(unittest.TestCase):
|
||||
def setUp(self):
|
||||
set_depreciation_settings_in_company()
|
||||
create_asset()
|
||||
frappe.db.sql("delete from `tabTax Rule`")
|
||||
|
||||
def test_purchase_asset(self):
|
||||
asset = frappe.get_doc("Asset", "Macbook Pro 1")
|
||||
|
||||
@@ -12,8 +12,8 @@ from erpnext.hr.doctype.expense_claim.expense_claim import update_reimbursed_amo
|
||||
from erpnext.hr.doctype.employee_loan.employee_loan import update_disbursement_status
|
||||
|
||||
class JournalEntry(AccountsController):
|
||||
def __init__(self, arg1, arg2=None):
|
||||
super(JournalEntry, self).__init__(arg1, arg2)
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(JournalEntry, self).__init__(*args, **kwargs)
|
||||
|
||||
def get_feed(self):
|
||||
return self.voucher_type
|
||||
@@ -54,7 +54,7 @@ class JournalEntry(AccountsController):
|
||||
def update_advance_paid(self):
|
||||
advance_paid = frappe._dict()
|
||||
for d in self.get("accounts"):
|
||||
if d.is_advance:
|
||||
if d.is_advance == "Yes":
|
||||
if d.reference_type in ("Sales Order", "Purchase Order"):
|
||||
advance_paid.setdefault(d.reference_type, []).append(d.reference_name)
|
||||
|
||||
@@ -76,7 +76,7 @@ class JournalEntry(AccountsController):
|
||||
|
||||
def unlink_advance_entry_reference(self):
|
||||
for d in self.get("accounts"):
|
||||
if d.is_advance and d.reference_type in ("Sales Invoice", "Purchase Invoice"):
|
||||
if d.is_advance == "Yes" and d.reference_type in ("Sales Invoice", "Purchase Invoice"):
|
||||
doc = frappe.get_doc(d.reference_type, d.reference_name)
|
||||
doc.delink_advance_entries(self.name)
|
||||
d.reference_type = ''
|
||||
|
||||
@@ -403,6 +403,9 @@ frappe.ui.form.on('Payment Entry', {
|
||||
|
||||
frm.events.set_difference_amount(frm);
|
||||
}
|
||||
|
||||
// Make read only if Accounts Settings doesn't allow stale rates
|
||||
frm.set_df_property("source_exchange_rate", "read_only", erpnext.stale_rate_allowed());
|
||||
},
|
||||
|
||||
target_exchange_rate: function(frm) {
|
||||
@@ -421,6 +424,9 @@ frappe.ui.form.on('Payment Entry', {
|
||||
frm.events.set_difference_amount(frm);
|
||||
}
|
||||
frm.set_paid_amount_based_on_received_amount = false;
|
||||
|
||||
// Make read only if Accounts Settings doesn't allow stale rates
|
||||
frm.set_df_property("target_exchange_rate", "read_only", erpnext.stale_rate_allowed());
|
||||
},
|
||||
|
||||
paid_amount: function(frm) {
|
||||
@@ -642,12 +648,19 @@ frappe.ui.form.on('Payment Entry', {
|
||||
|
||||
set_difference_amount: function(frm) {
|
||||
var unallocated_amount = 0;
|
||||
var total_deductions = frappe.utils.sum($.map(frm.doc.deductions || [],
|
||||
function(d) { return flt(d.amount) }));
|
||||
|
||||
if(frm.doc.party) {
|
||||
var party_amount = frm.doc.payment_type=="Receive" ?
|
||||
frm.doc.paid_amount : frm.doc.received_amount;
|
||||
|
||||
if(frm.doc.total_allocated_amount < party_amount) {
|
||||
unallocated_amount = party_amount - frm.doc.total_allocated_amount;
|
||||
if(frm.doc.payment_type == "Receive") {
|
||||
unallocated_amount = party_amount - (frm.doc.total_allocated_amount - total_deductions);
|
||||
} else {
|
||||
unallocated_amount = party_amount - (frm.doc.total_allocated_amount + total_deductions);
|
||||
}
|
||||
}
|
||||
}
|
||||
frm.set_value("unallocated_amount", unallocated_amount);
|
||||
@@ -666,9 +679,6 @@ frappe.ui.form.on('Payment Entry', {
|
||||
difference_amount = flt(frm.doc.base_paid_amount) - flt(frm.doc.base_received_amount);
|
||||
}
|
||||
|
||||
var total_deductions = frappe.utils.sum($.map(frm.doc.deductions || [],
|
||||
function(d) { return flt(d.amount) }));
|
||||
|
||||
frm.set_value("difference_amount", difference_amount - total_deductions);
|
||||
|
||||
frm.events.hide_unhide_fields(frm);
|
||||
|
||||
@@ -285,8 +285,13 @@ class PaymentEntry(AccountsController):
|
||||
if self.party:
|
||||
party_amount = self.paid_amount if self.payment_type=="Receive" else self.received_amount
|
||||
|
||||
total_deductions = sum([flt(d.amount) for d in self.get("deductions")])
|
||||
|
||||
if self.total_allocated_amount < party_amount:
|
||||
self.unallocated_amount = party_amount - self.total_allocated_amount
|
||||
if self.payment_type == "Receive":
|
||||
self.unallocated_amount = party_amount - (self.total_allocated_amount - total_deductions)
|
||||
else:
|
||||
self.unallocated_amount = party_amount - (self.total_allocated_amount + total_deductions)
|
||||
|
||||
def set_difference_amount(self):
|
||||
base_unallocated_amount = flt(self.unallocated_amount) * (flt(self.source_exchange_rate)
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
QUnit.module('Payment Entry');
|
||||
|
||||
QUnit.test("test payment entry", function(assert) {
|
||||
assert.expect(7 );
|
||||
let done = assert.async();
|
||||
|
||||
frappe.run_serially([
|
||||
() => {
|
||||
return frappe.tests.make('Purchase Invoice', [
|
||||
{supplier: 'Test Supplier'},
|
||||
{bill_no: 'in1234'},
|
||||
{items: [
|
||||
[
|
||||
{'qty': 2},
|
||||
{'item_code': 'Test Product 1'},
|
||||
{'rate':1000},
|
||||
]
|
||||
]},
|
||||
{update_stock:1},
|
||||
{supplier_address: 'Test1-Billing'},
|
||||
{contact_person: 'Contact 3-Test Supplier'},
|
||||
{tc_name: 'Test Term 1'},
|
||||
{terms: 'This is just a Test'}
|
||||
]);
|
||||
},
|
||||
|
||||
() => cur_frm.save(),
|
||||
() => frappe.tests.click_button('Submit'),
|
||||
() => frappe.tests.click_button('Yes'),
|
||||
() => frappe.timeout(1),
|
||||
() => frappe.click_button('Make'),
|
||||
() => frappe.timeout(2),
|
||||
() => frappe.click_link('Payment'),
|
||||
() => frappe.timeout(3),
|
||||
() => cur_frm.set_value('mode_of_payment','Cash'),
|
||||
() => frappe.timeout(3),
|
||||
() => {
|
||||
assert.equal(frappe.get_route()[1], 'Payment Entry',
|
||||
'made payment entry');
|
||||
assert.equal(cur_frm.doc.party, 'Test Supplier',
|
||||
'supplier set in payment entry');
|
||||
assert.equal(cur_frm.doc.paid_amount, 2000,
|
||||
'paid amount set in payment entry');
|
||||
assert.equal(cur_frm.doc.references[0].allocated_amount, 2000,
|
||||
'amount allocated against purchase invoice');
|
||||
assert.equal(cur_frm.doc.references[0].bill_no, 'in1234',
|
||||
'invoice number allocated against purchase invoice');
|
||||
assert.equal(cur_frm.get_field('total_allocated_amount').value, 2000,
|
||||
'correct amount allocated in Write Off');
|
||||
assert.equal(cur_frm.get_field('unallocated_amount').value, 0,
|
||||
'correct amount unallocated in Write Off');
|
||||
},
|
||||
|
||||
() => cur_frm.save(),
|
||||
() => frappe.tests.click_button('Submit'),
|
||||
() => frappe.tests.click_button('Yes'),
|
||||
() => frappe.timeout(3),
|
||||
() => done()
|
||||
]);
|
||||
});
|
||||
@@ -296,7 +296,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-09-04 17:37:01.192312",
|
||||
"modified": "2017-10-16 17:37:01.192312",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Payment Entry Reference",
|
||||
@@ -311,4 +311,4 @@
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1,
|
||||
"track_seen": 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,10 +37,10 @@ frappe.ui.form.on('POS Profile', {
|
||||
return { filters: { doc_type: "Sales Invoice", print_format_type: "Js"} };
|
||||
});
|
||||
|
||||
frappe.db.get_value('POS Settings', {name: 'POS Settings'}, 'is_online', (r) => {
|
||||
is_online = r && cint(r.is_online)
|
||||
frm.toggle_display('offline_pos_section', !is_online);
|
||||
frm.toggle_display('print_format_for_online', is_online);
|
||||
frappe.db.get_value('POS Settings', {name: 'POS Settings'}, 'use_pos_in_offline_mode', (r) => {
|
||||
is_offline = r && cint(r.use_pos_in_offline_mode)
|
||||
frm.toggle_display('offline_pos_section', is_offline);
|
||||
frm.toggle_display('print_format_for_online', !is_offline);
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
@@ -3,7 +3,17 @@
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class POSSettings(Document):
|
||||
pass
|
||||
def validate(self):
|
||||
self.set_link_for_pos()
|
||||
|
||||
def set_link_for_pos(self):
|
||||
link = 'pos' if self.use_pos_in_offline_mode else 'point-of-sale'
|
||||
desktop_icon = frappe.db.get_value('Desktop Icon',
|
||||
{'standard': 1, 'module_name': 'POS'}, 'name')
|
||||
|
||||
if desktop_icon:
|
||||
frappe.db.set_value('Desktop Icon', desktop_icon, 'link', link)
|
||||
@@ -1023,7 +1023,7 @@
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "2",
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
@@ -1284,7 +1284,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-08-31 16:34:41.614743",
|
||||
"modified": "2017-09-27 08:31:38.432574",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Pricing Rule",
|
||||
|
||||
@@ -348,6 +348,8 @@ def apply_internal_priority(pricing_rules, field_set, args):
|
||||
return filtered_rules or pricing_rules
|
||||
|
||||
def set_transaction_type(args):
|
||||
if args.transaction_type:
|
||||
return
|
||||
if args.doctype in ("Opportunity", "Quotation", "Sales Order", "Delivery Note", "Sales Invoice"):
|
||||
args.transaction_type = "selling"
|
||||
elif args.doctype in ("Material Request", "Supplier Quotation", "Purchase Order",
|
||||
@@ -356,4 +358,4 @@ def set_transaction_type(args):
|
||||
elif args.customer:
|
||||
args.transaction_type = "selling"
|
||||
else:
|
||||
args.transaction_type = "buying"
|
||||
args.transaction_type = "buying"
|
||||
|
||||
@@ -3440,139 +3440,13 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "subscription",
|
||||
"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": "Subscription",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Subscription",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"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": 1,
|
||||
"collapsible_depends_on": "is_recurring",
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.docstatus<2 && !doc.__islocal",
|
||||
"fieldname": "recurring_invoice",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Recurring Invoice",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "fa fa-time",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.docstatus<2",
|
||||
"description": "",
|
||||
"fieldname": "is_recurring",
|
||||
"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": "Is Recurring",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "Select the period when the invoice will be generated automatically",
|
||||
"fieldname": "recurring_type",
|
||||
"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": "Recurring Type",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Monthly\nQuarterly\nHalf-yearly\nYearly",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"depends_on": "",
|
||||
"description": "Start date of current invoice's period",
|
||||
"fieldname": "from_date",
|
||||
"fieldtype": "Date",
|
||||
@@ -3603,7 +3477,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"depends_on": "",
|
||||
"description": "End date of current invoice's period",
|
||||
"fieldname": "to_date",
|
||||
"fieldtype": "Date",
|
||||
@@ -3628,138 +3502,13 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name",
|
||||
"fieldname": "submit_on_creation",
|
||||
"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": "Submit on creation",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name",
|
||||
"description": "",
|
||||
"fieldname": "notify_by_email",
|
||||
"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": "Notify by email",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "The day of the month on which auto invoice will be generated e.g. 05, 28 etc",
|
||||
"fieldname": "repeat_on_day_of_month",
|
||||
"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": "Repeat on Day of Month",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "The date on which recurring invoice will be stop",
|
||||
"fieldname": "end_date",
|
||||
"fieldtype": "Date",
|
||||
"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": "End Date",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_82",
|
||||
"fieldname": "column_break_114",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
@@ -3771,101 +3520,8 @@
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0,
|
||||
"width": "50%"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "The date on which next invoice will be generated. It is generated on submit.",
|
||||
"fieldname": "next_date",
|
||||
"fieldtype": "Date",
|
||||
"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 Date",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"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,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "The unique id for tracking all recurring invoices. It is generated on submit.",
|
||||
"fieldname": "recurring_id",
|
||||
"fieldtype": "Data",
|
||||
"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": "Recurring Id",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"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": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"description": "Enter Email Address separated by commas, invoice will be mailed automatically on particular date",
|
||||
"fieldname": "notification_email_address",
|
||||
"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": "Notification Email Address",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
@@ -3881,8 +3537,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.is_recurring==1",
|
||||
"fieldname": "recurring_print_format",
|
||||
"fieldname": "subscription",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
@@ -3891,15 +3546,15 @@
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Recurring Print Format",
|
||||
"label": "Subscription",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Print Format",
|
||||
"no_copy": 1,
|
||||
"options": "Subscription",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
@@ -3920,7 +3575,7 @@
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2017-09-19 11:22:47.074420",
|
||||
"modified": "2017-10-24 12:51:51.199594",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Invoice",
|
||||
|
||||
@@ -22,8 +22,8 @@ form_grid_templates = {
|
||||
}
|
||||
|
||||
class PurchaseInvoice(BuyingController):
|
||||
def __init__(self, arg1, arg2=None):
|
||||
super(PurchaseInvoice, self).__init__(arg1, arg2)
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(PurchaseInvoice, self).__init__(*args, **kwargs)
|
||||
self.status_updater = [{
|
||||
'source_dt': 'Purchase Invoice Item',
|
||||
'target_dt': 'Purchase Order Item',
|
||||
|
||||
@@ -7,6 +7,7 @@ QUnit.test("test purchase invoice", function(assert) {
|
||||
() => {
|
||||
return frappe.tests.make('Purchase Invoice', [
|
||||
{supplier: 'Test Supplier'},
|
||||
{bill_no: 'in123'},
|
||||
{items: [
|
||||
[
|
||||
{'qty': 5},
|
||||
@@ -36,7 +37,7 @@ QUnit.test("test purchase invoice", function(assert) {
|
||||
},
|
||||
() => frappe.tests.click_button('Submit'),
|
||||
() => frappe.tests.click_button('Yes'),
|
||||
() => frappe.timeout(0.3),
|
||||
() => frappe.timeout(1),
|
||||
() => done()
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -473,6 +473,7 @@ class TestPurchaseInvoice(unittest.TestCase):
|
||||
import test_records as jv_test_records
|
||||
|
||||
jv = frappe.copy_doc(jv_test_records[1])
|
||||
jv.accounts[0].is_advance = 'Yes'
|
||||
jv.insert()
|
||||
jv.submit()
|
||||
|
||||
|
||||
@@ -88,7 +88,7 @@ def update_pos_profile_data(doc, pos_profile, company_data):
|
||||
doc.naming_series = pos_profile.get('naming_series') or 'SINV-'
|
||||
doc.letter_head = pos_profile.get('letter_head') or company_data.default_letter_head
|
||||
doc.ignore_pricing_rule = pos_profile.get('ignore_pricing_rule') or 0
|
||||
doc.apply_discount_on = pos_profile.get('apply_discount_on') if pos_profile.get('apply_discount') else ''
|
||||
doc.apply_discount_on = pos_profile.get('apply_discount_on') or 'Grand Total'
|
||||
doc.customer_group = pos_profile.get('customer_group') or get_root('Customer Group')
|
||||
doc.territory = pos_profile.get('territory') or get_root('Territory')
|
||||
doc.terms = frappe.db.get_value('Terms and Conditions', pos_profile.get('tc_name'), 'terms') or doc.terms or ''
|
||||
@@ -417,6 +417,7 @@ def make_contact(args,customer):
|
||||
'link_doctype': 'Customer',
|
||||
'link_name': customer
|
||||
})
|
||||
doc.flags.ignore_mandatory = True
|
||||
doc.save(ignore_permissions=True)
|
||||
|
||||
def make_address(args, customer):
|
||||
@@ -441,6 +442,7 @@ def make_address(args, customer):
|
||||
address.is_primary_address = 1
|
||||
address.is_shipping_address = 1
|
||||
address.update(args)
|
||||
address.flags.ignore_mandatory = True
|
||||
address.save(ignore_permissions = True)
|
||||
|
||||
def make_email_queue(email_queue):
|
||||
@@ -484,17 +486,21 @@ def submit_invoice(si_doc, name, doc, name_list):
|
||||
if frappe.message_log: frappe.message_log.pop()
|
||||
frappe.db.rollback()
|
||||
frappe.log_error(frappe.get_traceback())
|
||||
name_list = save_invoice(e, si_doc, name, name_list)
|
||||
name_list = save_invoice(doc, name, name_list)
|
||||
|
||||
return name_list
|
||||
|
||||
def save_invoice(e, si_doc, name, name_list):
|
||||
def save_invoice(doc, name, name_list):
|
||||
try:
|
||||
if not frappe.db.exists('Sales Invoice', {'offline_pos_name': name}):
|
||||
si_doc.docstatus = 0
|
||||
si_doc.flags.ignore_mandatory = True
|
||||
si_doc.due_date = si_doc.posting_date
|
||||
si_doc.insert()
|
||||
si = frappe.new_doc('Sales Invoice')
|
||||
si.update(doc)
|
||||
si.set_posting_time = 1
|
||||
si.customer = get_customer_id(doc)
|
||||
si.due_date = doc.get('posting_date')
|
||||
si.flags.ignore_mandatory = True
|
||||
si.insert(ignore_permissions=True)
|
||||
frappe.db.commit()
|
||||
name_list.append(name)
|
||||
except Exception:
|
||||
frappe.log_error(frappe.get_traceback())
|
||||
|
||||
@@ -339,7 +339,7 @@ $.extend(cur_frm.cscript, new erpnext.accounts.SalesInvoiceController({frm: cur_
|
||||
// ------------
|
||||
cur_frm.cscript.hide_fields = function(doc) {
|
||||
var parent_fields = ['project', 'due_date', 'is_opening', 'source', 'total_advance', 'get_advances',
|
||||
'advances', 'sales_partner', 'commission_rate', 'total_commission', 'advances', 'from_date', 'to_date'];
|
||||
'advances', 'advances', 'from_date', 'to_date'];
|
||||
|
||||
if(cint(doc.is_pos) == 1) {
|
||||
hide_field(parent_fields);
|
||||
@@ -520,6 +520,24 @@ frappe.ui.form.on('Sales Invoice', {
|
||||
};
|
||||
});
|
||||
},
|
||||
//When multiple companies are set up. in case company name is changed set default company address
|
||||
company:function(frm){
|
||||
if (frm.doc.company)
|
||||
{
|
||||
frappe.call({
|
||||
method:"frappe.contacts.doctype.address.address.get_default_address",
|
||||
args:{ doctype:'Company',name:frm.doc.company},
|
||||
callback: function(r){
|
||||
if (r.message){
|
||||
frm.set_value("company_address",r.message)
|
||||
}
|
||||
else {
|
||||
frm.set_value("company_address","")
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
project: function(frm){
|
||||
frm.call({
|
||||
|
||||
@@ -4273,414 +4273,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "subscription",
|
||||
"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": "Subscription",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Subscription",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"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": 1,
|
||||
"collapsible_depends_on": "is_recurring",
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.docstatus<2 && !doc.__islocal",
|
||||
"fieldname": "recurring_invoice",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Recurring Invoice",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "fa fa-time",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break11",
|
||||
"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,
|
||||
"label": "Settings",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0,
|
||||
"width": "50%"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.docstatus<2",
|
||||
"description": "",
|
||||
"fieldname": "is_recurring",
|
||||
"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": "Is Recurring",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "is_recurring",
|
||||
"description": "",
|
||||
"fieldname": "recurring_id",
|
||||
"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": "Reference Document",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Sales Invoice",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"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": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name",
|
||||
"description": "",
|
||||
"fieldname": "recurring_type",
|
||||
"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": "Frequency",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "\nMonthly\nQuarterly\nHalf-yearly\nYearly",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name",
|
||||
"description": "",
|
||||
"fieldname": "repeat_on_day_of_month",
|
||||
"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": "Repeat on Day of Month",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name",
|
||||
"description": "",
|
||||
"fieldname": "end_date",
|
||||
"fieldtype": "Date",
|
||||
"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": "End Date",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name",
|
||||
"fieldname": "submit_on_creation",
|
||||
"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": "Submit on creation",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name",
|
||||
"description": "",
|
||||
"fieldname": "notify_by_email",
|
||||
"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": "Notify by email",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.is_recurring && doc.notify_by_email && doc.recurring_id === doc.name",
|
||||
"description": "",
|
||||
"fieldname": "notification_email_address",
|
||||
"fieldtype": "Code",
|
||||
"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": "Notification Email Address",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Email",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.is_recurring && doc.notify_by_email && doc.recurring_id === doc.name",
|
||||
"fieldname": "recurring_print_format",
|
||||
"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": "Recurring Print Format",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Print Format",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break12",
|
||||
"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,
|
||||
"label": "This Document",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0,
|
||||
"width": "50%"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "is_recurring",
|
||||
"depends_on": "",
|
||||
"description": "",
|
||||
"fieldname": "from_date",
|
||||
"fieldtype": "Date",
|
||||
@@ -4711,7 +4304,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "is_recurring",
|
||||
"depends_on": "",
|
||||
"description": "",
|
||||
"fieldname": "to_date",
|
||||
"fieldtype": "Date",
|
||||
@@ -4742,10 +4335,8 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "is_recurring",
|
||||
"description": "",
|
||||
"fieldname": "next_date",
|
||||
"fieldtype": "Date",
|
||||
"fieldname": "column_break_140",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
@@ -4753,11 +4344,11 @@
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Next Date",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
@@ -4767,6 +4358,37 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "subscription",
|
||||
"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": "Subscription",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Subscription",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"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,
|
||||
@@ -4811,7 +4433,7 @@
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"menu_index": 0,
|
||||
"modified": "2017-09-19 11:23:08.675028",
|
||||
"modified": "2017-10-24 12:46:48.331723",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice",
|
||||
|
||||
@@ -27,8 +27,8 @@ form_grid_templates = {
|
||||
}
|
||||
|
||||
class SalesInvoice(SellingController):
|
||||
def __init__(self, arg1, arg2=None):
|
||||
super(SalesInvoice, self).__init__(arg1, arg2)
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(SalesInvoice, self).__init__(*args, **kwargs)
|
||||
self.status_updater = [{
|
||||
'source_dt': 'Sales Invoice Item',
|
||||
'target_field': 'billed_amt',
|
||||
@@ -70,6 +70,7 @@ class SalesInvoice(SellingController):
|
||||
self.clear_unallocated_advances("Sales Invoice Advance", "advances")
|
||||
self.add_remarks()
|
||||
self.validate_write_off_account()
|
||||
self.validate_duplicate_offline_pos_entry()
|
||||
self.validate_account_for_change_amount()
|
||||
self.validate_fixed_asset()
|
||||
self.set_income_account_for_fixed_assets()
|
||||
@@ -462,6 +463,12 @@ class SalesInvoice(SellingController):
|
||||
if flt(self.write_off_amount) and not self.write_off_account:
|
||||
msgprint(_("Please enter Write Off Account"), raise_exception=1)
|
||||
|
||||
def validate_duplicate_offline_pos_entry(self):
|
||||
if self.is_pos and self.offline_pos_name \
|
||||
and frappe.db.get_value('Sales Invoice',
|
||||
{'offline_pos_name': self.offline_pos_name, 'docstatus': 1}):
|
||||
frappe.throw(_("Duplicate offline pos sales invoice {0}").format(self.offline_pos_name))
|
||||
|
||||
def validate_account_for_change_amount(self):
|
||||
if flt(self.change_amount) and not self.account_for_change_amount:
|
||||
msgprint(_("Please enter Account for Change Amount"), raise_exception=1)
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
import unittest, copy
|
||||
from frappe.utils import nowdate, add_days, flt
|
||||
import unittest, copy, time
|
||||
from frappe.utils import nowdate, add_days, flt, cint
|
||||
from frappe.model.dynamic_links import get_dynamic_link_map
|
||||
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry, get_qty_after_transaction
|
||||
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import unlink_payment_on_cancel_of_invoice
|
||||
@@ -665,6 +665,47 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
|
||||
self.pos_gl_entry(si, pos, 330)
|
||||
|
||||
def test_make_pos_invoice_in_draft(self):
|
||||
from erpnext.accounts.doctype.sales_invoice.pos import make_invoice
|
||||
from erpnext.stock.doctype.item.test_item import make_item
|
||||
|
||||
set_perpetual_inventory()
|
||||
|
||||
allow_negative_stock = frappe.db.get_single_value('Stock Settings', 'allow_negative_stock')
|
||||
if allow_negative_stock:
|
||||
frappe.db.set_value('Stock Settings', None, 'allow_negative_stock', 0)
|
||||
|
||||
make_pos_profile()
|
||||
timestamp = cint(time.time())
|
||||
|
||||
item = make_item("_Test POS Item")
|
||||
pos = copy.deepcopy(test_records[1])
|
||||
pos['items'][0]['item_code'] = item.name
|
||||
pos["is_pos"] = 1
|
||||
pos["offline_pos_name"] = timestamp
|
||||
pos["update_stock"] = 1
|
||||
pos["payments"] = [{'mode_of_payment': 'Bank Draft', 'account': '_Test Bank - _TC', 'amount': 300},
|
||||
{'mode_of_payment': 'Cash', 'account': 'Cash - _TC', 'amount': 330}]
|
||||
|
||||
invoice_data = [{timestamp: pos}]
|
||||
si = make_invoice(invoice_data).get('invoice')
|
||||
self.assertEquals(si[0], timestamp)
|
||||
|
||||
sales_invoice = frappe.get_all('Sales Invoice', fields =["*"], filters = {'offline_pos_name': timestamp})
|
||||
self.assertEquals(sales_invoice[0].docstatus, 0)
|
||||
|
||||
timestamp = cint(time.time())
|
||||
pos["offline_pos_name"] = timestamp
|
||||
invoice_data = [{timestamp: pos}]
|
||||
si1 = make_invoice(invoice_data).get('invoice')
|
||||
self.assertEquals(si1[0], timestamp)
|
||||
|
||||
sales_invoice1 = frappe.get_all('Sales Invoice', fields =["*"], filters = {'offline_pos_name': timestamp})
|
||||
self.assertEquals(sales_invoice1[0].docstatus, 0)
|
||||
|
||||
if allow_negative_stock:
|
||||
frappe.db.set_value('Stock Settings', None, 'allow_negative_stock', 1)
|
||||
|
||||
def pos_gl_entry(self, si, pos, cash_amount):
|
||||
# check stock ledger entries
|
||||
sle = frappe.db.sql("""select * from `tabStock Ledger Entry`
|
||||
@@ -1084,7 +1125,7 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
si.items[0].price_list_rate = price_list_rate
|
||||
si.items[0].margin_type = 'Percentage'
|
||||
si.items[0].margin_rate_or_amount = 25
|
||||
si.insert()
|
||||
si.save()
|
||||
self.assertEqual(si.get("items")[0].rate, flt((price_list_rate*25)/100 + price_list_rate))
|
||||
|
||||
def test_outstanding_amount_after_advance_jv_cancelation(self):
|
||||
@@ -1092,6 +1133,7 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
import test_records as jv_test_records
|
||||
|
||||
jv = frappe.copy_doc(jv_test_records[0])
|
||||
jv.accounts[0].is_advance = 'Yes'
|
||||
jv.insert()
|
||||
jv.submit()
|
||||
|
||||
|
||||
@@ -699,7 +699,7 @@
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "2",
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
@@ -2166,7 +2166,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-07-17 17:54:48.246507",
|
||||
"modified": "2017-09-27 08:31:37.827893",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice Item",
|
||||
|
||||
@@ -3,10 +3,17 @@
|
||||
|
||||
frappe.ui.form.on('Subscription', {
|
||||
setup: function(frm) {
|
||||
frm.fields_dict['reference_doctype'].get_query = function(doc) {
|
||||
return {
|
||||
query: "erpnext.accounts.doctype.subscription.subscription.subscription_doctype_query"
|
||||
};
|
||||
};
|
||||
|
||||
frm.fields_dict['reference_document'].get_query = function() {
|
||||
return {
|
||||
filters: {
|
||||
"docstatus": 1
|
||||
"docstatus": 1,
|
||||
"subscription": ''
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
@@ -135,66 +135,6 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "disabled",
|
||||
"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": "Disabled",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"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": "submit_on_creation",
|
||||
"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": "Submit on Creation",
|
||||
"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,
|
||||
@@ -286,12 +226,12 @@
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "next_schedule_date",
|
||||
"fieldtype": "Date",
|
||||
"fieldname": "submit_on_creation",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
@@ -299,14 +239,44 @@
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Next Schedule Date",
|
||||
"label": "Submit on Creation",
|
||||
"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": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "disabled",
|
||||
"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": "Disabled",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
@@ -320,7 +290,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "frequency_detail",
|
||||
"fieldname": "section_break_10",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
@@ -329,7 +299,6 @@
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
@@ -381,7 +350,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "column_break_12",
|
||||
"fieldname": "column_break_13",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
@@ -437,10 +406,40 @@
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "next_schedule_date",
|
||||
"fieldtype": "Date",
|
||||
"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 Schedule Date",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"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": 1,
|
||||
"columns": 0,
|
||||
"fieldname": "notification",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
@@ -495,6 +494,38 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval: doc.notify_by_email",
|
||||
"description": "To add dynamic subject, use jinja tags like\n\n<div><pre><code>New {{ doc.doctype }} #{{ doc.name }}</code></pre></div>",
|
||||
"fieldname": "subject",
|
||||
"fieldtype": "Data",
|
||||
"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": "Subject",
|
||||
"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,
|
||||
@@ -593,6 +624,69 @@
|
||||
"bold": 0,
|
||||
"collapsible": 1,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.notify_by_email",
|
||||
"fieldname": "section_break_20",
|
||||
"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": "Message",
|
||||
"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,
|
||||
"default": "Please find attached {{ doc.doctype }} #{{ doc.name }}",
|
||||
"fieldname": "message",
|
||||
"fieldtype": "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": "Message",
|
||||
"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": 1,
|
||||
"columns": 0,
|
||||
"depends_on": "eval: !doc.__islocal",
|
||||
"fieldname": "section_break_16",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 0,
|
||||
@@ -690,7 +784,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-09-14 12:09:38.471458",
|
||||
"modified": "2017-10-23 18:28:08.966403",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Subscription",
|
||||
@@ -700,7 +794,7 @@
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
@@ -720,7 +814,7 @@
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
@@ -740,7 +834,7 @@
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
|
||||
@@ -7,21 +7,28 @@ import frappe
|
||||
import calendar
|
||||
from frappe import _
|
||||
from frappe.desk.form import assign_to
|
||||
from frappe.utils.jinja import validate_template
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from frappe.utils.user import get_system_managers
|
||||
from frappe.utils import cstr, getdate, split_emails, add_days, today
|
||||
from frappe.utils import cstr, getdate, split_emails, add_days, today, get_last_day, get_first_day
|
||||
from frappe.model.document import Document
|
||||
|
||||
month_map = {'Monthly': 1, 'Quarterly': 3, 'Half-yearly': 6, 'Yearly': 12}
|
||||
class Subscription(Document):
|
||||
def validate(self):
|
||||
self.update_status()
|
||||
self.validate_reference_doctype()
|
||||
self.validate_dates()
|
||||
self.validate_next_schedule_date()
|
||||
self.validate_email_id()
|
||||
|
||||
validate_template(self.subject or "")
|
||||
validate_template(self.message or "")
|
||||
|
||||
def before_submit(self):
|
||||
self.set_next_schedule_date()
|
||||
if not self.next_schedule_date:
|
||||
self.next_schedule_date = get_next_schedule_date(self.start_date,
|
||||
self.frequency, self.repeat_on_day)
|
||||
|
||||
def on_submit(self):
|
||||
self.update_subscription_id()
|
||||
@@ -30,6 +37,18 @@ class Subscription(Document):
|
||||
self.validate_dates()
|
||||
self.set_next_schedule_date()
|
||||
|
||||
def before_cancel(self):
|
||||
self.unlink_subscription_id()
|
||||
self.next_schedule_date = None
|
||||
|
||||
def unlink_subscription_id(self):
|
||||
frappe.db.sql("update `tab{0}` set subscription = null where subscription=%s"
|
||||
.format(self.reference_doctype), self.name)
|
||||
|
||||
def validate_reference_doctype(self):
|
||||
if not frappe.get_meta(self.reference_doctype).has_field('subscription'):
|
||||
frappe.throw(_("Add custom field Subscription in the doctype {0}").format(self.reference_doctype))
|
||||
|
||||
def validate_dates(self):
|
||||
if self.end_date and getdate(self.start_date) > getdate(self.end_date):
|
||||
frappe.throw(_("End date must be greater than start date"))
|
||||
@@ -61,15 +80,11 @@ class Subscription(Document):
|
||||
frappe.throw(_("'Recipients' not specified"))
|
||||
|
||||
def set_next_schedule_date(self):
|
||||
self.next_schedule_date = get_next_schedule_date(self.start_date,
|
||||
self.frequency, self.repeat_on_day)
|
||||
if self.repeat_on_day:
|
||||
self.next_schedule_date = get_next_date(self.next_schedule_date, 0, self.repeat_on_day)
|
||||
|
||||
def update_subscription_id(self):
|
||||
doc = frappe.get_doc(self.reference_doctype, self.reference_document)
|
||||
if not doc.meta.get_field('subscription'):
|
||||
frappe.throw(_("Add custom field Subscription Id in the doctype {0}").format(self.reference_doctype))
|
||||
|
||||
doc.db_set('subscription', self.name)
|
||||
frappe.db.set_value(self.reference_doctype, self.reference_document, "subscription", self.name)
|
||||
|
||||
def update_status(self, status=None):
|
||||
self.status = {
|
||||
@@ -114,19 +129,19 @@ def create_documents(data, schedule_date):
|
||||
doc = make_new_document(data, schedule_date)
|
||||
if data.notify_by_email and data.recipients:
|
||||
print_format = data.print_format or "Standard"
|
||||
send_notification(doc, print_format, data.recipients)
|
||||
send_notification(doc, data, print_format=print_format)
|
||||
|
||||
frappe.db.commit()
|
||||
except Exception:
|
||||
frappe.db.rollback()
|
||||
frappe.db.begin()
|
||||
frappe.log_error(frappe.get_traceback())
|
||||
disabled_subscription(data)
|
||||
disable_subscription(data)
|
||||
frappe.db.commit()
|
||||
if data.reference_document and not frappe.flags.in_test:
|
||||
notify_error_to_user(data)
|
||||
|
||||
def disabled_subscription(data):
|
||||
def disable_subscription(data):
|
||||
subscription = frappe.get_doc('Subscription', data.name)
|
||||
subscription.db_set('disabled', 1)
|
||||
|
||||
@@ -160,28 +175,79 @@ def update_doc(new_document, reference_doc, args, schedule_date):
|
||||
if new_document.meta.get_field('set_posting_time'):
|
||||
new_document.set('set_posting_time', 1)
|
||||
|
||||
mcount = month_map.get(args.frequency)
|
||||
|
||||
if new_document.meta.get_field('subscription'):
|
||||
new_document.set('subscription', args.name)
|
||||
|
||||
new_document.run_method("on_recurring", reference_doc=reference_doc, subscription_doc=args)
|
||||
for fieldname in ['naming_series', 'ignore_pricing_rule', 'posting_time'
|
||||
'select_print_heading', 'remarks', 'owner']:
|
||||
if new_document.meta.get_field(fieldname):
|
||||
new_document.set(fieldname, reference_doc.get(fieldname))
|
||||
|
||||
# copy item fields
|
||||
if new_document.meta.get_field('items'):
|
||||
for i, item in enumerate(new_document.items):
|
||||
for fieldname in ("page_break",):
|
||||
item.set(fieldname, reference_doc.items[i].get(fieldname))
|
||||
|
||||
for data in new_document.meta.fields:
|
||||
if data.fieldtype == 'Date' and data.reqd:
|
||||
new_document.set(data.fieldname, schedule_date)
|
||||
|
||||
set_subscription_period(args, mcount, new_document)
|
||||
|
||||
new_document.run_method("on_recurring", reference_doc=reference_doc, subscription_doc=args)
|
||||
|
||||
def set_subscription_period(args, mcount, new_document):
|
||||
if mcount and new_document.meta.get_field('from_date') and new_document.meta.get_field('to_date'):
|
||||
last_ref_doc = frappe.db.sql("""
|
||||
select name, from_date, to_date
|
||||
from `tab{0}`
|
||||
where subscription=%s and docstatus < 2
|
||||
order by creation desc
|
||||
limit 1
|
||||
""".format(args.reference_doctype), args.name, as_dict=1)
|
||||
|
||||
if not last_ref_doc:
|
||||
return
|
||||
|
||||
from_date = get_next_date(last_ref_doc[0].from_date, mcount)
|
||||
|
||||
if (cstr(get_first_day(last_ref_doc[0].from_date)) == cstr(last_ref_doc[0].from_date)) and \
|
||||
(cstr(get_last_day(last_ref_doc[0].to_date)) == cstr(last_ref_doc[0].to_date)):
|
||||
to_date = get_last_day(get_next_date(last_ref_doc[0].to_date, mcount))
|
||||
else:
|
||||
to_date = get_next_date(last_ref_doc[0].to_date, mcount)
|
||||
|
||||
new_document.set('from_date', from_date)
|
||||
new_document.set('to_date', to_date)
|
||||
|
||||
def get_next_date(dt, mcount, day=None):
|
||||
dt = getdate(dt)
|
||||
dt += relativedelta(months=mcount, day=day)
|
||||
|
||||
return dt
|
||||
|
||||
def send_notification(new_rv, print_format='Standard', recipients=None):
|
||||
def send_notification(new_rv, subscription_doc, print_format='Standard'):
|
||||
"""Notify concerned persons about recurring document generation"""
|
||||
print_format = print_format
|
||||
|
||||
frappe.sendmail(recipients,
|
||||
subject= _("New {0}: #{1}").format(new_rv.doctype, new_rv.name),
|
||||
message = _("Please find attached {0} #{1}").format(new_rv.doctype, new_rv.name),
|
||||
attachments = [frappe.attach_print(new_rv.doctype, new_rv.name, file_name=new_rv.name, print_format=print_format)])
|
||||
if not subscription_doc.subject:
|
||||
subject = _("New {0}: #{1}").format(new_rv.doctype, new_rv.name)
|
||||
elif "{" in subscription_doc.subject:
|
||||
subject = frappe.render_template(subscription_doc.subject, {'doc': new_rv})
|
||||
|
||||
if not subscription_doc.message:
|
||||
message = _("Please find attached {0} #{1}").format(new_rv.doctype, new_rv.name)
|
||||
elif "{" in subscription_doc.message:
|
||||
message = frappe.render_template(subscription_doc.message, {'doc': new_rv})
|
||||
|
||||
attachments = [frappe.attach_print(new_rv.doctype, new_rv.name,
|
||||
file_name=new_rv.name, print_format=print_format)]
|
||||
|
||||
frappe.sendmail(subscription_doc.recipients,
|
||||
subject=subject, message=message, attachments=attachments)
|
||||
|
||||
def notify_errors(doc, doctype, party, owner, name):
|
||||
recipients = get_system_managers(only_name=True)
|
||||
@@ -210,8 +276,11 @@ def assign_task_to_owner(name, msg, users):
|
||||
@frappe.whitelist()
|
||||
def make_subscription(doctype, docname):
|
||||
doc = frappe.new_doc('Subscription')
|
||||
|
||||
reference_doc = frappe.get_doc(doctype, docname)
|
||||
doc.reference_doctype = doctype
|
||||
doc.reference_document = docname
|
||||
doc.start_date = reference_doc.get('posting_date') or reference_doc.get('transaction_date')
|
||||
return doc
|
||||
|
||||
@frappe.whitelist()
|
||||
@@ -225,4 +294,20 @@ def stop_resume_subscription(subscription, status):
|
||||
doc.update_status(status)
|
||||
doc.save()
|
||||
|
||||
return doc.status
|
||||
return doc.status
|
||||
|
||||
def subscription_doctype_query(doctype, txt, searchfield, start, page_len, filters):
|
||||
return frappe.db.sql("""select parent from `tabDocField`
|
||||
where fieldname = 'subscription'
|
||||
and parent like %(txt)s
|
||||
order by
|
||||
if(locate(%(_txt)s, parent), locate(%(_txt)s, parent), 99999),
|
||||
parent
|
||||
limit %(start)s, %(page_len)s""".format(**{
|
||||
'key': searchfield,
|
||||
}), {
|
||||
'txt': "%%%s%%" % txt,
|
||||
'_txt': txt.replace("%", ""),
|
||||
'start': start,
|
||||
'page_len': page_len
|
||||
})
|
||||
@@ -30,7 +30,7 @@ class TestSubscription(unittest.TestCase):
|
||||
|
||||
new_quotation = frappe.get_doc('Quotation', new_quotation)
|
||||
|
||||
for fieldname in ['customer', 'company', 'order_type', 'total', 'grand_total']:
|
||||
for fieldname in ['customer', 'company', 'order_type', 'total', 'net_total']:
|
||||
self.assertEquals(quotation.get(fieldname), new_quotation.get(fieldname))
|
||||
|
||||
for fieldname in ['item_code', 'qty', 'rate', 'amount']:
|
||||
|
||||
@@ -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 frappe.utils.nestedset import get_root_of
|
||||
from erpnext.setup.doctype.customer_group.customer_group import get_parent_customer_groups
|
||||
|
||||
class IncorrectCustomerGroup(frappe.ValidationError): pass
|
||||
@@ -136,7 +137,7 @@ def get_tax_template(posting_date, args):
|
||||
if key=="use_for_shopping_cart":
|
||||
conditions.append("use_for_shopping_cart = {0}".format(1 if value else 0))
|
||||
if key == 'customer_group':
|
||||
if not value: value = _("All Customer Groups")
|
||||
if not value: value = get_root_of("Customer Group")
|
||||
customer_group_condition = get_customer_group_condition(value)
|
||||
conditions.append("ifnull({0}, '') in ('', {1})".format(key, customer_group_condition))
|
||||
else:
|
||||
|
||||
@@ -11,7 +11,10 @@ test_records = frappe.get_test_records('Tax Rule')
|
||||
|
||||
class TestTaxRule(unittest.TestCase):
|
||||
def setUp(self):
|
||||
frappe.db.sql("delete from `tabTax Rule` where use_for_shopping_cart <> 1")
|
||||
frappe.db.sql("delete from `tabTax Rule`")
|
||||
|
||||
def tearDown(self):
|
||||
frappe.db.sql("delete from `tabTax Rule`")
|
||||
|
||||
def test_conflict(self):
|
||||
tax_rule1 = make_tax_rule(customer= "_Test Customer",
|
||||
|
||||
@@ -84,6 +84,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
||||
this.get_data_from_server(function () {
|
||||
me.make_control();
|
||||
me.create_new();
|
||||
me.make();
|
||||
});
|
||||
},
|
||||
|
||||
@@ -113,6 +114,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
||||
});
|
||||
|
||||
this.page.add_menu_item(__("Sync Offline Invoices"), function () {
|
||||
me.freeze_screen = true;
|
||||
me.sync_sales_invoice()
|
||||
});
|
||||
|
||||
@@ -381,7 +383,6 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
||||
},
|
||||
|
||||
setup: function () {
|
||||
this.make();
|
||||
this.set_primary_action();
|
||||
this.party_field.$input.attr('disabled', false);
|
||||
if(this.selected_row) {
|
||||
@@ -1340,6 +1341,12 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
||||
|
||||
this.wrapper.find('input.discount-percentage').on("change", function () {
|
||||
me.frm.doc.additional_discount_percentage = flt($(this).val(), precision("additional_discount_percentage"));
|
||||
|
||||
if(me.frm.doc.additional_discount_percentage && me.frm.doc.discount_amount) {
|
||||
// Reset discount amount
|
||||
me.frm.doc.discount_amount = 0;
|
||||
}
|
||||
|
||||
var total = me.frm.doc.grand_total
|
||||
|
||||
if (me.frm.doc.apply_discount_on == 'Net Total') {
|
||||
@@ -1347,15 +1354,15 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
||||
}
|
||||
|
||||
me.frm.doc.discount_amount = flt(total * flt(me.frm.doc.additional_discount_percentage) / 100, precision("discount_amount"));
|
||||
me.wrapper.find('input.discount-amount').val(me.frm.doc.discount_amount)
|
||||
me.refresh();
|
||||
me.wrapper.find('input.discount-amount').val(me.frm.doc.discount_amount)
|
||||
});
|
||||
|
||||
this.wrapper.find('input.discount-amount').on("change", function () {
|
||||
me.frm.doc.discount_amount = flt($(this).val(), precision("discount_amount"));
|
||||
me.frm.doc.additional_discount_percentage = 0.0;
|
||||
me.wrapper.find('input.discount-percentage').val(0);
|
||||
me.refresh();
|
||||
me.wrapper.find('input.discount-percentage').val(0);
|
||||
});
|
||||
},
|
||||
|
||||
@@ -1516,6 +1523,8 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
||||
var me = this;
|
||||
this.wrapper.find(".net-total").text(format_currency(me.frm.doc.total, me.frm.doc.currency));
|
||||
this.wrapper.find(".grand-total").text(format_currency(me.frm.doc.grand_total, me.frm.doc.currency));
|
||||
this.wrapper.find('input.discount-percentage').val(this.frm.doc.additional_discount_percentage);
|
||||
this.wrapper.find('input.discount-amount').val(this.frm.doc.discount_amount);
|
||||
},
|
||||
|
||||
set_primary_action: function () {
|
||||
@@ -1684,6 +1693,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
||||
set_interval_for_si_sync: function () {
|
||||
var me = this;
|
||||
setInterval(function () {
|
||||
me.freeze_screen = false;
|
||||
me.sync_sales_invoice()
|
||||
}, 60000)
|
||||
},
|
||||
@@ -1697,9 +1707,12 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
|
||||
this.freeze = this.customer_doc.display
|
||||
}
|
||||
|
||||
freeze_screen = this.freeze_screen || false;
|
||||
|
||||
if ((this.si_docs.length || this.email_queue_list || this.customers_list) && !this.freeze) {
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.sales_invoice.pos.make_invoice",
|
||||
freeze: freeze_screen,
|
||||
args: {
|
||||
doc_list: me.si_docs,
|
||||
email_queue_list: me.email_queue_list,
|
||||
|
||||
@@ -68,16 +68,18 @@ def set_address_details(out, party, party_type, doctype=None, company=None):
|
||||
billing_address_field = "customer_address" if party_type == "Lead" \
|
||||
else party_type.lower() + "_address"
|
||||
out[billing_address_field] = get_default_address(party_type, party.name)
|
||||
out.update(get_fetch_values(doctype, billing_address_field, out[billing_address_field]))
|
||||
if doctype:
|
||||
out.update(get_fetch_values(doctype, billing_address_field, out[billing_address_field]))
|
||||
|
||||
# address display
|
||||
out.address_display = get_address_display(out[billing_address_field])
|
||||
|
||||
# shipping address
|
||||
if party_type in ["Customer", "Lead"]:
|
||||
out.shipping_address_name = get_default_address(party_type, party.name, 'is_shipping_address')
|
||||
out.shipping_address_name = get_party_shipping_address(party_type, party.name)
|
||||
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:
|
||||
out.update(get_fetch_values(doctype, 'shipping_address_name', out.shipping_address_name))
|
||||
|
||||
if doctype and doctype in ['Delivery Note', 'Sales Invoice']:
|
||||
out.update(get_company_address(company))
|
||||
@@ -320,11 +322,15 @@ def set_taxes(party, party_type, posting_date, company, customer_group=None, sup
|
||||
from erpnext.accounts.doctype.tax_rule.tax_rule import get_tax_template, get_party_details
|
||||
args = {
|
||||
party_type.lower(): party,
|
||||
"customer_group": customer_group,
|
||||
"supplier_type": supplier_type,
|
||||
"company": company
|
||||
}
|
||||
|
||||
if customer_group:
|
||||
args['customer_group'] = customer_group
|
||||
|
||||
if supplier_type:
|
||||
args['supplier_type'] = supplier_type
|
||||
|
||||
if billing_address or shipping_address:
|
||||
args.update(get_party_details(party, party_type, {"billing_address": billing_address, \
|
||||
"shipping_address": shipping_address }))
|
||||
@@ -412,3 +418,32 @@ def get_dashboard_info(party_type, party):
|
||||
info["total_unpaid"] = -1 * info["total_unpaid"]
|
||||
|
||||
return info
|
||||
|
||||
|
||||
def get_party_shipping_address(doctype, name):
|
||||
"""
|
||||
Returns an Address name (best guess) for the given doctype and name for which `address_type == 'Shipping'` is true.
|
||||
and/or `is_shipping_address = 1`.
|
||||
|
||||
It returns an empty string if there is no matching record.
|
||||
|
||||
:param doctype: Party Doctype
|
||||
:param name: Party name
|
||||
:return: String
|
||||
"""
|
||||
out = frappe.db.sql(
|
||||
'SELECT dl.parent '
|
||||
'from `tabDynamic Link` dl join `tabAddress` ta on dl.parent=ta.name '
|
||||
'where '
|
||||
'dl.link_doctype=%s '
|
||||
'and dl.link_name=%s '
|
||||
'and dl.parenttype="Address" '
|
||||
'and '
|
||||
'(ta.address_type="Shipping" or ta.is_shipping_address=1) '
|
||||
'order by ta.is_shipping_address desc, ta.address_type desc limit 1',
|
||||
(doctype, name)
|
||||
)
|
||||
if out:
|
||||
return out[0][0]
|
||||
else:
|
||||
return ''
|
||||
|
||||
@@ -44,7 +44,7 @@ class ReceivablePayableReport(object):
|
||||
})
|
||||
|
||||
columns += [_("Age (Days)") + ":Int:80"]
|
||||
|
||||
|
||||
self.ageing_col_idx_start = len(columns)
|
||||
|
||||
if not "range1" in self.filters:
|
||||
@@ -53,7 +53,7 @@ class ReceivablePayableReport(object):
|
||||
self.filters["range2"] = "60"
|
||||
if not "range3" in self.filters:
|
||||
self.filters["range3"] = "90"
|
||||
|
||||
|
||||
for label in ("0-{range1}".format(range1=self.filters["range1"]),
|
||||
"{range1}-{range2}".format(range1=cint(self.filters["range1"])+ 1, range2=self.filters["range2"]),
|
||||
"{range2}-{range3}".format(range2=cint(self.filters["range2"])+ 1, range3=self.filters["range3"]),
|
||||
@@ -74,14 +74,14 @@ class ReceivablePayableReport(object):
|
||||
})
|
||||
if args.get("party_type") == "Customer":
|
||||
columns += [
|
||||
_("Territory") + ":Link/Territory:80",
|
||||
_("Territory") + ":Link/Territory:80",
|
||||
_("Customer Group") + ":Link/Customer Group:120"
|
||||
]
|
||||
if args.get("party_type") == "Supplier":
|
||||
columns += [_("Supplier Type") + ":Link/Supplier Type:80"]
|
||||
|
||||
|
||||
columns.append(_("Remarks") + "::200")
|
||||
|
||||
|
||||
return columns
|
||||
|
||||
def get_data(self, party_naming_by, args):
|
||||
@@ -97,13 +97,13 @@ class ReceivablePayableReport(object):
|
||||
self.filters["company"] = frappe.db.get_single_value('Global Defaults', 'default_company')
|
||||
|
||||
company_currency = frappe.db.get_value("Company", self.filters.get("company"), "default_currency")
|
||||
|
||||
|
||||
return_entries = self.get_return_entries(args.get("party_type"))
|
||||
|
||||
data = []
|
||||
for gle in self.get_entries_till(self.filters.report_date, args.get("party_type")):
|
||||
if self.is_receivable_or_payable(gle, dr_or_cr, future_vouchers):
|
||||
outstanding_amount, credit_note_amount = self.get_outstanding_amount(gle,
|
||||
outstanding_amount, credit_note_amount = self.get_outstanding_amount(gle,
|
||||
self.filters.report_date, dr_or_cr, return_entries, currency_precision)
|
||||
if abs(outstanding_amount) > 0.1/10**currency_precision:
|
||||
row = [gle.posting_date, gle.party]
|
||||
@@ -179,15 +179,15 @@ class ReceivablePayableReport(object):
|
||||
# entries adjusted with future vouchers
|
||||
((gle.against_voucher_type, gle.against_voucher) in future_vouchers)
|
||||
)
|
||||
|
||||
|
||||
def get_return_entries(self, party_type):
|
||||
doctype = "Sales Invoice" if party_type=="Customer" else "Purchase Invoice"
|
||||
return [d.name for d in frappe.get_all(doctype, filters={"is_return": 1, "docstatus": 1})]
|
||||
return [d.name for d in frappe.get_all(doctype, filters={"is_return": 1, "docstatus": 1})]
|
||||
|
||||
def get_outstanding_amount(self, gle, report_date, dr_or_cr, return_entries, currency_precision):
|
||||
payment_amount, credit_note_amount = 0.0, 0.0
|
||||
reverse_dr_or_cr = "credit" if dr_or_cr=="debit" else "debit"
|
||||
|
||||
|
||||
for e in self.get_gl_entries_for(gle.party, gle.party_type, gle.voucher_type, gle.voucher_no):
|
||||
if getdate(e.posting_date) <= report_date and e.name!=gle.name:
|
||||
amount = flt(e.get(reverse_dr_or_cr)) - flt(e.get(dr_or_cr))
|
||||
@@ -195,11 +195,11 @@ class ReceivablePayableReport(object):
|
||||
payment_amount += amount
|
||||
else:
|
||||
credit_note_amount += amount
|
||||
|
||||
|
||||
outstanding_amount = flt((flt(gle.get(dr_or_cr)) - flt(gle.get(reverse_dr_or_cr)) \
|
||||
- payment_amount - credit_note_amount), currency_precision)
|
||||
credit_note_amount = flt(credit_note_amount, currency_precision)
|
||||
|
||||
|
||||
return outstanding_amount, credit_note_amount
|
||||
|
||||
def get_party_name(self, party_type, party_name):
|
||||
@@ -207,7 +207,7 @@ class ReceivablePayableReport(object):
|
||||
|
||||
def get_territory(self, party_name):
|
||||
return self.get_party_map("Customer").get(party_name, {}).get("territory") or ""
|
||||
|
||||
|
||||
def get_customer_group(self, party_name):
|
||||
return self.get_party_map("Customer").get(party_name, {}).get("customer_group") or ""
|
||||
|
||||
@@ -220,7 +220,7 @@ class ReceivablePayableReport(object):
|
||||
select_fields = "name, customer_name, territory, customer_group"
|
||||
elif party_type == "Supplier":
|
||||
select_fields = "name, supplier_name, supplier_type"
|
||||
|
||||
|
||||
self.party_map = dict(((r.name, r) for r in frappe.db.sql("select {0} from `tab{1}`"
|
||||
.format(select_fields, party_type), as_dict=True)))
|
||||
|
||||
@@ -250,8 +250,8 @@ class ReceivablePayableReport(object):
|
||||
else:
|
||||
select_fields = "sum(debit) as debit, sum(credit) as credit"
|
||||
|
||||
self.gl_entries = frappe.db.sql("""select name, posting_date, account, party_type, party,
|
||||
voucher_type, voucher_no, against_voucher_type, against_voucher,
|
||||
self.gl_entries = frappe.db.sql("""select name, posting_date, account, party_type, party,
|
||||
voucher_type, voucher_no, against_voucher_type, against_voucher,
|
||||
account_currency, remarks, {0}
|
||||
from `tabGL Entry`
|
||||
where docstatus < 2 and party_type=%s and (party is not null and party != '') {1}
|
||||
@@ -277,13 +277,13 @@ class ReceivablePayableReport(object):
|
||||
|
||||
if party_type_field=="customer":
|
||||
if self.filters.get("customer_group"):
|
||||
lft, rgt = frappe.db.get_value("Customer Group",
|
||||
lft, rgt = frappe.db.get_value("Customer Group",
|
||||
self.filters.get("customer_group"), ["lft", "rgt"])
|
||||
|
||||
conditions.append("""party in (select name from tabCustomer
|
||||
where exists(select name from `tabCustomer Group` where lft >= {0} and rgt <= {1}
|
||||
|
||||
conditions.append("""party in (select name from tabCustomer
|
||||
where exists(select name from `tabCustomer Group` where lft >= {0} and rgt <= {1}
|
||||
and name=tabCustomer.customer_group))""".format(lft, rgt))
|
||||
|
||||
|
||||
if self.filters.get("credit_days_based_on"):
|
||||
conditions.append("party in (select name from tabCustomer where credit_days_based_on=%s)")
|
||||
values.append(self.filters.get("credit_days_based_on"))
|
||||
@@ -303,22 +303,22 @@ class ReceivablePayableReport(object):
|
||||
return self.gl_entries_map.get(party, {})\
|
||||
.get(against_voucher_type, {})\
|
||||
.get(against_voucher, [])
|
||||
|
||||
|
||||
def get_chart_data(self, columns, data):
|
||||
ageing_columns = columns[self.ageing_col_idx_start : self.ageing_col_idx_start+4]
|
||||
|
||||
|
||||
rows = []
|
||||
for d in data:
|
||||
rows.append(d[self.ageing_col_idx_start : self.ageing_col_idx_start+4])
|
||||
|
||||
if rows:
|
||||
rows.insert(0, [[d.get("label")] for d in ageing_columns])
|
||||
|
||||
|
||||
return {
|
||||
"data": {
|
||||
'rows': rows
|
||||
'labels': rows
|
||||
},
|
||||
"chart_type": 'pie'
|
||||
"type": 'percentage'
|
||||
}
|
||||
|
||||
def execute(filters=None):
|
||||
|
||||
@@ -8,18 +8,18 @@ from frappe.utils import flt, cint
|
||||
from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data)
|
||||
|
||||
def execute(filters=None):
|
||||
period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year,
|
||||
period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year,
|
||||
filters.periodicity, company=filters.company)
|
||||
|
||||
asset = get_data(filters.company, "Asset", "Debit", period_list,
|
||||
asset = get_data(filters.company, "Asset", "Debit", period_list,
|
||||
only_current_fiscal_year=False, filters=filters,
|
||||
accumulated_values=filters.accumulated_values)
|
||||
|
||||
liability = get_data(filters.company, "Liability", "Credit", period_list,
|
||||
|
||||
liability = get_data(filters.company, "Liability", "Credit", period_list,
|
||||
only_current_fiscal_year=False, filters=filters,
|
||||
accumulated_values=filters.accumulated_values)
|
||||
|
||||
equity = get_data(filters.company, "Equity", "Credit", period_list,
|
||||
|
||||
equity = get_data(filters.company, "Equity", "Credit", period_list,
|
||||
only_current_fiscal_year=False, filters=filters,
|
||||
accumulated_values=filters.accumulated_values)
|
||||
|
||||
@@ -43,17 +43,17 @@ def execute(filters=None):
|
||||
unclosed[period.key] = opening_balance
|
||||
if provisional_profit_loss:
|
||||
provisional_profit_loss[period.key] = provisional_profit_loss[period.key] - opening_balance
|
||||
|
||||
|
||||
unclosed["total"]=opening_balance
|
||||
data.append(unclosed)
|
||||
|
||||
|
||||
if provisional_profit_loss:
|
||||
data.append(provisional_profit_loss)
|
||||
if total_credit:
|
||||
data.append(total_credit)
|
||||
data.append(total_credit)
|
||||
|
||||
columns = get_columns(filters.periodicity, period_list, filters.accumulated_values, company=filters.company)
|
||||
|
||||
|
||||
chart = get_chart_data(filters, columns, asset, liability, equity)
|
||||
|
||||
return columns, data, message, chart
|
||||
@@ -87,7 +87,7 @@ def get_provisional_profit_loss(asset, liability, equity, period_list, company):
|
||||
|
||||
total += flt(provisional_profit_loss[period.key])
|
||||
provisional_profit_loss["total"] = total
|
||||
|
||||
|
||||
total_row_total += flt(total_row[period.key])
|
||||
total_row["total"] = total_row_total
|
||||
|
||||
@@ -98,7 +98,7 @@ def get_provisional_profit_loss(asset, liability, equity, period_list, company):
|
||||
"warn_if_negative": True,
|
||||
"currency": currency
|
||||
})
|
||||
|
||||
|
||||
return provisional_profit_loss, total_row
|
||||
|
||||
def check_opening_balance(asset, liability, equity):
|
||||
@@ -111,17 +111,17 @@ def check_opening_balance(asset, liability, equity):
|
||||
opening_balance -= flt(liability[0].get("opening_balance", 0), float_precision)
|
||||
if equity:
|
||||
opening_balance -= flt(equity[0].get("opening_balance", 0), float_precision)
|
||||
|
||||
|
||||
opening_balance = flt(opening_balance, float_precision)
|
||||
if opening_balance:
|
||||
return _("Previous Financial Year is not closed"),opening_balance
|
||||
return None,None
|
||||
|
||||
|
||||
def get_chart_data(filters, columns, asset, liability, equity):
|
||||
x_intervals = ['x'] + [d.get("label") for d in columns[2:]]
|
||||
|
||||
labels = [d.get("label") for d in columns[2:]]
|
||||
|
||||
asset_data, liability_data, equity_data = [], [], []
|
||||
|
||||
|
||||
for p in columns[2:]:
|
||||
if asset:
|
||||
asset_data.append(asset[-2].get(p.get("fieldname")))
|
||||
@@ -129,23 +129,25 @@ def get_chart_data(filters, columns, asset, liability, equity):
|
||||
liability_data.append(liability[-2].get(p.get("fieldname")))
|
||||
if equity:
|
||||
equity_data.append(equity[-2].get(p.get("fieldname")))
|
||||
|
||||
columns = [x_intervals]
|
||||
|
||||
datasets = []
|
||||
if asset_data:
|
||||
columns.append(["Assets"] + asset_data)
|
||||
datasets.append({'title':'Assets', 'values': asset_data})
|
||||
if liability_data:
|
||||
columns.append(["Liabilities"] + liability_data)
|
||||
datasets.append({'title':'Liabilities', 'values': liability_data})
|
||||
if equity_data:
|
||||
columns.append(["Equity"] + equity_data)
|
||||
datasets.append({'title':'Equity', 'values': equity_data})
|
||||
|
||||
chart = {
|
||||
"data": {
|
||||
'x': 'x',
|
||||
'columns': columns
|
||||
'labels': labels,
|
||||
'datasets': datasets
|
||||
}
|
||||
}
|
||||
|
||||
if not filters.accumulated_values:
|
||||
chart["chart_type"] = "bar"
|
||||
chart["type"] = "bar"
|
||||
else:
|
||||
chart["type"] = "line"
|
||||
|
||||
return chart
|
||||
@@ -107,6 +107,8 @@ class GrossProfitGenerator(object):
|
||||
|
||||
def process(self):
|
||||
self.grouped = {}
|
||||
self.grouped_data = []
|
||||
|
||||
for row in self.si_list:
|
||||
if self.skip_row(row, self.product_bundles):
|
||||
continue
|
||||
@@ -150,7 +152,6 @@ class GrossProfitGenerator(object):
|
||||
|
||||
def get_average_rate_based_on_group_by(self):
|
||||
# sum buying / selling totals for group
|
||||
self.grouped_data = []
|
||||
for key in self.grouped.keys():
|
||||
if self.filters.get("group_by") != "Invoice":
|
||||
for i, row in enumerate(self.grouped[key]):
|
||||
|
||||
@@ -8,15 +8,15 @@ from frappe.utils import flt
|
||||
from erpnext.accounts.report.financial_statements import (get_period_list, get_columns, get_data)
|
||||
|
||||
def execute(filters=None):
|
||||
period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year,
|
||||
period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year,
|
||||
filters.periodicity, filters.accumulated_values, filters.company)
|
||||
|
||||
income = get_data(filters.company, "Income", "Credit", period_list, filters = filters,
|
||||
accumulated_values=filters.accumulated_values,
|
||||
accumulated_values=filters.accumulated_values,
|
||||
ignore_closing_entries=True, ignore_accumulated_values_for_fy= True)
|
||||
|
||||
|
||||
expense = get_data(filters.company, "Expense", "Debit", period_list, filters=filters,
|
||||
accumulated_values=filters.accumulated_values,
|
||||
accumulated_values=filters.accumulated_values,
|
||||
ignore_closing_entries=True, ignore_accumulated_values_for_fy= True)
|
||||
|
||||
net_profit_loss = get_net_profit_loss(income, expense, period_list, filters.company)
|
||||
@@ -61,7 +61,7 @@ def get_net_profit_loss(income, expense, period_list, company):
|
||||
|
||||
|
||||
def get_chart_data(filters, columns, income, expense, net_profit_loss):
|
||||
x_intervals = ['x'] + [d.get("label") for d in columns[2:]]
|
||||
labels = [d.get("label") for d in columns[2:]]
|
||||
|
||||
income_data, expense_data, net_profit = [], [], []
|
||||
|
||||
@@ -73,27 +73,24 @@ def get_chart_data(filters, columns, income, expense, net_profit_loss):
|
||||
if net_profit_loss:
|
||||
net_profit.append(net_profit_loss.get(p.get("fieldname")))
|
||||
|
||||
columns = [x_intervals]
|
||||
datasets = []
|
||||
if income_data:
|
||||
columns.append(["Income"] + income_data)
|
||||
datasets.append({'title': 'Income', 'values': income_data})
|
||||
if expense_data:
|
||||
columns.append(["Expense"] + expense_data)
|
||||
datasets.append({'title': 'Expense', 'values': expense_data})
|
||||
if net_profit:
|
||||
columns.append(["Net Profit/Loss"] + net_profit)
|
||||
datasets.append({'title': 'Net Profit/Loss', 'values': net_profit})
|
||||
|
||||
chart = {
|
||||
"data": {
|
||||
'x': 'x',
|
||||
'columns': columns,
|
||||
'colors': {
|
||||
'Income': '#5E64FF',
|
||||
'Expense': '#b8c2cc',
|
||||
'Net Profit/Loss': '#ff5858'
|
||||
}
|
||||
'labels': labels,
|
||||
'datasets': datasets
|
||||
}
|
||||
}
|
||||
|
||||
if not filters.accumulated_values:
|
||||
chart["chart_type"] = "bar"
|
||||
chart["type"] = "bar"
|
||||
else:
|
||||
chart["type"] = "line"
|
||||
|
||||
return chart
|
||||
84
erpnext/accounts/test/test_utils.py
Normal file
@@ -0,0 +1,84 @@
|
||||
import unittest
|
||||
from erpnext.accounts.party import get_party_shipping_address
|
||||
from frappe.test_runner import make_test_objects
|
||||
|
||||
|
||||
class TestUtils(unittest.TestCase):
|
||||
@classmethod
|
||||
def setUpClass(cls):
|
||||
super(TestUtils, cls).setUpClass()
|
||||
make_test_objects('Address', ADDRESS_RECORDS)
|
||||
|
||||
def test_get_party_shipping_address(self):
|
||||
address = get_party_shipping_address('Customer', '_Test Customer 1')
|
||||
self.assertEqual(address, '_Test Billing Address 2 Title-Billing')
|
||||
|
||||
def test_get_party_shipping_address2(self):
|
||||
address = get_party_shipping_address('Customer', '_Test Customer 2')
|
||||
self.assertEqual(address, '_Test Shipping Address 2 Title-Shipping')
|
||||
|
||||
|
||||
ADDRESS_RECORDS = [
|
||||
{
|
||||
"doctype": "Address",
|
||||
"address_type": "Billing",
|
||||
"address_line1": "Address line 1",
|
||||
"address_title": "_Test Billing Address Title",
|
||||
"city": "Lagos",
|
||||
"country": "Nigeria",
|
||||
"links": [
|
||||
{
|
||||
"link_doctype": "Customer",
|
||||
"link_name": "_Test Customer 2",
|
||||
"doctype": "Dynamic Link"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"doctype": "Address",
|
||||
"address_type": "Shipping",
|
||||
"address_line1": "Address line 2",
|
||||
"address_title": "_Test Shipping Address 1 Title",
|
||||
"city": "Lagos",
|
||||
"country": "Nigeria",
|
||||
"links": [
|
||||
{
|
||||
"link_doctype": "Customer",
|
||||
"link_name": "_Test Customer 2",
|
||||
"doctype": "Dynamic Link"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"doctype": "Address",
|
||||
"address_type": "Shipping",
|
||||
"address_line1": "Address line 3",
|
||||
"address_title": "_Test Shipping Address 2 Title",
|
||||
"city": "Lagos",
|
||||
"country": "Nigeria",
|
||||
"is_shipping_address": "1",
|
||||
"links": [
|
||||
{
|
||||
"link_doctype": "Customer",
|
||||
"link_name": "_Test Customer 2",
|
||||
"doctype": "Dynamic Link"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"doctype": "Address",
|
||||
"address_type": "Billing",
|
||||
"address_line1": "Address line 4",
|
||||
"address_title": "_Test Billing Address 2 Title",
|
||||
"city": "Lagos",
|
||||
"country": "Nigeria",
|
||||
"is_shipping_address": "1",
|
||||
"links": [
|
||||
{
|
||||
"link_doctype": "Customer",
|
||||
"link_name": "_Test Customer 1",
|
||||
"doctype": "Dynamic Link"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -15,6 +15,8 @@ frappe.ui.form.on("Purchase Order", {
|
||||
},
|
||||
|
||||
onload: function(frm) {
|
||||
set_schedule_date(frm);
|
||||
|
||||
erpnext.queries.setup_queries(frm, "Warehouse", function() {
|
||||
return erpnext.queries.warehouse(frm.doc);
|
||||
});
|
||||
@@ -24,6 +26,29 @@ frappe.ui.form.on("Purchase Order", {
|
||||
},
|
||||
});
|
||||
|
||||
frappe.ui.form.on("Purchase Order Item", {
|
||||
item_code: function(frm) {
|
||||
frappe.call({
|
||||
method: "get_last_purchase_rate",
|
||||
doc: frm.doc,
|
||||
callback: function(r, rt) {
|
||||
frm.trigger('calculate_taxes_and_totals');
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
schedule_date: function(frm, cdt, cdn) {
|
||||
var row = locals[cdt][cdn];
|
||||
if (row.schedule_date) {
|
||||
if(!frm.doc.schedule_date) {
|
||||
erpnext.utils.copy_value_in_all_row(frm.doc, cdt, cdn, "items", "schedule_date");
|
||||
} else {
|
||||
set_schedule_date(frm);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend({
|
||||
refresh: function(doc, cdt, cdn) {
|
||||
var me = this;
|
||||
@@ -107,12 +132,7 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
||||
},
|
||||
|
||||
validate: function() {
|
||||
// set default schedule date as today if missing.
|
||||
(this.frm.doc.items || []).forEach(function(d) {
|
||||
if(!d.schedule_date) {
|
||||
d.schedule_date = frappe.datetime.nowdate();
|
||||
}
|
||||
})
|
||||
set_schedule_date(this.frm);
|
||||
},
|
||||
|
||||
make_stock_entry: function() {
|
||||
@@ -201,7 +221,12 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
||||
|
||||
items_add: function(doc, cdt, cdn) {
|
||||
var row = frappe.get_doc(cdt, cdn);
|
||||
this.frm.script_manager.copy_from_first_row("items", row, ["schedule_date"]);
|
||||
if(doc.schedule_date) {
|
||||
row.schedule_date = doc.schedule_date;
|
||||
refresh_field("schedule_date", cdn, "items");
|
||||
} else {
|
||||
this.frm.script_manager.copy_from_first_row("items", row, ["schedule_date"]);
|
||||
}
|
||||
},
|
||||
|
||||
unclose_purchase_order: function(){
|
||||
@@ -225,8 +250,15 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
||||
cur_frm.cscript.calculate_taxes_and_totals();
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
|
||||
items_on_form_rendered: function() {
|
||||
set_schedule_date(this.frm);
|
||||
},
|
||||
|
||||
schedule_date: function() {
|
||||
set_schedule_date(this.frm);
|
||||
}
|
||||
});
|
||||
|
||||
// for backward compatibility: combine new and previous states
|
||||
@@ -268,8 +300,10 @@ cur_frm.cscript.on_submit = function(doc, cdt, cdn) {
|
||||
}
|
||||
}
|
||||
|
||||
cur_frm.cscript.schedule_date = function(doc, cdt, cdn) {
|
||||
erpnext.utils.copy_value_in_all_row(doc, cdt, cdn, "items", "schedule_date");
|
||||
function set_schedule_date(frm) {
|
||||
if(frm.doc.schedule_date){
|
||||
erpnext.utils.copy_value_in_all_row(frm.doc, frm.doc.doctype, frm.doc.name, "items", "schedule_date");
|
||||
}
|
||||
}
|
||||
|
||||
frappe.provide("erpnext.buying");
|
||||
|
||||
@@ -291,9 +291,40 @@
|
||||
"search_index": 1,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"default": "",
|
||||
"fieldname": "schedule_date",
|
||||
"fieldtype": "Date",
|
||||
"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": "Reqd By Date",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
@@ -1206,37 +1237,6 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.docstatus===0 && (doc.items && doc.items.length)",
|
||||
"fieldname": "get_last_purchase_rate",
|
||||
"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": "Get last purchase rate",
|
||||
"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,
|
||||
@@ -2948,418 +2948,13 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "subscription",
|
||||
"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": "Subscription",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Subscription",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"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": 1,
|
||||
"collapsible_depends_on": "is_recurring",
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.docstatus<2 && !doc.__islocal",
|
||||
"fieldname": "recurring_order",
|
||||
"fieldtype": "Section Break",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Recurring Order",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "fa fa-time",
|
||||
"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_break",
|
||||
"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,
|
||||
"label": "Settings",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"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": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.docstatus<2",
|
||||
"description": "",
|
||||
"fieldname": "is_recurring",
|
||||
"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": "Is Recurring",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "is_recurring",
|
||||
"description": "",
|
||||
"fieldname": "recurring_id",
|
||||
"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": "Reference Document",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Purchase Order",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"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": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name",
|
||||
"description": "",
|
||||
"fieldname": "recurring_type",
|
||||
"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": "Frequency",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Monthly\nQuarterly\nHalf-yearly\nYearly",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name",
|
||||
"description": "",
|
||||
"fieldname": "repeat_on_day_of_month",
|
||||
"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": "Repeat on Day of Month",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name",
|
||||
"description": "",
|
||||
"fieldname": "end_date",
|
||||
"fieldtype": "Date",
|
||||
"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": "End Date",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name",
|
||||
"fieldname": "submit_on_creation",
|
||||
"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": "Submit on creation",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.is_recurring && doc.recurring_id === doc.name",
|
||||
"description": "",
|
||||
"fieldname": "notify_by_email",
|
||||
"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": "Notify by email",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.is_recurring && doc.notify_by_email && doc.recurring_id === doc.name",
|
||||
"description": "",
|
||||
"fieldname": "notification_email_address",
|
||||
"fieldtype": "Code",
|
||||
"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": "Notification Email Address",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Email",
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "eval:doc.is_recurring && doc.notify_by_email && doc.recurring_id === doc.name",
|
||||
"fieldname": "recurring_print_format",
|
||||
"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": "Recurring Print Format",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "Print Format",
|
||||
"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": "column_break83",
|
||||
"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,
|
||||
"label": "This Document",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 1,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "is_recurring",
|
||||
"depends_on": "",
|
||||
"description": "",
|
||||
"fieldname": "from_date",
|
||||
"fieldtype": "Date",
|
||||
@@ -3390,7 +2985,7 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "is_recurring",
|
||||
"depends_on": "",
|
||||
"description": "",
|
||||
"fieldname": "to_date",
|
||||
"fieldtype": "Date",
|
||||
@@ -3421,10 +3016,8 @@
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"depends_on": "is_recurring",
|
||||
"description": "",
|
||||
"fieldname": "next_date",
|
||||
"fieldtype": "Date",
|
||||
"fieldname": "column_break_97",
|
||||
"fieldtype": "Column Break",
|
||||
"hidden": 0,
|
||||
"ignore_user_permissions": 0,
|
||||
"ignore_xss_filter": 0,
|
||||
@@ -3432,11 +3025,11 @@
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Next Date",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"no_copy": 0,
|
||||
"permlevel": 0,
|
||||
"print_hide": 1,
|
||||
"precision": "",
|
||||
"print_hide": 0,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 0,
|
||||
"remember_last_selected_value": 0,
|
||||
@@ -3445,6 +3038,37 @@
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "subscription",
|
||||
"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": "Subscription",
|
||||
"length": 0,
|
||||
"no_copy": 1,
|
||||
"options": "Subscription",
|
||||
"permlevel": 0,
|
||||
"precision": "",
|
||||
"print_hide": 1,
|
||||
"print_hide_if_no_value": 0,
|
||||
"read_only": 1,
|
||||
"remember_last_selected_value": 0,
|
||||
"report_hide": 0,
|
||||
"reqd": 0,
|
||||
"search_index": 0,
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
}
|
||||
],
|
||||
"has_web_view": 0,
|
||||
@@ -3458,7 +3082,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-09-19 11:22:30.190589",
|
||||
"modified": "2017-10-24 12:52:11.272306",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Purchase Order",
|
||||
|
||||
@@ -20,8 +20,8 @@ form_grid_templates = {
|
||||
}
|
||||
|
||||
class PurchaseOrder(BuyingController):
|
||||
def __init__(self, arg1, arg2=None):
|
||||
super(PurchaseOrder, self).__init__(arg1, arg2)
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(PurchaseOrder, self).__init__(*args, **kwargs)
|
||||
self.status_updater = [{
|
||||
'source_dt': 'Purchase Order Item',
|
||||
'target_dt': 'Material Request Item',
|
||||
@@ -41,6 +41,7 @@ class PurchaseOrder(BuyingController):
|
||||
self.set_status()
|
||||
|
||||
self.validate_supplier()
|
||||
self.validate_schedule_date()
|
||||
validate_for_items(self)
|
||||
self.check_for_closed_status()
|
||||
|
||||
@@ -116,14 +117,13 @@ class PurchaseOrder(BuyingController):
|
||||
d.discount_percentage = last_purchase_details['discount_percentage']
|
||||
d.base_rate = last_purchase_details['base_rate'] * (flt(d.conversion_factor) or 1.0)
|
||||
d.price_list_rate = d.base_price_list_rate / conversion_rate
|
||||
d.rate = d.base_rate / conversion_rate
|
||||
d.last_purchase_rate = d.base_rate / conversion_rate
|
||||
else:
|
||||
msgprint(_("Last purchase rate not found"))
|
||||
|
||||
item_last_purchase_rate = frappe.db.get_value("Item", d.item_code, "last_purchase_rate")
|
||||
if item_last_purchase_rate:
|
||||
d.base_price_list_rate = d.base_rate = d.price_list_rate \
|
||||
= d.rate = item_last_purchase_rate
|
||||
= d.last_purchase_rate = item_last_purchase_rate
|
||||
|
||||
# Check for Closed status
|
||||
def check_for_closed_status(self):
|
||||
|
||||
@@ -118,6 +118,7 @@ class TestPurchaseOrder(unittest.TestCase):
|
||||
"company": "_Test Company",
|
||||
"supplier" : "_Test Supplier",
|
||||
"is_subcontracted" : "No",
|
||||
"schedule_date": add_days(nowdate(), 1),
|
||||
"currency" : frappe.db.get_value("Company", "_Test Company", "default_currency"),
|
||||
"conversion_factor" : 1,
|
||||
"items" : get_same_items(),
|
||||
@@ -149,6 +150,7 @@ def create_purchase_order(**args):
|
||||
if args.transaction_date:
|
||||
po.transaction_date = args.transaction_date
|
||||
|
||||
po.schedule_date = add_days(nowdate(), 1)
|
||||
po.company = args.company or "_Test Company"
|
||||
po.supplier = args.customer or "_Test Supplier"
|
||||
po.is_subcontracted = args.is_subcontracted or "No"
|
||||
|
||||
@@ -30,7 +30,8 @@
|
||||
],
|
||||
"supplier": "_Test Supplier",
|
||||
"supplier_name": "_Test Supplier",
|
||||
"transaction_date": "2013-02-12"
|
||||
"transaction_date": "2013-02-12",
|
||||
"schedule_date": "2013-02-13"
|
||||
},
|
||||
{
|
||||
"advance_paid": 0.0,
|
||||
@@ -63,6 +64,7 @@
|
||||
],
|
||||
"supplier": "_Test Supplier",
|
||||
"supplier_name": "_Test Supplier",
|
||||
"transaction_date": "2013-02-12"
|
||||
"transaction_date": "2013-02-12",
|
||||
"schedule_date": "2013-02-13"
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
QUnit.module('Buying');
|
||||
|
||||
QUnit.test("test: purchase order", function(assert) {
|
||||
assert.expect(11);
|
||||
assert.expect(16);
|
||||
let done = assert.async();
|
||||
|
||||
frappe.run_serially([
|
||||
@@ -13,12 +13,21 @@ QUnit.test("test: purchase order", function(assert) {
|
||||
{items: [
|
||||
[
|
||||
{"item_code": 'Test Product 4'},
|
||||
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
|
||||
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 2)},
|
||||
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
|
||||
{"qty": 5},
|
||||
{"uom": 'Unit'},
|
||||
{"rate": 100},
|
||||
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
|
||||
],
|
||||
[
|
||||
{"item_code": 'Test Product 1'},
|
||||
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
|
||||
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
|
||||
{"qty": 2},
|
||||
{"uom": 'Unit'},
|
||||
{"rate": 100},
|
||||
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
|
||||
]
|
||||
]},
|
||||
|
||||
@@ -30,14 +39,20 @@ QUnit.test("test: purchase order", function(assert) {
|
||||
() => {
|
||||
// Get supplier details
|
||||
assert.ok(cur_frm.doc.supplier_name == 'Test Supplier', "Supplier name correct");
|
||||
assert.ok($('div.control-value.like-disabled-input.for-description').text().includes('Contact 3'), "Contact display correct");
|
||||
assert.ok(cur_frm.doc.schedule_date == frappe.datetime.add_days(frappe.datetime.now_date(), 1), "Schedule Date correct");
|
||||
assert.ok(cur_frm.doc.contact_email == 'test@supplier.com', "Contact email correct");
|
||||
// Get item details
|
||||
assert.ok(cur_frm.doc.items[0].item_name == 'Test Product 4', "Item name correct");
|
||||
assert.ok(cur_frm.doc.items[0].description == 'Test Product 4', "Description correct");
|
||||
assert.ok(cur_frm.doc.items[0].qty == 5, "Quantity correct");
|
||||
assert.ok(cur_frm.doc.items[0].schedule_date == frappe.datetime.add_days(frappe.datetime.now_date(), 2), "Schedule Date correct");
|
||||
|
||||
assert.ok(cur_frm.doc.items[1].item_name == 'Test Product 1', "Item name correct");
|
||||
assert.ok(cur_frm.doc.items[1].description == 'Test Product 1', "Description correct");
|
||||
assert.ok(cur_frm.doc.items[1].qty == 2, "Quantity correct");
|
||||
assert.ok(cur_frm.doc.items[1].schedule_date == cur_frm.doc.schedule_date, "Schedule Date correct");
|
||||
// Calculate total
|
||||
assert.ok(cur_frm.doc.total == 500, "Total correct");
|
||||
assert.ok(cur_frm.doc.total == 700, "Total correct");
|
||||
// Get terms
|
||||
assert.ok(cur_frm.doc.terms == 'This is a term.', "Terms correct");
|
||||
},
|
||||
@@ -54,7 +69,7 @@ QUnit.test("test: purchase order", function(assert) {
|
||||
|
||||
() => frappe.tests.click_button('Submit'),
|
||||
() => frappe.tests.click_button('Yes'),
|
||||
() => frappe.timeout(0.3),
|
||||
() => frappe.timeout(1),
|
||||
|
||||
() => {
|
||||
assert.ok(cur_frm.doc.status == 'To Receive and Bill', "Submitted successfully");
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
QUnit.module('Buying');
|
||||
|
||||
QUnit.test("test: purchase order with last purchase rate", function(assert) {
|
||||
assert.expect(5);
|
||||
let done = assert.async();
|
||||
|
||||
frappe.run_serially([
|
||||
() => {
|
||||
return frappe.tests.make('Purchase Order', [
|
||||
{supplier: 'Test Supplier'},
|
||||
{is_subcontracted: 'No'},
|
||||
{currency: 'INR'},
|
||||
{items: [
|
||||
[
|
||||
{"item_code": 'Test Product 4'},
|
||||
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
|
||||
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
|
||||
{"qty": 1},
|
||||
{"rate": 800},
|
||||
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
|
||||
],
|
||||
[
|
||||
{"item_code": 'Test Product 1'},
|
||||
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
|
||||
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
|
||||
{"qty": 1},
|
||||
{"rate": 400},
|
||||
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
|
||||
]
|
||||
]}
|
||||
]);
|
||||
},
|
||||
|
||||
() => {
|
||||
// Get item details
|
||||
assert.ok(cur_frm.doc.items[0].item_name == 'Test Product 4', "Item 1 name correct");
|
||||
assert.ok(cur_frm.doc.items[1].item_name == 'Test Product 1', "Item 2 name correct");
|
||||
},
|
||||
|
||||
() => frappe.timeout(1),
|
||||
|
||||
() => frappe.tests.click_button('Submit'),
|
||||
() => frappe.tests.click_button('Yes'),
|
||||
() => frappe.timeout(3),
|
||||
|
||||
() => frappe.tests.click_button('Close'),
|
||||
() => frappe.timeout(1),
|
||||
|
||||
() => {
|
||||
return frappe.tests.make('Purchase Order', [
|
||||
{supplier: 'Test Supplier'},
|
||||
{is_subcontracted: 'No'},
|
||||
{currency: 'INR'},
|
||||
{items: [
|
||||
[
|
||||
{"item_code": 'Test Product 4'},
|
||||
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
|
||||
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
|
||||
{"qty": 1},
|
||||
{"rate": 600},
|
||||
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
|
||||
],
|
||||
[
|
||||
{"item_code": 'Test Product 1'},
|
||||
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
|
||||
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
|
||||
{"qty": 1},
|
||||
{"rate": 200},
|
||||
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
|
||||
]
|
||||
]}
|
||||
]);
|
||||
},
|
||||
|
||||
() => frappe.timeout(2),
|
||||
|
||||
// Get the last purchase rate of items
|
||||
() => {
|
||||
assert.ok(cur_frm.doc.items[0].last_purchase_rate == 800, "Last purchase rate of item 1 correct");
|
||||
},
|
||||
() => {
|
||||
assert.ok(cur_frm.doc.items[1].last_purchase_rate == 400, "Last purchase rate of item 2 correct");
|
||||
},
|
||||
|
||||
() => frappe.tests.click_button('Submit'),
|
||||
() => frappe.tests.click_button('Yes'),
|
||||
() => frappe.timeout(3),
|
||||
|
||||
() => frappe.tests.click_button('Close'),
|
||||
|
||||
() => frappe.timeout(1),
|
||||
|
||||
() => {
|
||||
assert.ok(cur_frm.doc.status == 'To Receive and Bill', "Submitted successfully");
|
||||
},
|
||||
|
||||
() => done()
|
||||
]);
|
||||
});
|
||||
@@ -11,6 +11,7 @@ QUnit.test("test: purchase order with taxes and charges", function(assert) {
|
||||
{is_subcontracted: 'No'},
|
||||
{buying_price_list: 'Test-Buying-USD'},
|
||||
{currency: 'USD'},
|
||||
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
|
||||
{items: [
|
||||
[
|
||||
{"item_code": 'Test Product 4'},
|
||||
|
||||
@@ -140,7 +140,7 @@
|
||||
"allow_on_submit": 1,
|
||||
"bold": 1,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"columns": 2,
|
||||
"fieldname": "schedule_date",
|
||||
"fieldtype": "Date",
|
||||
"hidden": 0,
|
||||
@@ -148,7 +148,7 @@
|
||||
"ignore_xss_filter": 0,
|
||||
"in_filter": 0,
|
||||
"in_global_search": 0,
|
||||
"in_list_view": 0,
|
||||
"in_list_view": 1,
|
||||
"in_standard_filter": 0,
|
||||
"label": "Reqd By Date",
|
||||
"length": 0,
|
||||
@@ -382,7 +382,7 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 1,
|
||||
"collapsible": 0,
|
||||
"columns": 2,
|
||||
"columns": 1,
|
||||
"fieldname": "qty",
|
||||
"fieldtype": "Float",
|
||||
"hidden": 0,
|
||||
@@ -655,6 +655,37 @@
|
||||
"set_only_once": 0,
|
||||
"unique": 0
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 0,
|
||||
"allow_on_submit": 0,
|
||||
"bold": 0,
|
||||
"collapsible": 0,
|
||||
"columns": 0,
|
||||
"fieldname": "last_purchase_rate",
|
||||
"fieldtype": "Currency",
|
||||
"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": "Last Purchase Rate",
|
||||
"length": 0,
|
||||
"no_copy": 0,
|
||||
"options": "currency",
|
||||
"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,
|
||||
@@ -718,7 +749,7 @@
|
||||
"allow_on_submit": 0,
|
||||
"bold": 1,
|
||||
"collapsible": 0,
|
||||
"columns": 3,
|
||||
"columns": 2,
|
||||
"fieldname": "rate",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 0,
|
||||
@@ -1714,7 +1745,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 1,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-08-02 22:15:47.411235",
|
||||
"modified": "2017-10-05 19:47:12.433095",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Purchase Order Item",
|
||||
|
||||
@@ -254,6 +254,21 @@ erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.e
|
||||
}
|
||||
})
|
||||
}, __("Get items from"));
|
||||
// Get items from Opportunity
|
||||
this.frm.add_custom_button(__('Opportunity'),
|
||||
function() {
|
||||
erpnext.utils.map_current_doc({
|
||||
method: "erpnext.crm.doctype.opportunity.opportunity.make_request_for_quotation",
|
||||
source_doctype: "Opportunity",
|
||||
target: me.frm,
|
||||
setters: {
|
||||
company: me.frm.doc.company
|
||||
},
|
||||
get_query_filters: {
|
||||
enquiry_type: "Sales"
|
||||
}
|
||||
})
|
||||
}, __("Get items from"));
|
||||
// Get items from open Material Requests based on supplier
|
||||
this.frm.add_custom_button(__('Possible Supplier'), function() {
|
||||
// Create a dialog window for the user to pick their supplier
|
||||
|
||||
@@ -816,7 +816,7 @@
|
||||
"issingle": 0,
|
||||
"istable": 0,
|
||||
"max_attachments": 0,
|
||||
"modified": "2017-07-21 14:06:46.309322",
|
||||
"modified": "2017-10-17 17:27:06.281494",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Request for Quotation",
|
||||
@@ -903,26 +903,6 @@
|
||||
"submit": 0,
|
||||
"write": 0
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
"cancel": 0,
|
||||
"create": 0,
|
||||
"delete": 0,
|
||||
"email": 1,
|
||||
"export": 0,
|
||||
"if_owner": 0,
|
||||
"import": 0,
|
||||
"permlevel": 0,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Supplier",
|
||||
"set_user_permissions": 0,
|
||||
"share": 0,
|
||||
"submit": 0,
|
||||
"write": 0
|
||||
},
|
||||
{
|
||||
"amend": 0,
|
||||
"apply_user_permissions": 0,
|
||||
|
||||
@@ -138,7 +138,7 @@ class RequestforQuotation(BuyingController):
|
||||
'user_fullname': full_name
|
||||
}
|
||||
|
||||
subject = _("Request for Quotation")
|
||||
subject = _("Request for Quotation: {0}".format(self.name))
|
||||
template = "templates/emails/request_for_quotation.html"
|
||||
sender = frappe.session.user not in STANDARD_USERS and frappe.session.user or None
|
||||
message = frappe.get_template(template).render(args)
|
||||
|
||||
@@ -27,6 +27,7 @@ QUnit.test("test: request_for_quotation", function(assert) {
|
||||
{tc_name: 'Test Term 1'}
|
||||
]);
|
||||
},
|
||||
() => frappe.timeout(3),
|
||||
() => {
|
||||
assert.ok(cur_frm.doc.transaction_date == date, "Date correct");
|
||||
assert.ok(cur_frm.doc.company == cur_frm.doc.company, "Company correct");
|
||||
@@ -38,7 +39,7 @@ QUnit.test("test: request_for_quotation", function(assert) {
|
||||
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");
|
||||
},
|
||||
() => frappe.timeout(0.3),
|
||||
() => frappe.timeout(3),
|
||||
() => cur_frm.print_doc(),
|
||||
() => frappe.timeout(1),
|
||||
() => {
|
||||
@@ -65,7 +66,7 @@ QUnit.test("test: request_for_quotation", function(assert) {
|
||||
assert.ok(cur_frm.doc.docstatus == 1, "Quotation request submitted");
|
||||
},
|
||||
() => frappe.click_button('Send Supplier Emails'),
|
||||
() => frappe.timeout(4),
|
||||
() => frappe.timeout(6),
|
||||
() => {
|
||||
assert.ok($('div.modal.fade.in > div.modal-dialog > div > div.modal-body.ui-front > div.msgprint').text().includes("Email sent to supplier Test Supplier"), "Send emails working");
|
||||
},
|
||||
|
||||
@@ -56,7 +56,8 @@ QUnit.test("test: supplier", function(assert) {
|
||||
() => frappe.click_button('New Contact'),
|
||||
() => {
|
||||
return frappe.tests.set_form_values(cur_frm, [
|
||||
{first_name: "Contact 3"}
|
||||
{first_name: "Contact 3"},
|
||||
{email_id: "test@supplier.com"}
|
||||
]);
|
||||
},
|
||||
() => cur_frm.save(),
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import flt
|
||||
from frappe.utils import flt, nowdate, add_days
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
|
||||
from erpnext.controllers.buying_controller import BuyingController
|
||||
@@ -151,5 +151,4 @@ def make_quotation(source_name, target_doc=None):
|
||||
}
|
||||
}, target_doc)
|
||||
|
||||
return doclist
|
||||
|
||||
return doclist
|
||||
@@ -27,12 +27,13 @@ QUnit.test("test: supplier quotation", function(assert) {
|
||||
{terms: 'This is a term'}
|
||||
]);
|
||||
},
|
||||
() => frappe.timeout(3),
|
||||
() => {
|
||||
// Get Supplier details
|
||||
assert.ok(cur_frm.doc.supplier == 'Test Supplier', "Supplier 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_person == 'Contact 3-Test Supplier', "Conatct correct");
|
||||
assert.ok(cur_frm.doc.contact_email == 'test@supplier.com', "Email correct");
|
||||
// Get uom
|
||||
assert.ok(cur_frm.doc.items[0].uom == 'Unit', "Multi uom correct");
|
||||
|
||||
@@ -2,29 +2,29 @@
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.query_reports["Quoted Item Comparison"] = {
|
||||
"filters": [
|
||||
filters: [
|
||||
{
|
||||
"fieldname": "supplier_quotation",
|
||||
"label": __("Supplier Quotation"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Supplier Quotation",
|
||||
"default": "",
|
||||
"get_query": function () {
|
||||
fieldtype: "Link",
|
||||
label: __("Supplier Quotation"),
|
||||
options: "Supplier Quotation",
|
||||
fieldname: "supplier_quotation",
|
||||
default: "",
|
||||
get_query: () => {
|
||||
return { filters: { "docstatus": ["<", 2] } }
|
||||
}
|
||||
},
|
||||
{
|
||||
"fieldname": "item",
|
||||
"label": __("Item"),
|
||||
"fieldtype": "Link",
|
||||
"options": "Item",
|
||||
"default": "",
|
||||
"reqd": 1,
|
||||
"get_query": function () {
|
||||
var quote = frappe.query_report_filters_by_name.supplier_quotation.get_value();
|
||||
reqd: 1,
|
||||
default: "",
|
||||
options: "Item",
|
||||
label: __("Item"),
|
||||
fieldname: "item",
|
||||
fieldtype: "Link",
|
||||
get_query: () => {
|
||||
let quote = frappe.query_report_filters_by_name.supplier_quotation.get_value();
|
||||
if (quote != "") {
|
||||
return {
|
||||
query: "erpnext.buying.doctype.quality_inspection.quality_inspection.item_query",
|
||||
query: "erpnext.stock.doctype.quality_inspection.quality_inspection.item_query",
|
||||
filters: {
|
||||
"from": "Supplier Quotation Item",
|
||||
"parent": quote
|
||||
@@ -39,47 +39,50 @@ frappe.query_reports["Quoted Item Comparison"] = {
|
||||
}
|
||||
}
|
||||
],
|
||||
onload: function (report) {
|
||||
onload: (report) => {
|
||||
// Create a button for setting the default supplier
|
||||
report.page.add_inner_button(__("Select Default Supplier"), function () {
|
||||
|
||||
var reporter = frappe.query_reports["Quoted Item Comparison"];
|
||||
report.page.add_inner_button(__("Select Default Supplier"), () => {
|
||||
let reporter = frappe.query_reports["Quoted Item Comparison"];
|
||||
|
||||
//Always make a new one so that the latest values get updated
|
||||
reporter.make_default_supplier_dialog(report);
|
||||
report.dialog.show();
|
||||
setTimeout(function () { report.dialog.input.focus(); }, 1000);
|
||||
|
||||
}, 'Tools');
|
||||
|
||||
},
|
||||
"make_default_supplier_dialog": function (report) {
|
||||
make_default_supplier_dialog: (report) => {
|
||||
// Get the name of the item to change
|
||||
var filters = report.get_values();
|
||||
var item_code = filters.item;
|
||||
if(!report.data) return;
|
||||
|
||||
let filters = report.get_values();
|
||||
let item_code = filters.item;
|
||||
|
||||
// Get a list of the suppliers (with a blank as well) for the user to select
|
||||
var select_options = "";
|
||||
for (let supplier of report.data) {
|
||||
select_options += supplier.supplier_name + '\n'
|
||||
}
|
||||
let suppliers = $.map(report.data, (row, idx)=>{ return row.supplier_name })
|
||||
|
||||
// Create a dialog window for the user to pick their supplier
|
||||
var d = new frappe.ui.Dialog({
|
||||
let dialog = new frappe.ui.Dialog({
|
||||
title: __('Select Default Supplier'),
|
||||
fields: [
|
||||
{ fieldname: 'supplier', fieldtype: 'Select', label: 'Supplier', reqd: 1, options: select_options },
|
||||
{ fieldname: 'ok_button', fieldtype: 'Button', label: 'Set Default Supplier' },
|
||||
{
|
||||
reqd: 1,
|
||||
label: 'Supplier',
|
||||
fieldtype: 'Link',
|
||||
options: 'Supplier',
|
||||
fieldname: 'supplier',
|
||||
get_query: () => {
|
||||
return {
|
||||
filters: {
|
||||
'name': ['in', suppliers]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
// On the user clicking the ok button
|
||||
d.fields_dict.ok_button.input.onclick = function () {
|
||||
var btn = d.fields_dict.ok_button.input;
|
||||
var v = report.dialog.get_values();
|
||||
if (v) {
|
||||
$(btn).set_working();
|
||||
|
||||
dialog.set_primary_action("Set Default Supplier", () => {
|
||||
let values = dialog.get_values();
|
||||
if(values) {
|
||||
// Set the default_supplier field of the appropriate Item to the selected supplier
|
||||
frappe.call({
|
||||
method: "frappe.client.set_value",
|
||||
@@ -87,17 +90,17 @@ frappe.query_reports["Quoted Item Comparison"] = {
|
||||
doctype: "Item",
|
||||
name: item_code,
|
||||
fieldname: "default_supplier",
|
||||
value: v.supplier,
|
||||
value: values.supplier,
|
||||
},
|
||||
callback: function (r) {
|
||||
$(btn).done_working();
|
||||
freeze: true,
|
||||
callback: (r) => {
|
||||
frappe.msgprint("Successfully Set Supplier");
|
||||
report.dialog.hide();
|
||||
dialog.hide();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
report.dialog = d;
|
||||
});
|
||||
dialog.show();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,53 +8,55 @@ import frappe
|
||||
|
||||
def execute(filters=None):
|
||||
qty_list = get_quantity_list(filters.item)
|
||||
|
||||
data = get_quote_list(filters.item, qty_list)
|
||||
|
||||
columns = get_columns(qty_list)
|
||||
|
||||
return columns, data
|
||||
|
||||
def get_quote_list(item, qty_list):
|
||||
out = []
|
||||
if item:
|
||||
price_data = []
|
||||
suppliers = []
|
||||
company_currency = frappe.db.get_default("currency")
|
||||
float_precision = cint(frappe.db.get_default("float_precision")) or 2
|
||||
# Get the list of suppliers
|
||||
for root in frappe.db.sql("""select parent, qty, rate from `tabSupplier Quotation Item` where item_code=%s and docstatus < 2""", item, as_dict=1):
|
||||
for splr in frappe.db.sql("""SELECT supplier from `tabSupplier Quotation` where name =%s and docstatus < 2""", root.parent, as_dict=1):
|
||||
ip = frappe._dict({
|
||||
if not item:
|
||||
return []
|
||||
|
||||
suppliers = []
|
||||
price_data = []
|
||||
company_currency = frappe.db.get_default("currency")
|
||||
float_precision = cint(frappe.db.get_default("float_precision")) or 2
|
||||
# Get the list of suppliers
|
||||
for root in frappe.db.sql("""select parent, qty, rate from `tabSupplier Quotation Item`
|
||||
where item_code=%s and docstatus < 2""", item, as_dict=1):
|
||||
for splr in frappe.db.sql("""select supplier from `tabSupplier Quotation`
|
||||
where name =%s and docstatus < 2""", root.parent, as_dict=1):
|
||||
ip = frappe._dict({
|
||||
"supplier": splr.supplier,
|
||||
"qty": root.qty,
|
||||
"parent": root.parent,
|
||||
"rate": root.rate})
|
||||
price_data.append(ip)
|
||||
suppliers.append(splr.supplier)
|
||||
|
||||
#Add a row for each supplier
|
||||
for root in set(suppliers):
|
||||
supplier_currency = frappe.db.get_value("Supplier", root, "default_currency")
|
||||
if supplier_currency:
|
||||
exchange_rate = get_exchange_rate(supplier_currency, company_currency)
|
||||
else:
|
||||
exchange_rate = 1
|
||||
|
||||
row = frappe._dict({
|
||||
"supplier_name": root
|
||||
"rate": root.rate
|
||||
})
|
||||
for col in qty_list:
|
||||
# Get the quantity for this row
|
||||
for item_price in price_data:
|
||||
if str(item_price.qty) == col.key and item_price.supplier == root:
|
||||
row[col.key] = flt(item_price.rate * exchange_rate, float_precision)
|
||||
row[col.key + "QUOTE"] = item_price.parent
|
||||
break
|
||||
else:
|
||||
row[col.key] = ""
|
||||
row[col.key + "QUOTE"] = ""
|
||||
out.append(row)
|
||||
price_data.append(ip)
|
||||
suppliers.append(splr.supplier)
|
||||
|
||||
#Add a row for each supplier
|
||||
for root in set(suppliers):
|
||||
supplier_currency = frappe.db.get_value("Supplier", root, "default_currency")
|
||||
if supplier_currency:
|
||||
exchange_rate = get_exchange_rate(supplier_currency, company_currency)
|
||||
else:
|
||||
exchange_rate = 1
|
||||
|
||||
row = frappe._dict({
|
||||
"supplier_name": root
|
||||
})
|
||||
for col in qty_list:
|
||||
# Get the quantity for this row
|
||||
for item_price in price_data:
|
||||
if str(item_price.qty) == col.key and item_price.supplier == root:
|
||||
row[col.key] = flt(item_price.rate * exchange_rate, float_precision)
|
||||
row[col.key + "QUOTE"] = item_price.parent
|
||||
break
|
||||
else:
|
||||
row[col.key] = ""
|
||||
row[col.key + "QUOTE"] = ""
|
||||
out.append(row)
|
||||
|
||||
return out
|
||||
|
||||
@@ -62,7 +64,8 @@ def get_quantity_list(item):
|
||||
out = []
|
||||
|
||||
if item:
|
||||
qty_list = frappe.db.sql("""select distinct qty from `tabSupplier Quotation Item` where ifnull(item_code,'')=%s and docstatus < 2""", item, as_dict=1)
|
||||
qty_list = frappe.db.sql("""select distinct qty from `tabSupplier Quotation Item`
|
||||
where ifnull(item_code,'')=%s and docstatus < 2""", item, as_dict=1)
|
||||
qty_list.sort(reverse=False)
|
||||
for qt in qty_list:
|
||||
col = frappe._dict({
|
||||
@@ -98,4 +101,4 @@ def get_columns(qty_list):
|
||||
"width": 90
|
||||
})
|
||||
|
||||
return columns
|
||||
return columns
|
||||
@@ -315,11 +315,16 @@ def get_data():
|
||||
"name": "Payment Gateway Account",
|
||||
"description": _("Setup Gateway accounts.")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "POS Settings",
|
||||
"description": _("Setup mode of POS (Online / Offline)")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "POS Profile",
|
||||
"label": _("Point-of-Sale Profile"),
|
||||
"description": _("Rules to calculate shipping amount for a sale")
|
||||
"description": _("Setup default values for POS Invoices")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# coding=utf-8
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from frappe import _
|
||||
|
||||
@@ -127,7 +129,6 @@ def get_data():
|
||||
{
|
||||
"module_name": "POS",
|
||||
"color": "#589494",
|
||||
"icon": "fa fa-th",
|
||||
"icon": "octicon octicon-credit-card",
|
||||
"type": "page",
|
||||
"link": "pos",
|
||||
@@ -267,6 +268,30 @@ def get_data():
|
||||
"color": "#FF888B",
|
||||
"icon": "octicon octicon-plus",
|
||||
"type": "module",
|
||||
"label": _("Healthcare")
|
||||
"label": _("Healthcare"),
|
||||
},
|
||||
{
|
||||
"module_name": "Hub",
|
||||
"color": "#009248",
|
||||
"icon": "/assets/erpnext/images/hub_logo.svg",
|
||||
"type": "page",
|
||||
"link": "hub",
|
||||
"label": _("Hub")
|
||||
},
|
||||
{
|
||||
"module_name": "Data Import Tool",
|
||||
"color": "#7f8c8d",
|
||||
"icon": "octicon octicon-circuit-board",
|
||||
"type": "page",
|
||||
"link": "data-import-tool",
|
||||
"label": _("Data Import Tool")
|
||||
},
|
||||
{
|
||||
"module_name": "Restaurant",
|
||||
"color": "#EA81E8",
|
||||
"icon": "🍔",
|
||||
"_doctype": "Restaurant",
|
||||
"link": "List/Restaurant",
|
||||
"label": _("Restaurant")
|
||||
}
|
||||
]
|
||||
|
||||
@@ -176,6 +176,10 @@ def get_data():
|
||||
{
|
||||
"label": _("Training"),
|
||||
"items": [
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Training Program"
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Training Event"
|
||||
|
||||
24
erpnext/config/hub_node.py
Normal file
@@ -0,0 +1,24 @@
|
||||
from __future__ import unicode_literals
|
||||
from frappe import _
|
||||
|
||||
def get_data():
|
||||
return [
|
||||
{
|
||||
"label": _("Setup"),
|
||||
"items": [
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Hub Settings"
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": _("Hub"),
|
||||
"items": [
|
||||
{
|
||||
"type": "page",
|
||||
"name": "hub"
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
@@ -123,6 +123,12 @@ def get_data():
|
||||
"is_query_report": True,
|
||||
"name": "BOM Search",
|
||||
"doctype": "BOM"
|
||||
},
|
||||
{
|
||||
"type": "report",
|
||||
"is_query_report": True,
|
||||
"name": "BOM Stock Report",
|
||||
"doctype": "BOM"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -105,6 +105,11 @@ def get_data():
|
||||
"name": "Pricing Rule",
|
||||
"description": _("Rules for applying pricing and discount.")
|
||||
},
|
||||
{
|
||||
"type": "doctype",
|
||||
"name": "Item Variant Settings",
|
||||
"description": _("Item Variant Settings."),
|
||||
},
|
||||
|
||||
]
|
||||
},
|
||||
|
||||
@@ -15,8 +15,8 @@ from erpnext.exceptions import InvalidCurrency
|
||||
force_item_fields = ("item_group", "barcode", "brand", "stock_uom")
|
||||
|
||||
class AccountsController(TransactionBase):
|
||||
def __init__(self, arg1, arg2=None):
|
||||
super(AccountsController, self).__init__(arg1, arg2)
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(AccountsController, self).__init__(*args, **kwargs)
|
||||
|
||||
@property
|
||||
def company_currency(self):
|
||||
@@ -83,6 +83,9 @@ class AccountsController(TransactionBase):
|
||||
self.set(fieldname, today())
|
||||
break
|
||||
|
||||
# set taxes table if missing from `taxes_and_charges`
|
||||
self.set_taxes()
|
||||
|
||||
def calculate_taxes_and_totals(self):
|
||||
from erpnext.controllers.taxes_and_totals import calculate_taxes_and_totals
|
||||
calculate_taxes_and_totals(self)
|
||||
@@ -187,9 +190,6 @@ class AccountsController(TransactionBase):
|
||||
if stock_qty != len(get_serial_nos(item.get('serial_no'))):
|
||||
item.set(fieldname, value)
|
||||
|
||||
elif fieldname == "conversion_factor" and not item.get("conversion_factor"):
|
||||
item.set(fieldname, value)
|
||||
|
||||
if ret.get("pricing_rule"):
|
||||
# if user changed the discount percentage then set user's discount percentage ?
|
||||
item.set("discount_percentage", ret.get("discount_percentage"))
|
||||
@@ -262,9 +262,9 @@ class AccountsController(TransactionBase):
|
||||
if not account_currency:
|
||||
account_currency = get_account_currency(gl_dict.account)
|
||||
|
||||
if gl_dict.account and self.doctype not in ["Journal Entry",
|
||||
if gl_dict.account and self.doctype not in ["Journal Entry",
|
||||
"Period Closing Voucher", "Payment Entry"]:
|
||||
|
||||
|
||||
self.validate_account_currency(gl_dict.account, account_currency)
|
||||
set_balance_in_account_currency(gl_dict, account_currency, self.get("conversion_rate"), self.company_currency)
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _, msgprint
|
||||
from frappe.utils import flt,cint, cstr
|
||||
from frappe.utils import flt,cint, cstr, getdate
|
||||
|
||||
from erpnext.accounts.party import get_party_details
|
||||
from erpnext.stock.get_item_details import get_conversion_factor
|
||||
@@ -61,7 +61,7 @@ class BuyingController(StockController):
|
||||
|
||||
# set contact and address details for supplier, if they are not mentioned
|
||||
if getattr(self, "supplier", None):
|
||||
self.update_if_missing(get_party_details(self.supplier, party_type="Supplier", ignore_permissions=self.flags.ignore_permissions))
|
||||
self.update_if_missing(get_party_details(self.supplier, party_type="Supplier", ignore_permissions=self.flags.ignore_permissions, doctype=self.doctype, company=self.company))
|
||||
|
||||
self.set_missing_item_details(for_validate)
|
||||
|
||||
@@ -408,3 +408,16 @@ class BuyingController(StockController):
|
||||
"actual_qty": -1*flt(d.consumed_qty),
|
||||
}))
|
||||
|
||||
def validate_schedule_date(self):
|
||||
if not self.schedule_date:
|
||||
self.schedule_date = min([d.schedule_date for d in self.get("items")])
|
||||
|
||||
if self.schedule_date:
|
||||
for d in self.get('items'):
|
||||
if not d.schedule_date:
|
||||
d.schedule_date = self.schedule_date
|
||||
|
||||
if d.schedule_date and getdate(d.schedule_date) < getdate(self.transaction_date):
|
||||
frappe.throw(_("Expected Date cannot be before Transaction Date"))
|
||||
else:
|
||||
frappe.throw(_("Please enter Schedule Date"))
|
||||
@@ -5,7 +5,7 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import cstr, flt
|
||||
import json
|
||||
import json, copy
|
||||
|
||||
class ItemVariantExistsError(frappe.ValidationError): pass
|
||||
class InvalidItemAttributeValueError(frappe.ValidationError): pass
|
||||
@@ -174,28 +174,43 @@ def copy_attributes_to_variant(item, variant):
|
||||
|
||||
# copy non no-copy fields
|
||||
|
||||
exclude_fields = ["item_code", "item_name", "show_in_website"]
|
||||
exclude_fields = ["naming_series", "item_code", "item_name", "show_in_website",
|
||||
"show_variant_in_website", "opening_stock", "variant_of", "valuation_rate"]
|
||||
|
||||
if item.variant_based_on=='Manufacturer':
|
||||
# don't copy manufacturer values if based on part no
|
||||
exclude_fields += ['manufacturer', 'manufacturer_part_no']
|
||||
|
||||
allow_fields = [d.field_name for d in frappe.get_all("Variant Field", fields = ['field_name'])]
|
||||
if "variant_based_on" not in allow_fields:
|
||||
allow_fields.append("variant_based_on")
|
||||
for field in item.meta.fields:
|
||||
# "Table" is part of `no_value_field` but we shouldn't ignore tables
|
||||
if (field.fieldtype == 'Table' or field.fieldtype not in no_value_fields) \
|
||||
and (not field.no_copy) and field.fieldname not in exclude_fields:
|
||||
if (field.reqd or field.fieldname in allow_fields) and field.fieldname not in exclude_fields:
|
||||
if variant.get(field.fieldname) != item.get(field.fieldname):
|
||||
variant.set(field.fieldname, item.get(field.fieldname))
|
||||
if field.fieldtype == "Table":
|
||||
variant.set(field.fieldname, [])
|
||||
for d in item.get(field.fieldname):
|
||||
row = copy.deepcopy(d)
|
||||
if row.get("name"):
|
||||
row.name = None
|
||||
variant.append(field.fieldname, row)
|
||||
else:
|
||||
variant.set(field.fieldname, item.get(field.fieldname))
|
||||
|
||||
variant.variant_of = item.name
|
||||
variant.has_variants = 0
|
||||
if not variant.description:
|
||||
variant.description = ''
|
||||
variant.description = ""
|
||||
|
||||
if item.variant_based_on=='Item Attribute':
|
||||
if variant.attributes:
|
||||
variant.description += "\n"
|
||||
attributes_description = ""
|
||||
for d in variant.attributes:
|
||||
variant.description += "<p>" + d.attribute + ": " + cstr(d.attribute_value) + "</p>"
|
||||
attributes_description += "<div>" + d.attribute + ": " + cstr(d.attribute_value) + "</div>"
|
||||
|
||||
if attributes_description not in variant.description:
|
||||
variant.description += attributes_description
|
||||
|
||||
def make_variant_item_code(template_item_code, template_item_name, variant):
|
||||
"""Uses template's item code and abbreviations to make variant's item code"""
|
||||
|
||||
@@ -165,6 +165,7 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals
|
||||
and (tabItem.`{key}` LIKE %(txt)s
|
||||
or tabItem.item_group LIKE %(txt)s
|
||||
or tabItem.item_name LIKE %(txt)s
|
||||
or tabItem.barcode LIKE %(txt)s
|
||||
or tabItem.description LIKE %(txt)s)
|
||||
{fcond} {mcond}
|
||||
order by
|
||||
@@ -172,7 +173,8 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals
|
||||
if(locate(%(_txt)s, item_name), locate(%(_txt)s, item_name), 99999),
|
||||
idx desc,
|
||||
name, item_name
|
||||
limit %(start)s, %(page_len)s """.format(key=searchfield,
|
||||
limit %(start)s, %(page_len)s """.format(
|
||||
key=searchfield,
|
||||
fcond=get_filters_cond(doctype, filters, conditions).replace('%', '%%'),
|
||||
mcond=get_match_cond(doctype).replace('%', '%%')),
|
||||
{
|
||||
|
||||
@@ -49,7 +49,8 @@ class SellingController(StockController):
|
||||
if getattr(self, "customer", None):
|
||||
from erpnext.accounts.party import _get_party_details
|
||||
party_details = _get_party_details(self.customer,
|
||||
ignore_permissions=self.flags.ignore_permissions)
|
||||
ignore_permissions=self.flags.ignore_permissions,
|
||||
doctype=self.doctype, company=self.company)
|
||||
if not self.meta.get_field("sales_team"):
|
||||
party_details.pop("sales_team")
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import frappe
|
||||
import json
|
||||
import unittest
|
||||
|
||||
from erpnext.stock.doctype.item.test_item import set_item_variant_settings
|
||||
from erpnext.controllers.item_variant import copy_attributes_to_variant, make_variant_item_code
|
||||
|
||||
# python 3 compatibility stuff
|
||||
@@ -54,5 +55,7 @@ def make_item_variant():
|
||||
|
||||
class TestItemVariant(unittest.TestCase):
|
||||
def test_tables_in_template_copied_to_variant(self):
|
||||
fields = [{'field_name': 'quality_parameters'}]
|
||||
set_item_variant_settings(fields)
|
||||
variant = make_item_variant()
|
||||
self.assertNotEqual(variant.get("quality_parameters"), [])
|
||||
|
||||
@@ -42,10 +42,28 @@ class Opportunity(TransactionBase):
|
||||
if not self.with_items:
|
||||
self.items = []
|
||||
|
||||
|
||||
def make_new_lead_if_required(self):
|
||||
"""Set lead against new opportunity"""
|
||||
if not (self.lead or self.customer) and self.contact_email:
|
||||
# check if customer is already created agains the self.contact_email
|
||||
customer = frappe.db.sql("""select
|
||||
distinct `tabDynamic Link`.link_name as customer
|
||||
from
|
||||
`tabContact`,
|
||||
`tabDynamic Link`
|
||||
where `tabContact`.email_id='{0}'
|
||||
and
|
||||
`tabContact`.name=`tabDynamic Link`.parent
|
||||
and
|
||||
ifnull(`tabDynamic Link`.link_name, '')<>''
|
||||
and
|
||||
`tabDynamic Link`.link_doctype='Customer'
|
||||
""".format(self.contact_email), as_dict=True)
|
||||
if customer and customer[0].customer:
|
||||
self.customer = customer[0].customer
|
||||
self.enquiry_from = "Customer"
|
||||
return
|
||||
|
||||
lead_name = frappe.db.get_value("Lead", {"email_id": self.contact_email})
|
||||
if not lead_name:
|
||||
sender_name = get_fullname(self.contact_email)
|
||||
@@ -245,6 +263,27 @@ def make_quotation(source_name, target_doc=None):
|
||||
|
||||
return doclist
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_request_for_quotation(source_name, target_doc=None):
|
||||
doclist = get_mapped_doc("Opportunity", source_name, {
|
||||
"Opportunity": {
|
||||
"doctype": "Request for Quotation",
|
||||
"validation": {
|
||||
"enquiry_type": ["=", "Sales"]
|
||||
}
|
||||
},
|
||||
"Opportunity Item": {
|
||||
"doctype": "Request for Quotation Item",
|
||||
"field_map": [
|
||||
["name", "opportunity_item"],
|
||||
["parent", "opportunity"],
|
||||
["uom", "uom"]
|
||||
]
|
||||
}
|
||||
}, target_doc)
|
||||
|
||||
return doclist
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_supplier_quotation(source_name, target_doc=None):
|
||||
doclist = get_mapped_doc("Opportunity", source_name, {
|
||||
@@ -284,4 +323,4 @@ def auto_close_opportunity():
|
||||
doc.status = "Closed"
|
||||
doc.flags.ignore_permissions = True
|
||||
doc.flags.ignore_mandatory = True
|
||||
doc.save()
|
||||
doc.save()
|
||||
|
||||
@@ -4,6 +4,7 @@ from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
from frappe.utils import today
|
||||
from erpnext.crm.doctype.lead.lead import make_customer
|
||||
from erpnext.crm.doctype.opportunity.opportunity import make_quotation
|
||||
import unittest
|
||||
|
||||
@@ -25,12 +26,45 @@ class TestOpportunity(unittest.TestCase):
|
||||
doc = frappe.get_doc('Opportunity', doc.name)
|
||||
self.assertEquals(doc.status, "Quotation")
|
||||
|
||||
def test_make_new_lead_if_required(self):
|
||||
args = {
|
||||
"doctype": "Opportunity",
|
||||
"contact_email":"new.opportunity@example.com",
|
||||
"enquiry_type": "Sales",
|
||||
"with_items": 0,
|
||||
"transaction_date": today()
|
||||
}
|
||||
# new lead should be created against the new.opportunity@example.com
|
||||
opp_doc = frappe.get_doc(args).insert(ignore_permissions=True)
|
||||
|
||||
self.assertTrue(opp_doc.lead)
|
||||
self.assertEquals(opp_doc.enquiry_from, "Lead")
|
||||
self.assertEquals(frappe.db.get_value("Lead", opp_doc.lead, "email_id"),
|
||||
'new.opportunity@example.com')
|
||||
|
||||
# create new customer and create new contact against 'new.opportunity@example.com'
|
||||
customer = make_customer(opp_doc.lead).insert(ignore_permissions=True)
|
||||
frappe.get_doc({
|
||||
"doctype": "Contact",
|
||||
"email_id": "new.opportunity@example.com",
|
||||
"first_name": "_Test Opportunity Customer",
|
||||
"links": [{
|
||||
"link_doctype": "Customer",
|
||||
"link_name": customer.name
|
||||
}]
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
opp_doc = frappe.get_doc(args).insert(ignore_permissions=True)
|
||||
self.assertTrue(opp_doc.customer)
|
||||
self.assertEquals(opp_doc.enquiry_from, "Customer")
|
||||
self.assertEquals(opp_doc.customer, customer.name)
|
||||
|
||||
def make_opportunity(**args):
|
||||
args = frappe._dict(args)
|
||||
|
||||
opp_doc = frappe.get_doc({
|
||||
"doctype": "Opportunity",
|
||||
"enquiry_from": "Customer" or args.enquiry_from,
|
||||
"enquiry_from": args.enquiry_from or "Customer",
|
||||
"enquiry_type": "Sales",
|
||||
"with_items": args.with_items or 0,
|
||||
"transaction_date": today()
|
||||
|
||||
@@ -21,23 +21,13 @@ frappe.query_reports["Minutes to First Response for Opportunity"] = {
|
||||
get_chart_data: function (columns, result) {
|
||||
return {
|
||||
data: {
|
||||
x: 'Date',
|
||||
columns: [
|
||||
['Date'].concat($.map(result, function (d) { return d[0]; })),
|
||||
['Mins to first response'].concat($.map(result, function (d) { return d[1]; }))
|
||||
]
|
||||
// rows: [['Date', 'Mins to first response']].concat(result)
|
||||
labels: result.map(d => d[0]),
|
||||
datasets: [{
|
||||
title: 'Mins to first response',
|
||||
values: result.map(d => d[1])
|
||||
}]
|
||||
},
|
||||
axis: {
|
||||
x: {
|
||||
type: 'timeseries',
|
||||
tick: {
|
||||
format: frappe.ui.py_date_format
|
||||
}
|
||||
}
|
||||
},
|
||||
chart_type: 'line',
|
||||
|
||||
type: 'line',
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,5 +278,16 @@
|
||||
"item_code": "Autocad",
|
||||
"item_name": "Autocad",
|
||||
"item_group": "All Item Groups"
|
||||
},
|
||||
{
|
||||
"is_stock_item": 1,
|
||||
"has_batch_no": 1,
|
||||
"create_new_batch": 1,
|
||||
"valuation_rate": 200,
|
||||
"default_warehouse": "Stores",
|
||||
"description": "Corrugated Box",
|
||||
"item_code": "Corrugated Box",
|
||||
"item_name": "Corrugated Box",
|
||||
"item_group": "All Item Groups"
|
||||
}
|
||||
]
|
||||
@@ -16,6 +16,7 @@ def setup(domain):
|
||||
setup_user()
|
||||
setup_employee()
|
||||
setup_user_roles()
|
||||
setup_role_permissions()
|
||||
|
||||
employees = frappe.get_all('Employee', fields=['name', 'date_of_joining'])
|
||||
|
||||
@@ -91,7 +92,8 @@ def setup_fiscal_year():
|
||||
pass
|
||||
|
||||
# set the last fiscal year (current year) as default
|
||||
fiscal_year.set_as_default()
|
||||
if fiscal_year:
|
||||
fiscal_year.set_as_default()
|
||||
|
||||
def setup_holiday_list():
|
||||
"""Setup Holiday List for the current year"""
|
||||
@@ -374,6 +376,22 @@ def setup_pos_profile():
|
||||
|
||||
pos.insert()
|
||||
|
||||
def setup_role_permissions():
|
||||
role_permissions = {'Batch': ['Accounts User', 'Item Manager']}
|
||||
for doctype, roles in role_permissions.items():
|
||||
for role in roles:
|
||||
if not frappe.db.get_value('Custom DocPerm',
|
||||
{'parent': doctype, 'role': role}):
|
||||
frappe.get_doc({
|
||||
'doctype': 'Custom DocPerm',
|
||||
'role': role,
|
||||
'read': 1,
|
||||
'write': 1,
|
||||
'create': 1,
|
||||
'delete': 1,
|
||||
'parent': doctype
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
def import_json(doctype, submit=False, values=None):
|
||||
frappe.flags.in_import = True
|
||||
data = json.loads(open(frappe.get_app_path('erpnext', 'demo', 'data',
|
||||
|
||||
@@ -103,6 +103,7 @@ def make_material_request(item_code, qty):
|
||||
mr.material_request_type = "Purchase"
|
||||
|
||||
mr.transaction_date = frappe.flags.current_date
|
||||
mr.schedule_date = frappe.utils.add_days(mr.transaction_date, 7)
|
||||
|
||||
mr.append("items", {
|
||||
"doctype": "Material Request Item",
|
||||
@@ -128,6 +129,7 @@ def make_subcontract():
|
||||
po = frappe.new_doc("Purchase Order")
|
||||
po.is_subcontracted = "Yes"
|
||||
po.supplier = get_random("Supplier")
|
||||
po.schedule_date = frappe.utils.add_days(frappe.flags.current_date, 7)
|
||||
|
||||
item_code = get_random("Item", {"is_sub_contracted_item": 1})
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import frappe, random
|
||||
from frappe.desk import query_report
|
||||
from erpnext.stock.stock_ledger import NegativeStockError
|
||||
from erpnext.stock.doctype.serial_no.serial_no import SerialNoRequiredError, SerialNoQtyError
|
||||
from erpnext.stock.doctype.batch.batch import UnableToSelectBatchError
|
||||
from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_return
|
||||
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_return
|
||||
|
||||
@@ -59,7 +60,7 @@ def make_delivery_note():
|
||||
try:
|
||||
dn.submit()
|
||||
frappe.db.commit()
|
||||
except (NegativeStockError, SerialNoRequiredError, SerialNoQtyError):
|
||||
except (NegativeStockError, SerialNoRequiredError, SerialNoQtyError, UnableToSelectBatchError):
|
||||
frappe.db.rollback()
|
||||
|
||||
def make_stock_reconciliation():
|
||||
|
||||
|
Before Width: | Height: | Size: 90 KiB After Width: | Height: | Size: 70 KiB |
BIN
erpnext/docs/assets/img/human-resources/training_program.png
Normal file
|
After Width: | Height: | Size: 63 KiB |
BIN
erpnext/docs/assets/img/restaurant/order-entry-bill.png
Normal file
|
After Width: | Height: | Size: 207 KiB |
BIN
erpnext/docs/assets/img/restaurant/order-entry.png
Normal file
|
After Width: | Height: | Size: 218 KiB |
BIN
erpnext/docs/assets/img/restaurant/reservation-kanban.png
Normal file
|
After Width: | Height: | Size: 208 KiB |
BIN
erpnext/docs/assets/img/restaurant/restaurant-invoice.png
Normal file
|
After Width: | Height: | Size: 195 KiB |
BIN
erpnext/docs/assets/img/restaurant/restaurant-menu.png
Normal file
|
After Width: | Height: | Size: 202 KiB |
BIN
erpnext/docs/assets/img/restaurant/restaurant-reservation.png
Normal file
|
After Width: | Height: | Size: 223 KiB |
BIN
erpnext/docs/assets/img/restaurant/restaurant-table.png
Normal file
|
After Width: | Height: | Size: 201 KiB |
BIN
erpnext/docs/assets/img/restaurant/restaurant.png
Normal file
|
After Width: | Height: | Size: 260 KiB |
BIN
erpnext/docs/assets/img/schools/admission/student-admission.gif
Normal file
|
After Width: | Height: | Size: 2.5 MiB |
BIN
erpnext/docs/assets/img/stock/item_variants_settings.png
Normal file
|
After Width: | Height: | Size: 45 KiB |
@@ -16,7 +16,9 @@ To set credit limit go to Customer - Master
|
||||
|
||||
<img class="screenshot" alt="Credit Limit" src="/docs/assets/img/accounts/credit-limit-1.png">
|
||||
|
||||
Go to the 'More Info section' and enter the amount in the field Credit Limit.
|
||||
Go to the 'CREDIT LIMIT' section and enter the amount in the field Credit Limit.
|
||||
|
||||
If you leave CREDIT LIMIT as 0.00, it has no effect.
|
||||
|
||||
In case a need arises to allow more credit to the customer as a good-will, the
|
||||
Credit Controller has access to submit order even if credit limit is crossed.
|
||||
@@ -26,6 +28,25 @@ has expired, go to accounting settings and make changes.
|
||||
|
||||
In the field Credit Controller, select the role who would be authorized to
|
||||
accept orders or raise credit limits of customers.
|
||||
|
||||
To set credit limit at Customer Group Level go to Selling -> Customers -> Customer Group
|
||||
|
||||
Go to the 'CREDIT LIMIT' field and enter the amount.
|
||||
If you leave CREDIT LIMIT as 0.00, it has no effect.
|
||||
|
||||
|
||||
To set credit limit at Company level go to Account -> Company
|
||||
|
||||
Go to the 'ACCOUNT SETTINGS' section and enter the amount in the CREDIT LIMIT field.
|
||||
If you leave CREDIT LIMIT as 0.00, it has no effect.
|
||||
|
||||
For 'CREDIT LIMIT' check functionality, Priority (High to Low) is as below
|
||||
1) Customer
|
||||
2) Customer Group
|
||||
3) Company
|
||||
|
||||
|
||||
|
||||
|
||||
#### Figure 2: Credit Controller
|
||||
|
||||
|
||||
@@ -13,4 +13,8 @@
|
||||
|
||||
* Unlink Payment on Cancellation of Invoice: If checked, system will unlink the payment against the invoice. Otherwise, it will show the link error.
|
||||
|
||||
* Allow Stale Exchange Rate: This should be unchecked if you want ERPNext to check the age of records fetched from Currency Exchange in foreign currency transactions. If it is unchecked, the exchange rate field will be read-only in documents.
|
||||
|
||||
* Stale Days: The number of days to use when deciding if a Currency Exchange record is stale. E.g If Currency Exchange records are to be updated every day, the Stale Days should be set as 1.
|
||||
|
||||
{next}
|
||||
|
||||
9
erpnext/docs/user/manual/en/hospitality/index.md
Normal file
@@ -0,0 +1,9 @@
|
||||
# Hospitality
|
||||
|
||||
ERPNext Hospitality module is designed to handle workflows for Hotels and Restaurants. This is still in early development stage.
|
||||
|
||||
### Manage Restaurants
|
||||
|
||||
The Restaurant module in ERPNext will help you manage a chain of restaurants. You can create Restaurants, Menus, Tables, Reservations and a manage Order Entry and Billing.
|
||||
|
||||
{index}
|
||||
4
erpnext/docs/user/manual/en/hospitality/index.txt
Normal file
@@ -0,0 +1,4 @@
|
||||
restaurant
|
||||
restaurant-menu
|
||||
reservations
|
||||
order-entry
|
||||
26
erpnext/docs/user/manual/en/hospitality/order-entry.md
Normal file
@@ -0,0 +1,26 @@
|
||||
# Restaurant Order Entry
|
||||
|
||||
The Restaurant Order Entry is the screen where the waiters will punch in orders related to a particular table.
|
||||
|
||||
This screen makes it easy for the waiters in your restaurant to punch in orders from various tables.
|
||||
|
||||
When the guest places an order, the waiter will select the table number and add the items in the Order Entry. This can be changed until it is time for the bill. Unless you bill a table, you can change the items and they will automatically appear when you select the table ID.
|
||||
|
||||
To place an order you can select an item and click the enter key so that the item will be updated in the items table.
|
||||
|
||||
<img class="screenshot" alt="Order Entry" src="/docs/assets/img/restaurant/order-entry.png">
|
||||
|
||||
You can also choose items with the POS style item selector.
|
||||
|
||||
### Billing
|
||||
|
||||
When it is time for billing, you just choose the bill and you can select the customer and mode of payment. On saving, a Sales Invoice is generated and the order section becomes empty.
|
||||
|
||||
<img class="screenshot" alt="Order Entry" src="/docs/assets/img/restaurant/order-entry-bill.png">
|
||||
|
||||
### Sales Invoice
|
||||
|
||||
To print the invoice, you can click on the Invoice Link and print the invoice
|
||||
|
||||
<img class="screenshot" alt="Sales Invoice" src="/docs/assets/img/restaurant/restaurant-invoice.png">
|
||||
|
||||
13
erpnext/docs/user/manual/en/hospitality/reservations.md
Normal file
@@ -0,0 +1,13 @@
|
||||
# Restaurant Reservations
|
||||
|
||||
Once you have setup the restaurant and tables, you can start taking in reservations for your restaurant.
|
||||
|
||||
To take a reservation, just make a new Restaurant Reservation from the Restaurant Page and set the time, number of people and name of the guest.
|
||||
|
||||
<img class="screenshot" alt="Reservation" src="/docs/assets/img/restaurant/reservation.png">
|
||||
|
||||
### Kanban
|
||||
|
||||
As your guests walk in, You can also manage the reservations by making a simple Kanban board for the same.
|
||||
|
||||
<img class="screenshot" alt="Reservation Kanban Board" src="/docs/assets/img/restaurant/reservation-kanban.png">
|
||||
@@ -0,0 +1,7 @@
|
||||
# Restaurant Menu
|
||||
|
||||
For every restaurant you must set an active Restaurant Menu from which orders can be placed. You can also set the rates for each of the item for the day.
|
||||
|
||||
When you save the Restaurant Menu, a Price List is created for that Menu and all pricing is linked to that price list. This way you can easily control the items on offer and pricing from the menu.
|
||||
|
||||
<img class="screenshot" alt="Restaurant Menu" src="/docs/assets/img/restaurant/restaurant-menu.png">
|
||||
19
erpnext/docs/user/manual/en/hospitality/restaurant.md
Normal file
@@ -0,0 +1,19 @@
|
||||
# Restaurant
|
||||
|
||||
The Restaurant record represents one restaurant in your organization. To create a new Restaurant, just set the name, Company and Default Customer.
|
||||
|
||||
You can set a unique numbering prefix for each of your restaurants. All invoices for that restuarant will follow that numbering prefix.
|
||||
|
||||
If you have a default Sales Taxes and Charges Template, you can add it so that the same charge + tax will be applicable for all invoices in the restaurant.
|
||||
|
||||
<img class="screenshot" alt="Restaurant" src="/docs/assets/img/restaurant/restaurant.png">
|
||||
|
||||
After your restaurant is created, you can add Tables and Menus for that restaurant
|
||||
|
||||
### Adding Tables
|
||||
|
||||
You can add a Restaurant Table by creating a new Restaurant Table from the dashboard.
|
||||
|
||||
<img class="screenshot" alt="Restaurant Table" src="/docs/assets/img/restaurant/restaurant-table.png">
|
||||
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
# Training
|
||||
### Training Program
|
||||
|
||||
Create Training Program and schedule Training Events under it. It has a dashboard linked to Training Event to view which event is under the Training Program.
|
||||
|
||||
<img class="screenshot" alt="Employee" src="/docs/assets/img/human-resources/training_program.png">
|
||||
|
||||
### Training Event
|
||||
|
||||
Schedule seminars, workshops, conferences etc using Training Event. You can also invite your employees to attend the event using this feature.
|
||||
Schedule seminars, workshops, conferences etc using Training Event linked to a Training Program. You can also invite your employees to attend the event using this feature.
|
||||
|
||||
<img class="screenshot" alt="Employee" src="/docs/assets/img/human-resources/training_event.png">
|
||||
|
||||
@@ -14,11 +19,11 @@ By default the status of the employee will be 'Open'.
|
||||
|
||||
<img class="screenshot" alt="Employee" src="/docs/assets/img/human-resources/training_event_employee.png">
|
||||
|
||||
When you submit the Training Event, a notifcation will be sent to the employee notifying that the Training has been scheduled. This is sent via Email Alert "Training Scheduled". You can modifiy this Email Alert to customize the message.
|
||||
When you submit the Training Event, a notification will be sent to the employee notifying that the Training has been scheduled. This is sent via Email Alert "Training Scheduled". You can modify this Email Alert to customize the message.
|
||||
|
||||
### Training Result
|
||||
|
||||
After compleation of the training Employee-wise training results can be stored based on the Feedback received from the Trainer.
|
||||
After completion of the training Employee-wise training results can be stored based on the Feedback received from the Trainer.
|
||||
|
||||
<img class="screenshot" alt="Employee" src="/docs/assets/img/human-resources/training_result.png">
|
||||
|
||||
|
||||
@@ -97,8 +97,8 @@ fiscal year.
|
||||
|
||||
A Cost Center is like an Account, but the only difference is that its
|
||||
structure represents your business more closely than Accounts.
|
||||
For example, in your Chart of Accounts, you can separate your expenses by its type
|
||||
(i.e., travel, marketing, etc.). In your Chart of Cost Centers, you can separate
|
||||
For example, in your Chart of Accounts, you can separate your expenses by its type
|
||||
(i.e., travel, marketing, etc.). In your Chart of Cost Centers, you can separate
|
||||
them by product line or business group (e.g., online sales, retail sales, etc.).
|
||||
|
||||
> Accounts > Chart of Cost Centers
|
||||
@@ -316,7 +316,7 @@ A record of the monthly salary given to an Employee.
|
||||
|
||||
#### Salary Structure
|
||||
|
||||
A template identifying all the components of an Employees' salary (earnings),
|
||||
A template identifying all the components of an Employees' salary (earnings),
|
||||
tax and other social security deductions.
|
||||
|
||||
> Human Resource > Salary and Payroll > Salary Structure
|
||||
|
||||
@@ -1,7 +1,3 @@
|
||||
# Getting Started With Erpnext
|
||||
|
||||
<!-- Getting Started with ERPNext-->
|
||||
|
||||
# Getting Started with ERPNext
|
||||
|
||||
There are many ways to get started with ERPNext.
|
||||
|
||||