Compare commits

...

190 Commits

Author SHA1 Message Date
Nabin Hait
b75f5fd5ab Merge branch 'develop' 2015-07-30 14:59:01 +05:30
Nabin Hait
6f5815838e bumped to version 5.4.1 2015-07-30 15:29:01 +06:00
Nabin Hait
fe53651287 Merge pull request #3755 from nabinhait/fix1
repost stock for product bundle materials
2015-07-30 14:54:21 +05:30
Nabin Hait
d8546c4316 Fixed include holiday logic in leave calculation 2015-07-30 14:53:27 +05:30
Nabin Hait
17405d4130 repost stock for product bundle materials 2015-07-30 14:53:27 +05:30
Anand Doshi
2f443a5789 [hotfix] Item checkbox mandatory and removed Module Def Contacts 2015-07-30 14:44:43 +05:30
Nabin Hait
a5e11d7195 Merge branch 'develop' 2015-07-30 11:57:53 +05:30
Nabin Hait
3832e1f9be bumped to version 5.4.0 2015-07-30 12:27:53 +06:00
Nabin Hait
67079d292c Merge pull request #3725 from neilLasrado/test
Fixed test cases related to Time Logs
2015-07-30 11:51:26 +05:30
Nabin Hait
01a3a81f71 Merge pull request #3753 from nabinhait/return1
[fix] Actual tax amount should be negative while creating return entry
2015-07-30 11:50:45 +05:30
Nabin Hait
befa8d613f Merge pull request #3750 from nabinhait/payreco
Minor fixes
2015-07-30 11:50:19 +05:30
Nabin Hait
9e26bcfeeb [fix] Actual tax amount should be negative while creating return entry 2015-07-30 11:48:18 +05:30
Neil Trini Lasrado
f948fea3bf simulate added to make_time_log in test_time_log 2015-07-29 18:48:58 +05:30
Neil Trini Lasrado
d851bd8d85 Fixed test cases related to Time Logs 2015-07-29 18:48:58 +05:30
Nabin Hait
30672c6b55 Merge pull request #3751 from neilLasrado/leave-application
Fixes in leave application
2015-07-29 18:47:59 +05:30
Neil Trini Lasrado
e73941b9cc Fixes in leave application 2015-07-29 18:39:27 +05:30
Nabin Hait
f33787a706 [fix] Get serial nos using FIFO in Return Delivery Note 2015-07-29 18:08:10 +05:30
Nabin Hait
fcc4021e44 minor fix in payment reconciliation 2015-07-29 17:41:51 +05:30
Nabin Hait
3b67c89e0b Merge pull request #3749 from nabinhait/changelog
Change log added
2015-07-29 16:41:57 +05:30
Nabin Hait
7364ebaff6 minor fix 2015-07-29 16:41:23 +05:30
Nabin Hait
fd8f34018d Change log added 2015-07-29 16:38:02 +05:30
Nabin Hait
799fa09a23 Merge pull request #3748 from nabinhait/fix7
[fix][patch] Reload Leave Type
2015-07-29 16:37:29 +05:30
Nabin Hait
d1dc622914 [fix][patch] Reload Leave Type 2015-07-29 16:37:02 +05:30
Nabin Hait
8ce47862f9 Merge branch 'meatechsupport-develop' into develop 2015-07-29 16:15:27 +05:30
Nabin Hait
b24d2efc4b [fix] Updated 'Include Holiday' in existing and default leave type records 2015-07-29 16:15:08 +05:30
Nabin Hait
a184fbfa66 Merge branch 'develop' of https://github.com/meatechsupport/erpnext into meatechsupport-develop 2015-07-29 16:05:47 +05:30
Nabin Hait
763821d57d Merge pull request #3746 from nabinhait/get_item_details
[fix] Get item details based on selected company
2015-07-29 16:02:39 +05:30
Nabin Hait
a132767126 Merge pull request #3739 from nabinhait/packed-items
Packed items in sales invoice
2015-07-29 16:01:30 +05:30
Nabin Hait
453cc374d4 - Fixed logic of reserved qty calculation while delivered product bundle via Sales Invoice
- Delete stock ledger entries on cancellation of Sales Invoice while product bundle delivered
- Make packing list on validate
2015-07-29 15:59:57 +05:30
Nabin Hait
43e46a8506 Merge pull request #3733 from nabinhait/fix3
POS Profile: removed income account, cost center, territory and bank/cash account from mandatory
2015-07-29 15:57:05 +05:30
Nabin Hait
2c95130451 Merge pull request #3741 from nabinhait/sponsors
sponsors page updated
2015-07-29 15:54:58 +05:30
Nabin Hait
d46fb5eb8c Merge pull request #3743 from rmehta/indicator-fix
[fix] indicators for sales order, purchase order #3736
2015-07-29 15:53:44 +05:30
Nabin Hait
e95c18a42d Merge pull request #3744 from nabinhait/return
Sales / Purchase Return Improvements
2015-07-29 15:51:34 +05:30
Nabin Hait
18c2c54633 Merge pull request #3718 from rmehta/item-cleanup
[cleanup] yes/no selects changed to checks in Item
2015-07-29 15:49:54 +05:30
Nabin Hait
0edec9d25b Merge pull request #3730 from nabinhait/fix1
minor fixes
2015-07-29 15:48:24 +05:30
Nabin Hait
15216fdd6f [fix] Get item details based on selected company 2015-07-29 15:47:45 +05:30
Nabin Hait
9117af3077 [fix] Uncheck Is POS option while creating return entry against POS Invoice 2015-07-29 15:40:59 +05:30
Rushabh Mehta
92b6f76612 [fix] disable save in naming series 2015-07-29 15:17:30 +05:30
Rushabh Mehta
8bf0b89595 [minor] currency field in list view for item price 2015-07-29 14:55:48 +05:30
Nabin Hait
746c162055 Return status in listview for return entry 2015-07-29 14:54:33 +05:30
Nabin Hait
9eb9ccd785 Make Debit / Credit Note button in Purchase / Sales Invoice 2015-07-29 13:23:37 +05:30
Nabin Hait
1e046aa49a Hide buttons for return entry 2015-07-29 13:16:37 +05:30
Nabin Hait
3ad26e4dd1 Fix Against Voucher in GL Entry for return against purchase invoice 2015-07-29 13:15:57 +05:30
Rushabh Mehta
2f3b097d63 [fix] indicators for sales order, purchase order #3736 2015-07-29 13:08:27 +05:30
Nabin Hait
c505cbc988 sponsors page updated 2015-07-29 12:34:18 +05:30
Rushabh Mehta
1e8025b327 [cleanup] yes/no selects changed to checks in Item 2015-07-29 11:38:33 +05:30
Anand Doshi
0e6f2474e8 [minor] removed trailing space in offer letter status 2015-07-29 11:27:02 +05:30
Meatechsupport
e102332f08 removed testcase 2015-07-28 19:03:30 +04:00
Meatechsupport
145227e4ec Edited the Test case 2015-07-28 18:39:04 +04:00
Nabin Hait
18e033514e Merge pull request #3679 from neilLasrado/freeze-customer
Added ability to freeze Customer/Supplier
2015-07-28 15:21:06 +05:30
Nabin Hait
26052df76e POS Profile: removed income account, cost center, territory and bank-cash account from mandatory 2015-07-28 15:12:54 +05:30
Nabin Hait
b2f354a614 Merge pull request #3731 from nabinhait/fix2
[fix] Contact section visibility in Warranty Claim
2015-07-28 15:12:22 +05:30
Rushabh Mehta
49a59c075f [minor] [setup-wizard] add check for sample data 2015-07-28 12:23:40 +05:30
Neil Trini Lasrado
ff3b220c79 Fixed gl_entry Validation 2015-07-28 12:23:14 +05:30
Neil Trini Lasrado
cc7cb2a70a Allowed frozen_accounts_modifier to create records against frozen customer/supplier 2015-07-28 12:17:03 +05:30
Neil Trini Lasrado
79bf233734 Test Cases added to check if customer is frozen 2015-07-28 12:17:03 +05:30
Neil Trini Lasrado
3698b84d7c Typo fixes in Supplier and Customer Document 2015-07-28 12:17:02 +05:30
Neil Trini Lasrado
3fbbb71afc Added ability to freeze Customer/Supplier 2015-07-28 12:17:02 +05:30
Nabin Hait
a2f18ba794 [fix] Contact section visibility in Warranty Claim 2015-07-28 12:00:45 +05:30
Nabin Hait
9088432f10 Fetch default expense account and cost center from item if company matches, otherwise fetch from company master 2015-07-28 11:16:22 +05:30
Nabin Hait
c80059e10e [fix] Ignore making SLE for opening stock reco with zero qty 2015-07-28 11:16:22 +05:30
Anand Doshi
8e8e9c61ca [minor] Show description in Sales Order 2015-07-28 10:46:14 +05:30
Rushabh Mehta
424f0c7b84 [fix] [setup wizard] ignore fiscal year 2015-07-28 10:36:33 +05:30
Rushabh Mehta
aa5fb5e50e [fix] [setup wizard] ignore if user is the primary user 2015-07-28 10:31:09 +05:30
Rushabh Mehta
08a60653ba [fix] [setup wizard] ignore if user is the primary user 2015-07-28 09:22:36 +05:30
Meatechsupport
8b96fdac12 Indentation Error
realligned
2015-07-27 16:55:54 +04:00
Meatechsupport
e319598c51 IndentationError
alligned code
2015-07-27 16:42:22 +04:00
Nabin Hait
0c7fd6cd94 Merge branch 'develop' 2015-07-27 17:11:16 +05:30
Nabin Hait
0b5260acf0 bumped to version 5.3.1 2015-07-27 17:41:16 +06:00
Nabin Hait
d4be82cf9b corrected typo 2015-07-27 17:09:54 +05:30
Nabin Hait
0166e9e47a Merge pull request #3726 from nabinhait/fix1
minor fix
2015-07-27 17:09:38 +05:30
Nabin Hait
b71471fcb5 minor fix 2015-07-27 17:09:07 +05:30
Nabin Hait
7e8d7d05ef Merge branch 'develop' 2015-07-27 15:55:57 +05:30
Nabin Hait
228ff87ea2 bumped to version 5.3.0 2015-07-27 16:25:56 +06:00
Nabin Hait
8d8655e1cd Change log added 2015-07-27 15:54:39 +05:30
Meatechsupport
5c73bafeaa Modifying the number of leave days calculation part.
we don't need to exclude the Holiday list (that comes in between) from the total number of leaves applied.

Add option in the leave type

added a field in the leave type to include and exclude the holidays from
the tolal leave applied days

Added the field

Added a field Include Holiday to leave Type doctype

changed the lable

changed the lable from "Include Holiday" to "Include holidays within leaves
as leaves"

Rearranged the function

moved holidays = leave_app.get_holidays() under if
Corrected 'total_leave_days' : flt(tot_days)-flt(holidays)

Adding test case

added the test case

Added test case

Added test case to test_leave_application.py

adding default value

added default value and corrected the syntax.

IndentationError

removed extra tabs after
2015-07-27 13:46:00 +04:00
Anand Doshi
14859faf17 [minor] Accounts Payable Report Type='Script Report' 2015-07-24 17:30:39 +05:30
Nabin Hait
2b7eda8135 Merge pull request #3683 from nabinhait/return
Sales / Purchase Return Enhancement
2015-07-24 16:53:01 +05:30
Anand Doshi
751d7ecd85 Merge pull request #3716 from tmimori/develop
Removed HTML from messages
2015-07-24 14:28:59 +05:30
Nabin Hait
6a09b3f7ef Merge pull request #3702 from neilLasrado/capacity-planning
Global switch to set capacity planning in manufacturing
2015-07-24 14:26:28 +05:30
Nabin Hait
21897e3c52 Merge pull request #3714 from nabinhait/fix4
[fix] gross profit report
2015-07-24 14:25:35 +05:30
Nabin Hait
15b4f6310c Merge pull request #3701 from neilLasrado/po
Disallowed End of Life Items from getting selected in Production Orders and Stock Reconciliation
2015-07-24 14:25:13 +05:30
Tsutomu Mimori
196a0bc675 Amend for frappe/erpnext/pull/3716 2015-07-24 17:06:00 +09:00
Tsutomu Mimori
7c011985f0 Merge remote-tracking branch 'frappe/develop' into develop 2015-07-24 17:02:13 +09:00
Nabin Hait
3cf67a462b Cleanup and test cases for serialized item 2015-07-24 13:26:54 +05:30
Nabin Hait
f061877b4f [fix] Stock Entry permissions 2015-07-24 13:26:54 +05:30
Nabin Hait
04d244a360 Credit Note print format 2015-07-24 13:26:54 +05:30
Nabin Hait
b74999da82 [testcase] Testcase for return purchase invoice 2015-07-24 13:26:53 +05:30
Nabin Hait
061f7079ed Test case for purchase return 2015-07-24 13:26:53 +05:30
Nabin Hait
246ed3f122 Test cases for sales return 2015-07-24 13:26:53 +05:30
Nabin Hait
6b25708b7a Minor fixes 2015-07-24 13:26:53 +05:30
Nabin Hait
623ed57663 Removed Sales/Purchase Return option from Stock Entry 2015-07-24 13:26:53 +05:30
Nabin Hait
1d21842f68 Sales / Purchase Return redesigned via negative DN / SI / PR / PI 2015-07-24 13:26:53 +05:30
Nabin Hait
ada485f096 Outgoing rate in Purchase Return based on reference/original Purchase Receipt rate 2015-07-24 13:26:53 +05:30
Neil Trini Lasrado
8a9d41a92e Modified Test Cases for Production Order 2015-07-24 13:18:45 +05:30
Neil Trini Lasrado
f965c5d203 now_datetime changed to nowdate 2015-07-24 13:14:57 +05:30
Neil Trini Lasrado
21647974c4 Test Cases Added to Production Order 2015-07-24 13:14:57 +05:30
Neil Trini Lasrado
9c3dca63fa Disallowed End of Life Items from getting selected in Production Orders and Stock Reconciliation 2015-07-24 13:14:56 +05:30
Tsutomu Mimori
c40b99be26 Merge remote-tracking branch 'frappe/develop' into develop 2015-07-23 22:10:40 +09:00
Tsutomu Mimori
982f4ae44d Removed HTML from messages 2015-07-23 22:09:35 +09:00
Anand Doshi
9257413b68 [minor] Added 'Import Data' in sample data 2015-07-23 18:16:44 +05:30
Neil Trini Lasrado
c723c8b5aa Disable Capacity Planning label changed to Disable Capacity Planning & Time Tracking in Manufacturing Settings 2015-07-23 17:43:27 +05:30
Neil Trini Lasrado
2771b7d828 Track Operations removed and Global Switch added to disable capacity planning in manufacturing settings 2015-07-23 17:39:35 +05:30
Anand Doshi
75ebed815f Merge pull request #3711 from nabinhait/budget
Validation on Account for assigning budget
2015-07-23 17:26:56 +05:30
Nabin Hait
975ef07c48 Merge pull request #3706 from neilLasrado/variant
Fetch Template Bom if no BOM is set against Item Variant in Production Order
2015-07-23 17:26:11 +05:30
Nabin Hait
f666535223 [fix] gross profit report 2015-07-23 17:08:44 +05:30
Nabin Hait
08fb19ac8c Validation on Account for assigning budget 2015-07-23 16:59:43 +05:30
Anand Doshi
db9762be3f Merge pull request #3712 from rmehta/setup-wizard-fix
[fix] contact name in setup wizard
2015-07-23 16:46:09 +05:30
Anand Doshi
03ae61afce Merge pull request #3681 from rmehta/sample-data
[enhancement] update to setup wizard, added users, employees, sample data
2015-07-23 16:45:28 +05:30
Anand Doshi
1847b705fe Merge pull request #3704 from nabinhait/fix2
[fix] SMS status and log
2015-07-23 16:45:00 +05:30
Anand Doshi
4a573a7e8b Merge pull request #3709 from nabinhait/contacts
[cleanup] Party Type deleted
2015-07-23 16:41:36 +05:30
Nabin Hait
f74b9b06f7 Merge pull request #3708 from neilLasrado/material-request
Fetch items from Packing List if Exists in Sales Order while raising …
2015-07-23 15:21:50 +05:30
Nabin Hait
2820a8749f Merge pull request #3710 from neilLasrado/stock-report
Opening Balance row added to Stock Ledger Report
2015-07-23 15:21:26 +05:30
Rushabh Mehta
41b4864f0c Merge pull request #3713 from neilLasrado/manage-variants
Fixes for Item Variants
2015-07-23 15:01:28 +05:30
Neil Trini Lasrado
13df8a40ef Validation added to prevent user to Manage Variants if Item Template is Unsaved. Prevented message stating variants updated while saving item template if there are no variants against that item Template 2015-07-23 12:46:59 +05:30
Rushabh Mehta
7cfa5f0508 [fix] contact name in setup wizard 2015-07-23 12:39:44 +05:30
Nabin Hait
bb274fce2e [cleanup] Party Type deleted 2015-07-23 11:45:30 +05:30
Rushabh Mehta
fec61fe33e [minor] fix report type for Accounts Payable 2015-07-23 10:39:13 +05:30
Neil Trini Lasrado
d0387f41df Opening Balance row added to Stock Ledger Report 2015-07-22 18:44:59 +05:30
Neil Trini Lasrado
05d8174696 Fetch items from Packing List if Exists in Sales Order while raising Material Request against SO 2015-07-22 16:31:34 +05:30
Rushabh Mehta
ba02ce6adc [docs] added description 2015-07-22 15:07:48 +05:30
Anand Doshi
886def0a69 [fix] convert Item and Item Grid images to absolute urls 2015-07-22 14:43:37 +05:30
Anand Doshi
7590aa2524 [minor] raise EmptyStockReconciliationItemsError when no change in any of the items 2015-07-22 14:43:36 +05:30
Neil Trini Lasrado
8b48ceab8c Removed logic for get_item_details in Production Planning Tool, Used get_item_details function of Production Order instead 2015-07-22 12:42:21 +05:30
Nabin Hait
d900e12ae7 Change log for sms 2015-07-22 12:21:55 +05:30
Nabin Hait
098760f0e2 [fix] SMS status and log 2015-07-22 12:17:48 +05:30
Neil Trini Lasrado
cc431716ed Fetch Template Bom if no BOM is set against Item Variant in Production Order 2015-07-22 12:13:47 +05:30
Anand Doshi
f5ea801b69 Merge pull request #3700 from nabinhait/fix1
[report] Letter Head option in General Ledger
2015-07-21 12:19:57 +05:30
Nabin Hait
e2b8ccf1bb [report] Letter Head option in General Ledger report 2015-07-21 12:02:36 +05:30
Anand Doshi
f447c8258a [minor] Newsletter Message should be mandatory 2015-07-20 16:10:04 +05:30
Nabin Hait
f78ffd84b4 Merge branch 'develop' 2015-07-20 15:22:10 +05:30
Nabin Hait
63d345c6ab bumped to version 5.2.1 2015-07-20 15:52:10 +06:00
Anand Doshi
d0d1af2072 Merge pull request #3696 from nabinhait/fix1
[fix][report] Show only active employee in Employee leave balance report
2015-07-20 13:27:47 +05:30
Anand Doshi
83694fd180 [minor] removed is_group from search fields of Account 2015-07-20 13:26:22 +05:30
Anand Doshi
fc353efeca Merge pull request #3670 from neilLasrado/expense-claim
Default Expense Account added to Expense Claim Type.
2015-07-20 13:04:33 +05:30
Anand Doshi
16edf8b478 Merge pull request #3685 from nabinhait/fix6
[fix] Fetch debit/credit from reference journal entry
2015-07-20 13:00:10 +05:30
Nabin Hait
c9e4fbeda0 [fix][report] Show only active employee in Employee leave balance report 2015-07-20 12:57:48 +05:30
Anand Doshi
46ae789f2f Merge pull request #3682 from anandpdoshi/anand-july-17
Remove 'Stopped' banner in Sales Order, show ERPNext icon instead of 'ERPNext'
2015-07-20 12:57:17 +05:30
Anand Doshi
a9a284a5ae Merge pull request #3693 from rmehta/close-task
[minor] close button for task and item not found error fix
2015-07-20 12:56:50 +05:30
Rushabh Mehta
823e88d5bd [minor] close button for task and item not found error fix 2015-07-20 10:56:39 +05:30
Nabin Hait
1be94efcfa [fix] Fetch debit/credit from reference journal entry 2015-07-17 17:25:52 +05:30
Anand Doshi
a5e9c71397 [ui] changed Learn icon 2015-07-17 15:55:10 +05:30
Anand Doshi
285135da8e [ui] Remove 'Stopped' alert banner in Sales Order 2015-07-17 15:14:40 +05:30
Anand Doshi
906c2babaa [ui] Show ERPNext icon instead of ERPNext in navbar 2015-07-17 15:14:39 +05:30
Nabin Hait
14a394cde1 Update fields_to_be_renamed.py 2015-07-17 15:14:19 +05:30
Nabin Hait
3c821c89f2 Update rename_table_fieldnames.py 2015-07-17 15:13:29 +05:30
Nabin Hait
a041297861 Update rename_total_fields.py 2015-07-17 15:12:52 +05:30
Rushabh Mehta
856ee10dc4 [enhancement] update to setup wizard, added users, employees, sample data 2015-07-17 15:03:18 +05:30
Nabin Hait
1401e3f679 Update rename_total_fields.py 2015-07-17 14:43:19 +05:30
Nabin Hait
dae29bf2d4 Update fields_to_be_renamed.py 2015-07-17 14:42:46 +05:30
Nabin Hait
0a3be6da27 Update rename_table_fieldnames.py 2015-07-17 14:39:41 +05:30
Nabin Hait
3631e9cbe0 Merge branch 'develop' 2015-07-16 17:15:41 +05:30
Nabin Hait
1c9a7d2a1b bumped to version 5.2.0 2015-07-16 17:45:40 +06:00
Anand Doshi
972f2f9194 [change-log] 2015-07-16 16:19:30 +05:30
Anand Doshi
239296d16a Merge pull request #3657 from nabinhait/fix4
Reserved warehouse should not be validated on cancellation of sales order
2015-07-16 15:57:59 +05:30
Anand Doshi
3210db9056 Merge pull request #3656 from rmehta/sms-log
[fix] create SMS Log
2015-07-16 15:56:08 +05:30
Neil Trini Lasrado
feda4f9bc9 Default Expense Account added to Expense Claim Type. Fetch account while making Bank Entry 2015-07-16 15:44:56 +05:30
Anand Doshi
f6954fb798 Merge pull request #3663 from neilLasrado/bom
Bom
2015-07-16 15:02:40 +05:30
Anand Doshi
c6656e68b8 Merge pull request #3666 from nabinhait/patch-fix
[patch][fix] Sales BOM should be renamed before rename_table_fields patch
2015-07-16 14:55:35 +05:30
Anand Doshi
a39387d352 Merge pull request #3664 from rmehta/develop
[cleanup] removed welcome emails
2015-07-16 14:25:24 +05:30
Rushabh Mehta
f3791797d6 [fix] [patch] 2015-07-16 14:17:59 +05:30
Rushabh Mehta
ea4d63cef3 [cleanup] removed welcome emails 2015-07-16 14:17:59 +05:30
Nabin Hait
5464ca8a73 [patch][fix] Sales BOM should be renamed before rename_table_fields patch 2015-07-16 12:19:57 +05:30
Rushabh Mehta
6c6875f503 [cleanup] removed welcome emails 2015-07-16 11:53:18 +05:30
Neil Trini Lasrado
20523c45c7 Patch for default BOM 2015-07-16 11:27:06 +05:30
Neil Trini Lasrado
0248811e53 Fixed deafault BOM problem 2015-07-16 11:26:40 +05:30
Rushabh Mehta
93416ee72c Update sponsors.md 2015-07-15 16:42:46 +05:30
Rushabh Mehta
51e7086a08 [patch] rename roles 2015-07-15 16:30:49 +05:30
Anand Doshi
5e849ae53e [help] added new links to Learn 2015-07-15 15:23:02 +05:30
Rushabh Mehta
de0db0d000 Merge pull request #3658 from anandpdoshi/anand-july-15
[fix] Mozilla hack for images in table for print
2015-07-15 12:58:19 +05:30
Anand Doshi
f3a67c4533 [fix] Mozilla hack for images in table for print 2015-07-15 12:37:45 +05:30
Nabin Hait
0847f9a074 Reserved warehouse should not be validated on cancellation of sales order 2015-07-14 18:23:05 +05:30
Anand Doshi
9490c21b8a Merge pull request #3641 from rmehta/role-rename
[rename] Material User > Stock User
2015-07-14 12:10:48 +05:30
Rushabh Mehta
bdb71bca4e [perm] added stock, project user for company 2015-07-14 11:54:42 +05:30
Anand Doshi
e8861e2871 Merge pull request #3649 from rmehta/purchase-invoice-fix
[fix] supplier invoice number fix
2015-07-14 11:50:07 +05:30
Anand Doshi
b084b5e449 Merge pull request #3651 from rmehta/pos-default-pay-fix
[fix] pos default payment #3631
2015-07-14 11:49:46 +05:30
Anand Doshi
41d3e57702 Merge pull request #3650 from rmehta/time-log-fix-2
[fix] overlap fix in time log #3647
2015-07-14 11:45:49 +05:30
Anand Doshi
9df2899f72 Merge pull request #3648 from rmehta/time-log-fix
[fixes] hours in time-log #3644, project buttons on condition, removed Guest permission in notification_control
2015-07-14 11:25:46 +05:30
Rushabh Mehta
dacf127f1b [fix] time log overlap condition, #SavedByATestCase 2015-07-14 11:06:28 +05:30
Rushabh Mehta
2b49f9b30a [fix] pos default payment #3631 2015-07-14 11:01:42 +05:30
Rushabh Mehta
533434e878 [fix] overlap fix in time log #3647 2015-07-14 10:39:33 +05:30
Rushabh Mehta
1956028ddc [fix] supplier invoice number fix 2015-07-14 10:26:50 +05:30
Rushabh Mehta
a87dc3b4e6 [minor] move position in accounts settings 2015-07-14 10:22:07 +05:30
Rushabh Mehta
97b3f750c9 [fixes] hours in time-log #3644, project buttons on condition, removed Guest permission in notification_control 2015-07-14 10:14:48 +05:30
Anand Doshi
c41b63eff1 Merge pull request #3642 from anandpdoshi/anand-july-13
Fixes to Cart
2015-07-13 16:56:37 +05:30
Anand Doshi
e6f7ac961f [fix] Add to Cart visibility, Customer's Price List in Shopping Cart and Address creation from Shopping Cart 2015-07-13 16:24:37 +05:30
Rushabh Mehta
cf26964deb Merge pull request #3640 from anandpdoshi/anand-july-13
Fixes to Issues
2015-07-13 15:37:11 +05:30
Rushabh Mehta
94157334a7 [rename] Material User > Stock User 2015-07-13 15:06:12 +05:30
Anand Doshi
c530161de0 [fix] Item image urls can now have paranthesis 2015-07-13 15:05:39 +05:30
Rushabh Mehta
9624bac3cf [fix] create SMS Log 2015-05-20 15:48:58 +05:30
213 changed files with 4708 additions and 4046 deletions

View File

@@ -1,2 +1,2 @@
from __future__ import unicode_literals
__version__ = '5.1.6'
__version__ = '5.4.1'

View File

@@ -173,7 +173,7 @@
"icon": "icon-money",
"idx": 1,
"in_create": 0,
"modified": "2015-06-14 20:57:55.471334",
"modified": "2015-07-20 03:54:14.297995",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Account",
@@ -257,5 +257,5 @@
"write": 1
}
],
"search_fields": "is_group"
"search_fields": ""
}

View File

@@ -49,7 +49,7 @@ class Account(Document):
self.root_type = par.root_type
def validate_root_details(self):
#does not exists parent
# does not exists parent
if frappe.db.exists("Account", self.name):
if not frappe.db.get_value("Account", self.name, "parent_account"):
throw(_("Root cannot be edited."))

View File

@@ -4,13 +4,6 @@
"docstatus": 0,
"doctype": "DocType",
"fields": [
{
"fieldname": "check_supplier_invoice_uniqueness",
"fieldtype": "Check",
"label": "Check Supplier Invoice Number Uniqueness",
"permlevel": 0,
"precision": ""
},
{
"default": "1",
"description": "If enabled, the system will post accounting entries for inventory automatically.",
@@ -45,12 +38,19 @@
"label": "Credit Controller",
"options": "Role",
"permlevel": 0
},
{
"fieldname": "check_supplier_invoice_uniqueness",
"fieldtype": "Check",
"label": "Check Supplier Invoice Number Uniqueness",
"permlevel": 0,
"precision": ""
}
],
"icon": "icon-cog",
"idx": 1,
"issingle": 1,
"modified": "2015-06-11 06:06:34.047890",
"modified": "2015-07-14 00:51:48.095525",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Settings",

View File

@@ -17,7 +17,7 @@ erpnext.accounts.CostCenterController = frappe.ui.form.Controller.extend({
return {
filters:[
['Account', 'company', '=', me.frm.doc.company],
['Account', 'report_type', '=', 'Profit and Loss'],
['Account', 'root_type', '=', 'Expense'],
['Account', 'is_group', '=', '0'],
]
}

View File

@@ -66,7 +66,7 @@
"precision": ""
},
{
"description": "Define Budget for this Cost Center. To set budget action, see <a href=\"#!List/Company\">Company Master</a>",
"description": "Define Budget for this Cost Center. To set budget action, see \"Company List\"",
"fieldname": "sb1",
"fieldtype": "Section Break",
"label": "Budget",
@@ -139,7 +139,7 @@
"icon": "icon-money",
"idx": 1,
"in_create": 0,
"modified": "2015-04-23 02:54:26.934607",
"modified": "2015-07-13 05:28:25.504801",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Cost Center",
@@ -189,8 +189,8 @@
"apply_user_permissions": 1,
"permlevel": 0,
"read": 1,
"role": "Material User"
"role": "Stock User"
}
],
"search_fields": "parent_cost_center, is_group"
}
}

View File

@@ -3,9 +3,7 @@
from __future__ import unicode_literals
import frappe
from frappe import msgprint, _
from frappe import _
from frappe.utils.nestedset import NestedSet
class CostCenter(NestedSet):
@@ -14,18 +12,46 @@ class CostCenter(NestedSet):
def autoname(self):
self.name = self.cost_center_name.strip() + ' - ' + \
frappe.db.get_value("Company", self.company, "abbr")
def validate(self):
self.validate_mandatory()
self.validate_accounts()
def validate_mandatory(self):
if self.cost_center_name != self.company and not self.parent_cost_center:
msgprint(_("Please enter parent cost center"), raise_exception=1)
frappe.throw(_("Please enter parent cost center"))
elif self.cost_center_name == self.company and self.parent_cost_center:
msgprint(_("Root cannot have a parent cost center"), raise_exception=1)
frappe.throw(_("Root cannot have a parent cost center"))
def validate_accounts(self):
if self.is_group==1 and self.get("budgets"):
frappe.throw(_("Budget cannot be set for Group Cost Center"))
check_acc_list = []
for d in self.get('budgets'):
if d.account:
account_details = frappe.db.get_value("Account", d.account,
["is_group", "company", "root_type"], as_dict=1)
if account_details.is_group:
frappe.throw(_("Budget cannot be assigned against Group Account {0}").format(d.account))
elif account_details.company != self.company:
frappe.throw(_("Account {0} does not belongs to company {1}").format(d.account, self.company))
elif account_details.root_type != "Expense":
frappe.throw(_("Budget cannot be assigned against {0}, as it's not an Expense account")
.format(d.account))
if [d.account, d.fiscal_year] in check_acc_list:
frappe.throw(_("Account {0} has been entered more than once for fiscal year {1}")
.format(d.account, d.fiscal_year))
else:
check_acc_list.append([d.account, d.fiscal_year])
def convert_group_to_ledger(self):
if self.check_if_child_exists():
msgprint(_("Cannot convert Cost Center to ledger as it has child nodes"), raise_exception=1)
frappe.throw(_("Cannot convert Cost Center to ledger as it has child nodes"))
elif self.check_gle_exists():
msgprint(_("Cost Center with existing transactions can not be converted to ledger"), raise_exception=1)
frappe.throw(_("Cost Center with existing transactions can not be converted to ledger"))
else:
self.is_group = 0
self.save()
@@ -33,7 +59,7 @@ class CostCenter(NestedSet):
def convert_ledger_to_group(self):
if self.check_gle_exists():
msgprint(_("Cost Center with existing transactions can not be converted to group"), raise_exception=1)
frappe.throw(_("Cost Center with existing transactions can not be converted to group"))
else:
self.is_group = 1
self.save()
@@ -46,21 +72,6 @@ class CostCenter(NestedSet):
return frappe.db.sql("select name from `tabCost Center` where \
parent_cost_center = %s and docstatus != 2", self.name)
def validate_budget_details(self):
check_acc_list = []
for d in self.get('budgets'):
if self.is_group==1:
msgprint(_("Budget cannot be set for Group Cost Centers"), raise_exception=1)
if [d.account, d.fiscal_year] in check_acc_list:
msgprint(_("Account {0} has been entered more than once for fiscal year {1}").format(d.account, d.fiscal_year), raise_exception=1)
else:
check_acc_list.append([d.account, d.fiscal_year])
def validate(self):
self.validate_mandatory()
self.validate_budget_details()
def before_rename(self, olddn, newdn, merge=False):
# Add company abbr if not provided
from erpnext.setup.doctype.company.company import get_name_with_abbr

View File

@@ -56,7 +56,7 @@
],
"icon": "icon-calendar",
"idx": 1,
"modified": "2015-04-18 07:33:23.922518",
"modified": "2015-07-13 05:28:27.745408",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Fiscal Year",
@@ -78,11 +78,63 @@
{
"apply_user_permissions": 1,
"delete": 0,
"email": 1,
"email": 0,
"permlevel": 0,
"print": 1,
"print": 0,
"read": 1,
"role": "All"
"role": "Sales User"
},
{
"create": 0,
"delete": 0,
"email": 0,
"export": 0,
"permlevel": 0,
"print": 0,
"read": 1,
"report": 0,
"role": "Purchase User",
"share": 0,
"write": 0
},
{
"create": 0,
"delete": 0,
"email": 0,
"export": 0,
"permlevel": 0,
"print": 0,
"read": 1,
"report": 0,
"role": "Accounts User",
"share": 0,
"write": 0
},
{
"create": 0,
"delete": 0,
"email": 0,
"export": 0,
"permlevel": 0,
"print": 0,
"read": 1,
"report": 0,
"role": "Stock User",
"share": 0,
"write": 0
},
{
"create": 0,
"delete": 0,
"email": 0,
"export": 0,
"permlevel": 0,
"print": 0,
"read": 1,
"report": 0,
"role": "Employee",
"share": 0,
"write": 0
}
],
"sort_field": "name",

View File

@@ -9,6 +9,8 @@ from frappe import _
from frappe.model.document import Document
class CustomerFrozen(frappe.ValidationError): pass
class GLEntry(Document):
def validate(self):
self.flags.ignore_submit_comment = True
@@ -17,6 +19,7 @@ class GLEntry(Document):
self.validate_posting_date()
self.check_pl_account()
self.validate_cost_center()
self.validate_party()
def on_update_with_args(self, adv_adj, update_outstanding = 'Yes'):
self.validate_account_details(adv_adj)
@@ -88,6 +91,13 @@ class GLEntry(Document):
if self.cost_center and _get_cost_center_company() != self.company:
frappe.throw(_("Cost Center {0} does not belong to Company {1}").format(self.cost_center, self.company))
def validate_party(self):
if self.party_type and self.party:
frozen_accounts_modifier = frappe.db.get_value( 'Accounts Settings', None,'frozen_accounts_modifier')
if not frozen_accounts_modifier in frappe.get_roles():
if frappe.db.get_value(self.party_type, self.party, "is_frozen"):
frappe.throw("{0} {1} is frozen".format(self.party_type, self.party), CustomerFrozen)
def validate_balance_type(account, adv_adj=False):
if not adv_adj and account:
@@ -139,9 +149,9 @@ def update_outstanding_amt(account, party_type, party, against_voucher_type, aga
if against_voucher_amount < 0:
bal = -bal
# Validation : Outstanding can not be negative
if bal < 0 and not on_cancel:
frappe.throw(_("Outstanding for {0} cannot be less than zero ({1})").format(against_voucher, fmt_money(bal)))
# Validation : Outstanding can not be negative for JV
if bal < 0 and not on_cancel:
frappe.throw(_("Outstanding for {0} cannot be less than zero ({1})").format(against_voucher, fmt_money(bal)))
# Update outstanding amt on against voucher
if against_voucher_type in ["Sales Invoice", "Purchase Invoice"]:

View File

@@ -109,7 +109,7 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
against_jv: function(doc, cdt, cdn) {
var d = frappe.get_doc(cdt, cdn);
if (d.against_jv && d.party && !flt(d.credit) && !flt(d.debit)) {
if (d.against_jv && !flt(d.credit) && !flt(d.debit)) {
this.get_outstanding('Journal Entry', d.against_jv, d);
}
},
@@ -119,7 +119,8 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
var args = {
"doctype": doctype,
"docname": docname,
"party": child.party
"party": child.party,
"account": child.account
}
return this.frm.call({

View File

@@ -310,7 +310,7 @@
"depends_on": "eval:doc.voucher_type == 'Write Off Entry'",
"fieldname": "write_off_amount",
"fieldtype": "Currency",
"label": "Write Off Amount <=",
"label": "Write Off Amount",
"options": "Company:company:default_currency",
"permlevel": 0,
"print_hide": 1,
@@ -503,4 +503,4 @@
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "title"
}
}

View File

@@ -545,12 +545,14 @@ def get_against_jv(doctype, txt, searchfield, start, page_len, filters):
@frappe.whitelist()
def get_outstanding(args):
args = eval(args)
if args.get("doctype") == "Journal Entry" and args.get("party"):
if args.get("doctype") == "Journal Entry":
condition = " and party=%(party)s" if args.get("party") else ""
against_jv_amount = frappe.db.sql("""
select sum(ifnull(debit, 0)) - sum(ifnull(credit, 0))
from `tabJournal Entry Account` where parent=%s and party=%s
from `tabJournal Entry Account` where parent=%(docname)s and account=%(account)s {0}
and ifnull(against_invoice, '')='' and ifnull(against_voucher, '')=''
and ifnull(against_jv, '')=''""", (args['docname'], args['party']))
and ifnull(against_jv, '')=''""".format(condition), args)
against_jv_amount = flt(against_jv_amount[0][0]) if against_jv_amount else 0
if against_jv_amount > 0:

View File

@@ -190,8 +190,9 @@ class PaymentReconciliation(Document):
if flt(p.allocated_amount) > flt(p.amount):
frappe.throw(_("Row {0}: Allocated amount {1} must be less than or equals to JV amount {2}")
.format(p.idx, p.allocated_amount, p.amount))
if flt(p.allocated_amount) > unreconciled_invoices.get(p.invoice_type, {}).get(p.invoice_number):
invoice_outstanding = unreconciled_invoices.get(p.invoice_type, {}).get(p.invoice_number)
if abs(flt(p.allocated_amount) - invoice_outstanding) > 0.009:
frappe.throw(_("Row {0}: Allocated amount {1} must be less than or equals to invoice outstanding amount {2}")
.format(p.idx, p.allocated_amount, unreconciled_invoices.get(p.invoice_type, {}).get(p.invoice_number)))

View File

@@ -27,7 +27,7 @@
"options": "Territory",
"permlevel": 0,
"read_only": 0,
"reqd": 1
"reqd": 0
},
{
"fieldname": "naming_series",
@@ -167,7 +167,7 @@
"options": "Account",
"permlevel": 0,
"read_only": 0,
"reqd": 1
"reqd": 0
},
{
"fieldname": "income_account",
@@ -178,7 +178,7 @@
"options": "Account",
"permlevel": 0,
"read_only": 0,
"reqd": 1
"reqd": 0
},
{
"depends_on": "eval:cint(sys_defaults.auto_accounting_for_stock)",
@@ -201,7 +201,7 @@
"options": "Cost Center",
"permlevel": 0,
"read_only": 0,
"reqd": 1
"reqd": 0
},
{
"fieldname": "write_off_account",
@@ -234,7 +234,7 @@
],
"icon": "icon-cog",
"idx": 1,
"modified": "2015-07-07 08:56:04.381471",
"modified": "2015-07-28 15:07:14.417200",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Profile",

View File

@@ -20,26 +20,19 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
this._super();
// Show / Hide button
if(doc.docstatus==1 && doc.outstanding_amount > 0)
this.frm.add_custom_button(__('Make Payment Entry'), this.make_bank_entry,
frappe.boot.doctype_icons["Journal Entry"]);
if(doc.docstatus==1) {
cur_frm.add_custom_button(__('View Ledger'), function() {
frappe.route_options = {
"voucher_no": doc.name,
"from_date": doc.posting_date,
"to_date": doc.posting_date,
"company": doc.company,
group_by_voucher: 0
};
frappe.set_route("query-report", "General Ledger");
}, "icon-table");
}
if(doc.docstatus===0) {
cur_frm.add_custom_button(__('From Purchase Order'),
function() {
this.show_general_ledger();
if(!doc.is_return) {
if(doc.docstatus==1) {
if(doc.outstanding_amount > 0) {
this.frm.add_custom_button(__('Make Payment Entry'), this.make_bank_entry);
}
cur_frm.add_custom_button(__('Make Debit Note'), this.make_debit_note);
}
if(doc.docstatus===0) {
cur_frm.add_custom_button(__('From Purchase Order'), function() {
frappe.model.map_current_doc({
method: "erpnext.buying.doctype.purchase_order.purchase_order.make_purchase_invoice",
source_doctype: "Purchase Order",
@@ -51,10 +44,9 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
company: cur_frm.doc.company
}
})
}, "icon-download", "btn-default");
});
cur_frm.add_custom_button(__('From Purchase Receipt'),
function() {
cur_frm.add_custom_button(__('From Purchase Receipt'), function() {
frappe.model.map_current_doc({
method: "erpnext.stock.doctype.purchase_receipt.purchase_receipt.make_purchase_invoice",
source_doctype: "Purchase Receipt",
@@ -64,11 +56,11 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
company: cur_frm.doc.company
}
})
}, "icon-download", "btn-default");
});
}
}
},
supplier: function() {
var me = this;
if(this.frm.updating_party_details)
@@ -109,7 +101,14 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
$.each(this.frm.doc["items"] || [], function(i, row) {
if(row.purchase_receipt) frappe.model.clear_doc("Purchase Receipt", row.purchase_receipt)
})
}
},
make_debit_note: function() {
frappe.model.open_mapped_doc({
method: "erpnext.accounts.doctype.purchase_invoice.purchase_invoice.make_debit_note",
frm: cur_frm
})
},
});
cur_frm.script_manager.make(erpnext.accounts.PurchaseInvoice);
@@ -144,7 +143,7 @@ cur_frm.fields_dict['items'].grid.get_field("item_code").get_query = function(do
return {
query: "erpnext.controllers.queries.item_query",
filters:{
'is_purchase_item': 'Yes'
'is_purchase_item': 1
}
}
}
@@ -224,4 +223,3 @@ cur_frm.cscript.select_print_heading = function(doc,cdt,cdn){
else
cur_frm.pformat.print_heading = __("Purchase Invoice");
}

View File

@@ -12,7 +12,7 @@
"no_copy": 1,
"oldfieldname": "naming_series",
"oldfieldtype": "Select",
"options": "PINV-",
"options": "PINV-\nPINV-RET-",
"permlevel": 0,
"print_hide": 1,
"read_only": 0,
@@ -154,6 +154,28 @@
"read_only": 0,
"search_index": 0
},
{
"fieldname": "is_return",
"fieldtype": "Check",
"label": "Is Return",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 1
},
{
"depends_on": "is_return",
"fieldname": "return_against",
"fieldtype": "Link",
"label": "Return Against Purchase Invoice",
"no_copy": 0,
"options": "Purchase Invoice",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "currency_and_price_list",
"fieldtype": "Section Break",
@@ -940,7 +962,7 @@
"icon": "icon-file-text",
"idx": 1,
"is_submittable": 1,
"modified": "2015-07-03 03:26:32.934540",
"modified": "2015-07-24 11:49:59.762109",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice",

View File

@@ -37,14 +37,16 @@ class PurchaseInvoice(BuyingController):
super(PurchaseInvoice, self).validate()
self.po_required()
self.pr_required()
self.validate_supplier_invoice()
if not self.is_return:
self.po_required()
self.pr_required()
self.validate_supplier_invoice()
self.validate_advance_jv("advances", "purchase_order")
self.check_active_purchase_items()
self.check_conversion_rate()
self.validate_credit_to_acc()
self.clear_unallocated_advances("Purchase Invoice Advance", "advances")
self.validate_advance_jv("advances", "purchase_order")
self.check_for_stopped_status()
self.validate_with_previous_doc()
self.validate_uom_is_integer("uom", "qty")
@@ -71,13 +73,14 @@ class PurchaseInvoice(BuyingController):
super(PurchaseInvoice, self).set_missing_values(for_validate)
def get_advances(self):
super(PurchaseInvoice, self).get_advances(self.credit_to, "Supplier", self.supplier,
"Purchase Invoice Advance", "advances", "debit", "purchase_order")
if not self.is_return:
super(PurchaseInvoice, self).get_advances(self.credit_to, "Supplier", self.supplier,
"Purchase Invoice Advance", "advances", "debit", "purchase_order")
def check_active_purchase_items(self):
for d in self.get('items'):
if d.item_code: # extra condn coz item_code is not mandatory in PV
if frappe.db.get_value("Item", d.item_code, "is_purchase_item") != 'Yes':
if frappe.db.get_value("Item", d.item_code, "is_purchase_item") != 1:
msgprint(_("Item {0} is not Purchase Item").format(d.item_code), raise_exception=True)
def check_conversion_rate(self):
@@ -126,7 +129,7 @@ class PurchaseInvoice(BuyingController):
if cint(frappe.db.get_single_value('Buying Settings', 'maintain_same_rate')):
self.validate_rate_with_reference_doc([
["Purchase Order", "purchase_order", "po_detail"],
["Purchase Order", "purchase_order", "po_detail"],
["Purchase Receipt", "purchase_receipt", "pr_detail"]
])
@@ -226,9 +229,11 @@ class PurchaseInvoice(BuyingController):
# this sequence because outstanding may get -negative
self.make_gl_entries()
self.update_against_document_in_jv()
self.update_prevdoc_status()
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
if not self.is_return:
self.update_against_document_in_jv()
self.update_prevdoc_status()
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
self.update_project()
def make_gl_entries(self):
@@ -250,7 +255,7 @@ class PurchaseInvoice(BuyingController):
"against": self.against_expense_account,
"credit": self.total_amount_to_pay,
"remarks": self.remarks,
"against_voucher": self.name,
"against_voucher": self.return_against if cint(self.is_return) else self.name,
"against_voucher_type": self.doctype,
})
)
@@ -358,11 +363,12 @@ class PurchaseInvoice(BuyingController):
make_gl_entries(gl_entries, cancel=(self.docstatus == 2))
def on_cancel(self):
from erpnext.accounts.utils import remove_against_link_from_jv
remove_against_link_from_jv(self.doctype, self.name, "against_voucher")
if not self.is_return:
from erpnext.accounts.utils import remove_against_link_from_jv
remove_against_link_from_jv(self.doctype, self.name, "against_voucher")
self.update_prevdoc_status()
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
self.update_prevdoc_status()
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
self.make_gl_entries_on_cancel()
self.update_project()
@@ -382,7 +388,8 @@ class PurchaseInvoice(BuyingController):
frappe.throw("Supplier Invoice Date cannot be greater than Posting Date")
if self.bill_no:
if cint(frappe.db.get_single_value("Accounts Settings", "check_supplier_invoice_uniqueness")):
pi = frappe.db.exists("Purchase Invoice", {"bill_no": self.bill_no, "fiscal_year": self.fiscal_year})
pi = frappe.db.exists("Purchase Invoice", {"bill_no": self.bill_no,
"fiscal_year": self.fiscal_year, "name": ("!=", self.name)})
if pi:
frappe.throw("Supplier Invoice No exists in Purchase Invoice {0}".format(pi))
@@ -402,3 +409,8 @@ def get_expense_account(doctype, txt, searchfield, start, page_len, filters):
and tabAccount.%(key)s LIKE '%(txt)s'
%(mcond)s""" % {'company': filters['company'], 'key': searchfield,
'txt': "%%%s%%" % frappe.db.escape(txt), 'mcond':get_match_cond(doctype)})
@frappe.whitelist()
def make_debit_note(source_name, target_doc=None):
from erpnext.controllers.sales_and_purchase_return import make_return_doc
return make_return_doc("Purchase Invoice", source_name, target_doc)

View File

@@ -4,9 +4,11 @@
// render
frappe.listview_settings['Purchase Invoice'] = {
add_fields: ["supplier", "supplier_name", "base_grand_total", "outstanding_amount", "due_date", "company",
"currency"],
"currency", "is_return"],
get_indicator: function(doc) {
if(flt(doc.outstanding_amount) > 0 && doc.docstatus==1) {
if(cint(doc.is_return)==1) {
return [__("Return"), "darkgrey", "is_return,=,1"];
} else if(flt(doc.outstanding_amount) > 0 && doc.docstatus==1) {
if(frappe.datetime.get_diff(doc.due_date) < 0) {
return [__("Overdue"), "red", "outstanding_amount,>,0|due_date,<,Today"];
} else {

View File

@@ -275,5 +275,58 @@ class TestPurchaseInvoice(unittest.TestCase):
purchase_invoice.cancel()
self.assertEqual(frappe.db.get_value("Project", "_Test Project", "total_purchase_cost"), 0)
def test_return_purchase_invoice(self):
set_perpetual_inventory()
pi = make_purchase_invoice()
return_pi = make_purchase_invoice(is_return=1, return_against=pi.name, qty=-2)
# check gl entries for return
gl_entries = frappe.db.sql("""select account, debit, credit
from `tabGL Entry` where voucher_type=%s and voucher_no=%s
order by account desc""", ("Purchase Invoice", return_pi.name), as_dict=1)
self.assertTrue(gl_entries)
expected_values = {
"Creditors - _TC": [100.0, 0.0],
"Stock Received But Not Billed - _TC": [0.0, 100.0],
}
for gle in gl_entries:
self.assertEquals(expected_values[gle.account][0], gle.debit)
self.assertEquals(expected_values[gle.account][1], gle.credit)
set_perpetual_inventory(0)
def make_purchase_invoice(**args):
pi = frappe.new_doc("Purchase Invoice")
args = frappe._dict(args)
if args.posting_date:
pi.posting_date = args.posting_date
if args.posting_time:
pi.posting_time = args.posting_time
pi.company = args.company or "_Test Company"
pi.supplier = args.supplier or "_Test Supplier"
pi.currency = args.currency or "INR"
pi.is_return = args.is_return
pi.return_against = args.return_against
pi.append("items", {
"item_code": args.item or args.item_code or "_Test Item",
"warehouse": args.warehouse or "_Test Warehouse - _TC",
"qty": args.qty or 5,
"rate": args.rate or 50,
"conversion_factor": 1.0,
"serial_no": args.serial_no,
"stock_uom": "_Test UOM"
})
if not args.do_not_save:
pi.insert()
if not args.do_not_submit:
pi.submit()
return pi
test_records = frappe.get_test_records('Purchase Invoice')

View File

@@ -4,10 +4,9 @@
from __future__ import unicode_literals
from frappe.model.document import Document
from erpnext.controllers.accounts_controller import validate_taxes_and_charges, validate_inclusive_tax
from erpnext.accounts.doctype.sales_taxes_and_charges_template.sales_taxes_and_charges_template \
import valdiate_taxes_and_charges_template
class PurchaseTaxesandChargesTemplate(Document):
def validate(self):
for tax in self.get("taxes"):
validate_taxes_and_charges(tax)
validate_inclusive_tax(tax, self)
valdiate_taxes_and_charges_template(self)

View File

@@ -11,9 +11,9 @@ def get_items(price_list, sales_or_purchase, item=None):
args = {"price_list": price_list}
if sales_or_purchase == "Sales":
condition = "i.is_sales_item='Yes'"
condition = "i.is_sales_item=1"
else:
condition = "i.is_purchase_item='Yes'"
condition = "i.is_purchase_item=1"
if item:
# search serial no

View File

@@ -40,22 +40,14 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
this._super();
cur_frm.dashboard.reset();
if(doc.docstatus==1) {
cur_frm.add_custom_button('View Ledger', function() {
frappe.route_options = {
"voucher_no": doc.name,
"from_date": doc.posting_date,
"to_date": doc.posting_date,
"company": doc.company,
group_by_voucher: 0
};
frappe.set_route("query-report", "General Ledger");
}, "icon-table");
// var percent_paid = cint(flt(doc.base_grand_total - doc.outstanding_amount) / flt(doc.base_grand_total) * 100);
// cur_frm.dashboard.add_progress(percent_paid + "% Paid", percent_paid);
this.frm.toggle_reqd("due_date", !this.frm.doc.is_return);
this.show_general_ledger();
if(doc.update_stock) this.show_stock_ledger();
if(doc.docstatus==1 && !doc.is_return) {
if(cint(doc.update_stock)!=1) {
// show Make Delivery Note button only if Sales Invoice is not created from Delivery Note
var from_delivery_note = false;
@@ -65,17 +57,20 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
});
if(!from_delivery_note) {
cur_frm.add_custom_button(__('Make Delivery'), cur_frm.cscript['Make Delivery Note'], "icon-truck")
cur_frm.add_custom_button(__('Make Delivery'), cur_frm.cscript['Make Delivery Note'])
}
}
if(doc.outstanding_amount!=0) {
cur_frm.add_custom_button(__('Make Payment Entry'), cur_frm.cscript.make_bank_entry, "icon-money");
if(doc.outstanding_amount!=0 && !cint(doc.is_return)) {
cur_frm.add_custom_button(__('Make Payment Entry'), cur_frm.cscript.make_bank_entry);
}
cur_frm.add_custom_button(doc.update_stock ? __('Make Sales Return') : __('Make Credit Note'),
this.make_sales_return);
}
// Show buttons only when pos view is active
if (cint(doc.docstatus==0) && cur_frm.page.current_view_name!=="pos") {
if (cint(doc.docstatus==0) && cur_frm.page.current_view_name!=="pos" && !doc.is_return) {
cur_frm.cscript.sales_order_btn();
cur_frm.cscript.delivery_note_btn();
}
@@ -205,8 +200,14 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
items_on_form_rendered: function() {
erpnext.setup_serial_no();
},
make_sales_return: function() {
frappe.model.open_mapped_doc({
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.make_sales_return",
frm: cur_frm
})
}
});
// for backward compatibility: combine new and previous states
@@ -283,16 +284,6 @@ cur_frm.cscript.make_bank_entry = function() {
});
}
cur_frm.fields_dict.debit_to.get_query = function(doc) {
return{
filters: {
'report_type': 'Balance Sheet',
'is_group': 0,
'company': doc.company
}
}
}
cur_frm.fields_dict.cash_bank_account.get_query = function(doc) {
return {
filters: [
@@ -399,4 +390,4 @@ cur_frm.set_query("debit_to", function(doc) {
['Account', 'account_type', '=', 'Receivable']
]
}
});
});

View File

@@ -21,7 +21,7 @@
"no_copy": 1,
"oldfieldname": "naming_series",
"oldfieldtype": "Select",
"options": "SINV-",
"options": "SINV-\nSINV-RET-",
"permlevel": 0,
"print_hide": 1,
"read_only": 0,
@@ -156,7 +156,7 @@
"oldfieldtype": "Date",
"permlevel": 0,
"read_only": 0,
"reqd": 1,
"reqd": 0,
"search_index": 0
},
{
@@ -169,6 +169,28 @@
"print_hide": 1,
"read_only": 0
},
{
"fieldname": "is_return",
"fieldtype": "Check",
"label": "Is Return",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 1
},
{
"depends_on": "is_return",
"fieldname": "return_against",
"fieldtype": "Link",
"label": "Return Against Sales Invoice",
"no_copy": 0,
"options": "Sales Invoice",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "shipping_address_name",
"fieldtype": "Link",
@@ -1252,8 +1274,8 @@
],
"icon": "icon-file-text",
"idx": 1,
"is_submittable": 1,
"modified": "2015-07-09 17:33:28.583808",
"is_submittable": 1,
"modified": "2015-07-24 11:48:07.544569",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",

View File

@@ -4,7 +4,7 @@
from __future__ import unicode_literals
import frappe
import frappe.defaults
from frappe.utils import cint, cstr, flt
from frappe.utils import cint, flt
from frappe import _, msgprint, throw
from erpnext.accounts.party import get_party_account, get_due_date
from erpnext.controllers.stock_controller import update_gl_entries_after
@@ -66,6 +66,7 @@ class SalesInvoice(SellingController):
self.validate_c_form()
self.validate_time_logs_are_submitted()
self.validate_multiple_billing("Delivery Note", "dn_detail", "amount", "items")
self.update_packing_list()
def on_submit(self):
super(SalesInvoice, self).on_submit()
@@ -80,14 +81,16 @@ class SalesInvoice(SellingController):
self.check_prev_docstatus()
self.update_status_updater_args()
self.update_prevdoc_status()
self.update_billing_status_for_zero_amount_refdoc("Sales Order")
self.check_credit_limit()
if not self.is_return:
self.update_status_updater_args()
self.update_prevdoc_status()
self.update_billing_status_for_zero_amount_refdoc("Sales Order")
self.check_credit_limit()
# this sequence because outstanding may get -ve
self.make_gl_entries()
if not cint(self.is_pos) == 1:
if not cint(self.is_pos) == 1 and not self.is_return:
self.update_against_document_in_jv()
self.update_time_log_batch(self.name)
@@ -104,9 +107,11 @@ class SalesInvoice(SellingController):
from erpnext.accounts.utils import remove_against_link_from_jv
remove_against_link_from_jv(self.doctype, self.name, "against_invoice")
self.update_status_updater_args()
self.update_prevdoc_status()
self.update_billing_status_for_zero_amount_refdoc("Sales Order")
if not self.is_return:
self.update_status_updater_args()
self.update_prevdoc_status()
self.update_billing_status_for_zero_amount_refdoc("Sales Order")
self.validate_c_form_on_cancel()
self.make_gl_entries_on_cancel()
@@ -199,8 +204,9 @@ class SalesInvoice(SellingController):
self.set_taxes()
def get_advances(self):
super(SalesInvoice, self).get_advances(self.debit_to, "Customer", self.customer,
"Sales Invoice Advance", "advances", "credit", "sales_order")
if not self.is_return:
super(SalesInvoice, self).get_advances(self.debit_to, "Customer", self.customer,
"Sales Invoice Advance", "advances", "credit", "sales_order")
def get_company_abbr(self):
return frappe.db.sql("select abbr from tabCompany where name=%s", self.company)[0][0]
@@ -243,12 +249,10 @@ class SalesInvoice(SellingController):
def validate_fixed_asset_account(self):
"""Validate Fixed Asset and whether Income Account Entered Exists"""
for d in self.get('items'):
item = frappe.db.sql("""select name,is_asset_item,is_sales_item from `tabItem`
where name = %s""", d.item_code)
acc = frappe.db.sql("""select account_type from `tabAccount`
where name = %s and docstatus != 2""", d.income_account)
if item and item[0][1] == 'Yes' and acc and acc[0][0] != 'Fixed Asset':
msgprint(_("Account {0} must be of type 'Fixed Asset' as Item {1} is an Asset Item").format(acc[0][0], d.item_code), raise_exception=True)
is_asset_item = frappe.db.get_value("Item", d.item_code, "is_asset_item")
account_type = frappe.db.get_value("Account", d.income_account, "account_type")
if is_asset_item == 1 and account_type != 'Fixed Asset':
msgprint(_("Account {0} must be of type 'Fixed Asset' as Item {1} is an Asset Item").format(d.income_account, d.item_code), raise_exception=True)
def validate_with_previous_doc(self):
super(SalesInvoice, self).validate_with_previous_doc({
@@ -266,7 +270,7 @@ class SalesInvoice(SellingController):
if cint(frappe.db.get_single_value('Selling Settings', 'maintain_same_sales_rate')):
self.validate_rate_with_reference_doc([
["Sales Order", "sales_order", "so_detail"],
["Sales Order", "sales_order", "so_detail"],
["Delivery Note", "delivery_note", "dn_detail"]
])
@@ -285,11 +289,13 @@ class SalesInvoice(SellingController):
def so_dn_required(self):
"""check in manage account if sales order / delivery note required or not."""
if self.is_return:
return
dic = {'Sales Order':'so_required','Delivery Note':'dn_required'}
for i in dic:
if frappe.db.get_value('Selling Settings', None, dic[i]) == 'Yes':
for d in self.get('items'):
if frappe.db.get_value('Item', d.item_code, 'is_stock_item') == 'Yes' \
if frappe.db.get_value('Item', d.item_code, 'is_stock_item') == 1 \
and not d.get(i.lower().replace(' ','_')):
msgprint(_("{0} is mandatory for Item {1}").format(i,d.item_code), raise_exception=1)
@@ -358,6 +364,13 @@ class SalesInvoice(SellingController):
d.actual_qty = bin and flt(bin[0]['actual_qty']) or 0
d.projected_qty = bin and flt(bin[0]['projected_qty']) or 0
def update_packing_list(self):
if cint(self.update_stock) == 1:
from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
make_packing_list(self, 'items')
else:
self.set('packed_items', [])
def get_warehouse(self):
user_pos_profile = frappe.db.sql("""select name, warehouse from `tabPOS Profile`
@@ -376,20 +389,6 @@ class SalesInvoice(SellingController):
return warehouse
def on_update(self):
if cint(self.update_stock) == 1:
# Set default warehouse from POS Profile
if cint(self.is_pos) == 1:
w = self.get_warehouse()
if w:
for d in self.get('items'):
if not d.warehouse:
d.warehouse = cstr(w)
from erpnext.stock.doctype.packed_item.packed_item import make_packing_list
make_packing_list(self, 'items')
else:
self.set('packed_items', [])
if cint(self.is_pos) == 1:
if flt(self.paid_amount) == 0:
if self.cash_bank_account:
@@ -419,13 +418,19 @@ class SalesInvoice(SellingController):
def update_stock_ledger(self):
sl_entries = []
for d in self.get_item_list():
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == "Yes" \
and d.warehouse:
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1 and d.warehouse and flt(d['qty']):
self.update_reserved_qty(d)
incoming_rate = 0
if cint(self.is_return) and self.return_against and self.docstatus==1:
incoming_rate = self.get_incoming_rate_for_sales_return(d.item_code,
self.return_against)
sl_entries.append(self.get_sl_entries(d, {
"actual_qty": -1*flt(d.qty),
"stock_uom": frappe.db.get_value("Item", d.item_code, "stock_uom")
"stock_uom": frappe.db.get_value("Item", d.item_code, "stock_uom"),
"incoming_rate": incoming_rate
}))
self.make_sl_entries(sl_entries)
def make_gl_entries(self, repost_future_gle=True):
@@ -435,8 +440,7 @@ class SalesInvoice(SellingController):
from erpnext.accounts.general_ledger import make_gl_entries
# if POS and amount is written off, there's no outstanding and hence no need to update it
update_outstanding = cint(self.is_pos) and self.write_off_account \
and 'No' or 'Yes'
update_outstanding = "No" if (cint(self.is_pos) or self.write_off_account) else "Yes"
make_gl_entries(gl_entries, cancel=(self.docstatus == 2),
update_outstanding=update_outstanding, merge_entries=False)
@@ -484,7 +488,7 @@ class SalesInvoice(SellingController):
"against": self.against_income_account,
"debit": self.base_grand_total,
"remarks": self.remarks,
"against_voucher": self.name,
"against_voucher": self.return_against if cint(self.is_return) else self.name,
"against_voucher_type": self.doctype
})
)
@@ -519,7 +523,6 @@ class SalesInvoice(SellingController):
# expense account gl entries
if cint(frappe.defaults.get_global_default("auto_accounting_for_stock")) \
and cint(self.update_stock):
gl_entries += super(SalesInvoice, self).get_gl_entries()
def make_pos_gl_entries(self, gl_entries):
@@ -533,7 +536,7 @@ class SalesInvoice(SellingController):
"against": self.cash_bank_account,
"credit": self.paid_amount,
"remarks": self.remarks,
"against_voucher": self.name,
"against_voucher": self.return_against if cint(self.is_return) else self.name,
"against_voucher_type": self.doctype,
})
)
@@ -557,7 +560,7 @@ class SalesInvoice(SellingController):
"against": self.write_off_account,
"credit": self.write_off_amount,
"remarks": self.remarks,
"against_voucher": self.name,
"against_voucher": self.return_against if cint(self.is_return) else self.name,
"against_voucher_type": self.doctype,
})
)
@@ -651,3 +654,9 @@ def make_delivery_note(source_name, target_doc=None):
}, target_doc, set_missing_values)
return doclist
@frappe.whitelist()
def make_sales_return(source_name, target_doc=None):
from erpnext.controllers.sales_and_purchase_return import make_return_doc
return make_return_doc("Sales Invoice", source_name, target_doc)

View File

@@ -4,9 +4,11 @@
// render
frappe.listview_settings['Sales Invoice'] = {
add_fields: ["customer", "customer_name", "base_grand_total", "outstanding_amount", "due_date", "company",
"currency"],
"currency", "is_return"],
get_indicator: function(doc) {
if(flt(doc.outstanding_amount)==0) {
if(cint(doc.is_return)==1) {
return [__("Return"), "darkgrey", "is_return,=,1"];
} else if(flt(doc.outstanding_amount)==0) {
return [__("Paid"), "green", "outstanding_amount,=,0"]
} else if (flt(doc.outstanding_amount) > 0 && doc.due_date > frappe.datetime.get_today()) {
return [__("Unpaid"), "orange", "outstanding_amount,>,0|due_date,>,Today"]

View File

@@ -4,12 +4,9 @@ from __future__ import unicode_literals
import frappe
import unittest, copy
import time
from frappe.utils import nowdate, add_days
from erpnext.accounts.utils import get_stock_and_account_difference
from frappe.utils import nowdate, add_days, flt
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry, get_qty_after_transaction
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
from erpnext.projects.doctype.time_log_batch.test_time_log_batch import *
class TestSalesInvoice(unittest.TestCase):
def make(self):
@@ -403,32 +400,6 @@ class TestSalesInvoice(unittest.TestCase):
jv.cancel()
self.assertEquals(frappe.db.get_value("Sales Invoice", w.name, "outstanding_amount"), 561.8)
def test_time_log_batch(self):
delete_time_log_and_batch()
time_log = create_time_log()
tlb = create_time_log_batch(time_log)
tlb = frappe.get_doc("Time Log Batch", tlb.name)
tlb.submit()
si = frappe.get_doc(frappe.copy_doc(test_records[0]))
si.get("items")[0].time_log_batch = tlb.name
si.insert()
si.submit()
self.assertEquals(frappe.db.get_value("Time Log Batch", tlb.name, "status"), "Billed")
self.assertEquals(frappe.db.get_value("Time Log", time_log, "status"), "Billed")
si.cancel()
self.assertEquals(frappe.db.get_value("Time Log Batch", tlb.name, "status"), "Submitted")
self.assertEquals(frappe.db.get_value("Time Log", time_log, "status"), "Batched for Billing")
frappe.delete_doc("Sales Invoice", si.name)
delete_time_log_and_batch()
def test_sales_invoice_gl_entry_without_aii(self):
set_perpetual_inventory(0)
si = frappe.copy_doc(test_records[1])
@@ -772,6 +743,53 @@ class TestSalesInvoice(unittest.TestCase):
si1 = create_sales_invoice(posting_date="2015-07-05")
self.assertEqual(si1.due_date, "2015-08-31")
def test_return_sales_invoice(self):
set_perpetual_inventory()
make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=50, incoming_rate=100)
actual_qty_0 = get_qty_after_transaction()
si = create_sales_invoice(qty=5, rate=500, update_stock=1)
actual_qty_1 = get_qty_after_transaction()
self.assertEquals(actual_qty_0 - 5, actual_qty_1)
# outgoing_rate
outgoing_rate = frappe.db.get_value("Stock Ledger Entry", {"voucher_type": "Sales Invoice",
"voucher_no": si.name}, "stock_value_difference") / 5
# return entry
si1 = create_sales_invoice(is_return=1, return_against=si.name, qty=-2, rate=500, update_stock=1)
actual_qty_2 = get_qty_after_transaction()
self.assertEquals(actual_qty_1 + 2, actual_qty_2)
incoming_rate, stock_value_difference = frappe.db.get_value("Stock Ledger Entry",
{"voucher_type": "Sales Invoice", "voucher_no": si1.name},
["incoming_rate", "stock_value_difference"])
self.assertEquals(flt(incoming_rate, 3), abs(flt(outgoing_rate, 3)))
# Check gl entry
gle_warehouse_amount = frappe.db.get_value("GL Entry", {"voucher_type": "Sales Invoice",
"voucher_no": si1.name, "account": "_Test Warehouse - _TC"}, "debit")
self.assertEquals(gle_warehouse_amount, stock_value_difference)
party_credited = frappe.db.get_value("GL Entry", {"voucher_type": "Sales Invoice",
"voucher_no": si1.name, "account": "Debtors - _TC", "party": "_Test Customer"}, "credit")
self.assertEqual(party_credited, 1000)
# Check outstanding amount
self.assertFalse(si1.outstanding_amount)
self.assertEqual(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"), 1500)
set_perpetual_inventory(0)
def create_sales_invoice(**args):
si = frappe.new_doc("Sales Invoice")
@@ -784,6 +802,10 @@ def create_sales_invoice(**args):
si.debit_to = args.debit_to or "Debtors - _TC"
si.update_stock = args.update_stock
si.is_pos = args.is_pos
si.is_return = args.is_return
si.return_against = args.return_against
si.currency="INR"
si.conversion_rate = 1
si.append("items", {
"item_code": args.item or args.item_code or "_Test Item",

View File

@@ -5,21 +5,25 @@ from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
from erpnext.controllers.accounts_controller import validate_taxes_and_charges, validate_inclusive_tax
from frappe.utils.nestedset import get_root_of
class SalesTaxesandChargesTemplate(Document):
def validate(self):
if self.is_default == 1:
frappe.db.sql("""update `tabSales Taxes and Charges Template`
set is_default = 0
where ifnull(is_default,0) = 1
and name != %s and company = %s""",
(self.name, self.company))
valdiate_taxes_and_charges_template(self)
# at least one territory
self.validate_table_has_rows("territories")
def valdiate_taxes_and_charges_template(doc):
if not doc.is_default and not frappe.get_all(doc.doctype, filters={"is_default": 1}):
doc.is_default = 1
for tax in self.get("taxes"):
validate_taxes_and_charges(tax)
validate_inclusive_tax(tax, self)
if doc.is_default == 1:
frappe.db.sql("""update `tab{0}` set is_default = 0
where ifnull(is_default,0) = 1 and name != %s and company = %s""".format(doc.doctype),
(doc.name, doc.company))
if doc.meta.get_field("territories"):
if not doc.territories:
doc.append("territories", {"territory": get_root_of("Territory") })
for tax in doc.get("taxes"):
validate_taxes_and_charges(tax)
validate_inclusive_tax(tax, doc)

View File

@@ -1,19 +1,19 @@
{
"creation": "2014-08-28 11:11:39.796473",
"disabled": 0,
"doc_type": "Journal Entry",
"docstatus": 0,
"doctype": "Print Format",
"html": "{%- from \"templates/print_formats/standard_macros.html\" import add_header -%}\n\n<div class=\"page-break\">\n {%- if not doc.get(\"print_heading\") and not doc.get(\"select_print_heading\") \n and doc.set(\"select_print_heading\", _(\"Credit Note\")) -%}{%- endif -%}\n {{ add_header(0, 1, doc, letter_head, no_letterhead) }}\n\n {%- for label, value in (\n (_(\"Credit To\"), doc.pay_to_recd_from),\n (_(\"Date\"), frappe.utils.formatdate(doc.voucher_date)),\n (_(\"Amount\"), \"<strong>\" + doc.get_formatted(\"total_amount\") + \"</strong><br>\" + (doc.total_amount_in_words or \"\") + \"<br>\"),\n (_(\"Remarks\"), doc.remark)\n ) -%}\n\n <div class=\"row\">\n <div class=\"col-xs-3\"><label class=\"text-right\">{{ label }}</label></div>\n <div class=\"col-xs-9\">{{ value }}</div>\n </div>\n\n {%- endfor -%}\n\n <hr>\n <br>\n <p class=\"strong\">\n {{ _(\"For\") }} {{ doc.company }},<br>\n <br>\n <br>\n <br>\n {{ _(\"Authorized Signatory\") }}\n </p>\n</div>\n\n\n",
"idx": 2,
"modified": "2015-01-12 11:02:25.716825",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Credit Note",
"owner": "Administrator",
"parent": "Journal Entry",
"parentfield": "__print_formats",
"parenttype": "DocType",
"print_format_type": "Server",
"creation": "2014-08-28 11:11:39.796473",
"custom_format": 0,
"disabled": 0,
"doc_type": "Journal Entry",
"docstatus": 0,
"doctype": "Print Format",
"html": "{%- from \"templates/print_formats/standard_macros.html\" import add_header -%}\n\n<div class=\"page-break\">\n {%- if not doc.get(\"print_heading\") and not doc.get(\"select_print_heading\") \n and doc.set(\"select_print_heading\", _(\"Credit Note\")) -%}{%- endif -%}\n {{ add_header(0, 1, doc, letter_head, no_letterhead) }}\n\n {%- for label, value in (\n (_(\"Credit To\"), doc.pay_to_recd_from),\n (_(\"Date\"), frappe.utils.formatdate(doc.voucher_date)),\n (_(\"Amount\"), \"<strong>\" + doc.get_formatted(\"total_amount\") + \"</strong><br>\" + (doc.total_amount_in_words or \"\") + \"<br>\"),\n (_(\"Remarks\"), doc.remark)\n ) -%}\n\n <div class=\"row\">\n <div class=\"col-xs-3\"><label class=\"text-right\">{{ label }}</label></div>\n <div class=\"col-xs-9\">{{ value }}</div>\n </div>\n\n {%- endfor -%}\n\n <hr>\n <br>\n <p class=\"strong\">\n {{ _(\"For\") }} {{ doc.company }},<br>\n <br>\n <br>\n <br>\n {{ _(\"Authorized Signatory\") }}\n </p>\n</div>\n\n\n",
"idx": 2,
"modified": "2015-07-22 17:42:01.560817",
"modified_by": "Administrator",
"name": "Credit Note",
"owner": "Administrator",
"parent": "Journal Entry",
"parentfield": "__print_formats",
"parenttype": "DocType",
"print_format_type": "Server",
"standard": "Yes"
}
}

View File

@@ -0,0 +1,17 @@
{
"creation": "2015-07-22 17:45:22.220567",
"custom_format": 1,
"disabled": 0,
"doc_type": "Sales Invoice",
"docstatus": 0,
"doctype": "Print Format",
"font": "Default",
"html": "<style>\n\t.print-format table, .print-format tr, \n\t.print-format td, .print-format div, .print-format p {\n\t\tfont-family: Monospace;\n\t\tline-height: 200%;\n\t\tvertical-align: middle;\n\t}\n\t@media screen {\n\t\t.print-format {\n\t\t\twidth: 4in;\n\t\t\tpadding: 0.25in;\n\t\t\tmin-height: 6in;\n\t\t}\n\t}\n</style>\n\n<p class=\"text-center\">\n\t{{ doc.company }}<br>\n\t{{ doc.select_print_heading or _(\"Credit Note\") }}<br>\n</p>\n\n<hr>\n\n{%- for label, value in (\n (_(\"Receipt No\"), doc.name),\n (_(\"Date\"), doc.get_formatted(\"posting_date\")),\n\t(_(\"Customer\"), doc.customer_name),\n (_(\"Amount\"), \"<strong>\" + doc.get_formatted(\"grand_total\", absolute_value=True) + \"</strong><br>\" + (doc.in_words or \"\")),\n\t(_(\"Against\"), doc.return_against),\n (_(\"Remarks\"), doc.remarks)\n) -%}\n\n\t\t<div class=\"row\">\n\t\t <div class=\"col-xs-4\"><label class=\"text-right\">{{ label }}</label></div>\n\t\t <div class=\"col-xs-8\">{{ value }}</div>\n\t\t</div>\n{%- endfor -%}\n\n<hr>\n<br>\n<p class=\"strong\">\n {{ _(\"For\") }} {{ doc.company }},<br>\n <br>\n <br>\n <br>\n {{ _(\"Authorized Signatory\") }}\n</p>",
"modified": "2015-07-22 17:45:22.220567",
"modified_by": "Administrator",
"name": "Credit Note - Negative Invoice",
"owner": "Administrator",
"print_format_builder": 0,
"print_format_type": "Server",
"standard": "Yes"
}

View File

@@ -1,17 +1,17 @@
{
"add_total_row": 1,
"apply_user_permissions": 1,
"creation": "2013-04-22 16:16:03",
"docstatus": 0,
"doctype": "Report",
"idx": 1,
"is_standard": "Yes",
"modified": "2014-06-03 07:18:10.985354",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Payable",
"owner": "Administrator",
"ref_doctype": "Purchase Invoice",
"report_name": "Accounts Payable",
"report_type": "Report Builder"
}
"add_total_row": 1,
"apply_user_permissions": 1,
"creation": "2013-04-22 16:16:03",
"docstatus": 0,
"doctype": "Report",
"idx": 1,
"is_standard": "Yes",
"modified": "2015-07-24 01:08:20.996267",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Payable",
"owner": "Administrator",
"ref_doctype": "Purchase Invoice",
"report_name": "Accounts Payable",
"report_type": "Script Report"
}

View File

@@ -1,5 +1,5 @@
<div style="margin-bottom: 7px;" class="text-center">
{%= frappe.boot.letter_heads[frappe.defaults.get_default("letter_head")] %}
{%= frappe.boot.letter_heads[filters.letter_head || frappe.defaults.get_default("letter_head")] %}
</div>
<h2 class="text-center">{%= __("Statement of Account") %}</h2>
<h4 class="text-center">{%= (filters.party || filters.account) && ((filters.party || filters.account) + ", ") || "" %} {%= filters.company %}</h4>

View File

@@ -80,6 +80,13 @@ frappe.query_reports["General Ledger"] = {
"fieldname":"group_by_account",
"label": __("Group by Account"),
"fieldtype": "Check",
},
{
"fieldname":"letter_head",
"label": __("Letter Head"),
"fieldtype": "Link",
"options": "Letter Head",
"default": frappe.defaults.get_default("letter_head"),
}
]
}

View File

@@ -174,12 +174,12 @@ class GrossProfitGenerator(object):
return flt(row.qty) * item_rate
else:
if row.update_stock or row.dn_detail:
my_sle = self.sle.get((item_code, row.warehouse))
if (row.update_stock or row.dn_detail) and my_sle:
parenttype, parent, item_row = row.parenttype, row.parent, row.item_row
if row.dn_detail:
parenttype, parent, item_row = "Delivery Note", row.delivery_note, row.dn_detail
my_sle = self.sle.get((item_code, row.warehouse))
for i, sle in enumerate(my_sle):
# find the stock valution rate from stock ledger entry
if sle.voucher_type == parenttype and parent == sle.voucher_no and \
@@ -215,7 +215,7 @@ class GrossProfitGenerator(object):
if self.filters.to_date:
conditions += " and posting_date <= %(to_date)s"
self.si_list = frappe.db.sql("""select item.parenttype, item.parent,
self.si_list = frappe.db.sql("""select item.parenttype, item.parent,
si.posting_date, si.posting_time, si.project_name, si.update_stock,
si.customer, si.customer_group, si.territory,
item.item_code, item.item_name, item.description, item.warehouse,
@@ -257,4 +257,4 @@ class GrossProfitGenerator(object):
def load_non_stock_items(self):
self.non_stock_items = frappe.db.sql_list("""select name from tabItem
where ifnull(is_stock_item, 'No')='No'""")
where is_stock_item=0""")

View File

@@ -44,12 +44,12 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
if(me.frm.doc.is_subcontracted == "Yes") {
return{
query: "erpnext.controllers.queries.item_query",
filters:{ 'is_sub_contracted_item': 'Yes' }
filters:{ 'is_sub_contracted_item': 1 }
}
} else {
return{
query: "erpnext.controllers.queries.item_query",
filters: { 'is_purchase_item': 'Yes' }
filters: { 'is_purchase_item': 1 }
}
}
});
@@ -164,8 +164,10 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
frappe.model.round_floats_in(this.frm.doc, ["base_grand_total", "total_advance", "write_off_amount"]);
this.frm.doc.total_amount_to_pay = flt(this.frm.doc.base_grand_total - this.frm.doc.write_off_amount,
precision("total_amount_to_pay"));
this.frm.doc.outstanding_amount = flt(this.frm.doc.total_amount_to_pay - this.frm.doc.total_advance,
precision("outstanding_amount"));
if (!this.frm.doc.is_return) {
this.frm.doc.outstanding_amount = flt(this.frm.doc.total_amount_to_pay - this.frm.doc.total_advance,
precision("outstanding_amount"));
}
}
}
});

View File

@@ -41,8 +41,7 @@ class PurchaseCommon(BuyingController):
def validate_for_items(self, obj):
items = []
for d in obj.get("items"):
# validation for valid qty
if flt(d.qty) < 0 or (d.parenttype != 'Purchase Receipt' and not flt(d.qty)):
if not d.qty:
frappe.throw(_("Please enter quantity for Item {0}").format(d.item_code))
# udpate with latest quantities
@@ -57,24 +56,25 @@ class PurchaseCommon(BuyingController):
d.set(x, f_lst[x])
item = frappe.db.sql("""select is_stock_item, is_purchase_item,
is_sub_contracted_item, end_of_life from `tabItem` where name=%s""", d.item_code)
is_sub_contracted_item, end_of_life from `tabItem` where name=%s""",
d.item_code, as_dict=1)[0]
from erpnext.stock.doctype.item.item import validate_end_of_life
validate_end_of_life(d.item_code, item[0][3])
validate_end_of_life(d.item_code, item.end_of_life)
# validate stock item
if item[0][0]=='Yes' and d.qty and not d.warehouse:
if item.is_stock_item==1 and d.qty and not d.warehouse:
frappe.throw(_("Warehouse is mandatory for stock Item {0} in row {1}").format(d.item_code, d.idx))
# validate purchase item
if not (obj.doctype=="Material Request" and getattr(obj, "material_request_type", None)=="Material Transfer"):
if item[0][1] != 'Yes' and item[0][2] != 'Yes':
if item.is_purchase_item != 1 and item.is_sub_contracted_item != 1:
frappe.throw(_("{0} must be a Purchased or Sub-Contracted Item in row {1}").format(d.item_code, d.idx))
items.append(cstr(d.item_code))
if items and len(items) != len(set(items)):
frappe.msgprint(_("Warning: Same item has been entered multiple times."))
def check_for_stopped_status(self, doctype, docname):
stopped = frappe.db.sql("""select name from `tab%s` where name = %s and

View File

@@ -11,39 +11,32 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
this._super();
// this.frm.dashboard.reset();
if(doc.docstatus == 1 && doc.status != 'Stopped'){
// cur_frm.dashboard.add_progress(cint(doc.per_received) + __("% Received"),
// doc.per_received);
// cur_frm.dashboard.add_progress(cint(doc.per_billed) + __("% Billed"),
// doc.per_billed);
if(doc.docstatus == 1 && doc.status != 'Stopped') {
if(flt(doc.per_received, 2) < 100) {
cur_frm.add_custom_button(__('Make Purchase Receipt'),
this.make_purchase_receipt);
cur_frm.add_custom_button(__('Make Purchase Receipt'), this.make_purchase_receipt);
if(doc.is_subcontracted==="Yes") {
cur_frm.add_custom_button(__('Transfer Material to Supplier'),
function() { me.make_stock_entry() });
cur_frm.add_custom_button(__('Transfer Material to Supplier'), this.make_stock_entry);
}
}
if(flt(doc.per_billed, 2) < 100)
cur_frm.add_custom_button(__('Make Invoice'), this.make_purchase_invoice,
frappe.boot.doctype_icons["Purchase Invoice"]);
cur_frm.add_custom_button(__('Make Invoice'), this.make_purchase_invoice);
if(flt(doc.per_billed, 2) < 100 || doc.per_received < 100)
cur_frm.add_custom_button(__('Stop'), cur_frm.cscript['Stop Purchase Order'],
"icon-exclamation", "btn-default");
cur_frm.add_custom_button(__('Stop'), cur_frm.cscript['Stop Purchase Order']);
} else if(doc.docstatus===0) {
cur_frm.cscript.add_from_mappers();
}
if(doc.docstatus == 1 && doc.status == 'Stopped')
cur_frm.add_custom_button(__('Unstop Purchase Order'),
cur_frm.cscript['Unstop Purchase Order'], "icon-check");
cur_frm.add_custom_button(__('Unstop Purchase Order'), cur_frm.cscript['Unstop Purchase Order']);
},
make_stock_entry: function() {
var items = $.map(cur_frm.doc.items, function(d) { return d.bom ? d.item_code : false; }),
me = this;
var items = $.map(cur_frm.doc.items, function(d) { return d.bom ? d.item_code : false; });
var me = this;
if(items.length===1) {
me._make_stock_entry(items[0]);
return;
@@ -96,7 +89,7 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
company: cur_frm.doc.company
}
})
}, "icon-download", "btn-default"
}
);
cur_frm.add_custom_button(__('From Supplier Quotation'),
@@ -110,7 +103,7 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
company: cur_frm.doc.company
}
})
}, "icon-download", "btn-default"
}
);
cur_frm.add_custom_button(__('For Supplier'),
@@ -122,7 +115,7 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
docstatus: ["!=", 2],
}
})
}, "icon-download", "btn-default"
}
);
},

View File

@@ -882,7 +882,7 @@
"icon": "icon-file-text",
"idx": 1,
"is_submittable": 1,
"modified": "2015-07-03 03:26:43.080551",
"modified": "2015-07-13 05:28:29.397705",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order",
@@ -899,7 +899,7 @@
"print": 0,
"read": 1,
"report": 1,
"role": "Material User",
"role": "Stock User",
"submit": 0,
"write": 0
},

View File

@@ -153,7 +153,7 @@ class PurchaseOrder(BuyingController):
item_wh_list = []
for d in self.get("items"):
if (not po_item_rows or d.name in po_item_rows) and [d.item_code, d.warehouse] not in item_wh_list \
and frappe.db.get_value("Item", d.item_code, "is_stock_item") == "Yes" and d.warehouse:
and frappe.db.get_value("Item", d.item_code, "is_stock_item") and d.warehouse:
item_wh_list.append([d.item_code, d.warehouse])
for item_code, warehouse in item_wh_list:

View File

@@ -5,7 +5,13 @@ frappe.listview_settings['Purchase Order'] = {
if(doc.status==="Stopped") {
return [__("Stopped"), "darkgrey", "status,=,Stopped"];
} else if(flt(doc.per_received) < 100 && doc.status!=="Stopped") {
return [__("Not Received"), "orange", "per_received,<,100|status,!=,Stopped"];
if(flt(doc.per_billed) < 100) {
return [__("To Receive and Bill"), "orange",
"per_received,<,100|per_billed,<,100|status,!=,Stopped"];
} else {
return [__("To Receive"), "orange",
"per_received,<,100|per_billed,=,100|status,!=,Stopped"];
}
} else if(flt(doc.per_received) == 100 && flt(doc.per_billed) < 100 && doc.status!=="Stopped") {
return [__("To Bill"), "orange", "per_received,=,100|per_billed,<,100|status,!=,Stopped"];
} else if(flt(doc.per_received) == 100 && flt(doc.per_billed) == 100 && doc.status!=="Stopped") {

View File

@@ -1,247 +1,254 @@
{
"allow_import": 1,
"allow_rename": 1,
"autoname": "naming_series:",
"creation": "2013-01-10 16:34:11",
"description": "Supplier of Goods or Services.",
"docstatus": 0,
"doctype": "DocType",
"document_type": "Master",
"allow_import": 1,
"allow_rename": 1,
"autoname": "naming_series:",
"creation": "2013-01-10 16:34:11",
"description": "Supplier of Goods or Services.",
"docstatus": 0,
"doctype": "DocType",
"document_type": "Master",
"fields": [
{
"fieldname": "basic_info",
"fieldtype": "Section Break",
"label": "",
"oldfieldtype": "Section Break",
"options": "icon-user",
"fieldname": "basic_info",
"fieldtype": "Section Break",
"label": "",
"oldfieldtype": "Section Break",
"options": "icon-user",
"permlevel": 0
},
},
{
"fieldname": "naming_series",
"fieldtype": "Select",
"label": "Series",
"no_copy": 1,
"oldfieldname": "naming_series",
"oldfieldtype": "Select",
"options": "SUPP-",
"fieldname": "naming_series",
"fieldtype": "Select",
"label": "Series",
"no_copy": 1,
"oldfieldname": "naming_series",
"oldfieldtype": "Select",
"options": "SUPP-",
"permlevel": 0
},
},
{
"fieldname": "supplier_name",
"fieldtype": "Data",
"in_list_view": 0,
"label": "Supplier Name",
"no_copy": 1,
"oldfieldname": "supplier_name",
"oldfieldtype": "Data",
"permlevel": 0,
"fieldname": "supplier_name",
"fieldtype": "Data",
"in_list_view": 0,
"label": "Supplier Name",
"no_copy": 1,
"oldfieldname": "supplier_name",
"oldfieldtype": "Data",
"permlevel": 0,
"reqd": 1
},
},
{
"fieldname": "column_break0",
"fieldtype": "Column Break",
"permlevel": 0,
"fieldname": "column_break0",
"fieldtype": "Column Break",
"permlevel": 0,
"width": "50%"
},
},
{
"fieldname": "supplier_type",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Supplier Type",
"oldfieldname": "supplier_type",
"oldfieldtype": "Link",
"options": "Supplier Type",
"permlevel": 0,
"fieldname": "supplier_type",
"fieldtype": "Link",
"in_list_view": 1,
"label": "Supplier Type",
"oldfieldname": "supplier_type",
"oldfieldtype": "Link",
"options": "Supplier Type",
"permlevel": 0,
"reqd": 1
},
},
{
"depends_on": "eval:!doc.__islocal",
"fieldname": "address_contacts",
"fieldtype": "Section Break",
"label": "Address & Contacts",
"oldfieldtype": "Column Break",
"options": "icon-map-marker",
"fieldname": "is_frozen",
"fieldtype": "Check",
"label": "Is Frozen",
"permlevel": 0,
"precision": ""
},
{
"depends_on": "eval:!doc.__islocal",
"fieldname": "address_contacts",
"fieldtype": "Section Break",
"label": "Address & Contacts",
"oldfieldtype": "Column Break",
"options": "icon-map-marker",
"permlevel": 0
},
},
{
"fieldname": "address_html",
"fieldtype": "HTML",
"label": "Address HTML",
"permlevel": 0,
"fieldname": "address_html",
"fieldtype": "HTML",
"label": "Address HTML",
"permlevel": 0,
"read_only": 1
},
},
{
"fieldname": "column_break1",
"fieldtype": "Column Break",
"permlevel": 0,
"fieldname": "column_break1",
"fieldtype": "Column Break",
"permlevel": 0,
"width": "50%"
},
},
{
"fieldname": "contact_html",
"fieldtype": "HTML",
"label": "Contact HTML",
"permlevel": 0,
"fieldname": "contact_html",
"fieldtype": "HTML",
"label": "Contact HTML",
"permlevel": 0,
"read_only": 1
},
},
{
"fieldname": "default_payable_accounts",
"fieldtype": "Section Break",
"label": "Default Payable Accounts",
"fieldname": "default_payable_accounts",
"fieldtype": "Section Break",
"label": "Default Payable Accounts",
"permlevel": 0
},
},
{
"depends_on": "eval:!doc.__islocal",
"description": "Mention if non-standard receivable account applicable",
"fieldname": "accounts",
"fieldtype": "Table",
"label": "Accounts",
"options": "Party Account",
"depends_on": "eval:!doc.__islocal",
"description": "Mention if non-standard receivable account applicable",
"fieldname": "accounts",
"fieldtype": "Table",
"label": "Accounts",
"options": "Party Account",
"permlevel": 0
},
},
{
"fieldname": "more_info",
"fieldtype": "Section Break",
"label": "More Info",
"oldfieldtype": "Section Break",
"options": "icon-file-text",
"fieldname": "more_info",
"fieldtype": "Section Break",
"label": "More Info",
"oldfieldtype": "Section Break",
"options": "icon-file-text",
"permlevel": 0
},
},
{
"fieldname": "default_currency",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"label": "Default Currency",
"no_copy": 1,
"options": "Currency",
"fieldname": "default_currency",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"label": "Default Currency",
"no_copy": 1,
"options": "Currency",
"permlevel": 0
},
},
{
"fieldname": "default_price_list",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"label": "Price List",
"options": "Price List",
"fieldname": "default_price_list",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"label": "Price List",
"options": "Price List",
"permlevel": 0
},
},
{
"fieldname": "default_taxes_and_charges",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"label": "Taxes and Charges",
"options": "Purchase Taxes and Charges Template",
"fieldname": "default_taxes_and_charges",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"label": "Taxes and Charges",
"options": "Purchase Taxes and Charges Template",
"permlevel": 0
},
},
{
"fieldname": "credit_days",
"fieldtype": "Int",
"label": "Credit Days",
"fieldname": "credit_days",
"fieldtype": "Int",
"label": "Credit Days",
"permlevel": 0
},
},
{
"fieldname": "column_break2",
"fieldtype": "Column Break",
"permlevel": 0,
"fieldname": "column_break2",
"fieldtype": "Column Break",
"permlevel": 0,
"width": "50%"
},
},
{
"fieldname": "website",
"fieldtype": "Data",
"label": "Website",
"oldfieldname": "website",
"oldfieldtype": "Data",
"fieldname": "website",
"fieldtype": "Data",
"label": "Website",
"oldfieldname": "website",
"oldfieldtype": "Data",
"permlevel": 0
},
},
{
"description": "Statutory info and other general information about your Supplier",
"fieldname": "supplier_details",
"fieldtype": "Text",
"label": "Supplier Details",
"oldfieldname": "supplier_details",
"oldfieldtype": "Code",
"description": "Statutory info and other general information about your Supplier",
"fieldname": "supplier_details",
"fieldtype": "Text",
"label": "Supplier Details",
"oldfieldname": "supplier_details",
"oldfieldtype": "Code",
"permlevel": 0
},
},
{
"fieldname": "communications",
"fieldtype": "Table",
"hidden": 1,
"label": "Communications",
"options": "Communication",
"permlevel": 0,
"fieldname": "communications",
"fieldtype": "Table",
"hidden": 1,
"label": "Communications",
"options": "Communication",
"permlevel": 0,
"print_hide": 1
}
],
"icon": "icon-user",
"idx": 1,
"modified": "2015-02-24 17:35:03.821319",
"modified_by": "Administrator",
"module": "Buying",
"name": "Supplier",
"owner": "Administrator",
],
"icon": "icon-user",
"idx": 1,
"modified": "2015-07-17 09:39:05.318826",
"modified_by": "Administrator",
"module": "Buying",
"name": "Supplier",
"owner": "Administrator",
"permissions": [
{
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Purchase User"
},
},
{
"amend": 0,
"create": 0,
"delete": 0,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Purchase Manager",
"submit": 0,
"amend": 0,
"create": 0,
"delete": 0,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Purchase Manager",
"submit": 0,
"write": 0
},
},
{
"amend": 0,
"create": 1,
"delete": 1,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Purchase Master Manager",
"share": 1,
"submit": 0,
"amend": 0,
"create": 1,
"delete": 1,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Purchase Master Manager",
"share": 1,
"submit": 0,
"write": 1
},
},
{
"apply_user_permissions": 1,
"permlevel": 0,
"read": 1,
"role": "Material User"
},
"apply_user_permissions": 1,
"permlevel": 0,
"read": 1,
"role": "Stock User"
},
{
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Material Manager"
},
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Stock Manager"
},
{
"apply_user_permissions": 1,
"permlevel": 0,
"read": 1,
"apply_user_permissions": 1,
"permlevel": 0,
"read": 1,
"role": "Accounts User"
},
},
{
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"email": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts Manager"
}
],
"search_fields": "supplier_name, supplier_type",
],
"search_fields": "supplier_name, supplier_type",
"title_field": "supplier_name"
}
}

View File

@@ -660,7 +660,7 @@
"icon": "icon-shopping-cart",
"idx": 1,
"is_submittable": 1,
"modified": "2015-06-15 15:39:08.954248",
"modified": "2015-07-13 05:28:30.252636",
"modified_by": "Administrator",
"module": "Buying",
"name": "Supplier Quotation",
@@ -723,7 +723,7 @@
"print": 1,
"read": 1,
"report": 1,
"role": "Material User",
"role": "Stock User",
"submit": 0,
"write": 0
},

View File

@@ -0,0 +1,3 @@
Leave change log files in this folder for user release notes.
(this file is just a place holder, don't delete it)

View File

@@ -0,0 +1,9 @@
- New help videos for Selling, Buying, Human Resource, Manufacturing and Buying
- Role rename: **Material User** is now **Stock User**
- Role rename: **Material Manager** is now **Stock Manager**
- Role rename: **Material Master Manager** is now **Item Manager**
- Fixed inconsistent visibility of 'Add to Cart' button
- Use Customer's Price List in Shopping Cart
- Fixed Address creation from Shopping Cart
- Display images in website's Item and Item List pages when the filename has paranthesis in its name

View File

@@ -0,0 +1,11 @@
- **Sales Return**: Create Delivery Note or Sales Invoice ('Update Stock' option checked) with negative quantity.
- **Purchase Return**: Create Purchase Receipt with negative quantity
- **Credit / Debit Note**: Create Sales / Purchase Invoice with negative qtuantity against original invoice.
- Outgoing rate in Purchase Return based on reference / original Purchase Receipt rate
- Global switch added to disable capacity planning in manufacturing settings
- Opening Balance row added to Stock Ledger Report
- SMS delivery message and log
- Added users, employees, sample data via Setup Wizard
- Letter Head option in General Ledger report
- Fetch Template Bom if no BOM is set against Item Variant in Production Order
- Fetch items from Packing List while raising Material Request against SO

View File

@@ -0,0 +1,9 @@
- Sales / Purchase Return Enahancement - **Sponsored by [Strella Consulting Sdn Bhd](http://www.strellagroup.com)**
* Now you can make Return entry by creating Delivery Note, Purchase Receipt or Sales / Purchase Invoice with negative quantity
- **Item** form cleanups: "Yes" / "No" type fields changed to checkboxes.<br>
**Warning**: This could break your 3rd party integrations with Item, if any
- Leave Application: Consideration of **holidays** in calculation of 'Number of Days' is now optional. You can set it in Leave Type record.
- Customer / Supplier can be **freezed** now
- Fix: Reserved qty calculation while delivering Product Bundle via Sales Invoice
- Fix: Deleted stock ledger entries on cancellation of Sales Invoice while Product Bundle delivered
- Fix: Fetch default expense account and cost center from item based on selected company

View File

@@ -42,6 +42,11 @@ def get_data():
"name": "SMS Center",
"description":_("Send mass SMS to your contacts"),
},
{
"type": "doctype",
"name": "SMS Log",
"description":_("Logs for maintaining sms delivery status"),
}
]
},
{

View File

@@ -64,7 +64,7 @@ def get_data():
"type": "module"
},
"Learn": {
"color": "#7272FF",
"color": "#FCB868",
"force_show": True,
"icon": "icon-facetime-video",
"type": "module",

View File

@@ -15,8 +15,12 @@ def get_data():
"type": "help",
"label": _("Setup Wizard"),
"youtube_id": "oIOf_zCFWKQ"
}
},
{
"type": "help",
"label": _("Customizing Forms"),
"youtube_id": "pJhL9mmxV_U"
},
]
},
@@ -93,6 +97,16 @@ def get_data():
"label": _("Customer and Supplier"),
"youtube_id": "anoGi_RpQ20"
},
{
"type": "help",
"label": _("Sales Order to Payment"),
"youtube_id": "7AMq4lqkN4A"
},
{
"type": "help",
"label": _("Point-of-Sale"),
"youtube_id": "4WkelWkbP_c"
}
]
},
{
@@ -108,11 +122,6 @@ def get_data():
"label": _("Opening Stock Balance"),
"youtube_id": "0yPgrtfeCTs"
},
{
"type": "help",
"label": _("Item Variants"),
"youtube_id": "OGBETlCzU5o"
},
]
},
{
@@ -123,6 +132,12 @@ def get_data():
"label": _("Customer and Supplier"),
"youtube_id": "anoGi_RpQ20"
},
{
"type": "help",
"label": _("Material Request to Purchase Order"),
"youtube_id": "4TN9kPyfIqM"
},
]
},
{
@@ -133,6 +148,47 @@ def get_data():
"label": _("Bill of Materials"),
"youtube_id": "hDV0c1OeWLo"
},
{
"type": "help",
"label": _("Production Planning Tool"),
"youtube_id": "CzatSl4zJ2Y"
},
{
"type": "help",
"label": _("Production Order"),
"youtube_id": "ZotgLyp2YFY"
},
]
}
},
{
"label": _("Human Resource"),
"items": [
{
"type": "help",
"label": _("Setting up Employees"),
"youtube_id": "USfIUdZlUhw"
},
{
"type": "help",
"label": _("Leave Management"),
"youtube_id": "fc0p_AXebc8"
},
{
"type": "help",
"label": _("Expense Claims"),
"youtube_id": "5SZHJF--ZFY"
},
]
},
{
"label": _("Projects"),
"items": [
{
"type": "help",
"label": _("Managing Projects"),
"youtube_id": "egxIGwtoKI4"
},
]
},
]

View File

@@ -48,6 +48,11 @@ def get_data():
"name": "SMS Center",
"description":_("Send mass SMS to your contacts"),
},
{
"type": "doctype",
"name": "SMS Log",
"description":_("Logs for maintaining sms delivery status"),
},
{
"type": "doctype",
"name": "Newsletter",

View File

@@ -1,92 +0,0 @@
{
"allow_rename": 1,
"autoname": "field:party_type_name",
"creation": "2014-04-07 12:32:18.010384",
"docstatus": 0,
"doctype": "DocType",
"document_type": "Master",
"fields": [
{
"fieldname": "party_type_name",
"fieldtype": "Data",
"in_list_view": 1,
"label": "Party Type Name",
"permlevel": 0,
"reqd": 1
},
{
"fieldname": "parent_party_type",
"fieldtype": "Link",
"label": "Parent Party Type",
"options": "Party Type",
"permlevel": 0
},
{
"default": "Yes",
"fieldname": "allow_children",
"fieldtype": "Select",
"label": "Allow Children",
"options": "Yes\nNo",
"permlevel": 0
},
{
"fieldname": "default_price_list",
"fieldtype": "Link",
"ignore_user_permissions": 1,
"label": "Default Price List",
"options": "Price List",
"permlevel": 0
},
{
"fieldname": "lft",
"fieldtype": "Int",
"hidden": 1,
"label": "LFT",
"permlevel": 0,
"read_only": 1,
"search_index": 1
},
{
"fieldname": "rgt",
"fieldtype": "Int",
"hidden": 1,
"label": "RGT",
"permlevel": 0,
"read_only": 1,
"search_index": 1
},
{
"fieldname": "old_parent",
"fieldtype": "Data",
"hidden": 1,
"label": "Old Parent",
"permlevel": 0,
"read_only": 1
}
],
"modified": "2015-02-05 05:11:42.046004",
"modified_by": "Administrator",
"module": "Contacts",
"name": "Party Type",
"owner": "Administrator",
"permissions": [
{
"apply_user_permissions": 1,
"create": 1,
"permlevel": 0,
"read": 1,
"role": "Sales User",
"share": 1,
"write": 1
},
{
"apply_user_permissions": 1,
"create": 1,
"permlevel": 0,
"read": 1,
"role": "Purchase User",
"share": 1,
"write": 1
}
]
}

View File

@@ -1,9 +0,0 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe.utils.nestedset import NestedSet
class PartyType(NestedSet):
nsm_parent_field = 'parent_party_type';

View File

@@ -9,24 +9,34 @@ from erpnext.setup.utils import get_company_currency, get_exchange_rate
from erpnext.accounts.utils import get_fiscal_year, validate_fiscal_year
from erpnext.utilities.transaction_base import TransactionBase
from erpnext.controllers.recurring_document import convert_to_recurring, validate_recurring_document
from erpnext.controllers.sales_and_purchase_return import validate_return
class CustomerFrozen(frappe.ValidationError): pass
class AccountsController(TransactionBase):
def validate(self):
if self.get("_action") and self._action != "update_after_submit":
self.set_missing_values(for_validate=True)
self.validate_date_with_fiscal_year()
if self.meta.get_field("currency"):
self.calculate_taxes_and_totals()
self.validate_value("base_grand_total", ">=", 0)
if not self.meta.get_field("is_return") or not self.is_return:
self.validate_value("base_grand_total", ">=", 0)
validate_return(self)
self.set_total_in_words()
self.validate_due_date()
if self.doctype in ("Sales Invoice", "Purchase Invoice") and not self.is_return:
self.validate_due_date()
if self.meta.get_field("is_recurring"):
validate_recurring_document(self)
if self.meta.get_field("taxes_and_charges"):
self.validate_enabled_taxes_and_charges()
self.validate_party()
def on_submit(self):
if self.meta.get_field("is_recurring"):
@@ -74,6 +84,9 @@ class AccountsController(TransactionBase):
def validate_due_date(self):
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)
elif self.doctype == "Purchase Invoice":
validate_due_date(self.posting_date, self.due_date, "Supplier", self.supplier, self.company)
@@ -294,7 +307,7 @@ class AccountsController(TransactionBase):
item_codes = list(set(item.item_code for item in self.get("items")))
if item_codes:
stock_items = [r[0] for r in frappe.db.sql("""select name
from `tabItem` where name in (%s) and is_stock_item='Yes'""" % \
from `tabItem` where name in (%s) and is_stock_item=1""" % \
(", ".join((["%s"]*len(item_codes))),), item_codes)]
return stock_items
@@ -332,6 +345,23 @@ class AccountsController(TransactionBase):
return self._abbr
def validate_party(self):
frozen_accounts_modifier = frappe.db.get_value( 'Accounts Settings', None,'frozen_accounts_modifier')
if frozen_accounts_modifier in frappe.get_roles():
return
party_type = None
if self.meta.get_field("customer"):
party_type = 'Customer'
elif self.meta.get_field("supplier"):
party_type = 'Supplier'
if party_type:
party = self.get(party_type.lower())
if frappe.db.get_value(party_type, party, "is_frozen"):
frappe.throw("{0} {1} is frozen".format(party_type, party), CustomerFrozen)
@frappe.whitelist()
def get_tax_rate(account_head):
return frappe.db.get_value("Account", account_head, "tax_rate")

View File

@@ -26,8 +26,7 @@ class BuyingController(StockController):
def validate(self):
super(BuyingController, self).validate()
if getattr(self, "supplier", None) and not self.supplier_name:
self.supplier_name = frappe.db.get_value("Supplier",
self.supplier, "supplier_name")
self.supplier_name = frappe.db.get_value("Supplier", self.supplier, "supplier_name")
self.is_item_table_empty()
self.set_qty_as_per_stock_uom()
self.validate_stock_or_nonstock_items()
@@ -239,8 +238,8 @@ class BuyingController(StockController):
t2.rate, t2.stock_uom, t2.name, t2.description
from `tabBOM` t1, `tabBOM Item` t2, tabItem t3
where t2.parent = t1.name and t1.item = %s
and t1.docstatus = 1 and t1.is_active = 1 and t1.name = %s
and t2.item_code = t3.name and ifnull(t3.is_stock_item, 'No') = 'Yes'""", (item_code, bom), as_dict=1)
and t1.docstatus = 1 and t1.is_active = 1 and t1.name = %s
and t2.item_code = t3.name and t3.is_stock_item = 1""", (item_code, bom), as_dict=1)
if not bom_items:
msgprint(_("Specified BOM {0} does not exist for Item {1}").format(bom, item_code), raise_exception=1)
@@ -255,7 +254,7 @@ class BuyingController(StockController):
self.get("items")))
if item_codes:
self._sub_contracted_items = [r[0] for r in frappe.db.sql("""select name
from `tabItem` where name in (%s) and is_sub_contracted_item='Yes'""" % \
from `tabItem` where name in (%s) and is_sub_contracted_item=1""" % \
(", ".join((["%s"]*len(item_codes))),), item_codes)]
return self._sub_contracted_items

View File

@@ -0,0 +1,145 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.utils import flt, get_datetime, format_datetime
class StockOverReturnError(frappe.ValidationError): pass
def validate_return(doc):
if not doc.meta.get_field("is_return") or not doc.is_return:
return
validate_return_against(doc)
validate_returned_items(doc)
def validate_return_against(doc):
if not doc.return_against:
frappe.throw(_("{0} is mandatory for Return").format(doc.meta.get_label("return_against")))
else:
filters = {"doctype": doc.doctype, "docstatus": 1, "company": doc.company}
if doc.meta.get_field("customer"):
filters["customer"] = doc.customer
elif doc.meta.get_field("supplier"):
filters["supplier"] = doc.supplier
if not frappe.db.exists(filters):
frappe.throw(_("Invalid {0}: {1}")
.format(doc.meta.get_label("return_against"), doc.return_against))
else:
ref_doc = frappe.get_doc(doc.doctype, doc.return_against)
# validate posting date time
return_posting_datetime = "%s %s" % (doc.posting_date, doc.get("posting_time") or "00:00:00")
ref_posting_datetime = "%s %s" % (ref_doc.posting_date, ref_doc.get("posting_time") or "00:00:00")
if get_datetime(return_posting_datetime) < get_datetime(ref_posting_datetime):
frappe.throw(_("Posting timestamp must be after {0}").format(format_datetime(ref_posting_datetime)))
# validate same exchange rate
if doc.conversion_rate != ref_doc.conversion_rate:
frappe.throw(_("Exchange Rate must be same as {0} {1} ({2})")
.format(doc.doctype, doc.return_against, ref_doc.conversion_rate))
# validate update stock
if doc.doctype == "Sales Invoice" and doc.update_stock and not ref_doc.update_stock:
frappe.throw(_("'Update Stock' can not be checked because items are not delivered via {0}")
.format(doc.return_against))
def validate_returned_items(doc):
valid_items = frappe._dict()
for d in frappe.db.sql("""select item_code, sum(qty) as qty, rate from `tab{0} Item`
where parent = %s group by item_code""".format(doc.doctype), doc.return_against, as_dict=1):
valid_items.setdefault(d.item_code, d)
if doc.doctype in ("Delivery Note", "Sales Invoice"):
for d in frappe.db.sql("""select item_code, sum(qty) as qty from `tabPacked Item`
where parent = %s group by item_code""".format(doc.doctype), doc.return_against, as_dict=1):
valid_items.setdefault(d.item_code, d)
already_returned_items = get_already_returned_items(doc)
items_returned = False
for d in doc.get("items"):
if flt(d.qty) < 0:
if d.item_code not in valid_items:
frappe.throw(_("Row # {0}: Returned Item {1} does not exists in {2} {3}")
.format(d.idx, d.item_code, doc.doctype, doc.return_against))
else:
ref = valid_items.get(d.item_code, frappe._dict())
already_returned_qty = flt(already_returned_items.get(d.item_code))
max_return_qty = flt(ref.qty) - already_returned_qty
if already_returned_qty >= ref.qty:
frappe.throw(_("Item {0} has already been returned").format(d.item_code), StockOverReturnError)
elif abs(d.qty) > max_return_qty:
frappe.throw(_("Row # {0}: Cannot return more than {1} for Item {2}")
.format(d.idx, ref.qty, d.item_code), StockOverReturnError)
elif ref.rate and flt(d.rate) != ref.rate:
frappe.throw(_("Row # {0}: Rate must be same as {1} {2}")
.format(d.idx, doc.doctype, doc.return_against))
items_returned = True
if not items_returned:
frappe.throw(_("Atleast one item should be entered with negative quantity in return document"))
def get_already_returned_items(doc):
return frappe._dict(frappe.db.sql("""
select
child.item_code, sum(abs(child.qty)) as qty
from
`tab{0} Item` child, `tab{1}` par
where
child.parent = par.name and par.docstatus = 1
and ifnull(par.is_return, 0) = 1 and par.return_against = %s and child.qty < 0
group by item_code
""".format(doc.doctype, doc.doctype), doc.return_against))
def make_return_doc(doctype, source_name, target_doc=None):
from frappe.model.mapper import get_mapped_doc
def set_missing_values(source, target):
doc = frappe.get_doc(target)
doc.is_return = 1
doc.return_against = source.name
doc.ignore_pricing_rule = 1
if doctype == "Sales Invoice":
doc.is_pos = 0
for tax in doc.get("taxes"):
if tax.charge_type == "Actual":
tax.tax_amount = -1 * tax.tax_amount
doc.run_method("calculate_taxes_and_totals")
def update_item(source_doc, target_doc, source_parent):
target_doc.qty = -1* source_doc.qty
if doctype == "Purchase Receipt":
target_doc.received_qty = -1* source_doc.qty
elif doctype == "Purchase Invoice":
target_doc.purchase_receipt = source_doc.purchase_receipt
target_doc.pr_detail = source_doc.pr_detail
doclist = get_mapped_doc(doctype, source_name, {
doctype: {
"doctype": doctype,
"validation": {
"docstatus": ["=", 1],
}
},
doctype +" Item": {
"doctype": doctype + " Item",
"fields": {
"purchase_order": "purchase_order",
"purchase_receipt": "purchase_receipt"
},
"postprocess": update_item
},
}, target_doc, set_missing_values)
return doclist

View File

@@ -110,15 +110,14 @@ class SellingController(StockController):
from frappe.utils import money_in_words
company_currency = get_company_currency(self.company)
disable_rounded_total = cint(frappe.db.get_value("Global Defaults", None,
"disable_rounded_total"))
disable_rounded_total = cint(frappe.db.get_value("Global Defaults", None, "disable_rounded_total"))
if self.meta.get_field("base_in_words"):
self.base_in_words = money_in_words(disable_rounded_total and
self.base_grand_total or self.base_rounded_total, company_currency)
abs(self.base_grand_total) or abs(self.base_rounded_total), company_currency)
if self.meta.get_field("in_words"):
self.in_words = money_in_words(disable_rounded_total and
self.grand_total or self.rounded_total, self.currency)
abs(self.grand_total) or abs(self.rounded_total), self.currency)
def calculate_commission(self):
if self.meta.get_field("commission_rate"):
@@ -171,19 +170,18 @@ class SellingController(StockController):
frappe.throw(_("Row {0}: Qty is mandatory").format(d.idx))
if self.doctype == "Sales Order":
if (frappe.db.get_value("Item", d.item_code, "is_stock_item") == 'Yes' or
self.has_product_bundle(d.item_code)) and not d.warehouse:
frappe.throw(_("Reserved Warehouse required for stock Item {0} in row {1}").format(d.item_code, d.idx))
reserved_warehouse = d.warehouse
if flt(d.qty) > flt(d.delivered_qty):
reserved_qty_for_main_item = flt(d.qty) - flt(d.delivered_qty)
elif self.doctype == "Delivery Note" and d.against_sales_order:
elif (((self.doctype == "Delivery Note" and d.against_sales_order)
or (self.doctype == "Sales Invoice" and d.sales_order and self.update_stock))
and not self.is_return):
# if SO qty is 10 and there is tolerance of 20%, then it will allow DN of 12.
# But in this case reserved qty should only be reduced by 10 and not 12
already_delivered_qty = self.get_already_delivered_qty(self.name,
d.against_sales_order, d.so_detail)
d.against_sales_order if self.doctype=="Delivery Note" else d.sales_order, d.so_detail)
so_qty, reserved_warehouse = self.get_so_qty_and_warehouse(d.so_detail)
if already_delivered_qty + d.qty > so_qty:
@@ -214,7 +212,7 @@ class SellingController(StockController):
'qty': d.qty,
'reserved_qty': reserved_qty_for_main_item,
'uom': d.stock_uom,
'stock_uom': d.stock_uom,
'stock_uom': d.stock_uom,
'batch_no': cstr(d.get("batch_no")).strip(),
'serial_no': cstr(d.get("serial_no")).strip(),
'name': d.name
@@ -225,12 +223,21 @@ class SellingController(StockController):
return frappe.db.sql("""select name from `tabProduct Bundle`
where new_item_code=%s and docstatus != 2""", item_code)
def get_already_delivered_qty(self, dn, so, so_detail):
qty = frappe.db.sql("""select sum(qty) from `tabDelivery Note Item`
def get_already_delivered_qty(self, current_docname, so, so_detail):
delivered_via_dn = frappe.db.sql("""select sum(qty) from `tabDelivery Note Item`
where so_detail = %s and docstatus = 1
and against_sales_order = %s
and parent != %s""", (so_detail, so, dn))
return qty and flt(qty[0][0]) or 0.0
and parent != %s""", (so_detail, so, current_docname))
delivered_via_si = frappe.db.sql("""select sum(qty) from `tabSales Invoice Item`
where so_detail = %s and docstatus = 1
and sales_order = %s
and parent != %s""", (so_detail, so, current_docname))
total_delivered_qty = (flt(delivered_via_dn[0][0]) if delivered_via_dn else 0) \
+ (flt(delivered_via_si[0][0]) if delivered_via_si else 0)
return total_delivered_qty
def get_so_qty_and_warehouse(self, so_detail):
so_item = frappe.db.sql("""select qty, warehouse from `tabSales Order Item`
@@ -252,7 +259,7 @@ def check_active_sales_items(obj):
item = frappe.db.sql("""select docstatus, is_sales_item,
is_service_item, income_account from tabItem where name = %s""",
d.item_code, as_dict=True)[0]
if item.is_sales_item == 'No' and item.is_service_item == 'No':
if item.is_sales_item == 0 and item.is_service_item == 0:
frappe.throw(_("Item {0} must be Sales or Service Item in {1}").format(d.item_code, d.idx))
if getattr(d, "income_account", None) and not item.income_account:
frappe.db.set_value("Item", d.item_code, "income_account",

View File

@@ -9,6 +9,8 @@ import frappe.defaults
from erpnext.controllers.accounts_controller import AccountsController
from erpnext.accounts.general_ledger import make_gl_entries, delete_gl_entries, process_gl_map
from erpnext.stock.utils import update_bin
class StockController(AccountsController):
def make_gl_entries(self, repost_future_gle=True):
@@ -212,10 +214,38 @@ class StockController(AccountsController):
item_codes = list(set([d.item_code for d in self.get("items")]))
if item_codes:
serialized_items = frappe.db.sql_list("""select name from `tabItem`
where has_serial_no='Yes' and name in ({})""".format(", ".join(["%s"]*len(item_codes))),
where has_serial_no=1 and name in ({})""".format(", ".join(["%s"]*len(item_codes))),
tuple(item_codes))
return serialized_items
def get_incoming_rate_for_sales_return(self, item_code, against_document):
incoming_rate = 0.0
if against_document and item_code:
incoming_rate = frappe.db.sql("""select abs(ifnull(stock_value_difference, 0) / actual_qty)
from `tabStock Ledger Entry`
where voucher_type = %s and voucher_no = %s and item_code = %s limit 1""",
(self.doctype, against_document, item_code))
incoming_rate = incoming_rate[0][0] if incoming_rate else 0.0
return incoming_rate
def update_reserved_qty(self, d):
if d['reserved_qty'] < 0 :
# Reduce reserved qty from reserved warehouse mentioned in so
if not d["reserved_warehouse"]:
frappe.throw(_("Reserved Warehouse is missing in Sales Order"))
args = {
"item_code": d['item_code'],
"warehouse": d["reserved_warehouse"],
"voucher_type": self.doctype,
"voucher_no": self.name,
"reserved_qty": (self.docstatus==1 and 1 or -1)*flt(d['reserved_qty']),
"posting_date": self.posting_date,
"is_amended": self.amended_from and 'Yes' or 'No'
}
update_bin(args)
def update_gl_entries_after(posting_date, posting_time, for_warehouses=None, for_items=None,
warehouse_account=None):

View File

@@ -396,13 +396,15 @@ class calculate_taxes_and_totals(object):
# total_advance is only for non POS Invoice
if self.doc.doctype == "Sales Invoice":
self.doc.round_floats_in(self.doc, ["base_grand_total", "total_advance", "write_off_amount", "paid_amount"])
total_amount_to_pay = self.doc.base_grand_total - self.doc.write_off_amount
self.doc.outstanding_amount = flt(total_amount_to_pay - self.doc.total_advance - self.doc.paid_amount,
self.doc.precision("outstanding_amount"))
if not self.doc.is_return:
self.doc.round_floats_in(self.doc, ["base_grand_total", "total_advance", "write_off_amount", "paid_amount"])
total_amount_to_pay = self.doc.base_grand_total - self.doc.write_off_amount
self.doc.outstanding_amount = flt(total_amount_to_pay - self.doc.total_advance - self.doc.paid_amount,
self.doc.precision("outstanding_amount"))
else:
self.doc.round_floats_in(self.doc, ["total_advance", "write_off_amount"])
self.doc.total_amount_to_pay = flt(self.doc.base_grand_total - self.doc.write_off_amount,
self.doc.precision("total_amount_to_pay"))
self.doc.outstanding_amount = flt(self.doc.total_amount_to_pay - self.doc.total_advance,
self.doc.precision("outstanding_amount"))
if not self.doc.is_return:
self.doc.outstanding_amount = flt(self.doc.total_amount_to_pay - self.doc.total_advance,
self.doc.precision("outstanding_amount"))

View File

@@ -52,7 +52,7 @@
"fieldtype": "Text Editor",
"label": "Message",
"permlevel": 0,
"reqd": 0
"reqd": 1
},
{
"description": "",
@@ -78,7 +78,7 @@
],
"icon": "icon-envelope",
"idx": 1,
"modified": "2015-03-20 05:27:31.613881",
"modified": "2015-07-20 05:43:33.818567",
"modified_by": "Administrator",
"module": "CRM",
"name": "Newsletter",

View File

@@ -32,6 +32,7 @@
"default": "0",
"fieldname": "total_subscribers",
"fieldtype": "Int",
"in_list_view": 1,
"label": "Total Subscribers",
"permlevel": 0,
"precision": "",
@@ -45,7 +46,7 @@
"is_submittable": 0,
"issingle": 0,
"istable": 0,
"modified": "2015-03-18 08:08:37.692367",
"modified": "2015-07-15 07:18:30.094155",
"modified_by": "Administrator",
"module": "CRM",
"name": "Newsletter List",

View File

@@ -48,7 +48,7 @@ erpnext.crm.Opportunity = frappe.ui.form.Controller.extend({
return {
query: "erpnext.controllers.queries.item_query",
filters: me.frm.doc.enquiry_type === "Maintenance" ?
{"is_service_item": "Yes"} : {"is_sales_item": "Yes"}
{"is_service_item": 1} : {"is_sales_item":1}
};
});

View File

@@ -1,11 +1,34 @@
from __future__ import unicode_literals
app_name = "erpnext"
app_title = "ERPNext"
app_publisher = "Frappe Technologies Pvt. Ltd. and Contributors"
app_description = "Open Source Enterprise Resource Planning for Small and Midsized Organizations"
app_publisher = "Frappe Technologies Pvt. Ltd."
app_description = """## ERPNext
ERPNext is a fully featured ERP system designed for Small and Medium Sized
business. ERPNext covers a wide range of features including Accounting, CRM,
Inventory management, Selling, Purchasing, Manufacturing, Projects, HR &
Payroll, Website, E-Commerce and much more.
ERPNext is based on the Frappe Framework is highly customizable and extendable.
You can create Custom Form, Fields, Scripts and can also create your own Apps
to extend ERPNext functionality.
ERPNext is Open Source under the GNU General Public Licence v3 and has been
listed as one of the Best Open Source Softwares in the world by my online
blogs.
### Links
- Website: [https://erpnext.com](https://erpnext.com)
- GitHub: [https://github.com/frappe/erpnext](https://github.com/frappe/erpnext)
- Forum: [https://discuss.erpnext.com](https://discuss.erpnext.com)
- Frappe Framework: [https://frappe.io](https://frappe.io)
"""
app_icon = "icon-th"
app_color = "#e74c3c"
app_version = "5.1.6"
app_version = "5.4.1"
github_link = "https://github.com/frappe/erpnext"
error_report_email = "support@erpnext.com"

View File

@@ -19,10 +19,13 @@ erpnext.hr.ExpenseClaimController = frappe.ui.form.Controller.extend({
jv.company = cur_frm.doc.company;
jv.remark = 'Payment against Expense Claim: ' + cur_frm.doc.name;
jv.fiscal_year = cur_frm.doc.fiscal_year;
var d1 = frappe.model.add_child(jv, 'Journal Entry Account', 'accounts');
d1.debit = cur_frm.doc.total_sanctioned_amount;
d1.against_expense_claim = cur_frm.doc.name;
var expense = cur_frm.doc.expenses || [];
for(var i = 0; i < expense.length; i++){
var d1 = frappe.model.add_child(jv, 'Journal Entry Account', 'accounts');
d1.debit = expense[i].sanctioned_amount;
d1.account = expense[i].default_account;
d1.against_expense_claim = cur_frm.doc.name;
}
// credit to bank
var d1 = frappe.model.add_child(jv, 'Journal Entry Account', 'accounts');
@@ -43,6 +46,7 @@ $.extend(cur_frm.cscript, new erpnext.hr.ExpenseClaimController({frm: cur_frm}))
cur_frm.add_fetch('employee', 'company', 'company');
cur_frm.add_fetch('employee','employee_name','employee_name');
cur_frm.add_fetch('expense_type', 'default_account', 'default_account');
cur_frm.cscript.onload = function(doc,cdt,cdn) {
if(!doc.approval_status)

View File

@@ -34,6 +34,18 @@
"reqd": 1,
"width": "150px"
},
{
"depends_on": "expense_type",
"fieldname": "default_account",
"fieldtype": "Link",
"hidden": 1,
"label": "Default Account",
"options": "Account",
"permlevel": 0,
"precision": "",
"read_only": 1,
"unique": 0
},
{
"fieldname": "section_break_4",
"fieldtype": "Section Break",
@@ -93,7 +105,7 @@
],
"idx": 1,
"istable": 1,
"modified": "2015-04-08 06:18:47.539134",
"modified": "2015-07-16 06:13:32.090048",
"modified_by": "Administrator",
"module": "HR",
"name": "Expense Claim Detail",

View File

@@ -17,6 +17,14 @@
"reqd": 1,
"search_index": 0
},
{
"fieldname": "default_account",
"fieldtype": "Link",
"label": "Default Account",
"options": "Account",
"permlevel": 0,
"precision": ""
},
{
"fieldname": "description",
"fieldtype": "Small Text",
@@ -29,7 +37,7 @@
],
"icon": "icon-flag",
"idx": 1,
"modified": "2015-04-19 06:47:51.860833",
"modified": "2015-07-15 08:57:23.069980",
"modified_by": "Administrator",
"module": "HR",
"name": "Expense Claim Type",

View File

@@ -238,10 +238,16 @@ def get_total_leave_days(leave_app):
ret = {'total_leave_days' : 0.5}
if not leave_app.half_day:
tot_days = date_diff(leave_app.to_date, leave_app.from_date) + 1
holidays = leave_app.get_holidays()
ret = {
'total_leave_days' : flt(tot_days)-flt(holidays)
}
if frappe.db.get_value("Leave Type", leave_app.leave_type, "include_holiday"):
ret = {
'total_leave_days' : flt(tot_days)
}
else:
holidays = leave_app.get_holidays()
ret = {
'total_leave_days' : flt(tot_days)-flt(holidays)
}
return ret
@frappe.whitelist()

View File

@@ -246,4 +246,4 @@ class TestLeaveApplication(unittest.TestCase):
frappe.db.sql("""delete from `tabEmployee Leave Approver` where parent=%s""",
"_T-Employee-0001")
frappe.db.set_value("Employee", "_T-Employee-0001", "department", original_department)
frappe.db.set_value("Employee", "_T-Employee-0001", "department", original_department)

View File

@@ -59,11 +59,17 @@
"fieldtype": "Check",
"label": "Allow Negative Balance",
"permlevel": 0
},
{
"fieldname": "include_holiday",
"fieldtype": "Check",
"label": "Include holidays within leaves as leaves",
"permlevel": 0
}
],
"icon": "icon-flag",
"idx": 1,
"modified": "2015-05-08 05:15:24.194053",
"modified": "2015-07-29 05:15:24",
"modified_by": "Administrator",
"module": "HR",
"name": "Leave Type",

View File

@@ -1,11 +1,13 @@
[
{
"doctype": "Leave Type",
"leave_type_name": "_Test Leave Type"
"leave_type_name": "_Test Leave Type",
"include_holiday": 1
},
{
"doctype": "Leave Type",
"is_lwp": 1,
"leave_type_name": "_Test Leave Type LWP"
"leave_type_name": "_Test Leave Type LWP",
"include_holiday": 1
}
]

View File

@@ -1,193 +1,193 @@
{
"allow_copy": 0,
"allow_import": 1,
"allow_rename": 0,
"autoname": "Offer-.#####",
"creation": "2015-03-04 14:20:17.662207",
"custom": 0,
"default_print_format": "Offer Letter",
"docstatus": 0,
"doctype": "DocType",
"document_type": "Transaction",
"allow_copy": 0,
"allow_import": 1,
"allow_rename": 0,
"autoname": "Offer-.#####",
"creation": "2015-03-04 14:20:17.662207",
"custom": 0,
"default_print_format": "Offer Letter",
"docstatus": 0,
"doctype": "DocType",
"document_type": "Transaction",
"fields": [
{
"allow_on_submit": 0,
"fieldname": "job_applicant",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Job Applicant",
"no_copy": 0,
"options": "Job Applicant",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"allow_on_submit": 0,
"fieldname": "job_applicant",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Job Applicant",
"no_copy": 0,
"options": "Job Applicant",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0
},
},
{
"fieldname": "applicant_name",
"fieldtype": "Data",
"label": "Applicant Name",
"options": "job_applicant.applicant_name",
"permlevel": 0,
"precision": "",
"read_only": 1,
"fieldname": "applicant_name",
"fieldtype": "Data",
"label": "Applicant Name",
"options": "job_applicant.applicant_name",
"permlevel": 0,
"precision": "",
"read_only": 1,
"reqd": 1
},
},
{
"fieldname": "column_break_3",
"fieldtype": "Column Break",
"permlevel": 0,
"fieldname": "column_break_3",
"fieldtype": "Column Break",
"permlevel": 0,
"precision": ""
},
},
{
"allow_on_submit": 1,
"fieldname": "status",
"fieldtype": "Select",
"label": "Status",
"options": "Awaiting Response\nAccepted\nRejected ",
"permlevel": 0,
"precision": "",
"allow_on_submit": 1,
"fieldname": "status",
"fieldtype": "Select",
"label": "Status",
"options": "Awaiting Response\nAccepted\nRejected",
"permlevel": 0,
"precision": "",
"print_hide": 1
},
},
{
"default": "",
"fieldname": "offer_date",
"fieldtype": "Date",
"label": "Offer Date",
"permlevel": 0,
"default": "",
"fieldname": "offer_date",
"fieldtype": "Date",
"label": "Offer Date",
"permlevel": 0,
"precision": ""
},
},
{
"allow_on_submit": 0,
"fieldname": "designation",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Designation",
"no_copy": 0,
"options": "Designation",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"allow_on_submit": 0,
"fieldname": "designation",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Designation",
"no_copy": 0,
"options": "Designation",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0
},
},
{
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"options": "Company",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"options": "Company",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"reqd": 1
},
},
{
"fieldname": "section_break_4",
"fieldtype": "Section Break",
"permlevel": 0,
"fieldname": "section_break_4",
"fieldtype": "Section Break",
"permlevel": 0,
"precision": ""
},
},
{
"fieldname": "offer_terms",
"fieldtype": "Table",
"label": "Offer Letter Terms",
"options": "Offer Letter Term",
"permlevel": 0,
"fieldname": "offer_terms",
"fieldtype": "Table",
"label": "Offer Letter Terms",
"options": "Offer Letter Term",
"permlevel": 0,
"precision": ""
},
},
{
"fieldname": "section_break_14",
"fieldtype": "Section Break",
"permlevel": 0,
"fieldname": "section_break_14",
"fieldtype": "Section Break",
"permlevel": 0,
"precision": ""
},
},
{
"allow_on_submit": 0,
"fieldname": "select_terms",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Select Terms and Conditions",
"no_copy": 0,
"options": "Terms and Conditions",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"allow_on_submit": 0,
"fieldname": "select_terms",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Select Terms and Conditions",
"no_copy": 0,
"options": "Terms and Conditions",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0
},
},
{
"fieldname": "terms",
"fieldtype": "Text Editor",
"label": "Terms and Conditions",
"options": "",
"permlevel": 0,
"fieldname": "terms",
"fieldtype": "Text Editor",
"label": "Terms and Conditions",
"options": "",
"permlevel": 0,
"precision": ""
},
},
{
"fieldname": "amended_from",
"fieldtype": "Link",
"label": "Amended From",
"no_copy": 1,
"options": "Offer Letter",
"permlevel": 0,
"print_hide": 1,
"fieldname": "amended_from",
"fieldtype": "Link",
"label": "Amended From",
"no_copy": 1,
"options": "Offer Letter",
"permlevel": 0,
"print_hide": 1,
"read_only": 1
}
],
"hide_heading": 0,
"hide_toolbar": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"modified": "2015-04-01 05:51:39.841591",
"modified_by": "Administrator",
"module": "HR",
"name": "Offer Letter",
"name_case": "",
"owner": "Administrator",
],
"hide_heading": 0,
"hide_toolbar": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"modified": "2015-07-29 05:51:39.841591",
"modified_by": "Administrator",
"module": "HR",
"name": "Offer Letter",
"name_case": "",
"owner": "Administrator",
"permissions": [
{
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"import": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "HR User",
"share": 1,
"submit": 1,
"amend": 1,
"cancel": 1,
"create": 1,
"delete": 1,
"email": 1,
"export": 1,
"import": 1,
"permlevel": 0,
"print": 1,
"read": 1,
"report": 1,
"role": "HR User",
"share": 1,
"submit": 1,
"write": 1
}
],
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC",
],
"read_only": 0,
"read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC",
"title_field": "applicant_name"
}
}

View File

@@ -9,8 +9,13 @@ from frappe.desk.reportview import execute as runreport
def execute(filters=None):
if not filters: filters = {}
employee_filters = filters.get("company") and \
[["Employee", "company", "=", filters.get("company")]] or None
employee_filters = {
"status": "Active"
}
if filters.get("company"):
filters["company"] = filters.company
employees = runreport(doctype="Employee", fields=["name", "employee_name", "department"],
filters=employee_filters, limit_page_length=None)

View File

@@ -24,7 +24,7 @@ class HubSettings(Document):
def publish_selling_items(self):
"""Set `publish_in_hub`=1 for all Sales Items"""
for item in frappe.get_all("Item", fields=["name"],
filters={"is_sales_item": "Yes", "publish_in_hub": "0"}):
filters={"is_sales_item": 1, "publish_in_hub": "0"}):
frappe.db.set_value("Item", item.name, "publish_in_hub", 1)
def register(self):

View File

@@ -107,7 +107,7 @@ class BOM(Document):
rate = 0
if arg['bom_no']:
rate = self.get_bom_unitcost(arg['bom_no'])
elif arg and (arg['is_purchase_item'] == 'Yes' or arg['is_sub_contracted_item'] == 'Yes'):
elif arg and (arg['is_purchase_item'] == 1 or arg['is_sub_contracted_item'] == 1):
if self.rm_cost_as_per == 'Valuation Rate':
rate = self.get_valuation_rate(arg)
elif self.rm_cost_as_per == 'Last Purchase Rate':
@@ -181,15 +181,12 @@ class BOM(Document):
if item.default_bom != self.name:
item.default_bom = self.name
item.save()
else:
if not self.is_active:
frappe.db.set(self, "is_default", 0)
item = frappe.get_doc("Item", self.item)
if item.default_bom == self.name:
item.default_bom = None
item.save()
frappe.db.set(self, "is_default", 0)
item = frappe.get_doc("Item", self.item)
if item.default_bom == self.name:
item.default_bom = None
item.save()
def clear_operations(self):
if not self.with_operations:
@@ -367,7 +364,7 @@ class BOM(Document):
if self.with_operations and not self.get('operations'):
frappe.throw(_("Operations cannot be left blank."))
def get_bom_items_as_dict(bom, qty=1, fetch_exploded=1):
def get_bom_items_as_dict(bom, company, qty=1, fetch_exploded=1):
item_dict = {}
# Did not use qty_consumed_per_unit in the query, as it leads to rounding loss
@@ -388,14 +385,14 @@ def get_bom_items_as_dict(bom, qty=1, fetch_exploded=1):
and bom_item.docstatus < 2
and bom_item.parent = %(bom)s
and item.name = bom_item.item_code
and ifnull(item.is_stock_item, 'No') = 'Yes'
and is_stock_item = 1
{conditions}
group by item_code, stock_uom"""
if fetch_exploded:
query = query.format(table="BOM Explosion Item",
conditions="""and ifnull(item.is_pro_applicable, 'No') = 'No'
and ifnull(item.is_sub_contracted_item, 'No') = 'No' """)
conditions="""and item.is_pro_applicable = 0
and item.is_sub_contracted_item = 0 """)
items = frappe.db.sql(query, { "qty": qty, "bom": bom }, as_dict=True)
else:
query = query.format(table="BOM Item", conditions="")
@@ -408,11 +405,18 @@ def get_bom_items_as_dict(bom, qty=1, fetch_exploded=1):
else:
item_dict[item.item_code] = item
for item, item_details in item_dict.items():
for d in [["Account", "expense_account", "default_expense_account"],
["Cost Center", "cost_center", "cost_center"], ["Warehouse", "default_warehouse", ""]]:
company_in_record = frappe.db.get_value(d[0], item_details.get(d[1]), "company")
if not item_details.get(d[1]) or (company_in_record and company != company_in_record):
item_dict[item][d[1]] = frappe.db.get_value("Company", company, d[2]) if d[2] else None
return item_dict
@frappe.whitelist()
def get_bom_items(bom, qty=1, fetch_exploded=1):
items = get_bom_items_as_dict(bom, qty, fetch_exploded).values()
def get_bom_items(bom, company, qty=1, fetch_exploded=1):
items = get_bom_items_as_dict(bom, company, qty, fetch_exploded).values()
items.sort(lambda a, b: a.item_code > b.item_code and 1 or -1)
return items

View File

@@ -12,14 +12,14 @@ test_records = frappe.get_test_records('BOM')
class TestBOM(unittest.TestCase):
def test_get_items(self):
from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict
items_dict = get_bom_items_as_dict(bom=get_default_bom(), qty=1, fetch_exploded=0)
items_dict = get_bom_items_as_dict(bom=get_default_bom(), company="_Test Company", qty=1, fetch_exploded=0)
self.assertTrue(test_records[2]["items"][0]["item_code"] in items_dict)
self.assertTrue(test_records[2]["items"][1]["item_code"] in items_dict)
self.assertEquals(len(items_dict.values()), 2)
def test_get_items_exploded(self):
from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict
items_dict = get_bom_items_as_dict(bom=get_default_bom(), qty=1, fetch_exploded=1)
items_dict = get_bom_items_as_dict(bom=get_default_bom(), company="_Test Company", qty=1, fetch_exploded=1)
self.assertTrue(test_records[2]["items"][0]["item_code"] in items_dict)
self.assertFalse(test_records[2]["items"][1]["item_code"] in items_dict)
self.assertTrue(test_records[0]["items"][0]["item_code"] in items_dict)
@@ -28,7 +28,7 @@ class TestBOM(unittest.TestCase):
def test_get_items_list(self):
from erpnext.manufacturing.doctype.bom.bom import get_bom_items
self.assertEquals(len(get_bom_items(bom=get_default_bom())), 3)
self.assertEquals(len(get_bom_items(bom=get_default_bom(), company="_Test Company")), 3)
def test_default_bom(self):
def _get_default_bom_in_item():

View File

@@ -15,6 +15,14 @@
"permlevel": 0,
"precision": ""
},
{
"description": "Disables creation of time logs against Production Orders.\nOperations shall not be tracked against Production Order",
"fieldname": "disable_capacity_planning",
"fieldtype": "Check",
"label": "Disable Capacity Planning and Time Tracking",
"permlevel": 0,
"precision": ""
},
{
"description": "Plan time logs outside Workstation Working Hours.",
"fieldname": "allow_overtime",
@@ -72,7 +80,7 @@
"is_submittable": 0,
"issingle": 1,
"istable": 0,
"modified": "2015-06-15 05:52:22.986958",
"modified": "2015-07-23 08:12:33.889753",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Manufacturing Settings",

View File

@@ -186,27 +186,15 @@ $.extend(cur_frm.cscript, {
},
bom_no: function() {
if (this.frm.doc.track_operations) {
return this.frm.call({
doc: this.frm.doc,
method: "set_production_order_operations"
});
}
return this.frm.call({
doc: this.frm.doc,
method: "set_production_order_operations"
});
},
qty: function() {
frappe.ui.form.trigger("Production Order", 'bom_no')
},
track_operations: function(doc) {
if (doc.track_operations) {
frappe.ui.form.trigger("Production Order", 'bom_no')
}
else {
doc.operations =[];
}
},
show_time_logs: function(doc, cdt, cdn) {
var child = locals[cdt][cdn]
frappe.route_options = {"operation_id": child.name};
@@ -261,8 +249,9 @@ cur_frm.cscript['Update Finished Goods'] = function() {
cur_frm.fields_dict['production_item'].get_query = function(doc) {
return {
filters:[
['Item', 'is_pro_applicable', '=', 'Yes'],
['Item', 'has_variants', '=', 'No']
['Item', 'is_pro_applicable', '=', 1],
['Item', 'has_variants', '=', 0]
['Item', 'end_of_life', '>=', frappe.datetime.nowdate()]
]
}
}
@@ -274,7 +263,3 @@ cur_frm.fields_dict['project_name'].get_query = function(doc, dt, dn) {
]
}
}

View File

@@ -73,14 +73,6 @@
"label": "Use Multi-Level BOM",
"permlevel": 0
},
{
"default": "1",
"fieldname": "track_operations",
"fieldtype": "Check",
"label": "Track Operations",
"permlevel": 0,
"precision": ""
},
{
"fieldname": "column_break1",
"fieldtype": "Column Break",
@@ -215,7 +207,7 @@
"read_only": 1
},
{
"depends_on": "track_operations",
"depends_on": "",
"fieldname": "operations_section",
"fieldtype": "Section Break",
"label": "Operations",
@@ -234,7 +226,7 @@
"read_only": 1
},
{
"depends_on": "track_operations",
"depends_on": "operations",
"fieldname": "section_break_22",
"fieldtype": "Section Break",
"label": "Operation Cost",
@@ -368,7 +360,7 @@
"idx": 1,
"in_create": 0,
"is_submittable": 1,
"modified": "2015-07-09 03:31:01.291811",
"modified": "2015-07-21 07:45:53.206902",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Production Order",
@@ -395,7 +387,7 @@
"permlevel": 0,
"read": 1,
"report": 1,
"role": "Material User"
"role": "Stock User"
}
],
"title_field": "production_item"

View File

@@ -9,10 +9,13 @@ from frappe import _
from frappe.model.document import Document
from erpnext.manufacturing.doctype.bom.bom import validate_bom_no
from dateutil.relativedelta import relativedelta
from erpnext.stock.doctype.item.item import validate_end_of_life
class OverProductionError(frappe.ValidationError): pass
class StockOverProductionError(frappe.ValidationError): pass
class OperationTooLongError(frappe.ValidationError): pass
class ProductionNotApplicableError(frappe.ValidationError): pass
class ItemHasVariantError(frappe.ValidationError): pass
from erpnext.manufacturing.doctype.workstation.workstation import WorkstationHolidayError, NotInWorkingHoursError
from erpnext.projects.doctype.time_log.time_log import OverlapError
@@ -91,7 +94,7 @@ class ProductionOrder(Document):
(self.sales_order, self.production_item))[0][0]
# total qty in SO
so_qty = flt(so_item_qty) + flt(dnpi_qty)
allowance_percentage = flt(frappe.db.get_single_value("Manufacturing Settings", "over_production_allowance_percentage"))
if total_qty > so_qty + (allowance_percentage/100 * so_qty):
frappe.throw(_("Cannot produce more Item {0} than Sales Order quantity {1}").format(self.production_item,
@@ -174,17 +177,12 @@ class ProductionOrder(Document):
def set_production_order_operations(self):
"""Fetch operations from BOM and set in 'Production Order'"""
if not self.bom_no:
if not self.bom_no or cint(frappe.db.get_single_value("Manufacturing Settings", "disable_capacity_planning")):
return
self.set('operations', [])
operations = frappe.db.sql("""select operation, description, workstation, idx,
hour_rate, time_in_mins, "Pending" as status from `tabBOM Operation`
where parent = %s order by idx""", self.bom_no, as_dict=1)
if operations:
self.track_operations=1
else:
self.track_operations=0
frappe.msgprint(_("Cannot 'track operations' as selected BOM does not have Operations."))
self.set('operations', operations)
self.calculate_time()
@@ -322,25 +320,30 @@ class ProductionOrder(Document):
def delete_time_logs(self):
for time_log in frappe.get_all("Time Log", ["name"], {"production_order": self.name}):
frappe.delete_doc("Time Log", time_log.name)
def validate_production_item(self):
if frappe.db.get_value("Item", self.production_item, "is_pro_applicable")=='No':
frappe.throw(_("Item is not allowed to have Production Order."))
if not frappe.db.get_value("Item", self.production_item, "is_pro_applicable"):
frappe.throw(_("Item is not allowed to have Production Order."), ProductionNotApplicableError)
if frappe.db.get_value("Item", self.production_item, "has_variants"):
frappe.throw(_("Production Order cannot be raised against a Item Template"))
frappe.throw(_("Production Order cannot be raised against a Item Template"), ItemHasVariantError)
validate_end_of_life(self.production_item)
@frappe.whitelist()
def get_item_details(item):
res = frappe.db.sql("""select stock_uom, description
from `tabItem` where (ifnull(end_of_life, "0000-00-00")="0000-00-00" or end_of_life > now())
and name=%s""", item, as_dict=1)
if not res:
return {}
res = res[0]
res["bom_no"] = frappe.db.get_value("BOM", filters={"item": item, "is_default": 1})
if not res["bom_no"]:
variant_of= frappe.db.get_value("Item", item, "variant_of")
if variant_of:
res["bom_no"] = frappe.db.get_value("BOM", filters={"item": variant_of, "is_default": 1})
return res
@frappe.whitelist()

View File

@@ -5,9 +5,10 @@
from __future__ import unicode_literals
import unittest
import frappe
from frappe.utils import flt, get_datetime, time_diff_in_hours
from frappe.utils import flt, time_diff_in_hours, now, add_days
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
from erpnext.manufacturing.doctype.production_order.production_order import make_stock_entry, make_time_log
from erpnext.manufacturing.doctype.production_order.production_order \
import make_stock_entry, ProductionNotApplicableError,ItemHasVariantError
from erpnext.stock.doctype.stock_entry import test_stock_entry
from erpnext.projects.doctype.time_log.time_log import OverProductionLoggedError
@@ -67,8 +68,9 @@ class TestProductionOrder(unittest.TestCase):
self.assertRaises(StockOverProductionError, s.submit)
def test_make_time_log(self):
from erpnext.projects.doctype.time_log.test_time_log import make_time_log_test_record
prod_order = make_prod_order_test_record(item="_Test FG Item 2",
planned_start_date="2014-11-25 00:00:00", qty=1, do_not_save=True)
planned_start_date=now(), qty=1, do_not_save=True)
prod_order.set_production_order_operations()
prod_order.insert()
@@ -78,17 +80,13 @@ class TestProductionOrder(unittest.TestCase):
d.completed_qty = flt(d.completed_qty)
time_log = make_time_log(prod_order.name, d.operation, \
d.planned_start_time, d.planned_end_time, prod_order.qty - d.completed_qty,
operation_id=d.name)
time_log = make_time_log_test_record(hours=1, production_order= prod_order.name, operation= d.operation,
completed_qty= prod_order.qty - d.completed_qty, operation_id=d.name, for_manufacturing=1, simulate=True)
self.assertEqual(prod_order.name, time_log.production_order)
self.assertEqual((prod_order.qty - d.completed_qty), time_log.completed_qty)
self.assertEqual(time_diff_in_hours(d.planned_end_time, d.planned_start_time),time_log.hours)
time_log.save()
time_log.submit()
manufacturing_settings = frappe.get_doc({
"doctype": "Manufacturing Settings",
"allow_production_on_holidays": 0
@@ -100,11 +98,6 @@ class TestProductionOrder(unittest.TestCase):
self.assertEqual(prod_order.operations[0].status, "Completed")
self.assertEqual(prod_order.operations[0].completed_qty, prod_order.qty)
self.assertEqual(get_datetime(prod_order.operations[0].actual_start_time),
get_datetime(time_log.from_time))
self.assertEqual(get_datetime(prod_order.operations[0].actual_end_time),
get_datetime(time_log.to_time))
self.assertEqual(prod_order.operations[0].actual_operation_time, 60)
self.assertEqual(prod_order.operations[0].actual_operating_cost, 100)
@@ -117,24 +110,36 @@ class TestProductionOrder(unittest.TestCase):
self.assertEqual(flt(prod_order.operations[0].actual_operation_time), 0)
self.assertEqual(flt(prod_order.operations[0].actual_operating_cost), 0)
time_log2 = frappe.copy_doc(time_log)
time_log2.update({
"completed_qty": 10,
"from_time": "2014-11-26 00:00:00",
"to_time": "2014-11-26 00:00:00",
"docstatus": 0
})
time_log2 = make_time_log_test_record(from_time= add_days(time_log.to_time, 1) ,production_order= prod_order.name, operation= d.operation,
completed_qty= 5, operation_id=d.name, for_manufacturing=1, do_not_save=True)
self.assertRaises(OverProductionLoggedError, time_log2.save)
def test_planned_operating_cost(self):
prod_order = make_prod_order_test_record(item="_Test FG Item 2",
planned_start_date="2014-11-25 00:00:00", qty=1, do_not_save=True)
planned_start_date=now(), qty=1, do_not_save=True)
prod_order.set_production_order_operations()
cost = prod_order.planned_operating_cost
prod_order.qty = 2
prod_order.set_production_order_operations()
self.assertEqual(prod_order.planned_operating_cost, cost*2)
def test_production_item(self):
frappe.db.set_value("Item", "_Test FG Item", "is_pro_applicable", 0)
prod_order = make_prod_order_test_record(item="_Test FG Item", qty=1, do_not_save=True)
self.assertRaises(ProductionNotApplicableError, prod_order.save)
frappe.db.set_value("Item", "_Test FG Item", "is_pro_applicable", 1)
frappe.db.set_value("Item", "_Test FG Item", "end_of_life", "2000-1-1")
self.assertRaises(frappe.ValidationError, prod_order.save)
frappe.db.set_value("Item", "_Test FG Item", "end_of_life", None)
prod_order = make_prod_order_test_record(item="_Test Variant Item", qty=1, do_not_save=True)
self.assertRaises(ItemHasVariantError, prod_order.save)
def make_prod_order_test_record(**args):
args = frappe._dict(args)

View File

@@ -52,7 +52,7 @@ cur_frm.fields_dict['sales_orders'].grid.get_field('sales_order').get_query = fu
cur_frm.fields_dict['items'].grid.get_field('item_code').get_query = function(doc) {
return erpnext.queries.item({
'is_pro_applicable': 'Yes'
'is_pro_applicable': 1
});
}

View File

@@ -9,6 +9,7 @@ from frappe import msgprint, _
from frappe.model.document import Document
from erpnext.manufacturing.doctype.bom.bom import validate_bom_no
from erpnext.manufacturing.doctype.production_order.production_order import get_item_details
class ProductionPlanningTool(Document):
def __init__(self, arg1, arg2=None):
@@ -27,16 +28,7 @@ class ProductionPlanningTool(Document):
return ret
def get_item_details(self, item_code):
""" Pull other item details from item master"""
item = frappe.db.sql("""select description, stock_uom, default_bom
from `tabItem` where name = %s""", item_code, as_dict =1)
ret = {
'description' : item and item[0]['description'],
'stock_uom' : item and item[0]['stock_uom'],
'bom_no' : item and item[0]['default_bom']
}
return ret
return get_item_details(item_code)
def clear_so_table(self):
self.set('sales_orders', [])
@@ -69,13 +61,13 @@ class ProductionPlanningTool(Document):
and so.company = %s
and ifnull(so_item.qty, 0) > ifnull(so_item.delivered_qty, 0) %s
and (exists (select name from `tabItem` item where item.name=so_item.item_code
and (ifnull(item.is_pro_applicable, 'No') = 'Yes'
or ifnull(item.is_sub_contracted_item, 'No') = 'Yes') %s)
and (item.is_pro_applicable = 1
or item.is_sub_contracted_item = 1 %s)
or exists (select name from `tabPacked Item` pi
where pi.parent = so.name and pi.parent_item = so_item.item_code
and exists (select name from `tabItem` item where item.name=pi.item_code
and (ifnull(item.is_pro_applicable, 'No') = 'Yes'
or ifnull(item.is_sub_contracted_item, 'No') = 'Yes') %s)))
and (item.is_pro_applicable = 1
or item.is_sub_contracted_item = 1) %s)))
""" % ('%s', so_filter, item_filter, item_filter), self.company, as_dict=1)
self.add_so_in_table(open_so)
@@ -116,8 +108,8 @@ class ProductionPlanningTool(Document):
from `tabSales Order Item` so_item
where parent in (%s) and docstatus = 1 and ifnull(qty, 0) > ifnull(delivered_qty, 0)
and exists (select * from `tabItem` item where item.name=so_item.item_code
and (ifnull(item.is_pro_applicable, 'No') = 'Yes'
or ifnull(item.is_sub_contracted_item, 'No') = 'Yes')) %s""" % \
and (item.is_pro_applicable = 1
or item.is_sub_contracted_item = 1)) %s""" % \
(", ".join(["%s"] * len(so_list)), item_condition), tuple(so_list), as_dict=1)
if self.fg_item:
@@ -131,8 +123,8 @@ class ProductionPlanningTool(Document):
and pi.parent_item = so_item.item_code
and so_item.parent in (%s) and ifnull(so_item.qty, 0) > ifnull(so_item.delivered_qty, 0)
and exists (select * from `tabItem` item where item.name=pi.item_code
and (ifnull(item.is_pro_applicable, 'No') = 'Yes'
or ifnull(item.is_sub_contracted_item, 'No') = 'Yes')) %s""" % \
and (item.is_pro_applicable = 1
or item.is_sub_contracted_item = 1)) %s""" % \
(", ".join(["%s"] * len(so_list)), item_condition), tuple(so_list), as_dict=1)
return items + packed_items
@@ -142,15 +134,14 @@ class ProductionPlanningTool(Document):
self.clear_item_table()
for p in items:
item_details = frappe.db.sql("""select description, stock_uom, default_bom
from tabItem where name=%s""", p['item_code'])
item_details = get_item_details(p['item_code'])
pi = self.append('items', {})
pi.sales_order = p['parent']
pi.warehouse = p['warehouse']
pi.item_code = p['item_code']
pi.description = item_details and item_details[0][0] or ''
pi.stock_uom = item_details and item_details[0][1] or ''
pi.bom_no = item_details and item_details[0][2] or ''
pi.description = item_details and item_details.description or ''
pi.stock_uom = item_details and item_details.stock_uom or ''
pi.bom_no = item_details and item_details.bom_no or ''
pi.so_pending_qty = flt(p['pending_qty'])
pi.planned_qty = flt(p['pending_qty'])
@@ -187,7 +178,7 @@ class ProductionPlanningTool(Document):
for d in self.get("items"):
if d.bom_no:
bom_dict.setdefault(d.bom_no, []).append([d.sales_order, flt(d.planned_qty)])
if frappe.db.get_value("Item", d.item_code, "is_pro_applicable") == "Yes":
if frappe.db.get_value("Item", d.item_code, "is_pro_applicable"):
item_dict[(d.item_code, d.sales_order, d.warehouse)] = {
"production_item" : d.item_code,
"sales_order" : d.sales_order,
@@ -248,10 +239,10 @@ class ProductionPlanningTool(Document):
ifnull(sum(ifnull(fb.qty, 0)/ifnull(bom.quantity, 1)), 0) as qty,
fb.description, fb.stock_uom, it.min_order_qty
from `tabBOM Explosion Item` fb, `tabBOM` bom, `tabItem` it
where bom.name = fb.parent and it.name = fb.item_code
and ifnull(it.is_pro_applicable, 'No') = 'No'
and ifnull(it.is_sub_contracted_item, 'No') = 'No'
and ifnull(it.is_stock_item, 'No') = 'Yes'
where bom.name = fb.parent and it.name = fb.item_code
and is_pro_applicable = 0
and is_sub_contracted_item = 0
and is_stock_item = 1
and fb.docstatus<2 and bom.name=%s
group by item_code, stock_uom""", bom, as_dict=1):
bom_wise_item_details.setdefault(d.item_code, d)
@@ -264,7 +255,7 @@ class ProductionPlanningTool(Document):
from `tabBOM Item` bom_item, `tabBOM` bom, tabItem item
where bom.name = bom_item.parent and bom.name = %s and bom_item.docstatus < 2
and bom_item.item_code = item.name
and ifnull(item.is_stock_item, 'No') = 'Yes'
and item.is_stock_item = 1
group by item_code""", bom, as_dict=1):
bom_wise_item_details.setdefault(d.item_code, d)
@@ -359,7 +350,7 @@ class ProductionPlanningTool(Document):
def insert_purchase_request(self):
items_to_be_requested = self.get_requested_items()
purchase_request_list = []
if items_to_be_requested:
for item in items_to_be_requested:

View File

@@ -9,6 +9,5 @@ Manufacturing
Stock
Support
Utilities
Contacts
Shopping Cart
Hub Node

View File

@@ -24,6 +24,9 @@ erpnext.patches.v4_0.map_charge_to_taxes_and_charges
execute:frappe.reload_doc('support', 'doctype', 'newsletter') # 2014-01-31
execute:frappe.reload_doc('hr', 'doctype', 'employee') # 2014-02-03
execute:frappe.db.sql("update tabPage set module='Core' where name='Setup'")
# inserted this patch here since Item types are being changed
erpnext.patches.v5_2.change_item_selects_to_checks
erpnext.patches.v4_0.fields_to_be_renamed
erpnext.patches.v4_0.rename_sitemap_to_route
erpnext.patches.v4_0.fix_contact_address
@@ -100,6 +103,7 @@ erpnext.patches.v5_0.capacity_planning
execute:frappe.reload_doc('crm', 'doctype', 'lead')
execute:frappe.reload_doc('crm', 'doctype', 'opportunity')
erpnext.patches.v5_0.rename_taxes_and_charges_master
erpnext.patches.v5_1.sales_bom_rename
erpnext.patches.v5_0.rename_table_fieldnames
execute:frappe.db.sql("update `tabJournal Entry` set voucher_type='Journal Entry' where ifnull(voucher_type, '')=''")
erpnext.patches.v5_0.is_group
@@ -172,6 +176,11 @@ erpnext.patches.v5_0.item_variants
erpnext.patches.v5_0.update_item_desc_in_invoice
erpnext.patches.v5_1.fix_against_account
erpnext.patches.v5_1.fix_credit_days_based_on
erpnext.patches.v5_1.track_operations
erpnext.patches.v5_1.sales_bom_rename
execute:frappe.rename_doc("DocType", "Salary Manager", "Process Payroll", force=True)
erpnext.patches.v5_1.rename_roles
erpnext.patches.v5_1.default_bom
execute:frappe.delete_doc("DocType", "Party Type")
execute:frappe.delete_doc("Module Def", "Contacts")
erpnext.patches.v5_4.fix_reserved_qty_and_sle_for_packed_items # 30-07-2015
execute:frappe.reload_doctype("Leave Type")
execute:frappe.db.sql("update `tabLeave Type` set include_holiday=0")

View File

@@ -3,7 +3,7 @@
from __future__ import unicode_literals
import frappe
from frappe.model import rename_field
from frappe.model.utils.rename_field import rename_field
from frappe.modules import scrub, get_doctype_module
rename_map = {

View File

@@ -5,61 +5,61 @@ from __future__ import unicode_literals
import frappe
def execute():
frappe.db.sql("update tabItem set has_batch_no = 'No' where ifnull(has_batch_no, '') = ''")
frappe.db.sql("update tabItem set has_serial_no = 'No' where ifnull(has_serial_no, '') = ''")
item_list = frappe.db.sql("""select name, has_batch_no, has_serial_no from tabItem
where ifnull(is_stock_item, 'No') = 'Yes'""", as_dict=1)
frappe.db.sql("update tabItem set has_batch_no = 0 where ifnull(has_batch_no, '') = ''")
frappe.db.sql("update tabItem set has_serial_no = 0 where ifnull(has_serial_no, '') = ''")
item_list = frappe.db.sql("""select name, has_batch_no, has_serial_no from tabItem
where is_stock_item = 1""", as_dict=1)
sle_count = get_sle_count()
sle_with_batch = get_sle_with_batch()
sle_with_serial = get_sle_with_serial()
batch_items = get_items_with_batch()
serialized_items = get_items_with_serial()
for d in item_list:
if d.has_batch_no == 'Yes':
for d in item_list:
if d.has_batch_no == 1:
if d.name not in batch_items and sle_count.get(d.name) and sle_count.get(d.name) != sle_with_batch.get(d.name):
frappe.db.set_value("Item", d.name, "has_batch_no", "No")
frappe.db.set_value("Item", d.name, "has_batch_no", 0)
else:
if d.name in batch_items or (sle_count.get(d.name) and sle_count.get(d.name) == sle_with_batch.get(d.name)):
frappe.db.set_value("Item", d.name, "has_batch_no", "Yes")
if d.has_serial_no == 'Yes':
frappe.db.set_value("Item", d.name, "has_batch_no", 1)
if d.has_serial_no == 1:
if d.name not in serialized_items and sle_count.get(d.name) and sle_count.get(d.name) != sle_with_serial.get(d.name):
frappe.db.set_value("Item", d.name, "has_serial_no", "No")
frappe.db.set_value("Item", d.name, "has_serial_no", 0)
else:
if d.name in serialized_items or (sle_count.get(d.name) and sle_count.get(d.name) == sle_with_serial.get(d.name)):
frappe.db.set_value("Item", d.name, "has_serial_no", "Yes")
frappe.db.set_value("Item", d.name, "has_serial_no", 1)
def get_sle_count():
sle_count = {}
for d in frappe.db.sql("""select item_code, count(name) as cnt from `tabStock Ledger Entry` group by item_code""", as_dict=1):
sle_count.setdefault(d.item_code, d.cnt)
return sle_count
def get_sle_with_batch():
sle_with_batch = {}
for d in frappe.db.sql("""select item_code, count(name) as cnt from `tabStock Ledger Entry`
for d in frappe.db.sql("""select item_code, count(name) as cnt from `tabStock Ledger Entry`
where ifnull(batch_no, '') != '' group by item_code""", as_dict=1):
sle_with_batch.setdefault(d.item_code, d.cnt)
return sle_with_batch
def get_sle_with_serial():
sle_with_serial = {}
for d in frappe.db.sql("""select item_code, count(name) as cnt from `tabStock Ledger Entry`
for d in frappe.db.sql("""select item_code, count(name) as cnt from `tabStock Ledger Entry`
where ifnull(serial_no, '') != '' group by item_code""", as_dict=1):
sle_with_serial.setdefault(d.item_code, d.cnt)
return sle_with_serial
def get_items_with_batch():
return frappe.db.sql_list("select item from tabBatch")
def get_items_with_serial():
return frappe.db.sql_list("select item_code from `tabSerial No`")
return frappe.db.sql_list("select item_code from `tabSerial No`")

View File

@@ -2,7 +2,7 @@
# License: GNU General Public License v3. See license.txt
import frappe
from frappe.model import rename_field
from frappe.model.utils.rename_field import rename_field
from frappe.modules import scrub, get_doctype_module
rename_map = {
@@ -111,7 +111,6 @@ rename_map = {
["installed_item_details", "items"]
],
"Item": [
["item_variants", "variants"],
["item_reorder", "reorder_levels"],
["uom_conversion_details", "uoms"],
["item_supplier_details", "supplier_items"],
@@ -168,7 +167,7 @@ rename_map = {
["earning_details", "earnings"],
["deduction_details", "deductions"]
],
"Sales BOM": [
"Product Bundle": [
["sales_bom_items", "items"]
],
"SMS Settings": [

View File

@@ -3,7 +3,7 @@
from __future__ import unicode_literals
import frappe
from frappe.model import rename_field
from frappe.model.utils.rename_field import rename_field
from frappe.modules import scrub, get_doctype_module
selling_doctypes = ("Quotation", "Sales Order", "Delivery Note", "Sales Invoice")

View File

@@ -0,0 +1,7 @@
from __future__ import unicode_literals
import frappe
def execute():
frappe.db.sql("""Update `tabItem` as item set default_bom = NULL where
not exists(select name from `tabBOM` as bom where item.default_bom = bom.name and bom.docstatus =1 )""")

View File

@@ -4,6 +4,6 @@ import frappe
def execute():
for dt in ("Customer", "Customer Group", "Company"):
frappe.reload_doctype(dt)
frappe.reload_doctype(dt, force=True)
frappe.db.sql("""update `tab{0}` set credit_days_based_on='Fixed Days'
where ifnull(credit_days, 0) > 0""".format(dt))
where ifnull(credit_days, 0) > 0""".format(dt))

View File

@@ -0,0 +1,9 @@
import frappe
def execute():
if not frappe.db.exists("Role", "Stock User"):
frappe.rename_doc("Role", "Material User", "Stock User")
if not frappe.db.exists("Role", "Stock Manager"):
frappe.rename_doc("Role", "Material Manager", "Stock Manager")
if not frappe.db.exists("Role", "Stock Manager"):
frappe.rename_doc("Role", "Material Master Manager", "Item Manager")

View File

@@ -1,8 +0,0 @@
from __future__ import unicode_literals
import frappe
def execute():
frappe.reload_doctype("Production Order")
frappe.db.sql("""Update `tabProduction Order` as po set track_operations=1 where
exists(select name from `tabProduction Order Operation` as po_operation where po_operation.parent = po.name )""")

View File

@@ -0,0 +1 @@
from __future__ import unicode_literals

View File

@@ -0,0 +1,21 @@
from __future__ import unicode_literals
import frappe
def execute():
fields = ("is_stock_item", "is_asset_item", "has_batch_no", "has_serial_no",
"is_purchase_item", "is_sales_item", "is_service_item", "inspection_required",
"is_pro_applicable", "is_sub_contracted_item")
# convert to 1 or 0
update_str = ", ".join(["`{0}`=if(`{0}`='Yes',1,0)".format(f) for f in fields])
frappe.db.sql("update tabItem set {0}".format(update_str))
frappe.db.commit()
# alter fields to int
for f in fields:
frappe.db.sql("alter table tabItem change {0} {0} int(1) default '0'".format(f, f))
frappe.reload_doctype("Item")

View File

@@ -0,0 +1,28 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
from erpnext.utilities.repost_stock import update_bin_qty, get_reserved_qty, repost_actual_qty
def execute():
cancelled_invoices = frappe.db.sql_list("""select name from `tabSales Invoice`
where docstatus = 2 and ifnull(update_stock, 0) = 1""")
if cancelled_invoices:
repost_for = frappe.db.sql("""select distinct item_code, warehouse from `tabStock Ledger Entry`
where voucher_type = 'Sales Invoice' and voucher_no in (%s)"""
% (', '.join(['%s']*len(cancelled_invoices))), tuple(cancelled_invoices))
frappe.db.sql("""delete from `tabStock Ledger Entry`
where voucher_type = 'Sales Invoice' and voucher_no in (%s)"""
% (', '.join(['%s']*len(cancelled_invoices))), tuple(cancelled_invoices))
for item_code, warehouse in repost_for:
repost_actual_qty(item_code, warehouse)
for item_code, warehouse in frappe.db.sql("""select distinct item_code, warehouse
from `tabPacked Item` where parenttype = 'Sales Invoice' and docstatus = 1"""):
update_bin_qty(item_code, warehouse, {
"reserved_qty": get_reserved_qty(item_code, warehouse)
})

View File

@@ -15,10 +15,11 @@ class TestActivityCost(unittest.TestCase):
activity_cost1.update({
"employee": "_T-Employee-0001",
"employee_name": "_Test Employee",
"activity_type": "_Test Activity Type",
"activity_type": "_Test Activity Type 1",
"billing_rate": 100,
"costing_rate": 50
})
activity_cost1.insert()
activity_cost2 = frappe.copy_doc(activity_cost1)
self.assertRaises(DuplicationError, activity_cost2.insert )
frappe.db.sql("delete from `tabActivity Cost`")

View File

@@ -26,22 +26,29 @@ frappe.ui.form.on("Project Task", "edit_task", function(frm, doctype, name) {
// show tasks
cur_frm.cscript.refresh = function(doc) {
if(!doc.__islocal) {
cur_frm.add_custom_button(__("Gantt Chart"), function() {
frappe.route_options = {"project": doc.name, "start": doc.expected_start_date, "end": doc.expected_end_date};
frappe.set_route("Gantt", "Task");
}, "icon-tasks", true);
cur_frm.add_custom_button(__("Tasks"), function() {
frappe.route_options = {"project": doc.name}
frappe.set_route("List", "Task");
}, "icon-list", true);
cur_frm.add_custom_button(__("Time Logs"), function() {
frappe.route_options = {"project": doc.name}
frappe.set_route("List", "Time Log");
}, "icon-list", true);
cur_frm.add_custom_button(__("Expense Claims"), function() {
frappe.route_options = {"project": doc.name}
frappe.set_route("List", "Expense Claim");
}, "icon-list", true);
if(frappe.model.can_read("Task")) {
cur_frm.add_custom_button(__("Gantt Chart"), function() {
frappe.route_options = {"project": doc.name, "start": doc.expected_start_date, "end": doc.expected_end_date};
frappe.set_route("Gantt", "Task");
}, "icon-tasks", true);
cur_frm.add_custom_button(__("Tasks"), function() {
frappe.route_options = {"project": doc.name}
frappe.set_route("List", "Task");
}, "icon-list", true);
}
if(frappe.model.can_read("Time Log")) {
cur_frm.add_custom_button(__("Time Logs"), function() {
frappe.route_options = {"project": doc.name}
frappe.set_route("List", "Time Log");
}, "icon-list", true);
}
if(frappe.model.can_read("Expense Claim")) {
cur_frm.add_custom_button(__("Expense Claims"), function() {
frappe.route_options = {"project": doc.name}
frappe.set_route("List", "Expense Claim");
}, "icon-list", true);
}
}
}
@@ -56,5 +63,5 @@ cur_frm.fields_dict['sales_order'].get_query = function(doc) {
filters:{
'project_name': doc.name
}
}
}
}

View File

@@ -5,42 +5,59 @@ frappe.provide("erpnext.projects");
cur_frm.add_fetch("project", "company", "company");
erpnext.projects.Task = frappe.ui.form.Controller.extend({
setup: function() {
this.frm.fields_dict.project.get_query = function() {
frappe.ui.form.on("Task", {
refresh: function(frm) {
var doc = frm.doc;
if(!doc.__islocal) {
if(frappe.model.can_read("Time Log")) {
frm.add_custom_button(__("Time Logs"), function() {
frappe.route_options = {"project": doc.project, "task": doc.name}
frappe.set_route("List", "Time Log");
}, "icon-list", true);
}
if(frappe.model.can_read("Expense Claim")) {
frm.add_custom_button(__("Expense Claims"), function() {
frappe.route_options = {"project": doc.project, "task": doc.name}
frappe.set_route("List", "Expense Claim");
}, "icon-list", true);
}
if(frm.perm[0].write) {
if(frm.doc.status==="Open") {
frm.add_custom_button("Close", function() {
frm.set_value("status", "Closed");
frm.save();
});
} else {
frm.add_custom_button("Reopen", function() {
frm.set_value("status", "Open");
frm.save();
});
}
}
}
},
setup: function(frm) {
frm.fields_dict.project.get_query = function() {
return {
query: "erpnext.projects.doctype.task.task.get_project"
}
};
},
project: function() {
if(this.frm.doc.project) {
return get_server_fields('get_project_details', '','', this.frm.doc, this.frm.doc.doctype,
this.frm.doc.name, 1);
project: function(frm) {
if(frm.doc.project) {
return get_server_fields('get_project_details', '','', frm.doc, frm.doc.doctype,
frm.doc.name, 1);
}
},
validate: function() {
this.frm.doc.project && frappe.model.remove_from_locals("Project",
this.frm.doc.project);
validate: function(frm) {
frm.doc.project && frappe.model.remove_from_locals("Project",
frm.doc.project);
},
refresh: function(doc) {
if(!doc.__islocal) {
cur_frm.add_custom_button(__("Time Logs"), function() {
frappe.route_options = {"project": doc.project, "task": doc.name}
frappe.set_route("List", "Time Log");
}, "icon-list", true);
cur_frm.add_custom_button(__("Expense Claims"), function() {
frappe.route_options = {"project": doc.project, "task": doc.name}
frappe.set_route("List", "Expense Claim");
}, "icon-list", true);
}
}
});
cur_frm.add_fetch('task', 'subject', 'subject');
cur_frm.cscript = new erpnext.projects.Task({frm: cur_frm});

View File

@@ -1,10 +1 @@
[
{
"activity_type": "_Test Activity Type",
"docstatus": 1,
"doctype": "Time Log",
"from_time": "2013-01-01 10:00:00.000000",
"note": "_Test Note",
"to_time": "2013-01-01 11:00:00.000000"
}
]
[]

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