Compare commits

..

124 Commits

Author SHA1 Message Date
Saurabh
9a7838265d Merge branch 'hotfix' 2018-08-10 17:07:01 +05:30
Saurabh
57d3c93e6b bumped to version 10.1.47 2018-08-10 17:37:01 +06:00
rohitwaghchaure
a0b846fb88 [Minor] Added condition for get_bank_cash_account (#15112) 2018-08-09 15:51:38 +05:30
rohitwaghchaure
8356d4b892 [Fix] disable_rounded_total not found error (#15113) 2018-08-09 15:51:11 +05:30
rohitwaghchaure
ba62013cbb [Fix] Not able to delete the task even if task removed from the project (#15105)
* [Fix] Not able to delete the task even if task removed from the project

* [Fix] Custom field's data in task not updating from the project
2018-08-09 15:50:23 +05:30
rohitwaghchaure
59de1dae6a [Fix] Math domain error (#15107) 2018-08-09 10:58:37 +05:30
Saurabh
91cecf8eac Merge branch 'hotfix' 2018-08-07 12:55:51 +05:30
Saurabh
6ec558bd59 bumped to version 10.1.46 2018-08-07 13:25:51 +06:00
Shreya Shah
11dd3ffdb5 Force delete item on deletion of Lab test template (#15079) 2018-08-06 14:43:00 +05:30
Shreya Shah
f0ef673498 Accounts receivable aging based on supplier due date (#15080)
* Accounts receivable aging based on supplier due date (#13801)

* Remove spaces
2018-08-06 14:42:35 +05:30
rohitwaghchaure
57d07d6b8e Merge pull request #15097 from rohitwaghchaure/incorrect_tax_amount_in_report
[Fix] Incorrect tax amount calculation because of same fieldname in the code
2018-08-06 14:32:44 +05:30
rohitwaghchaure
fc682c86c6 [Fix] Inactive student validation issue while disabling the student group (#15095) 2018-08-06 14:29:46 +05:30
Rohit Waghchaure
15f52e7b71 [Fix] Incorrect tax amount calculation because of same fieldname in the code 2018-08-06 14:29:34 +05:30
Ameya Shenoy
26bfede116 Merge pull request #15078 from rohitwaghchaure/task_email_alert_not_working_from_project
Fix] Task's email alert on status change not working if we change the task status from the project form
2018-08-06 12:33:07 +05:30
Rohit Waghchaure
86217ca96c [Fix] Task email alert on status change not working if we change the task status from the project form 2018-08-02 16:41:53 +05:30
rohitwaghchaure
ae4ff5a403 [Fix] In words showing grand total instead of rounded total (#15065) 2018-08-01 18:09:51 +05:30
rohitwaghchaure
0452b405f4 [Fix] Not able to submit the expense claim (#15057) 2018-08-01 17:45:51 +05:30
rohitwaghchaure
dc7004e083 [Fix] Patient Appointment Calendar not showing data properly (#15052) 2018-07-31 16:01:02 +05:30
Nabin Hait
ebdefade9b Merge branch 'hotfix' 2018-07-31 12:18:23 +05:30
Nabin Hait
e9afbfde76 bumped to version 10.1.45 2018-07-31 12:48:22 +06:00
rohitwaghchaure
086da451ca Removed academic term from validation to check duplicate program enrollement (#15046) 2018-07-30 19:11:14 +05:30
rohitwaghchaure
91d2ace9bb Account no + account name in trial balance and financial statements reports (#15038) 2018-07-30 10:38:51 +05:30
Shreya Shah
91a9ee5179 Merge accounts functionality (#14993)
* Add dialog to merge accounts

* Add conditions and merge functionality

* Fix travis

* Add test case

* Add more test scenarios
2018-07-28 10:26:11 +05:30
Nabin Hait
11137850cb Merge branch 'hotfix' 2018-07-27 12:35:42 +05:30
Nabin Hait
584153a1b1 bumped to version 10.1.44 2018-07-27 13:05:42 +06:00
Shreya Shah
eed0a4e2df Do not copy remarks field while duplicating (#15023) 2018-07-27 11:26:35 +05:30
FinByz Tech Pvt. Ltd
f5cc1bd44c Created Eway Bill Report (#14926)
* Created Eway Bill Report

* Field value changes

* Changed module from Stock to Regional

* Added comments and minor fix

* minor fix
2018-07-27 11:21:42 +05:30
Yaqin Shurrab
cc56ff5c37 Get the correct Summation of Closing Balances (#14990)
Fix total raw values for Closing Credit and Closing Debit
2018-07-27 11:13:00 +05:30
rohitwaghchaure
20d3bef3c3 [Fix] Deducted tax amount adding up in the total amount in the GST Itemised Purchase Register report (#14994) 2018-07-27 11:03:53 +05:30
Shreya Shah
1f4c263a63 [minor] Program Enrollment Tool (#15013)
* Make academic term field mandatory

* Add disabled field to Student Group

* Mandatory check in Education Settings for Academic Term

* Fix as per review
2018-07-27 10:59:14 +05:30
Nabin Hait
d0a2b1619b Optimization for marking student attendance 2018-07-27 10:51:55 +05:30
rohitwaghchaure
0fe6ced99d [Fix] Valuation rate for serialized items (#15017) 2018-07-27 10:33:30 +05:30
Nabin Hait
e50e0e4b30 Update repost_reserved_qty_for_production.py 2018-07-25 17:49:47 +05:30
Shreya Shah
cd717e422f Modify renaming process of Account (#14839)
* Disable renaming for Account Master

* Update account name along with account number

* Fix as per suggestions

* Update account.js

* Fix typo

* Remove after_rename and before_rename methods

* Modify test case

* Modify field placement

* Remove unused method
2018-07-23 14:15:20 +05:30
rohitwaghchaure
6b862fcae5 [Fix] Rounded total is not working in the purchase receipt (#14984) 2018-07-23 13:02:07 +05:30
Zarrar
a22e0d0c99 return empty if no stock ledger entry found (#14968) 2018-07-23 11:31:04 +05:30
Saurabh
8ca5940f76 Merge branch 'hotfix' 2018-07-18 16:24:54 +05:30
Saurabh
6c46b4edc4 bumped to version 10.1.43 2018-07-18 16:54:54 +06:00
Shreya Shah
8395a7ad6f Modifications in last purchase rate button (#14948)
* Make last purchase rate button visible until submitted

* Move buttons

- Get last purchase rate and link to material request buttons moved to toolbar under Tools button

* Remove disable fetch last rate functionality

* Make last purchase rate field read_only
2018-07-18 12:27:27 +05:30
rohitwaghchaure
6863b03790 Naming series increase even if there is an exception while saving the invoice from the backend (#14938) 2018-07-17 17:20:15 +05:30
rohitwaghchaure
a76067eea1 [Fix] Per billed showing as 100% in the sales order even if sales return has made against the sales order (#14899)
* [Fix] Per billed showing as 100% in the sales order even if sales return has made against the sales order

* Added test cases
2018-07-17 16:50:50 +05:30
rohitwaghchaure
216f9373c9 Provision to select the cost center in the asset (#14934) 2018-07-17 16:47:51 +05:30
rohitwaghchaure
5fe88ea93e [Fix] Not able to update the current value of the prefixes which are defined in the autoname field (#14918) 2018-07-17 12:45:25 +05:30
Shreya Shah
fbeab5bf73 Show opening balance of leaves (#14925)
* Show opening balance of leaves

* Update employee_leave_balance.py
2018-07-17 11:59:51 +05:30
rohitwaghchaure
a3e070bc6b [Fix] Invoices not showing in the gross profit report against which sales return entry has created (#14914) 2018-07-16 18:11:19 +05:30
rohitwaghchaure
3ffe89659a [Fix] System always fetches the payment terms template from the company even if it's removed from the sales invoice (#14879) 2018-07-13 17:40:48 +05:30
Ameya Shenoy
5c3b69476d shift to self-hosted frankfurter api (#14792) 2018-07-13 12:46:07 +05:30
Nabin Hait
da17ceea73 patch fixes 2018-07-13 12:04:06 +05:30
rohitwaghchaure
2d84945839 [Fix] Stock reconciliation wrong difference amount calculation (#14893) 2018-07-13 11:07:27 +05:30
Shreya Shah
8dc4c945dc Fix UX (#14862) 2018-07-11 14:36:49 +05:30
rohitwaghchaure
cc9c7d5e2a [Fix] GSTIN not displaying in the address print (#14855)
* [Fix] GSTIN not displaying in the address print

* Made changes in the patch and set Patch as False

* Update update_address_template_for_india.py
2018-07-10 15:41:42 +05:30
rohitwaghchaure
07d0e9a2b2 Don't show disabled items in the stock balance report (#14851) 2018-07-09 16:56:41 +05:30
Saurabh
f38d6d9c44 Merge branch 'hotfix' 2018-07-09 15:53:50 +05:30
Saurabh
07bb1811af bumped to version 10.1.42 2018-07-09 16:23:50 +06:00
rohitwaghchaure
f07462860b Merge pull request #14846 from shreyashah115/fix-digest
Filter out pending orders based on company
2018-07-09 14:48:13 +05:30
rohitwaghchaure
720b06495c [Fix] Not able to save project (#14848) 2018-07-09 13:57:16 +05:30
Shreya
83ad646af4 Filter out pending orders based on company 2018-07-09 12:14:03 +05:30
rohitwaghchaure
8179bcbabd [Fix] Code cleanup (#14843) 2018-07-09 09:29:40 +05:30
rohitwaghchaure
6be8bea934 Provision to view return entries in Sales Person-wise Transaction Summary (#14830) 2018-07-08 19:25:13 +05:30
Saurabh
0b9129e519 Merge branch 'hotfix' 2018-07-06 13:02:58 +05:30
Saurabh
778e06d0ee bumped to version 10.1.41 2018-07-06 13:32:57 +06:00
rohitwaghchaure
3f5e80ebe3 Allow bulk edit for the sales invoice item (#14826) 2018-07-06 12:39:04 +05:30
rohitwaghchaure
ea943c6fe4 [Fix] Due date validate issue in the POS (#14825) 2018-07-06 12:28:58 +05:30
rohitwaghchaure
82a9fde921 Added compact print option for the material request (#14799) 2018-07-06 10:40:51 +05:30
Saurabh
b35721029b Merge pull request #14820 from saurabh6790/fix_task_status
[fix] Add Overdue status in Project Task
2018-07-06 05:54:47 +05:30
Saurabh
f403031449 [fix] Add Overdue status in Project Task 2018-07-05 19:38:59 +05:30
rohitwaghchaure
25f36489b3 Merge pull request #14805 from rohitwaghchaure/pos_ignore_pricing_issue_online
[Fix] POS Profile ignore pricing rule not working for online pos
2018-07-04 16:26:48 +05:30
Rohit Waghchaure
5005e467a0 [Fix] POS Profile ignore pricing rule not working for online pos 2018-07-04 12:37:38 +05:30
Zarrar
68a148e293 half_day enable/disable check (#14795) 2018-07-03 17:00:38 +05:30
rohitwaghchaure
205eae322b [Fix] BOM replace timeout issue (#14782) 2018-07-03 15:06:38 +05:30
Ameya Shenoy
935f4a474b Prevent creation of more than 500 item variants (#14790) 2018-07-03 10:48:59 +05:30
Shreya Shah
aa62a62bb0 Add VAT related fields after printing settings fields (#14773) 2018-07-03 10:28:20 +05:30
Nabin Hait
104f62090d Add filters for cost center in Budget Variance report 2018-06-29 12:25:03 +05:30
Manas Solanki
3ff7f79cc7 Merge pull request #14740 from manassolanki/fix-20
[minor] fix for the args in the frm.call
2018-06-28 20:47:25 +05:30
Manas Solanki
89e7f8b387 [minor] fix for the args in the frm.call 2018-06-28 20:44:53 +05:30
Shreya Shah
2fbc8509f5 Fix error message (#14716) 2018-06-28 14:14:28 +05:30
rohitwaghchaure
67d87e29e3 [Fix] Wrong rate calculation in Itemwise sales register report for multi uom (#14690) 2018-06-27 11:22:03 +05:30
Deepak kumar
55cf6c5f7e Membership (#14688)
* Correct the label and field name

* Update membership.json
2018-06-27 09:17:33 +05:30
rohitwaghchaure
f3cf15fa0d [Fix] While saving project getting timeout error (#14672) 2018-06-26 17:06:00 +05:30
Prateeksha Singh
d53d2ed791 Merge pull request #14679 from pratu16x7/hotfix
[fix] yet another python attribute fetch
2018-06-26 12:20:26 +05:30
Prateeksha Singh
34aeefc0a1 [fix] yet another python attribute fetch 2018-06-26 12:18:29 +05:30
Shreya Shah
d8031d1a53 Clear child table before appending (#14648) 2018-06-25 10:34:55 +05:30
Ameya Shenoy
1098755d26 Merge branch 'hotfix' 2018-06-22 05:10:27 +00:00
Ameya Shenoy
774f676a25 bumped to version 10.1.40 2018-06-22 05:10:27 +00:00
Ameya Shenoy
1629393618 Merge pull request #14628 from Zlash65/fix-pe
Expense Claim - Payment Entry related fix
2018-06-21 18:49:50 +05:30
Zlash65
4cf46edabc expense_claim related fix in payment entry
outstanding amount calculation fix, manual entry of expense trigger calculation of allocation added
2018-06-21 15:18:13 +05:30
Saurabh
e6498996fa Merge branch 'hotfix' 2018-06-20 15:21:40 +05:30
Saurabh
98b7bb5679 bumped to version 10.1.39 2018-06-20 15:51:40 +06:00
Zarrar
99017b173d naming for asset maintenance task should be task name (#14607) 2018-06-20 14:53:13 +05:30
Zarrar
42f2d1a7a8 fix incase no customer fetched (#14596) 2018-06-20 10:50:11 +05:30
Nabin Hait
a125a6199c Allowed paid amount against debit/credit note 2018-06-19 12:07:02 +05:30
Zarrar
ba1cdc6c57 fix opening invoice related authorization control - item=None (#14567) 2018-06-19 10:45:47 +05:30
Nabin Hait
875c941f3d fixes in setting itemised tax breakup 2018-06-18 17:41:27 +05:30
Nabin Hait
8eeb5694ba Fixed qty in item-wise sales history report 2018-06-18 17:41:27 +05:30
Nabin Hait
0bef91c241 minor fix in item type validation 2018-06-18 17:41:27 +05:30
Nabin Hait
ae02209b6a Removed autoname of child table 2018-06-18 17:41:27 +05:30
Zarrar
e0adb6bbd6 cost_center overridden by company's default fix in SI (#14560) 2018-06-18 15:49:12 +05:30
Manas Solanki
efa191c72e [minor fix] change the unicode/str to the datetime obejct (#14534) 2018-06-15 18:04:12 +05:30
Ameya Shenoy
40fe3a2cdf Merge branch 'hotfix' 2018-06-15 09:29:56 +00:00
Ameya Shenoy
ed77429496 bumped to version 10.1.38 2018-06-15 09:29:56 +00:00
Manas Solanki
c7afbea708 Update bom.py (#14524) 2018-06-14 17:30:26 +05:30
Shreya Shah
dd8fba6783 Do not check for batch on cancellation (#14506) 2018-06-14 11:49:45 +05:30
Saurabh
8e74df09a4 [fix] On deletion of salary slip decrease the series count (#14508) 2018-06-14 11:47:21 +05:30
Shreya Shah
3d73a6f7f2 Fetch details from Journal Entry in Accounts Payable report (#14495)
* Fetch bill_no, bill_date from Journal Entry

* Fix as per suggestion

* Fetch details only if bill_no exists
2018-06-13 16:33:06 +05:30
FinByz Tech Pvt. Ltd
46c8ccc6a8 Fix conflict for taxable value having same item in GSTR-1 Report (#14492)
* Update gstr_1.py

Merge conflict for taxable value having same item

* Update gstr_1.py

* Minor Fix

* Update gstr_1.py
2018-06-13 15:34:17 +05:30
Prateeksha Singh
5d0674a23a Merge pull request #14452 from pratu16x7/hotfix
[hotfix][enhance][tax-breakup] store itemized tax by item_name when possible
2018-06-12 17:42:01 +05:30
Prateeksha Singh
a831868d02 Merge pull request #14460 from chdecultot/hotfix_customer
Customer timeline data hotfix
2018-06-11 16:31:17 +05:30
Charles-Henri Decultot
943dc09f29 Customer timeline data hotfix 2018-06-11 09:42:05 +00:00
Prateeksha Singh
ea7533fa75 [tax-breakup] store itemized tax by item_name when can 2018-06-11 13:31:33 +05:30
Shreya Shah
b9399d8781 Cancel attendance if leave application is cancelled (#14372)
* Cancel attendance if leave application is cancelled

* Fix as per review
2018-06-08 11:24:08 +05:30
Zarrar
8cd0f67b25 query fix if name contains apostrophe (#14370) 2018-06-07 16:48:31 +05:30
Zarrar
32a118d68c fix precision in stock entry (#14366) 2018-06-06 15:06:52 +05:30
Ameya Shenoy
a02d5d1104 Merge branch 'hotfix' 2018-06-06 05:41:37 +00:00
Ameya Shenoy
36cd433999 bumped to version 10.1.37 2018-06-06 05:41:37 +00:00
Shreya Shah
6ff3a094e2 Fix currency exchange tests (#14356) 2018-06-05 14:16:10 +05:30
rohitwaghchaure
50d8c4a10f [Fix] Not able to save purchase invoice, due to invalid condition (#14355) 2018-06-05 13:08:10 +05:30
Zarrar
7c088ff623 dont validate items if item_code not found (#14344)
item_code field is not set when created using invoice creation tool
2018-06-05 10:32:09 +05:30
rohitwaghchaure
7cf01e7c75 Replaced fixer api with exchangeratesapi (#14347) 2018-06-05 10:30:20 +05:30
Zarrar
fc03a0463c [Fix] Validate items for saleable, purchaseable or subcontractable in transactions (#14316)
* validate items to see if they are saleable

* check if items are subcontractable or saleable

* improvise name,error message and code
Validate type function validates item if they are of proper type for that transaction and throws descriptive error.
2018-06-04 12:52:52 +05:30
rohitwaghchaure
5ecc26bf15 [Fix] get_shipping_address() takes at least 1 argument (0 given) (#14328) 2018-06-04 11:00:24 +05:30
rohitwaghchaure
c1cf495a93 [Fix] Special character issue (#14322) 2018-06-03 16:00:31 +05:30
rohitwaghchaure
ce642d73cb [Fix] Special character issue while upadting customer group in customer (#14312) 2018-06-01 15:11:21 +05:30
rohitwaghchaure
2fa057e4ad [Fix] Expense claim showing status as Unpaid even if Total Advance Amount and Total Sanctioned Amount is same (#14311) 2018-06-01 15:10:55 +05:30
Zarrar
7e0e85912b item_name not fetched in packing slip (#14310) 2018-06-01 15:10:33 +05:30
113 changed files with 2045 additions and 902 deletions

View File

@@ -5,7 +5,7 @@ import frappe
from erpnext.hooks import regional_overrides
from frappe.utils import getdate
__version__ = '10.1.36'
__version__ = '10.1.47'
def get_default_company(user=None):
'''Get default company for user'''

View File

@@ -49,10 +49,16 @@ frappe.ui.form.on('Account', {
}
if(!frm.doc.__islocal) {
frm.add_custom_button(__('Update Account Number'), function () {
frm.add_custom_button(__('Update Account Name / Number'), function () {
frm.trigger("update_account_number");
});
}
if(!frm.doc.__islocal) {
frm.add_custom_button(__('Merge Account'), function () {
frm.trigger("merge_account");
});
}
},
account_type: function (frm) {
if (frm.doc.is_group == 0) {
@@ -98,20 +104,65 @@ frappe.ui.form.on('Account', {
}
},
update_account_number: function(frm) {
merge_account: function(frm) {
var d = new frappe.ui.Dialog({
title: __('Update Account Number'),
title: __('Merge with Existing Account'),
fields: [
{
"label": "Account Number",
"fieldname": "account_number",
"label" : "Name",
"fieldname": "name",
"fieldtype": "Data",
"reqd": 1
"reqd": 1,
"default": frm.doc.name
}
],
primary_action: function() {
var data = d.get_values();
if(data.account_number === frm.doc.account_number) {
frappe.call({
method: "erpnext.accounts.doctype.account.account.merge_account",
args: {
old: frm.doc.name,
new: data.name,
is_group: frm.doc.is_group,
root_type: frm.doc.root_type,
company: frm.doc.company
},
callback: function(r) {
if(!r.exc) {
if(r.message) {
frappe.set_route("Form", "Account", r.message);
}
d.hide();
}
}
});
},
primary_action_label: __('Merge')
});
d.show();
},
update_account_number: function(frm) {
var d = new frappe.ui.Dialog({
title: __('Update Account Number / Name'),
fields: [
{
"label": "Account Name",
"fieldname": "account_name",
"fieldtype": "Data",
"reqd": 1,
"default": frm.doc.account_name
},
{
"label": "Account Number",
"fieldname": "account_number",
"fieldtype": "Data",
"default": frm.doc.account_number
}
],
primary_action: function() {
var data = d.get_values();
if(data.account_number === frm.doc.account_number && data.account_name === frm.doc.account_name) {
d.hide();
return;
}
@@ -120,6 +171,7 @@ frappe.ui.form.on('Account', {
method: "erpnext.accounts.doctype.account.account.update_account_number",
args: {
account_number: data.account_number,
account_name: data.account_name,
name: frm.doc.name
},
callback: function(r) {
@@ -128,6 +180,7 @@ frappe.ui.form.on('Account', {
frappe.set_route("Form", "Account", r.message);
} else {
frm.set_value("account_number", data.account_number);
frm.set_value("account_name", data.account_name);
}
d.hide();
}
@@ -138,4 +191,4 @@ frappe.ui.form.on('Account', {
});
d.show();
}
});
});

View File

@@ -2,7 +2,7 @@
"allow_copy": 1,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 1,
"allow_rename": 0,
"beta": 0,
"creation": "2013-01-30 12:49:46",
"custom": 0,
@@ -40,6 +40,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -68,6 +69,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0,
"width": "50%"
},
@@ -100,6 +102,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -130,6 +133,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -161,6 +165,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -193,6 +198,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -223,6 +229,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -253,6 +260,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -285,6 +293,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -313,6 +322,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0,
"width": "50%"
},
@@ -346,6 +356,7 @@
"reqd": 1,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -379,6 +390,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -411,6 +423,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -444,6 +457,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -474,6 +488,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -503,6 +518,7 @@
"reqd": 0,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -532,6 +548,7 @@
"reqd": 0,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -561,6 +578,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
@@ -575,7 +593,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-08-22 17:39:10.711343",
"modified": "2018-07-08 09:47:04.287841",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Account",

View File

@@ -165,53 +165,6 @@ class Account(NestedSet):
super(Account, self).on_trash(True)
def before_rename(self, old, new, merge=False):
# Add company abbr if not provided
from erpnext.setup.doctype.company.company import get_name_with_abbr
new_account = get_name_with_abbr(new, self.company)
if not merge:
new_account = get_name_with_number(new_account, self.account_number)
else:
# Validate properties before merging
if not frappe.db.exists("Account", new):
throw(_("Account {0} does not exist").format(new))
val = list(frappe.db.get_value("Account", new_account,
["is_group", "root_type", "company"]))
if val != [self.is_group, self.root_type, self.company]:
throw(_("""Merging is only possible if following properties are same in both records. Is Group, Root Type, Company"""))
if self.is_group and frappe.db.get_value("Account", new, "parent_account") == old:
frappe.db.set_value("Account", new, "parent_account",
frappe.db.get_value("Account", old, "parent_account"))
return new_account
def after_rename(self, old, new, merge=False):
super(Account, self).after_rename(old, new, merge)
if not merge:
new_acc = frappe.db.get_value("Account", new, ["account_name", "account_number"], as_dict=1)
# exclude company abbr
new_parts = new.split(" - ")[:-1]
# update account number and remove from parts
if new_parts[0][0].isdigit():
# if account number is separate by space, split using space
if len(new_parts) == 1:
new_parts = new.split(" ")
if new_acc.account_number != new_parts[0]:
self.account_number = new_parts[0]
self.db_set("account_number", new_parts[0])
new_parts = new_parts[1:]
# update account name
account_name = " - ".join(new_parts)
if new_acc.account_name != account_name:
self.account_name = account_name
self.db_set("account_name", account_name)
def get_parent_account(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql("""select name from tabAccount
where is_group = 1 and docstatus != 2 and company = %s
@@ -252,25 +205,35 @@ def validate_account_number(name, account_number, company):
.format(account_number, account_with_same_number))
@frappe.whitelist()
def update_account_number(name, account_number):
account = frappe.db.get_value("Account", name, ["account_name", "company"], as_dict=True)
def update_account_number(name, account_name, account_number=None):
account = frappe.db.get_value("Account", name, ["company"], as_dict=True)
validate_account_number(name, account_number, account.company)
frappe.db.set_value("Account", name, "account_number", account_number)
account_name = account.account_name
if account_name[0].isdigit():
separator = " - " if " - " in account_name else " "
account_name = account_name.split(separator, 1)[1]
frappe.db.set_value("Account", name, "account_name", account_name)
if account_number:
frappe.db.set_value("Account", name, "account_number", account_number.strip())
frappe.db.set_value("Account", name, "account_name", account_name.strip())
new_name = get_account_autoname(account_number, account_name, account.company)
if name != new_name:
frappe.rename_doc("Account", name, new_name)
frappe.rename_doc("Account", name, new_name, ignore_permissions=1)
return new_name
def get_name_with_number(new_account, account_number):
if account_number and not new_account[0].isdigit():
new_account = account_number + " - " + new_account
return new_account
@frappe.whitelist()
def merge_account(old, new, is_group, root_type, company):
# Validate properties before merging
if not frappe.db.exists("Account", new):
throw(_("Account {0} does not exist").format(new))
val = list(frappe.db.get_value("Account", new,
["is_group", "root_type", "company"]))
if val != [cint(is_group), root_type, company]:
throw(_("""Merging is only possible if following properties are same in both records. Is Group, Root Type, Company"""))
if is_group and frappe.db.get_value("Account", new, "parent_account") == old:
frappe.db.set_value("Account", new, "parent_account",
frappe.db.get_value("Account", old, "parent_account"))
frappe.rename_doc("Account", old, new, merge=1, ignore_permissions=1)
return new

View File

@@ -5,6 +5,8 @@ from __future__ import unicode_literals
import unittest
import frappe
from erpnext.stock import get_warehouse_account, get_company_default_inventory_account
from erpnext.accounts.doctype.account.account import update_account_number
from erpnext.accounts.doctype.account.account import merge_account
class TestAccount(unittest.TestCase):
def test_rename_account(self):
@@ -21,21 +23,79 @@ class TestAccount(unittest.TestCase):
self.assertEqual(account_number, "1210")
self.assertEqual(account_name, "Debtors")
frappe.rename_doc("Account", "1210 - Debtors - _TC", "1211 - Debtors 1 - _TC")
new_account_number = "1211-11-4 - 6 - "
new_account_name = "Debtors 1 - Test - "
new_acc = frappe.db.get_value("Account", "1211 - Debtors 1 - _TC",
update_account_number("1210 - Debtors - _TC", new_account_name, new_account_number)
new_acc = frappe.db.get_value("Account", "1211-11-4 - 6 - - Debtors 1 - Test - - _TC",
["account_name", "account_number"], as_dict=1)
self.assertEqual(new_acc.account_name, "Debtors 1")
self.assertEqual(new_acc.account_number, "1211")
frappe.rename_doc("Account", "1211 - Debtors 1 - _TC", "Debtors 2")
self.assertEqual(new_acc.account_name, "Debtors 1 - Test -")
self.assertEqual(new_acc.account_number, "1211-11-4 - 6 -")
new_acc = frappe.db.get_value("Account", "1211 - Debtors 2 - _TC",
["account_name", "account_number"], as_dict=1)
self.assertEqual(new_acc.account_name, "Debtors 2")
self.assertEqual(new_acc.account_number, "1211")
frappe.delete_doc("Account", "1211-11-4 - 6 - Debtors 1 - Test - - _TC")
frappe.delete_doc("Account", "1211 - Debtors 2 - _TC")
def test_merge_account(self):
if not frappe.db.exists("Account", "Current Assets - _TC"):
acc = frappe.new_doc("Account")
acc.account_name = "Current Assets"
acc.is_group = 1
acc.parent_account = "Application of Funds (Assets) - _TC"
acc.company = "_Test Company"
acc.insert()
if not frappe.db.exists("Account", "Securities and Deposits - _TC"):
acc = frappe.new_doc("Account")
acc.account_name = "Securities and Deposits"
acc.parent_account = "Current Assets - _TC"
acc.is_group = 1
acc.company = "_Test Company"
acc.insert()
if not frappe.db.exists("Account", "Earnest Money - _TC"):
acc = frappe.new_doc("Account")
acc.account_name = "Earnest Money"
acc.parent_account = "Securities and Deposits - _TC"
acc.company = "_Test Company"
acc.insert()
if not frappe.db.exists("Account", "Cash In Hand - _TC"):
acc = frappe.new_doc("Account")
acc.account_name = "Cash In Hand"
acc.is_group = 1
acc.parent_account = "Current Assets - _TC"
acc.company = "_Test Company"
acc.insert()
if not frappe.db.exists("Account", "Accumulated Depreciation - _TC"):
acc = frappe.new_doc("Account")
acc.account_name = "Accumulated Depreciation"
acc.parent_account = "Fixed Assets - _TC"
acc.company = "_Test Company"
acc.insert()
doc = frappe.get_doc("Account", "Securities and Deposits - _TC")
parent = frappe.db.get_value("Account", "Earnest Money - _TC", "parent_account")
self.assertEqual(parent, "Securities and Deposits - _TC")
merge_account("Securities and Deposits - _TC", "Cash In Hand - _TC", doc.is_group, doc.root_type, doc.company)
parent = frappe.db.get_value("Account", "Earnest Money - _TC", "parent_account")
# Parent account of the child account changes after merging
self.assertEqual(parent, "Cash In Hand - _TC")
# Old account doesn't exist after merging
self.assertFalse(frappe.db.exists("Account", "Securities and Deposits - _TC"))
doc = frappe.get_doc("Account", "Current Assets - _TC")
# Raise error as is_group property doesn't match
self.assertRaises(frappe.ValidationError, merge_account, "Current Assets - _TC",\
"Accumulated Depreciation - _TC", doc.is_group, doc.root_type, doc.company)
doc = frappe.get_doc("Account", "Capital Stock - _TC")
# Raise error as root_type property doesn't match
self.assertRaises(frappe.ValidationError, merge_account, "Capital Stock - _TC",\
"Softwares - _TC", doc.is_group, doc.root_type, doc.company)
def _make_test_records(verbose):
from frappe.test_runner import make_test_objects

View File

@@ -588,6 +588,10 @@ frappe.ui.form.on('Payment Entry', {
allocate_party_amount_against_ref_docs: function(frm, paid_amount) {
var total_positive_outstanding_including_order = 0;
var total_negative_outstanding = 0;
var total_deductions = frappe.utils.sum($.map(frm.doc.deductions || [],
function(d) { return flt(d.amount) }));
paid_amount -= total_deductions;
$.each(frm.doc.references || [], function(i, row) {
if(flt(row.outstanding_amount) > 0)
@@ -815,23 +819,30 @@ frappe.ui.form.on('Payment Entry Reference', {
reference_name: function(frm, cdt, cdn) {
var row = locals[cdt][cdn];
return frappe.call({
method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_reference_details",
args: {
reference_doctype: row.reference_doctype,
reference_name: row.reference_name,
party_account_currency: frm.doc.payment_type=="Receive" ?
frm.doc.paid_from_account_currency : frm.doc.paid_to_account_currency
},
callback: function(r, rt) {
if(r.message) {
$.each(r.message, function(field, value) {
frappe.model.set_value(cdt, cdn, field, value);
})
frm.refresh_fields();
if (row.reference_name && row.reference_doctype) {
return frappe.call({
method: "erpnext.accounts.doctype.payment_entry.payment_entry.get_reference_details",
args: {
reference_doctype: row.reference_doctype,
reference_name: row.reference_name,
party_account_currency: frm.doc.payment_type=="Receive" ?
frm.doc.paid_from_account_currency : frm.doc.paid_to_account_currency
},
callback: function(r, rt) {
if(r.message) {
$.each(r.message, function(field, value) {
frappe.model.set_value(cdt, cdn, field, value);
})
let allocated_amount = frm.doc.unallocated_amount > row.outstanding_amount ?
row.outstanding_amount : frm.doc.unallocated_amount;
frappe.model.set_value(cdt, cdn, 'allocated_amount', allocated_amount);
frm.refresh_fields();
}
}
}
})
})
}
},
allocated_amount: function(frm) {

View File

@@ -40,7 +40,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -72,7 +71,6 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 1,
"translatable": 0,
"unique": 0
},
{
@@ -104,7 +102,6 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -134,7 +131,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -166,7 +162,6 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -198,7 +193,6 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -230,7 +224,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -262,7 +255,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -296,7 +288,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -329,7 +320,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -359,7 +349,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -392,7 +381,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -423,7 +411,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -455,7 +442,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -488,7 +474,6 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -521,7 +506,6 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -554,7 +538,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -584,7 +567,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -617,7 +599,6 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -650,7 +631,6 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -683,7 +663,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -716,7 +695,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -749,7 +727,6 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -781,7 +758,6 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -814,7 +790,6 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -844,7 +819,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -877,7 +851,6 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -909,7 +882,6 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -942,7 +914,6 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -975,7 +946,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1008,7 +978,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1041,7 +1010,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1073,7 +1041,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1105,7 +1072,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1138,7 +1104,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1169,7 +1134,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1199,7 +1163,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1231,7 +1194,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1264,7 +1226,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1296,7 +1257,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1329,7 +1289,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1361,7 +1320,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1392,7 +1350,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1424,7 +1381,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1454,7 +1410,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1486,7 +1441,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1518,7 +1472,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1551,7 +1504,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1584,7 +1536,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1604,7 +1555,7 @@
"in_standard_filter": 0,
"label": "Remarks",
"length": 0,
"no_copy": 0,
"no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -1615,7 +1566,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1645,7 +1595,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1677,7 +1626,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1709,7 +1657,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1740,7 +1687,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1772,7 +1718,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1803,7 +1748,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
@@ -1834,7 +1778,6 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
@@ -1848,7 +1791,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-02-19 16:58:23.899015",
"modified": "2018-07-27 01:49:24.720317",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Entry",

View File

@@ -337,14 +337,15 @@ class PaymentEntry(AccountsController):
total_negative_outstanding = sum([abs(flt(d.outstanding_amount))
for d in self.get("references") if flt(d.outstanding_amount) < 0])
party_amount = self.paid_amount if self.payment_type=="Receive" else self.received_amount
paid_amount = self.paid_amount if self.payment_type=="Receive" else self.received_amount
additional_charges = sum([flt(d.amount) for d in self.deductions])
if not total_negative_outstanding:
frappe.throw(_("Cannot {0} {1} {2} without any negative outstanding invoice")
.format(self.payment_type, ("to" if self.party_type=="Customer" else "from"),
self.party_type), InvalidPaymentEntry)
elif party_amount > total_negative_outstanding:
elif paid_amount - additional_charges > total_negative_outstanding:
frappe.throw(_("Paid Amount cannot be greater than total negative outstanding amount {0}")
.format(total_negative_outstanding), InvalidPaymentEntry)
@@ -724,8 +725,11 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
exchange_rate = ref_doc.get("conversion_rate") or \
get_exchange_rate(party_account_currency, company_currency, ref_doc.posting_date)
if reference_doctype in ("Sales Invoice", "Purchase Invoice", "Expense Claim"):
if reference_doctype in ("Sales Invoice", "Purchase Invoice"):
outstanding_amount = ref_doc.get("outstanding_amount")
elif reference_doctype == "Expense Claim":
outstanding_amount = flt(ref_doc.get("total_sanctioned_amount")) \
- flt(ref_doc.get("total_amount+reimbursed")) - flt(ref_doc.get("total_advance_amount"))
elif reference_doctype == "Employee Advance":
outstanding_amount = ref_doc.advance_amount - flt(ref_doc.paid_amount)
else:

View File

@@ -21,8 +21,6 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
if(!this.frm.doc.supplier && this.frm.doc.credit_to) {
this.frm.set_df_property("credit_to", "print_hide", 0);
}
} else {
this.frm.set_value("disable_rounded_total", cint(frappe.sys_defaults.disable_rounded_total));
}
},

View File

@@ -1239,7 +1239,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_bulk_edit": 1,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -3973,7 +3973,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2018-05-28 02:38:40.310899",
"modified": "2018-07-06 02:38:40.310899",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice",

View File

@@ -507,5 +507,6 @@ def save_invoice(doc, name, name_list):
name_list.append(name)
except Exception:
frappe.log_error(frappe.get_traceback())
frappe.db.rollback()
return name_list

View File

@@ -639,6 +639,66 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_21",
"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,
"depends_on": "eval: doc.is_return && doc.return_against",
"fieldname": "update_billed_amount_in_sales_order",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Update Billed Amount in Sales Order",
"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,
@@ -1459,7 +1519,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_bulk_edit": 1,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -4713,7 +4773,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2018-05-28 04:47:29.879475",
"modified": "2018-07-17 13:46:52.449142",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",
@@ -4804,7 +4864,7 @@
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 1,
"search_fields": "posting_date, due_date, customer, base_grand_total, outstanding_amount",
"search_fields": "posting_date,due_date,customer,base_grand_total,outstanding_amount",
"show_name_in_global_search": 1,
"sort_field": "modified",
"sort_order": "DESC",

View File

@@ -116,7 +116,7 @@ class SalesInvoice(SellingController):
self.check_prev_docstatus()
if self.is_return:
if self.is_return and not self.update_billed_amount_in_sales_order:
# NOTE status updating bypassed for is_return
self.status_updater = []
@@ -161,7 +161,7 @@ class SalesInvoice(SellingController):
if frappe.db.get_single_value('Accounts Settings', 'unlink_payment_on_cancellation_of_invoice'):
unlink_ref_doc_from_payment_entries(self)
if self.is_return:
if self.is_return and not self.update_billed_amount_in_sales_order:
# NOTE status updating bypassed for is_return
self.status_updater = []
@@ -318,6 +318,7 @@ class SalesInvoice(SellingController):
if not for_validate and not self.customer:
self.customer = pos.customer
self.ignore_pricing_rule = pos.ignore_pricing_rule
if pos.get('account_for_change_amount'):
self.account_for_change_amount = pos.get('account_for_change_amount')

View File

@@ -20,8 +20,8 @@ from erpnext import get_default_currency, get_company_currency
class DuplicatePartyAccountError(frappe.ValidationError): pass
@frappe.whitelist()
def get_party_details(party=None, account=None, party_type="Customer", company=None,
posting_date=None, price_list=None, currency=None, doctype=None, ignore_permissions=False):
def get_party_details(party=None, account=None, party_type="Customer", company=None, posting_date=None,
price_list=None, currency=None, doctype=None, ignore_permissions=False, fetch_payment_terms_template=True):
if not party:
return {}
@@ -30,10 +30,10 @@ def get_party_details(party=None, account=None, party_type="Customer", company=N
frappe.throw(_("{0}: {1} does not exists").format(party_type, party))
return _get_party_details(party, account, party_type,
company, posting_date, price_list, currency, doctype, ignore_permissions)
company, posting_date, price_list, currency, doctype, ignore_permissions, fetch_payment_terms_template)
def _get_party_details(party=None, account=None, party_type="Customer", company=None,
posting_date=None, price_list=None, currency=None, doctype=None, ignore_permissions=False):
def _get_party_details(party=None, account=None, party_type="Customer", company=None, posting_date=None,
price_list=None, currency=None, doctype=None, ignore_permissions=False, fetch_payment_terms_template=True):
out = frappe._dict(set_account_and_due_date(party, account, party_type, company, posting_date, doctype))
@@ -50,7 +50,9 @@ def _get_party_details(party=None, account=None, party_type="Customer", company=
set_other_values(out, party, party_type)
set_price_list(out, party, party_type, price_list)
out["taxes_and_charges"] = set_taxes(party.name, party_type, posting_date, company, out.customer_group, out.supplier_type)
out["payment_terms_template"] = get_pyt_term_template(party.name, party_type, company)
if fetch_payment_terms_template:
out["payment_terms_template"] = get_pyt_term_template(party.name, party_type, company)
if not out.get("currency"):
out["currency"] = currency
@@ -272,6 +274,7 @@ def get_due_date(posting_date, party_type, party, company=None):
if posting_date and party:
due_date = posting_date
template_name = get_pyt_term_template(party, party_type, company)
if template_name:
due_date = get_due_date_from_template(template_name, posting_date).strftime("%Y-%m-%d")
else:
@@ -304,11 +307,13 @@ def get_due_date_from_template(template_name, posting_date):
return due_date
def validate_due_date(posting_date, due_date, party_type, party, company=None):
def validate_due_date(posting_date, due_date, party_type, party, company=None, template_name=None):
if getdate(due_date) < getdate(posting_date):
frappe.throw(_("Due Date cannot be before Posting Date"))
else:
default_due_date = get_due_date(posting_date, party_type, party, company)
if not template_name: return
default_due_date = get_due_date_from_template(template_name, posting_date).strftime("%Y-%m-%d")
if not default_due_date:
return
@@ -414,10 +419,10 @@ def get_timeline_data(doctype, name):
# fetch and append data from Activity Log
data += frappe.db.sql("""select {fields}
from `tabActivity Log`
where reference_doctype='{doctype}' and reference_name='{name}'
where reference_doctype="{doctype}" and reference_name="{name}"
and status!='Success' and creation > {after}
{group_by} order by creation desc
""".format(doctype=doctype, name=name, fields=fields,
""".format(doctype=frappe.db.escape(doctype), name=frappe.db.escape(name), fields=fields,
group_by=group_by, after=after), as_dict=False)
timeline_items = dict(data)

View File

@@ -32,7 +32,7 @@ frappe.query_reports["Accounts Payable"] = {
"fieldname":"ageing_based_on",
"label": __("Ageing Based On"),
"fieldtype": "Select",
"options": 'Posting Date\nDue Date',
"options": 'Posting Date\nDue Date\nSupplier Invoice Date',
"default": "Posting Date"
},
{

View File

@@ -151,6 +151,7 @@ class ReceivablePayableReport(object):
# get due date
due_date = voucher_details.get(gle.voucher_no, {}).get("due_date", "")
bill_date = voucher_details.get(gle.voucher_no, {}).get("bill_date", "")
row += [gle.voucher_type, gle.voucher_no, due_date]
@@ -167,15 +168,25 @@ class ReceivablePayableReport(object):
row += [invoiced_amount, paid_amt, credit_note_amount, outstanding_amount]
# ageing data
entry_date = due_date if self.filters.ageing_based_on == "Due Date" else gle.posting_date
if self.filters.ageing_based_on == "Due Date":
entry_date = due_date
elif self.filters.ageing_based_on == "Supplier Invoice Date":
entry_date = bill_date
else:
entry_date = gle.posting_date
row += get_ageing_data(cint(self.filters.range1), cint(self.filters.range2),
cint(self.filters.range3), self.age_as_on, entry_date, outstanding_amount)
# issue 6371-Ageing buckets should not have amounts if due date is not reached
if self.filters.ageing_based_on == "Due Date" \
and getdate(due_date) > getdate(self.filters.report_date):
row[-1]=row[-2]=row[-3]=row[-4]=0
if self.filters.ageing_based_on == "Supplier Invoice Date" \
and getdate(bill_date) > getdate(self.filters.report_date):
row[-1]=row[-2]=row[-3]=row[-4]=0
if self.filters.get(scrub(args.get("party_type"))):
row.append(gle.account_currency)
else:
@@ -286,7 +297,10 @@ class ReceivablePayableReport(object):
if party_type == "Supplier":
for pi in frappe.db.sql("""select name, due_date, bill_no, bill_date
from `tabPurchase Invoice` where docstatus=1""", as_dict=1):
from `tabPurchase Invoice` where docstatus = 1
union
select name, due_date, bill_no, bill_date from `tabJournal Entry`
where docstatus = 1 and bill_no is not NULL""", as_dict=1):
voucher_details.setdefault(pi.name, pi)
return voucher_details

View File

@@ -39,6 +39,12 @@ frappe.query_reports["Budget Variance Report"] = {
options: ["Cost Center", "Project"],
default: "Cost Center",
reqd: 1
}
},
{
fieldname: "cost_center",
label: __("Cost Center"),
fieldtype: "Link",
options: "Cost Center"
},
]
}

View File

@@ -10,9 +10,13 @@ from erpnext.controllers.trends import get_period_date_ranges, get_period_month_
def execute(filters=None):
if not filters: filters = {}
validate_filters(filters)
columns = get_columns(filters)
cost_centers = get_cost_centers(filters)
if filters.get("cost_center"):
cost_centers = [filters.get("cost_center")]
else:
cost_centers = get_cost_centers(filters)
period_month_ranges = get_period_month_ranges(filters["period"], filters["fiscal_year"])
cam_map = get_cost_center_account_month_map(filters)
@@ -39,6 +43,10 @@ def execute(filters=None):
return columns, data
def validate_filters(filters):
if filters.get("budget_against")=="Project" and filters.get("cost_center"):
frappe.throw(_("Filter based on Cost Center is only applicable if Budget Against is selected as Cost Center"))
def get_columns(filters):
columns = [_(filters.get("budget_against")) + ":Link/%s:120"%(filters.get("budget_against")), _("Account") + ":Link/Account:120"]
@@ -66,12 +74,16 @@ def get_cost_centers(filters):
#Get cost center & target details
def get_cost_center_target_details(filters):
cond = ""
if filters.get("cost_center"):
cond += " and b.cost_center='%s'" % frappe.db.escape(filters.get("cost_center"))
return frappe.db.sql("""
select b.{budget_against} as budget_against, b.monthly_distribution, ba.account, ba.budget_amount
from `tabBudget` b, `tabBudget Account` ba
where b.name=ba.parent and b.docstatus = 1 and b.fiscal_year=%s
and b.budget_against = %s and b.company=%s
""".format(budget_against=filters.get("budget_against").replace(" ", "_").lower()),
and b.budget_against = %s and b.company=%s {cond}
""".format(budget_against=filters.get("budget_against").replace(" ", "_").lower(), cond=cond),
(filters.fiscal_year, filters.budget_against, filters.company), as_dict=True)
#Get target distribution details of accounts of cost center

View File

@@ -185,14 +185,15 @@ def prepare_data(accounts, balance_must_be, period_list, company_currency):
has_value = False
total = 0
row = frappe._dict({
"account_name": _(d.account_name),
"account": _(d.name),
"parent_account": _(d.parent_account),
"indent": flt(d.indent),
"year_start_date": year_start_date,
"year_end_date": year_end_date,
"currency": company_currency,
"opening_balance": d.get("opening_balance", 0.0) * (1 if balance_must_be=="Debit" else -1)
"opening_balance": d.get("opening_balance", 0.0) * (1 if balance_must_be=="Debit" else -1),
"account_name": ('{} - {}'.format(_(d.account_number), _(d.account_name))
if d.account_number else _(d.account_name))
})
for period in period_list:
if d.get(period.key) and balance_must_be=="Credit":
@@ -253,7 +254,7 @@ def add_total_row(out, root_type, balance_must_be, period_list, company_currency
out.append({})
def get_accounts(company, root_type):
return frappe.db.sql("""select name, parent_account, lft, rgt, root_type, report_type, account_name from `tabAccount`
return frappe.db.sql("""select name, account_number, parent_account, lft, rgt, root_type, report_type, account_name from `tabAccount`
where company=%s and root_type=%s order by lft""", (company, root_type), as_dict=True)
def filter_accounts(accounts, depth=10):

View File

@@ -171,7 +171,7 @@ class GrossProfitGenerator(object):
row.qty += returned_item_row.qty
row.base_amount += returned_item_row.base_amount
row.buying_amount = row.qty * row.buying_rate
if row.qty:
if row.qty or row.base_amount:
row = self.set_average_rate(row)
self.grouped_data.append(row)

View File

@@ -53,7 +53,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
delivery_note, d.income_account, d.cost_center, d.stock_qty, d.stock_uom
]
row += [d.base_net_rate/d.stock_qty, d.base_net_amount] \
row += [(d.base_net_rate * d.qty)/d.stock_qty, d.base_net_amount] \
if d.stock_uom != d.uom else [d.base_net_rate, d.base_net_amount]
total_tax = 0
@@ -133,7 +133,7 @@ def get_items(filters, additional_query_columns):
`tabSales Invoice Item`.stock_uom, `tabSales Invoice Item`.base_net_rate,
`tabSales Invoice Item`.base_net_amount, `tabSales Invoice`.customer_name,
`tabSales Invoice`.customer_group, `tabSales Invoice Item`.so_detail,
`tabSales Invoice`.update_stock, `tabSales Invoice Item`.uom {0}
`tabSales Invoice`.update_stock, `tabSales Invoice Item`.uom, `tabSales Invoice Item`.qty {0}
from `tabSales Invoice`, `tabSales Invoice Item`
where `tabSales Invoice`.name = `tabSales Invoice Item`.parent
and `tabSales Invoice`.docstatus = 1 %s %s
@@ -157,6 +157,9 @@ def get_delivery_notes_against_sales_order(item_list):
return so_dn_map
def get_deducted_taxes():
return frappe.db.sql_list("select name from `tabPurchase Taxes and Charges` where add_deduct_tax = 'Deduct'")
def get_tax_accounts(item_list, columns, company_currency,
doctype="Sales Invoice", tax_doctype="Sales Taxes and Charges"):
import json
@@ -176,9 +179,10 @@ def get_tax_accounts(item_list, columns, company_currency,
if doctype == "Purchase Invoice":
conditions = " and category in ('Total', 'Valuation and Total') and base_tax_amount_after_discount_amount != 0"
deducted_tax = get_deducted_taxes()
tax_details = frappe.db.sql("""
select
parent, description, item_wise_tax_detail,
name, parent, description, item_wise_tax_detail,
charge_type, base_tax_amount_after_discount_amount
from `tab%s`
where
@@ -190,7 +194,7 @@ def get_tax_accounts(item_list, columns, company_currency,
""" % (tax_doctype, '%s', ', '.join(['%s']*len(invoice_item_row)), conditions),
tuple([doctype] + invoice_item_row.keys()))
for parent, description, item_wise_tax_detail, charge_type, tax_amount in tax_details:
for name, parent, description, item_wise_tax_detail, charge_type, tax_amount in tax_details:
description = handle_html(description)
if description not in tax_columns and tax_amount:
# as description is text editor earlier and markup can break the column convention in reports
@@ -219,9 +223,13 @@ def get_tax_accounts(item_list, columns, company_currency,
item_tax_amount = flt((tax_amount * d.base_net_amount) / item_net_amount) \
if item_net_amount else 0
if item_tax_amount:
tax_value = flt(item_tax_amount, tax_amount_precision)
tax_value = (tax_value * -1
if (doctype == 'Purchase Invoice' and name in deducted_tax) else tax_value)
itemised_tax.setdefault(d.name, {})[description] = frappe._dict({
"tax_rate": tax_rate,
"tax_amount": flt(item_tax_amount, tax_amount_precision)
"tax_amount": tax_value
})
except ValueError:

View File

@@ -51,7 +51,7 @@ def validate_filters(filters):
filters.to_date = filters.year_end_date
def get_data(filters):
accounts = frappe.db.sql("""select name, parent_account, account_name, root_type, report_type, lft, rgt
accounts = frappe.db.sql("""select name, account_number, parent_account, account_name, root_type, report_type, lft, rgt
from `tabAccount` where company=%s order by lft""", filters.company, as_dict=True)
company_currency = erpnext.get_company_currency(filters.company)
@@ -162,8 +162,6 @@ def calculate_values(accounts, gl_entries_by_account, opening_balances, filters,
total_row["credit"] += d["credit"]
total_row["opening_debit"] += d["opening_debit"]
total_row["opening_credit"] += d["opening_credit"]
total_row["closing_debit"] += (d["opening_debit"] + d["debit"])
total_row["closing_credit"] += (d["opening_credit"] + d["credit"])
return total_row
@@ -176,16 +174,19 @@ def accumulate_values_into_parents(accounts, accounts_by_name):
def prepare_data(accounts, filters, total_row, parent_children_map, company_currency):
data = []
total_row["closing_debit"] = total_row["closing_credit"] = 0
for d in accounts:
has_value = False
row = {
"account_name": d.account_name,
"account": d.name,
"parent_account": d.parent_account,
"indent": d.indent,
"from_date": filters.from_date,
"to_date": filters.to_date,
"currency": company_currency
"currency": company_currency,
"account_name": ('{} - {}'.format(d.account_number, d.account_name)
if d.account_number else d.account_name)
}
prepare_opening_and_closing(d)
@@ -200,6 +201,10 @@ def prepare_data(accounts, filters, total_row, parent_children_map, company_curr
row["has_value"] = has_value
data.append(row)
if not d.parent_account:
total_row["closing_debit"] += (d["debit"] - d["credit"]) if (d["debit"] - d["credit"]) > 0 else 0
total_row["closing_credit"] += abs(d["debit"] - d["credit"]) if (d["debit"] - d["credit"]) < 0 else 0
data.extend([{},total_row])
return data

View File

@@ -23,6 +23,14 @@ frappe.ui.form.on('Asset', {
}
};
});
frm.set_query("cost_center", function() {
return {
"filters": {
"company": frm.doc.company,
}
};
});
},
refresh: function(frm) {

View File

@@ -415,6 +415,37 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "cost_center",
"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": "Cost Center",
"length": 0,
"no_copy": 0,
"options": "Cost Center",
"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,
@@ -1221,7 +1252,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-12-19 12:58:44.137460",
"modified": "2018-07-17 06:30:25.506194",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset",

View File

@@ -38,7 +38,7 @@ def make_depreciation_entry(asset_name, date=None):
depreciation_cost_center, depreciation_series = frappe.db.get_value("Company", asset.company,
["depreciation_cost_center", "series_for_depreciation_entry"])
depreciation_cost_center = asset.cost_center or depreciation_cost_center
for d in asset.get("schedules"):
if not d.journal_entry and getdate(d.schedule_date) <= getdate(date):
@@ -154,6 +154,7 @@ def restore_asset(asset_name):
def get_gl_entries_on_asset_disposal(asset, selling_amount=0):
fixed_asset_account, accumulated_depr_account, depr_expense_account = get_depreciation_accounts(asset)
disposal_account, depreciation_cost_center = get_disposal_account_and_cost_center(asset.company)
depreciation_cost_center = asset.cost_center or depreciation_cost_center
accumulated_depr_amount = flt(asset.gross_purchase_amount) - flt(asset.value_after_depreciation)

View File

@@ -3,7 +3,7 @@
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "field:maintenance_task",
"autoname": "",
"beta": 0,
"creation": "2017-10-20 07:10:55.903571",
"custom": 0,
@@ -42,7 +42,7 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 1
"unique": 0
},
{
"allow_bulk_edit": 0,
@@ -625,7 +625,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-11-28 12:22:34.151430",
"modified": "2018-06-18 16:12:04.330021",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset Maintenance Task",

View File

@@ -7,4 +7,5 @@ import frappe
from frappe.model.document import Document
class AssetMaintenanceTask(Document):
pass
def autoname(self):
self.name = self.maintenance_task

View File

@@ -1,231 +1,276 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"beta": 0,
"creation": "2013-06-25 11:04:03",
"custom": 0,
"description": "Settings for Buying Module",
"docstatus": 0,
"doctype": "DocType",
"document_type": "Other",
"editable_grid": 0,
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "Supplier Name",
"fieldname": "supp_master_name",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Supplier Naming By",
"length": 0,
"no_copy": 0,
"options": "Supplier Name\nNaming Series",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "supplier_type",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Default Supplier Type",
"length": 0,
"no_copy": 0,
"options": "Supplier Type",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "buying_price_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": "Default Buying Price List",
"length": 0,
"no_copy": 0,
"options": "Price List",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 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,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "po_required",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Purchase Order Required",
"length": 0,
"no_copy": 0,
"options": "No\nYes",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "pr_required",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Purchase Receipt Required",
"length": 0,
"no_copy": 0,
"options": "No\nYes",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "maintain_same_rate",
"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": "Maintain same rate throughout purchase cycle",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "allow_multiple_items",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Allow Item to be added multiple times in a transaction",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "0",
"description": "If enabled, last purchase details of items will not be fetched from previous purchase order or purchase receipt",
"fieldname": "disable_fetch_last_purchase_rate",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Disable Fetching Last Purchase Details in Purchase Order",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "fa fa-cog",
"idx": 1,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 0,
"issingle": 1,
"istable": 0,
"modified": "2017-12-27 15:20:06.052342",
"max_attachments": 0,
"modified": "2018-07-18 07:52:38.062488",
"modified_by": "Administrator",
"module": "Buying",
"name": "Buying Settings",
@@ -252,6 +297,10 @@
"write": 1
}
],
"quick_entry": 0,
"read_only": 0,
"read_only_onload": 0
"read_only_onload": 0,
"show_name_in_global_search": 0,
"track_changes": 0,
"track_seen": 0
}

View File

@@ -32,12 +32,7 @@ frappe.ui.form.on("Purchase Order", {
erpnext.queries.setup_queries(frm, "Warehouse", function() {
return erpnext.queries.warehouse(frm.doc);
});
if (frm.doc.__onload) {
frm.toggle_display('get_last_purchase_rate',
frm.doc.__onload.disable_fetch_last_purchase_rate);
}
},
}
});
frappe.ui.form.on("Purchase Order Item", {
@@ -318,6 +313,73 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
})
}, __("Add items from"));
this.frm.add_custom_button(__('Update rate as per last purchase'),
function() {
frappe.call({
"method": "get_last_purchase_rate",
"doc": me.frm.doc,
callback: function(r, rt) {
me.frm.dirty();
me.frm.cscript.calculate_taxes_and_totals();
}
})
}, __("Tools"));
this.frm.add_custom_button(__('Link to Material Request'),
function() {
var my_items = [];
for (var i in me.frm.doc.items) {
if(!me.frm.doc.items[i].material_request){
my_items.push(me.frm.doc.items[i].item_code);
}
}
frappe.call({
method: "erpnext.buying.utils.get_linked_material_requests",
args:{
items: my_items
},
callback: function(r) {
if(r.exc) return;
var i = 0;
var item_length = me.frm.doc.items.length;
while (i < item_length) {
var qty = me.frm.doc.items[i].qty;
(r.message[0] || []).forEach(function(d) {
if (d.qty > 0 && qty > 0 && me.frm.doc.items[i].item_code == d.item_code && !me.frm.doc.items[i].material_request_item)
{
me.frm.doc.items[i].material_request = d.mr_name;
me.frm.doc.items[i].material_request_item = d.mr_item;
var my_qty = Math.min(qty, d.qty);
qty = qty - my_qty;
d.qty = d.qty - my_qty;
me.frm.doc.items[i].stock_qty = my_qty * me.frm.doc.items[i].conversion_factor;
me.frm.doc.items[i].qty = my_qty;
frappe.msgprint("Assigning " + d.mr_name + " to " + d.item_code + " (row " + me.frm.doc.items[i].idx + ")");
if (qty > 0) {
frappe.msgprint("Splitting " + qty + " units of " + d.item_code);
var new_row = frappe.model.add_child(me.frm.doc, me.frm.doc.items[i].doctype, "items");
item_length++;
for (var key in me.frm.doc.items[i]) {
new_row[key] = me.frm.doc.items[i][key];
}
new_row.idx = item_length;
new_row["stock_qty"] = new_row.conversion_factor * qty;
new_row["qty"] = qty;
new_row["material_request"] = "";
new_row["material_request_item"] = "";
}
}
});
i++;
}
refresh_field("items");
}
});
}, __("Tools"));
},
tc_name: function() {
@@ -346,17 +408,6 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
cur_frm.cscript.update_status('Deliver', 'Delivered')
},
get_last_purchase_rate: function() {
frappe.call({
"method": "get_last_purchase_rate",
"doc": cur_frm.doc,
callback: function(r, rt) {
cur_frm.dirty();
cur_frm.cscript.calculate_taxes_and_totals();
}
})
},
items_on_form_rendered: function() {
set_schedule_date(this.frm);
},

View File

@@ -1207,7 +1207,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_bulk_edit": 1,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1238,68 +1238,6 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.docstatus===0 && (doc.items && doc.items.length)",
"fieldname": "get_last_purchase_rate",
"fieldtype": "Button",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Get last purchase rate",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.docstatus===0 && (doc.items && doc.items.length)",
"fieldname": "link_to_mrs",
"fieldtype": "Button",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Link to material requests",
"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,
@@ -2440,6 +2378,37 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "rounded_total",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Rounded Total",
"length": 0,
"no_copy": 0,
"options": "currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@@ -2471,6 +2440,36 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "disable_rounded_total",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Disable Rounded Total",
"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,
@@ -3355,7 +3354,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-02-17 11:00:05.037716",
"modified": "2018-08-01 15:18:33.155409",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order",

View File

@@ -33,12 +33,6 @@ class PurchaseOrder(BuyingController):
'percent_join_field': 'material_request'
}]
def onload(self):
super(PurchaseOrder, self).onload()
self.set_onload('disable_fetch_last_purchase_rate',
cint(frappe.db.get_single_value("Buying Settings", "disable_fetch_last_purchase_rate")))
def validate(self):
super(PurchaseOrder, self).validate()
@@ -122,7 +116,6 @@ class PurchaseOrder(BuyingController):
def get_last_purchase_rate(self):
"""get last purchase rates for all items"""
if cint(frappe.db.get_single_value("Buying Settings", "disable_fetch_last_purchase_rate")): return
conversion_rate = flt(self.get('conversion_rate')) or 1.0
for d in self.get("items"):
@@ -286,7 +279,6 @@ class PurchaseOrder(BuyingController):
def item_last_purchase_rate(name, conversion_rate, item_code, conversion_factor= 1.0):
"""get last purchase rate for an item"""
if cint(frappe.db.get_single_value("Buying Settings", "disable_fetch_last_purchase_rate")): return
conversion_rate = flt(conversion_rate) or 1.0

View File

@@ -1,156 +0,0 @@
QUnit.module('Buying');
QUnit.test("test: purchase order with last purchase rate", function(assert) {
assert.expect(9);
let done = assert.async();
frappe.run_serially([
() => {
return frappe.tests.make('Purchase Order', [
{supplier: 'Test Supplier'},
{is_subcontracted: 'No'},
{currency: 'INR'},
{items: [
[
{"item_code": 'Test Product 4'},
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
{"qty": 1},
{"rate": 800},
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
],
[
{"item_code": 'Test Product 1'},
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
{"qty": 1},
{"rate": 400},
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
]
]}
]);
},
() => {
// Get item details
assert.ok(cur_frm.doc.items[0].item_name == 'Test Product 4', "Item 1 name correct");
assert.ok(cur_frm.doc.items[1].item_name == 'Test Product 1', "Item 2 name correct");
},
() => frappe.timeout(1),
() => frappe.tests.click_button('Submit'),
() => frappe.tests.click_button('Yes'),
() => frappe.timeout(3),
() => frappe.tests.click_button('Close'),
() => frappe.timeout(1),
() => {
return frappe.tests.make('Purchase Order', [
{supplier: 'Test Supplier'},
{is_subcontracted: 'No'},
{currency: 'INR'},
{items: [
[
{"item_code": 'Test Product 4'},
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
{"qty": 1},
{"rate": 600},
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
],
[
{"item_code": 'Test Product 1'},
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
{"qty": 1},
{"rate": 200},
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
]
]}
]);
},
() => frappe.timeout(2),
// Get the last purchase rate of items
() => {
assert.ok(cur_frm.doc.items[0].last_purchase_rate == 800, "Last purchase rate of item 1 correct");
assert.ok(cur_frm.doc.items[1].last_purchase_rate != 0);
},
() => {
assert.ok(cur_frm.doc.items[1].last_purchase_rate == 400, "Last purchase rate of item 2 correct");
assert.ok(cur_frm.doc.items[1].last_purchase_rate != 0);
},
() => frappe.tests.click_button('Submit'),
() => frappe.tests.click_button('Yes'),
() => frappe.timeout(3),
() => frappe.tests.click_button('Close'),
() => frappe.timeout(1),
() => {
assert.ok(cur_frm.doc.status == 'To Receive and Bill', "Submitted successfully");
},
// enable allow_last_purchase_rate
() => {
return frappe.tests.make('Buying Settings', [
// values to be set
{"disable_fetch_last_purchase_rate": 1}
]);
},
() => {
return frappe.tests.make('Purchase Order', [
{supplier: 'Test Supplier'},
{is_subcontracted: 'No'},
{currency: 'INR'},
{items: [
[
{"item_code": 'Test Product 4'},
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
{"qty": 1},
{"rate": 800},
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
],
[
{"item_code": 'Test Product 1'},
{"schedule_date": frappe.datetime.add_days(frappe.datetime.now_date(), 1)},
{"expected_delivery_date": frappe.datetime.add_days(frappe.datetime.now_date(), 5)},
{"qty": 1},
{"rate": 400},
{"warehouse": 'Stores - '+frappe.get_abbr(frappe.defaults.get_default("Company"))}
]
]}
]);
},
() => {
// Get item details
assert.ok(cur_frm.doc.items[0].last_purchase_rate == 0);
assert.ok(cur_frm.doc.items[1].last_purchase_rate == 0);
},
() => frappe.timeout(1),
() => frappe.tests.click_button('Submit'),
() => frappe.tests.click_button('Yes'),
() => frappe.timeout(3),
() => frappe.tests.click_button('Close'),
() => frappe.timeout(1),
// enable allow_last_purchase_rate
() => frappe.tests.make('Buying Settings', [
// values to be set
{"disable_fetch_last_purchase_rate": 0}
]),
() => done()
]);
});

View File

@@ -680,7 +680,7 @@
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
@@ -1897,7 +1897,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-12-14 09:36:40.837027",
"modified": "2018-07-18 07:53:54.677844",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order Item",

View File

@@ -781,7 +781,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_bulk_edit": 1,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1983,6 +1983,37 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "rounded_total",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Rounded Total",
"length": 0,
"no_copy": 0,
"options": "currency",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
@@ -2014,6 +2045,36 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "disable_rounded_total",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Disable Rounded Total",
"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,
@@ -2177,65 +2238,65 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "group_same_items",
"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": "Group same items",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"fieldname": "group_same_items",
"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": "Group same items",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_72",
"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_72",
"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": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
"allow_bulk_edit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "letter_head",
"fieldtype": "Link",
"hidden": 0,
@@ -2549,7 +2610,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2018-05-28 02:45:48.616334",
"modified": "2018-08-01 15:18:23.265621",
"modified_by": "Administrator",
"module": "Buying",
"name": "Supplier Quotation",

View File

@@ -36,13 +36,13 @@ def get_data():
"items": [
{
"type": "doctype",
"name": "Job Applicant",
"description": _("Applicant for a Job."),
"name": "Job Opening",
"description": _("Opening for a Job."),
},
{
"type": "doctype",
"name": "Job Opening",
"description": _("Opening for a Job."),
"name": "Job Applicant",
"description": _("Applicant for a Job."),
},
{
"type": "doctype",

View File

@@ -131,14 +131,18 @@ class AccountsController(TransactionBase):
self.meta.get_label(date_field), self)
def validate_due_date(self):
if self.get('is_pos'): return
from erpnext.accounts.party import validate_due_date
if self.doctype == "Sales Invoice":
if not self.due_date:
frappe.throw(_("Due Date is mandatory"))
validate_due_date(self.posting_date, self.due_date, "Customer", self.customer, self.company)
validate_due_date(self.posting_date, self.due_date,
"Customer", self.customer, self.company, self.payment_terms_template)
elif self.doctype == "Purchase Invoice":
validate_due_date(self.posting_date, self.due_date, "Supplier", self.supplier, self.company)
validate_due_date(self.posting_date, self.due_date,
"Supplier", self.supplier, self.company, self.payment_terms_template)
def set_price_list_currency(self, buying_or_selling):
if self.meta.get_field("posting_date"):

View File

@@ -35,6 +35,7 @@ class BuyingController(StockController):
if getattr(self, "supplier", None) and not self.supplier_name:
self.supplier_name = frappe.db.get_value("Supplier", self.supplier, "supplier_name")
self.validate_items()
self.set_qty_as_per_stock_uom()
self.validate_stock_or_nonstock_items()
self.validate_warehouse()
@@ -98,9 +99,16 @@ class BuyingController(StockController):
def set_total_in_words(self):
from frappe.utils import money_in_words
if self.meta.get_field("base_in_words"):
self.base_in_words = money_in_words(self.base_grand_total, self.company_currency)
amount = (self.base_rounded_total
if not self.get("disable_rounded_total") else self.base_grand_total)
self.base_in_words = money_in_words(amount, self.company_currency)
if self.meta.get_field("in_words"):
self.in_words = money_in_words(self.grand_total, self.currency)
amount = (self.rounded_total
if not self.get("disable_rounded_total") else self.grand_total)
self.in_words = money_in_words(amount, self.currency)
# update valuation rate
def update_valuation_rate(self, parentfield):
@@ -456,3 +464,32 @@ class BuyingController(StockController):
else:
frappe.throw(_("Please enter Reqd by Date"))
def validate_items(self):
# validate items to see if they have is_purchase_item or is_subcontracted_item enabled
if self.doctype=="Material Request": return
if hasattr(self, "is_subcontracted") and self.is_subcontracted == 'Yes':
validate_item_type(self, "is_sub_contracted_item", "subcontracted")
else:
validate_item_type(self, "is_purchase_item", "purchase")
def validate_item_type(doc, fieldname, message):
# iterate through items and check if they are valid sales or purchase items
items = [d.item_code for d in doc.items if d.item_code]
# No validation check inase of creating transaction using 'Opening Invoice Creation Tool'
if not items:
return
item_list = ", ".join(["'%s'" % frappe.db.escape(d) for d in items])
invalid_items = [d[0] for d in frappe.db.sql("""
select item_code from tabItem where name in ({0}) and {1}=0
""".format(item_list, fieldname), as_list=True)]
if invalid_items:
frappe.throw(_("Following item {items} {verb} not marked as {message} item.\
You can enable them as {message} item from its Item master".format(
items = ", ".join([d for d in invalid_items]),
verb = "are" if len(invalid_items) > 1 else "is",
message = message)))

View File

@@ -176,6 +176,14 @@ def create_variant(item, args):
@frappe.whitelist()
def enqueue_multiple_variant_creation(item, args):
# There can be innumerable attribute combinations, enqueue
if isinstance(args, basestring):
variants = json.loads(args)
total_variants = 1
for key in variants:
total_variants *= len(variants[key])
if total_variants >= 600:
frappe.msgprint("Please do not create more than 500 items at a time", raise_exception=1)
return
frappe.enqueue("erpnext.controllers.item_variant.create_multiple_variants",
item=item, args=args, now=frappe.flags.in_test);

View File

@@ -35,6 +35,7 @@ class SellingController(StockController):
def validate(self):
super(SellingController, self).validate()
self.validate_items()
self.validate_max_discount()
self.validate_selling_price()
self.set_qty_as_per_stock_uom()
@@ -51,9 +52,15 @@ class SellingController(StockController):
def set_missing_lead_customer_details(self):
if getattr(self, "customer", None):
from erpnext.accounts.party import _get_party_details
fetch_payment_terms_template = False
if (self.get("__islocal") or
self.company != frappe.db.get_value(self.doctype, self.name, 'company')):
fetch_payment_terms_template = True
party_details = _get_party_details(self.customer,
ignore_permissions=self.flags.ignore_permissions,
doctype=self.doctype, company=self.company)
doctype=self.doctype, company=self.company,
fetch_payment_terms_template=fetch_payment_terms_template)
if not self.meta.get_field("sales_team"):
party_details.pop("sales_team")
@@ -337,6 +344,11 @@ class SellingController(StockController):
po_nos = frappe.get_all('Sales Order', 'po_no', filters = {'name': ('in', sales_orders)})
self.po_no = ', '.join(list(set([d.po_no for d in po_nos if d.po_no])))
def validate_items(self):
# validate items to see if they have is_sales_item enabled
from erpnext.controllers.buying_controller import validate_item_type
validate_item_type(self, "is_sales_item", "sales")
def check_active_sales_items(obj):
for d in obj.get("items"):
if d.item_code:

View File

@@ -600,16 +600,19 @@ def get_itemised_tax(taxes):
for item_code, tax_data in item_tax_map.items():
itemised_tax.setdefault(item_code, frappe._dict())
tax_rate = 0.0
tax_amount = 0.0
if isinstance(tax_data, list):
itemised_tax[item_code][tax.description] = frappe._dict(dict(
tax_rate=flt(tax_data[0]),
tax_amount=flt(tax_data[1])
))
tax_rate = flt(tax_data[0])
tax_amount = flt(tax_data[1])
else:
itemised_tax[item_code][tax.description] = frappe._dict(dict(
tax_rate=flt(tax_data),
tax_amount=0.0
))
tax_rate = flt(tax_data)
itemised_tax[item_code][tax.description] = frappe._dict(dict(
tax_rate = tax_rate,
tax_amount = tax_amount
))
return itemised_tax

View File

@@ -51,12 +51,12 @@ def enroll_random_student(current_date):
def assign_student_group(student, student_name, program, courses, batch):
course_list = [d["course"] for d in courses]
for d in frappe.get_list("Student Group", fields=("name"), filters={"program": program, "course":("in", course_list)}):
for d in frappe.get_list("Student Group", fields=("name"), filters={"program": program, "course":("in", course_list), "disabled": 0}):
student_group = frappe.get_doc("Student Group", d.name)
student_group.append("students", {"student": student, "student_name": student_name,
"group_roll_number":len(student_group.students)+1, "active":1})
student_group.save()
student_batch = frappe.get_list("Student Group", fields=("name"), filters={"program": program, "group_based_on":"Batch", "batch":batch})[0]
student_batch = frappe.get_list("Student Group", fields=("name"), filters={"program": program, "group_based_on":"Batch", "batch":batch, "disabled": 0})[0]
student_batch_doc = frappe.get_doc("Student Group", student_batch.name)
student_batch_doc.append("students", {"student": student, "student_name": student_name,
"group_roll_number":len(student_batch_doc.students)+1, "active":1})
@@ -65,7 +65,7 @@ def assign_student_group(student, student_name, program, courses, batch):
def mark_student_attendance(current_date):
status = ["Present", "Absent"]
for d in frappe.db.get_list("Student Group", filters={"group_based_on": "Batch"}):
for d in frappe.db.get_list("Student Group", filters={"group_based_on": "Batch", "disabled": 0}):
students = get_student_group_students(d.name)
for stud in students:
make_attendance_records(stud.student, stud.student_name, status[weighted_choice([9,4])], None, d.name, current_date)
@@ -77,7 +77,7 @@ def make_fees():
def make_assessment_plan(date):
for d in range(1,4):
random_group = get_random("Student Group", {"group_based_on": "Course"}, True)
random_group = get_random("Student Group", {"group_based_on": "Course", "disabled": 0}, True)
doc = frappe.new_doc("Assessment Plan")
doc.student_group = random_group.name
doc.course = random_group.course

View File

@@ -88,16 +88,14 @@ def make_attendance_records(student, student_name, status, course_schedule=None,
:param course_schedule: Course Schedule.
:param status: Status (Present/Absent)
"""
student_attendance_list = frappe.get_list("Student Attendance", fields = ['name'], filters = {
student_attendance = frappe.get_doc({
"doctype": "Student Attendance",
"student": student,
"course_schedule": course_schedule,
"student_group": student_group,
"date": date
})
if student_attendance_list:
student_attendance = frappe.get_doc("Student Attendance", student_attendance_list[0])
else:
if not student_attendance:
student_attendance = frappe.new_doc("Student Attendance")
student_attendance.student = student
student_attendance.student_name = student_name

View File

@@ -195,6 +195,38 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "0",
"description": "If enabled, field Academic Term will be Mandatory in Program Enrollment Tool.",
"fieldname": "academic_term_reqd",
"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": "Make Academic Term Mandatory",
"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,
@@ -267,7 +299,7 @@
"issingle": 1,
"istable": 0,
"max_attachments": 0,
"modified": "2017-11-28 15:45:30.324324",
"modified": "2018-07-26 04:43:35.406690",
"modified_by": "Administrator",
"module": "Education",
"name": "Education Settings",

View File

@@ -31,7 +31,8 @@ frappe.ui.form.on('Fee Schedule', {
return {
"program": frm.doc.program,
"academic_term": frm.doc.academic_term,
"academic_year": frm.doc.academic_year
"academic_year": frm.doc.academic_year,
"disabled": 0
};
});
frappe.realtime.on("fee_schedule_progress", function(data) {

View File

@@ -671,7 +671,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2017-12-27 10:59:36.838548",
"modified": "2018-07-26 04:44:03.781418",
"modified_by": "Administrator",
"module": "Education",
"name": "Program Enrollment",

View File

@@ -26,7 +26,6 @@ class ProgramEnrollment(Document):
"student": self.student,
"program": self.program,
"academic_year": self.academic_year,
"academic_term": self.academic_term,
"docstatus": ("<", 2),
"name": ("!=", self.name)
})
@@ -86,7 +85,6 @@ def get_program_courses(doctype, txt, searchfield, start, page_len, filters):
"program": filters['program']
})
@frappe.whitelist()
def get_students(doctype, txt, searchfield, start, page_len, filters):
if not filters.get("academic_term"):

View File

@@ -5,6 +5,9 @@ frappe.ui.form.on("Program Enrollment Tool", {
setup: function(frm) {
frm.add_fetch("student", "title", "student_name");
frm.add_fetch("student_applicant", "title", "student_name");
if(frm.doc.__onload && frm.doc.__onload.academic_term_reqd) {
frm.toggle_reqd("academic_term", true);
}
},
"refresh": function(frm) {

View File

@@ -513,7 +513,7 @@
"issingle": 1,
"istable": 0,
"max_attachments": 0,
"modified": "2018-01-02 11:59:40.230689",
"modified": "2018-07-26 04:44:13.232146",
"modified_by": "Administrator",
"module": "Education",
"name": "Program Enrollment Tool",

View File

@@ -7,8 +7,13 @@ import frappe
from frappe import _
from frappe.model.document import Document
from erpnext.education.api import enroll_student
from frappe.utils import cint
class ProgramEnrollmentTool(Document):
def onload(self):
academic_term_reqd = cint(frappe.db.get_single_value('Education Settings', 'academic_term_reqd'))
self.set_onload("academic_term_reqd", academic_term_reqd)
def get_students(self):
students = []
if not self.get_students_from:

View File

@@ -3,7 +3,7 @@
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 0,
"autoname": "SA.######",
"autoname": "",
"beta": 0,
"creation": "2015-11-05 15:20:23.045996",
"custom": 0,
@@ -40,7 +40,7 @@
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"search_index": 1,
"set_only_once": 0,
"unique": 0
},
@@ -101,7 +101,7 @@
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"search_index": 1,
"set_only_once": 0,
"unique": 0
},
@@ -239,7 +239,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-11-10 19:09:51.041960",
"modified": "2018-07-27 10:48:22.301531",
"modified_by": "Administrator",
"module": "Education",
"name": "Student Attendance",

View File

@@ -7,7 +7,8 @@ frappe.ui.form.on('Student Attendance Tool', {
frm.set_query("student_group", function() {
return {
"filters": {
"group_based_on": frm.doc.group_based_on
"group_based_on": frm.doc.group_based_on,
"disabled": 0
}
};
});

View File

@@ -293,6 +293,37 @@
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "0",
"fieldname": "disabled",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Disabled",
"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,
@@ -459,7 +490,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2017-11-10 19:09:37.370864",
"modified": "2018-07-26 04:17:10.836912",
"modified_by": "Administrator",
"module": "Education",
"name": "Student Group",

View File

@@ -33,7 +33,7 @@ class StudentGroup(Document):
program_enrollment = get_program_enrollment(self.academic_year, self.academic_term, self.program, self.batch, self.course)
students = [d.student for d in program_enrollment] if program_enrollment else []
for d in self.students:
if not frappe.db.get_value("Student", d.student, "enabled") and d.active:
if not frappe.db.get_value("Student", d.student, "enabled") and d.active and not self.disabled:
frappe.throw(_("{0} - {1} is inactive student".format(d.group_roll_number, d.student_name)))
if (self.group_based_on == "Batch") and cint(frappe.defaults.get_defaults().validate_batch)\

View File

@@ -99,7 +99,7 @@ def get_guardian_map(student_list):
def get_student_roll_no(academic_year, program, batch):
student_group = frappe.get_all("Student Group",
filters={"academic_year":academic_year, "program":program, "batch":batch})
filters={"academic_year":academic_year, "program":program, "batch":batch, "disabled": 0})
if student_group:
roll_no_dict = dict(frappe.db.sql('''select student, group_roll_number from `tabStudent Group Student` where parent=%s''',
(student_group[0].name)))

View File

@@ -34,7 +34,7 @@ class LabTestTemplate(Document):
# remove template refernce from item and disable item
if(self.item):
try:
frappe.delete_doc("Item",self.item)
frappe.delete_doc("Item",self.item, force=True)
except Exception:
frappe.throw("""Not permitted. Please disable the Test Template""")

View File

@@ -292,10 +292,10 @@ def get_events(start, end, filters=None):
conditions = get_event_conditions("Patient Appointment", filters)
data = frappe.db.sql("""select name, patient, physician, status,
duration, timestamp(appointment_date, appointment_time) as
'start' from `tabPatient Appointment` where
'appointment_date' from `tabPatient Appointment` where
(appointment_date between %(start)s and %(end)s)
and docstatus < 2 {conditions}""".format(conditions=conditions),
{"start": start, "end": end}, as_dict=True, update={"allDay": 0})
for item in data:
item.end = item.start + datetime.timedelta(minutes = item.duration)
item.appointment_datetime = item.appointment_date + datetime.timedelta(minutes = item.duration)
return data

View File

@@ -24,6 +24,7 @@ cur_frm.cscript.refresh = function(doc,cdt,cdn){
}
cur_frm.cscript.kra_template = function(doc, dt, dn) {
doc.goals = [];
erpnext.utils.map_current_doc({
method: "erpnext.hr.doctype.appraisal.appraisal.fetch_appraisal_template",
source_name: cur_frm.doc.kra_template,

View File

@@ -71,19 +71,22 @@ frappe.ui.form.on('Employee Loan', {
}
})
},
mode_of_payment: function (frm) {
frappe.call({
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.get_bank_cash_account",
args: {
"mode_of_payment": frm.doc.mode_of_payment,
"company": frm.doc.company
},
callback: function (r, rt) {
if (r.message) {
frm.set_value("payment_account", r.message.account);
if (frm.doc.mode_of_payment && frm.doc.company) {
frappe.call({
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.get_bank_cash_account",
args: {
"mode_of_payment": frm.doc.mode_of_payment,
"company": frm.doc.company
},
callback: function (r, rt) {
if (r.message) {
frm.set_value("payment_account", r.message.account);
}
}
}
});
});
}
},
employee_loan_application: function (frm) {

View File

@@ -29,8 +29,13 @@ class EmployeeLoanApplication(Document):
if self.repayment_method == "Repay Fixed Amount per Period":
monthly_interest_rate = flt(self.rate_of_interest) / (12 *100)
if monthly_interest_rate:
monthly_interest_amount = self.loan_amount * monthly_interest_rate
if monthly_interest_amount >= self.repayment_amount:
frappe.throw(_("Repayment amount {} should be greater than monthly interest amount {}").
format(self.repayment_amount, monthly_interest_amount))
self.repayment_periods = math.ceil((math.log(self.repayment_amount) -
math.log(self.repayment_amount - (self.loan_amount*monthly_interest_rate))) /
math.log(self.repayment_amount - (monthly_interest_amount))) /
(math.log(1 + monthly_interest_rate)))
else:
self.repayment_periods = self.loan_amount / self.repayment_amount

View File

@@ -30,7 +30,7 @@ class ExpenseClaim(AccountsController):
self.validate_expense_approver()
self.calculate_total_amount()
set_employee_name(self)
self.set_expense_account()
self.set_expense_account(validate=True)
self.set_payable_account()
self.set_cost_center()
self.set_status()
@@ -45,8 +45,9 @@ class ExpenseClaim(AccountsController):
}[cstr(self.docstatus or 0)]
paid_amount = flt(self.total_amount_reimbursed) + flt(self.total_advance_amount)
precision = self.precision("total_sanctioned_amount")
if (self.is_paid or (flt(self.total_sanctioned_amount) > 0
and flt(self.total_sanctioned_amount) == paid_amount)) \
and flt(self.total_sanctioned_amount, precision) == flt(paid_amount, precision))) \
and self.docstatus == 1 and self.approval_status == 'Approved':
self.status = "Paid"
elif flt(self.total_sanctioned_amount) > 0 and self.docstatus == 1 and self.approval_status == 'Approved':
@@ -234,9 +235,9 @@ class ExpenseClaim(AccountsController):
if flt(d.sanctioned_amount) > flt(d.claim_amount):
frappe.throw(_("Sanctioned Amount cannot be greater than Claim Amount in Row {0}.").format(d.idx))
def set_expense_account(self):
def set_expense_account(self, validate=False):
for expense in self.expenses:
if not expense.default_account:
if not expense.default_account or not validate:
expense.default_account = get_expense_claim_account(expense.expense_type, self.company)["account"]
def update_reimbursed_amount(doc):

View File

@@ -60,6 +60,7 @@ class LeaveApplication(Document):
def on_cancel(self):
# notify leave applier about cancellation
self.notify_employee("cancelled")
self.cancel_attendance()
def validate_dates(self):
if self.from_date and self.to_date and (getdate(self.to_date) < getdate(self.from_date)):
@@ -116,7 +117,7 @@ class LeaveApplication(Document):
frappe.db.sql("""update `tabAttendance` set status = %s, leave_type = %s\
where name = %s""",(status, self.leave_type, d.name))
elif self.to_date <= nowdate():
elif getdate(self.to_date) <= getdate(nowdate()):
for dt in daterange(getdate(self.from_date), getdate(self.to_date)):
date = dt.strftime("%Y-%m-%d")
if not date == self.half_day_date:
@@ -138,6 +139,13 @@ class LeaveApplication(Document):
doc.insert(ignore_permissions=True)
doc.submit()
def cancel_attendance(self):
if self.docstatus == 2:
attendance = frappe.db.sql("""select name from `tabAttendance` where employee = %s\
and (attendance_date between %s and %s) and docstatus < 2 and status in ('On Leave', 'Half Day')""",(self.employee, self.from_date, self.to_date), as_dict=1)
for name in attendance:
frappe.db.set_value("Attendance", name, "docstatus", 2)
def validate_salary_processed_days(self):
if not frappe.db.get_value("Leave Type", self.leave_type, "is_lwp"):
return
@@ -347,7 +355,7 @@ def get_approvers(doctype, txt, searchfield, start, page_len, filters):
@frappe.whitelist()
def get_number_of_leave_days(employee, leave_type, from_date, to_date, half_day = None, half_day_date = None):
number_of_days = 0
if half_day == 1:
if cint(half_day) == 1:
if from_date == to_date:
number_of_days = 0.5
else:

View File

@@ -14,8 +14,12 @@ from erpnext.utilities.transaction_base import TransactionBase
from frappe.utils.background_jobs import enqueue
class SalarySlip(TransactionBase):
def __init__(self, *args, **kwargs):
super(SalarySlip, self).__init__(*args, **kwargs)
self.series = 'Sal Slip/{0}/.#####'.format(self.employee)
def autoname(self):
self.name = make_autoname('Sal Slip/' +self.employee + '/.#####')
self.name = make_autoname(self.series)
def validate(self):
self.status = self.get_status()
@@ -418,6 +422,10 @@ class SalarySlip(TransactionBase):
self.set_status()
self.update_status()
def on_trash(self):
from frappe.model.naming import revert_series_if_last
revert_series_if_last(self.series, self.name)
def email_salary_slip(self):
receiver = frappe.db.get_value("Employee", self.employee, "prefered_email")

View File

@@ -24,6 +24,7 @@ def get_columns(leave_types):
]
for leave_type in leave_types:
columns.append(_(leave_type) + " " + _("Opening") + ":Float:160")
columns.append(_(leave_type) + " " + _("Taken") + ":Float:160")
columns.append(_(leave_type) + " " + _("Balance") + ":Float:160")
@@ -32,6 +33,7 @@ def get_columns(leave_types):
def get_data(filters, leave_types):
user = frappe.session.user
allocation_records_based_on_to_date = get_leave_allocation_records(filters.to_date)
allocation_records_based_on_from_date = get_leave_allocation_records(filters.from_date)
active_employees = frappe.get_all("Employee",
filters = { "status": "Active", "company": filters.company},
@@ -48,13 +50,17 @@ def get_data(filters, leave_types):
# leaves taken
leaves_taken = get_approved_leaves_for_period(employee.name, leave_type,
filters.from_date, filters.to_date)
# opening balance
opening = get_leave_balance_on(employee.name, leave_type, filters.from_date,
allocation_records_based_on_from_date.get(employee.name, frappe._dict()))
# closing balance
closing = get_leave_balance_on(employee.name, leave_type, filters.to_date,
allocation_records_based_on_to_date.get(employee.name, frappe._dict()))
row += [leaves_taken, closing]
row += [opening, leaves_taken, closing]
data.append(row)
return data
return data

View File

@@ -9,7 +9,7 @@ from frappe.utils import flt,cstr
from erpnext.accounts.report.financial_statements import get_period_list
def execute(filters=None):
columns, data = [], []
columns, data, chart = [], [], []
if filters.get('fiscal_year'):
company = erpnext.get_default_company()
period_list = get_period_list(filters.get('fiscal_year'), filters.get('fiscal_year'),"Monthly", company)

View File

@@ -158,7 +158,7 @@ class BOM(WebsiteGenerator):
if not self.buying_price_list:
frappe.throw(_("Please select Price List"))
rate = frappe.db.get_value("Item Price", {"price_list": self.buying_price_list,
"item_code": arg["item_code"]}, "price_list_rate")
"item_code": arg["item_code"]}, "price_list_rate") or 0.0
price_list_currency = frappe.db.get_value("Price List",
self.buying_price_list, "currency")
@@ -652,4 +652,4 @@ def get_boms_in_bottom_up_order(bom_no=None):
bom_list.append(child_bom)
count += 1
return bom_list
return bom_list

View File

@@ -22,6 +22,21 @@ frappe.ui.form.on('BOM Update Tool', {
frm.disable_save();
},
replace: function(frm) {
if (frm.doc.current_bom && frm.doc.new_bom) {
frappe.call({
method: "erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.enqueue_replace_bom",
freeze: true,
args: {
args: {
"current_bom": frm.doc.current_bom,
"new_bom": frm.doc.new_bom
}
}
});
}
},
update_latest_price_in_all_boms: function() {
frappe.call({
method: "erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.enqueue_update_cost",

View File

@@ -123,7 +123,7 @@
"label": "Replace",
"length": 0,
"no_copy": 0,
"options": "replace_bom",
"options": "",
"permlevel": 0,
"print_hide": 0,
"print_hide_if_no_value": 0,
@@ -208,7 +208,7 @@
"issingle": 1,
"istable": 0,
"max_attachments": 0,
"modified": "2017-07-31 18:08:05.919276",
"modified": "2018-07-02 16:17:09.014102",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "BOM Update Tool",

View File

@@ -3,9 +3,10 @@
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
import frappe, json
from frappe.utils import cstr, flt
from frappe import _
from six import string_types
from erpnext.manufacturing.doctype.bom.bom import get_boms_in_bottom_up_order
from frappe.model.document import Document
@@ -17,12 +18,14 @@ class BOMUpdateTool(Document):
updated_bom = []
for bom in bom_list:
bom_obj = frappe.get_doc("BOM", bom)
bom_obj.get_doc_before_save()
updated_bom = bom_obj.update_cost_and_exploded_items(updated_bom)
bom_obj.calculate_cost()
bom_obj.update_parent_cost()
bom_obj.db_update()
frappe.msgprint(_("BOM replaced"))
if (getattr(bom_obj.meta, 'track_changes', False)
and bom_obj._doc_before_save and not bom_obj.flags.ignore_version):
bom_obj.save_version()
def validate_bom(self):
if cstr(self.current_bom) == cstr(self.new_bom):
@@ -54,6 +57,14 @@ class BOMUpdateTool(Document):
return bom_list
@frappe.whitelist()
def enqueue_replace_bom(args):
if isinstance(args, string_types):
args = json.loads(args)
frappe.enqueue("erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.replace_bom", args=args)
frappe.msgprint(_("Queued for replacing the BOM. It may take a few minutes."))
@frappe.whitelist()
def enqueue_update_cost():
frappe.enqueue("erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.update_cost")
@@ -63,6 +74,14 @@ def update_latest_price_in_all_boms():
if frappe.db.get_single_value("Manufacturing Settings", "update_bom_costs_automatically"):
update_cost()
def replace_bom(args):
args = frappe._dict(args)
doc = frappe.get_doc("BOM Update Tool")
doc.current_bom = args.current_bom
doc.new_bom = args.new_bom
doc.replace_bom()
def update_cost():
bom_list = get_boms_in_bottom_up_order()
for bom in bom_list:

View File

@@ -15,6 +15,7 @@
"fields": [
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -42,10 +43,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -73,10 +76,12 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -102,10 +107,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -133,15 +140,17 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "membership_validaty_section",
"fieldname": "membership_validity_section",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
@@ -150,7 +159,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "membership validaty section",
"label": "Validity",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -163,10 +172,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -193,10 +204,12 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -223,10 +236,12 @@
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -252,10 +267,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -282,10 +299,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -312,10 +331,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -342,10 +363,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -373,10 +396,12 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -403,6 +428,7 @@
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
@@ -416,7 +442,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-12-05 07:02:56.859408",
"modified": "2018-06-26 19:23:17.911121",
"modified_by": "Administrator",
"module": "Non Profit",
"name": "Membership",
@@ -425,7 +451,6 @@
"permissions": [
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
@@ -445,7 +470,6 @@
},
{
"amend": 0,
"apply_user_permissions": 0,
"cancel": 0,
"create": 1,
"delete": 1,
@@ -472,5 +496,6 @@
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 1,
"track_seen": 0
}
"track_seen": 0,
"track_views": 0
}

View File

@@ -266,7 +266,6 @@ erpnext.patches.v6_24.map_customer_address_to_shipping_address_on_po
erpnext.patches.v6_27.fix_recurring_order_status
erpnext.patches.v6_20x.update_product_bundle_description
erpnext.patches.v7_0.update_party_status #2016-09-22
erpnext.patches.v7_0.update_item_projected
erpnext.patches.v7_0.remove_features_setup
erpnext.patches.v7_0.update_home_page
execute:frappe.delete_doc_if_exists("Page", "financial-analytics")
@@ -503,3 +502,4 @@ erpnext.patches.v10_0.taxes_issue_with_pos
erpnext.patches.v10_0.set_qty_in_transactions_based_on_serial_no_input
erpnext.patches.v10_0.show_leaves_of_all_department_members_in_calendar
erpnext.patches.v10_0.update_status_in_purchase_receipt
erpnext.patches.v10_0.update_address_template_for_india

View File

@@ -0,0 +1,12 @@
# Copyright (c) 2017, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
from erpnext.regional.india.setup import update_address_template
def execute():
if frappe.db.get_value('Company', {'country': 'India'}, 'name'):
address_template = frappe.db.get_value('Address Template', 'India', 'template')
if not address_template or "gstin" not in address_template:
update_address_template()

View File

@@ -9,7 +9,7 @@ def execute():
def repost_bin_qty():
for bin in frappe.db.sql(""" select name from `tabBin`
where (actual_qty + ordered_qty + indented_qty + planned_qty- reserved_qty - reserved_qty_for_production) != projected_qty """, as_dict=1):
where (actual_qty + ordered_qty + indented_qty + planned_qty - reserved_qty - reserved_qty_for_production - reserved_qty_for_sub_contract) != projected_qty """, as_dict=1):
bin_doc = frappe.get_doc('Bin', bin.name)
bin_doc.set_projected_qty()
bin_doc.db_set("projected_qty", bin_doc.projected_qty, update_modified = False)

View File

@@ -1,7 +0,0 @@
import frappe
def execute():
frappe.reload_doctype("Item")
from erpnext.stock.doctype.bin.bin import update_item_projected_qty
for item in frappe.get_all("Item", filters={"is_stock_item": 1}):
update_item_projected_qty(item.name)

View File

@@ -1,7 +1,8 @@
import frappe
def execute():
frappe.reload_doc("stock", "doctype", "bin")
bins = frappe.db.sql("select name from `tabBin` where reserved_qty_for_production > 0")
for d in bins:
bin_doc = frappe.get_doc("Bin", d[0])
bin_doc.update_reserved_qty_for_production()
bin_doc.update_reserved_qty_for_production()

View File

@@ -4,7 +4,7 @@
from __future__ import unicode_literals
import frappe
from frappe.utils import flt, getdate, get_url
from frappe.utils import flt, getdate, get_url, now
from frappe import _
from frappe.model.document import Document
@@ -81,38 +81,79 @@ class Project(Document):
def sync_tasks(self):
"""sync tasks and remove table"""
if not hasattr(self, "deleted_task_list"):
self.set("deleted_task_list", [])
if self.flags.dont_sync_tasks: return
task_names = []
existing_task_data = {}
for d in frappe.get_all('Project Task',
fields = ["title", "status", "start_date", "end_date", "description", "task_weight", "task_id"],
filters = {'parent': self.name}):
existing_task_data.setdefault(d.task_id, d)
for t in self.tasks:
if t.task_id:
task = frappe.get_doc("Task", t.task_id)
else:
task = frappe.new_doc("Task")
task.project = self.name
task.update({
"subject": t.title,
"status": t.status,
"exp_start_date": t.start_date,
"exp_end_date": t.end_date,
"description": t.description,
"task_weight": t.task_weight
})
self.map_custom_fields(t, task)
if not t.task_id or self.is_row_updated(t, existing_task_data):
task.update({
"subject": t.title,
"status": t.status,
"exp_start_date": t.start_date,
"exp_end_date": t.end_date,
"description": t.description,
"task_weight": t.task_weight
})
task.flags.ignore_links = True
task.flags.from_project = True
task.flags.ignore_feed = True
task.save(ignore_permissions = True)
task_names.append(task.name)
self.map_custom_fields(t, task)
task.flags.ignore_links = True
task.flags.from_project = True
task.flags.ignore_feed = True
if t.task_id:
task.update({
"modified_by": frappe.session.user,
"modified": now()
})
task.run_method("validate")
task.db_update()
else:
task.save(ignore_permissions = True)
task_names.append(task.name)
else:
task_names.append(task.name)
# delete
for t in frappe.get_all("Task", ["name"], {"project": self.name, "name": ("not in", task_names)}):
frappe.delete_doc("Task", t.name)
self.deleted_task_list.append(t.name)
def update_costing_and_percentage_complete(self):
self.update_percent_complete()
self.update_costing()
def is_row_updated(self, row, existing_task_data):
if self.get("__islocal") or not existing_task_data: return True
project_task_custom_fields = frappe.get_all("Custom Field", {"dt": "Project Task"}, "fieldname")
d = existing_task_data.get(row.task_id)
for field in project_task_custom_fields:
if row.get(field) != d.get(field):
return True
if (d and (row.title != d.title or row.status != d.status
or getdate(row.start_date) != getdate(d.start_date) or getdate(row.end_date) != getdate(d.end_date)
or row.description != d.description or row.task_weight != d.task_weight)):
return True
def map_custom_fields(self, source, target):
project_task_custom_fields = frappe.get_all("Custom Field", {"dt": "Project Task"}, "fieldname")
@@ -207,6 +248,9 @@ class Project(Document):
self.total_billed_amount = total_billed_amount and total_billed_amount[0][0] or 0
def after_rename(self, old_name, new_name, merge=False):
if old_name == self.copied_from:
frappe.db.set_value('Project', new_name, 'copied_from', new_name)
def send_welcome_email(self):
url = get_url("/project/?name={0}".format(self.name))
@@ -227,10 +271,19 @@ class Project(Document):
user.welcome_email_sent=1
def on_update(self):
self.delete_task()
self.load_tasks()
self.sync_tasks()
self.update_costing_and_percentage_complete()
self.update_dependencies_on_duplicated_project()
def delete_task(self):
if not self.get('deleted_task_list'): return
for d in self.get('deleted_task_list'):
frappe.delete_doc("Task", d)
self.deleted_task_list = []
def update_dependencies_on_duplicated_project(self):
if self.flags.dont_sync_tasks: return
if not self.copied_from:
@@ -251,9 +304,7 @@ class Project(Document):
continue
name = _task.name
depends_on_tasks = _task.depends_on_tasks
depends_on_tasks = [x for x in depends_on_tasks.split(',') if x]
dependency_map[task.title] = [ x['subject'] for x in frappe.get_list(
'Task Depends On', {"parent": name}, ['subject'])]
@@ -264,7 +315,8 @@ class Project(Document):
for dt in value:
dt_name = frappe.db.get_value('Task', {"subject": dt, "project": self.name })
task_doc.append('depends_on', {"task": dt_name})
task_doc.save()
task_doc.db_update()
def get_timeline_data(doctype, name):
'''Return timeline for attendance'''

View File

@@ -61,7 +61,7 @@
"label": "Status",
"length": 0,
"no_copy": 1,
"options": "Open\nWorking\nPending Review\nClosed\nCancelled",
"options": "Open\nWorking\nPending Review\nOverdue\nClosed\nCancelled",
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -371,7 +371,7 @@
"remember_last_selected_value": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"search_index": 1,
"set_only_once": 0,
"unique": 0
}
@@ -386,7 +386,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-12-19 14:49:15.886339",
"modified": "2018-07-05 19:34:31.204454",
"modified_by": "Administrator",
"module": "Projects",
"name": "Project Task",

View File

@@ -38,7 +38,7 @@
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"search_index": 1,
"set_only_once": 0,
"unique": 0
},
@@ -70,7 +70,7 @@
"remember_last_selected_value": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"search_index": 1,
"set_only_once": 0,
"unique": 0
},
@@ -1214,7 +1214,7 @@
"istable": 0,
"max_attachments": 5,
"menu_index": 0,
"modified": "2017-11-10 18:37:19.660293",
"modified": "2018-06-26 11:46:06.678115",
"modified_by": "Administrator",
"module": "Projects",
"name": "Task",

View File

@@ -420,9 +420,10 @@ def get_timesheets_list(doctype, txt, filters, limit_start, limit_page_length=20
# find customer name from contact.
customer = frappe.db.sql('''SELECT dl.link_name FROM `tabContact` AS c inner join \
`tabDynamic Link` AS dl ON c.first_name=dl.link_name WHERE c.email_id=%s''',user)
# find list of Sales Invoice for made for customer.
sales_invoice = frappe.db.sql('''SELECT name FROM `tabSales Invoice` WHERE customer = %s''',customer)
if customer:
# find list of Sales Invoice for made for customer.
sales_invoice = frappe.db.sql('''SELECT name FROM `tabSales Invoice` WHERE customer = %s''',customer)
# Return timesheet related data to web portal.
return frappe. db.sql('''SELECT ts.name, tsd.activity_type, ts.status, ts.total_billable_hours, \
tsd.sales_invoice, tsd.project FROM `tabTimesheet` AS ts inner join `tabTimesheet Detail` \

View File

@@ -26,6 +26,11 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
};
});
if (this.frm.doc.__islocal
&& frappe.meta.has_field(this.frm.doc.doctype, "disable_rounded_total")) {
this.frm.set_value("disable_rounded_total", cint(frappe.sys_defaults.disable_rounded_total));
}
/* eslint-disable */
// no idea where me is coming from
if(this.frm.get_field('shipping_address')) {
@@ -222,60 +227,6 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
tc_name: function() {
this.get_terms();
},
link_to_mrs: function() {
var my_items = [];
for (var i in cur_frm.doc.items) {
if(!cur_frm.doc.items[i].material_request){
my_items.push(cur_frm.doc.items[i].item_code);
}
}
frappe.call({
method: "erpnext.buying.utils.get_linked_material_requests",
args:{
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) {
var qty = cur_frm.doc.items[i].qty;
(r.message[0] || []).forEach(function(d) {
if (d.qty > 0 && qty > 0 && cur_frm.doc.items[i].item_code == d.item_code && !cur_frm.doc.items[i].material_request_item)
{
cur_frm.doc.items[i].material_request = d.mr_name;
cur_frm.doc.items[i].material_request_item = d.mr_item;
var my_qty = Math.min(qty, d.qty);
qty = qty - my_qty;
d.qty = d.qty - my_qty;
cur_frm.doc.items[i].stock_qty = my_qty*cur_frm.doc.items[i].conversion_factor;
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) {
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]) {
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");
}
});
}
});

View File

@@ -338,12 +338,14 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
set_item_wise_tax: function(item, tax, tax_rate, current_tax_amount) {
// store tax breakup for each item
var key = item.item_code || item.item_name;
var item_wise_tax_amount = current_tax_amount * this.frm.doc.conversion_rate;
if (tax.item_wise_tax_detail && tax.item_wise_tax_detail[key])
item_wise_tax_amount += tax.item_wise_tax_detail[key][1];
let tax_detail = tax.item_wise_tax_detail;
let key = item.item_code || item.item_name;
tax.item_wise_tax_detail[key] = [tax_rate, flt(item_wise_tax_amount, precision("base_tax_amount", tax))];
let item_wise_tax_amount = current_tax_amount * this.frm.doc.conversion_rate;
if (tax_detail && tax_detail[key])
item_wise_tax_amount += tax_detail[key][1];
tax_detail[key] = [tax_rate, flt(item_wise_tax_amount, precision("base_tax_amount", tax))];
},
round_off_totals: function(tax) {

View File

@@ -169,21 +169,25 @@ erpnext.utils.validate_mandatory = function(frm, label, value, trigger_on) {
}
erpnext.utils.get_shipping_address = function(frm, callback){
frappe.call({
method: "frappe.contacts.doctype.address.address.get_shipping_address",
args: {
company: frm.doc.company,
address: frm.doc.shipping_address
},
callback: function(r){
if(r.message){
frm.set_value("shipping_address", r.message[0]) //Address title or name
frm.set_value("shipping_address_display", r.message[1]) //Address to be displayed on the page
}
if (frm.doc.company) {
frappe.call({
method: "frappe.contacts.doctype.address.address.get_shipping_address",
args: {
company: frm.doc.company,
address: frm.doc.shipping_address
},
callback: function(r){
if(r.message){
frm.set_value("shipping_address", r.message[0]) //Address title or name
frm.set_value("shipping_address_display", r.message[1]) //Address to be displayed on the page
}
if(callback){
return callback();
if(callback){
return callback();
}
}
}
});
});
} else {
frappe.msgprint(__("Select company first"));
}
}

View File

@@ -0,0 +1,33 @@
// Copyright (c) 2016, FinByz Tech Pvt. Ltd. and contributors
// For license information, please see license.txt
/* eslint-disable */
frappe.query_reports["Eway Bill"] = {
"filters": [
{
'fieldname': 'delivery_note',
'label': __("Delivery Note"),
'fieldtype': 'Link',
'options': 'Delivery Note'
},
{
'fieldname': 'posting_date',
'label': __("Date"),
'fieldtype': 'DateRange',
'default': [frappe.datetime.nowdate(), frappe.datetime.nowdate()]
},
{
'fieldname': 'customer',
'label': __("Customer"),
'fieldtype': 'Link',
'options': 'Customer'
},
{
"fieldname":"company",
"label": __("Company"),
"fieldtype": "Link",
"options": "Company",
"default": frappe.defaults.get_user_default("Company")
},
]
}

View File

@@ -0,0 +1,32 @@
{
"add_total_row": 0,
"apply_user_permissions": 1,
"creation": "2018-07-13 19:59:18.922829",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"modified": "2018-07-19 12:08:07.400295",
"modified_by": "Administrator",
"module": "Regional",
"name": "Eway Bill",
"owner": "Administrator",
"ref_doctype": "Delivery Note",
"report_name": "Eway Bill",
"report_type": "Script Report",
"roles": [
{
"role": "Stock User"
},
{
"role": "Stock Manager"
},
{
"role": "Sales User"
},
{
"role": "Accounts User"
}
]
}

View File

@@ -0,0 +1,385 @@
# Copyright (c) 2013, FinByz Tech Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
import json
import re
from frappe import _
from frappe.utils import nowdate
def execute(filters=None):
if not filters: filters.setdefault('posting_date', [nowdate(), nowdate()])
columns, data = [], []
columns = get_columns()
data = get_data(filters)
return columns, data
def get_data(filters):
conditions = get_conditions(filters)
data = frappe.db.sql("""
SELECT
dn.name as dn_id, dn.posting_date, dn.company, dn.company_gstin, dn.customer, dn.customer_gstin, dni.item_code, dni.item_name, dni.description, dni.gst_hsn_code, dni.uom, dni.qty, dni.amount, dn.transport_mode, dn.distance, dn.transporter_name, dn.transporter, dn.lr_no, dn.lr_date, dn.vehicle_no, dn.vehicle_type, dn.company_address, dn.shipping_address_name
FROM
`tabDelivery Note` AS dn join `tabDelivery Note Item` AS dni on (dni.parent = dn.name)
WHERE
dn.docstatus < 2
%s """ % conditions, as_dict=1)
unit = {
'Bag': "BAGS",
'Bottle': "BOTTLES",
'Kg': "KILOGRAMS",
'Liter': "LITERS",
'Meter': "METERS",
'Nos': "NUMBERS",
'PKT': "PACKS",
'Roll': "ROLLS",
'Set': "SETS"
}
# Regular expression set to remove all the special characters
special_characters = "[$%^*()+\\[\]{};':\"\\|<>.?]"
for row in data:
set_defaults(row)
set_taxes(row, filters)
set_address_details(row, special_characters)
# Eway Bill accepts date as dd/mm/yyyy and not dd-mm-yyyy
row.posting_date = '/'.join(str(row.posting_date).replace("-", "/").split('/')[::-1])
row.lr_date = '/'.join(str(row.lr_date).replace("-", "/").split('/')[::-1])
row.item_name = re.sub(special_characters, " ", row.item_name)
row.description = row.item_name
row.uom = unit.get(row.uom, row.uom)
# For removing special charactes and numbers from customer.
row.customer = re.sub(special_characters[:-1] + "&0-9" + "]", "", row.customer)
return data
def get_conditions(filters):
conditions = ""
conditions += filters.get('company') and " AND dn.company = '%s' " % filters.get('company') or ""
conditions += filters.get('posting_date') and " AND dn.posting_date >= '%s' AND dn.posting_date <= '%s' " % (filters.get('posting_date')[0], filters.get('posting_date')[1]) or ""
conditions += filters.get('delivery_note') and " AND dn.name = '%s' " % filters.get('delivery_note') or ""
conditions += filters.get('customer') and " AND dn.customer = '%s' " % filters.get('customer').replace("'", "\'") or ""
return conditions
def set_defaults(row):
row.setdefault(u'supply_type', "Outward")
row.setdefault(u'sub_type', "Supply")
row.setdefault(u'doc_type', "Delivery Challan")
def set_address_details(row, special_characters):
if row.get('company_address'):
address_line1, address_line2, city, pincode, state = frappe.db.get_value("Address", row.get('company_address'), ['address_line1', 'address_line2', 'city', 'pincode', 'state'])
row.update({'from_address_1': re.sub(special_characters, "", address_line1 or '')})
row.update({'from_address_2': re.sub(special_characters, "", address_line2 or '')})
row.update({'from_place': city and city.upper() or ''})
row.update({'from_pin_code': pincode and pincode.replace(" ", "") or ''})
row.update({'from_state': state and state.upper() or ''})
row.update({'dispatch_state': row.from_state})
if row.get('shipping_address_name'):
address_line1, address_line2, city, pincode, state = frappe.db.get_value("Address", row.get('shipping_address_name'), ['address_line1', 'address_line2', 'city', 'pincode', 'state'])
row.update({'to_address_1': re.sub(special_characters, "", address_line1 or '')})
row.update({'to_address_2': re.sub(special_characters, "", address_line2 or '')})
row.update({'to_place': city and city.upper() or ''})
row.update({'to_pin_code': pincode and pincode.replace(" ", "") or ''})
row.update({'to_state': state and state.upper() or ''})
row.update({'ship_to_state': row.to_state})
def set_taxes(row, filters):
taxes = frappe.get_list("Sales Taxes and Charges",
filters={
'parent': row.dn_id
},
fields=('item_wise_tax_detail', 'account_head'))
account_list = ["cgst_account", "sgst_account", "igst_account", "cess_account"]
taxes_list = frappe.get_list("GST Account",
filters={
"parent": "GST Settings",
"company": filters.company
},
fields=account_list)
item_tax_rate = {}
for tax in taxes:
item_wise_tax = json.loads(tax.item_wise_tax_detail)
item_tax_rate[tax.account_head] = item_wise_tax.get(row.item_code)
tax_rate = []
tax = taxes_list[0]
for key in account_list:
if tax[key] not in item_tax_rate.keys():
item_tax_rate[tax[key]] = [0.0, 0.0]
tax_rate.append(str(item_tax_rate[tax[key]][0]))
row.update({key[:5] + "amount": round(item_tax_rate.get(tax[key], 0.0)[1], 2)})
item_tax_rate.pop(tax[key])
row.amount = float(row.amount) + sum(i[1] for i in item_tax_rate.values())
row.update({'tax_rate': '+'.join(tax_rate)})
def get_columns():
columns = [
{
"fieldname": "supply_type",
"label": _("Supply Type"),
"fieldtype": "Data",
"width": 100
},
{
"fieldname": "sub_type",
"label": _("Sub Type"),
"fieldtype": "Data",
"width": 100
},
{
"fieldname": "doc_type",
"label": _("Doc Type"),
"fieldtype": "Data",
"width": 100
},
{
"fieldname": "dn_id",
"label": _("Doc Name"),
"fieldtype": "Link",
"options": "Delivery Note",
"width": 140
},
{
"fieldname": "posting_date",
"label": _("Doc Date"),
"fieldtype": "Data",
"width": 100
},
{
"fieldname": "company",
"label": _("From Party Name"),
"fieldtype": "Link",
"options": "Company",
"width": 120
},
{
"fieldname": "company_gstin",
"label": _("From GSTIN"),
"fieldtype": "Data",
"width": 100
},
{
"fieldname": "from_address_1",
"label": _("From Address 1"),
"fieldtype": "Data",
"width": 120
},
{
"fieldname": "from_address_2",
"label": _("From Address 2"),
"fieldtype": "Data",
"width": 120
},
{
"fieldname": "from_place",
"label": _("From Place"),
"fieldtype": "Data",
"width": 80
},
{
"fieldname": "from_pin_code",
"label": _("From Pin Code"),
"fieldtype": "Data",
"width": 80
},
{
"fieldname": "from_state",
"label": _("From State"),
"fieldtype": "Data",
"width": 80
},
{
"fieldname": "dispatch_state",
"label": _("Dispatch State"),
"fieldtype": "Data",
"width": 100
},
{
"fieldname": "customer",
"label": _("To Party Name"),
"fieldtype": "Data",
"width": 120
},
{
"fieldname": "customer_gstin",
"label": _("To GSTIN"),
"fieldtype": "Data",
"width": 120
},
{
"fieldname": "to_address_1",
"label": _("To Address 1"),
"fieldtype": "Data",
"width": 120
},
{
"fieldname": "to_address_2",
"label": _("To Address 2"),
"fieldtype": "Data",
"width": 120
},
{
"fieldname": "to_place",
"label": _("To Place"),
"fieldtype": "Data",
"width": 80
},
{
"fieldname": "to_pin_code",
"label": _("To Pin Code"),
"fieldtype": "Data",
"width": 80
},
{
"fieldname": "to_state",
"label": _("To State"),
"fieldtype": "Data",
"width": 80
},
{
"fieldname": "ship_to_state",
"label": _("Ship To State"),
"fieldtype": "Data",
"width": 100
},
{
"fieldname": "item_name",
"label": _("Product"),
"fieldtype": "Link",
"options": "Item",
"width": 120
},
{
"fieldname": "description",
"label": _("Description"),
"fieldtype": "Data",
"width": 100
},
{
"fieldname": "gst_hsn_code",
"label": _("HSN"),
"fieldtype": "Data",
"width": 120
},
{
"fieldname": "uom",
"label": _("Unit"),
"fieldtype": "Data",
"width": 100
},
{
"fieldname": "qty",
"label": _("Qty"),
"fieldtype": "Float",
"width": 100
},
{
"fieldname": "amount",
"label": _("Accessable Value"),
"fieldtype": "Float",
"width": 120
},
{
"fieldname": "tax_rate",
"label": _("Tax Rate"),
"fieldtype": "Data",
"width": 100
},
{
"fieldname": "cgst_amount",
"label": _("CGST Amount"),
"fieldtype": "Data",
"width": 100
},
{
"fieldname": "sgst_amount",
"label": _("SGST Amount"),
"fieldtype": "Data",
"width": 100
},
{
"fieldname": "igst_amount",
"label": _("IGST Amount"),
"fieldtype": "Data",
"width": 100
},
{
"fieldname": "cess_amount",
"label": _("CESS Amount"),
"fieldtype": "Data",
"width": 100
},
{
"fieldname": "transport_mode",
"label": _("Transport Mode"),
"fieldtype": "Data",
"width": 100
},
{
"fieldname": "distance",
"label": _("Distance"),
"fieldtype": "Data",
"width": 100
},
{
"fieldname": "transporter_name",
"label": _("Transporter Name"),
"fieldtype": "Data",
"width": 120
},
{
"fieldname": "transporter_id",
"label": _("Transporter ID"),
"fieldtype": "Data",
"width": 100
},
{
"fieldname": "lr_no",
"label": _("Transporter Doc No"),
"fieldtype": "Data",
"width": 120
},
{
"fieldname": "lr_date",
"label": _("Transporter Date"),
"fieldtype": "Data",
"width": 120
},
{
"fieldname": "vehicle_no",
"label": _("Vehicle No"),
"fieldtype": "Data",
"width": 100
},
{
"fieldname": "vehicle_type",
"label": _("Vehicle Type"),
"fieldtype": "Data",
"width": 100
},
]
return columns

View File

@@ -144,17 +144,10 @@ class Gstr1Report(object):
""" % (self.doctype, ', '.join(['%s']*len(self.invoices))), tuple(self.invoices), as_dict=1)
for d in items:
item_details = {}
item_details[d.item_code] = d.base_net_amount
if d.parent in self.invoice_items:
parent_dict = self.invoice_items[d.parent]
if d.item_code in parent_dict:
item_details[d.item_code] += parent_dict[d.item_code]
else:
item_details.update(parent_dict)
self.invoice_items[d.parent] = item_details
if d.item_code not in self.invoice_items.get(d.parent, {}):
self.invoice_items.setdefault(d.parent, {}).setdefault(d.item_code,
sum(i.get('base_net_amount', 0) for i in items
if i.item_code == d.item_code and i.parent == d.parent))
def get_items_based_on_tax_rate(self):
self.tax_details = frappe.db.sql("""

View File

@@ -17,7 +17,7 @@ def setup(company=None, patch=True):
def make_custom_fields():
invoice_fields = [
dict(fieldname='vat_section', label='VAT Details', fieldtype='Section Break',
insert_after='select_print_heading', print_hide=1, collapsible=1),
insert_after='group_same_items', print_hide=1, collapsible=1),
dict(fieldname='permit_no', label='Permit Number',
fieldtype='Data', insert_after='vat_section', print_hide=1),
dict(fieldname='reverse_charge_applicable', label='Reverse Charge Applicable',

View File

@@ -79,7 +79,7 @@ class Customer(TransactionBase):
def update_customer_groups(self):
ignore_doctypes = ["Lead", "Opportunity", "POS Profile", "Tax Rule", "Pricing Rule"]
if frappe.flags.customer_group_changed:
update_linked_doctypes('Customer', self.name, 'Customer Group',
update_linked_doctypes('Customer', frappe.db.escape(self.name), 'Customer Group',
self.customer_group, ignore_doctypes)
def create_primary_contact(self):

View File

@@ -186,7 +186,7 @@ cur_frm.cscript['Declare Order Lost'] = function(){
return cur_frm.call({
method: "declare_order_lost",
doc: cur_frm.doc,
args: args.reason,
args: args,
callback: function(r) {
if(r.exc) {
frappe.msgprint(__("There were errors."));

View File

@@ -1076,7 +1076,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_bulk_edit": 1,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -2911,7 +2911,7 @@
"istable": 0,
"max_attachments": 1,
"menu_index": 0,
"modified": "2018-05-28 03:23:15.354674",
"modified": "2018-07-06 03:23:15.354674",
"modified_by": "Administrator",
"module": "Selling",
"name": "Quotation",

View File

@@ -70,10 +70,10 @@ class Quotation(SellingController):
opp.status = None
opp.set_status(update=True)
def declare_order_lost(self, arg):
def declare_order_lost(self, reason):
if not self.has_sales_order():
frappe.db.set(self, 'status', 'Lost')
frappe.db.set(self, 'order_lost_reason', arg)
frappe.db.set(self, 'order_lost_reason', reason)
self.update_opportunity()
self.update_lead()
else:
@@ -185,6 +185,7 @@ def _make_sales_invoice(source_name, target_doc=None, ignore_permissions=False):
target.run_method("calculate_taxes_and_totals")
def update_item(obj, target, source_parent):
target.cost_center = None
target.stock_qty = flt(obj.qty) * flt(obj.conversion_factor)
doclist = get_mapped_doc("Quotation", source_name, {

View File

@@ -1131,7 +1131,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_bulk_edit": 1,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -3529,7 +3529,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-01-12 15:56:12.483019",
"modified": "2018-07-06 15:56:12.483019",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Order",

View File

@@ -60,6 +60,21 @@ class TestSalesOrder(unittest.TestCase):
si1 = make_sales_invoice(so.name)
self.assertEquals(len(si1.get("items")), 0)
def test_so_billed_amount_against_return_entry(self):
from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_sales_return
so = make_sales_order(do_not_submit=True)
so.submit()
si = make_sales_invoice(so.name)
si.insert()
si.submit()
si1 = make_sales_return(si.name)
si1.update_billed_amount_in_sales_order = 1
si1.submit()
so.load_from_db()
self.assertEquals(so.per_billed, 0)
def test_make_sales_invoice_with_terms(self):
so = make_sales_order(do_not_submit=True)

View File

@@ -7,12 +7,12 @@
"doctype": "Report",
"idx": 3,
"is_standard": "Yes",
"modified": "2017-02-24 20:08:44.614482",
"modified": "2018-06-18 16:42:05.860309",
"modified_by": "Administrator",
"module": "Selling",
"name": "Item-wise Sales History",
"owner": "Administrator",
"query": "select\n so_item.item_code as \"Item Code:Link/Item:120\",\n\tso_item.item_name as \"Item Name::120\",\n so_item.item_group as \"Item Group:Link/Item Group:120\",\n\tso_item.description as \"Description::150\",\n\tso_item.qty as \"Qty:Float:100\",\n\tso_item.stock_uom as \"UOM:Link/UOM:80\",\n\tso_item.base_rate as \"Rate:Currency:120\",\n\tso_item.base_amount as \"Amount:Currency:120\",\n\tso.name as \"Sales Order:Link/Sales Order:120\",\n\tso.transaction_date as \"Transaction Date:Date:140\",\n\tso.customer as \"Customer:Link/Customer:130\",\n cu.customer_name as \"Customer Name::150\",\n\tso.territory as \"Territory:Link/Territory:130\",\n so.project as \"Project:Link/Project:130\",\n\tifnull(so_item.delivered_qty, 0) as \"Delivered Qty:Float:120\",\n\tifnull(so_item.billed_amt, 0) as \"Billed Amount:Currency:120\",\n\tso.company as \"Company:Link/Company:\"\nfrom\n\t`tabSales Order` so, `tabSales Order Item` so_item, `tabCustomer` cu\nwhere\n\tso.name = so_item.parent and so.customer=cu.name\n\tand so.docstatus = 1\norder by so.name desc",
"query": "select\n so_item.item_code as \"Item Code:Link/Item:120\",\n\tso_item.item_name as \"Item Name::120\",\n so_item.item_group as \"Item Group:Link/Item Group:120\",\n\tso_item.description as \"Description::150\",\n\tso_item.qty as \"Qty:Data:100\",\n\tso_item.uom as \"UOM:Link/UOM:80\",\n\tso_item.base_rate as \"Rate:Currency:120\",\n\tso_item.base_amount as \"Amount:Currency:120\",\n\tso.name as \"Sales Order:Link/Sales Order:120\",\n\tso.transaction_date as \"Transaction Date:Date:140\",\n\tso.customer as \"Customer:Link/Customer:130\",\n cu.customer_name as \"Customer Name::150\",\n\tso.territory as \"Territory:Link/Territory:130\",\n so.project as \"Project:Link/Project:130\",\n\tifnull(so_item.delivered_qty, 0) as \"Delivered Qty:Float:120\",\n\tifnull(so_item.billed_amt, 0) as \"Billed Amount:Currency:120\",\n\tso.company as \"Company:Link/Company:\"\nfrom\n\t`tabSales Order` so, `tabSales Order Item` so_item, `tabCustomer` cu\nwhere\n\tso.name = so_item.parent and so.customer=cu.name\n\tand so.docstatus = 1\norder by so.name desc",
"ref_doctype": "Sales Order",
"report_name": "Item-wise Sales History",
"report_type": "Query Report",

View File

@@ -59,5 +59,11 @@ frappe.query_reports["Sales Person-wise Transaction Summary"] = {
fieldtype: "Link",
options: "Territory",
},
{
fieldname:"show_return_entries",
label: __("Show Return Entries"),
fieldtype: "Check",
default: 0,
},
]
}

View File

@@ -15,7 +15,7 @@ def execute(filters=None):
data = []
for d in entries:
if d.stock_qty > 0:
if d.stock_qty > 0 or filters.get('show_return_entries', 0):
data.append([
d.name, d.customer, d.territory, d.posting_date, d.item_code,
item_details.get(d.item_code, {}).get("item_group"), item_details.get(d.item_code, {}).get("brand"),

View File

@@ -40,7 +40,7 @@ class AuthorizationControl(TransactionBase):
chk = 1
add_cond1,add_cond2 = '',''
if based_on == 'Itemwise Discount':
add_cond1 += " and master_name = '"+cstr(item).replace("'", "\\'")+"'"
add_cond1 += " and master_name = '"+frappe.db.escape(cstr(item))+"'"
itemwise_exists = frappe.db.sql("""select value from `tabAuthorization Rule`
where transaction = %s and value <= %s
and based_on = %s and company = %s and docstatus != 2 %s %s""" %

View File

@@ -355,7 +355,7 @@ def install_country_fixtures(company):
path = frappe.get_app_path('erpnext', 'regional', frappe.scrub(company_doc.country))
if os.path.exists(path.encode("utf-8")):
frappe.get_attr("erpnext.regional.{0}.setup.setup"
.format(frappe.scrub(company_doc.country)))(company_doc)
.format(frappe.scrub(company_doc.country)))(company_doc, False)
def update_company_current_month_sales(company):
current_month_year = formatdate(today(), "MM-yyyy")

View File

@@ -2,6 +2,7 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe, unittest
from frappe.utils import flt
from erpnext.setup.utils import get_exchange_rate
test_records = frappe.get_test_records('Currency Exchange')
@@ -44,7 +45,7 @@ class TestCurrencyExchange(unittest.TestCase):
# Start with allow_stale is True
exchange_rate = get_exchange_rate("USD", "INR", "2016-01-01")
self.assertEqual(exchange_rate, 60.0)
self.assertEqual(flt(exchange_rate, 3), 60.0)
exchange_rate = get_exchange_rate("USD", "INR", "2016-01-15")
self.assertEqual(exchange_rate, 65.1)
@@ -56,7 +57,7 @@ class TestCurrencyExchange(unittest.TestCase):
self.clear_cache()
exchange_rate = get_exchange_rate("USD", "INR", "2015-12-15")
self.assertFalse(exchange_rate == 60)
self.assertEqual(exchange_rate, 66.894)
self.assertEqual(flt(exchange_rate, 3), 66.894)
def test_exchange_rate_strict(self):
# strict currency settings
@@ -69,7 +70,7 @@ class TestCurrencyExchange(unittest.TestCase):
# Will fetch from fixer.io
self.clear_cache()
exchange_rate = get_exchange_rate("USD", "INR", "2016-01-15")
self.assertEqual(exchange_rate, 67.79)
self.assertEqual(flt(exchange_rate, 3), 67.79)
exchange_rate = get_exchange_rate("USD", "INR", "2016-01-30")
self.assertEqual(exchange_rate, 62.9)
@@ -77,7 +78,7 @@ class TestCurrencyExchange(unittest.TestCase):
# Exchange rate as on 15th Dec, 2015, should be fetched from fixer.io
self.clear_cache()
exchange_rate = get_exchange_rate("USD", "INR", "2015-12-15")
self.assertEqual(exchange_rate, 66.894)
self.assertEqual(flt(exchange_rate, 3), 66.894)
exchange_rate = get_exchange_rate("INR", "NGN", "2016-01-10")
self.assertEqual(exchange_rate, 65.1)
@@ -100,4 +101,4 @@ class TestCurrencyExchange(unittest.TestCase):
# Will fetch from fixer.io
self.clear_cache()
exchange_rate = get_exchange_rate("USD", "INR", "2016-01-15")
self.assertEqual(exchange_rate, 67.79)
self.assertEqual(flt(exchange_rate, 3), 67.79)

View File

@@ -417,8 +417,9 @@ class EmailDigest(Document):
value, count, billed_value, delivered_value = frappe.db.sql("""select ifnull(sum(grand_total),0), count(*),
ifnull(sum(grand_total*per_billed/100),0), ifnull(sum(grand_total*{0}/100),0) from `tab{1}`
where (transaction_date <= %(to_date)s)
and status not in ('Closed','Cancelled', 'Completed') """.format(getfield, doc_type),
{"to_date": self.future_to_date})[0]
and status not in ('Closed','Cancelled', 'Completed')
and company = %(company)s """.format(getfield, doc_type),
{"to_date": self.future_to_date, "company": self.company})[0]
return {
"label": self.meta.get_label(fieldname),
@@ -432,11 +433,13 @@ class EmailDigest(Document):
value, count = frappe.db.sql("""select ifnull(sum(grand_total),0), count(*) from `tabQuotation`
where (transaction_date <= %(to_date)s)
and status not in ('Ordered','Cancelled', 'Lost') """,{"to_date": self.future_to_date})[0]
and company = %(company)s
and status not in ('Ordered','Cancelled', 'Lost') """,{"to_date": self.future_to_date, "company": self.company})[0]
last_value = frappe.db.sql("""select ifnull(sum(grand_total),0) from `tabQuotation`
where (transaction_date <= %(to_date)s)
and status not in ('Ordered','Cancelled', 'Lost') """,{"to_date": self.past_to_date})[0][0]
and company = %(company)s
and status not in ('Ordered','Cancelled', 'Lost') """,{"to_date": self.past_to_date, "company": self.company})[0][0]
return {
"label": self.meta.get_label(fieldname),
@@ -462,8 +465,9 @@ class EmailDigest(Document):
def get_total_on(self, doc_type, from_date, to_date):
return frappe.db.sql("""select ifnull(sum(grand_total),0), count(*) from `tab{0}`
where (transaction_date between %(from_date)s and %(to_date)s) and status not in ('Cancelled')""".format(doc_type),
{"from_date": from_date, "to_date": to_date})[0]
where (transaction_date between %(from_date)s and %(to_date)s) and company=%(company)s
and status not in ('Cancelled')""".format(doc_type),
{"from_date": from_date, "to_date": to_date, "company": self.company})[0]
def get_from_to_date(self):
today = now_datetime().date()

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