Compare commits

...

109 Commits

Author SHA1 Message Date
Saurabh
734e635ef6 Merge branch 'hotfix' 2017-07-27 17:50:27 +05:30
Saurabh
6f9ef5b890 bumped to version 8.6.3 2017-07-27 18:20:27 +06:00
Makarand Bauskar
145393b12f [hotfix] set the account name in GL entry instead of warehouse (#10134) 2017-07-27 16:46:40 +05:30
Makarand Bauskar
9c6e2c3637 [hotfix] included the get_purchase_trends_filters instead of get_sales_trends_filters in Purchase Receipts trends (#10127) 2017-07-27 16:23:57 +05:30
mbauskar
cfc2693b2e Merge branch 'hotfix' 2017-07-27 12:18:15 +05:30
mbauskar
448d919cc1 bumped to version 8.6.2 2017-07-27 12:48:15 +06:00
tundebabzy
87ec6a12ef adds options to currency fields (#10118) 2017-07-27 11:38:17 +05:30
rohitwaghchaure
059f99e621 [Fix] Wrong avg. buying rate in the Gross Profit report (#10110) 2017-07-27 11:37:30 +05:30
Saurabh
4c28fa77bd Merge branch 'hotfix' 2017-07-25 18:02:58 +05:30
Saurabh
394dbca0e4 bumped to version 8.6.1 2017-07-25 18:32:58 +06:00
Saurabh
33ebd9f88e Merge pull request #10088 from saurabh6790/hot_fix_v_8_6_0
[hot][fix] updated modified date for sales order
2017-07-25 17:58:12 +05:30
Saurabh
4c1caa7e98 [hot][fix] updated modified date for sales order 2017-07-25 17:35:14 +05:30
Saurabh
08c3b3c925 Merge branch 'staging' 2017-07-25 16:42:27 +05:30
Saurabh
bb5812cf0f bumped to version 8.6.0 2017-07-25 17:12:26 +06:00
Saurabh
36645e4e2f Merge branch 'master' into staging 2017-07-25 16:15:51 +05:30
Saurabh
b296bb1551 Merge branch 'hotfix' 2017-07-25 16:15:50 +05:30
Saurabh
e7c14fcc3d bumped to version 8.5.5 2017-07-25 16:45:49 +06:00
Saurabh
31bb34bbae Merge pull request #10081 from rohitwaghchaure/calendar_view_SO_issue_fix
[Fix] Calendar view for sales order
2017-07-25 15:54:09 +05:30
Saurabh
6436b9d089 Merge pull request #10080 from rohitwaghchaure/hotfix
[Fix] Negative amount showing in the grand total for multicurrency if discount has applied
2017-07-25 15:43:00 +05:30
Rohit Waghchaure
d62fa84ed9 [Fix] Calendar view for sales order 2017-07-25 15:34:20 +05:30
Rohit Waghchaure
baa937aa52 [Fix] Negative amount showing in the grand total for multicurrency if discount has applied 2017-07-25 15:26:01 +05:30
Frappe PR Bot
3b128cabb2 [Translation] Updated Translations (#10047) 2017-07-25 11:46:16 +05:30
Makarand Bauskar
dfc5a454b3 [minor] fixed the delivery date issue in Ordered Item to be Dellivered report (#10028) 2017-07-25 10:48:43 +05:30
bcornwellmott
1c32f5ace9 Whitelist method for adding production orders ops (#9997) 2017-07-24 22:43:44 +05:30
Prateeksha Singh
283d5550e6 [fix] set sales base field as base_grand_total (#10008) 2017-07-21 14:21:35 +05:30
Rushabh Mehta
dda608dd00 [fix] update_company_current_month_sales in company.py (#10005) 2017-07-21 11:58:14 +05:30
Faris Ansari
e355f99786 Remove ellipsis in title and description (#9992) 2017-07-20 17:51:19 +05:30
mbauskar
27334c28a9 Merge branch 'hotfix' 2017-07-20 16:49:30 +05:30
mbauskar
6c8d4678db Merge branch 'master' into develop 2017-07-20 16:49:30 +05:30
mbauskar
ea50c9d1be bumped to version 8.5.4 2017-07-20 17:19:30 +06:00
Rushabh Mehta
35da7d1fb4 [minor] we buy is also checked by default 2017-07-20 15:58:20 +05:30
Rushabh Mehta
ced14cc789 [fix] setup-wizard 2017-07-20 15:55:54 +05:30
Rushabh Mehta
9d27cf3c62 [tests] refactored (#9984)
* [tests] refactored

* [fix] test_quotation.js

* [fix] tests.text

* [fix] fiscal year not needed

* [test] add long test

* [fix] add timeout in lead
2017-07-20 15:35:03 +05:30
mbauskar
0e6933a1e8 resolved merge conflicts 2017-07-20 14:19:47 +05:30
mbauskar
51a76885b8 Merge branch 'hotfix' 2017-07-20 14:16:57 +05:30
mbauskar
a919be111a bumped to version 8.5.3 2017-07-20 14:46:57 +06:00
Nabin Hait
9c42161061 Itemised tax breakup fix in docs other than invoice (#9961)
* Itemised tax breakup fix in docs other than invoice

* Set itemised tax breakup and hsn code in existing docs
2017-07-20 13:32:01 +05:30
rohitwaghchaure
79d6266c7b [Fix] Unable to create production order from the sales order for the bundle items (#9976) 2017-07-20 10:35:51 +05:30
tundebabzy
11d23f84d7 Can't set Start and End Dates in Salary Slip (#9513) (#9944)
* remove trigger from end_date

* adds new function `get_end_date`:
- it tries to calculate the appropriate end date for a given frequency
- returns an empty string if frequency is 'biweekly'
- adds test cases

* changes logic in `set_start_end_dates`:
- if start_date is empty in form, call process_payroll.get_start_end_dates
- else, call process_payroll.get_end_date

* `get_end_date` should return a dict

* changed "biweekly" to "bimonthly"

* change the behaviour of process payroll start and end date:
- when payroll frequency is changed, change start/end date as usual
- if start date is manually changed, use the frequency to calculate the end date

* clean up

* further cleanup

* in `get_end_date`, if `frequency` isn"t given, "monthly"

* remove end_date from cscript and introduce `set_end_date`

* fix tests

* removed whitespaces
2017-07-20 10:33:03 +05:30
Nabin Hait
8e0f23efc7 Multiple delivery dates in Sales Order and make DN based on selected delivery dates (#9933)
* Multiple delivery dates in Sales Order and make DN based on selected delivery dates

* Test case and some other minor fixes

* Updated docs for multi delivery date

* removed the trailing whitespace

* removed the trailing whitespace

* removed trailing whitespace
2017-07-20 10:30:59 +05:30
Makarand Bauskar
ac9b1332d2 [minor] set no copy to sales goal fields (#9956) 2017-07-19 18:55:12 +05:30
Doridel Cahanap
7b021e0fac Show Expected End Date in Project List View (#9964) 2017-07-19 18:21:31 +05:30
KanchanChauhan
d6dd25a666 Added indicators on Project Tasks (#9952) 2017-07-19 18:06:52 +05:30
rohitwaghchaure
f86100a734 [Fix] Calendar view not working for leave application (#9963) 2017-07-19 17:09:30 +05:30
_JG_
96bb6099d6 [fix] correctly choice 'Credit Limit' (#9932)
* [fix] correctly choice 'credit limit'

If 'Credit Days Based On' is empty in 'Supplier Type/Customer Group' then value 'Due Date' in 'Purchase/Sales Invoice' doesn't taken from the default settings

* Update party.py

* [fix] correctly choice 'Credit Limit'
2017-07-19 10:27:09 +05:30
Nabin Hait
e6c2ae3682 Merge pull request #9940 from rmehta/workstation-form
[ux] workstation form layout
2017-07-19 10:24:28 +05:30
Nabin Hait
8d56f2959b Merge pull request #9942 from bcornwellmott/bix_bom_qty
Update qty from stock_qty before validate materials
2017-07-19 10:22:00 +05:30
Rushabh Mehta
d2d24554b3 [ux] workstation form layout 2017-07-18 22:29:59 +05:30
Ben Cornwell-Mott
c7d2bc67e8 Update qty from stock_qty before validate materials 2017-07-18 08:56:25 -07:00
Rushabh Mehta
1283f6308d [fix] company graph based on base currency 2017-07-18 18:22:16 +05:30
mbauskar
d36c136fc6 Merge branch 'master' into develop 2017-07-18 16:38:24 +05:30
mbauskar
3951f6971e Merge branch 'hotfix' 2017-07-18 16:38:23 +05:30
mbauskar
75e65e7079 bumped to version 8.5.2 2017-07-18 17:08:23 +06:00
Saurabh
7f95d587b2 [fix] escape company filter (#9924) 2017-07-18 16:09:34 +05:30
Makarand Bauskar
75b145fe2c [hotfix] used frappe.db.set value instead of frappe.set_value (#9923) 2017-07-18 16:05:52 +05:30
Nabin Hait
bc3acdd0ba Merge pull request #9510 from tundebabzy/issue-9166
Unable to create Manual Depreciation of Asset due to Error #9166
2017-07-18 15:44:18 +05:30
mbauskar
8f42f60dc9 Merge branch 'hotfix' 2017-07-18 15:10:19 +05:30
mbauskar
823b3ca540 bumped to version 8.5.1 2017-07-18 15:40:19 +06:00
Makarand Bauskar
5e75e3ba03 [hotfix] set_restrict_to_domain_for_module_def patch fixes (#9921) 2017-07-18 15:09:30 +05:30
mbauskar
113df55e64 Merge branch 'develop' 2017-07-18 13:07:01 +05:30
mbauskar
3e4b2743c6 bumped to version 8.5.0 2017-07-18 13:37:00 +06:00
Nabin Hait
338c28e78e Merge pull request #9902 from rohitwaghchaure/sales_invoice_serial_no_issue_from_dn
[Fix] Sales invoice serial no validation issue
2017-07-18 13:05:50 +05:30
Rohit Waghchaure
7e14996995 [Fix] Sales invoice serial no validation issue 2017-07-18 12:59:55 +05:30
Makarand Bauskar
6e30f04181 [domainify] patch to set the restrict to domain for module_def (#9912) 2017-07-18 12:30:57 +05:30
Nabin Hait
4a10f18ee3 Merge pull request #9913 from mbauskar/quotation
[minor] fixes for TypeError: get_lead_details() takes at least 1 argument (2 given)
2017-07-18 12:20:47 +05:30
Nabin Hait
f37d43d0c1 Remove newline from serial no values 2017-07-18 12:15:16 +05:30
pratu16x7
aea60f349f [minor] default qty 0, fixes frappe/erpnext#9880 2017-07-18 12:15:16 +05:30
pratu16x7
90bd5681d1 [batch modal] bind serial_no field in onchange 2017-07-18 12:15:16 +05:30
mbauskar
30e03cc4c8 [minor] fixes for TypeError: get_lead_details() takes at least 1 argument (2 given) 2017-07-18 11:45:53 +05:30
Nabin Hait
4c40a416e6 Merge pull request #9900 from frappe/fixes_9899
[fix] #9899
2017-07-18 11:17:45 +05:30
Nabin Hait
3020c8086c Update stock_balance.py 2017-07-18 11:17:32 +05:30
Nabin Hait
8b3ef1e70a Merge pull request #9896 from rohitwaghchaure/rejected_expense_claim_issue
[Fix] Expense claim status issue
2017-07-18 11:10:52 +05:30
Makarand Bauskar
c446bf6117 Merge branch 'develop' into rejected_expense_claim_issue 2017-07-18 10:54:13 +05:30
Rushabh Mehta
660de515b5 [fix] filters for calendars frappe/erpnext#9850 (#9870) 2017-07-18 10:50:30 +05:30
Prateeksha Singh
e012e24423 Sales Goal by Company (#9723)
* [sales goal] in company; dashboard, graph, notifs, wiz

* [test] target notifications

* cache past year monthly sales of every company daily, patch

* [minor] query fixes

* update sales goal docs
2017-07-18 10:35:12 +05:30
Nabin Hait
e2d0d0a0c1 Merge pull request #9904 from nabinhait/hotfix777
Removed a deprecated function call
2017-07-17 20:29:55 +05:30
Nabin Hait
22e82dff20 Removed a deprecated function call 2017-07-17 20:28:30 +05:30
tunde
78d2f542d0 code fix as per review 2017-07-17 15:00:45 +01:00
Nabin Hait
b962fc1573 Show hsn code in tax breakup for India and render via template (#9866)
* Show hsn code in tax breakup for India and render via template

* tax breakup if gst_tax_field does not exists

* Fixed tax-breakup test cases
2017-07-17 18:02:31 +05:30
tunde
1ee534889f Revert "adds if statement for reference_type === "Asset""
This reverts commit c7e3a09cfb.
2017-07-17 13:27:59 +01:00
Frappe PR Bot
fa04236c8d [Translation] Updated Translations (#9898) 2017-07-17 17:50:36 +05:30
pawan
36025468a1 [fix] #9899 2017-07-17 17:28:44 +05:30
Rohit Waghchaure
0e376a464b test cases 2017-07-17 16:47:01 +05:30
Rohit Waghchaure
8333b5754b [Fix] Expense claim status issue 2017-07-17 16:38:20 +05:30
Rushabh Mehta
dab1172a18 [refactor] party.js get_party_details (#9888) 2017-07-17 15:31:17 +05:30
rohitwaghchaure
ea4497c8d2 Renamed the report Support Hours to Support Hours Distribution (#9874) 2017-07-17 14:55:42 +05:30
rohitwaghchaure
b994b3dcda Allow to select asset account in the payable in the expense claim for imprest management (#9891) 2017-07-17 14:33:33 +05:30
Makarand Bauskar
805a41d06c [minor] fixed the set_portal_settings patch (#9890) 2017-07-17 13:44:56 +05:30
Nabin Hait
e06526ffff Add indexes in some transaction doctypes (#9889) 2017-07-17 13:28:27 +05:30
Nabin Hait
2df7db0346 Merge pull request #9884 from mbauskar/patch-fixes
[hotfix] fixed GST code for Uttarakhand
2017-07-17 13:04:54 +05:30
Narciso E. Núñez Arias
c9877c5c1e Translation of ERPNext manual .md files (#9872)
* Translate Do I Need An ERP page

* Translate getting started with erpnext Page

* Translate Implementation Strategy Page

* Translate Spanish Index page

* Translate Flow Chart of transactions page

* Translate open source page

* Translate The Champion Page

* Fix spanish translation on Index page
2017-07-17 12:10:47 +05:30
strixaluco
372a881d8c Make 'Financial Year' translatable in Setup wizard (#9879) 2017-07-17 12:06:25 +05:30
mbauskar
71b5250cbd [hotfix] fixed the state code for Uttarakhand 2017-07-16 21:28:39 +05:30
mbauskar
3ceebaec3f Merge branch 'master' into develop 2017-07-14 17:47:07 +05:30
Nabin Hait
35d0de8276 Merge pull request #9858 from mbauskar/expense-claim
[minor] don't trigger the expense type trigger if value is not set
2017-07-14 15:28:34 +05:30
Rushabh Mehta
812853aa86 [refactor] account.js to new style (#9787)
* [fix] conference site update

* [test] run all js tests
2017-07-14 15:28:04 +05:30
KanchanChauhan
319c58266b Changes Quotes to Quotations in website sidebar because that seems more legit (#9825) 2017-07-14 14:30:42 +05:30
rohitwaghchaure
dcf10ee4f6 Fixed patch (#9862) 2017-07-14 13:02:38 +05:30
mbauskar
1394a6557d [minor] check mode_of_payment in Payment entry 2017-07-14 12:09:17 +05:30
mbauskar
00e825a8af [minor] check if account is selected or not in Payment Entry 2017-07-14 11:38:39 +05:30
mbauskar
ed89a83584 [minor] don't trigger the expense type trigger if value is not set 2017-07-14 11:21:27 +05:30
Faris Ansari
2c5b3e83f5 New design for daily work summary (#9844)
* New design for daily work summary

* Update tests
2017-07-13 18:37:18 +05:30
Rushabh Mehta
8e2531e2bb Update Test Runner to run tests one by one (#9843)
* [update] tests as per new api

* [test] unset test_quotation.js

* [test] unset test_quotation.js

* [test] unset test_quotation.js

* [test] unset test_quotation.js
2017-07-13 18:22:20 +05:30
Nabin Hait
d5dd9f1706 Merge pull request #9839 from rmehta/regional-decorators
[feature] override a function regionally by adding a decorator
2017-07-13 17:51:29 +05:30
Rushabh Mehta
393becce0b [fix] name decorator as allow_regional 2017-07-13 15:49:37 +05:30
Rushabh Mehta
8f2e21def2 [feature] override a function regionally by adding a decorator 2017-07-13 15:07:51 +05:30
Rushabh Mehta
7231f29e78 [feature] override a function regionally by adding a decorator 2017-07-13 15:00:56 +05:30
tunde
690de64734 Merge branch 'develop' into issue-9166 2017-06-28 13:37:04 +01:00
tunde
c7e3a09cfb adds if statement for reference_type === "Asset" 2017-06-28 13:23:18 +01:00
205 changed files with 27094 additions and 24731 deletions

View File

@@ -53,4 +53,4 @@ script:
- set -e
- bench --verbose run-tests
- sleep 5
- bench --verbose run-tests --ui-tests
- bench --verbose run-ui-tests --app erpnext

View File

@@ -1,8 +1,10 @@
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import inspect
import frappe
from erpnext.hooks import regional_overrides
__version__ = '8.4.3'
__version__ = '8.6.3'
def get_default_company(user=None):
'''Get default company for user'''
@@ -65,3 +67,34 @@ def is_perpetual_inventory_enabled(company):
company, "enable_perpetual_inventory") or 0
return frappe.local.enable_perpetual_inventory[company]
def get_region(company=None):
'''Return the default country based on flag, company or global settings
You can also set global company flag in `frappe.flags.company`
'''
if company or frappe.flags.company:
return frappe.db.get_value('Company',
company or frappe.flags.company, 'country')
elif frappe.flags.country:
return frappe.flags.country
else:
return frappe.get_system_settings('country')
def allow_regional(fn):
'''Decorator to make a function regionally overridable
Example:
@erpnext.allow_regional
def myfunction():
pass'''
def caller(*args, **kwargs):
region = get_region()
fn_name = inspect.getmodule(fn).__name__ + '.' + fn.__name__
if region in regional_overrides and fn_name in regional_overrides[region]:
return frappe.get_attr(regional_overrides[region][fn_name])(*args, **kwargs)
else:
return fn(*args, **kwargs)
return caller

View File

@@ -1,94 +1,94 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// Copyright (c) 2017, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
cur_frm.cscript.refresh = function (doc, cdt, cdn) {
if (doc.__islocal) {
frappe.msgprint(__("Please create new account from Chart of Accounts."));
throw "cannot create";
}
cur_frm.toggle_display('account_name', doc.__islocal);
// hide fields if group
cur_frm.toggle_display(['account_type', 'tax_rate'], cint(doc.is_group) == 0)
// disable fields
cur_frm.toggle_enable(['account_name', 'is_group', 'company'], false);
if (cint(doc.is_group) == 0) {
cur_frm.toggle_display('freeze_account', doc.__onload && doc.__onload.can_freeze_account);
}
// read-only for root accounts
if (!doc.parent_account) {
cur_frm.set_read_only();
cur_frm.set_intro(__("This is a root account and cannot be edited."));
} else {
// credit days and type if customer or supplier
cur_frm.set_intro(null);
cur_frm.cscript.account_type(doc, cdt, cdn);
// show / hide convert buttons
cur_frm.cscript.add_toolbar_buttons(doc);
}
}
cur_frm.add_fetch('parent_account', 'report_type', 'report_type');
cur_frm.add_fetch('parent_account', 'root_type', 'root_type');
cur_frm.cscript.account_type = function (doc, cdt, cdn) {
if (doc.is_group == 0) {
cur_frm.toggle_display(['tax_rate'], doc.account_type == 'Tax');
cur_frm.toggle_display('warehouse', doc.account_type == 'Stock');
}
}
cur_frm.cscript.add_toolbar_buttons = function (doc) {
cur_frm.add_custom_button(__('Chart of Accounts'),
function () { frappe.set_route("Tree", "Account"); });
if (doc.is_group == 1) {
cur_frm.add_custom_button(__('Group to Non-Group'),
function () { cur_frm.cscript.convert_to_ledger(); }, 'fa fa-retweet', 'btn-default');
} else if (cint(doc.is_group) == 0) {
cur_frm.add_custom_button(__('Ledger'), function () {
frappe.route_options = {
"account": doc.name,
"from_date": frappe.sys_defaults.year_start_date,
"to_date": frappe.sys_defaults.year_end_date,
"company": doc.company
frappe.ui.form.on('Account', {
setup: function(frm) {
frm.add_fetch('parent_account', 'report_type', 'report_type');
frm.add_fetch('parent_account', 'root_type', 'root_type');
},
onload: function(frm) {
frm.set_query('parent_account', function(doc) {
return {
filters: {
"is_group": 1,
"company": doc.company
}
};
frappe.set_route("query-report", "General Ledger");
});
},
refresh: function(frm) {
if (frm.doc.__islocal) {
frappe.msgprint(__("Please create new account from Chart of Accounts."));
throw "cannot create";
}
frm.toggle_display('account_name', frm.doc.__islocal);
// hide fields if group
frm.toggle_display(['account_type', 'tax_rate'], cint(frm.doc.is_group) == 0);
// disable fields
frm.toggle_enable(['account_name', 'is_group', 'company'], false);
if (cint(frm.doc.is_group) == 0) {
frm.toggle_display('freeze_account', frm.doc.__onload
&& frm.doc.__onload.can_freeze_account);
}
// read-only for root accounts
if (!frm.doc.parent_account) {
frm.set_read_only();
frm.set_intro(__("This is a root account and cannot be edited."));
} else {
// credit days and type if customer or supplier
frm.set_intro(null);
frm.trigger('account_type');
// show / hide convert buttons
frm.trigger('add_toolbar_buttons');
}
},
account_type: function (frm) {
if (frm.doc.is_group == 0) {
frm.toggle_display(['tax_rate'], frm.doc.account_type == 'Tax');
frm.toggle_display('warehouse', frm.doc.account_type == 'Stock');
}
},
add_toolbar_buttons: function(frm) {
frm.add_custom_button(__('Chart of Accounts'),
function () { frappe.set_route("Tree", "Account"); });
if (frm.doc.is_group == 1) {
frm.add_custom_button(__('Group to Non-Group'), function () {
return frappe.call({
doc: frm.doc,
method: 'convert_group_to_ledger',
callback: function() {
frm.refresh();
}
});
});
} else if (cint(frm.doc.is_group) == 0) {
cur_frm.add_custom_button(__('Ledger'), function () {
frappe.route_options = {
"account": frm.doc.name,
"from_date": frappe.sys_defaults.year_start_date,
"to_date": frappe.sys_defaults.year_end_date,
"company": frm.doc.company
};
frappe.set_route("query-report", "General Ledger");
});
frm.add_custom_button(__('Non-Group to Group'), function () {
return frappe.call({
doc: frm.doc,
method: 'convert_ledger_to_group',
callback: function() {
frm.refresh();
}
});
});
}
cur_frm.add_custom_button(__('Non-Group to Group'),
function () { cur_frm.cscript.convert_to_group(); }, 'fa fa-retweet', 'btn-default')
}
}
cur_frm.cscript.convert_to_ledger = function (doc, cdt, cdn) {
return $c_obj(cur_frm.doc, 'convert_group_to_ledger', '', function (r, rt) {
if (r.message == 1) {
cur_frm.refresh();
}
});
}
cur_frm.cscript.convert_to_group = function (doc, cdt, cdn) {
return $c_obj(cur_frm.doc, 'convert_ledger_to_group', '', function (r, rt) {
if (r.message == 1) {
cur_frm.refresh();
}
});
}
cur_frm.fields_dict['parent_account'].get_query = function (doc) {
return {
filters: {
"is_group": 1,
"company": doc.company
}
}
}
});

View File

@@ -0,0 +1,27 @@
QUnit.module('accounts');
QUnit.test("test account", function(assert) {
assert.expect(4);
let done = assert.async();
frappe.run_serially([
() => frappe.set_route('Tree', 'Account'),
() => frappe.click_button('Expand All'),
() => frappe.click_link('Debtors'),
() => frappe.click_button('Edit'),
() => frappe.timeout(1),
() => {
assert.ok(cur_frm.doc.root_type=='Asset');
assert.ok(cur_frm.doc.report_type=='Balance Sheet');
assert.ok(cur_frm.doc.account_type=='Receivable');
},
() => frappe.click_button('Ledger'),
() => frappe.timeout(1),
() => {
// check if general ledger report shown
assert.deepEqual(frappe.get_route(), ['query-report', 'General Ledger']);
window.history.back();
return frappe.timeout(1);
},
() => done()
]);
});

View File

@@ -96,7 +96,14 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
// expense claim
if(jvd.reference_type==="Expense Claim") {
return {};
return {
filters: {
'approval_status': 'Approved',
'total_sanctioned_amount': ['>', 0],
'status': ['!=', 'Paid'],
'docstatus': 1
}
};
}
// journal entry
@@ -122,10 +129,11 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
// account filter
frappe.model.validate_missing(jvd, "account");
var party_account_field = jvd.reference_type==="Sales Invoice" ? "debit_to": "credit_to";
out.filters.push([jvd.reference_type, party_account_field, "=", jvd.account]);
} else {
}
if(in_list(["Sales Order", "Purchase Order"], jvd.reference_type)) {
// party_type and party mandatory
frappe.model.validate_missing(jvd, "party_type");
frappe.model.validate_missing(jvd, "party");

View File

@@ -1516,6 +1516,36 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
"columns": 0,
"fieldname": "sec_tax_breakup",
"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": "Tax Breakup",
"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,
@@ -1523,7 +1553,7 @@
"collapsible": 0,
"columns": 0,
"fieldname": "other_charges_calculation",
"fieldtype": "HTML",
"fieldtype": "Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@@ -1533,12 +1563,12 @@
"in_standard_filter": 0,
"label": "Taxes and Charges Calculation",
"length": 0,
"no_copy": 0,
"no_copy": 1,
"oldfieldtype": "HTML",
"permlevel": 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,
@@ -3767,7 +3797,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2017-06-29 10:48:09.707735",
"modified": "2017-07-19 13:53:48.673757",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice",

View File

@@ -98,6 +98,26 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
this.set_default_print_format();
},
on_submit: function(doc, dt, dn) {
var me = this;
$.each(doc["items"], function(i, row) {
if(row.delivery_note) frappe.model.clear_doc("Delivery Note", row.delivery_note)
})
if(this.frm.doc.is_pos) {
this.frm.msgbox = frappe.msgprint(
`<a class="btn btn-primary" onclick="cur_frm.print_preview.printit(true)" style="margin-right: 5px;">
${__('Print')}</a>
<a class="btn btn-default" href="javascript:frappe.new_doc(cur_frm.doctype);">
${__('New')}</a>`
);
} else if(cint(frappe.boot.notification_settings.sales_invoice)) {
this.frm.email_doc(frappe.boot.notification_settings.sales_invoice_message);
}
},
set_default_print_format: function() {
// set default print format to POS type
if(cur_frm.doc.is_pos) {
@@ -306,7 +326,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
this.frm.refresh_fields();
},
company_address: function() {
var me = this;
if(this.frm.doc.company_address) {
@@ -343,13 +363,6 @@ cur_frm.cscript.hide_fields = function(doc) {
}
}
var item_fields_stock = ['batch_no', 'actual_batch_qty', 'actual_qty', 'expense_account',
'warehouse', 'expense_account', 'quality_inspection']
cur_frm.fields_dict['items'].grid.set_column_disp(item_fields_stock,
(cint(doc.update_stock)==1 || cint(doc.is_return)==1 ? true : false));
// India related fields
if (frappe.boot.sysdefaults.country == 'India') unhide_field(['c_form_applicable', 'c_form_no']);
else hide_field(['c_form_applicable', 'c_form_no']);
@@ -445,24 +458,6 @@ cur_frm.cscript.cost_center = function(doc, cdt, cdn) {
erpnext.utils.copy_value_in_all_row(doc, cdt, cdn, "items", "cost_center");
}
cur_frm.cscript.on_submit = function(doc, cdt, cdn) {
$.each(doc["items"], function(i, row) {
if(row.delivery_note) frappe.model.clear_doc("Delivery Note", row.delivery_note)
})
if(cur_frm.doc.is_pos) {
cur_frm.msgbox = frappe.msgprint(
`<a class="btn btn-primary" onclick="cur_frm.print_preview.printit(true)" style="margin-right: 5px;">
${__('Print')}</a>
<a class="btn btn-default" href="javascript:frappe.new_doc(cur_frm.doctype);">
${__('New')}</a>`
);
} else if(cint(frappe.boot.notification_settings.sales_invoice)) {
cur_frm.email_doc(frappe.boot.notification_settings.sales_invoice_message);
}
}
cur_frm.set_query("debit_to", function(doc) {
// filter on Account
if (doc.customer) {

View File

@@ -18,6 +18,7 @@ from erpnext.projects.doctype.timesheet.timesheet import get_projectwise_timeshe
from erpnext.accounts.doctype.asset.depreciation \
import get_disposal_account_and_cost_center, get_gl_entries_on_asset_disposal
from erpnext.stock.doctype.batch.batch import set_batch_nos
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos, get_delivery_note_serial_no
form_grid_templates = {
"items": "templates/form_grid/item_grid.html"
@@ -83,10 +84,10 @@ class SalesInvoice(SellingController):
if not self.is_opening:
self.is_opening = 'No'
if self._action != 'submit' and self.update_stock and not self.is_return:
set_batch_nos(self, 'warehouse', True)
self.set_against_income_account()
self.validate_c_form()
@@ -98,7 +99,7 @@ class SalesInvoice(SellingController):
self.set_billing_hours_and_amount()
self.update_timesheet_billing_for_project()
self.set_status()
def before_save(self):
set_account_for_mode_of_payment(self)
@@ -139,6 +140,8 @@ class SalesInvoice(SellingController):
self.update_time_sheet(self.name)
frappe.enqueue('erpnext.setup.doctype.company.company.update_company_current_month_sales', company=self.company)
def validate_pos_paid_amount(self):
if len(self.payments) == 0 and self.is_pos:
frappe.throw(_("At least one mode of payment is required for POS invoice."))
@@ -812,11 +815,18 @@ class SalesInvoice(SellingController):
"""
validate serial number agains Delivery Note and Sales Invoice
"""
self.set_serial_no_against_delivery_note()
self.validate_serial_against_delivery_note()
self.validate_serial_against_sales_invoice()
def set_serial_no_against_delivery_note(self):
for item in self.items:
if item.serial_no and item.delivery_note and \
item.qty != len(get_serial_nos(item.serial_no)):
item.serial_no = get_delivery_note_serial_no(item.item_code, item.qty, item.delivery_note)
def validate_serial_against_delivery_note(self):
"""
"""
validate if the serial numbers in Sales Invoice Items are same as in
Delivery Note Item
"""
@@ -826,14 +836,18 @@ class SalesInvoice(SellingController):
continue
serial_nos = frappe.db.get_value("Delivery Note Item", item.dn_detail, "serial_no") or ""
dn_serial_nos = set(serial_nos.split("\n"))
dn_serial_nos = set(get_serial_nos(serial_nos))
serial_nos = item.serial_no or ""
si_serial_nos = set(serial_nos.split("\n"))
si_serial_nos = set(get_serial_nos(serial_nos))
if si_serial_nos - dn_serial_nos:
frappe.throw(_("Serial Numbers in row {0} does not match with Delivery Note".format(item.idx)))
if item.serial_no and cint(item.qty) != len(si_serial_nos):
frappe.throw(_("Row {0}: {1} Serial numbers required for Item {2}. You have provided {3}.".format(
item.idx, item.qty, item.item_code, len(si_serial_nos))))
def validate_serial_against_sales_invoice(self):
""" check if serial number is already used in other sales invoice """
for item in self.items:
@@ -918,7 +932,6 @@ def make_delivery_note(source_name, target_doc=None):
return doclist
@frappe.whitelist()
def make_sales_return(source_name, target_doc=None):
from erpnext.controllers.sales_and_purchase_return import make_return_doc

View File

@@ -13,6 +13,7 @@ from erpnext.exceptions import InvalidAccountCurrency, InvalidCurrency
from erpnext.stock.doctype.serial_no.serial_no import SerialNoWarehouseError
from frappe.model.naming import make_autoname
from erpnext.accounts.doctype.account.test_account import get_inventory_account
from erpnext.controllers.taxes_and_totals import get_itemised_tax_breakup_data
class TestSalesInvoice(unittest.TestCase):
def make(self):
@@ -1105,10 +1106,75 @@ class TestSalesInvoice(unittest.TestCase):
for i, k in enumerate(expected_values["keys"]):
self.assertEquals(d.get(k), expected_values[d.item_code][i])
def test_item_wise_tax_breakup(self):
def test_item_wise_tax_breakup_india(self):
frappe.flags.country = "India"
si = self.create_si_to_test_tax_breakup()
itemised_tax, itemised_taxable_amount = get_itemised_tax_breakup_data(si)
expected_itemised_tax = {
"999800": {
"Service Tax": {
"tax_rate": 10.0,
"tax_amount": 1500.0
}
}
}
expected_itemised_taxable_amount = {
"999800": 15000.0
}
self.assertEqual(itemised_tax, expected_itemised_tax)
self.assertEqual(itemised_taxable_amount, expected_itemised_taxable_amount)
frappe.flags.country = None
def test_item_wise_tax_breakup_outside_india(self):
frappe.flags.country = "United States"
si = self.create_si_to_test_tax_breakup()
itemised_tax, itemised_taxable_amount = get_itemised_tax_breakup_data(si)
expected_itemised_tax = {
"_Test Item": {
"Service Tax": {
"tax_rate": 10.0,
"tax_amount": 1000.0
}
},
"_Test Item 2": {
"Service Tax": {
"tax_rate": 10.0,
"tax_amount": 500.0
}
}
}
expected_itemised_taxable_amount = {
"_Test Item": 10000.0,
"_Test Item 2": 5000.0
}
self.assertEqual(itemised_tax, expected_itemised_tax)
self.assertEqual(itemised_taxable_amount, expected_itemised_taxable_amount)
frappe.flags.country = None
def create_si_to_test_tax_breakup(self):
si = create_sales_invoice(qty=100, rate=50, do_not_save=True)
si.append("items", {
"item_code": "_Test Item",
"gst_hsn_code": "999800",
"warehouse": "_Test Warehouse - _TC",
"qty": 100,
"rate": 50,
"income_account": "Sales - _TC",
"expense_account": "Cost of Goods Sold - _TC",
"cost_center": "_Test Cost Center - _TC"
})
si.append("items", {
"item_code": "_Test Item 2",
"gst_hsn_code": "999800",
"warehouse": "_Test Warehouse - _TC",
"qty": 100,
"rate": 50,
@@ -1125,11 +1191,7 @@ class TestSalesInvoice(unittest.TestCase):
"rate": 10
})
si.insert()
tax_breakup_html = '''\n<div class="tax-break-up" style="overflow-x: auto;">\n\t<table class="table table-bordered table-hover">\n\t\t<thead><tr><th class="text-left" style="min-width: 120px;">Item Name</th><th class="text-right" style="min-width: 80px;">Taxable Amount</th><th class="text-right" style="min-width: 80px;">_Test Account Service Tax - _TC</th></tr></thead>\n\t\t<tbody><tr><td>_Test Item</td><td class="text-right">\u20b9 10,000.00</td><td class="text-right">(10.0%) \u20b9 1,000.00</td></tr></tbody>\n\t</table>\n</div>'''
self.assertEqual(si.other_charges_calculation, tax_breakup_html)
return si
def create_sales_invoice(**args):
si = frappe.new_doc("Sales Invoice")
@@ -1150,6 +1212,7 @@ def create_sales_invoice(**args):
si.append("items", {
"item_code": args.item or args.item_code or "_Test Item",
"gst_hsn_code": "999800",
"warehouse": args.warehouse or "_Test Warehouse - _TC",
"qty": args.qty or 1,
"rate": args.rate or 100,

View File

@@ -1424,7 +1424,7 @@
"collapsible": 1,
"collapsible_depends_on": "eval:doc.serial_no || doc.batch_no",
"columns": 0,
"depends_on": "eval: parent.update_stock",
"depends_on": "",
"fieldname": "warehouse_and_reference",
"fieldtype": "Section Break",
"hidden": 0,
@@ -2166,7 +2166,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-07-06 17:54:03.347700",
"modified": "2017-07-17 17:54:48.246507",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Item",

View File

@@ -1398,10 +1398,6 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
return erpnext.get_currency(this.frm.doc.company);
},
show_item_wise_taxes: function () {
return null;
},
show_items_in_item_cart: function () {
var me = this;
var $items = this.wrapper.find(".items").empty();

View File

@@ -284,12 +284,14 @@ def get_credit_days(party_type, party, company):
if not credit_days_based_on:
if party_type == "Customer":
credit_days_based_on, credit_days = \
frappe.db.get_value("Customer Group", customer_group, ["credit_days_based_on", "credit_days"]) \
or frappe.db.get_value("Company", company, ["credit_days_based_on", "credit_days"])
frappe.db.get_value("Customer Group", customer_group, ["credit_days_based_on", "credit_days"])
else:
credit_days_based_on, credit_days = \
frappe.db.get_value("Supplier Type", supplier_type, ["credit_days_based_on", "credit_days"])\
or frappe.db.get_value("Company", company, ["credit_days_based_on", "credit_days"] )
frappe.db.get_value("Supplier Type", supplier_type, ["credit_days_based_on", "credit_days"])
if not credit_days_based_on:
credit_days_based_on, credit_days = \
frappe.db.get_value("Company", company, ["credit_days_based_on", "credit_days"])
return credit_days_based_on, credit_days

View File

@@ -209,7 +209,10 @@ class GrossProfitGenerator(object):
sle.voucher_detail_no == row.item_row:
previous_stock_value = len(my_sle) > i+1 and \
flt(my_sle[i+1].stock_value) or 0.0
return previous_stock_value - flt(sle.stock_value)
if previous_stock_value:
return previous_stock_value - flt(sle.stock_value)
else:
return flt(row.qty) * self.get_average_buying_rate(row, item_code)
else:
return flt(row.qty) * self.get_average_buying_rate(row, item_code)

View File

@@ -1546,6 +1546,36 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
"columns": 0,
"fieldname": "sec_tax_breakup",
"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": "Tax Breakup",
"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,
@@ -1553,7 +1583,7 @@
"collapsible": 0,
"columns": 0,
"fieldname": "other_charges_calculation",
"fieldtype": "HTML",
"fieldtype": "Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@@ -1568,7 +1598,7 @@
"permlevel": 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,
@@ -3305,7 +3335,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-06-13 14:29:21.066814",
"modified": "2017-07-19 14:03:51.838328",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order",

View File

@@ -11,3 +11,6 @@ from erpnext.controllers.print_settings import print_settings_for_item_table
class PurchaseOrderItem(Document):
def __setup__(self):
print_settings_for_item_table(self)
def on_doctype_update():
frappe.db.add_index("Purchase Order Item", ["item_code", "warehouse"])

View File

@@ -1120,6 +1120,36 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
"columns": 0,
"fieldname": "tax_breakup",
"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": "Tax Breakup",
"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,
@@ -1127,7 +1157,7 @@
"collapsible": 0,
"columns": 0,
"fieldname": "other_charges_calculation",
"fieldtype": "HTML",
"fieldtype": "Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@@ -1142,7 +1172,7 @@
"permlevel": 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,
@@ -2217,7 +2247,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2017-06-13 14:28:54.466450",
"modified": "2017-07-19 13:51:18.929697",
"modified_by": "Administrator",
"module": "Buying",
"name": "Supplier Quotation",

View File

@@ -160,6 +160,7 @@ class AccountsController(TransactionBase):
def set_missing_item_details(self, for_validate=False):
"""set missing item values"""
from erpnext.stock.get_item_details import get_item_details
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
if hasattr(self, "items"):
parent_dict = {}
@@ -196,7 +197,7 @@ class AccountsController(TransactionBase):
elif fieldname == "serial_no":
stock_qty = item.get("stock_qty") * -1 if item.get("stock_qty") < 0 else item.get("stock_qty")
if stock_qty != len(item.get('serial_no').split('\n')):
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"):

View File

@@ -5,7 +5,7 @@ from __future__ import unicode_literals
import json
import frappe, erpnext
from frappe import _, scrub
from frappe.utils import cint, flt, cstr, fmt_money, round_based_on_smallest_currency_fraction
from frappe.utils import cint, flt, round_based_on_smallest_currency_fraction
from erpnext.controllers.accounts_controller import validate_conversion_rate, \
validate_taxes_and_charges, validate_inclusive_tax
@@ -509,108 +509,75 @@ class calculate_taxes_and_totals(object):
return rate_with_margin
def set_item_wise_tax_breakup(self):
item_tax = {}
tax_accounts = []
company_currency = erpnext.get_company_currency(self.doc.company)
self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc)
item_tax, tax_accounts = self.get_item_tax(item_tax, tax_accounts, company_currency)
headings = get_table_column_headings(tax_accounts)
distinct_items, taxable_amount = self.get_distinct_items()
rows = get_table_rows(distinct_items, item_tax, tax_accounts, company_currency, taxable_amount)
if not rows:
self.doc.other_charges_calculation = ""
else:
self.doc.other_charges_calculation = '''
<div class="tax-break-up" style="overflow-x: auto;">
<table class="table table-bordered table-hover">
<thead><tr>{headings}</tr></thead>
<tbody>{rows}</tbody>
</table>
</div>'''.format(**{
"headings": "".join(headings),
"rows": "".join(rows)
})
def get_item_tax(self, item_tax, tax_accounts, company_currency):
for tax in self.doc.taxes:
tax_amount_precision = tax.precision("tax_amount")
tax_rate_precision = tax.precision("rate");
item_tax_map = self._load_item_tax_rate(tax.item_wise_tax_detail)
for item_code, tax_data in item_tax_map.items():
if not item_tax.get(item_code):
item_tax[item_code] = {}
if isinstance(tax_data, list):
tax_rate = ""
if tax_data[0]:
if tax.charge_type == "Actual":
tax_rate = fmt_money(flt(tax_data[0], tax_amount_precision),
tax_amount_precision, company_currency)
else:
tax_rate = cstr(flt(tax_data[0], tax_rate_precision)) + "%"
tax_amount = fmt_money(flt(tax_data[1], tax_amount_precision),
tax_amount_precision, company_currency)
item_tax[item_code][tax.name] = [tax_rate, tax_amount]
else:
item_tax[item_code][tax.name] = [cstr(flt(tax_data, tax_rate_precision)) + "%", "0.00"]
tax_accounts.append([tax.name, tax.account_head])
return item_tax, tax_accounts
def get_itemised_tax_breakup_html(doc):
if not doc.taxes:
return
frappe.flags.company = doc.company
def get_distinct_items(self):
distinct_item_names = []
distinct_items = []
taxable_amount = {}
for item in self.doc.items:
item_code = item.item_code or item.item_name
if item_code not in distinct_item_names:
distinct_item_names.append(item_code)
distinct_items.append(item)
taxable_amount[item_code] = item.net_amount
else:
taxable_amount[item_code] = taxable_amount.get(item_code, 0) + item.net_amount
return distinct_items, taxable_amount
# get headers
tax_accounts = list(set([d.description for d in doc.taxes]))
headers = get_itemised_tax_breakup_header(doc.doctype + " Item", tax_accounts)
# get tax breakup data
itemised_tax, itemised_taxable_amount = get_itemised_tax_breakup_data(doc)
frappe.flags.company = None
return frappe.render_template(
"templates/includes/itemised_tax_breakup.html", dict(
headers=headers,
itemised_tax=itemised_tax,
itemised_taxable_amount=itemised_taxable_amount,
tax_accounts=tax_accounts,
company_currency=erpnext.get_company_currency(doc.company)
)
)
def get_table_column_headings(tax_accounts):
headings_name = [_("Item Name"), _("Taxable Amount")] + [d[1] for d in tax_accounts]
headings = []
for head in headings_name:
if head == _("Item Name"):
headings.append('<th style="min-width: 120px;" class="text-left">' + (head or "") + "</th>")
else:
headings.append('<th style="min-width: 80px;" class="text-right">' + (head or "") + "</th>")
@erpnext.allow_regional
def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
return [_("Item"), _("Taxable Amount")] + tax_accounts
@erpnext.allow_regional
def get_itemised_tax_breakup_data(doc):
itemised_tax = get_itemised_tax(doc.taxes)
itemised_taxable_amount = get_itemised_taxable_amount(doc.items)
return itemised_tax, itemised_taxable_amount
def get_itemised_tax(taxes):
itemised_tax = {}
for tax in taxes:
tax_amount_precision = tax.precision("tax_amount")
tax_rate_precision = tax.precision("rate")
item_tax_map = json.loads(tax.item_wise_tax_detail) if tax.item_wise_tax_detail else {}
for item_code, tax_data in item_tax_map.items():
itemised_tax.setdefault(item_code, frappe._dict())
return headings
def get_table_rows(distinct_items, item_tax, tax_accounts, company_currency, taxable_amount):
rows = []
for item in distinct_items:
item_tax_record = item_tax.get(item.item_code or item.item_name)
if not item_tax_record:
continue
taxes = []
for head in tax_accounts:
if item_tax_record[head[0]]:
taxes.append("<td class='text-right'>(" + item_tax_record[head[0]][0] + ") "
+ item_tax_record[head[0]][1] + "</td>")
if isinstance(tax_data, list) and tax_data[0]:
precision = tax_amount_precision if tax.charge_type == "Actual" else tax_rate_precision
itemised_tax[item_code][tax.description] = frappe._dict(dict(
tax_rate=flt(tax_data[0], precision),
tax_amount=flt(tax_data[1], tax_amount_precision)
))
else:
taxes.append("<td></td>")
itemised_tax[item_code][tax.description] = frappe._dict(dict(
tax_rate=flt(tax_data, tax_rate_precision),
tax_amount=0.0
))
return itemised_tax
def get_itemised_taxable_amount(items):
itemised_taxable_amount = frappe._dict()
for item in items:
item_code = item.item_code or item.item_name
rows.append("<tr><td>{item_name}</td><td class='text-right'>{taxable_amount}</td>{taxes}</tr>".format(**{
"item_name": item.item_name,
"taxable_amount": fmt_money(taxable_amount.get(item_code, 0), item.precision("net_amount"), company_currency),
"taxes": "".join(taxes)
}))
return rows
itemised_taxable_amount.setdefault(item_code, 0)
itemised_taxable_amount[item_code] += item.net_amount
return itemised_taxable_amount

View File

@@ -27,7 +27,8 @@ def test_recurring_document(obj, test_records):
base_doc.set(date_field, today)
if base_doc.doctype == "Sales Order":
base_doc.set("delivery_date", add_days(today, 15))
for d in base_doc.get("items"):
d.set("delivery_date", add_days(today, 15))
# monthly
doc1 = frappe.copy_doc(base_doc)

View File

@@ -0,0 +1,43 @@
QUnit.module("sales");
QUnit.test("test: lead", function (assert) {
assert.expect(4);
let done = assert.async();
let lead_name = frappe.utils.get_random(10);
frappe.run_serially([
// test lead creation
() => frappe.set_route("List", "Lead"),
() => frappe.new_doc("Lead"),
() => frappe.timeout(1),
() => cur_frm.set_value("lead_name", lead_name),
() => cur_frm.save(),
() => frappe.timeout(1),
() => {
assert.ok(cur_frm.doc.lead_name.includes(lead_name),
'name correctly set');
frappe.lead_name = cur_frm.doc.name;
},
// create address and contact
() => frappe.click_link('Address & Contact'),
() => frappe.click_button('New Address'),
() => frappe.timeout(1),
() => frappe.set_control('address_line1', 'Gateway'),
() => frappe.set_control('city', 'Mumbai'),
() => cur_frm.save(),
() => frappe.timeout(3),
() => assert.equal(frappe.get_route()[1], 'Lead',
'back to lead form'),
() => frappe.click_link('Address & Contact'),
() => assert.ok($('.address-box').text().includes('Mumbai'),
'city is seen in address box'),
// make opportunity
() => frappe.click_button('Make'),
() => frappe.click_link('Opportunity'),
() => frappe.timeout(2),
() => assert.equal(cur_frm.doc.lead, frappe.lead_name,
'lead name correctly mapped'),
() => done()
]);
});

View File

@@ -0,0 +1,56 @@
QUnit.test("test: opportunity", function (assert) {
assert.expect(8);
let done = assert.async();
frappe.run_serially([
() => frappe.set_route('List', 'Opportunity'),
() => frappe.timeout(1),
() => frappe.click_button('New'),
() => frappe.timeout(1),
() => cur_frm.set_value('enquiry_from', 'Customer'),
() => cur_frm.set_value('customer', 'Test Customer 1'),
// check items
() => cur_frm.set_value('with_items', 1),
() => frappe.tests.set_grid_values(cur_frm, 'items', [
[
{item_code:'Test Product 1'},
{qty: 4}
]
]),
() => cur_frm.save(),
() => frappe.timeout(1),
() => {
assert.notOk(cur_frm.is_new(), 'saved');
frappe.opportunity_name = cur_frm.doc.name;
},
// close and re-open
() => frappe.click_button('Close'),
() => frappe.timeout(1),
() => assert.equal(cur_frm.doc.status, 'Closed',
'closed'),
() => frappe.click_button('Reopen'),
() => assert.equal(cur_frm.doc.status, 'Open',
'reopened'),
() => frappe.timeout(1),
// make quotation
() => frappe.click_button('Make'),
() => frappe.click_link('Quotation', 1),
() => frappe.timeout(2),
() => {
assert.equal(frappe.get_route()[1], 'Quotation',
'made quotation');
assert.equal(cur_frm.doc.customer, 'Test Customer 1',
'customer set in quotation');
assert.equal(cur_frm.doc.items[0].item_code, 'Test Product 1',
'item set in quotation');
assert.equal(cur_frm.doc.items[0].qty, 4,
'qty set in quotation');
assert.equal(cur_frm.doc.items[0].prevdoc_docname, frappe.opportunity_name,
'opportunity set in quotation');
},
() => done()
]);
});

View File

@@ -118,7 +118,8 @@ def make_sales_order():
from erpnext.selling.doctype.quotation.quotation import make_sales_order
so = frappe.get_doc(make_sales_order(q))
so.transaction_date = frappe.flags.current_date
so.delivery_date = frappe.utils.add_days(frappe.flags.current_date, 10)
for d in so.get("items"):
d.delivery_date = frappe.utils.add_days(frappe.flags.current_date, 10)
so.insert()
frappe.db.commit()
so.submit()

Binary file not shown.

Before

Width:  |  Height:  |  Size: 185 KiB

After

Width:  |  Height:  |  Size: 215 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.8 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 273 KiB

After

Width:  |  Height:  |  Size: 443 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

After

Width:  |  Height:  |  Size: 38 KiB

View File

@@ -15,5 +15,6 @@ third-party-backups
workflows
bar-code
company-setup
setting-company-sales-goal
calculate-incentive-for-sales-team
articles

View File

@@ -31,7 +31,7 @@ Most of the information in your Sales Order is the same as the Quotation.
There are a few amongst other things that a Sales Order will ask you to
update.
* Expected date of delivery.
* Enter delivery date agaist each item. If there are multiple items and if you enter delivery date in the first row, the date will be copied to other rows as well where it is blank.
* Customer Purchase Order number: If your customer has sent you a Purchase Order, you can update its number for future reference (in billing).
### Packing List
@@ -89,9 +89,9 @@ ERPNext will automatically create new Order and mail a notification to the Email
Once you “Submit” your Sales Order, you can now trigger different aspects of
your organization:
* To begin purchase click on Make Purchase Request
* To make a shipment entry click on Make Delivery Note
* To bill, make Make Sales Invoice
* To begin purchase click on Make -> Purchase Request
* To make a shipment entry click on Make -> Delivery Note. You can also make Delivery Note for selected items based on delivery date.
* To bill, make Make -> Sales Invoice
* To stop further process on this Sales Order, click on “Stop”
### Submission

View File

@@ -0,0 +1,15 @@
# Setting Company Sales Goal
Monthly sales targets can be set for a company via the Company master. By default, the Company master dashboard features past sales stats.
<img class="screenshot" alt="Sales Graph" src="{{docs_base_url}}/assets/img/sales_goal/sales_history_graph.png">
You can set the **Sales Target** field to track progress to track progress with respect to it.
<img class="screenshot" alt="Setting Sales Goal" src="{{docs_base_url}}/assets/img/sales_goal/setting_sales_goal.gif">
The target progress is also shown in notifications:
<img class="screenshot" alt="Sales Notification" src="{{docs_base_url}}/assets/img/sales_goal/sales_goal_notification.png">
{next}

View File

@@ -3,10 +3,10 @@ WORK IN PROGRESS
-->
# Manual de Usuario (Español)
### Contenido:
### Contenido:
{index}
**Trabajo en progreso.**
[The Spanish Translation of the ERPNext manual is in progress. Click here to see the english manual]({{ docs_base_url }}/user/manual/en)
[La traducción al Español del manual de ERPNext está en progreso. Click aquí para ver el manual en ingles]({{ docs_base_url }}/user/manual/en)

View File

@@ -1 +1,2 @@
introduction
accounts

View File

@@ -0,0 +1,20 @@
ERPNext es una herramienta moderna que no solo abarca el módulo de contabilidad,
sino que también, cubre todas las otras funciones de su negocio en una plataforma integrada.
Tiene muchos beneficios sobre los sistemas tradicionales de contabilidad y otros ERP en el mercado.
### Beneficios sobre los sistemas de contabilidad tradicionales:
* Es más que solo contabilidad! Gestionar inventario, facturación, cotizaciones, clientes potenciales, nómina y mucho más.
* Mantiene toda tu información segura y en un solo lugar. No siga buscando sus datos cuando más lo necesitas en diferente hojas de calculo y en diferentes ordenadores.
Gestiona a todos tus empleados en el mismo lugar. Todos los usuarios obtienen información actualizada.
* No más trabajo doble. No introduzcas la misma información desde su procesador de textos a su herramienta de contabilidad. Todo está integrado!
* Manten un historial. Obten el historial completo de un cliente o un acuerdo en un solo lugar
### Beneficios sobre ERPs más grandes
* $$$ - Ahorra dinero.
* **Más facíl de configurar:** Grandes ERP son extramadamente complicados para configurar y van a preguntar demasiadas preguntas amtes de que puedas hacer algo utíl.
* **Más facíl de usar:** Una moderna y limpia interfaz web va a mantener sus usuarios contentos y en un entorno mas familiar.
* **Código Abierto :** Este sistema es completamente gratis y puedes instalarlo/configurarlo donde desees.
{next}

View File

@@ -0,0 +1,31 @@
Hay muchas manera de comenzar a utilizar ERPNext.
### 1\. Ver el Demo
Si deseas entrar en contacto con la interfaz de usuario de ERPNext y **sentir** la aplicación, solo tienes que ver el demo en:
see the demo at:
* <https://demo.erpnext.com>
### 2\. Comienza con una cuenta gratis en ERPNext.com
ERPNext.com es manejado por la organización (Frappe) que publicó ERPNext.
Puedes iniciar con su propia cuenta en [registrandote en la página](https://erpnext.com).
También, puedes hostear tu aplicación en erpnext.com comprando un plan de alojamiento.
De esta forma, estas aportando a la organización que desarrolla y mejora ERPNext.
También obten soporte de uno-a-uno con los planes de alojamiento.
### 3\. Descarga una Maquina Virtual
Para evitar las molestias de instalar el sistema, ERPNext está disponible como una image virtual (un sistema operativo completo con ERPNext instalado).
Puedes usarla en **cualquier** plataforma incluyendo Microsoft Windows.
[Click aquí para ver las instrucciones de como usar la imagen](https://erpnext.com/download)
### 4\. Instalar ERPNext en su ordenador Unix/Linux/Mac
En caso de estar relacionado con la instalación de aplicaciones en plataformas *nix, leer las instrucciones de como instalarlo usando [Frappe Bench](https://github.com/frappe/bench).
{next}

View File

@@ -0,0 +1,32 @@
Antes de que empieces a manejar todas tus operaciones en ERPNext, primero
deberías estar familiarizado con el sistema y los términos que utiliza.
Por esa razón recomendamos que la implementación pase en dos fases.
* La **Fase de Prueba**, donde introduces información de prueba que representan sus transacciones del día a día y la **Fase de Producción**, donde comenzamos a introducir información real.
### Fase de Prueba
* Leer el manual
* Crea una cuenta gratis en [https://erpnext.com](https://erpnext.com) (La forma más facíl de experimental).
* Crea su primer Cliente, Suplidor y Producto. Agrega varios de estos para que se familiarice con ellos.
* Crea un Grupo de Clientes, Grupo de Productos, Almacenes, Grupo de Suplidores, para que puedas clasificar sus productos.
* Completar un ciclo estandar de ventas - Iniciativa > Oportunidad > Cotización > Orden de Venta > Nota de Entrega > Factura de Venta > Pago (Entrada de diario)
* Completa un ciclo estandar de compra - Solicitud de Material > Orden de Compra > Recibo de Compra > Pagos (Entrada de diario).
* Completar un ciclo de manofactura (si aplica) - BOM > Herramienta de Planificación de Producción > Orden de Producción > Problema de material
* Replicar un escenario de su día a día dentro del sistema.
* Crea un custom fields, formato de impresión, etc como sea requerido.
### Fase de Producción
Una vez ya estes falimiliarizado con ERPNext, inicia introduciendo la información real!
* Borra toda la información de prueba de la cuenta o inicia con una nueva instalación.
* Si solo quieres borrar las transacciones y no las demás informaciones sobre Productos, Clientes, Suplidores, BOM etc, puedes dar click en Eliminar Transacciones de su compañia y inicia desde cero. Para hacerlo, abre el registro de la compañia via Setup > Masters > Company y eliminar las transacciones de su compañia clickeando en el botón **Eliminar las transacciones de la compañia** al final del formulario de la compañia.
* También puedes configurar una nueva cuenta en [https://erpnext.com](https://erpnext.com), y usa los 30 días gratis. [Encuentra mas formas de usar ERPNext](/introduction/getting-started-with-erpnext)
* Configura todos los módulos con Grupos de Clientes, Grupos de Productos, Almacenes, BOMs etc.
* Importar Clientes, Suplidores, Productos, Contactos y Direcciones usando la Herramienta de Importación de Data.
* Importar el inventario de apertura usando la Herramienta de Reconciliación de Inventario.
* Crear la entrada de apertura de cuenta usando la Entrada de Diario y crea facturas de ventas pendientes y facturas de compra.
* Si necesitas ayuda, [puedes pagar por soporte](https://erpnext.com/pricing) o [preguntar en el foro de la comunidad](https://discuss.erpnext.com).
{next}

View File

@@ -0,0 +1,40 @@
## ¿Qué es un ERP y Por qué debería interesarme?
(Si ya sabes que necesitas un sistema todo-en-uno para su compañia, puedes pasar a la siguiente página)
Si eres dueño de una pequeña empresa que tiene varios empleados, debes entender que es difícil manejar la naturaleza dinámica de hacer negocios.
Pequeñas empresas no son tan diferentes que las grandes empresas. Las pequeñas empresas contienen la mayoria de las complejidades que posee una empresa grande junto a otras reestricciones.
Las pequeñas empresas tienen que comunicarse con clientes, hacer contabilidad, pagar impuestos, pagar nómina, gestionar tiempos,
proporsionar bienes y servicios de calidad, responder preguntar, y mantener a todos contentos como lo hacen las grandes empresas.
Grandes empresas tienen la venraja de usar sistemas avanzados para manejar sus procesos de una forma mas eficiente.
Pequeñas empresas, sin embargo, luchan para mantener las cosas organizadas. Normalmente usan un conjuntos de aplicaciones como hojas de calculos, sistemas de contabilidad,
un CRM etc para administrarse. El problema es que no todos estan en la misma página. Un ERP cambia todo eso.
## ¿Qué es ERPNext?
ERPNext es una solución de negocio de extremo a extremo que te ayuda a manejar toda la información de su negocio en una sola aplicación
y usado no solo para manejar operaciones, sino que tambien le permite tomar decisiones efectivas y bien documentadas justo en el momento que las necesites.
Forma una columna vertebral de su negocio para agregar fuerza, transparencia y control a su compañia.
Junto con otras cosas, ERPNext te ayudará con todo lo siguiente:
* Mantener registro de todas sus facturas y pagos.
* Saber que cantidad de cada producto hay disponible en almacen.
* Identificar y hacer seguimiento de los indicadores de rendimientos (KPI's)
* Identificar consultas abiertas de los clientes.
* Gestionar Nómina.
* Asignar tareas y hacer seguimiento de las mismas.
* Mantener una base de datos de todos sus clientes, suplidores y sus contactos.
* Preparar presupuestos.
* Hacer seguimiento a su presupuesto y sus gastos.
* Determinar el precio efectivo para ventas basado en la materia prima disponible, maquinaria y costo de esfuerzo.
* Obtener recordatorios sobre el calendario de mantenimientos.
* Publicar su página web.
Y Mucho mucho más.
### Temas
{index}

View File

@@ -0,0 +1,7 @@
do-i-need-an-erp
open-source
getting-started-with-erpnext
the-champion
implementation-strategy
key-workflows
concepts-and-terms

View File

@@ -0,0 +1,13 @@
# Flujo De Transacciones En ERPNext
Este diagrama cubre como ERPNext hace el seguimiento de la información de su compañia a través de funciones claves.
Este diagrama no cubre toda la funcionalidad o características de ERPNext.
![]({{docs_base_url}}/assets/old_images/erpnext/overview.png)
<img class="screenshot" alt="Workflow" src="{{docs_base_url}}/assets/img/setup/overview.png">
_Nota: No todos los pasos son obligatorios. ERPNext te permite pasar algunos pasos si deseas simplificar el proceso._
{next}

View File

@@ -0,0 +1,34 @@
El código fuente de ERPNext es de código abierto. Está abierto para que todos
podamos entenderlo, extenderlo o mejorarlo. Y es gratis!
Las ventajas de un Sistema de Código Abierto:
1. Puedes cambiar tu proveedor de servicios cuando quieras.
2. Puedas hostear la aplicación donde quieras, incluyendo en tu propio servidor para tener completa propiedad y privacidad de la información.
3. Puedes pedir ayuda a la comunidad en caso de necesitarla. No estas atado a un proveedor de servicios.
4. Te puedes beneficiar de un producto que es criticado y usado por una gran cantidad de personas,
quienes han reportado cientos de fallos y sugerencias para mejorarlo, y esto siempre va a continuar así.
---
### Código Fuente de ERPNext
El repositorio que contiene el código fuente de ERPnext está disponible en GitHub y puede ser encontrado aquí
- [https://github.com/frappe/erpnext](https://github.com/frappe/erpnext)
---
### Alternativas
Hay muchas soluciones ERP que puedes considerar. Los más populares son:
1. Odoo
2. OpenBravo
3. Apache OfBiz
4. xTuple
5. Compiere (y clones)
{next}

View File

@@ -0,0 +1,41 @@
<!-- no-heading -->
<h1 class="white">El campeón</h1>
<img alt="Champion" class="screenshot" src="{{docs_base_url}}/assets/img/setup/implementation-image.png">
Hemos visto docenas de implementaciones de sistemas ERP en los últimos años
y nos hemos dado cuenta que una implementación exitosa es más sobre cosas intangibles y actitudes.
**Los ERP no son requeridos.**
Como el ejercicio.
El cuerpo humano puede que parezca que no requiere ejercicio hoy ni quizas mañana, pero con el pasar del tiempo,
si desea mantener su cuerpo y su salud deberá comenzar a hacer ejercicio.
En esta misma forma, ERPs mejoran la salud de su compañia a largo plazo manteniendola ajustada y eficiente.
Mientas más demores en poner las cosas en orden, más tiempo pierdes, y estas más cerca de una desastre mayor.
Por tanto, cuando comienzas a implementar un ERP, manten la visión en beneficios a largo plazo.
Como el ejercicio, es doloroso al comienzo, pero va a hacer cosas maravillosas si te mantienes haciendolo.
* * *
## El Campeón
Un ERP significa un cambio en la organización y un cambio no sucede sin exfuerzo.
Cada cambio requiere un campeón y es la responsabilidad de el campeón el
organizar y motivar al equipo completo durante la implementación.
El campeón necesita ser activo en caso que algo salga mal.
En muchas organizaciones que hemos visto, frecuentemente el campeón es el dueño o un Administrador.
Ocasionalmente, el campeón es una persona externa quien es contratado con un propósito específico.
En cualquier caso, debes identificar su campeón primero.
Lo más seguro es que sea **usted!**
Comencemos!
{next}

View File

@@ -1,6 +1,5 @@
from __future__ import unicode_literals
from frappe import _
from . import __version__ as app_version
app_name = "erpnext"
app_title = "ERPNext"
@@ -80,13 +79,7 @@ website_route_rules = [
"parents": [{"title": _("Supplier Quotation"), "name": "quotations"}]
}
},
{"from_route": "/quotes", "to_route": "Quotation"},
{"from_route": "/quotes/<path:name>", "to_route": "order",
"defaults": {
"doctype": "Quotation",
"parents": [{"title": _("Quotes"), "name": "quotes"}]
}
},
{"from_route": "/quotation", "to_route": "Quotation"},
{"from_route": "/shipments", "to_route": "Delivery Note"},
{"from_route": "/shipments/<path:name>", "to_route": "order",
"defaults": {
@@ -117,7 +110,7 @@ standard_portal_menu_items = [
{"title": _("Projects"), "route": "/project", "reference_doctype": "Project"},
{"title": _("Request for Quotations"), "route": "/rfq", "reference_doctype": "Request for Quotation", "role": "Supplier"},
{"title": _("Supplier Quotation"), "route": "/quotations", "reference_doctype": "Supplier Quotation", "role": "Supplier"},
{"title": _("Quotes"), "route": "/quotes", "reference_doctype": "Quotation", "role":"Customer"},
{"title": _("Quotations"), "route": "/quotation", "reference_doctype": "Quotation", "role":"Customer"},
{"title": _("Orders"), "route": "/orders", "reference_doctype": "Sales Order", "role":"Customer"},
{"title": _("Invoices"), "route": "/invoices", "reference_doctype": "Sales Invoice", "role":"Customer"},
{"title": _("Shipments"), "route": "/shipments", "reference_doctype": "Delivery Note", "role":"Customer"},
@@ -190,10 +183,13 @@ scheduler_events = {
"erpnext.projects.doctype.task.task.set_tasks_as_overdue",
"erpnext.accounts.doctype.asset.depreciation.post_depreciation_entries",
"erpnext.hr.doctype.daily_work_summary_settings.daily_work_summary_settings.send_summary",
"erpnext.stock.doctype.serial_no.serial_no.update_maintenance_status"
"erpnext.stock.doctype.serial_no.serial_no.update_maintenance_status",
"erpnext.setup.doctype.company.company.cache_companies_monthly_sales_history"
]
}
email_brand_image = "assets/erpnext/images/erpnext-logo.jpg"
default_mail_footer = """<div style="text-align: center;">
<a href="https://erpnext.com?source=via_email_footer" target="_blank" style="color: #8d99a6;">
Sent via ERPNext
@@ -211,3 +207,11 @@ bot_parsers = [
get_site_info = 'erpnext.utilities.get_site_info'
payment_gateway_enabled = "erpnext.accounts.utils.create_payment_gateway_account"
regional_overrides = {
'India': {
'erpnext.tests.test_regional.test_method': 'erpnext.regional.india.utils.test_method',
'erpnext.controllers.taxes_and_totals.get_itemised_tax_breakup_header': 'erpnext.regional.india.utils.get_itemised_tax_breakup_header',
'erpnext.controllers.taxes_and_totals.get_itemised_tax_breakup_data': 'erpnext.regional.india.utils.get_itemised_tax_breakup_data'
}
}

View File

@@ -8,7 +8,7 @@ from frappe.model.document import Document
from frappe import _
from email_reply_parser import EmailReplyParser
from erpnext.hr.doctype.employee.employee import is_holiday
from frappe.utils import formatdate
from frappe.utils import global_date_format
from markdown2 import markdown
class DailyWorkSummary(Document):
@@ -24,17 +24,18 @@ class DailyWorkSummary(Document):
def send_summary(self):
'''Send summary of all replies. Called at midnight'''
message = self.get_summary_message()
args = self.get_message_details()
frappe.sendmail(recipients = get_employee_emails(self.company, False),
message = message,
template='daily_work_summary',
args=args,
subject = _('Daily Work Summary for {0}').format(self.company),
reference_doctype=self.doctype, reference_name=self.name)
self.db_set('status', 'Sent')
def get_summary_message(self):
'''Return summary of replies as HTML'''
def get_message_details(self):
'''Return args for template'''
settings = frappe.get_doc('Daily Work Summary Settings')
replies = frappe.get_all('Communication', fields=['content', 'text_content', 'sender'],
@@ -45,8 +46,12 @@ class DailyWorkSummary(Document):
did_not_reply = self.email_sent_to.split()
for d in replies:
d.sender_name = frappe.db.get_value("Employee", {"user_id": d.sender},
"employee_name") or d.sender
emp = frappe.db.get_values("Employee", {"user_id": d.sender},
["employee_name", "image"], as_dict=True)
d.sender_name = emp[0].employee_name if emp else d.sender
d.image = emp[0].image if emp and emp[0].image else None
if d.sender in did_not_reply:
did_not_reply.remove(d.sender)
if d.text_content:
@@ -56,30 +61,12 @@ class DailyWorkSummary(Document):
did_not_reply = [(frappe.db.get_value("Employee", {"user_id": email}, "employee_name") or email)
for email in did_not_reply]
return frappe.render_template(self.get_summary_template(),
dict(replies=replies,
original_message=settings.message,
title=_('Daily Work Summary for {0}'.format(formatdate(self.creation))),
did_not_reply= ', '.join(did_not_reply) or '',
did_not_reply_title = _('No replies from')))
return dict(replies=replies,
original_message=settings.message,
title=_('Daily Work Summary for {0}'.format(global_date_format(self.creation))),
did_not_reply= ', '.join(did_not_reply) or '',
did_not_reply_title = _('No replies from'))
def get_summary_template(self):
return '''
<h3>{{ title }}</h3>
{% for reply in replies %}
<h4>{{ reply.sender_name }}</h4>
<p style="padding-bottom: 20px">
{{ reply.content }}
</p>
<hr>
{% endfor %}
{% if did_not_reply %}
<p>{{ did_not_reply_title }}: {{ did_not_reply }}</p>
{% endif %}
'''
def get_employee_emails(company, only_working=True):
'''Returns list of Employee user ids for the given company who are working today

View File

@@ -46,9 +46,9 @@ class TestDailyWorkSummary(unittest.TestCase):
daily_work_summary = frappe.get_doc('Daily Work Summary',
frappe.get_all('Daily Work Summary')[0].name)
summary = daily_work_summary.get_summary_message()
args = daily_work_summary.get_message_details()
self.assertTrue('I built Daily Work Summary!' in summary)
self.assertTrue('I built Daily Work Summary!' in args.get('replies')[0].content)
def setup_and_prepare_test(self, hour=None):
frappe.db.sql('delete from `tabDaily Work Summary`')

View File

@@ -230,7 +230,7 @@ frappe.ui.form.on("Expense Claim",{
frm.fields_dict["payable_account"].get_query = function() {
return {
filters: {
"root_type": "Liability",
"report_type": "Balance Sheet",
"account_type": "Payable"
}
}

View File

@@ -905,7 +905,7 @@
"label": "Status",
"length": 0,
"no_copy": 1,
"options": "Draft\nPaid\nUnpaid\nSubmitted\nCancelled",
"options": "Draft\nPaid\nUnpaid\nRejected\nSubmitted\nCancelled",
"permlevel": 0,
"precision": "",
"print_hide": 1,
@@ -964,7 +964,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2017-06-13 14:29:16.914609",
"modified": "2017-07-17 15:47:23.255142",
"modified_by": "Administrator",
"module": "HR",
"name": "Expense Claim",

View File

@@ -39,10 +39,13 @@ class ExpenseClaim(AccountsController):
"2": "Cancelled"
}[cstr(self.docstatus or 0)]
if self.total_sanctioned_amount == self.total_amount_reimbursed and self.docstatus == 1:
if self.total_sanctioned_amount > 0 and self.total_sanctioned_amount == self.total_amount_reimbursed \
and self.docstatus == 1 and self.approval_status == 'Approved':
self.status = "Paid"
elif self.docstatus == 1:
elif self.total_sanctioned_amount > 0 and self.docstatus == 1 and self.approval_status == 'Approved':
self.status = "Unpaid"
elif self.docstatus == 1 and self.approval_status == 'Rejected':
self.status = 'Rejected'
def set_payable_account(self):
if not self.payable_account and not self.is_paid:
@@ -157,6 +160,9 @@ class ExpenseClaim(AccountsController):
self.total_claimed_amount = 0
self.total_sanctioned_amount = 0
for d in self.get('expenses'):
if self.approval_status == 'Rejected':
d.sanctioned_amount = 0.0
self.total_claimed_amount += flt(d.claim_amount)
self.total_sanctioned_amount += flt(d.sanctioned_amount)

View File

@@ -4,8 +4,10 @@ frappe.listview_settings['Expense Claim'] = {
get_indicator: function(doc) {
if(doc.status == "Paid") {
return [__("Paid"), "green", "status,=,'Paid'"];
} else {
}else if(doc.status == "Unpaid") {
return [__("Unpaid"), "orange"];
} else if(doc.status == "Rejected") {
return [__("Rejected"), "grey"];
}
}
};

View File

@@ -113,5 +113,23 @@ class TestExpenseClaim(unittest.TestCase):
self.assertEquals(expected_values[gle.account][1], gle.debit)
self.assertEquals(expected_values[gle.account][2], gle.credit)
def test_rejected_expense_claim(self):
payable_account = get_payable_account("Wind Power LLC")
expense_claim = frappe.get_doc({
"doctype": "Expense Claim",
"employee": "_T-Employee-0001",
"payable_account": payable_account,
"approval_status": "Rejected",
"expenses":
[{ "expense_type": "Travel", "default_account": "Travel Expenses - WP", "claim_amount": 300, "sanctioned_amount": 200 }]
})
expense_claim.submit()
self.assertEquals(expense_claim.status, 'Rejected')
self.assertEquals(expense_claim.total_sanctioned_amount, 0.0)
gl_entry = frappe.get_all('GL Entry', {'voucher_type': 'Expense Claim', 'voucher_no': expense_claim.name})
self.assertEquals(len(gl_entry), 0)
def get_payable_account(company):
return frappe.db.get_value('Company', company, 'default_payable_account')

View File

@@ -69,27 +69,17 @@ def get_events(start, end, filters=None):
:param end: End date-time.
:param filters: Filters (JSON).
"""
condition = ''
values = {
"start_date": getdate(start),
"end_date": getdate(end)
}
if filters:
if isinstance(filters, basestring):
filters = json.loads(filters)
filters = json.loads(filters)
else:
filters = []
if filters.get('holiday_list'):
condition = 'and hlist.name=%(holiday_list)s'
values['holiday_list'] = filters['holiday_list']
if start:
filters.append(['Holiday', 'holiday_date', '>', getdate(start)])
if end:
filters.append(['Holiday', 'holiday_date', '<', getdate(end)])
data = frappe.db.sql("""select hlist.name, h.holiday_date, h.description
from `tabHoliday List` hlist, tabHoliday h
where h.parent = hlist.name
and h.holiday_date is not null
and h.holiday_date >= %(start_date)s
and h.holiday_date <= %(end_date)s
{condition}""".format(condition=condition),
values, as_dict=True, update={"allDay": 1})
return data
return frappe.get_list('Holiday List',
fields=['name', '`tabHoliday`.holiday_date', '`tabHoliday`.description'],
filters = filters,
update={"allDay": 1})

View File

@@ -63,13 +63,13 @@ class LeaveApplication(Document):
def validate_dates(self):
if self.from_date and self.to_date and (getdate(self.to_date) < getdate(self.from_date)):
frappe.throw(_("To date cannot be before from date"))
if self.half_day and self.half_day_date \
and (getdate(self.half_day_date) < getdate(self.from_date)
and (getdate(self.half_day_date) < getdate(self.from_date)
or getdate(self.half_day_date) > getdate(self.to_date)):
frappe.throw(_("Half Day Date should be between From Date and To Date"))
if not is_lwp(self.leave_type):
self.validate_dates_acorss_allocation()
self.validate_back_dated_application()
@@ -158,7 +158,7 @@ class LeaveApplication(Document):
self.name = "New Leave Application"
for d in frappe.db.sql("""
select
select
name, leave_type, posting_date, from_date, to_date, total_leave_days, half_day_date
from `tabLeave Application`
where employee = %(employee)s and docstatus < 2 and status in ("Open", "Approved")
@@ -169,12 +169,12 @@ class LeaveApplication(Document):
"to_date": self.to_date,
"name": self.name
}, as_dict = 1):
if cint(self.half_day)==1 and getdate(self.half_day_date) == getdate(d.half_day_date) and (
flt(self.total_leave_days)==0.5
or getdate(self.from_date) == getdate(d.to_date)
flt(self.total_leave_days)==0.5
or getdate(self.from_date) == getdate(d.to_date)
or getdate(self.to_date) == getdate(d.from_date)):
total_leaves_on_half_day = self.get_total_leaves_on_half_day()
if total_leaves_on_half_day >= 1:
self.throw_overlap_error(d)
@@ -199,7 +199,7 @@ class LeaveApplication(Document):
"half_day_date": self.half_day_date,
"name": self.name
})[0][0]
return leave_count_on_half_day_date * 0.5
def validate_max_days(self):
@@ -400,7 +400,7 @@ def is_lwp(leave_type):
return lwp and cint(lwp[0][0]) or 0
@frappe.whitelist()
def get_events(start, end):
def get_events(start, end, filters=None):
events = []
employee = frappe.db.get_value("Employee", {"user_id": frappe.session.user}, ["name", "company"],
@@ -411,14 +411,14 @@ def get_events(start, end):
employee=''
company=frappe.db.get_value("Global Defaults", None, "default_company")
from frappe.desk.reportview import build_match_conditions
match_conditions = build_match_conditions("Leave Application")
from frappe.desk.reportview import get_filters_cond
conditions = get_filters_cond("Leave Application", filters, [])
# show department leaves for employee
if "Employee" in frappe.get_roles():
add_department_leaves(events, start, end, employee, company)
add_leaves(events, start, end, match_conditions)
add_leaves(events, start, end, conditions)
add_block_dates(events, start, end, employee, company)
add_holidays(events, start, end, employee, company)
@@ -435,7 +435,7 @@ def add_department_leaves(events, start, end, employee, company):
department_employees = frappe.db.sql_list("""select name from tabEmployee where department=%s
and company=%s""", (department, company))
match_conditions = "employee in (\"%s\")" % '", "'.join(department_employees)
match_conditions = "and employee in (\"%s\")" % '", "'.join(department_employees)
add_leaves(events, start, end, match_conditions=match_conditions)
def add_leaves(events, start, end, match_conditions=None):
@@ -446,7 +446,7 @@ def add_leaves(events, start, end, match_conditions=None):
and docstatus < 2
and status!="Rejected" """
if match_conditions:
query += " and " + match_conditions
query += match_conditions
for d in frappe.db.sql(query, {"start":start, "end": end}, as_dict=True):
e = {

View File

@@ -1,6 +1,8 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
var in_progress = false;
frappe.ui.form.on("Process Payroll", {
onload: function (frm) {
frm.doc.posting_date = frappe.datetime.nowdate();
@@ -47,11 +49,12 @@ frappe.ui.form.on("Process Payroll", {
},
start_date: function (frm) {
frm.trigger("set_start_end_dates");
},
end_date: function (frm) {
frm.trigger("set_start_end_dates");
if(!in_progress && frm.doc.start_date){
frm.trigger("set_end_date");
}else{
// reset flag
in_progress = false
}
},
salary_slip_based_on_timesheet: function (frm) {
@@ -68,10 +71,11 @@ frappe.ui.form.on("Process Payroll", {
method: 'erpnext.hr.doctype.process_payroll.process_payroll.get_start_end_dates',
args: {
payroll_frequency: frm.doc.payroll_frequency,
start_date: frm.doc.start_date || frm.doc.posting_date
start_date: frm.doc.posting_date
},
callback: function (r) {
if (r.message) {
in_progress = true;
frm.set_value('start_date', r.message.start_date);
frm.set_value('end_date', r.message.end_date);
}
@@ -79,6 +83,21 @@ frappe.ui.form.on("Process Payroll", {
})
}
},
set_end_date: function(frm){
frappe.call({
method: 'erpnext.hr.doctype.process_payroll.process_payroll.get_end_date',
args: {
frequency: frm.doc.payroll_frequency,
start_date: frm.doc.start_date
},
callback: function (r) {
if (r.message) {
frm.set_value('end_date', r.message.end_date);
}
}
})
}
})
cur_frm.cscript.display_activity_log = function (msg) {

View File

@@ -3,7 +3,8 @@
from __future__ import unicode_literals
import frappe
from frappe.utils import cint, flt, nowdate, add_days, getdate, fmt_money
from dateutil.relativedelta import relativedelta
from frappe.utils import cint, nowdate, add_days, getdate, fmt_money, add_to_date, DATE_FORMAT
from frappe import _
from erpnext.accounts.utils import get_fiscal_year
@@ -47,7 +48,6 @@ class ProcessPayroll(Document):
%s """% cond, {"sal_struct": sal_struct})
return emp_list
def get_filter_condition(self):
self.check_mandatory()
@@ -58,7 +58,6 @@ class ProcessPayroll(Document):
return cond
def get_joining_releiving_condition(self):
cond = """
and ifnull(t1.date_of_joining, '0000-00-00') <= '%(end_date)s'
@@ -66,7 +65,6 @@ class ProcessPayroll(Document):
""" % {"start_date": self.start_date, "end_date": self.end_date}
return cond
def check_mandatory(self):
for fieldname in ['company', 'start_date', 'end_date']:
if not self.get(fieldname):
@@ -110,7 +108,6 @@ class ProcessPayroll(Document):
ss_list.append(ss_dict)
return self.create_log(ss_list)
def create_log(self, ss_list):
if not ss_list or len(ss_list) < 1:
log = "<p>" + _("No employee for the above selected criteria OR salary slip already created") + "</p>"
@@ -134,7 +131,6 @@ class ProcessPayroll(Document):
""" % ('%s', '%s', '%s','%s', cond), (ss_status, self.start_date, self.end_date, self.salary_slip_based_on_timesheet), as_dict=as_dict)
return ss_list
def submit_salary_slips(self):
"""
Submit all salary slips based on selected criteria
@@ -194,7 +190,6 @@ class ProcessPayroll(Document):
def format_as_links(self, salary_slip):
return ['<a href="#Form/Salary Slip/{0}">{0}</a>'.format(salary_slip)]
def get_total_salary_and_loan_amounts(self):
"""
Get total loan principal, loan interest and salary amount from submitted salary slip based on selected criteria
@@ -257,7 +252,6 @@ class ProcessPayroll(Document):
return payroll_payable_account
def make_accural_jv_entry(self):
self.check_permission('write')
earnings = self.get_salary_component_total(component_type = "earnings") or {}
@@ -358,7 +352,6 @@ class ProcessPayroll(Document):
self.update(get_start_end_dates(self.payroll_frequency,
self.start_date or self.posting_date, self.company))
@frappe.whitelist()
def get_start_end_dates(payroll_frequency, start_date=None, company=None):
'''Returns dict of start and end dates for given payroll frequency based on start_date'''
@@ -391,6 +384,29 @@ def get_start_end_dates(payroll_frequency, start_date=None, company=None):
'start_date': start_date, 'end_date': end_date
})
def get_frequency_kwargs(frequency_name):
frequency_dict = {
'monthly': {'months': 1},
'fortnightly': {'days': 14},
'weekly': {'days': 7},
'daily': {'days': 1}
}
return frequency_dict.get(frequency_name)
@frappe.whitelist()
def get_end_date(start_date, frequency):
start_date = getdate(start_date)
frequency = frequency.lower() if frequency else 'monthly'
kwargs = get_frequency_kwargs(frequency) if frequency != 'bimonthly' else get_frequency_kwargs('monthly')
# weekly, fortnightly and daily intervals have fixed days so no problems
end_date = add_to_date(start_date, **kwargs) - relativedelta(days=1)
if frequency != 'bimonthly':
return dict(end_date=end_date.strftime(DATE_FORMAT))
else:
return dict(end_date='')
def get_month_details(year, month):
ysd = frappe.db.get_value("Fiscal Year", year, "year_start_date")
if ysd:

View File

@@ -3,10 +3,12 @@
from __future__ import unicode_literals
import unittest
import frappe
import erpnext
from frappe.utils import flt, add_months, cint, nowdate, getdate, add_days, random_string
from frappe.utils.make_random import get_random
import frappe
from frappe.utils import nowdate
from erpnext.hr.doctype.process_payroll.process_payroll import get_end_date
class TestProcessPayroll(unittest.TestCase):
def test_process_payroll(self):
@@ -30,6 +32,16 @@ class TestProcessPayroll(unittest.TestCase):
process_payroll.submit_salary_slips()
if process_payroll.get_sal_slip_list(ss_status = 1):
r = process_payroll.make_payment_entry()
def test_get_end_date(self):
self.assertEqual(get_end_date('2017-01-01', 'monthly'), {'end_date': '2017-01-31'})
self.assertEqual(get_end_date('2017-02-01', 'monthly'), {'end_date': '2017-02-28'})
self.assertEqual(get_end_date('2017-02-01', 'fortnightly'), {'end_date': '2017-02-14'})
self.assertEqual(get_end_date('2017-02-01', 'bimonthly'), {'end_date': ''})
self.assertEqual(get_end_date('2017-01-01', 'bimonthly'), {'end_date': ''})
self.assertEqual(get_end_date('2020-02-15', 'bimonthly'), {'end_date': ''})
self.assertEqual(get_end_date('2017-02-15', 'monthly'), {'end_date': '2017-03-14'})
self.assertEqual(get_end_date('2017-02-15', 'daily'), {'end_date': '2017-02-15'})
def get_salary_component_account(sal_comp):

View File

@@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
@@ -12,6 +13,7 @@
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -22,6 +24,7 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Payment Date",
@@ -40,6 +43,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -50,11 +54,13 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Principal Amount",
"length": 0,
"no_copy": 1,
"options": "Company:company:default_currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -68,6 +74,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -78,11 +85,13 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Interest Amount",
"length": 0,
"no_copy": 1,
"options": "Company:company:default_currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -96,6 +105,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -106,11 +116,13 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Total Payment",
"length": 0,
"no_copy": 0,
"options": "Company:company:default_currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -124,6 +136,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -134,11 +147,13 @@
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Balance Loan Amount",
"length": 0,
"no_copy": 1,
"options": "Company:company:default_currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -152,17 +167,17 @@
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-01-09 12:00:10.818772",
"modified": "2017-07-26 14:47:29.862084",
"modified_by": "Administrator",
"module": "HR",
"name": "Repayment Schedule",
@@ -172,6 +187,7 @@
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,

View File

@@ -29,6 +29,27 @@ frappe.ui.form.on("Salary Slip", {
})
},
start_date: function(frm){
if(frm.doc.start_date){
frm.trigger("set_end_date");
}
},
set_end_date: function(frm){
frappe.call({
method: 'erpnext.hr.doctype.process_payroll.process_payroll.get_end_date',
args: {
frequency: frm.doc.payroll_frequency,
start_date: frm.doc.start_date
},
callback: function (r) {
if (r.message) {
frm.set_value('end_date', r.message.end_date);
}
}
})
},
company: function(frm) {
var company = locals[':Company'][frm.doc.company];
if(!frm.doc.letter_head && company.default_letter_head) {
@@ -45,11 +66,17 @@ frappe.ui.form.on("Salary Slip", {
},
salary_slip_based_on_timesheet: function(frm) {
frm.trigger("toggle_fields")
frm.trigger("toggle_fields");
frm.set_value('start_date', '');
},
payroll_frequency: function(frm) {
frm.trigger("toggle_fields")
frm.trigger("toggle_fields");
frm.set_value('start_date', '');
},
employee: function(frm){
frm.set_value('start_date', '');
},
toggle_fields: function(frm) {
@@ -74,18 +101,19 @@ frappe.ui.form.on('Salary Detail', {
// Get leave details
//---------------------------------------------------------------------
cur_frm.cscript.start_date = function(doc, dt, dn){
return frappe.call({
method: 'get_emp_and_leave_details',
doc: locals[dt][dn],
callback: function(r, rt) {
cur_frm.refresh();
calculate_all(doc, dt, dn);
}
});
if(!doc.start_date){
return frappe.call({
method: 'get_emp_and_leave_details',
doc: locals[dt][dn],
callback: function(r, rt) {
cur_frm.refresh();
calculate_all(doc, dt, dn);
}
});
}
}
cur_frm.cscript.payroll_frequency = cur_frm.cscript.salary_slip_based_on_timesheet = cur_frm.cscript.start_date;
cur_frm.cscript.end_date = cur_frm.cscript.start_date;
cur_frm.cscript.employee = function(doc,dt,dn){
doc.salary_structure = ''

View File

@@ -51,6 +51,7 @@ class BOM(WebsiteGenerator):
from erpnext.utilities.transaction_base import validate_uom_is_integer
validate_uom_is_integer(self, "stock_uom", "stock_qty", "BOM Item")
self.update_stock_qty()
self.validate_materials()
self.set_bom_material_details()
self.validate_operations()

View File

@@ -77,7 +77,6 @@ frappe.ui.form.on("Production Order", {
if (!frm.doc.status)
frm.doc.status = 'Draft';
frm.add_fetch("sales_order", "delivery_date", "expected_delivery_date");
frm.add_fetch("sales_order", "project", "project");
if(frm.doc.__islocal) {

View File

@@ -50,8 +50,12 @@ class ProductionOrder(Document):
def validate_sales_order(self):
if self.sales_order:
so = frappe.db.sql("""select name, delivery_date, project from `tabSales Order`
where name=%s and docstatus = 1""", self.sales_order, as_dict=1)
so = frappe.db.sql("""
select so.name, so_item.delivery_date, so.project
from `tabSales Order` so, `tabSales Order Item` so_item
where so.name=%s and so.name=so_item.parent
and so.docstatus = 1 and so_item.item_code=%s
""", (self.sales_order, self.production_item), as_dict=1)
if len(so):
if not self.expected_delivery_date:
@@ -510,6 +514,12 @@ def check_if_scrap_warehouse_mandatory(bom_no):
return res
@frappe.whitelist()
def set_production_order_ops(name):
po = frappe.get_doc('Production Order', name)
po.set_production_order_operations()
po.save()
@frappe.whitelist()
def make_stock_entry(production_order_id, purpose, qty=None):
production_order = frappe.get_doc("Production Order", production_order_id)

View File

@@ -8,3 +8,6 @@ from frappe.model.document import Document
class ProductionOrderItem(Document):
pass
def on_doctype_update():
frappe.db.add_index("Production Order Item", ["item_code", "source_warehouse"])

View File

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

View File

@@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:workstation_name",
@@ -12,11 +13,12 @@
"editable_grid": 0,
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"collapsible": 1,
"columns": 0,
"fieldname": "description_and_warehouse",
"fieldname": "description_section",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
@@ -25,7 +27,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "",
"label": "Description",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -41,6 +43,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -71,6 +74,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -102,65 +106,7 @@
"width": "300px"
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_4",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "",
"fieldname": "holiday_list",
"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": "Holiday List",
"length": 0,
"no_copy": 0,
"options": "Holiday List",
"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,
@@ -190,6 +136,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -221,6 +168,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -252,6 +200,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -280,6 +229,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -311,6 +261,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -342,6 +293,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -373,6 +325,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -402,6 +355,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -430,20 +384,52 @@
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "",
"fieldname": "holiday_list",
"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": "Holiday List",
"length": 0,
"no_copy": 0,
"options": "Holiday List",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "icon-wrench",
"idx": 1,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-02-17 17:26:57.238167",
"modified": "2017-07-18 22:28:50.163219",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Workstation",
@@ -470,11 +456,11 @@
"write": 1
}
],
"quick_entry": 0,
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 1,
"sort_order": "ASC",
"track_changes": 0,
"track_changes": 1,
"track_seen": 0
}

View File

@@ -0,0 +1,5 @@
/* eslint-disable */
frappe.listview_settings['Workstation'] = {
// add_fields: ["status"],
// filters:[["status","=", "Open"]]
};

View File

@@ -416,4 +416,11 @@ erpnext.patches.v8_0.update_production_orders
erpnext.patches.v8_1.remove_sales_invoice_from_returned_serial_no
erpnext.patches.v8_1.allow_invoice_copy_to_edit_after_submit
erpnext.patches.v8_1.add_hsn_sac_codes
erpnext.patches.v8_1.update_gst_state
erpnext.patches.v8_1.update_gst_state #17-07-2017
erpnext.patches.v8_1.removed_report_support_hours
erpnext.patches.v8_1.add_indexes_in_transaction_doctypes
erpnext.patches.v8_3.set_restrict_to_domain_for_module_def
erpnext.patches.v8_1.update_expense_claim_status
erpnext.patches.v8_3.update_company_total_sales
erpnext.patches.v8_1.set_delivery_date_in_so_item
erpnext.patches.v8_5.fix_tax_breakup_for_non_invoice_docs

View File

@@ -11,6 +11,9 @@ def execute():
for dt in ("assessment", "course", "fees"):
frappe.reload_doc("schools", "doctype", dt)
for dt in ("domain", "has_domain", "domain_settings"):
frappe.reload_doc("core", "doctype", dt)
frappe.reload_doc('website', 'doctype', 'portal_menu_item')
frappe.get_doc('Portal Settings').sync_menu()

View File

@@ -0,0 +1,9 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
import frappe
def execute():
for dt in ("Sales Order Item", "Purchase Order Item",
"Material Request Item", "Production Order Item", "Packed Item"):
frappe.get_doc("DocType", dt).run_module_method("on_doctype_update")

View File

@@ -0,0 +1,14 @@
# Copyright (c) 2017, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
frappe.db.sql(""" update `tabAuto Email Report` set report = %s
where name = %s""", ('Support Hour Distribution', 'Support Hours'))
frappe.db.sql(""" update `tabCustom Role` set report = %s
where report = %s""", ('Support Hour Distribution', 'Support Hours'))
frappe.delete_doc('Report', 'Support Hours')

View File

@@ -0,0 +1,13 @@
import frappe
def execute():
frappe.reload_doctype("Sales Order")
frappe.reload_doctype("Sales Order Item")
frappe.db.sql("""update `tabSales Order` set final_delivery_date = delivery_date where docstatus=1""")
frappe.db.sql("""
update `tabSales Order` so, `tabSales Order Item` so_item
set so_item.delivery_date = so.delivery_date
where so.name = so_item.parent
""")

View File

@@ -0,0 +1,23 @@
# Copyright (c) 2017, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
frappe.reload_doctype('Expense Claim')
for data in frappe.db.sql(""" select name from `tabExpense Claim`
where (docstatus=1 and total_sanctioned_amount=0 and status = 'Paid') or
(docstatus = 1 and approval_status = 'Rejected' and total_sanctioned_amount > 0)""", as_dict=1):
doc = frappe.get_doc('Expense Claim', data.name)
if doc.approval_status == 'Rejected':
for d in doc.expenses:
d.db_set("sanctioned_amount", 0, update_modified = False)
doc.db_set("total_sanctioned_amount", 0, update_modified = False)
frappe.db.sql(""" delete from `tabGL Entry` where voucher_type = 'Expense Claim'
and voucher_no = %s""", (doc.name))
doc.set_status()
doc.db_set("status", doc.status, update_modified = False)

View File

@@ -11,3 +11,4 @@ def execute():
frappe.db.sql("update `tabCustom Field` set options=%s where fieldname='gst_state'", '\n'.join(states))
frappe.db.sql("update `tabAddress` set gst_state='Chhattisgarh' where gst_state='Chattisgarh'")
frappe.db.sql("update `tabAddress` set gst_state_number='05' where gst_state='Uttarakhand'")

View File

View File

@@ -0,0 +1,14 @@
# Copyright (c) 2017, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
from erpnext.setup.setup_wizard.domainify import update_module_def_restrict_to_domain
def execute():
""" set the restrict to domain in module def """
frappe.reload_doc("core", "doctype", "module_def")
if frappe.db.get_single_value('System Settings', 'setup_complete'):
update_module_def_restrict_to_domain()

View File

@@ -0,0 +1,15 @@
# Copyright (c) 2017, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
from erpnext.setup.doctype.company.company import update_company_current_month_sales, update_company_monthly_sales
def execute():
'''Update company monthly sales history based on sales invoices'''
frappe.reload_doctype("Company")
companies = [d['name'] for d in frappe.get_list("Company")]
for company in companies:
update_company_current_month_sales(company)
update_company_monthly_sales(company)

View File

View File

@@ -0,0 +1,47 @@
import frappe
from erpnext.regional.india.setup import make_custom_fields
from erpnext.controllers.taxes_and_totals import get_itemised_tax_breakup_html
def execute():
companies = [d.name for d in frappe.get_all('Company', filters = {'country': 'India'})]
if not companies:
return
make_custom_fields()
# update invoice copy value
values = ["Original for Recipient", "Duplicate for Transporter",
"Duplicate for Supplier", "Triplicate for Supplier"]
for d in values:
frappe.db.sql("update `tabSales Invoice` set invoice_copy=%s where invoice_copy=%s", (d, d))
# update tax breakup in transactions made after 1st July 2017
doctypes = ["Quotation", "Sales Order", "Delivery Note", "Sales Invoice",
"Supplier Quotation", "Purchase Order", "Purchase Receipt", "Purchase Invoice"]
for doctype in doctypes:
frappe.reload_doctype(doctype)
date_field = "posting_date"
if doctype in ["Quotation", "Sales Order", "Supplier Quotation", "Purchase Order"]:
date_field = "transaction_date"
records = [d.name for d in frappe.get_all(doctype, filters={
"docstatus": ["!=", 2],
date_field: [">=", "2017-07-01"],
"company": ["in", companies],
"total_taxes_and_charges": [">", 0],
"other_charges_calculation": ""
})]
if records:
frappe.db.sql("""
update `tab%s Item` dt_item
set gst_hsn_code = (select gst_hsn_code from tabItem where name=dt_item.item_code)
where parent in (%s)
and (gst_hsn_code is null or gst_hsn_code = '')
""" % (doctype, ', '.join(['%s']*len(records))), tuple(records))
for record in records:
doc = frappe.get_doc(doctype, record)
html = get_itemised_tax_breakup_html(doc)
doc.db_set("other_charges_calculation", html, update_modified=False)

View File

@@ -3,6 +3,22 @@
frappe.ui.form.on("Project", {
onload: function(frm) {
frm.set_indicator_formatter('title',
function(doc) {
let indicator = 'orange';
if (doc.status == 'Overdue') {
indicator = 'red';
}
else if (doc.status == 'Cancelled') {
indicator = 'dark grey';
}
else if (doc.status == 'Closed') {
indicator = 'green';
}
return indicator;
}
);
var so = frappe.meta.get_docfield("Project", "sales_order");
so.get_route_options_for_new_doc = function(field) {
if(frm.is_new()) return;
@@ -35,6 +51,7 @@ frappe.ui.form.on("Project", {
}
});
},
refresh: function(frm) {
if(frm.doc.__islocal) {
frm.web_link && frm.web_link.remove();

View File

@@ -14,6 +14,7 @@
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -44,6 +45,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -76,6 +78,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -107,6 +110,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -138,6 +142,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -169,6 +174,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -197,6 +203,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -228,6 +235,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -258,6 +266,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
@@ -269,7 +278,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": "Expected End Date",
"length": 0,
@@ -288,6 +297,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
@@ -316,6 +326,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -346,6 +357,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -377,6 +389,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -405,6 +418,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -435,6 +449,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -464,6 +479,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -495,6 +511,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -525,6 +542,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -555,6 +573,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -584,6 +603,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -614,6 +634,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -644,6 +665,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -673,6 +695,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -702,6 +725,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -731,6 +755,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -759,6 +784,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -789,6 +815,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -819,6 +846,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -850,6 +878,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -880,6 +909,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -910,6 +940,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -939,6 +970,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -968,6 +1000,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -996,6 +1029,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1026,6 +1060,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1055,6 +1090,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1084,6 +1120,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -1114,6 +1151,7 @@
"width": "50%"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1145,6 +1183,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1173,6 +1212,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1215,8 +1255,8 @@
"issingle": 0,
"istable": 0,
"max_attachments": 4,
"modified": "2017-04-19 13:16:32.462005",
"modified_by": "faris@erpnext.com",
"modified": "2017-07-19 14:36:20.857673",
"modified_by": "Administrator",
"module": "Projects",
"name": "Project",
"owner": "Administrator",

View File

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

View File

@@ -133,10 +133,15 @@ var calculate_end_time = function(frm, cdt, cdn) {
var child = locals[cdt][cdn];
var d = moment(child.from_time);
d.add(child.hours, "hours");
frm._setting_hours = true;
frappe.model.set_value(cdt, cdn, "to_time", d.format(moment.defaultDatetimeFormat));
frm._setting_hours = false;
if(child.hours) {
d.add(child.hours, "hours");
frm._setting_hours = true;
frappe.model.set_value(cdt, cdn, "to_time",
d.format(moment.defaultDatetimeFormat)).then(() => {
frm._setting_hours = false;
});
}
if((frm.doc.__islocal || frm.doc.__onload.maintain_bill_work_hours_same) && child.hours){
frappe.model.set_value(cdt, cdn, "billing_hours", child.hours);

View File

@@ -9,9 +9,8 @@ from frappe import _
import json
from datetime import timedelta
from erpnext.controllers.queries import get_match_cond
from frappe.utils import flt, time_diff_in_hours, get_datetime, getdate, cint, get_datetime_str
from frappe.utils import flt, time_diff_in_hours, get_datetime, getdate, cint
from frappe.model.document import Document
from frappe.model.mapper import get_mapped_doc
from erpnext.manufacturing.doctype.workstation.workstation import (check_if_within_operating_hours,
WorkstationHolidayError)
from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings import get_mins_between_operations
@@ -133,7 +132,7 @@ class Timesheet(Document):
if data.name == timesheet.operation_id:
summary = self.get_actual_timesheet_summary(timesheet.operation_id)
data.time_sheet = time_sheet
data.completed_qty = summary.completed_qty
data.completed_qty = summary.completed_qty
data.actual_operation_time = summary.mins
data.actual_start_time = summary.from_time
data.actual_end_time = summary.to_time
@@ -148,7 +147,7 @@ class Timesheet(Document):
"""Returns 'Actual Operating Time'. """
return frappe.db.sql("""select
sum(tsd.hours*60) as mins, sum(tsd.completed_qty) as completed_qty, min(tsd.from_time) as from_time,
max(tsd.to_time) as to_time from `tabTimesheet Detail` as tsd, `tabTimesheet` as ts where
max(tsd.to_time) as to_time from `tabTimesheet Detail` as tsd, `tabTimesheet` as ts where
ts.production_order = %s and tsd.operation_id = %s and ts.docstatus=1 and ts.name = tsd.parent""",
(self.production_order, operation_id), as_dict=1)[0]
@@ -192,7 +191,7 @@ class Timesheet(Document):
if fieldname == 'workstation':
cond = "tsd.`{0}`".format(fieldname)
existing = frappe.db.sql("""select ts.name as name, tsd.from_time as from_time, tsd.to_time as to_time from
existing = frappe.db.sql("""select ts.name as name, tsd.from_time as from_time, tsd.to_time as to_time from
`tabTimesheet Detail` tsd, `tabTimesheet` ts where {0}=%(val)s and tsd.parent = ts.name and
(
(%(from_time)s > tsd.from_time and %(from_time)s < tsd.to_time) or
@@ -211,8 +210,8 @@ class Timesheet(Document):
# check internal overlap
for time_log in self.time_logs:
if (fieldname != 'workstation' or args.get(fieldname) == time_log.get(fieldname)) and \
args.idx != time_log.idx and ((args.from_time > time_log.from_time and args.from_time < time_log.to_time) or
(args.to_time > time_log.from_time and args.to_time < time_log.to_time) or
args.idx != time_log.idx and ((args.from_time > time_log.from_time and args.from_time < time_log.to_time) or
(args.to_time > time_log.from_time and args.to_time < time_log.to_time) or
(args.from_time <= time_log.from_time and args.to_time >= time_log.to_time)):
return self
@@ -239,7 +238,7 @@ class Timesheet(Document):
self.check_workstation_working_day(data)
def get_last_working_slot(self, time_sheet, workstation):
return frappe.db.sql(""" select max(from_time) as from_time, max(to_time) as to_time
return frappe.db.sql(""" select max(from_time) as from_time, max(to_time) as to_time
from `tabTimesheet Detail` where workstation = %(workstation)s""",
{'workstation': workstation}, as_dict=True)[0]
@@ -277,7 +276,7 @@ def get_projectwise_timesheet_data(project, parent=None):
if parent:
cond = "and parent = %(parent)s"
return frappe.db.sql("""select name, parent, billing_hours, billing_amount as billing_amt
return frappe.db.sql("""select name, parent, billing_hours, billing_amount as billing_amt
from `tabTimesheet Detail` where docstatus=1 and project = %(project)s {0} and billable = 1
and sales_invoice is null""".format(cond), {'project': project, 'parent': parent}, as_dict=1)
@@ -290,9 +289,9 @@ def get_timesheet(doctype, txt, searchfield, start, page_len, filters):
condition = "and tsd.project = %(project)s"
return frappe.db.sql("""select distinct tsd.parent from `tabTimesheet Detail` tsd,
`tabTimesheet` ts where
ts.status in ('Submitted', 'Payslip') and tsd.parent = ts.name and
tsd.docstatus = 1 and ts.total_billable_amount > 0
`tabTimesheet` ts where
ts.status in ('Submitted', 'Payslip') and tsd.parent = ts.name and
tsd.docstatus = 1 and ts.total_billable_amount > 0
and tsd.parent LIKE %(txt)s {condition}
order by tsd.parent limit %(start)s, %(page_len)s"""
.format(condition=condition), {
@@ -305,7 +304,7 @@ def get_timesheet_data(name, project):
if project and project!='':
data = get_projectwise_timesheet_data(project, name)
else:
data = frappe.get_all('Timesheet',
data = frappe.get_all('Timesheet',
fields = ["(total_billable_amount - total_billed_amount) as billing_amt", "total_billable_hours as billing_hours"], filters = {'name': name})
return {
@@ -332,7 +331,7 @@ def make_sales_invoice(source_name, target=None):
@frappe.whitelist()
def make_salary_slip(source_name, target_doc=None):
target = frappe.new_doc("Salary Slip")
set_missing_values(source_name, target)
set_missing_values(source_name, target)
target.run_method("get_emp_and_leave_details")
return target
@@ -364,32 +363,21 @@ def get_events(start, end, filters=None):
:param filters: Filters (JSON).
"""
filters = json.loads(filters)
from frappe.desk.calendar import get_event_conditions
conditions = get_event_conditions("Timesheet", filters)
conditions = get_conditions(filters)
return frappe.db.sql("""select `tabTimesheet Detail`.name as name,
return frappe.db.sql("""select `tabTimesheet Detail`.name as name,
`tabTimesheet Detail`.docstatus as status, `tabTimesheet Detail`.parent as parent,
from_time as start_date, hours, activity_type,
`tabTimesheet Detail`.project, to_time as end_date,
CONCAT(`tabTimesheet Detail`.parent, ' (', ROUND(hours,2),' hrs)') as title
from `tabTimesheet Detail`, `tabTimesheet`
where `tabTimesheet Detail`.parent = `tabTimesheet`.name
and `tabTimesheet`.docstatus < 2
from_time as start_date, hours, activity_type,
`tabTimesheet Detail`.project, to_time as end_date,
CONCAT(`tabTimesheet Detail`.parent, ' (', ROUND(hours,2),' hrs)') as title
from `tabTimesheet Detail`, `tabTimesheet`
where `tabTimesheet Detail`.parent = `tabTimesheet`.name
and `tabTimesheet`.docstatus < 2
and (from_time <= %(end)s and to_time >= %(start)s) {conditions} {match_cond}
""".format(conditions=conditions, match_cond = get_match_cond('Timesheet')),
""".format(conditions=conditions, match_cond = get_match_cond('Timesheet')),
{
"start": start,
"end": end
}, as_dict=True, update={"allDay": 0})
def get_conditions(filters):
conditions = []
for key in filters:
if filters.get(key):
if frappe.get_meta("Timesheet").has_field(key):
dt = 'tabTimesheet'
elif frappe.get_meta("Timesheet Detail").has_field(key):
dt = 'tabTimesheet Detail'
conditions.append("`%s`.%s = '%s'"%(dt, key, filters.get(key)))
return " and {}".format(" and ".join(conditions)) if conditions else ""

View File

@@ -228,6 +228,8 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
items: my_items
},
callback: function(r) {
if(r.exc) return;
var i = 0;
var item_length = cur_frm.doc.items.length;
while (i < item_length) {
@@ -244,35 +246,26 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
cur_frm.doc.items[i].qty = my_qty;
frappe.msgprint("Assigning " + d.mr_name + " to " + d.item_code + " (row " + cur_frm.doc.items[i].idx + ")");
if (qty > 0)
{
if (qty > 0) {
frappe.msgprint("Splitting " + qty + " units of " + d.item_code);
var newrow = frappe.model.add_child(cur_frm.doc, cur_frm.doc.items[i].doctype, "items");
item_length++;
for (var key in cur_frm.doc.items[i])
{
for (var key in cur_frm.doc.items[i]) {
newrow[key] = cur_frm.doc.items[i][key];
}
newrow.idx = item_length;
newrow["stock_qty"] = newrow.conversion_factor*qty;
newrow["qty"] = qty;
newrow["material_request"] = "";
newrow["material_request_item"] = "";
}
}
});
i++;
}
refresh_field("items");
//cur_frm.save();
}
});
}

View File

@@ -40,8 +40,10 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
},
calculate_discount_amount: function(){
if (frappe.meta.get_docfield(this.frm.doc.doctype, "discount_amount"))
if (frappe.meta.get_docfield(this.frm.doc.doctype, "discount_amount")) {
this.set_discount_amount();
this.apply_discount_amount();
}
},
_calculate_taxes_and_totals: function() {
@@ -54,7 +56,6 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
this.manipulate_grand_total_for_inclusive_tax();
this.calculate_totals();
this._cleanup();
this.show_item_wise_taxes();
},
validate_conversion_rate: function() {
@@ -452,6 +453,13 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
}
},
set_discount_amount: function() {
if(this.frm.doc.additional_discount_percentage) {
this.frm.doc.discount_amount = flt(flt(this.frm.doc[frappe.scrub(this.frm.doc.apply_discount_on)])
* this.frm.doc.additional_discount_percentage / 100, precision("discount_amount"));
}
},
apply_discount_amount: function() {
var me = this;
var distributed_amount = 0.0;
@@ -634,99 +642,5 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
}
this.calculate_outstanding_amount(false)
},
show_item_wise_taxes: function() {
if(this.frm.fields_dict.other_charges_calculation) {
this.frm.toggle_display("other_charges_calculation", this.frm.doc.other_charges_calculation);
}
},
set_item_wise_tax_breakup: function() {
if(this.frm.fields_dict.other_charges_calculation) {
var html = this.get_item_wise_taxes_html();
// console.log(html);
this.frm.set_value("other_charges_calculation", html);
this.show_item_wise_taxes();
}
},
get_item_wise_taxes_html: function() {
var item_tax = {};
var tax_accounts = [];
var company_currency = this.get_company_currency();
$.each(this.frm.doc["taxes"] || [], function(i, tax) {
var tax_amount_precision = precision("tax_amount", tax);
var tax_rate_precision = precision("rate", tax);
$.each(JSON.parse(tax.item_wise_tax_detail || '{}'),
function(item_code, tax_data) {
if(!item_tax[item_code]) item_tax[item_code] = {};
if($.isArray(tax_data)) {
var tax_rate = "";
if(tax_data[0] != null) {
tax_rate = (tax.charge_type === "Actual") ?
format_currency(flt(tax_data[0], tax_amount_precision),
company_currency, tax_amount_precision) :
(flt(tax_data[0], tax_rate_precision) + "%");
}
var tax_amount = format_currency(flt(tax_data[1], tax_amount_precision),
company_currency, tax_amount_precision);
item_tax[item_code][tax.name] = [tax_rate, tax_amount];
} else {
item_tax[item_code][tax.name] = [flt(tax_data, tax_rate_precision) + "%", "0.00"];
}
});
tax_accounts.push([tax.name, tax.account_head]);
});
var headings = $.map([__("Item Name"), __("Taxable Amount")].concat($.map(tax_accounts,
function(head) { return head[1]; })), function(head) {
if(head==__("Item Name")) {
return '<th style="min-width: 100px;" class="text-left">' + (head || "") + "</th>";
} else {
return '<th style="min-width: 80px;" class="text-right">' + (head || "") + "</th>";
}
}
).join("");
var distinct_item_names = [];
var distinct_items = [];
var taxable_amount = {};
$.each(this.frm.doc["items"] || [], function(i, item) {
var item_code = item.item_code || item.item_name;
if(distinct_item_names.indexOf(item_code)===-1) {
distinct_item_names.push(item_code);
distinct_items.push(item);
taxable_amount[item_code] = item.net_amount;
} else {
taxable_amount[item_code] = taxable_amount[item_code] + item.net_amount;
}
});
var rows = $.map(distinct_items, function(item) {
var item_code = item.item_code || item.item_name;
var item_tax_record = item_tax[item_code];
if(!item_tax_record) { return null; }
return repl("<tr><td>%(item_name)s</td><td class='text-right'>%(taxable_amount)s</td>%(taxes)s</tr>", {
item_name: item.item_name,
taxable_amount: format_currency(taxable_amount[item_code],
company_currency, precision("net_amount", item)),
taxes: $.map(tax_accounts, function(head) {
return item_tax_record[head[0]] ?
"<td class='text-right'>(" + item_tax_record[head[0]][0] + ") " + item_tax_record[head[0]][1] + "</td>" :
"<td></td>";
}).join("")
});
}).join("");
if(!rows) return "";
return '<div class="tax-break-up" style="overflow-x: auto;">\
<table class="table table-bordered table-hover">\
<thead><tr>' + headings + '</tr></thead> \
<tbody>' + rows + '</tbody> \
</table></div>';
}
})

View File

@@ -210,7 +210,6 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
refresh: function() {
erpnext.toggle_naming_series();
erpnext.hide_company();
this.show_item_wise_taxes();
this.set_dynamic_labels();
this.setup_sms();
},
@@ -367,7 +366,6 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
validate: function() {
this.calculate_taxes_and_totals(false);
this.set_item_wise_tax_breakup();
},
company: function() {

View File

@@ -104,7 +104,7 @@ var erpnext_slides = [
options: "", fieldtype: 'Select'
},
{ fieldtype: "Section Break", label: "Financial Year" },
{ fieldtype: "Section Break", label: __('Financial Year') },
{ fieldname: 'fy_start_date', label: __('Start Date'), fieldtype: 'Date', reqd: 1 },
{ fieldtype: "Column Break" },
{ fieldname: 'fy_end_date', label: __('End Date'), fieldtype: 'Date', reqd: 1 },
@@ -216,6 +216,17 @@ var erpnext_slides = [
]
},
{
// Sales Target
name: 'Goals',
domains: ['manufacturing', 'services', 'retail', 'distribution'],
title: __("Set your Target"),
help: __("Set a sales target you'd like to achieve."),
fields: [
{fieldtype:"Currency", fieldname:"sales_target", label:__("Monthly Sales Target")},
]
},
{
// Taxes
name: 'taxes',
@@ -225,7 +236,7 @@ var erpnext_slides = [
help: __("List your tax heads (e.g. VAT, Customs etc; they should have unique names) and their standard rates. This will create a standard template, which you can edit and add more later."),
add_more: 1,
max_count: 3,
mandatory_entry: 1,
mandatory_entry: 0,
fields: [
{fieldtype:"Section Break"},
{fieldtype:"Data", fieldname:"tax", label:__("Tax"),
@@ -297,8 +308,10 @@ var erpnext_slides = [
options:[__("Unit"), __("Nos"), __("Box"), __("Pair"), __("Kg"), __("Set"),
__("Hour"), __("Minute"), __("Litre"), __("Meter"), __("Gram")],
"default": __("Unit"), static: 1},
{fieldtype: "Check", fieldname: "is_sales_item", label:__("We sell this Item"), default: 1, static: 1},
{fieldtype: "Check", fieldname: "is_purchase_item", label:__("We buy this Item"), static: 1},
{fieldtype: "Check", fieldname: "is_sales_item",
label:__("We sell this Item"), default: 1, static: 1},
{fieldtype: "Check", fieldname: "is_purchase_item",
label:__("We buy this Item"), default: 1, static: 1},
{fieldtype:"Column Break"},
{fieldtype:"Currency", fieldname:"item_price", label:__("Rate"), static: 1},
{fieldtype:"Attach Image", fieldname:"item_img", label:__("Attach Image"), is_private: 0, static: 1},

View File

@@ -42,11 +42,15 @@ erpnext.utils.get_party_details = function(frm, method, args, callback) {
callback: function(r) {
if(r.message) {
frm.updating_party_details = true;
frm.set_value(r.message);
frm.updating_party_details = false;
if(callback) callback();
frm.refresh();
erpnext.utils.add_item(frm);
frappe.run_serially([
() => frm.set_value(r.message),
() => {
frm.updating_party_details = false;
if(callback) callback();
frm.refresh();
erpnext.utils.add_item(frm);
}
]);
}
}
});

View File

@@ -66,7 +66,7 @@ erpnext.SerialNoBatchSelector = Class.extend({
fieldtype:'Float',
read_only: 1,
label: __(me.has_batch ? 'Total Qty' : 'Qty'),
default: me.qty
default: 0
},
];
@@ -155,14 +155,10 @@ erpnext.SerialNoBatchSelector = Class.extend({
},
bind_qty: function() {
let serial_no_link = this.dialog.fields_dict.serial_no_select;
let serial_no_list_field = this.dialog.fields_dict.serial_no;
let batches_field = this.dialog.fields_dict.batches;
let qty_field = this.dialog.fields_dict.qty;
let update_quantity = (batch) => {
if(batch) {
if(batches_field) {
batches_field.grid.wrapper.on('change', function() {
let total_qty = 0;
batches_field.grid.wrapper.find(
'input[data-fieldname="selected_qty"]').each(function() {
@@ -170,48 +166,6 @@ erpnext.SerialNoBatchSelector = Class.extend({
total_qty += Number($(this).val());
});
qty_field.set_input(total_qty);
} else {
let serial_numbers = serial_no_list_field.get_value()
.replace(/\n/g, ' ').match(/\S+/g) || [];
qty_field.set_input(serial_numbers.length);
}
};
if(serial_no_link) {
let serial_list = [];
serial_no_link.$input.on('awesomplete-selectcomplete', function() {
if(serial_no_link.get_value().length > 0) {
let new_no = serial_no_link.get_value();
let list_value = serial_no_list_field.get_value();
let new_line = '\n';
if(!serial_no_list_field.get_value()) {
new_line = '';
} else {
serial_list = list_value.replace(/\s+/g, ' ').split(' ');
}
if(!serial_list.includes(new_no)) {
serial_no_link.set_new_description('');
serial_no_list_field.set_value(list_value + new_line + new_no);
update_quantity(0);
} else {
serial_no_link.set_new_description(new_no + ' is already selected.');
}
}
// Should, but doesn't work
serial_no_link.set_input('');
serial_no_link.$input.blur();
});
serial_no_list_field.$input.on('input', function() {
serial_list = serial_no_list_field.get_value().replace(/\s+/g, ' ').split(' ');
update_quantity(0);
});
}
if(batches_field) {
batches_field.grid.wrapper.on('change', function() {
update_quantity(1);
});
}
},
@@ -319,6 +273,7 @@ erpnext.SerialNoBatchSelector = Class.extend({
get_serial_no_fields: function() {
var me = this;
this.serial_list = [];
return [
{fieldtype: 'Section Break', label: __('Serial No')},
{
@@ -326,10 +281,46 @@ erpnext.SerialNoBatchSelector = Class.extend({
label: __('Select'),
get_query: function() {
return { filters: {item_code: me.item_code}};
},
onchange: function(e) {
if(this.in_local_change) return;
this.in_local_change = 1;
let serial_no_list_field = this.layout.fields_dict.serial_no;
let qty_field = this.layout.fields_dict.qty;
let new_number = this.get_value();
let list_value = serial_no_list_field.get_value();
let new_line = '\n';
if(!list_value) {
new_line = '';
} else {
me.serial_list = list_value.replace(/\n/g, ' ').match(/\S+/g) || [];
}
if(!me.serial_list.includes(new_number)) {
this.set_new_description('');
serial_no_list_field.set_value(me.serial_list.join('\n') + new_line + new_number);
me.serial_list = serial_no_list_field.get_value().replace(/\n/g, ' ').match(/\S+/g) || [];
} else {
this.set_new_description(new_number + ' is already selected.');
}
qty_field.set_input(me.serial_list.length);
this.$input.val("");
this.in_local_change = 0;
}
},
{fieldtype: 'Column Break'},
{fieldname: 'serial_no', fieldtype: 'Small Text'}
{
fieldname: 'serial_no',
fieldtype: 'Small Text',
onchange: function() {
me.serial_list = this.get_value()
.replace(/\n/g, ' ').match(/\S+/g) || [];
this.layout.fields_dict.qty.set_input(me.serial_list.length);
}
}
];
}
});

View File

@@ -10,7 +10,7 @@
"state_name": "Uttar Pradesh"
},
{
"state_number": "36",
"state_number": "05",
"state_code": "UT",
"state_name": "Uttarakhand"
},

View File

@@ -80,7 +80,7 @@ def add_print_formats():
def make_custom_fields():
hsn_sac_field = dict(fieldname='gst_hsn_code', label='HSN/SAC',
fieldtype='Data', options='item_code.gst_hsn_code', insert_after='description')
fieldtype='Data', options='item_code.gst_hsn_code', insert_after='description', print_hide=1)
custom_fields = {
'Address': [
@@ -107,13 +107,15 @@ def make_custom_fields():
fieldtype='Data', insert_after='company_address',
options='company_address.gstin', print_hide=1),
dict(fieldname='invoice_copy', label='Invoice Copy',
fieldtype='Select', insert_after='project', print_hide=1, allow_on_submit=1,
options='ORIGINAL FOR RECIPIENT\nDUPLICATE FOR TRANSPORTER\nDUPLICATE FOR SUPPLIER\nTRIPLICATE FOR SUPPLIER')
fieldtype='Select', insert_after='select_print_heading', print_hide=1, allow_on_submit=1,
options='Original for Recipient\nDuplicate for Transporter\nDuplicate for Supplier\nTriplicate for Supplier')
],
'Item': [
dict(fieldname='gst_hsn_code', label='HSN/SAC',
fieldtype='Link', options='GST HSN Code', insert_after='item_group'),
],
'Quotation Item': [hsn_sac_field],
'Supplier Quotation Item': [hsn_sac_field],
'Sales Order Item': [hsn_sac_field],
'Delivery Note Item': [hsn_sac_field],
'Sales Invoice Item': [hsn_sac_field],
@@ -131,7 +133,7 @@ def make_custom_fields():
custom_field = frappe.get_doc("Custom Field", field)
custom_field.update(df)
custom_field.save()
def make_fixtures():
docs = [
{'doctype': 'Salary Component', 'salary_component': 'Professional Tax', 'description': 'Professional Tax', 'type': 'Deduction'},

View File

@@ -1,6 +1,7 @@
import frappe, re
from frappe import _
from erpnext.regional.india import states, state_numbers
from erpnext.controllers.taxes_and_totals import get_itemised_tax, get_itemised_taxable_amount
def validate_gstin_for_india(doc, method):
if not hasattr(doc, 'gstin'):
@@ -22,3 +23,44 @@ def validate_gstin_for_india(doc, method):
if doc.gstin != "NA" and doc.gst_state_number != doc.gstin[:2]:
frappe.throw(_("First 2 digits of GSTIN should match with State number {0}")
.format(doc.gst_state_number))
def get_itemised_tax_breakup_header(item_doctype, tax_accounts):
if frappe.get_meta(item_doctype).has_field('gst_hsn_code'):
return [_("HSN/SAC"), _("Taxable Amount")] + tax_accounts
else:
return [_("Item"), _("Taxable Amount")] + tax_accounts
def get_itemised_tax_breakup_data(doc):
itemised_tax = get_itemised_tax(doc.taxes)
itemised_taxable_amount = get_itemised_taxable_amount(doc.items)
if not frappe.get_meta(doc.doctype + " Item").has_field('gst_hsn_code'):
return itemised_tax, itemised_taxable_amount
item_hsn_map = frappe._dict()
for d in doc.items:
item_hsn_map.setdefault(d.item_code or d.item_name, d.get("gst_hsn_code"))
hsn_tax = {}
for item, taxes in itemised_tax.items():
hsn_code = item_hsn_map.get(item)
hsn_tax.setdefault(hsn_code, frappe._dict())
for tax_account, tax_detail in taxes.items():
hsn_tax[hsn_code].setdefault(tax_account, {"tax_rate": 0, "tax_amount": 0})
hsn_tax[hsn_code][tax_account]["tax_rate"] = tax_detail.get("tax_rate")
hsn_tax[hsn_code][tax_account]["tax_amount"] += tax_detail.get("tax_amount")
# set taxable amount
hsn_taxable_amount = frappe._dict()
for item, taxable_amount in itemised_taxable_amount.items():
hsn_code = item_hsn_map.get(item)
hsn_taxable_amount.setdefault(hsn_code, 0)
hsn_taxable_amount[hsn_code] += itemised_taxable_amount.get(item)
return hsn_tax, hsn_taxable_amount
# don't remove this function it is used in tests
def test_method():
'''test function'''
return 'overridden'

View File

@@ -25,7 +25,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": "Academic Year",
"length": 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": "Program",
"length": 0,
@@ -269,7 +269,7 @@
"issingle": 1,
"istable": 0,
"max_attachments": 0,
"modified": "2017-05-15 12:43:32.317942",
"modified": "2017-07-17 21:57:35.602091",
"modified_by": "Administrator",
"module": "Schools",
"name": "Student Group Creation Tool",
@@ -300,6 +300,7 @@
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Education",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",

View File

@@ -1,346 +1,357 @@
{
"allow_copy": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "SLA.######",
"beta": 0,
"creation": "2016-11-28 15:38:54.793854",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "SLA.######",
"beta": 0,
"creation": "2016-11-28 15:38:54.793854",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "student",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 1,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Student",
"length": 0,
"no_copy": 0,
"options": "Student",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "student",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 1,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Student",
"length": 0,
"no_copy": 0,
"options": "Student",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "student_name",
"fieldtype": "Read Only",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 1,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Student Name",
"length": 0,
"no_copy": 0,
"options": "student.title",
"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,
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "student_name",
"fieldtype": "Read Only",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 1,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Student Name",
"length": 0,
"no_copy": 0,
"options": "student.title",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_3",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "",
"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,
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_3",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "",
"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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "from_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "From 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": 1,
"search_index": 0,
"set_only_once": 0,
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "from_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "From 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": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "to_date",
"fieldtype": "Date",
"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": "To 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": 1,
"search_index": 0,
"set_only_once": 0,
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "to_date",
"fieldtype": "Date",
"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": "To 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": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "Will show the student as Present in Student Monthly Attendance Report",
"fieldname": "mark_as_present",
"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": "Mark as Present",
"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,
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"description": "Will show the student as Present in Student Monthly Attendance Report",
"fieldname": "mark_as_present",
"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": "Mark as Present",
"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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_5",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"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,
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_5",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "reason",
"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": "Reason",
"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,
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "reason",
"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": "Reason",
"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_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "amended_from",
"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": "Amended From",
"length": 0,
"no_copy": 1,
"options": "Student Leave Application",
"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,
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "amended_from",
"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": "Amended From",
"length": 0,
"no_copy": 1,
"options": "Student Leave Application",
"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
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-02-20 13:21:08.828872",
"modified_by": "Administrator",
"module": "Schools",
"name": "Student Leave Application",
"name_case": "",
"owner": "Administrator",
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-07-17 21:57:57.804413",
"modified_by": "Administrator",
"module": "Schools",
"name": "Student Leave Application",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 1,
"apply_user_permissions": 0,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Instructor",
"set_user_permissions": 0,
"share": 0,
"submit": 1,
"amend": 1,
"apply_user_permissions": 0,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 0,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Instructor",
"set_user_permissions": 0,
"share": 0,
"submit": 1,
"write": 1
},
},
{
"amend": 1,
"apply_user_permissions": 0,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Academics User",
"set_user_permissions": 0,
"share": 1,
"submit": 1,
"amend": 1,
"apply_user_permissions": 0,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Academics User",
"set_user_permissions": 0,
"share": 1,
"submit": 1,
"write": 1
}
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 1,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "student_name",
"track_changes": 0,
],
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Education",
"show_name_in_global_search": 1,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "student_name",
"track_changes": 0,
"track_seen": 0
}

View File

@@ -1,399 +1,400 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "SLog.####",
"beta": 0,
"creation": "2016-07-29 03:27:22.451772",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "SLog.####",
"beta": 0,
"creation": "2016-07-29 03:27:22.451772",
"custom": 0,
"docstatus": 0,
"doctype": "DocType",
"document_type": "",
"editable_grid": 1,
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "student",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 1,
"label": "Student",
"length": 0,
"no_copy": 0,
"options": "Student",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "student",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 1,
"label": "Student",
"length": 0,
"no_copy": 0,
"options": "Student",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "student_name",
"fieldtype": "Read Only",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 1,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Student Name",
"length": 0,
"no_copy": 0,
"options": "student.title",
"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,
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "student_name",
"fieldtype": "Read Only",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 1,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Student Name",
"length": 0,
"no_copy": 0,
"options": "student.title",
"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": "type",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Type",
"length": 0,
"no_copy": 0,
"options": "General\nAcademic\nMedical\nAchievement",
"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,
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "type",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Type",
"length": 0,
"no_copy": 0,
"options": "General\nAcademic\nMedical\nAchievement",
"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": "date",
"fieldtype": "Date",
"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": "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,
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "date",
"fieldtype": "Date",
"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": "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,
"columns": 0,
"fieldname": "column_break_3",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"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,
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_3",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"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": "academic_year",
"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": "Academic Year",
"length": 0,
"no_copy": 0,
"options": "Academic Year",
"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,
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "academic_year",
"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": "Academic Year",
"length": 0,
"no_copy": 0,
"options": "Academic Year",
"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": "academic_term",
"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": "Academic Term",
"length": 0,
"no_copy": 0,
"options": "Academic Term",
"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,
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "academic_term",
"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": "Academic Term",
"length": 0,
"no_copy": 0,
"options": "Academic Term",
"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": "program",
"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": "Program",
"length": 0,
"no_copy": 0,
"options": "Program",
"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,
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "program",
"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": "Program",
"length": 0,
"no_copy": 0,
"options": "Program",
"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": "student_batch",
"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": "Student Batch",
"length": 0,
"no_copy": 0,
"options": "Student Batch Name",
"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,
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "student_batch",
"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": "Student Batch",
"length": 0,
"no_copy": 0,
"options": "Student Batch Name",
"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": "section_break_5",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"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,
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_5",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"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": "log",
"fieldtype": "Text Editor",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 1,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Log",
"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,
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "log",
"fieldtype": "Text Editor",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 1,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Log",
"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,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-07-06 12:42:05.777673",
"modified_by": "Administrator",
"module": "Schools",
"name": "Student Log",
"name_case": "",
"owner": "Administrator",
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-07-17 21:57:11.024049",
"modified_by": "Administrator",
"module": "Schools",
"name": "Student Log",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Academics User",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"if_owner": 0,
"import": 0,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Academics User",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "student_name",
"track_changes": 0,
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0,
"restrict_to_domain": "Education",
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "student_name",
"track_changes": 0,
"track_seen": 1
}

View File

@@ -105,6 +105,10 @@ erpnext.selling.QuotationController = erpnext.selling.SellingController.extend({
lead: function() {
var me = this;
if(!this.frm.doc.lead) {
return;
}
frappe.call({
method: "erpnext.crm.doctype.lead.lead.get_lead_details",
args: {

View File

@@ -1440,6 +1440,36 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
"columns": 0,
"fieldname": "sec_tax_breakup",
"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": "Tax Breakup",
"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,
@@ -1447,7 +1477,7 @@
"collapsible": 0,
"columns": 0,
"fieldname": "other_charges_calculation",
"fieldtype": "HTML",
"fieldtype": "Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@@ -1457,12 +1487,12 @@
"in_standard_filter": 0,
"label": "Taxes and Charges Calculation",
"length": 0,
"no_copy": 0,
"no_copy": 1,
"oldfieldtype": "HTML",
"permlevel": 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,
@@ -2542,7 +2572,7 @@
"istable": 0,
"max_attachments": 1,
"menu_index": 0,
"modified": "2017-06-13 14:29:18.553722",
"modified": "2017-07-19 13:49:33.388736",
"modified_by": "Administrator",
"module": "Selling",
"name": "Quotation",

View File

@@ -93,7 +93,7 @@ def get_list_context(context=None):
'show_sidebar': True,
'show_search': True,
'no_breadcrumbs': True,
'title': _('Quotes'),
'title': _('Quotations'),
})
return list_context

View File

@@ -0,0 +1,53 @@
QUnit.test("test: quotation", function (assert) {
assert.expect(10);
let done = assert.async();
frappe.run_serially([
() => {
return frappe.tests.make("Quotation", [
{customer: "Test Customer 1"},
{items: [
[
{"item_code": "Test Product 1"},
{"qty": 5}
]]
}
]);
},
() => {
// get_item_details
assert.ok(cur_frm.doc.items[0].item_name == "Test Product 1", "Added Test Product 1");
// calculate_taxes_and_totals
assert.ok(cur_frm.doc.grand_total === 500, "Total Amount is correct");
},
() => cur_frm.set_value("customer_address", "Test1-Billing"),
() => cur_frm.set_value("shipping_address_name", "Test1-Warehouse"),
() => cur_frm.set_value("contact_person", "Contact 1-Test Customer 1"),
() => cur_frm.set_value("currency", "USD"),
() => frappe.timeout(0.3),
() => cur_frm.set_value("selling_price_list", "Test-Selling-USD"),
() => frappe.timeout(0.5),
() => cur_frm.doc.items[0].rate = 200,
() => frappe.timeout(0.3),
() => cur_frm.set_value("tc_name", "Test Term 1"),
() => frappe.timeout(0.3),
() => cur_frm.save(),
() => {
// Check Address and Contact Info
assert.ok(cur_frm.doc.address_display.includes("Billing Street 1"), "Address Changed");
assert.ok(cur_frm.doc.shipping_address.includes("Warehouse Street 1"), "Address Changed");
assert.ok(cur_frm.doc.contact_display == "Contact 1", "Contact info changed");
// Check Currency
assert.ok(cur_frm.doc_currency == "USD", "Currency Changed");
assert.ok(cur_frm.doc.selling_price_list == "Test-Selling-USD", "Price List Changed");
assert.ok(cur_frm.doc.items[0].rate == 200, "Price Changed Manually");
assert.equal(cur_frm.doc.total, 1000, "New Total Calculated");
// Check Terms and Condtions
assert.ok(cur_frm.doc.tc_name == "Test Term 1", "Terms and Conditions Checked");
},
() => done()
]);
});

View File

@@ -27,7 +27,8 @@ class TestQuotation(unittest.TestCase):
self.assertEquals(sales_order.get("items")[0].prevdoc_docname, quotation.name)
self.assertEquals(sales_order.customer, "_Test Customer")
sales_order.delivery_date = "2014-01-01"
for d in sales_order.get("items"):
d.delivery_date = "2014-01-01"
sales_order.naming_series = "_T-Quotation-"
sales_order.transaction_date = "2013-05-12"
sales_order.insert()
@@ -51,9 +52,11 @@ class TestQuotation(unittest.TestCase):
quotation.submit()
sales_order = make_sales_order(quotation.name)
sales_order.delivery_date = "2016-01-02"
sales_order.naming_series = "_T-Quotation-"
sales_order.transaction_date = "2016-01-01"
for d in sales_order.get("items"):
d.delivery_date = "2016-01-02"
sales_order.insert()
self.assertEquals(quotation.get("items")[0].rate, rate_with_margin)
@@ -86,4 +89,4 @@ def get_quotation_dict(customer=None, item_code=None):
'rate': 100
}
]
}
}

View File

@@ -33,7 +33,13 @@ frappe.ui.form.on("Sales Order", {
function(doc) { return (doc.stock_qty<=doc.delivered_qty) ? "green" : "orange" })
erpnext.queries.setup_warehouse_query(frm);
}
},
});
frappe.ui.form.on("Sales Order Item", {
delivery_date: function(frm, cdt, cdn) {
erpnext.utils.copy_value_in_all_row(frm.doc, cdt, cdn, "items", "delivery_date");
}
});
erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend({
@@ -77,7 +83,7 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
// delivery note
if(flt(doc.per_delivered, 2) < 100 && ["Sales", "Shopping Cart"].indexOf(doc.order_type)!==-1 && allow_delivery) {
this.frm.add_custom_button(__('Delivery'),
function() { me.make_delivery_note() }, __("Make"));
function() { me.make_delivery_note_based_on_delivery_note(); }, __("Make"));
this.frm.add_custom_button(__('Production Order'),
function() { me.make_production_order() }, __("Make"));
@@ -235,7 +241,7 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
},
order_type: function() {
this.frm.toggle_reqd("delivery_date", this.frm.doc.order_type == "Sales");
this.frm.fields_dict.items.grid.toggle_reqd("delivery_date", this.frm.doc.order_type == "Sales");
},
tc_name: function() {
@@ -249,10 +255,72 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
})
},
make_delivery_note_based_on_delivery_note: function() {
var me = this;
var delivery_dates = [];
$.each(this.frm.doc.items || [], function(i, d) {
if(!delivery_dates.includes(d.delivery_date)) {
delivery_dates.push(d.delivery_date);
}
});
var item_grid = this.frm.fields_dict["items"].grid;
if(!item_grid.get_selected().length && delivery_dates.length > 1) {
var dialog = new frappe.ui.Dialog({
title: __("Select Items based on Delivery Date"),
fields: [{fieldtype: "HTML", fieldname: "dates_html"}]
});
var html = $(`
<div style="border: 1px solid #d1d8dd">
<div class="list-item list-item--head">
<div class="list-item__content list-item__content--flex-2">
${__('Delivery Date')}
</div>
</div>
${delivery_dates.map(date => `
<div class="list-item">
<div class="list-item__content list-item__content--flex-2">
<label>
<input type="checkbox" data-date="${date}" checked="checked"/>
${frappe.datetime.str_to_user(date)}
</label>
</div>
</div>
`).join("")}
</div>
`);
var wrapper = dialog.fields_dict.dates_html.$wrapper;
wrapper.html(html);
dialog.set_primary_action(__("Select"), function() {
var dates = wrapper.find('input[type=checkbox]:checked')
.map((i, el) => $(el).attr('data-date')).toArray();
if(!dates) return;
$.each(dates, function(i, d) {
$.each(item_grid.grid_rows || [], function(j, row) {
if(row.doc.delivery_date == d) {
row.doc.__checked = 1;
}
});
})
me.make_delivery_note();
dialog.hide();
});
dialog.show();
} else {
this.make_delivery_note();
}
},
make_delivery_note: function() {
frappe.model.open_mapped_doc({
method: "erpnext.selling.doctype.sales_order.sales_order.make_delivery_note",
frm: this.frm
frm: me.frm
})
},
@@ -344,6 +412,11 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
if(cint(frappe.boot.notification_settings.sales_order)) {
this.frm.email_doc(frappe.boot.notification_settings.sales_order_message);
}
},
items_add: function(doc, cdt, cdn) {
var row = frappe.get_doc(cdt, cdn);
this.frm.script_manager.copy_from_first_row("items", row, ["delivery_date"]);
}
});

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