Compare commits

..

299 Commits

Author SHA1 Message Date
Anand Doshi
5a49ded5d9 Merge branch 'develop' 2015-10-02 12:43:27 +05:30
Anand Doshi
71f23acc2b bumped to version 6.4.3 2015-10-02 13:13:27 +06:00
Anand Doshi
1a1f790150 [fix] test case 2015-10-02 12:34:18 +05:30
Rushabh Mehta
3c54e9779b Merge pull request #4108 from nabinhait/payment_reco
[fix] Payment Reconciliation in multi-currency
2015-10-02 12:32:54 +05:30
Nabin Hait
db48b7d764 [fix] Payment Reconciliation in multi-currency 2015-10-02 12:05:55 +05:30
Rushabh Mehta
83c0899c83 [fix] [minor] default ranges for demo 2015-10-02 11:31:37 +05:30
Nabin Hait
e5047ec90a Merge pull request #4106 from anandpdoshi/payment-tool-amount
In Payment Tool, Set Payment Amount = Outstanding Amount if checked
2015-10-02 10:39:57 +05:30
Anand Doshi
d9ab725be4 [fix] In Payment Tool, Set Payment Amount = Outstanding Amount if checked 2015-10-01 19:17:02 +05:30
Anand Doshi
7afaeb0820 [fix] the case when party account currency is missing 2015-10-01 18:55:43 +05:30
Anand Doshi
da2d8b958d Merge branch 'develop' 2015-10-01 14:01:22 +05:30
Anand Doshi
ba48f82e03 bumped to version 6.4.2 2015-10-01 14:31:22 +06:00
Anand Doshi
8e7e128e81 Merge pull request #4104 from nabinhait/addresses_and_contacts
[fix] get permission query condition if no permitted links
2015-10-01 14:00:38 +05:30
Anand Doshi
0353569e8b Merge pull request #4103 from nabinhait/monthly_attendance_sheet
[fix] Month issue fixed in Monthly Attendance Sheet
2015-10-01 13:58:33 +05:30
Anand Doshi
665e2f5418 Merge pull request #4100 from nabinhait/general_ledger_report
[fix] General Ledger report fixed
2015-10-01 13:56:13 +05:30
Nabin Hait
dace2b6796 [fix] get permission query condition if no permitted links 2015-10-01 13:54:46 +05:30
Nabin Hait
712b02593a [fix] Month issue fixed in Monthly Attendance Sheet 2015-10-01 12:35:54 +05:30
Anand Doshi
d2a60fd727 Merge pull request #4102 from nabinhait/lead_email
[fix] Validate duplicate email ids in Lead
2015-10-01 12:07:37 +05:30
Nabin Hait
f924e08b93 [fix] Validate duplicate email ids in Lead 2015-10-01 11:51:48 +05:30
Nabin Hait
5c623dae4d [fix] General Ledger report fixed 2015-10-01 11:22:15 +05:30
Anand Doshi
e2c3d40b57 [hotfix] Payment Reconciliation Invoice Type 2015-10-01 11:21:26 +05:30
Anand Doshi
673887455f [fix] Show Contribution (%) in Sales Team table in Customer Form 2015-10-01 11:21:25 +05:30
Anand Doshi
63199e486b Merge branch 'develop' 2015-09-30 20:51:00 +05:30
Anand Doshi
bf7294cf5c bumped to version 6.4.1 2015-09-30 21:20:59 +06:00
Anand Doshi
8aa06a809a Merge pull request #4096 from nabinhait/jv_reference
Repost GL Entries for Journal Entries where reference name is missing
2015-09-30 19:44:21 +05:30
Nabin Hait
72d2d682ae Repost GL Entries for Journal Entries where reference name is missing 2015-09-30 18:50:54 +05:30
Anand Doshi
21f6ea6f7e [hotfix] remove allow on submit from item tables 2015-09-30 18:45:29 +05:30
Anand Doshi
185f488c51 Merge branch 'develop' 2015-09-30 17:23:52 +05:30
Anand Doshi
f3006972d5 bumped to version 6.4.0 2015-09-30 17:53:52 +06:00
Anand Doshi
dccc6bc11d [change-log] 2015-09-30 17:19:51 +05:30
Anand Doshi
15d2c89939 Merge remote-tracking branch 'upstream/master' into develop
Conflicts:
	erpnext/accounts/report/general_ledger/general_ledger.py
2015-09-30 17:13:58 +05:30
Anand Doshi
ca4c8a2a46 [translations] 2015-09-30 17:10:58 +05:30
Anand Doshi
c320fe541d Merge pull request #4067 from anandpdoshi/remove-party-account-currency
Remove party_account_currency from Customer and Supplier, and use currency derived from get_party_account
2015-09-30 16:55:24 +05:30
Anand Doshi
f40a87511e Merge pull request #4091 from rmehta/repack-allow-low-rate
[fix] remove validation to disallow lower rate
2015-09-30 16:46:31 +05:30
Anand Doshi
4945b94950 [fix] added validation to match account currency with existing gle and test case 2015-09-30 16:41:15 +05:30
Rushabh Mehta
cb96b61449 [fix] remove validation to disallow lower rate 2015-09-30 16:22:05 +05:30
Anand Doshi
248c867a2c [minor] removed test case for party multi-currency validation, since party multi-currency is now based on account 2015-09-30 15:11:13 +05:30
Anand Doshi
da98ab6f3c [fix] Only 1 account per company for a party 2015-09-30 15:11:12 +05:30
Anand Doshi
cd0989e051 Added method get_account_currency 2015-09-30 15:11:12 +05:30
Anand Doshi
b20baf894f [fix] Remove party_account_currency from Customer and Supplier, and use currency derived from get_party_account 2015-09-30 15:11:12 +05:30
Nabin Hait
d3cf4f1264 Merge pull request #4075 from anandpdoshi/journal-entry-get-exchange-rate
return exchange rate as 1 if no account is specified
2015-09-30 15:05:41 +05:30
Anand Doshi
2dbe2b63b2 Merge pull request #4088 from anandpdoshi/address-contact-permissions
Apply permissions on address and contact based on permissions of Customer, Supplier
2015-09-30 12:19:50 +05:30
Anand Doshi
edba8f5582 [fix] Print 'Attach' field in Stock Entry Detail 2015-09-30 11:50:08 +05:30
Anand Doshi
89b8d11f9c [fix] Apply permissions on address and contact based on permissions of Customer, Supplier 2015-09-29 20:11:57 +05:30
Anand Doshi
19a33994da [cleanup] removed desk_home and desk_home_flows from conf.js 2015-09-29 20:06:22 +05:30
Rushabh Mehta
89349d3ae3 [fix] [payment-reconciliation] ignore rows with blank invoice numbers 2015-09-29 16:39:53 +05:30
Anand Doshi
f6a31a568a Merge pull request #4085 from rmehta/bulk-priority
[enhancement] lower priority for newsletter emails
2015-09-29 15:25:13 +05:30
Rushabh Mehta
4bcbcd29f7 [enhancement] lower priority for newsletter emails 2015-09-29 14:58:45 +05:30
Anand Doshi
740a11263f [minor] validate invoice number is selected in Payment Reconciliation 2015-09-29 12:55:11 +05:30
Anand Doshi
142859f36e Merge pull request #4076 from anandpdoshi/shopping-cart-order
check doc.has_website_permission in order.html. Merge after frappe/frappe#1316
2015-09-28 19:40:17 +05:30
Anand Doshi
6534ad082d Merge pull request #4069 from anandpdoshi/round-status-updater-percentages
Round status update percentages to 2 decimals
2015-09-28 19:40:01 +05:30
Anand Doshi
6361ae3495 Merge branch 'rmehta-backup-cleanup' into develop 2015-09-28 19:38:06 +05:30
Rushabh Mehta
7f75dbf061 [cleanup] remove backup manager 2015-09-28 19:12:54 +05:30
Anand Doshi
199d8a44fc [fix] check doc.has_website_permission in order.html 2015-09-28 19:04:42 +05:30
Rushabh Mehta
72b1128467 [fixes] accounts controller add flt 2015-09-28 17:29:46 +05:30
Anand Doshi
d6cb55ad1a [fix] return exchange rate as 1 if no account is specified 2015-09-28 17:20:02 +05:30
Anand Doshi
c76e34d7de [fix] In get_bom_items_as_dict don't filter by is_pro_applicable for multi-level bom 2015-09-28 17:01:44 +05:30
Anand Doshi
33b10faf94 Merge pull request #4074 from anandpdoshi/multi-level-bom-fetch
[fix] In get_bom_items_as_dict don't filter by is_pro_applicable for multi-level bom
2015-09-28 17:00:52 +05:30
Anand Doshi
d970b001a4 [fix] Round status update percentages to 2 decimals 2015-09-28 16:52:22 +05:30
Anand Doshi
d767fb6134 [fix] In get_bom_items_as_dict don't filter by is_pro_applicable for multi-level bom 2015-09-28 16:44:48 +05:30
Rushabh Mehta
6239923340 Merge pull request #4071 from gmarke/patch-2
Update get_item_details.py
2015-09-28 10:18:56 +05:30
gmarke
e5a31462fe Update get_item_details.py
like this it will be possible to correctly handle cost center for stock entries when no default cost center per company is set
2015-09-27 09:38:01 +02:00
Anand Doshi
6f39300d43 Merge pull request #4068 from anandpdoshi/due-date-validation
if no party, don't validate due date based on credit days
2015-09-25 17:31:33 +05:30
Anand Doshi
0ca587e018 [fix] if no party, don't validate due date based on credit days 2015-09-25 16:32:14 +05:30
Rushabh Mehta
8ffe12ebe4 [chart-of-accounts] added default for Guatemala 2015-09-25 15:40:33 +05:30
Anand Doshi
8579af371c Merge pull request #4057 from rmehta/gl-report-fix
GL Report Fix
2015-09-25 12:30:26 +05:30
Rushabh Mehta
b5ff9421e1 [optimize] customer outstanding query 2015-09-25 12:25:12 +05:30
Rushabh Mehta
ba8ec17f0b Merge pull request #4065 from nabinhait/receivable_payable
[fix] Fetch gl entries in receivable/payable report only if party mentioned
2015-09-25 11:34:14 +05:30
Nabin Hait
44bd3b2601 [fix] Fetch gl entries in receivable/payable report only if party mentioned 2015-09-25 10:33:51 +05:30
Rushabh Mehta
9d40eca428 Merge pull request #4064 from nabinhait/trial_balance_for_party
[report] Trial Balance for Party
2015-09-25 10:01:44 +05:30
Nabin Hait
1b6c00e2c7 [report] Trial Balance for Party 2015-09-25 09:18:03 +05:30
Anand Doshi
52efde31e7 Merge branch 'develop' 2015-09-24 17:31:12 +05:30
Anand Doshi
f723032fd7 bumped to version 6.3.2 2015-09-24 18:01:11 +06:00
Anand Doshi
3297c43bdf [fix] Tax Rule permission to Account Manager 2015-09-24 17:29:28 +05:30
Anand Doshi
aea250bc5a [fix] [patch] fix missing default lead 2015-09-24 16:31:36 +05:30
Anand Doshi
d37d4dfdec [fix] applicable territory patch and tax rule tests 2015-09-24 15:44:39 +05:30
Anand Doshi
0ea68b33ed Merge branch 'develop' 2015-09-24 15:23:05 +05:30
Anand Doshi
7f96c20f5b bumped to version 6.3.1 2015-09-24 15:53:05 +06:00
Anand Doshi
e75d947867 [fix] [patch] convert applicable territory 2015-09-24 15:22:12 +05:30
Anand Doshi
bba0a5d38f [fix] [patch] tax rule 2015-09-24 15:11:24 +05:30
Anand Doshi
953e97536a [fix] [patch] tax rule 2015-09-24 15:05:06 +05:30
Anand Doshi
e74d7ca33e [fix] [patch] tax rule 2015-09-24 15:04:15 +05:30
Rushabh Mehta
8f2b8afcb7 [fix] gl entry report if not filtered 2015-09-24 15:03:53 +05:30
Anand Doshi
b3f12c3109 [fix] [patch] convert_applicable_territory 2015-09-24 15:01:27 +05:30
Anand Doshi
f66e6aacd4 [fix] [patch] convert_applicable_territory 2015-09-24 14:59:26 +05:30
Anand Doshi
1d621be1f7 Merge branch 'develop' 2015-09-24 14:35:36 +05:30
Anand Doshi
ab57e52cbd bumped to version 6.3.0 2015-09-24 15:05:35 +06:00
Anand Doshi
e63da9813c Merge remote-tracking branch 'frappe/master' into develop
Conflicts:
	erpnext/patches.txt
2015-09-24 14:34:50 +05:30
Anand Doshi
c020e42d20 [fix] [patch] convert applicable territory 2015-09-24 13:08:35 +05:30
Anand Doshi
b126ba0132 [change-log] 2015-09-24 13:06:00 +05:30
Anand Doshi
20ae349ebd Merge pull request #4055 from anandpdoshi/project-sales-order-query
Project's Sales Order - get_query fix
2015-09-24 12:49:54 +05:30
Anand Doshi
c6e4b5978d [fix] Sales Order - get_query fix 2015-09-24 11:32:54 +05:30
Anand Doshi
361eca4cae Merge pull request #4054 from anandpdoshi/sales-team-update-after-submit
Sales Team editable after submit
2015-09-24 11:30:31 +05:30
Anand Doshi
9fc03b6c10 Merge pull request #4052 from rmehta/fix-operation-name
[fix] minor, remove operation naming field from operation
2015-09-24 11:30:07 +05:30
Anand Doshi
e2d46d0474 [fix] Allow Sales Team to be added/updated after submit 2015-09-23 19:26:29 +05:30
Rushabh Mehta
dd2f2f5321 [fix] minor, remove operation naming field from operation 2015-09-23 17:06:01 +05:30
Anand Doshi
6e4f5a214a Merge pull request #4033 from rmehta/portal-fixes
Portal Fixes
2015-09-23 16:05:12 +05:30
Anand Doshi
b07b0a9e54 [fix] Sales Order indicators for Maintenance 2015-09-23 15:52:11 +05:30
Rushabh Mehta
307978fea9 [test-fixes] 2015-09-23 15:43:09 +05:30
Anand Doshi
06ad308ca1 [fixes] setup wizard and other fixes related to cart 2015-09-23 12:50:35 +05:30
Anand Doshi
52dfc32eca [fix] validate_valuation_rate for Repack 2015-09-23 12:50:35 +05:30
Rushabh Mehta
72fbf902d7 [cleanup] added single price list for shopping cart, removed Applicable Territory 2015-09-23 12:50:34 +05:30
Rushabh Mehta
8ffd483e24 [wip] shopping cart shipping rule, price list cleanup 2015-09-23 12:49:16 +05:30
Rushabh Mehta
3d76686b82 [shopping-cart] cart via Jinja WIP 2015-09-23 12:49:16 +05:30
Rushabh Mehta
156ce607e2 [cleanup] [wip] portal, shopping cart cleanup 2015-09-23 12:49:16 +05:30
Anand Doshi
65b6762247 Merge pull request #4049 from bobzz-zone/patch-11
Update item_grid.html
2015-09-23 12:09:26 +05:30
Rushabh Mehta
abdfb4d3db [fixed] New button in POS Sales Invoice, reported via forum 2015-09-23 12:05:51 +05:30
bobzz-zone
3bcb13b1b8 Update item_grid.html
Change the colour of the indicator to be green when delivered qty is bigger than ordered qty
2015-09-23 11:42:06 +07:00
Anand Doshi
b6ec680c46 [fix] round percentages in get_indicator 2015-09-22 16:36:20 +05:30
Anand Doshi
53a0de7607 [fix] Recalculate rate after applying Pricing Rule 2015-09-22 15:26:53 +05:30
Anand Doshi
0a1d037f01 Merge pull request #3974 from rmehta/dms
[enhancement] document management system
2015-09-22 14:51:01 +05:30
Anand Doshi
ce3e15d30c Merge pull request #4042 from anandpdoshi/fix-payment-reconciliation-invoice
[fix] Show payment's invoice number as 'Invoice Type | Invoice Number' for better unsability
2015-09-22 14:21:23 +05:30
Nabin Hait
5b649521d1 [fix] Get outstanding invoices in Payment Reconciliation and Payment Tool 2015-09-22 13:06:34 +05:30
Anand Doshi
798e75832c [fix] Show payment's invoice number as 'Invoice Type | Invoice Number' for better unsability 2015-09-22 12:59:43 +05:30
Anand Doshi
aa95a1b1ef Merge pull request #4047 from nabinhait/payment_reco
[fix] Get outstanding invoices in Payment Reconciliation and Payment Tool
2015-09-22 12:55:27 +05:30
Nabin Hait
c0c94aef44 [fix] Get outstanding invoices in Payment Reconciliation and Payment Tool 2015-09-22 08:37:37 +05:30
Anand Doshi
e4dfeb651f [optimization] get balance on 2015-09-21 19:31:43 +05:30
Anand Doshi
f7e6eb4fd7 [optimization] get balance on 2015-09-21 19:30:21 +05:30
Rushabh Mehta
97c858a5e3 [fix] pull sales team from customer 2015-09-21 10:03:38 +05:30
Rushabh Mehta
974892bf87 Merge pull request #4036 from rmehta/no-party-currency
[fix] no default party currency
2015-09-18 13:40:42 +05:30
Rushabh Mehta
361df8993f [fix] no default party currency 2015-09-18 12:59:51 +05:30
Anand Doshi
b8b8de7a49 [hotfix] multicurrency gl entry 2015-09-17 21:07:45 +05:30
Anand Doshi
be6cfddc4d [hotfix] multicurrency gl entry 2015-09-17 21:07:04 +05:30
Anand Doshi
bf8c8df9ce Revert "[hotfix] set_balance_in_account_currency"
This reverts commit 9acd6a2629.
2015-09-17 20:55:50 +05:30
Anand Doshi
40759c284c Revert "[hotfix] set_balance_in_account_currency"
This reverts commit 9acd6a2629.
2015-09-17 20:51:41 +05:30
Nabin Hait
a48d754158 [fix] minor issue 2015-09-17 20:37:07 +05:30
Anand Doshi
26bcd89d10 [hotfix] journal entry - get balance 2015-09-17 20:36:44 +05:30
Anand Doshi
20fd360a63 [hotfix] journal entry - get balance 2015-09-17 20:36:19 +05:30
Anand Doshi
df8efce36f [hotfix] set_balance_in_account_currency 2015-09-17 20:29:57 +05:30
Anand Doshi
9acd6a2629 [hotfix] set_balance_in_account_currency 2015-09-17 20:29:31 +05:30
Anand Doshi
779ae439cd Merge pull request #4034 from nabinhait/mc5
[fix] minor issue
2015-09-17 20:10:19 +05:30
Nabin Hait
a8ef4c9220 [fix] minor issue 2015-09-17 19:38:11 +05:30
Anand Doshi
ce6b61b41e [fix] customer naming series validation and patch to fix missing default taxes and lead 2015-09-17 19:02:59 +05:30
Anand Doshi
0332f83bc2 [fix] customer naming series validation and patch to fix missing default taxes and lead 2015-09-17 19:01:48 +05:30
Nabin Hait
926ae17e5a Update journal_entry.py 2015-09-17 18:42:55 +05:30
Nabin Hait
8adb5f3e32 Update journal_entry.py 2015-09-17 18:40:35 +05:30
Anand Doshi
5532a14938 [hotfix] create customer contact and address from lead only on create 2015-09-17 18:27:56 +05:30
Anand Doshi
a69682c4e0 [hotfix] create customer contact and address from lead only on create 2015-09-17 18:27:36 +05:30
Anand Doshi
d20120e649 [fix] remove duplicate newsletter subscribers 2015-09-17 17:47:26 +05:30
Anand Doshi
e9b14e497b [fix] remove duplicate newsletter subscribers 2015-09-17 17:46:40 +05:30
Anand Doshi
ba0bf9e13d [hotfix] multicurrency patch 2015-09-17 17:36:20 +05:30
Anand Doshi
cba5a684cb [hotfix] multicurrency patch 2015-09-17 17:36:05 +05:30
Anand Doshi
c439b87ccc [hotfix] multicurrency patch 2015-09-17 17:28:58 +05:30
Anand Doshi
340709b2da [hotfix] multicurrency patch 2015-09-17 17:28:39 +05:30
Nabin Hait
a8de61e24b [fix] Multi currency patch 2015-09-17 17:16:58 +05:30
Anand Doshi
28386f551b Merge pull request #4032 from nabinhait/mc4
[fix] Multi currency patch
2015-09-17 17:16:08 +05:30
Nabin Hait
9ff52cd57e [fix] Multi currency patch 2015-09-17 17:07:26 +05:30
Rushabh Mehta
e3401182c8 Merge pull request #4012 from saurabh6790/cart
Taxation for Shopping Cart based on Tax Rule template
2015-09-17 16:26:35 +05:30
Anand Doshi
7523429ded Merge branch 'develop' 2015-09-17 15:35:09 +05:30
Anand Doshi
0401e418be bumped to version 6.2.0 2015-09-17 16:05:08 +06:00
Anand Doshi
ff6fd7fa9d [change-log] 2015-09-17 15:27:01 +05:30
Anand Doshi
7620393b9b Merge pull request #4031 from anandpdoshi/translations
Translations
2015-09-17 15:13:19 +05:30
Anand Doshi
6489e97726 [translations] 2015-09-17 15:08:09 +05:30
Anand Doshi
8c1a4c0c48 Merge branch 'nabinhait-mc3' into develop 2015-09-17 14:43:58 +05:30
Nabin Hait
77ca80217c [fix] Account Currency and Balance fixed in chart of accounts #4019 2015-09-17 14:43:33 +05:30
Nabin Hait
8247ad4028 [fix] Currency symbol fixed in Accounts receivable/payable report #4020 2015-09-17 14:40:15 +05:30
Anand Doshi
1194c6ef4b [fix] use the new split_emails method to split emails by comma 2015-09-17 14:40:15 +05:30
Anand Doshi
70fed64cd5 Merge pull request #4029 from nabinhait/mc2
[fix] Account Currency and Balance fixed in chart of accounts #4019
2015-09-17 14:37:00 +05:30
Nabin Hait
87c2d1d634 [fix] Dashboard info in accounting currency for Customer and Supplier #4017 2015-09-17 14:16:01 +05:30
Anand Doshi
d4aa27cbb8 Merge pull request #4025 from anandpdoshi/split-emails
use the new split_emails method to split emails by comma. Merge this after Communication CC is merged.
2015-09-17 14:00:03 +05:30
Nabin Hait
59f4fa9a8c [fix] Account Currency and Balance fixed in chart of accounts #4019 2015-09-17 13:57:42 +05:30
Anand Doshi
3b0c0a76b4 Merge pull request #4028 from nabinhait/mc1
[fix] Currency symbol fixed in Accounts receivable/payable report #4020
2015-09-17 13:38:32 +05:30
Nabin Hait
262ac09305 [fix] Currency symbol fixed in Accounts receivable/payable report #4020 2015-09-17 13:24:14 +05:30
Anand Doshi
2a04d98c16 Merge pull request #4023 from saurabh6790/error_report
[Fixes] Check Warehouse for Company Delete and Variant Attribute Value
2015-09-17 13:20:05 +05:30
Anand Doshi
fec9f75707 Merge pull request #4021 from anandpdoshi/payment-tool-ignore-journal-entry
ignore journal entry based invoice in payment tool and payment reconciliation tool
2015-09-17 13:19:06 +05:30
Anand Doshi
12106725fb [fix] set Payment Amount in Payment Tool 2015-09-17 12:59:44 +05:30
Anand Doshi
c5f919eb68 [fix] use the new split_emails method to split emails by comma 2015-09-16 19:20:55 +05:30
Saurabh
5fe0086d9a [Fixes] Warehouse check while company delete & Variant Attribute value check 2015-09-16 16:59:24 +05:30
Anand Doshi
0b031cdd6c [fix] ignore journal entry based invoice in payment tool and payment reconciliation tool 2015-09-16 12:46:54 +05:30
Anand Doshi
c19afa140d [fix] reload time log in update projects patch 2015-09-16 12:44:22 +05:30
Saurabh
0bd145a608 [fixes] test cases and tax rule validation 2015-09-15 17:06:52 +05:30
Saurabh
c663f5c2bd [Test] test state based taxasion 2015-09-15 15:41:01 +05:30
Saurabh
adde1cff48 [Enhancement] add state ccriteria in tax rule 2015-09-15 15:41:01 +05:30
Saurabh
bb9427d1ef [Fixes] deprecated 2015-09-15 15:41:01 +05:30
Saurabh
052babc6b6 [Fixes] [Minor] removed console statement 2015-09-15 15:41:01 +05:30
Saurabh
24fa06bc53 [Fixes] rebase conflict fixes 2015-09-15 15:41:01 +05:30
Saurabh
7c867ae9ad [Fixes] Tax Rule preparation and test taxes creation, remove dummy quotation 2015-09-15 15:41:01 +05:30
Saurabh
8f7317175d [enhancement] bring taxes from tax rules and test cases 2015-09-15 15:41:01 +05:30
Saurabh
957e7a37be [Shopping Cart][Fixes] tax calculation based on tax rule 2015-09-15 15:41:01 +05:30
Saurabh
def71d4d5d [Shopping Cart][Fixes] tax calculation based on tax rule 2015-09-15 15:41:00 +05:30
Neil Trini Lasrado
ef511b160e Added Tax Rule under ERPNext > Accounts > Setup 2015-09-15 15:41:00 +05:30
Neil Trini Lasrado
09f9c96c53 Fixes in Tax Rule 2015-09-15 15:41:00 +05:30
Neil Trini Lasrado
1a2d121073 Removed default taxes and charges from customer and supplier master. Added patch to create tax rules against customer/supplier 2015-09-15 15:41:00 +05:30
Neil Trini Lasrado
810bd35609 Apply Tax Rule based on Customer Selection in Sales / Purchase Transactions 2015-09-15 15:41:00 +05:30
Neil Trini Lasrado
949d7dbaba Added validations, test-cases to Tax Rule 2015-09-15 15:41:00 +05:30
Neil Trini Lasrado
72e6aa160c Added New Doctype Tax Rule 2015-09-15 15:41:00 +05:30
Anand Doshi
0870b185de Merge pull request #4015 from anandpdoshi/use-add-fetch
Use add_fetch in Product Bundle, Maintenance Visit and Maintenance Schedule instead of redundant get_item_details method
2015-09-15 14:02:06 +05:30
Anand Doshi
69f2cc8d24 [fix] don't fetch template item in get items of stock reconciliation 2015-09-15 11:54:13 +05:30
Anand Doshi
86102064a5 [fix] Use add_fetch in Product Bundle, Maintenance Visit and Maintenance Schedule instead of redundant get_item_details method 2015-09-15 11:50:30 +05:30
Anand Doshi
0fd3347148 [fix] handle invalid date in get_retirement_date 2015-09-15 11:29:51 +05:30
Anand Doshi
b74265c842 [minor] [patch] reload activity cost in default_activity_rate 2015-09-15 11:27:19 +05:30
Anand Doshi
1fef2fad2d [minor] fixes in queries and payment tool 2015-09-14 18:27:21 +05:30
Anand Doshi
9345240ff1 Merge pull request #4007 from anandpdoshi/setup-wizard-duplicate-entry
catch DuplicateEntryError in install_fixtures step of Setup Wizard
2015-09-14 16:33:16 +05:30
Anand Doshi
9ab23231e6 Merge pull request #4011 from anandpdoshi/defaults-during-creation
[fix] Use User Permission value as default only if the document type is Setup. Merge with frappe/frappe#1304
2015-09-14 16:33:00 +05:30
Anand Doshi
dbe623b167 [fix] Use User Permission value as default only if the document type is Setup 2015-09-14 13:08:53 +05:30
Anand Doshi
71e51c179c [fix] changed modified for multi-currency sync 2015-09-11 16:55:11 +05:30
Anand Doshi
4b8dbbdf98 [minor] Change modified of Sales Invoice.json 2015-09-11 16:39:03 +05:30
Anand Doshi
aaf1895a12 Merge branch 'nabinhait-multi-currency' into develop
Conflicts:
	erpnext/accounts/doctype/journal_entry/journal_entry.json
2015-09-11 16:35:17 +05:30
Anand Doshi
979326b0b1 minor fixes in multi-currency 2015-09-11 16:33:45 +05:30
Anand Doshi
5d9cfc76cd Don't copy title field in Duplicate 2015-09-11 15:35:26 +05:30
Nabin Hait
8db7bd2d8f [fix] Igonre permissions while saving Company and Warehouse from Accounts Settings 2015-09-11 15:34:42 +05:30
Rushabh Mehta
d461d462aa [minor] rename notified_modifed > notified_update. Merge with #1296 2015-09-11 15:34:41 +05:30
Anand Doshi
e67fa424b2 [fix] catch DuplicateEntryError in install_fixtures step of Setup Wizard 2015-09-11 15:32:06 +05:30
Anand Doshi
121176f0e9 Merge pull request #4002 from rmehta/notify-update-rename
[minor] rename notified_modifed > notified_update. Merge with #1296
2015-09-11 15:11:57 +05:30
Anand Doshi
1c2636e7b3 Merge pull request #4003 from nabinhait/fix8
[fix] Igonre permissions while saving Company and Warehouse from Accounts Settings
2015-09-11 15:00:13 +05:30
Anand Doshi
840cad0ff7 Don't copy title field in Duplicate 2015-09-11 12:26:57 +05:30
Nabin Hait
f76d63b92a Merge branch 'develop' of https://github.com/frappe/erpnext into multi-currency
[fix][patch] delete item variant attributes if no variants exists against that item
2015-09-10 19:26:02 +05:30
Nabin Hait
71ef6675ce [fix] Journal Entry client side minor fixes 2015-09-10 19:25:50 +05:30
Anand Doshi
793b87948c [fix] [patch] item template attributes 2015-09-10 18:10:15 +05:30
Anand Doshi
a1b0ff09a8 Merge pull request #4004 from nabinhait/fix9
[fix] Bypass Root Not Editable validation from Setup Wizard
2015-09-10 15:56:45 +05:30
Nabin Hait
5a803d76ad [fix] Bypass Root Not Editable validation from Setup Wizard 2015-09-10 13:20:35 +05:30
Nabin Hait
9513025130 [fix] Igonre permissions while saving Company and Warehouse from Accounts Settings 2015-09-10 12:13:27 +05:30
Rushabh Mehta
a13c60bf9f [minor] fix title in readme 2015-09-10 10:43:43 +05:30
Rushabh Mehta
bc23e5ac5c [minor] rename notified_modifed > notified_update. Merge with #1296 2015-09-10 10:30:41 +05:30
Nabin Hait
2377cdfa4e Fixed conflict 2015-09-09 18:45:41 +05:30
Nabin Hait
e3ae05aabd Multi-currency: Exchange Rate in Journal Entry 2015-09-09 18:43:12 +05:30
Anand Doshi
a2fda1c779 Merge branch 'develop' 2015-09-09 18:42:49 +05:30
Anand Doshi
a29577ca71 bumped to version 6.1.1 2015-09-09 19:12:48 +06:00
Anand Doshi
b1454cdf9f [fix] fix planned qty 2015-09-09 18:00:09 +05:30
Anand Doshi
f668a4d03c Merge branch 'develop' 2015-09-09 15:32:03 +05:30
Anand Doshi
c2b4ae6667 bumped to version 6.1.0 2015-09-09 16:02:03 +06:00
Anand Doshi
1857bb8b0b [change-log] 2015-09-09 15:23:16 +05:30
Anand Doshi
888a70c2d4 [fix] Setup Wizard - the case of forgetting mute emails 2015-09-09 15:14:54 +05:30
Anand Doshi
9d5b84e78a Merge pull request #3996 from nabinhait/fix6
Check credit limit in Delivery Note / Sales Invoice, only if not created against Sales Order
2015-09-09 15:11:12 +05:30
Anand Doshi
bdc125ad0f Merge pull request #3999 from nabinhait/planned_qty
Planned Qty logic fixed and reposted for existing
2015-09-09 13:14:09 +05:30
Nabin Hait
f0b3014a23 Planned Qty logic fixed and reposted for existing 2015-09-09 11:04:10 +05:30
Nabin Hait
0b17a91aba Merge pull request #3998 from nabinhait/fix7
[fix] Gross Profit report should not include return entries
2015-09-09 10:37:30 +05:30
Nabin Hait
83dd3e14e5 [fix] Gross Profit report should not include return entries 2015-09-09 10:37:04 +05:30
Anand Doshi
38f64ea3ab Merge pull request #3991 from anandpdoshi/apply-user-permissions-fix
[minor] Unset Apply User Permissions in default permissions
2015-09-08 22:09:44 +05:30
Nabin Hait
cd103c05d6 Check credit limit in Delivery Note / Sales Invoice, only if not created against Sales Order 2015-09-08 19:24:35 +05:30
Anand Doshi
bc1e8b4408 [minor] Unset Apply User Permissions in default permissions 2015-09-08 19:23:23 +05:30
Nabin Hait
6c3ff3e2ed Fixed conflict 2015-09-08 18:09:03 +05:30
Anand Doshi
4fb7f881c8 Merge branch 'nabinhait-fix4' into develop 2015-09-08 16:37:52 +05:30
Nabin Hait
83a358afc1 Fix invoice outstanding where party missing
Conflicts:
	erpnext/accounts/doctype/gl_entry/gl_entry.py
	erpnext/patches.txt
2015-09-08 16:37:26 +05:30
Nabin Hait
96bb070781 Payment against invoices where party not specified
Conflicts:
	erpnext/accounts/doctype/gl_entry/gl_entry.py
2015-09-08 16:36:44 +05:30
Nabin Hait
d387dd3bc6 [fix] Project: round percent_complete to 2 decimals 2015-09-08 16:15:00 +05:30
Anand Doshi
9f25575e0c Merge pull request #3988 from nabinhait/fix2
Set expense account in stock reco only if company available
2015-09-08 15:59:18 +05:30
Anand Doshi
e1af7f1a0f [minor] default mins_between_operations = 10 2015-09-08 15:55:09 +05:30
Anand Doshi
c35df5ce80 Merge branch 'rmehta-stock-settings' into develop
Conflicts:
	erpnext/patches.txt
2015-09-08 15:54:17 +05:30
Anand Doshi
2c2868db11 [translations] 2015-09-08 15:53:54 +05:30
Anand Doshi
17f28c13ed [fix] Customer match condition in autosuggest 2015-09-08 15:53:54 +05:30
Anand Doshi
3f1f2dd307 [fix] setup wizard - industry type 2015-09-08 15:53:53 +05:30
Anand Doshi
ab05dcd9f9 [fix] [patch] reload item attribute + value 2015-09-08 15:53:53 +05:30
Rushabh Mehta
f21edba97f [fixes] to task test case and fix tree ui for mobile 2015-09-08 15:53:53 +05:30
Rushabh Mehta
91b0e2348c [minor] task not mandatory for Time Log and Expense Claim #3904 2015-09-08 15:53:53 +05:30
Nabin Hait
4de91887db [patch] Re-run the 'default_title' patch 2015-09-08 15:53:35 +05:30
Anand Doshi
9b1288605f bumped to version 6.0.1 2015-09-08 15:53:05 +05:30
Anand Doshi
43888546f6 Merge pull request #3968 from rmehta/task-not-mandatory
[minor] task not mandatory for Time Log and Expense Claim #3904
2015-09-08 15:47:11 +05:30
Anand Doshi
68d2e317d2 Merge pull request #3992 from anandpdoshi/translations
[translations]
2015-09-08 15:45:08 +05:30
Nabin Hait
2249eea59f Fixed conflict 2015-09-08 12:55:21 +05:30
Nabin Hait
d608363d7b [fix] Map amount in advance payment entry agaionst SO/PO based on party account currency 2015-09-08 12:53:59 +05:30
Nabin Hait
b2739cbb79 Get average exchange rate in case of bank transfer 2015-09-08 12:18:45 +05:30
Anand Doshi
f5940907ae [translations] 2015-09-07 18:47:04 +05:30
Anand Doshi
90162078d5 Merge pull request #3989 from anandpdoshi/customer-query-match-condition
Customer match condition in autosuggest
2015-09-07 14:52:49 +05:30
Anand Doshi
cba99743a4 [fix] Customer match condition in autosuggest 2015-09-07 14:51:30 +05:30
Nabin Hait
f44128f81c Set expense account in stock reco only if company available 2015-09-07 10:45:15 +05:30
Nabin Hait
3607737b5e Merge branch 'develop' of https://github.com/frappe/erpnext into multi-currency 2015-09-07 10:41:01 +05:30
Nabin Hait
bf9691ed39 Multiple fixes 2015-09-07 10:40:48 +05:30
Anand Doshi
bbca95fd1b [fix] setup wizard - industry type 2015-09-07 10:11:46 +05:30
Anand Doshi
4df98d52c1 [fix] setup wizard - industry type 2015-09-07 10:11:23 +05:30
Anand Doshi
28f8664b93 [fix] [patch] reload item attribute + value 2015-09-04 16:41:27 +05:30
Nabin Hait
216aaaf1d6 Set currency and exchange rate based on Customer's currency while making Quotation from Opportunity 2015-09-04 16:41:04 +05:30
Rushabh Mehta
be9edae961 [fixes] to task test case and fix tree ui for mobile 2015-09-04 16:18:17 +05:30
Rushabh Mehta
4223d7c044 [minor] task not mandatory for Time Log and Expense Claim #3904 2015-09-04 16:18:17 +05:30
Nabin Hait
3a53d49b7e [fix] Set account and party balance in Journal Entry 2015-09-04 15:46:47 +05:30
Nabin Hait
4d62d7867e [report] Bank Reconciliation Statement in account currency 2015-09-04 13:26:45 +05:30
Nabin Hait
c0e3b1a0c7 Removed account and party balance in company currency from Journal Entry 2015-09-04 13:26:23 +05:30
Nabin Hait
34fe81f7eb Patch fixed 2015-09-03 19:18:00 +05:30
Nabin Hait
11a746d363 Fixed conflict 2015-09-03 16:04:11 +05:30
Nabin Hait
1609933748 Test cases for multi currency 2015-09-03 16:03:07 +05:30
Anand Doshi
5f90f7096d Merge pull request #3976 from nabinhait/fixes1
[patch] Re-run the 'default_title' patch
2015-09-03 11:15:53 +05:30
Nabin Hait
59c4ae5a46 [patch] Re-run the 'default_title' patch 2015-09-03 10:46:48 +05:30
Rushabh Mehta
85abdc4fad [minor] added stock settings for serial no. #3967. Merge with https://github.com/frappe/frappe/pull/1291 2015-09-03 10:29:38 +05:30
Nabin Hait
98096771c2 [multi currency] Introduced Accounting Currency in Customer and Supplier, and validation according to that 2015-09-03 10:28:08 +05:30
Rushabh Mehta
deb38f7a68 [enhancement] document management system 2015-09-02 18:34:24 +05:30
Nabin Hait
bac9b8eef6 Payment Tool changes due to multi currency 2015-08-31 19:01:11 +05:30
Nabin Hait
76bb927337 Fixed conflict 2015-08-31 17:28:05 +05:30
Nabin Hait
699751c531 [report] Accounts Receivable / Payable in multi currency 2015-08-31 17:02:04 +05:30
Nabin Hait
06b15bfd63 [report] General Ledger in multi currency 2015-08-31 15:39:03 +05:30
Nabin Hait
c68f68d6cc [fix] Patch fixed for account_currency field rename 2015-08-28 19:26:28 +05:30
Nabin Hait
6e439a5e53 Multi currency: test case and fixes 2015-08-28 19:24:22 +05:30
Nabin Hait
78be566428 minor fix 2015-08-27 16:27:44 +05:30
Nabin Hait
f6f38f2f2d [new fields] Exchange Rate in Journal Entry and some properties changes in Journal Entry Account 2015-08-27 16:27:33 +05:30
Nabin Hait
9a9a4293b3 [new fields] Party Account Currency, Paid Amount and Write Off Amount in Acompany Currency 2015-08-27 16:26:38 +05:30
Nabin Hait
09c68c9ad0 Merge branch 'develop' of https://github.com/frappe/erpnext into multi-currency 2015-08-27 14:55:55 +05:30
Nabin Hait
13d8835b75 Fixed merge conflict 2015-08-27 14:55:34 +05:30
Nabin Hait
2091f0c97e Checked out json files from upstream 2015-08-27 14:35:17 +05:30
Nabin Hait
54fe26dcfb [patch] Multi currency 2015-08-27 12:55:24 +05:30
Nabin Hait
4ffd7f3d05 Outstanding, paid and write-off amount in Invoice 2015-08-27 12:28:36 +05:30
Nabin Hait
a12d959729 GL Entries for Sales/Purchase Invoice in multi currency 2015-08-27 12:27:24 +05:30
Nabin Hait
895029dc15 Validate currency in transactions 2015-08-21 14:31:47 +05:30
Nabin Hait
c561a499d7 Multi currency GL entry fixes 2015-08-21 14:31:47 +05:30
Nabin Hait
69c1401764 Multi currency patch initialized 2015-08-21 14:31:47 +05:30
Nabin Hait
50ba6faaf6 Currency input from Chart of Accounts page 2015-08-21 14:31:47 +05:30
Nabin Hait
46bcbaf97b GL Entry in account currency 2015-08-21 14:31:47 +05:30
Nabin Hait
0e46a9b86e Journal Entry: client side triggers 2015-08-21 14:31:46 +05:30
Nabin Hait
e459cb4d09 Journal Entry: validate and post gle in multi currency 2015-08-21 14:31:46 +05:30
Nabin Hait
7dbd395781 Toggle alternative currency fields 2015-08-21 14:31:46 +05:30
Nabin Hait
6aea1c0da5 Currency validation in gl entry 2015-08-21 14:31:46 +05:30
Rushabh Mehta
aeab6c559c [enhancement] multi-currency, added fields #1002 2015-08-21 14:31:46 +05:30
348 changed files with 14860 additions and 10314 deletions

View File

@@ -1,6 +1,6 @@
# ERPNext - Open source ERP for small and medium-size business [![Build Status](https://travis-ci.org/frappe/erpnext.png)](https://travis-ci.org/frappe/erpnext) # ERPNext - ERP made simple
[![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/frappe/erpnext?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Build Status](https://travis-ci.org/frappe/erpnext.png)](https://travis-ci.org/frappe/erpnext) [![Gitter](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/frappe/erpnext?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
[https://erpnext.com](https://erpnext.com) [https://erpnext.com](https://erpnext.com)

View File

@@ -1,2 +1,2 @@
from __future__ import unicode_literals from __future__ import unicode_literals
__version__ = '6.0.1' __version__ = '6.4.3'

File diff suppressed because it is too large Load Diff

View File

@@ -7,6 +7,8 @@ from frappe.utils import cstr, cint
from frappe import throw, _ from frappe import throw, _
from frappe.model.document import Document from frappe.model.document import Document
class RootNotEditable(frappe.ValidationError): pass
class Account(Document): class Account(Document):
nsm_parent_field = 'parent_account' nsm_parent_field = 'parent_account'
@@ -28,6 +30,7 @@ class Account(Document):
self.validate_warehouse_account() self.validate_warehouse_account()
self.validate_frozen_accounts_modifier() self.validate_frozen_accounts_modifier()
self.validate_balance_must_be_debit_or_credit() self.validate_balance_must_be_debit_or_credit()
self.validate_account_currency()
def validate_parent(self): def validate_parent(self):
"""Fetch Parent Details and validate parent account""" """Fetch Parent Details and validate parent account"""
@@ -47,27 +50,27 @@ class Account(Document):
def set_root_and_report_type(self): def set_root_and_report_type(self):
if self.parent_account: if self.parent_account:
par = frappe.db.get_value("Account", self.parent_account, ["report_type", "root_type"], as_dict=1) par = frappe.db.get_value("Account", self.parent_account, ["report_type", "root_type"], as_dict=1)
if par.report_type: if par.report_type:
self.report_type = par.report_type self.report_type = par.report_type
if par.root_type: if par.root_type:
self.root_type = par.root_type self.root_type = par.root_type
if self.is_group: if self.is_group:
db_value = frappe.db.get_value("Account", self.name, ["report_type", "root_type"], as_dict=1) db_value = frappe.db.get_value("Account", self.name, ["report_type", "root_type"], as_dict=1)
if db_value: if db_value:
if self.report_type != db_value.report_type: if self.report_type != db_value.report_type:
frappe.db.sql("update `tabAccount` set report_type=%s where lft > %s and rgt < %s", frappe.db.sql("update `tabAccount` set report_type=%s where lft > %s and rgt < %s",
(self.report_type, self.lft, self.rgt)) (self.report_type, self.lft, self.rgt))
if self.root_type != db_value.root_type: if self.root_type != db_value.root_type:
frappe.db.sql("update `tabAccount` set root_type=%s where lft > %s and rgt < %s", frappe.db.sql("update `tabAccount` set root_type=%s where lft > %s and rgt < %s",
(self.root_type, self.lft, self.rgt)) (self.root_type, self.lft, self.rgt))
def validate_root_details(self): def validate_root_details(self):
# does not exists parent # does not exists parent
if frappe.db.exists("Account", self.name): if frappe.db.exists("Account", self.name):
if not frappe.db.get_value("Account", self.name, "parent_account"): if not frappe.db.get_value("Account", self.name, "parent_account"):
throw(_("Root cannot be edited.")) throw(_("Root cannot be edited."), RootNotEditable)
def validate_frozen_accounts_modifier(self): def validate_frozen_accounts_modifier(self):
old_value = frappe.db.get_value("Account", self.name, "freeze_account") old_value = frappe.db.get_value("Account", self.name, "freeze_account")
@@ -87,6 +90,14 @@ class Account(Document):
elif account_balance < 0 and self.balance_must_be == "Debit": elif account_balance < 0 and self.balance_must_be == "Debit":
frappe.throw(_("Account balance already in Credit, you are not allowed to set 'Balance Must Be' as 'Debit'")) frappe.throw(_("Account balance already in Credit, you are not allowed to set 'Balance Must Be' as 'Debit'"))
def validate_account_currency(self):
if not self.account_currency:
self.account_currency = frappe.db.get_value("Company", self.company, "default_currency")
elif self.account_currency != frappe.db.get_value("Account", self.name, "account_currency"):
if frappe.db.get_value("GL Entry", {"account": self.name}):
frappe.throw(_("Currency can not be changed after making entries using some other currency"))
def convert_group_to_ledger(self): def convert_group_to_ledger(self):
if self.check_if_child_exists(): if self.check_if_child_exists():
throw(_("Account with child nodes cannot be converted to ledger")) throw(_("Account with child nodes cannot be converted to ledger"))
@@ -196,3 +207,14 @@ def get_parent_account(doctype, txt, searchfield, start, page_len, filters):
and %s like %s order by name limit %s, %s""" % and %s like %s order by name limit %s, %s""" %
("%s", searchfield, "%s", "%s", "%s"), ("%s", searchfield, "%s", "%s", "%s"),
(filters["company"], "%%%s%%" % txt, start, page_len), as_list=1) (filters["company"], "%%%s%%" % txt, start, page_len), as_list=1)
def get_account_currency(account):
"""Helper function to get account currency"""
def generator():
account_currency, company = frappe.db.get_value("Account", account, ["account_currency", "company"])
if not account_currency:
account_currency = frappe.db.get_value("Company", company, "default_currency")
return account_currency
return frappe.local_cache("account_currency", account, generator)

View File

@@ -36,7 +36,8 @@ def create_charts(chart_name, company):
"is_group": is_group, "is_group": is_group,
"root_type": root_type, "root_type": root_type,
"report_type": report_type, "report_type": report_type,
"account_type": child.get("account_type") "account_type": child.get("account_type"),
"account_currency": frappe.db.get_value("Company", company, "default_currency")
}) })
if root_account: if root_account:

View File

@@ -0,0 +1,90 @@
{
"country_code": "gt",
"name": "Cuentas de Guatemala",
"is_active": "Yes",
"tree": {
"Activos": {
"Activo Corriente": {
"Caja y Bancos": {},
"Cuentas por Cobrar": {},
"Impuestos por Cobrar": {
"IVA por Cobrar": {},
"Retenciones de IVA recibidas": {}
},
"Inventario": {}
},
"No Corriente": {
"Activos Fijos": {},
"Cargos Diferidos": {}
},
"root_type": "Asset"
},
"Pasivos": {
"Pasivo Corriente": {
"Proveedores": {
"Inventario Recibido pero No Cobrado": {
"account_type": "Stock Received But Not Billed"
}
},
"Impuestos por Pagar": {},
"Sueldos por Liquidar": {},
"Prestaciones": {},
"Cuentas por Pagar": {},
"Otras Cuentas por Pagar": {},
"Acreedores": {}
},
"Pasivo No Corriente": {
"Provisión para Indemnizaciones": {},
"Acreedores": {}
},
"root_type": "Liability"
},
"Patrimonio": {
"Capital": {},
"Utilidades Retenidas": {},
"Resultados del Ejercicio": {},
"root_type": "Asset"
},
"Costos": {
"Costo de Ventas": {},
"Costos Incluidos en la Valuación": {
"account_type": "Expenses Included In Valuation"
},
"Stock Adjustment": {
"account_type": "Stock Adjustment"
},
"root_type": "Expense"
},
"Gastos": {
"Gastos de Personal": {},
"Honorarios Profesionales": {},
"Servicios Básicos": {},
"Alquileres": {},
"Seguros": {},
"Mantenimiento": {},
"Depreciaciones": {},
"Gastos Diversos": {},
"root_type": "Expense"
},
"Ingresos": {
"Productos": {},
"Servicios": {},
"root_type": "Income"
},
"Otros Gastos y Productos Financieros": {
"Otros Ingresos": {
"Otros Gastos y Productos Financieros": {
"Intereses": {},
"Otros Gastos Financieros": {}
}
},
"Otros Gastos": {
"Otros Gastos y Productos Financieros": {
"Intereses": {},
"Otros Gastos Financieros": {}
}
},
"root_type": "Expense"
}
}
}

View File

@@ -9,36 +9,39 @@ def _make_test_records(verbose):
accounts = [ accounts = [
# [account_name, parent_account, is_group] # [account_name, parent_account, is_group]
["_Test Account Bank Account", "Bank Accounts", 0, "Bank"], ["_Test Bank", "Bank Accounts", 0, "Bank", None],
["_Test Bank USD", "Bank Accounts", 0, "Bank", "USD"],
["_Test Bank EUR", "Bank Accounts", 0, "Bank", "EUR"],
["_Test Account Stock Expenses", "Direct Expenses", 1, None], ["_Test Account Stock Expenses", "Direct Expenses", 1, None, None],
["_Test Account Shipping Charges", "_Test Account Stock Expenses", 0, "Chargeable"], ["_Test Account Shipping Charges", "_Test Account Stock Expenses", 0, "Chargeable", None],
["_Test Account Customs Duty", "_Test Account Stock Expenses", 0, "Tax"], ["_Test Account Customs Duty", "_Test Account Stock Expenses", 0, "Tax", None],
["_Test Account Insurance Charges", "_Test Account Stock Expenses", 0, "Chargeable"], ["_Test Account Insurance Charges", "_Test Account Stock Expenses", 0, "Chargeable", None],
["_Test Account Stock Adjustment", "_Test Account Stock Expenses", 0, "Stock Adjustment"], ["_Test Account Stock Adjustment", "_Test Account Stock Expenses", 0, "Stock Adjustment", None],
["_Test Account Tax Assets", "Current Assets", 1, None, None],
["_Test Account VAT", "_Test Account Tax Assets", 0, "Tax", None],
["_Test Account Service Tax", "_Test Account Tax Assets", 0, "Tax", None],
["_Test Account Tax Assets", "Current Assets", 1, None], ["_Test Account Reserves and Surplus", "Current Liabilities", 0, None, None],
["_Test Account VAT", "_Test Account Tax Assets", 0, "Tax"],
["_Test Account Service Tax", "_Test Account Tax Assets", 0, "Tax"],
["_Test Account Reserves and Surplus", "Current Liabilities", 0, None], ["_Test Account Cost for Goods Sold", "Expenses", 0, None, None],
["_Test Account Excise Duty", "_Test Account Tax Assets", 0, "Tax", None],
["_Test Account Cost for Goods Sold", "Expenses", 0, None], ["_Test Account Education Cess", "_Test Account Tax Assets", 0, "Tax", None],
["_Test Account Excise Duty", "_Test Account Tax Assets", 0, "Tax"], ["_Test Account S&H Education Cess", "_Test Account Tax Assets", 0, "Tax", None],
["_Test Account Education Cess", "_Test Account Tax Assets", 0, "Tax"], ["_Test Account CST", "Direct Expenses", 0, "Tax", None],
["_Test Account S&H Education Cess", "_Test Account Tax Assets", 0, "Tax"], ["_Test Account Discount", "Direct Expenses", 0, None, None],
["_Test Account CST", "Direct Expenses", 0, "Tax"], ["_Test Write Off", "Indirect Expenses", 0, None, None],
["_Test Account Discount", "Direct Expenses", 0, None],
["_Test Write Off", "Indirect Expenses", 0, None],
# related to Account Inventory Integration # related to Account Inventory Integration
["_Test Account Stock In Hand", "Current Assets", 0, None], ["_Test Account Stock In Hand", "Current Assets", 0, None, None],
["_Test Account Fixed Assets", "Current Assets", 0, None], ["_Test Account Fixed Assets", "Current Assets", 0, None, None],
# Receivable / Payable Account # Receivable / Payable Account
["_Test Receivable", "Current Assets", 0, "Receivable"], ["_Test Receivable", "Current Assets", 0, "Receivable", None],
["_Test Payable", "Current Liabilities", 0, "Payable"], ["_Test Payable", "Current Liabilities", 0, "Payable", None],
["_Test Receivable USD", "Current Assets", 0, "Receivable", "USD"],
["_Test Payable USD", "Current Liabilities", 0, "Payable", "USD"]
] ]
for company, abbr in [["_Test Company", "_TC"], ["_Test Company 1", "_TC1"]]: for company, abbr in [["_Test Company", "_TC"], ["_Test Company 1", "_TC1"]]:
@@ -48,7 +51,8 @@ def _make_test_records(verbose):
"parent_account": parent_account + " - " + abbr, "parent_account": parent_account + " - " + abbr,
"company": company, "company": company,
"is_group": is_group, "is_group": is_group,
"account_type": account_type "account_type": account_type,
} for account_name, parent_account, is_group, account_type in accounts]) "account_currency": currency
} for account_name, parent_account, is_group, account_type, currency in accounts])
return test_objects return test_objects

View File

@@ -16,7 +16,9 @@ class AccountsSettings(Document):
if cint(self.auto_accounting_for_stock): if cint(self.auto_accounting_for_stock):
# set default perpetual account in company # set default perpetual account in company
for company in frappe.db.sql("select name from tabCompany"): for company in frappe.db.sql("select name from tabCompany"):
frappe.get_doc("Company", company[0]).save() company = frappe.get_doc("Company", company[0])
company.flags.ignore_permissions = True
company.save()
# Create account head for warehouses # Create account head for warehouses
warehouse_list = frappe.db.sql("select name, company from tabWarehouse", as_dict=1) warehouse_list = frappe.db.sql("select name, company from tabWarehouse", as_dict=1)
@@ -25,4 +27,5 @@ class AccountsSettings(Document):
frappe.throw(_("Company is missing in warehouses {0}").format(comma_and(warehouse_with_no_company))) frappe.throw(_("Company is missing in warehouses {0}").format(comma_and(warehouse_with_no_company)))
for wh in warehouse_list: for wh in warehouse_list:
wh_doc = frappe.get_doc("Warehouse", wh.name) wh_doc = frappe.get_doc("Warehouse", wh.name)
wh_doc.flags.ignore_permissions = True
wh_doc.save() wh_doc.save()

View File

@@ -344,7 +344,7 @@
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"max_attachments": 3, "max_attachments": 3,
"modified": "2015-02-05 05:11:35.427357", "modified": "2015-09-07 15:51:26",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "C-Form", "name": "C-Form",
@@ -352,7 +352,7 @@
"permissions": [ "permissions": [
{ {
"amend": 0, "amend": 0,
"apply_user_permissions": 1, "apply_user_permissions": 0,
"cancel": 0, "cancel": 0,
"create": 1, "create": 1,
"delete": 0, "delete": 0,

View File

@@ -8,7 +8,7 @@
"description": "Track separate Income and Expense for product verticals or divisions.", "description": "Track separate Income and Expense for product verticals or divisions.",
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master", "document_type": "Setup",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@@ -298,7 +298,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-07-13 05:28:25.504801", "modified": "2015-09-14 02:55:55.020690",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Cost Center", "name": "Cost Center",
@@ -326,7 +326,7 @@
}, },
{ {
"amend": 0, "amend": 0,
"apply_user_permissions": 1, "apply_user_permissions": 0,
"cancel": 0, "cancel": 0,
"create": 0, "create": 0,
"delete": 0, "delete": 0,
@@ -346,7 +346,7 @@
}, },
{ {
"amend": 0, "amend": 0,
"apply_user_permissions": 1, "apply_user_permissions": 0,
"cancel": 0, "cancel": 0,
"create": 0, "create": 0,
"delete": 0, "delete": 0,
@@ -366,7 +366,7 @@
}, },
{ {
"amend": 0, "amend": 0,
"apply_user_permissions": 1, "apply_user_permissions": 0,
"cancel": 0, "cancel": 0,
"create": 0, "create": 0,
"delete": 0, "delete": 0,
@@ -386,7 +386,7 @@
}, },
{ {
"amend": 0, "amend": 0,
"apply_user_permissions": 1, "apply_user_permissions": 0,
"cancel": 0, "cancel": 0,
"create": 0, "create": 0,
"delete": 0, "delete": 0,

View File

@@ -8,7 +8,7 @@
"description": "**Fiscal Year** represents a Financial Year. All accounting entries and other major transactions are tracked against **Fiscal Year**.", "description": "**Fiscal Year** represents a Financial Year. All accounting entries and other major transactions are tracked against **Fiscal Year**.",
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master", "document_type": "Setup",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@@ -133,7 +133,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-07-13 05:28:27.745408", "modified": "2015-09-14 02:55:56.280252",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Fiscal Year", "name": "Fiscal Year",
@@ -161,7 +161,7 @@
}, },
{ {
"amend": 0, "amend": 0,
"apply_user_permissions": 1, "apply_user_permissions": 0,
"cancel": 0, "cancel": 0,
"create": 0, "create": 0,
"delete": 0, "delete": 0,

View File

@@ -1,32 +1,38 @@
[ [
{ {
"doctype": "Fiscal Year", "doctype": "Fiscal Year",
"year": "_Test Fiscal Year 2012", "year": "_Test Fiscal Year 2012",
"year_end_date": "2012-12-31", "year_end_date": "2012-12-31",
"year_start_date": "2012-01-01" "year_start_date": "2012-01-01"
}, },
{ {
"doctype": "Fiscal Year", "doctype": "Fiscal Year",
"year": "_Test Fiscal Year 2013", "year": "_Test Fiscal Year 2013",
"year_end_date": "2013-12-31", "year_end_date": "2013-12-31",
"year_start_date": "2013-01-01" "year_start_date": "2013-01-01"
}, },
{ {
"doctype": "Fiscal Year", "doctype": "Fiscal Year",
"year": "_Test Fiscal Year 2014", "year": "_Test Fiscal Year 2014",
"year_end_date": "2014-12-31", "year_end_date": "2014-12-31",
"year_start_date": "2014-01-01" "year_start_date": "2014-01-01"
}, },
{ {
"doctype": "Fiscal Year", "doctype": "Fiscal Year",
"year": "_Test Fiscal Year 2015", "year": "_Test Fiscal Year 2015",
"year_end_date": "2015-12-31", "year_end_date": "2015-12-31",
"year_start_date": "2015-01-01" "year_start_date": "2015-01-01"
}, },
{ {
"doctype": "Fiscal Year", "doctype": "Fiscal Year",
"year": "_Test Fiscal Year 2016", "year": "_Test Fiscal Year 2016",
"year_end_date": "2016-12-31", "year_end_date": "2016-12-31",
"year_start_date": "2016-01-01" "year_start_date": "2016-01-01"
},
{
"doctype": "Fiscal Year",
"year": "_Test Fiscal Year 2017",
"year_end_date": "2017-12-31",
"year_start_date": "2017-01-01"
} }
] ]

View File

@@ -6,7 +6,7 @@
"custom": 0, "custom": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master", "document_type": "Setup",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@@ -39,7 +39,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 1,
"modified": "2014-10-02 13:35:44.155278", "modified": "2015-09-14 02:55:56.368682",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Fiscal Year Company", "name": "Fiscal Year Company",

File diff suppressed because it is too large Load Diff

View File

@@ -3,13 +3,13 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import flt, fmt_money, getdate, formatdate, cstr
from frappe import _ from frappe import _
from frappe.utils import flt, fmt_money, getdate, formatdate
from frappe.model.document import Document from frappe.model.document import Document
from erpnext.accounts.party import validate_party_gle_currency, get_party_account_currency
class CustomerFrozen(frappe.ValidationError): pass from erpnext.accounts.utils import get_account_currency
from erpnext.setup.doctype.company.company import get_company_currency
from erpnext.exceptions import InvalidAccountCurrency, CustomerFrozen
class GLEntry(Document): class GLEntry(Document):
def validate(self): def validate(self):
@@ -20,6 +20,7 @@ class GLEntry(Document):
self.check_pl_account() self.check_pl_account()
self.validate_cost_center() self.validate_cost_center()
self.validate_party() self.validate_party()
self.validate_currency()
def on_update_with_args(self, adv_adj, update_outstanding = 'Yes'): def on_update_with_args(self, adv_adj, update_outstanding = 'Yes'):
self.validate_account_details(adv_adj) self.validate_account_details(adv_adj)
@@ -91,7 +92,7 @@ class GLEntry(Document):
if self.cost_center and _get_cost_center_company() != self.company: 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)) frappe.throw(_("Cost Center {0} does not belong to Company {1}").format(self.cost_center, self.company))
def validate_party(self): def validate_party(self):
if self.party_type and self.party: if self.party_type and self.party:
frozen_accounts_modifier = frappe.db.get_value( 'Accounts Settings', None,'frozen_accounts_modifier') frozen_accounts_modifier = frappe.db.get_value( 'Accounts Settings', None,'frozen_accounts_modifier')
@@ -99,6 +100,26 @@ class GLEntry(Document):
if frappe.db.get_value(self.party_type, self.party, "is_frozen"): 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) frappe.throw("{0} {1} is frozen".format(self.party_type, self.party), CustomerFrozen)
def validate_currency(self):
company_currency = get_company_currency(self.company)
account_currency = get_account_currency(self.account)
if not self.account_currency:
self.account_currency = company_currency
if account_currency != self.account_currency:
frappe.throw(_("Accounting Entry for {0} can only be made in currency: {1}")
.format(self.account, (account_currency or company_currency)), InvalidAccountCurrency)
if self.party_type and self.party:
party_account_currency = get_party_account_currency(self.party_type, self.party, self.company)
if party_account_currency != self.account_currency:
frappe.throw(_("Accounting Entry for {0}: {1} can only be made in currency: {2}")
.format(self.party_type, self.party, party_account_currency), InvalidAccountCurrency)
validate_party_gle_currency(self.party_type, self.party, self.company)
def validate_balance_type(account, adv_adj=False): def validate_balance_type(account, adv_adj=False):
if not adv_adj and account: if not adv_adj and account:
balance_must_be = frappe.db.get_value("Account", account, "balance_must_be") balance_must_be = frappe.db.get_value("Account", account, "balance_must_be")
@@ -124,22 +145,28 @@ def check_freezing_date(posting_date, adv_adj=False):
frappe.throw(_("You are not authorized to add or update entries before {0}").format(formatdate(acc_frozen_upto))) frappe.throw(_("You are not authorized to add or update entries before {0}").format(formatdate(acc_frozen_upto)))
def update_outstanding_amt(account, party_type, party, against_voucher_type, against_voucher, on_cancel=False): def update_outstanding_amt(account, party_type, party, against_voucher_type, against_voucher, on_cancel=False):
if party_type and party:
party_condition = " and ifnull(party_type, '')='{0}' and ifnull(party, '')='{1}'"\
.format(frappe.db.escape(party_type), frappe.db.escape(party))
else:
party_condition = ""
# get final outstanding amt # get final outstanding amt
bal = flt(frappe.db.sql("""select sum(ifnull(debit, 0)) - sum(ifnull(credit, 0)) bal = flt(frappe.db.sql("""
select sum(ifnull(debit_in_account_currency, 0)) - sum(ifnull(credit_in_account_currency, 0))
from `tabGL Entry` from `tabGL Entry`
where against_voucher_type=%s and against_voucher=%s where against_voucher_type=%s and against_voucher=%s
and account = %s and ifnull(party_type, '')=%s and ifnull(party, '')=%s""", and account = %s {0}""".format(party_condition),
(against_voucher_type, against_voucher, account, party_type, party))[0][0] or 0.0) (against_voucher_type, against_voucher, account))[0][0] or 0.0)
if against_voucher_type == 'Purchase Invoice': if against_voucher_type == 'Purchase Invoice':
bal = -bal bal = -bal
elif against_voucher_type == "Journal Entry": elif against_voucher_type == "Journal Entry":
against_voucher_amount = flt(frappe.db.sql(""" against_voucher_amount = flt(frappe.db.sql("""
select sum(ifnull(debit, 0)) - sum(ifnull(credit, 0)) select sum(ifnull(debit_in_account_currency, 0)) - sum(ifnull(credit_in_account_currency, 0))
from `tabGL Entry` where voucher_type = 'Journal Entry' and voucher_no = %s from `tabGL Entry` where voucher_type = 'Journal Entry' and voucher_no = %s
and account = %s and ifnull(party_type, '')=%s and ifnull(party, '')=%s and account = %s and ifnull(against_voucher, '') = '' {0}"""
and ifnull(against_voucher, '') = ''""", .format(party_condition), (against_voucher, account))[0][0])
(against_voucher, account, cstr(party_type), cstr(party)))[0][0])
if not against_voucher_amount: if not against_voucher_amount:
frappe.throw(_("Against Journal Entry {0} is already adjusted against some other voucher") frappe.throw(_("Against Journal Entry {0} is already adjusted against some other voucher")

View File

@@ -11,7 +11,7 @@ class TestGLEntry(unittest.TestCase):
frappe.db.set_value("Company", "_Test Company", "round_off_cost_center", "_Test Cost Center - _TC") frappe.db.set_value("Company", "_Test Company", "round_off_cost_center", "_Test Cost Center - _TC")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC", jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Account Bank Account - _TC", 100, "_Test Cost Center - _TC", submit=False) "_Test Bank - _TC", 100, "_Test Cost Center - _TC", submit=False)
jv.get("accounts")[0].debit = 100.01 jv.get("accounts")[0].debit = 100.01
jv.flags.ignore_validate = True jv.flags.ignore_validate = True

View File

@@ -2,8 +2,54 @@
// License: GNU General Public License v3. See license.txt // License: GNU General Public License v3. See license.txt
frappe.provide("erpnext.accounts"); frappe.provide("erpnext.accounts");
frappe.provide("erpnext.journal_entry");
frappe.require("assets/erpnext/js/utils.js"); frappe.require("assets/erpnext/js/utils.js");
frappe.ui.form.on("Journal Entry", {
refresh: function(frm) {
erpnext.toggle_naming_series();
cur_frm.cscript.voucher_type(frm.doc);
if(frm.doc.docstatus==1) {
cur_frm.add_custom_button(__('View Ledger'), function() {
frappe.route_options = {
"voucher_no": frm.doc.name,
"from_date": frm.doc.posting_date,
"to_date": frm.doc.posting_date,
"company": frm.doc.company,
group_by_voucher: 0
};
frappe.set_route("query-report", "General Ledger");
}, "icon-table");
}
// hide /unhide fields based on currency
erpnext.journal_entry.toggle_fields_based_on_currency(frm);
},
multi_currency: function(frm) {
erpnext.journal_entry.toggle_fields_based_on_currency(frm);
}
})
erpnext.journal_entry.toggle_fields_based_on_currency = function(frm) {
var fields = ["currency_section", "account_currency", "exchange_rate", "debit", "credit"];
var grid = frm.get_field("accounts").grid;
if(grid) grid.set_column_disp(fields, frm.doc.multi_currency);
// dynamic label
var field_label_map = {
"debit_in_account_currency": "Debit",
"credit_in_account_currency": "Credit"
};
$.each(field_label_map, function (fieldname, label) {
var df = frappe.meta.get_docfield("Journal Entry Account", fieldname, frm.doc.name);
df.label = frm.doc.multi_currency ? (label + " in Account Currency") : label;
})
}
erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
onload: function() { onload: function() {
this.load_defaults(); this.load_defaults();
@@ -29,17 +75,27 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
setup_queries: function() { setup_queries: function() {
var me = this; var me = this;
$.each(["account", "cost_center"], function(i, fieldname) { me.frm.set_query("account", "accounts", function(doc, cdt, cdn) {
me.frm.set_query(fieldname, "accounts", function() { var filters = {
frappe.model.validate_missing(me.frm.doc, "company"); company: me.frm.doc.company,
return { is_group: 0
filters: { };
company: me.frm.doc.company, if(!doc.multi_currency) {
is_group: 0 $.extend(filters, {
} account_currency: frappe.get_doc(":Company", me.frm.doc.company).default_currency
}; });
}); }
return { filters: filters };
});
me.frm.set_query("cost_center", "accounts", function(doc, cdt, cdn) {
return {
filters: {
company: me.frm.doc.company,
is_group: 0
}
};
}); });
me.frm.set_query("party_type", "accounts", function(doc, cdt, cdn) { me.frm.set_query("party_type", "accounts", function(doc, cdt, cdn) {
@@ -59,7 +115,6 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
// journal entry // journal entry
if(jvd.reference_type==="Journal Entry") { if(jvd.reference_type==="Journal Entry") {
frappe.model.validate_missing(jvd, "account"); frappe.model.validate_missing(jvd, "account");
return { return {
query: "erpnext.accounts.doctype.journal_entry.journal_entry.get_against_jv", query: "erpnext.accounts.doctype.journal_entry.journal_entry.get_against_jv",
filters: { filters: {
@@ -69,23 +124,32 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
}; };
} }
// against party
frappe.model.validate_missing(jvd, "party_type");
frappe.model.validate_missing(jvd, "party");
var out = { var out = {
filters: [ filters: [
[jvd.reference_type, jvd.reference_type.indexOf("Sales")===0 ? "customer" : "supplier", "=", jvd.party], [jvd.reference_type, "docstatus", "=", 1]
[jvd.reference_type, "docstatus", "=", 1],
] ]
}; };
if(in_list(["Sales Invoice", "Purchase Invoice"], jvd.reference_type)) { if(in_list(["Sales Invoice", "Purchase Invoice"], jvd.reference_type)) {
out.filters.push([jvd.reference_type, "outstanding_amount", "!=", 0]); out.filters.push([jvd.reference_type, "outstanding_amount", "!=", 0]);
// account filter
frappe.model.validate_missing(jvd, "account");
party_account_field = jvd.reference_type==="Sales Invoice" ? "debit_to": "credit_to";
out.filters.push([jvd.reference_type, party_account_field, "=", jvd.account]);
} else { } else {
// party_type and party mandatory
frappe.model.validate_missing(jvd, "party_type");
frappe.model.validate_missing(jvd, "party");
out.filters.push([jvd.reference_type, "per_billed", "<", 100]); out.filters.push([jvd.reference_type, "per_billed", "<", 100]);
} }
if(jvd.party_type && jvd.party) {
out.filters.push([jvd.reference_type,
(jvd.reference_type.indexOf("Sales")===0 ? "customer" : "supplier"), "=", jvd.party]);
}
return out; return out;
}); });
@@ -110,32 +174,39 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
reference_name: function(doc, cdt, cdn) { reference_name: function(doc, cdt, cdn) {
var d = frappe.get_doc(cdt, cdn); var d = frappe.get_doc(cdt, cdn);
if (d.reference_type==="Purchase Invoice" && !flt(d.debit)) { if(d.reference_name) {
this.get_outstanding('Purchase Invoice', d.reference_name, d); if (d.reference_type==="Purchase Invoice" && !flt(d.debit)) {
} this.get_outstanding('Purchase Invoice', d.reference_name, doc.company, d);
if (d.reference_type==="Sales Invoice" && !flt(d.credit)) { }
this.get_outstanding('Sales Invoice', d.reference_name, d); if (d.reference_type==="Sales Invoice" && !flt(d.credit)) {
} this.get_outstanding('Sales Invoice', d.reference_name, doc.company, d);
if (d.reference_type==="Journal Entry" && !flt(d.credit) && !flt(d.debit)) { }
this.get_outstanding('Journal Entry', d.reference_name, d); if (d.reference_type==="Journal Entry" && !flt(d.credit) && !flt(d.debit)) {
this.get_outstanding('Journal Entry', d.reference_name, doc.company, d);
}
} }
}, },
get_outstanding: function(doctype, docname, child) { get_outstanding: function(doctype, docname, company, child) {
var me = this; var me = this;
var args = { var args = {
"doctype": doctype, "doctype": doctype,
"docname": docname, "docname": docname,
"party": child.party, "party": child.party,
"account": child.account "account": child.account,
"account_currency": child.account_currency,
"company": company
} }
return this.frm.call({ return frappe.call({
child: child, method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_outstanding",
method: "get_outstanding",
args: { args: args}, args: { args: args},
callback: function(r) { callback: function(r) {
cur_frm.cscript.update_totals(me.frm.doc); if(r.message) {
$.each(r.message, function(field, value) {
frappe.model.set_value(child.doctype, child.name, field, value);
})
}
} }
}); });
}, },
@@ -153,35 +224,20 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
// set difference // set difference
if(doc.difference) { if(doc.difference) {
if(doc.difference > 0) { if(doc.difference > 0) {
row.credit_in_account_currency = doc.difference;
row.credit = doc.difference; row.credit = doc.difference;
} else { } else {
row.debit_in_account_currency = -doc.difference;
row.debit = -doc.difference; row.debit = -doc.difference;
} }
} }
cur_frm.cscript.update_totals(doc);
}, },
}); });
cur_frm.script_manager.make(erpnext.accounts.JournalEntry); cur_frm.script_manager.make(erpnext.accounts.JournalEntry);
cur_frm.cscript.refresh = function(doc) {
erpnext.toggle_naming_series();
cur_frm.cscript.voucher_type(doc);
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");
}
}
cur_frm.cscript.company = function(doc, cdt, cdn) { cur_frm.cscript.company = function(doc, cdt, cdn) {
cur_frm.refresh_fields(); cur_frm.refresh_fields();
erpnext.get_fiscal_year(doc.company, doc.posting_date); erpnext.get_fiscal_year(doc.company, doc.posting_date);
@@ -193,10 +249,10 @@ cur_frm.cscript.posting_date = function(doc, cdt, cdn){
cur_frm.cscript.update_totals = function(doc) { cur_frm.cscript.update_totals = function(doc) {
var td=0.0; var tc =0.0; var td=0.0; var tc =0.0;
var el = doc.accounts || []; var accounts = doc.accounts || [];
for(var i in el) { for(var i in accounts) {
td += flt(el[i].debit, precision("debit", el[i])); td += flt(accounts[i].debit, precision("debit", accounts[i]));
tc += flt(el[i].credit, precision("credit", el[i])); tc += flt(accounts[i].credit, precision("credit", accounts[i]));
} }
var doc = locals[doc.doctype][doc.name]; var doc = locals[doc.doctype][doc.name];
doc.total_debit = td; doc.total_debit = td;
@@ -205,32 +261,12 @@ cur_frm.cscript.update_totals = function(doc) {
refresh_many(['total_debit','total_credit','difference']); refresh_many(['total_debit','total_credit','difference']);
} }
cur_frm.cscript.debit = function(doc,dt,dn) { cur_frm.cscript.update_totals(doc); }
cur_frm.cscript.credit = function(doc,dt,dn) { cur_frm.cscript.update_totals(doc); }
cur_frm.cscript.get_balance = function(doc,dt,dn) { cur_frm.cscript.get_balance = function(doc,dt,dn) {
cur_frm.cscript.update_totals(doc); cur_frm.cscript.update_totals(doc);
return $c_obj(cur_frm.doc, 'get_balance', '', function(r, rt){ return $c_obj(cur_frm.doc, 'get_balance', '', function(r, rt){
cur_frm.refresh(); cur_frm.refresh();
}); });
} }
// Get balance
// -----------
cur_frm.cscript.account = function(doc,dt,dn) {
var d = locals[dt][dn];
if(d.account) {
return frappe.call({
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_account_balance_and_party_type",
args: {account: d.account, date: doc.posting_date},
callback: function(r) {
$.extend(d, r.message);
refresh_field('balance', d.name, 'accounts');
refresh_field('party_type', d.name, 'accounts');
}
});
}
}
cur_frm.cscript.validate = function(doc,cdt,cdn) { cur_frm.cscript.validate = function(doc,cdt,cdn) {
cur_frm.cscript.update_totals(doc); cur_frm.cscript.update_totals(doc);
@@ -295,18 +331,67 @@ cur_frm.cscript.voucher_type = function(doc, cdt, cdn) {
} }
} }
frappe.ui.form.on("Journal Entry Account", "party", function(frm, cdt, cdn) { frappe.ui.form.on("Journal Entry Account", {
var d = frappe.get_doc(cdt, cdn); party: function(frm, cdt, cdn) {
if(!d.account && d.party_type && d.party) { var d = frappe.get_doc(cdt, cdn);
return frm.call({ if(!d.account && d.party_type && d.party) {
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_party_account_and_balance", if(!frm.doc.company) frappe.throw(__("Please select Company"));
child: d, return frm.call({
args: { method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_party_account_and_balance",
company: frm.doc.company, child: d,
party_type: d.party_type, args: {
party: d.party company: frm.doc.company,
} party_type: d.party_type,
}); party: d.party
}
});
}
},
account: function(frm, dt, dn) {
var d = locals[dt][dn];
if(d.account) {
if(!frm.doc.company) frappe.throw(__("Please select Company first"));
if(!frm.doc.posting_date) frappe.throw(__("Please select Posting Date first"));
return frappe.call({
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_account_balance_and_party_type",
args: {
account: d.account,
date: frm.doc.posting_date,
company: frm.doc.company,
debit: flt(d.debit_in_account_currency),
credit: flt(d.credit_in_account_currency),
exchange_rate: d.exchange_rate
},
callback: function(r) {
if(r.message) {
$.extend(d, r.message);
refresh_field('accounts');
}
}
});
}
},
debit_in_account_currency: function(frm, cdt, cdn) {
erpnext.journal_entry.set_debit_credit_in_company_currency(frm, cdt, cdn);
},
credit_in_account_currency: function(frm, cdt, cdn) {
erpnext.journal_entry.set_debit_credit_in_company_currency(frm, cdt, cdn);
},
debit: function(frm, dt, dn) {
cur_frm.cscript.update_totals(frm.doc);
},
credit: function(frm, dt, dn) {
cur_frm.cscript.update_totals(frm.doc);
},
exchange_rate: function(frm, cdt, cdn) {
erpnext.journal_entry.set_debit_credit_in_company_currency(frm, cdt, cdn);
} }
}) })
@@ -314,3 +399,41 @@ frappe.ui.form.on("Journal Entry Account", "accounts_remove", function(frm) {
cur_frm.cscript.update_totals(frm.doc); cur_frm.cscript.update_totals(frm.doc);
}); });
erpnext.journal_entry.set_debit_credit_in_company_currency = function(frm, cdt, cdn) {
erpnext.journal_entry.set_exchange_rate(frm, cdt, cdn);
var row = locals[cdt][cdn];
frappe.model.set_value(cdt, cdn, "debit",
flt(flt(row.debit_in_account_currency)*row.exchange_rate), precision("debit", row));
frappe.model.set_value(cdt, cdn, "credit",
flt(flt(row.credit_in_account_currency)*row.exchange_rate), precision("credit", row));
}
erpnext.journal_entry.set_exchange_rate = function(frm, cdt, cdn) {
var company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
var row = locals[cdt][cdn];
if(row.account_currency == company_currency || !frm.doc.multi_currency) {
frappe.model.set_value(cdt, cdn, "exchange_rate", 1);
} else if (!row.exchange_rate || row.account_type == "Bank") {
frappe.call({
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_exchange_rate",
args: {
account: row.account,
account_currency: row.account_currency,
company: frm.doc.company,
reference_type: cstr(row.reference_type),
reference_name: cstr(row.reference_name),
debit: flt(row.debit_in_account_currency),
credit: flt(row.credit_in_account_currency),
exchange_rate: row.exchange_rate
},
callback: function(r) {
if(r.message) {
frappe.model.set_value(cdt, cdn, "exchange_rate", r.message);
}
}
})
}
}

View File

@@ -42,7 +42,7 @@
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Title", "label": "Title",
"no_copy": 0, "no_copy": 1,
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
@@ -171,7 +171,7 @@
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 1, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "accounts", "fieldname": "accounts",
@@ -400,6 +400,28 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "multi_currency",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Multi Currency",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@@ -1002,7 +1024,7 @@
"is_submittable": 1, "is_submittable": 1,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-08-27 03:19:18.634225", "modified": "2015-09-30 08:52:57.388579",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Journal Entry", "name": "Journal Entry",
@@ -1010,7 +1032,7 @@
"permissions": [ "permissions": [
{ {
"amend": 1, "amend": 1,
"apply_user_permissions": 1, "apply_user_permissions": 0,
"cancel": 1, "cancel": 1,
"create": 1, "create": 1,
"delete": 1, "delete": 1,
@@ -1050,7 +1072,7 @@
}, },
{ {
"amend": 0, "amend": 0,
"apply_user_permissions": 1, "apply_user_permissions": 0,
"cancel": 0, "cancel": 0,
"create": 0, "create": 0,
"delete": 0, "delete": 0,

View File

@@ -3,11 +3,12 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import cstr, flt, fmt_money, formatdate, getdate, date_diff from frappe.utils import cstr, flt, fmt_money, formatdate
from frappe import msgprint, _, scrub from frappe import msgprint, _, scrub
from erpnext.setup.utils import get_company_currency
from erpnext.controllers.accounts_controller import AccountsController from erpnext.controllers.accounts_controller import AccountsController
from erpnext.accounts.utils import get_balance_on from erpnext.accounts.utils import get_balance_on, get_account_currency
from erpnext.accounts.party import get_party_account_currency
from erpnext.setup.utils import get_company_currency
class JournalEntry(AccountsController): class JournalEntry(AccountsController):
@@ -26,6 +27,7 @@ class JournalEntry(AccountsController):
self.validate_party() self.validate_party()
self.validate_cheque_info() self.validate_cheque_info()
self.validate_entries_for_advance() self.validate_entries_for_advance()
self.validate_multi_currency()
self.validate_debit_and_credit() self.validate_debit_and_credit()
self.validate_against_jv() self.validate_against_jv()
self.validate_reference_doc() self.validate_reference_doc()
@@ -35,6 +37,7 @@ class JournalEntry(AccountsController):
self.validate_expense_claim() self.validate_expense_claim()
self.validate_credit_debit_note() self.validate_credit_debit_note()
self.validate_empty_accounts_table() self.validate_empty_accounts_table()
self.set_account_and_party_balance()
self.set_title() self.set_title()
def on_submit(self): def on_submit(self):
@@ -144,6 +147,7 @@ class JournalEntry(AccountsController):
self.reference_totals = {} self.reference_totals = {}
self.reference_types = {} self.reference_types = {}
self.reference_accounts = {}
for d in self.get("accounts"): for d in self.get("accounts"):
if not d.reference_type: if not d.reference_type:
@@ -151,8 +155,8 @@ class JournalEntry(AccountsController):
if not d.reference_name: if not d.reference_name:
d.reference_type = None d.reference_type = None
if d.reference_type and d.reference_name and (d.reference_type in field_dict.keys()): if d.reference_type and d.reference_name and (d.reference_type in field_dict.keys()):
dr_or_cr = "credit" if d.reference_type in ("Sales Order", "Sales Invoice") \ dr_or_cr = "credit_in_account_currency" \
else "debit" if d.reference_type in ("Sales Order", "Sales Invoice") else "debit_in_account_currency"
# check debit or credit type Sales / Purchase Order # check debit or credit type Sales / Purchase Order
if d.reference_type=="Sales Order" and flt(d.debit) > 0: if d.reference_type=="Sales Order" and flt(d.debit) > 0:
@@ -166,6 +170,7 @@ class JournalEntry(AccountsController):
self.reference_totals[d.reference_name] = 0.0 self.reference_totals[d.reference_name] = 0.0
self.reference_totals[d.reference_name] += flt(d.get(dr_or_cr)) self.reference_totals[d.reference_name] += flt(d.get(dr_or_cr))
self.reference_types[d.reference_name] = d.reference_type self.reference_types[d.reference_name] = d.reference_type
self.reference_accounts[d.reference_name] = d.account
against_voucher = frappe.db.get_value(d.reference_type, d.reference_name, against_voucher = frappe.db.get_value(d.reference_type, d.reference_name,
[scrub(dt) for dt in field_dict.get(d.reference_type)]) [scrub(dt) for dt in field_dict.get(d.reference_type)])
@@ -191,23 +196,31 @@ class JournalEntry(AccountsController):
"""Validate totals, stopped and docstatus for orders""" """Validate totals, stopped and docstatus for orders"""
for reference_name, total in self.reference_totals.iteritems(): for reference_name, total in self.reference_totals.iteritems():
reference_type = self.reference_types[reference_name] reference_type = self.reference_types[reference_name]
account = self.reference_accounts[reference_name]
if reference_type in ("Sales Order", "Purchase Order"): if reference_type in ("Sales Order", "Purchase Order"):
voucher_properties = frappe.db.get_value(reference_type, reference_name, order = frappe.db.get_value(reference_type, reference_name,
["docstatus", "per_billed", "status", "advance_paid", "base_grand_total"]) ["docstatus", "per_billed", "status", "advance_paid",
"base_grand_total", "grand_total", "currency"], as_dict=1)
if voucher_properties[0] != 1: if order.docstatus != 1:
frappe.throw(_("{0} {1} is not submitted").format(reference_type, reference_name)) frappe.throw(_("{0} {1} is not submitted").format(reference_type, reference_name))
if flt(voucher_properties[1]) >= 100: if flt(order.per_billed) >= 100:
frappe.throw(_("{0} {1} is fully billed").format(reference_type, reference_name)) frappe.throw(_("{0} {1} is fully billed").format(reference_type, reference_name))
if cstr(voucher_properties[2]) == "Stopped": if cstr(order.status) == "Stopped":
frappe.throw(_("{0} {1} is stopped").format(reference_type, reference_name)) frappe.throw(_("{0} {1} is stopped").format(reference_type, reference_name))
if flt(voucher_properties[4]) < (flt(voucher_properties[3]) + total): account_currency = get_account_currency(account)
if account_currency == self.company_currency:
voucher_total = order.base_grand_total
else:
voucher_total = order.grand_total
if flt(voucher_total) < (flt(order.advance_paid) + total):
frappe.throw(_("Advance paid against {0} {1} cannot be greater \ frappe.throw(_("Advance paid against {0} {1} cannot be greater \
than Grand Total {2}").format(reference_type, reference_name, voucher_properties[4])) than Grand Total {2}").format(reference_type, reference_name, voucher_total))
def validate_invoices(self): def validate_invoices(self):
"""Validate totals and docstatus for invoices""" """Validate totals and docstatus for invoices"""
@@ -215,15 +228,15 @@ class JournalEntry(AccountsController):
reference_type = self.reference_types[reference_name] reference_type = self.reference_types[reference_name]
if reference_type in ("Sales Invoice", "Purchase Invoice"): if reference_type in ("Sales Invoice", "Purchase Invoice"):
voucher_properties = frappe.db.get_value(reference_type, reference_name, invoice = frappe.db.get_value(reference_type, reference_name,
["docstatus", "outstanding_amount"]) ["docstatus", "outstanding_amount"], as_dict=1)
if voucher_properties[0] != 1: if invoice.docstatus != 1:
frappe.throw(_("{0} {1} is not submitted").format(reference_type, reference_name)) frappe.throw(_("{0} {1} is not submitted").format(reference_type, reference_name))
if total and flt(voucher_properties[1]) < total: if total and flt(invoice.outstanding_amount) < total:
frappe.throw(_("Payment against {0} {1} cannot be greater \ frappe.throw(_("Payment against {0} {1} cannot be greater than Outstanding Amount {2}")
than Outstanding Amount {2}").format(reference_type, reference_name, voucher_properties[1])) .format(reference_type, reference_name, invoice.outstanding_amount))
def set_against_account(self): def set_against_account(self):
accounts_debited, accounts_credited = [], [] accounts_debited, accounts_credited = [], []
@@ -237,13 +250,12 @@ class JournalEntry(AccountsController):
def validate_debit_and_credit(self): def validate_debit_and_credit(self):
self.total_debit, self.total_credit, self.difference = 0, 0, 0 self.total_debit, self.total_credit, self.difference = 0, 0, 0
for d in self.get("accounts"): for d in self.get("accounts"):
if d.debit and d.credit: if d.debit and d.credit:
frappe.throw(_("You cannot credit and debit same account at the same time")) frappe.throw(_("You cannot credit and debit same account at the same time"))
self.total_debit = flt(self.total_debit) + flt(d.debit, self.precision("debit", "accounts")) self.total_debit = flt(self.total_debit) + flt(d.debit, d.precision("debit"))
self.total_credit = flt(self.total_credit) + flt(d.credit, self.precision("credit", "accounts")) self.total_credit = flt(self.total_credit) + flt(d.credit, d.precision("credit"))
self.difference = flt(self.total_debit, self.precision("total_debit")) - \ self.difference = flt(self.total_debit, self.precision("total_debit")) - \
flt(self.total_credit, self.precision("total_credit")) flt(self.total_credit, self.precision("total_credit"))
@@ -252,6 +264,41 @@ class JournalEntry(AccountsController):
frappe.throw(_("Total Debit must be equal to Total Credit. The difference is {0}") frappe.throw(_("Total Debit must be equal to Total Credit. The difference is {0}")
.format(self.difference)) .format(self.difference))
def validate_multi_currency(self):
alternate_currency = []
for d in self.get("accounts"):
account = frappe.db.get_value("Account", d.account, ["account_currency", "account_type"], as_dict=1)
d.account_currency = account.account_currency or self.company_currency
d.account_type = account.account_type
if d.account_currency!=self.company_currency and d.account_currency not in alternate_currency:
alternate_currency.append(d.account_currency)
if alternate_currency:
if not self.multi_currency:
frappe.throw(_("Please check Multi Currency option to allow accounts with other currency"))
if len(alternate_currency) > 1:
frappe.throw(_("Only one alternate currency can be used in a single Journal Entry"))
self.set_exchange_rate()
for d in self.get("accounts"):
d.debit = flt(flt(d.debit_in_account_currency)*flt(d.exchange_rate), d.precision("debit"))
d.credit = flt(flt(d.credit_in_account_currency)*flt(d.exchange_rate), d.precision("credit"))
def set_exchange_rate(self):
for d in self.get("accounts"):
if d.account_currency == self.company_currency:
d.exchange_rate = 1
elif not d.exchange_rate or d.account_type=="Bank" or \
(d.reference_type in ("Sales Invoice", "Purchase Invoice") and d.reference_name):
d.exchange_rate = get_exchange_rate(d.account, d.account_currency, self.company,
d.reference_type, d.reference_name, d.debit, d.credit, d.exchange_rate)
if not d.exchange_rate:
frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx))
def create_remarks(self): def create_remarks(self):
r = [] r = []
if self.cheque_no: if self.cheque_no:
@@ -260,15 +307,13 @@ class JournalEntry(AccountsController):
else: else:
msgprint(_("Please enter Reference date"), raise_exception=frappe.MandatoryError) msgprint(_("Please enter Reference date"), raise_exception=frappe.MandatoryError)
company_currency = get_company_currency(self.company)
for d in self.get('accounts'): for d in self.get('accounts'):
if d.reference_type=="Sales Invoice" and d.credit: if d.reference_type=="Sales Invoice" and d.credit:
r.append(_("{0} against Sales Invoice {1}").format(fmt_money(flt(d.credit), currency = company_currency), \ r.append(_("{0} against Sales Invoice {1}").format(fmt_money(flt(d.credit), currency = self.company_currency), \
d.reference_name)) d.reference_name))
if d.reference_type=="Sales Order" and d.credit: if d.reference_type=="Sales Order" and d.credit:
r.append(_("{0} against Sales Order {1}").format(fmt_money(flt(d.credit), currency = company_currency), \ r.append(_("{0} against Sales Order {1}").format(fmt_money(flt(d.credit), currency = self.company_currency), \
d.reference_name)) d.reference_name))
if d.reference_type == "Purchase Invoice" and d.debit: if d.reference_type == "Purchase Invoice" and d.debit:
@@ -276,11 +321,11 @@ class JournalEntry(AccountsController):
from `tabPurchase Invoice` where name=%s""", d.reference_name) from `tabPurchase Invoice` where name=%s""", d.reference_name)
if bill_no and bill_no[0][0] and bill_no[0][0].lower().strip() \ if bill_no and bill_no[0][0] and bill_no[0][0].lower().strip() \
not in ['na', 'not applicable', 'none']: not in ['na', 'not applicable', 'none']:
r.append(_('{0} against Bill {1} dated {2}').format(fmt_money(flt(d.debit), currency=company_currency), bill_no[0][0], r.append(_('{0} against Bill {1} dated {2}').format(fmt_money(flt(d.debit), currency=self.company_currency), bill_no[0][0],
bill_no[0][1] and formatdate(bill_no[0][1].strftime('%Y-%m-%d')))) bill_no[0][1] and formatdate(bill_no[0][1].strftime('%Y-%m-%d'))))
if d.reference_type == "Purchase Order" and d.debit: if d.reference_type == "Purchase Order" and d.debit:
r.append(_("{0} against Purchase Order {1}").format(fmt_money(flt(d.credit), currency = company_currency), \ r.append(_("{0} against Purchase Order {1}").format(fmt_money(flt(d.credit), currency = self.company_currency), \
d.reference_name)) d.reference_name))
if self.user_remark: if self.user_remark:
@@ -301,10 +346,9 @@ class JournalEntry(AccountsController):
self.set_total_amount(d.debit or d.credit) self.set_total_amount(d.debit or d.credit)
def set_total_amount(self, amt): def set_total_amount(self, amt):
company_currency = get_company_currency(self.company)
self.total_amount = amt self.total_amount = amt
from frappe.utils import money_in_words from frappe.utils import money_in_words
self.total_amount_in_words = money_in_words(amt, company_currency) self.total_amount_in_words = money_in_words(amt, self.company_currency)
def make_gl_entries(self, cancel=0, adv_adj=0): def make_gl_entries(self, cancel=0, adv_adj=0):
from erpnext.accounts.general_ledger import make_gl_entries from erpnext.accounts.general_ledger import make_gl_entries
@@ -318,8 +362,11 @@ class JournalEntry(AccountsController):
"party_type": d.party_type, "party_type": d.party_type,
"party": d.party, "party": d.party,
"against": d.against_account, "against": d.against_account,
"debit": flt(d.debit, self.precision("debit", "accounts")), "debit": flt(d.debit, d.precision("debit")),
"credit": flt(d.credit, self.precision("credit", "accounts")), "credit": flt(d.credit, d.precision("credit")),
"account_currency": d.account_currency,
"debit_in_account_currency": flt(d.debit_in_account_currency, d.precision("debit_in_account_currency")),
"credit_in_account_currency": flt(d.credit_in_account_currency, d.precision("credit_in_account_currency")),
"against_voucher_type": d.reference_type, "against_voucher_type": d.reference_type,
"against_voucher": d.reference_name, "against_voucher": d.reference_name,
"remarks": self.remark, "remarks": self.remark,
@@ -338,21 +385,22 @@ class JournalEntry(AccountsController):
diff = flt(self.difference, self.precision("difference")) diff = flt(self.difference, self.precision("difference"))
# If any row without amount, set the diff on that row # If any row without amount, set the diff on that row
for d in self.get('accounts'): if diff:
if not d.credit and not d.debit and diff != 0: blank_row = None
if diff>0: for d in self.get('accounts'):
d.credit = diff if not d.credit_in_account_currency and not d.debit_in_account_currency and diff != 0:
elif diff<0: blank_row = d
d.debit = diff
flag = 1
# Set the diff in a new row if not blank_row:
if flag == 0 and diff != 0: blank_row = self.append('accounts', {})
jd = self.append('accounts', {})
blank_row.exchange_rate = 1
if diff>0: if diff>0:
jd.credit = abs(diff) blank_row.credit_in_account_currency = diff
blank_row.credit = diff
elif diff<0: elif diff<0:
jd.debit = abs(diff) blank_row.debit_in_account_currency = abs(diff)
blank_row.debit = abs(diff)
self.validate_debit_and_credit() self.validate_debit_and_credit()
@@ -427,6 +475,20 @@ class JournalEntry(AccountsController):
if not self.get('accounts'): if not self.get('accounts'):
frappe.throw("Accounts table cannot be blank.") frappe.throw("Accounts table cannot be blank.")
def set_account_and_party_balance(self):
account_balance = {}
party_balance = {}
for d in self.get("accounts"):
if d.account not in account_balance:
account_balance[d.account] = get_balance_on(account=d.account, date=self.posting_date)
if (d.party_type, d.party) not in party_balance:
party_balance[(d.party_type, d.party)] = get_balance_on(party_type=d.party_type,
party=d.party, date=self.posting_date)
d.account_balance = account_balance[d.account]
d.party_balance = party_balance[(d.party_type, d.party)]
@frappe.whitelist() @frappe.whitelist()
def get_default_bank_cash_account(company, voucher_type, mode_of_payment=None): def get_default_bank_cash_account(company, voucher_type, mode_of_payment=None):
from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_bank_cash_account from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_bank_cash_account
@@ -446,9 +508,12 @@ def get_default_bank_cash_account(company, voucher_type, mode_of_payment=None):
account = frappe.db.get_value("Account", {"company": company, "account_type": "Cash", "is_group": 0}) account = frappe.db.get_value("Account", {"company": company, "account_type": "Cash", "is_group": 0})
if account: if account:
account_details = frappe.db.get_value("Account", account, ["account_currency", "account_type"], as_dict=1)
return { return {
"account": account, "account": account,
"balance": get_balance_on(account) "balance": get_balance_on(account),
"account_currency": account_details.account_currency,
"account_type": account_details.account_type
} }
@frappe.whitelist() @frappe.whitelist()
@@ -456,21 +521,38 @@ def get_payment_entry_from_sales_invoice(sales_invoice):
"""Returns new Journal Entry document as dict for given Sales Invoice""" """Returns new Journal Entry document as dict for given Sales Invoice"""
from erpnext.accounts.utils import get_balance_on from erpnext.accounts.utils import get_balance_on
si = frappe.get_doc("Sales Invoice", sales_invoice) si = frappe.get_doc("Sales Invoice", sales_invoice)
# exchange rate
exchange_rate = get_exchange_rate(si.debit_to, si.party_account_currency, si.company,
si.doctype, si.name)
jv = get_payment_entry(si) jv = get_payment_entry(si)
jv.remark = 'Payment received against Sales Invoice {0}. {1}'.format(si.name, si.remarks) jv.remark = 'Payment received against Sales Invoice {0}. {1}'.format(si.name, si.remarks)
# credit customer # credit customer
jv.get("accounts")[0].account = si.debit_to row1 = jv.get("accounts")[0]
jv.get("accounts")[0].party_type = "Customer" row1.account = si.debit_to
jv.get("accounts")[0].party = si.customer row1.account_currency = si.party_account_currency
jv.get("accounts")[0].balance = get_balance_on(si.debit_to) row1.party_type = "Customer"
jv.get("accounts")[0].party_balance = get_balance_on(party=si.customer, party_type="Customer") row1.party = si.customer
jv.get("accounts")[0].credit = si.outstanding_amount row1.balance = get_balance_on(si.debit_to)
jv.get("accounts")[0].reference_type = si.doctype row1.party_balance = get_balance_on(party=si.customer, party_type="Customer")
jv.get("accounts")[0].reference_name = si.name row1.credit_in_account_currency = si.outstanding_amount
row1.reference_type = si.doctype
row1.reference_name = si.name
row1.exchange_rate = exchange_rate
row1.account_type = "Receivable" if si.customer else ""
# debit bank # debit bank
jv.get("accounts")[1].debit = si.outstanding_amount row2 = jv.get("accounts")[1]
if row2.account_currency == si.party_account_currency:
row2.debit_in_account_currency = si.outstanding_amount
else:
row2.debit_in_account_currency = si.outstanding_amount * exchange_rate
# set multi currency check
if row1.account_currency != si.company_currency or row2.account_currency != si.company_currency:
jv.multi_currency = 1
return jv.as_dict() return jv.as_dict()
@@ -478,21 +560,38 @@ def get_payment_entry_from_sales_invoice(sales_invoice):
def get_payment_entry_from_purchase_invoice(purchase_invoice): def get_payment_entry_from_purchase_invoice(purchase_invoice):
"""Returns new Journal Entry document as dict for given Purchase Invoice""" """Returns new Journal Entry document as dict for given Purchase Invoice"""
pi = frappe.get_doc("Purchase Invoice", purchase_invoice) pi = frappe.get_doc("Purchase Invoice", purchase_invoice)
exchange_rate = get_exchange_rate(pi.credit_to, pi.party_account_currency, pi.company,
pi.doctype, pi.name)
jv = get_payment_entry(pi) jv = get_payment_entry(pi)
jv.remark = 'Payment against Purchase Invoice {0}. {1}'.format(pi.name, pi.remarks) jv.remark = 'Payment against Purchase Invoice {0}. {1}'.format(pi.name, pi.remarks)
jv.exchange_rate = exchange_rate
# credit supplier # credit supplier
jv.get("accounts")[0].account = pi.credit_to row1 = jv.get("accounts")[0]
jv.get("accounts")[0].party_type = "Supplier" row1.account = pi.credit_to
jv.get("accounts")[0].party = pi.supplier row1.account_currency = pi.party_account_currency
jv.get("accounts")[0].balance = get_balance_on(pi.credit_to) row1.party_type = "Supplier"
jv.get("accounts")[0].party_balance = get_balance_on(party=pi.supplier, party_type="Supplier") row1.party = pi.supplier
jv.get("accounts")[0].debit = pi.outstanding_amount row1.balance = get_balance_on(pi.credit_to)
jv.get("accounts")[0].reference_type = pi.doctype row1.party_balance = get_balance_on(party=pi.supplier, party_type="Supplier")
jv.get("accounts")[0].reference_name = pi.name row1.debit_in_account_currency = pi.outstanding_amount
row1.reference_type = pi.doctype
row1.reference_name = pi.name
row1.exchange_rate = exchange_rate
row1.account_type = "Payable" if pi.supplier else ""
# credit bank # credit bank
jv.get("accounts")[1].credit = pi.outstanding_amount row2 = jv.get("accounts")[1]
if row2.account_currency == pi.party_account_currency:
row2.credit_in_account_currency = pi.outstanding_amount
else:
row2.credit_in_account_currency = pi.outstanding_amount * exchange_rate
# set multi currency check
if row1.account_currency != pi.company_currency or row2.account_currency != pi.company_currency:
jv.multi_currency = 1
return jv.as_dict() return jv.as_dict()
@@ -501,6 +600,7 @@ def get_payment_entry_from_sales_order(sales_order):
"""Returns new Journal Entry document as dict for given Sales Order""" """Returns new Journal Entry document as dict for given Sales Order"""
from erpnext.accounts.utils import get_balance_on from erpnext.accounts.utils import get_balance_on
from erpnext.accounts.party import get_party_account from erpnext.accounts.party import get_party_account
so = frappe.get_doc("Sales Order", sales_order) so = frappe.get_doc("Sales Order", sales_order)
if flt(so.per_billed, 2) != 0.0: if flt(so.per_billed, 2) != 0.0:
@@ -508,23 +608,42 @@ def get_payment_entry_from_sales_order(sales_order):
jv = get_payment_entry(so) jv = get_payment_entry(so)
jv.remark = 'Advance payment received against Sales Order {0}.'.format(so.name) jv.remark = 'Advance payment received against Sales Order {0}.'.format(so.name)
party_account = get_party_account(so.company, so.customer, "Customer")
amount = flt(so.base_grand_total) - flt(so.advance_paid) party_account = get_party_account("Customer", so.customer, so.company)
party_account_currency = get_account_currency(party_account)
exchange_rate = get_exchange_rate(party_account, party_account_currency, so.company)
if party_account_currency == so.company_currency:
amount = flt(so.base_grand_total) - flt(so.advance_paid)
else:
amount = flt(so.grand_total) - flt(so.advance_paid)
# credit customer # credit customer
jv.get("accounts")[0].account = party_account row1 = jv.get("accounts")[0]
jv.get("accounts")[0].party_type = "Customer" row1.account = party_account
jv.get("accounts")[0].party = so.customer row1.account_currency = party_account_currency
jv.get("accounts")[0].balance = get_balance_on(party_account) row1.party_type = "Customer"
jv.get("accounts")[0].party_balance = get_balance_on(party=so.customer, party_type="Customer") row1.party = so.customer
jv.get("accounts")[0].credit = amount row1.balance = get_balance_on(party_account)
jv.get("accounts")[0].reference_type = so.doctype row1.party_balance = get_balance_on(party=so.customer, party_type="Customer")
jv.get("accounts")[0].reference_name = so.name row1.credit_in_account_currency = amount
jv.get("accounts")[0].is_advance = "Yes" row1.reference_type = so.doctype
row1.reference_name = so.name
row1.is_advance = "Yes"
row1.exchange_rate = exchange_rate
row1.account_type = "Receivable"
# debit bank # debit bank
jv.get("accounts")[1].debit = amount row2 = jv.get("accounts")[1]
if row2.account_currency == party_account_currency:
row2.debit_in_account_currency = amount
else:
row2.debit_in_account_currency = amount * exchange_rate
# set multi currency check
if row1.account_currency != so.company_currency or row2.account_currency != so.company_currency:
jv.multi_currency = 1
return jv.as_dict() return jv.as_dict()
@@ -540,23 +659,41 @@ def get_payment_entry_from_purchase_order(purchase_order):
jv = get_payment_entry(po) jv = get_payment_entry(po)
jv.remark = 'Advance payment made against Purchase Order {0}.'.format(po.name) jv.remark = 'Advance payment made against Purchase Order {0}.'.format(po.name)
party_account = get_party_account(po.company, po.supplier, "Supplier")
amount = flt(po.base_grand_total) - flt(po.advance_paid) party_account = get_party_account("Supplier", po.supplier, po.company)
party_account_currency = get_account_currency(party_account)
exchange_rate = get_exchange_rate(party_account, party_account_currency, po.company)
if party_account_currency == po.company_currency:
amount = flt(po.base_grand_total) - flt(po.advance_paid)
else:
amount = flt(po.grand_total) - flt(po.advance_paid)
# credit customer # credit customer
jv.get("accounts")[0].account = party_account row1 = jv.get("accounts")[0]
jv.get("accounts")[0].party_type = "Supplier" row1.account = party_account
jv.get("accounts")[0].party = po.supplier row1.party_type = "Supplier"
jv.get("accounts")[0].balance = get_balance_on(party_account) row1.party = po.supplier
jv.get("accounts")[0].party_balance = get_balance_on(party=po.supplier, party_type="Supplier") row1.balance = get_balance_on(party_account)
jv.get("accounts")[0].debit = amount row1.party_balance = get_balance_on(party=po.supplier, party_type="Supplier")
jv.get("accounts")[0].reference_type = po.doctype row1.debit_in_account_currency = amount
jv.get("accounts")[0].reference_name = po.name row1.reference_type = po.doctype
jv.get("accounts")[0].is_advance = "Yes" row1.reference_name = po.name
row1.is_advance = "Yes"
row1.exchange_rate = exchange_rate
row1.account_type = "Payable"
# debit bank # debit bank
jv.get("accounts")[1].credit = amount row2 = jv.get("accounts")[1]
if row2.account_currency == party_account_currency:
row2.credit_in_account_currency = amount
else:
row2.credit_in_account_currency = amount * exchange_rate
# set multi currency check
if row1.account_currency != po.company_currency or row2.account_currency != po.company_currency:
jv.multi_currency = 1
return jv.as_dict() return jv.as_dict()
@@ -574,6 +711,10 @@ def get_payment_entry(doc):
if bank_account: if bank_account:
d2.account = bank_account["account"] d2.account = bank_account["account"]
d2.balance = bank_account["balance"] d2.balance = bank_account["balance"]
d2.account_currency = bank_account["account_currency"]
d2.account_type = bank_account["account_type"]
d2.exchange_rate = get_exchange_rate(bank_account["account"],
bank_account["account_currency"], doc.company)
return jv return jv
@@ -599,27 +740,37 @@ def get_outstanding(args):
if not frappe.has_permission("Account"): if not frappe.has_permission("Account"):
frappe.msgprint(_("No Permission"), raise_exception=1) frappe.msgprint(_("No Permission"), raise_exception=1)
args = eval(args) args = eval(args)
company_currency = get_company_currency(args.get("company"))
if args.get("doctype") == "Journal Entry": if args.get("doctype") == "Journal Entry":
condition = " and party=%(party)s" if args.get("party") else "" condition = " and party=%(party)s" if args.get("party") else ""
against_jv_amount = frappe.db.sql(""" against_jv_amount = frappe.db.sql("""
select sum(ifnull(debit, 0)) - sum(ifnull(credit, 0)) select sum(ifnull(debit_in_account_currency, 0)) - sum(ifnull(credit_in_account_currency, 0))
from `tabJournal Entry Account` where parent=%(docname)s and account=%(account)s {0} from `tabJournal Entry Account` where parent=%(docname)s and account=%(account)s {0}
and ifnull(reference_type, '')=''""".format(condition), args) and ifnull(reference_type, '')=''""".format(condition), args)
against_jv_amount = flt(against_jv_amount[0][0]) if against_jv_amount else 0 against_jv_amount = flt(against_jv_amount[0][0]) if against_jv_amount else 0
amount_field = "credit_in_account_currency" if against_jv_amount > 0 else "debit_in_account_currency"
return { return {
("credit" if against_jv_amount > 0 else "debit"): abs(against_jv_amount) amount_field: abs(against_jv_amount)
} }
elif args.get("doctype") == "Sales Invoice": elif args.get("doctype") in ("Sales Invoice", "Purchase Invoice"):
outstanding_amount = flt(frappe.db.get_value("Sales Invoice", args["docname"], "outstanding_amount")) invoice = frappe.db.get_value(args["doctype"], args["docname"],
["outstanding_amount", "conversion_rate"], as_dict=1)
exchange_rate = invoice.conversion_rate if (args.get("account_currency") != company_currency) else 1
if args["doctype"] == "Sales Invoice":
amount_field = "credit_in_account_currency" \
if flt(invoice.outstanding_amount) > 0 else "debit_in_account_currency"
else:
amount_field = "debit_in_account_currency" \
if flt(invoice.outstanding_amount) > 0 else "credit_in_account_currency"
return { return {
("credit" if outstanding_amount > 0 else "debit"): abs(outstanding_amount) amount_field: abs(flt(invoice.outstanding_amount)),
} "exchange_rate": exchange_rate
elif args.get("doctype") == "Purchase Invoice":
outstanding_amount = flt(frappe.db.get_value("Purchase Invoice", args["docname"], "outstanding_amount"))
return {
("debit" if outstanding_amount > 0 else "credit"): abs(outstanding_amount)
} }
@frappe.whitelist() @frappe.whitelist()
@@ -628,7 +779,7 @@ def get_party_account_and_balance(company, party_type, party):
frappe.msgprint(_("No Permission"), raise_exception=1) frappe.msgprint(_("No Permission"), raise_exception=1)
from erpnext.accounts.party import get_party_account from erpnext.accounts.party import get_party_account
account = get_party_account(company, party, party_type) account = get_party_account(party_type, party, company)
account_balance = get_balance_on(account=account) account_balance = get_balance_on(account=account)
party_balance = get_balance_on(party_type=party_type, party=party) party_balance = get_balance_on(party_type=party_type, party=party)
@@ -640,14 +791,61 @@ def get_party_account_and_balance(company, party_type, party):
} }
@frappe.whitelist() @frappe.whitelist()
def get_account_balance_and_party_type(account, date): def get_account_balance_and_party_type(account, date, company, debit=None, credit=None, exchange_rate=None):
"""Returns dict of account balance and party type to be set in Journal Entry on selection of account.""" """Returns dict of account balance and party type to be set in Journal Entry on selection of account."""
if not frappe.has_permission("Account"): if not frappe.has_permission("Account"):
frappe.msgprint(_("No Permission"), raise_exception=1) frappe.msgprint(_("No Permission"), raise_exception=1)
account_type = frappe.db.get_value("Account", account, "account_type") company_currency = get_company_currency(company)
return { account_details = frappe.db.get_value("Account", account, ["account_type", "account_currency"], as_dict=1)
"balance": get_balance_on(account, date),
"party_type": {"Receivable":"Customer", "Payable":"Supplier"}.get(account_type, "")
}
if account_details.account_type == "Receivable":
party_type = "Customer"
elif account_details.account_type == "Payable":
party_type = "Supplier"
else:
party_type = ""
grid_values = {
"balance": get_balance_on(account, date),
"party_type": party_type,
"account_type": account_details.account_type,
"account_currency": account_details.account_currency or company_currency,
"exchange_rate": get_exchange_rate(account, account_details.account_currency,
company, debit=debit, credit=credit, exchange_rate=exchange_rate)
}
return grid_values
@frappe.whitelist()
def get_exchange_rate(account, account_currency, company,
reference_type=None, reference_name=None, debit=None, credit=None, exchange_rate=None):
from erpnext.setup.utils import get_exchange_rate
company_currency = get_company_currency(company)
account_details = frappe.db.get_value("Account", account, ["account_type", "root_type"], as_dict=1)
if account_currency != company_currency:
if reference_type in ("Sales Invoice", "Purchase Invoice") and reference_name:
exchange_rate = frappe.db.get_value(reference_type, reference_name, "conversion_rate")
elif account_details and account_details.account_type == "Bank" and \
((account_details.root_type == "Asset" and flt(credit) > 0) or
(account_details.root_type == "Liability" and debit)):
exchange_rate = get_average_exchange_rate(account)
if not exchange_rate and account_currency:
exchange_rate = get_exchange_rate(account_currency, company_currency)
else:
exchange_rate = 1
# don't return None or 0 as it is multipled with a value and that value could be lost
return exchange_rate or 1
def get_average_exchange_rate(account):
exchange_rate = 0
bank_balance_in_account_currency = get_balance_on(account)
if bank_balance_in_account_currency:
bank_balance_in_company_currency = get_balance_on(account, in_account_currency=False)
exchange_rate = bank_balance_in_company_currency / bank_balance_in_account_currency
return exchange_rate

View File

@@ -5,6 +5,7 @@ from __future__ import unicode_literals
import unittest, frappe import unittest, frappe
from frappe.utils import flt from frappe.utils import flt
from erpnext.accounts.utils import get_actual_expense, BudgetError, get_fiscal_year from erpnext.accounts.utils import get_actual_expense, BudgetError, get_fiscal_year
from erpnext.exceptions import InvalidAccountCurrency
class TestJournalEntry(unittest.TestCase): class TestJournalEntry(unittest.TestCase):
@@ -101,7 +102,7 @@ class TestJournalEntry(unittest.TestCase):
self.set_total_expense_zero("2013-02-28") self.set_total_expense_zero("2013-02-28")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC", jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Account Bank Account - _TC", 40000, "_Test Cost Center - _TC", submit=True) "_Test Bank - _TC", 40000, "_Test Cost Center - _TC", submit=True)
self.assertTrue(frappe.db.get_value("GL Entry", self.assertTrue(frappe.db.get_value("GL Entry",
{"voucher_type": "Journal Entry", "voucher_no": jv.name})) {"voucher_type": "Journal Entry", "voucher_no": jv.name}))
@@ -112,7 +113,7 @@ class TestJournalEntry(unittest.TestCase):
self.set_total_expense_zero("2013-02-28") self.set_total_expense_zero("2013-02-28")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC", jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Account Bank Account - _TC", 40000, "_Test Cost Center - _TC") "_Test Bank - _TC", 40000, "_Test Cost Center - _TC")
self.assertRaises(BudgetError, jv.submit) self.assertRaises(BudgetError, jv.submit)
@@ -126,7 +127,7 @@ class TestJournalEntry(unittest.TestCase):
self.set_total_expense_zero("2013-02-28") self.set_total_expense_zero("2013-02-28")
jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC", jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Account Bank Account - _TC", 150000, "_Test Cost Center - _TC") "_Test Bank - _TC", 150000, "_Test Cost Center - _TC")
self.assertRaises(BudgetError, jv.submit) self.assertRaises(BudgetError, jv.submit)
@@ -136,13 +137,13 @@ class TestJournalEntry(unittest.TestCase):
self.set_total_expense_zero("2013-02-28") self.set_total_expense_zero("2013-02-28")
jv1 = make_journal_entry("_Test Account Cost for Goods Sold - _TC", jv1 = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Account Bank Account - _TC", 20000, "_Test Cost Center - _TC", submit=True) "_Test Bank - _TC", 20000, "_Test Cost Center - _TC", submit=True)
self.assertTrue(frappe.db.get_value("GL Entry", self.assertTrue(frappe.db.get_value("GL Entry",
{"voucher_type": "Journal Entry", "voucher_no": jv1.name})) {"voucher_type": "Journal Entry", "voucher_no": jv1.name}))
jv2 = make_journal_entry("_Test Account Cost for Goods Sold - _TC", jv2 = make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Account Bank Account - _TC", 20000, "_Test Cost Center - _TC", submit=True) "_Test Bank - _TC", 20000, "_Test Cost Center - _TC", submit=True)
self.assertTrue(frappe.db.get_value("GL Entry", self.assertTrue(frappe.db.get_value("GL Entry",
{"voucher_type": "Journal Entry", "voucher_no": jv2.name})) {"voucher_type": "Journal Entry", "voucher_no": jv2.name}))
@@ -165,32 +166,112 @@ class TestJournalEntry(unittest.TestCase):
def set_total_expense_zero(self, posting_date): def set_total_expense_zero(self, posting_date):
existing_expense = self.get_actual_expense(posting_date) existing_expense = self.get_actual_expense(posting_date)
make_journal_entry("_Test Account Cost for Goods Sold - _TC", make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Account Bank Account - _TC", -existing_expense, "_Test Cost Center - _TC", submit=True) "_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", submit=True)
def make_journal_entry(account1, account2, amount, cost_center=None, submit=False): def test_multi_currency(self):
jv = make_journal_entry("_Test Bank USD - _TC",
"_Test Bank - _TC", 100, exchange_rate=50, save=False)
jv.get("accounts")[1].credit_in_account_currency = 5000
jv.submit()
gl_entries = frappe.db.sql("""select account, account_currency, debit, credit,
debit_in_account_currency, credit_in_account_currency
from `tabGL Entry` where voucher_type='Journal Entry' and voucher_no=%s
order by account asc""", jv.name, as_dict=1)
self.assertTrue(gl_entries)
expected_values = {
"_Test Bank USD - _TC": {
"account_currency": "USD",
"debit": 5000,
"debit_in_account_currency": 100,
"credit": 0,
"credit_in_account_currency": 0
},
"_Test Bank - _TC": {
"account_currency": "INR",
"debit": 0,
"debit_in_account_currency": 0,
"credit": 5000,
"credit_in_account_currency": 5000
}
}
for field in ("account_currency", "debit", "debit_in_account_currency", "credit", "credit_in_account_currency"):
for i, gle in enumerate(gl_entries):
self.assertEquals(expected_values[gle.account][field], gle[field])
# cancel
jv.cancel()
gle = frappe.db.sql("""select name from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s""", jv.name)
self.assertFalse(gle)
def test_disallow_change_in_account_currency_for_a_party(self):
# create jv in USD
jv = make_journal_entry("_Test Bank USD - _TC",
"_Test Receivable USD - _TC", 100, save=False)
jv.accounts[1].update({
"party_type": "Customer",
"party": "_Test Customer USD"
})
jv.submit()
# create jv in USD, but account currency in INR
jv = make_journal_entry("_Test Bank - _TC",
"_Test Receivable - _TC", 100, save=False)
jv.accounts[1].update({
"party_type": "Customer",
"party": "_Test Customer USD"
})
self.assertRaises(InvalidAccountCurrency, jv.submit)
# back in USD
jv = make_journal_entry("_Test Bank USD - _TC",
"_Test Receivable USD - _TC", 100, save=False)
jv.accounts[1].update({
"party_type": "Customer",
"party": "_Test Customer USD"
})
jv.submit()
def make_journal_entry(account1, account2, amount, cost_center=None, exchange_rate=1, save=True, submit=False):
jv = frappe.new_doc("Journal Entry") jv = frappe.new_doc("Journal Entry")
jv.posting_date = "2013-02-14" jv.posting_date = "2013-02-14"
jv.company = "_Test Company" jv.company = "_Test Company"
jv.fiscal_year = "_Test Fiscal Year 2013" jv.fiscal_year = "_Test Fiscal Year 2013"
jv.user_remark = "test" jv.user_remark = "test"
jv.multi_currency = 1
jv.set("accounts", [ jv.set("accounts", [
{ {
"account": account1, "account": account1,
"cost_center": cost_center, "cost_center": cost_center,
"debit": amount if amount > 0 else 0, "debit_in_account_currency": amount if amount > 0 else 0,
"credit": abs(amount) if amount < 0 else 0, "credit_in_account_currency": abs(amount) if amount < 0 else 0,
"exchange_rate": exchange_rate
}, { }, {
"account": account2, "account": account2,
"cost_center": cost_center, "cost_center": cost_center,
"credit": amount if amount > 0 else 0, "credit_in_account_currency": amount if amount > 0 else 0,
"debit": abs(amount) if amount < 0 else 0, "debit_in_account_currency": abs(amount) if amount < 0 else 0,
"exchange_rate": exchange_rate
} }
]) ])
jv.insert() if save or submit:
jv.insert()
if submit: if submit:
jv.submit() jv.submit()
return jv return jv

View File

@@ -9,15 +9,15 @@
"account": "_Test Receivable - _TC", "account": "_Test Receivable - _TC",
"party_type": "Customer", "party_type": "Customer",
"party": "_Test Customer", "party": "_Test Customer",
"credit": 400.0, "credit_in_account_currency": 400.0,
"debit": 0.0, "debit_in_account_currency": 0.0,
"doctype": "Journal Entry Account", "doctype": "Journal Entry Account",
"parentfield": "accounts" "parentfield": "accounts"
}, },
{ {
"account": "_Test Account Bank Account - _TC", "account": "_Test Bank - _TC",
"credit": 0.0, "credit_in_account_currency": 0.0,
"debit": 400.0, "debit_in_account_currency": 400.0,
"doctype": "Journal Entry Account", "doctype": "Journal Entry Account",
"parentfield": "accounts" "parentfield": "accounts"
} }
@@ -40,15 +40,15 @@
"account": "_Test Payable - _TC", "account": "_Test Payable - _TC",
"party_type": "Supplier", "party_type": "Supplier",
"party": "_Test Supplier", "party": "_Test Supplier",
"credit": 0.0, "credit_in_account_currency": 0.0,
"debit": 400.0, "debit_in_account_currency": 400.0,
"doctype": "Journal Entry Account", "doctype": "Journal Entry Account",
"parentfield": "accounts" "parentfield": "accounts"
}, },
{ {
"account": "_Test Account Bank Account - _TC", "account": "_Test Bank - _TC",
"credit": 400.0, "credit_in_account_currency": 400.0,
"debit": 0.0, "debit_in_account_currency": 0.0,
"doctype": "Journal Entry Account", "doctype": "Journal Entry Account",
"parentfield": "accounts" "parentfield": "accounts"
} }
@@ -71,16 +71,16 @@
"account": "_Test Receivable - _TC", "account": "_Test Receivable - _TC",
"party_type": "Customer", "party_type": "Customer",
"party": "_Test Customer", "party": "_Test Customer",
"credit": 0.0, "credit_in_account_currency": 0.0,
"debit": 400.0, "debit_in_account_currency": 400.0,
"doctype": "Journal Entry Account", "doctype": "Journal Entry Account",
"parentfield": "accounts" "parentfield": "accounts"
}, },
{ {
"account": "Sales - _TC", "account": "Sales - _TC",
"cost_center": "_Test Cost Center - _TC", "cost_center": "_Test Cost Center - _TC",
"credit": 400.0, "credit_in_account_currency": 400.0,
"debit": 0.0, "debit_in_account_currency": 0.0,
"doctype": "Journal Entry Account", "doctype": "Journal Entry Account",
"parentfield": "accounts" "parentfield": "accounts"
} }

View File

@@ -1,416 +1,574 @@
{ {
"allow_copy": 0, "allow_copy": 0,
"allow_import": 0, "allow_import": 0,
"allow_rename": 0, "allow_rename": 0,
"autoname": "hash", "autoname": "hash",
"creation": "2013-02-22 01:27:39", "creation": "2013-02-22 01:27:39",
"custom": 0, "custom": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "account", "fieldname": "account",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 1, "in_filter": 1,
"in_list_view": 1, "in_list_view": 1,
"label": "Account", "label": "Account",
"no_copy": 0, "no_copy": 0,
"oldfieldname": "account", "oldfieldname": "account",
"oldfieldtype": "Link", "oldfieldtype": "Link",
"options": "Account", "options": "Account",
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
"print_width": "250px", "print_width": "250px",
"read_only": 0, "read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 1, "reqd": 1,
"search_index": 1, "search_index": 1,
"set_only_once": 0, "set_only_once": 0,
"unique": 0, "unique": 0,
"width": "250px" "width": "250px"
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "balance", "fieldname": "account_type",
"fieldtype": "Currency", "fieldtype": "Data",
"hidden": 0, "hidden": 1,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Account Balance", "label": "Account Type",
"no_copy": 1, "no_copy": 0,
"oldfieldname": "balance", "permlevel": 0,
"oldfieldtype": "Data", "precision": "",
"options": "Company:company:default_currency", "print_hide": 1,
"permlevel": 0, "read_only": 0,
"print_hide": 1, "report_hide": 0,
"read_only": 1, "reqd": 0,
"report_hide": 0, "search_index": 0,
"reqd": 0, "set_only_once": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"default": ":Company", "fieldname": "balance",
"description": "If Income or Expense", "fieldtype": "Currency",
"fieldname": "cost_center", "hidden": 0,
"fieldtype": "Link", "ignore_user_permissions": 0,
"hidden": 0, "in_filter": 0,
"ignore_user_permissions": 0, "in_list_view": 0,
"in_filter": 1, "label": "Account Balance",
"in_list_view": 0, "no_copy": 1,
"label": "Cost Center", "oldfieldname": "balance",
"no_copy": 0, "oldfieldtype": "Data",
"oldfieldname": "cost_center", "options": "account_currency",
"oldfieldtype": "Link", "permlevel": 0,
"options": "Cost Center", "print_hide": 1,
"permlevel": 0, "read_only": 1,
"print_hide": 1, "report_hide": 0,
"print_width": "180px", "reqd": 0,
"read_only": 0, "search_index": 0,
"report_hide": 0, "set_only_once": 0,
"reqd": 0, "unique": 0
"search_index": 0, },
"set_only_once": 0, {
"unique": 0, "allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"default": ":Company",
"description": "If Income or Expense",
"fieldname": "cost_center",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 1,
"in_list_view": 0,
"label": "Cost Center",
"no_copy": 0,
"oldfieldname": "cost_center",
"oldfieldtype": "Link",
"options": "Cost Center",
"permlevel": 0,
"print_hide": 1,
"print_width": "180px",
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0,
"width": "180px" "width": "180px"
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "col_break1", "fieldname": "col_break1",
"fieldtype": "Column Break", "fieldtype": "Column Break",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
"read_only": 0, "read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "party_type", "fieldname": "party_type",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Party Type", "label": "Party Type",
"no_copy": 0, "no_copy": 0,
"options": "DocType", "options": "DocType",
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
"read_only": 0, "read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "party", "fieldname": "party",
"fieldtype": "Dynamic Link", "fieldtype": "Dynamic Link",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 1, "in_list_view": 1,
"label": "Party", "label": "Party",
"no_copy": 0, "no_copy": 0,
"options": "party_type", "options": "party_type",
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
"read_only": 0, "read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "party_balance", "fieldname": "party_balance",
"fieldtype": "Currency", "fieldtype": "Currency",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Party Balance", "label": "Party Balance",
"no_copy": 0, "no_copy": 0,
"options": "Company:company:default_currency", "options": "account_currency",
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
"read_only": 1, "read_only": 1,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "sec_break1", "collapsible_depends_on": "",
"fieldtype": "Section Break", "depends_on": "",
"hidden": 0, "fieldname": "currency_section",
"ignore_user_permissions": 0, "fieldtype": "Section Break",
"in_filter": 0, "hidden": 0,
"in_list_view": 0, "ignore_user_permissions": 0,
"label": "Amount", "in_filter": 0,
"no_copy": 0, "in_list_view": 0,
"permlevel": 0, "label": "Currency",
"print_hide": 0, "no_copy": 0,
"read_only": 0, "permlevel": 0,
"report_hide": 0, "precision": "",
"reqd": 0, "print_hide": 0,
"search_index": 0, "read_only": 0,
"set_only_once": 0, "report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "debit", "fieldname": "account_currency",
"fieldtype": "Currency", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 1, "in_list_view": 0,
"label": "Debit", "label": "Account Currency",
"no_copy": 0, "no_copy": 1,
"oldfieldname": "debit", "options": "Currency",
"oldfieldtype": "Currency", "permlevel": 0,
"options": "Company:company:default_currency", "precision": "",
"permlevel": 0, "print_hide": 1,
"print_hide": 0, "read_only": 1,
"read_only": 0, "report_hide": 0,
"report_hide": 0, "reqd": 0,
"reqd": 0, "search_index": 0,
"search_index": 0, "set_only_once": 0,
"set_only_once": 0,
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "col_break2", "fieldname": "column_break_10",
"fieldtype": "Column Break", "fieldtype": "Column Break",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "precision": "",
"read_only": 0, "print_hide": 0,
"report_hide": 0, "read_only": 0,
"reqd": 0, "report_hide": 0,
"search_index": 0, "reqd": 0,
"set_only_once": 0, "search_index": 0,
"set_only_once": 0,
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "credit", "fieldname": "exchange_rate",
"fieldtype": "Currency", "fieldtype": "Float",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 1, "in_list_view": 0,
"label": "Credit", "label": "Exchange Rate",
"no_copy": 0, "no_copy": 0,
"oldfieldname": "credit", "permlevel": 0,
"oldfieldtype": "Currency", "precision": "",
"options": "Company:company:default_currency", "print_hide": 1,
"permlevel": 0, "read_only": 0,
"print_hide": 0, "report_hide": 0,
"read_only": 0, "reqd": 0,
"report_hide": 0, "search_index": 0,
"reqd": 0, "set_only_once": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "reference", "fieldname": "sec_break1",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Reference", "label": "Amount",
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
"read_only": 0, "read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "reference_type", "fieldname": "debit_in_account_currency",
"fieldtype": "Select", "fieldtype": "Currency",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 1,
"label": "Reference Type", "label": "Debit in Account Currency",
"no_copy": 0, "no_copy": 0,
"options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim", "options": "account_currency",
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
"read_only": 0, "read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "reference_name", "fieldname": "debit",
"fieldtype": "Dynamic Link", "fieldtype": "Currency",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 1, "in_list_view": 0,
"label": "Reference Name", "label": "Debit in Company Currency",
"no_copy": 0, "no_copy": 1,
"options": "reference_type", "oldfieldname": "debit",
"permlevel": 0, "oldfieldtype": "Currency",
"precision": "", "options": "Company:company:default_currency",
"print_hide": 0, "permlevel": 0,
"read_only": 0, "print_hide": 1,
"report_hide": 0, "read_only": 1,
"reqd": 0, "report_hide": 0,
"search_index": 0, "reqd": 0,
"set_only_once": 0, "search_index": 0,
"set_only_once": 0,
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "col_break3", "fieldname": "col_break2",
"fieldtype": "Column Break", "fieldtype": "Column Break",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
"read_only": 0, "read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "is_advance", "fieldname": "credit_in_account_currency",
"fieldtype": "Select", "fieldtype": "Currency",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 1,
"label": "Is Advance", "label": "Credit in Account Currency",
"no_copy": 1, "no_copy": 0,
"oldfieldname": "is_advance", "options": "account_currency",
"oldfieldtype": "Select", "permlevel": 0,
"options": "No\nYes", "precision": "",
"permlevel": 0, "print_hide": 0,
"print_hide": 1, "read_only": 0,
"read_only": 0, "report_hide": 0,
"report_hide": 0, "reqd": 0,
"reqd": 0, "search_index": 0,
"search_index": 0, "set_only_once": 0,
"set_only_once": 0,
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "against_account", "fieldname": "credit",
"fieldtype": "Text", "fieldtype": "Currency",
"hidden": 1, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Against Account", "label": "Credit in Company Currency",
"no_copy": 1, "no_copy": 1,
"oldfieldname": "against_account", "oldfieldname": "credit",
"oldfieldtype": "Text", "oldfieldtype": "Currency",
"permlevel": 0, "options": "Company:company:default_currency",
"print_hide": 1, "permlevel": 0,
"read_only": 0, "print_hide": 1,
"report_hide": 0, "read_only": 1,
"reqd": 0, "report_hide": 0,
"search_index": 0, "reqd": 0,
"set_only_once": 0, "search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "reference",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Reference",
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "reference_type",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Reference Type",
"no_copy": 0,
"options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "reference_name",
"fieldtype": "Dynamic Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Reference Name",
"no_copy": 0,
"options": "reference_type",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "col_break3",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "is_advance",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Is Advance",
"no_copy": 1,
"oldfieldname": "is_advance",
"oldfieldtype": "Select",
"options": "No\nYes",
"permlevel": 0,
"print_hide": 1,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "against_account",
"fieldtype": "Text",
"hidden": 1,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Against Account",
"no_copy": 1,
"oldfieldname": "against_account",
"oldfieldtype": "Text",
"permlevel": 0,
"print_hide": 1,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0 "unique": 0
} }
], ],
"hide_heading": 0, "hide_heading": 0,
"hide_toolbar": 0, "hide_toolbar": 0,
"idx": 1, "idx": 1,
"in_create": 0, "in_create": 0,
"in_dialog": 0, "in_dialog": 0,
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 1,
"modified": "2015-08-17 02:11:33.991361", "modified": "2015-09-11 12:55:59.270539",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Journal Entry Account", "name": "Journal Entry Account",
"owner": "Administrator", "owner": "Administrator",
"permissions": [], "permissions": [],
"read_only": 0, "read_only": 0,
"read_only_onload": 0 "read_only_onload": 0
} }

View File

@@ -7,7 +7,7 @@
"custom": 0, "custom": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master", "document_type": "Setup",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@@ -65,7 +65,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-02-05 05:11:41.346436", "modified": "2015-09-14 02:55:58.003800",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Mode of Payment", "name": "Mode of Payment",
@@ -93,7 +93,7 @@
}, },
{ {
"amend": 0, "amend": 0,
"apply_user_permissions": 1, "apply_user_permissions": 0,
"cancel": 0, "cancel": 0,
"create": 0, "create": 0,
"delete": 0, "delete": 0,

View File

@@ -0,0 +1,12 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
import unittest
# test_records = frappe.get_test_records('Mode of Payment')
class TestModeofPayment(unittest.TestCase):
pass

View File

@@ -44,7 +44,7 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
} }
}); });
}, },
refresh: function() { refresh: function() {
this.frm.disable_save(); this.frm.disable_save();
}, },
@@ -74,21 +74,7 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
doc: me.frm.doc, doc: me.frm.doc,
method: 'get_unreconciled_entries', method: 'get_unreconciled_entries',
callback: function(r, rt) { callback: function(r, rt) {
var invoices = []; me.set_invoice_options();
$.each(me.frm.doc.invoices || [], function(i, row) {
if (row.invoice_number && !inList(invoices, row.invoice_number))
invoices.push(row.invoice_number);
});
frappe.meta.get_docfield("Payment Reconciliation Payment", "invoice_number",
me.frm.doc.name).options = invoices.join("\n");
$.each(me.frm.doc.payments || [], function(i, p) {
if(!inList(invoices, cstr(p.invoice_number))) p.invoice_number = null;
});
refresh_field("payments");
} }
}); });
@@ -98,8 +84,29 @@ erpnext.accounts.PaymentReconciliationController = frappe.ui.form.Controller.ext
var me = this; var me = this;
return this.frm.call({ return this.frm.call({
doc: me.frm.doc, doc: me.frm.doc,
method: 'reconcile' method: 'reconcile',
callback: function(r, rt) {
me.set_invoice_options();
}
}); });
},
set_invoice_options: function() {
var invoices = [];
$.each(me.frm.doc.invoices || [], function(i, row) {
if (row.invoice_number && !inList(invoices, row.invoice_number))
invoices.push(row.invoice_type + " | " + row.invoice_number);
});
frappe.meta.get_docfield("Payment Reconciliation Payment", "invoice_number",
me.frm.doc.name).options = invoices.join("\n");
$.each(me.frm.doc.payments || [], function(i, p) {
if(!inList(invoices, cstr(p.invoice_number))) p.invoice_number = null;
});
refresh_field("payments");
} }
}); });

View File

@@ -130,7 +130,7 @@
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Column Break", "label": "",
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
@@ -362,7 +362,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 1, "issingle": 1,
"istable": 0, "istable": 0,
"modified": "2015-02-05 05:11:42.105088", "modified": "2015-09-21 03:41:24.672227",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Payment Reconciliation", "name": "Payment Reconciliation",

View File

@@ -3,11 +3,8 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import flt from frappe.utils import flt
from frappe import msgprint, _ from frappe import msgprint, _
from frappe.model.document import Document from frappe.model.document import Document
class PaymentReconciliation(Document): class PaymentReconciliation(Document):
@@ -17,7 +14,8 @@ class PaymentReconciliation(Document):
def get_jv_entries(self): def get_jv_entries(self):
self.check_mandatory_to_fetch() self.check_mandatory_to_fetch()
dr_or_cr = "credit" if self.party_type == "Customer" else "debit" dr_or_cr = "credit_in_account_currency" if self.party_type == "Customer" \
else "debit_in_account_currency"
cond = self.check_condition(dr_or_cr) cond = self.check_condition(dr_or_cr)
@@ -68,7 +66,7 @@ class PaymentReconciliation(Document):
def get_invoice_entries(self): def get_invoice_entries(self):
#Fetch JVs, Sales and Purchase Invoices for 'invoices' to reconcile against #Fetch JVs, Sales and Purchase Invoices for 'invoices' to reconcile against
non_reconciled_invoices = [] non_reconciled_invoices = []
dr_or_cr = "debit" if self.party_type == "Customer" else "credit" dr_or_cr = "debit_in_account_currency" if self.party_type == "Customer" else "credit_in_account_currency"
cond = self.check_condition(dr_or_cr) cond = self.check_condition(dr_or_cr)
invoice_list = frappe.db.sql(""" invoice_list = frappe.db.sql("""
@@ -80,6 +78,11 @@ class PaymentReconciliation(Document):
where where
party_type = %(party_type)s and party = %(party)s party_type = %(party_type)s and party = %(party)s
and account = %(account)s and {dr_or_cr} > 0 {cond} and account = %(account)s and {dr_or_cr} > 0 {cond}
and (CASE
WHEN voucher_type = 'Journal Entry'
THEN ifnull(against_voucher, '') = ''
ELSE 1=1
END)
group by voucher_type, voucher_no group by voucher_type, voucher_no
""".format(**{ """.format(**{
"cond": cond, "cond": cond,
@@ -101,13 +104,15 @@ class PaymentReconciliation(Document):
and account = %(account)s and {0} > 0 and account = %(account)s and {0} > 0
and against_voucher_type = %(against_voucher_type)s and against_voucher_type = %(against_voucher_type)s
and ifnull(against_voucher, '') = %(against_voucher)s and ifnull(against_voucher, '') = %(against_voucher)s
""".format("credit" if self.party_type == "Customer" else "debit"), { """.format("credit_in_account_currency" if self.party_type == "Customer"
"party_type": self.party_type, else "debit_in_account_currency"), {
"party": self.party, "party_type": self.party_type,
"account": self.receivable_payable_account, "party": self.party,
"against_voucher_type": d.voucher_type, "account": self.receivable_payable_account,
"against_voucher": d.voucher_no "against_voucher_type": d.voucher_type,
}) "against_voucher": d.voucher_no
}
)
payment_amount = payment_amount[0][0] if payment_amount else 0 payment_amount = payment_amount[0][0] if payment_amount else 0
@@ -135,12 +140,18 @@ class PaymentReconciliation(Document):
ent.outstanding_amount = e.get('outstanding_amount') ent.outstanding_amount = e.get('outstanding_amount')
def reconcile(self, args): def reconcile(self, args):
for e in self.get('payments'):
e.invoice_type = None
if e.invoice_number and " | " in e.invoice_number:
e.invoice_type, e.invoice_number = e.invoice_number.split(" | ")
self.get_invoice_entries() self.get_invoice_entries()
self.validate_invoice() self.validate_invoice()
dr_or_cr = "credit" if self.party_type == "Customer" else "debit" dr_or_cr = "credit_in_account_currency" if self.party_type == "Customer" \
else "debit_in_account_currency"
lst = [] lst = []
for e in self.get('payments'): for e in self.get('payments'):
if e.invoice_type and e.invoice_number and e.allocated_amount: if e.invoice_number and e.allocated_amount:
lst.append({ lst.append({
'voucher_no' : e.journal_entry, 'voucher_no' : e.journal_entry,
'voucher_detail_no' : e.voucher_detail_number, 'voucher_detail_no' : e.voucher_detail_number,

View File

@@ -124,7 +124,7 @@
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Column Break", "label": "",
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
@@ -135,51 +135,6 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "allocated_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Allocated amount",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"default": "Sales Invoice",
"fieldname": "invoice_type",
"fieldtype": "Select",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Invoice Type",
"no_copy": 0,
"options": "\nSales Invoice\nPurchase Invoice\nJournal Entry",
"permlevel": 0,
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@@ -202,6 +157,28 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "allocated_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 1,
"label": "Allocated amount",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@@ -243,27 +220,6 @@
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "col_break2",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Column Break",
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
} }
], ],
"hide_heading": 0, "hide_heading": 0,
@@ -273,7 +229,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 1,
"modified": "2014-12-25 16:26:48.345281", "modified": "2015-09-21 03:39:40.320070",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Payment Reconciliation Payment", "name": "Payment Reconciliation Payment",

View File

@@ -25,8 +25,14 @@ frappe.ui.form.on("Payment Tool", "onload", function(frm) {
}); });
frm.set_query("against_voucher_type", "vouchers", function() { frm.set_query("against_voucher_type", "vouchers", function() {
if (frm.doc.party_type=="Customer") {
var doctypes = ["Sales Order", "Sales Invoice", "Journal Entry"];
} else {
var doctypes = ["Purchase Order", "Purchase Invoice", "Journal Entry"];
}
return { return {
filters: {"name": ["in", ["Sales Invoice", "Purchase Invoice", "Journal Entry", "Sales Order", "Purchase Order"]]} filters: { "name": ["in", doctypes] }
}; };
}); });
}); });
@@ -55,6 +61,25 @@ frappe.ui.form.on("Payment Tool", "party", function(frm) {
} }
}) })
frappe.ui.form.on("Payment Tool", "party_account", function(frm) {
if(frm.doc.party_account) {
frm.call({
method: "frappe.client.get_value",
args: {
doctype: "Account",
fieldname: "account_currency",
filters: { name: frm.doc.party_account },
},
callback: function(r, rt) {
if(r.message) {
frm.set_value("party_account_currency", r.message.account_currency);
erpnext.payment_tool.check_mandatory_to_set_button(frm);
}
}
});
}
})
frappe.ui.form.on("Payment Tool", "company", function(frm) { frappe.ui.form.on("Payment Tool", "company", function(frm) {
erpnext.payment_tool.check_mandatory_to_set_button(frm); erpnext.payment_tool.check_mandatory_to_set_button(frm);
}); });
@@ -63,10 +88,6 @@ frappe.ui.form.on("Payment Tool", "received_or_paid", function(frm) {
erpnext.payment_tool.check_mandatory_to_set_button(frm); erpnext.payment_tool.check_mandatory_to_set_button(frm);
}); });
frappe.ui.form.on("Payment Tool", "party", function(frm) {
erpnext.payment_tool.check_mandatory_to_set_button(frm);
});
// Fetch bank/cash account based on payment mode // Fetch bank/cash account based on payment mode
frappe.ui.form.on("Payment Tool", "payment_mode", function(frm) { frappe.ui.form.on("Payment Tool", "payment_mode", function(frm) {
return frappe.call({ return frappe.call({
@@ -75,7 +96,7 @@ frappe.ui.form.on("Payment Tool", "payment_mode", function(frm) {
"mode_of_payment": frm.doc.payment_mode, "mode_of_payment": frm.doc.payment_mode,
"company": frm.doc.company "company": frm.doc.company
}, },
callback: function(r, rt) { callback: function(r, rt) {
if(r.message) { if(r.message) {
cur_frm.set_value("payment_account", r.message['account']); cur_frm.set_value("payment_account", r.message['account']);
} }
@@ -120,6 +141,10 @@ frappe.ui.form.on("Payment Tool", "get_outstanding_vouchers", function(frm) {
c.against_voucher_no = d.voucher_no; c.against_voucher_no = d.voucher_no;
c.total_amount = d.invoice_amount; c.total_amount = d.invoice_amount;
c.outstanding_amount = d.outstanding_amount; c.outstanding_amount = d.outstanding_amount;
if (frm.doc.set_payment_amount) {
c.payment_amount = d.outstanding_amount;
}
}); });
} }
refresh_field("vouchers"); refresh_field("vouchers");
@@ -130,41 +155,63 @@ frappe.ui.form.on("Payment Tool", "get_outstanding_vouchers", function(frm) {
}); });
// validate against_voucher_type // validate against_voucher_type
frappe.ui.form.on("Payment Tool Detail", "against_voucher_type", function(frm) { frappe.ui.form.on("Payment Tool Detail", "against_voucher_type", function(frm, cdt, cdn) {
erpnext.payment_tool.validate_against_voucher(frm); var row = frappe.model.get_doc(cdt, cdn);
erpnext.payment_tool.validate_against_voucher(frm, row);
}); });
erpnext.payment_tool.validate_against_voucher = function(frm) { erpnext.payment_tool.validate_against_voucher = function(frm, row) {
$.each(frm.doc.vouchers || [], function(i, row) { var _validate = function(i, row) {
if (!row.against_voucher_type) {
return;
}
if(frm.doc.party_type=="Customer" if(frm.doc.party_type=="Customer"
&& !in_list(["Sales Order", "Sales Invoice", "Journal Entry"], row.against_voucher_type)) { && !in_list(["Sales Order", "Sales Invoice", "Journal Entry"], row.against_voucher_type)) {
frappe.model.set_value(row.doctype, row.name, "against_voucher_type", ""); frappe.model.set_value(row.doctype, row.name, "against_voucher_type", "");
frappe.throw(__("Against Voucher Type must be one of Sales Order, Sales Invoice or Journal Entry")) frappe.msgprint(__("Against Voucher Type must be one of Sales Order, Sales Invoice or Journal Entry"));
return false;
} }
if(frm.doc.party_type=="Supplier" if(frm.doc.party_type=="Supplier"
&& !in_list(["Purchase Order", "Purchase Invoice", "Journal Entry"], row.against_voucher_type)) { && !in_list(["Purchase Order", "Purchase Invoice", "Journal Entry"], row.against_voucher_type)) {
frappe.model.set_value(row.doctype, row.name, "against_voucher_type", ""); frappe.model.set_value(row.doctype, row.name, "against_voucher_type", "");
frappe.throw(__("Against Voucher Type must be one of Purchase Order, Purchase Invoice or Journal Entry")) frappe.msgprint(__("Against Voucher Type must be one of Purchase Order, Purchase Invoice or Journal Entry"));
return false;
} }
}); }
if (row) {
_validate(0, row);
} else {
$.each(frm.doc.vouchers || [], _validate);
}
} }
// validate against_voucher_type // validate against_voucher_type
frappe.ui.form.on("Payment Tool Detail", "against_voucher_no", function(frm, cdt, cdn) { frappe.ui.form.on("Payment Tool Detail", "against_voucher_no", function(frm, cdt, cdn) {
var row = locals[cdt][cdn]; var row = locals[cdt][cdn];
if (!row.against_voucher_no) {
return;
}
frappe.call({ frappe.call({
method: 'erpnext.accounts.doctype.payment_tool.payment_tool.get_against_voucher_amount', method: 'erpnext.accounts.doctype.payment_tool.payment_tool.get_against_voucher_amount',
args: { args: {
"against_voucher_type": row.against_voucher_type, "against_voucher_type": row.against_voucher_type,
"against_voucher_no": row.against_voucher_no "against_voucher_no": row.against_voucher_no,
"party_account": frm.doc.party_account,
"company": frm.doc.company
}, },
callback: function(r) { callback: function(r) {
if(!r.exc) { if(!r.exc) {
$.each(r.message, function(k, v) { $.each(r.message, function(k, v) {
frappe.model.set_value(cdt, cdn, k, v); frappe.model.set_value(cdt, cdn, k, v);
}); });
frappe.model.set_value(cdt, cdn, "payment_amount", r.message.outstanding_amount);
} }
} }
}); });
@@ -187,7 +234,7 @@ erpnext.payment_tool.set_total_payment_amount = function(frm) {
} else { } else {
if(row.payment_amount < 0) if(row.payment_amount < 0)
msgprint(__("Row {0}: Payment amount can not be negative", [row.idx])); msgprint(__("Row {0}: Payment amount can not be negative", [row.idx]));
else if(row.payment_amount >= row.outstanding_amount) else if(row.payment_amount > row.outstanding_amount)
msgprint(__("Row {0}: Payment Amount cannot be greater than Outstanding Amount", [__(row.idx)])); msgprint(__("Row {0}: Payment Amount cannot be greater than Outstanding Amount", [__(row.idx)]));
frappe.model.set_value(row.doctype, row.name, "payment_amount", 0.0); frappe.model.set_value(row.doctype, row.name, "payment_amount", 0.0);

View File

@@ -106,7 +106,7 @@
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Column Break 1", "label": "",
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
@@ -162,6 +162,51 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "party_account_currency",
"fieldtype": "Link",
"hidden": 1,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Party Account Currency",
"no_copy": 1,
"options": "Currency",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "set_payment_amount",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Set Payment Amount = Outstanding Amount",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@@ -306,6 +351,7 @@
"in_list_view": 0, "in_list_view": 0,
"label": "Total Payment Amount", "label": "Total Payment Amount",
"no_copy": 0, "no_copy": 0,
"options": "party_account_currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
"read_only": 1, "read_only": 1,
@@ -450,7 +496,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 1, "issingle": 1,
"istable": 0, "istable": 0,
"modified": "2015-06-05 11:17:33.843334", "modified": "2015-10-01 09:43:24.199025",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Payment Tool", "name": "Payment Tool",

View File

@@ -3,10 +3,11 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe import _ from frappe import _, scrub
from frappe.utils import flt from frappe.utils import flt
from frappe.model.document import Document from frappe.model.document import Document
import json import json
from erpnext.accounts.utils import get_account_currency
class PaymentTool(Document): class PaymentTool(Document):
def make_journal_entry(self): def make_journal_entry(self):
@@ -33,15 +34,18 @@ class PaymentTool(Document):
d1.party_type = self.party_type d1.party_type = self.party_type
d1.party = self.party d1.party = self.party
d1.balance = get_balance_on(self.party_account) d1.balance = get_balance_on(self.party_account)
d1.set("debit" if self.received_or_paid=="Paid" else "credit", flt(v.payment_amount)) d1.set("debit_in_account_currency" if self.received_or_paid=="Paid" \
else "credit_in_account_currency", flt(v.payment_amount))
d1.set("reference_type", v.against_voucher_type) d1.set("reference_type", v.against_voucher_type)
d1.set("reference_name", v.against_voucher_no) d1.set("reference_name", v.against_voucher_no)
d1.set('is_advance', 'Yes' if v.against_voucher_type in ['Sales Order', 'Purchase Order'] else 'No') d1.set('is_advance', 'Yes' if v.against_voucher_type in ['Sales Order', 'Purchase Order'] else 'No')
total_payment_amount = flt(total_payment_amount) + flt(d1.debit) - flt(d1.credit) total_payment_amount = flt(total_payment_amount) + \
flt(d1.debit_in_account_currency) - flt(d1.credit_in_account_currency)
d2 = jv.append("accounts") d2 = jv.append("accounts")
d2.account = self.payment_account d2.account = self.payment_account
d2.set('debit' if total_payment_amount < 0 else 'credit', abs(total_payment_amount)) d2.set('debit_in_account_currency' if total_payment_amount < 0 \
else 'credit_in_account_currency', abs(total_payment_amount))
if self.payment_account: if self.payment_account:
d2.balance = get_balance_on(self.payment_account) d2.balance = get_balance_on(self.payment_account)
@@ -56,10 +60,13 @@ def get_outstanding_vouchers(args):
args = json.loads(args) args = json.loads(args)
party_account_currency = get_account_currency(args.get("party_account"))
company_currency = frappe.db.get_value("Company", args.get("company"), "default_currency")
if args.get("party_type") == "Customer" and args.get("received_or_paid") == "Received": if args.get("party_type") == "Customer" and args.get("received_or_paid") == "Received":
amount_query = "ifnull(debit, 0) - ifnull(credit, 0)" amount_query = "ifnull(debit_in_account_currency, 0) - ifnull(credit_in_account_currency, 0)"
elif args.get("party_type") == "Supplier" and args.get("received_or_paid") == "Paid": elif args.get("party_type") == "Supplier" and args.get("received_or_paid") == "Paid":
amount_query = "ifnull(credit, 0) - ifnull(debit, 0)" amount_query = "ifnull(credit_in_account_currency, 0) - ifnull(debit_in_account_currency, 0)"
else: else:
frappe.throw(_("Please enter the Against Vouchers manually")) frappe.throw(_("Please enter the Against Vouchers manually"))
@@ -68,27 +75,34 @@ def get_outstanding_vouchers(args):
args.get("party_type"), args.get("party")) args.get("party_type"), args.get("party"))
# Get all SO / PO which are not fully billed or aginst which full advance not paid # Get all SO / PO which are not fully billed or aginst which full advance not paid
orders_to_be_billed = get_orders_to_be_billed(args.get("party_type"), args.get("party")) orders_to_be_billed = get_orders_to_be_billed(args.get("party_type"), args.get("party"),
party_account_currency, company_currency)
return outstanding_invoices + orders_to_be_billed return outstanding_invoices + orders_to_be_billed
def get_orders_to_be_billed(party_type, party): def get_orders_to_be_billed(party_type, party, party_account_currency, company_currency):
voucher_type = 'Sales Order' if party_type == "Customer" else 'Purchase Order' voucher_type = 'Sales Order' if party_type == "Customer" else 'Purchase Order'
ref_field = "base_grand_total" if party_account_currency == company_currency else "grand_total"
orders = frappe.db.sql(""" orders = frappe.db.sql("""
select select
name as voucher_no, name as voucher_no,
ifnull(base_grand_total, 0) as invoice_amount, ifnull({ref_field}, 0) as invoice_amount,
(ifnull(base_grand_total, 0) - ifnull(advance_paid, 0)) as outstanding_amount, (ifnull({ref_field}, 0) - ifnull(advance_paid, 0)) as outstanding_amount,
transaction_date as posting_date transaction_date as posting_date
from from
`tab%s` `tab{voucher_type}`
where where
%s = %s {party_type} = %s
and docstatus = 1 and docstatus = 1
and ifnull(status, "") != "Stopped" and ifnull(status, "") != "Stopped"
and ifnull(base_grand_total, 0) > ifnull(advance_paid, 0) and ifnull({ref_field}, 0) > ifnull(advance_paid, 0)
and abs(100 - ifnull(per_billed, 0)) > 0.01 and abs(100 - ifnull(per_billed, 0)) > 0.01
""" % (voucher_type, 'customer' if party_type == "Customer" else 'supplier', '%s'), """.format(**{
party, as_dict = True) "ref_field": ref_field,
"voucher_type": voucher_type,
"party_type": scrub(party_type)
}), party, as_dict = True)
order_list = [] order_list = []
for d in orders: for d in orders:
@@ -98,13 +112,19 @@ def get_orders_to_be_billed(party_type, party):
return order_list return order_list
@frappe.whitelist() @frappe.whitelist()
def get_against_voucher_amount(against_voucher_type, against_voucher_no): def get_against_voucher_amount(against_voucher_type, against_voucher_no, party_account, company):
party_account_currency = get_account_currency(party_account)
company_currency = frappe.db.get_value("Company", company, "default_currency")
ref_field = "base_grand_total" if party_account_currency == company_currency else "grand_total"
if against_voucher_type in ["Sales Order", "Purchase Order"]: if against_voucher_type in ["Sales Order", "Purchase Order"]:
select_cond = "base_grand_total as total_amount, ifnull(base_grand_total, 0) - ifnull(advance_paid, 0) as outstanding_amount" select_cond = "{0} as total_amount, ifnull({0}, 0) - ifnull(advance_paid, 0) as outstanding_amount"\
.format(ref_field)
elif against_voucher_type in ["Sales Invoice", "Purchase Invoice"]: elif against_voucher_type in ["Sales Invoice", "Purchase Invoice"]:
select_cond = "base_grand_total as total_amount, outstanding_amount" select_cond = "{0} as total_amount, outstanding_amount".format(ref_field)
elif against_voucher_type == "Journal Entry": elif against_voucher_type == "Journal Entry":
select_cond = "total_debit as total_amount" ref_field = "total_debit" if party_account_currency == company_currency else "total_debit/exchange_rate"
select_cond = "{0} as total_amount".format(ref_field)
details = frappe.db.sql("""select {0} from `tab{1}` where name = %s""" details = frappe.db.sql("""select {0} from `tab{1}` where name = %s"""
.format(select_cond, against_voucher_type), against_voucher_no, as_dict=1) .format(select_cond, against_voucher_type), against_voucher_no, as_dict=1)

View File

@@ -39,7 +39,7 @@ class TestPaymentTool(unittest.TestCase):
"party": "_Test Customer 3", "party": "_Test Customer 3",
"reference_type": "Sales Order", "reference_type": "Sales Order",
"reference_name": so2.name, "reference_name": so2.name,
"credit": 1000, "credit_in_account_currency": 1000,
"is_advance": "Yes" "is_advance": "Yes"
}) })
@@ -67,7 +67,7 @@ class TestPaymentTool(unittest.TestCase):
"party": "_Test Customer 3", "party": "_Test Customer 3",
"reference_type": si2.doctype, "reference_type": si2.doctype,
"reference_name": si2.name, "reference_name": si2.name,
"credit": 561.80 "credit_in_account_currency": 561.80
}) })
pi = self.create_voucher(pi_test_records[0], { pi = self.create_voucher(pi_test_records[0], {
@@ -91,7 +91,7 @@ class TestPaymentTool(unittest.TestCase):
"party": "_Test Customer 3", "party": "_Test Customer 3",
"party_account": "_Test Receivable - _TC", "party_account": "_Test Receivable - _TC",
"payment_mode": "Cheque", "payment_mode": "Cheque",
"payment_account": "_Test Account Bank Account - _TC", "payment_account": "_Test Bank - _TC",
"reference_no": "123456", "reference_no": "123456",
"reference_date": "2013-02-14" "reference_date": "2013-02-14"
} }
@@ -117,10 +117,10 @@ class TestPaymentTool(unittest.TestCase):
def create_against_jv(self, test_record, args): def create_against_jv(self, test_record, args):
jv = frappe.copy_doc(test_record) jv = frappe.copy_doc(test_record)
jv.get("accounts")[0].update(args) jv.get("accounts")[0].update(args)
if args.get("debit"): if args.get("debit_in_account_currency"):
jv.get("accounts")[1].credit = args["debit"] jv.get("accounts")[1].credit_in_account_currency = args["debit_in_account_currency"]
elif args.get("credit"): elif args.get("credit_in_account_currency"):
jv.get("accounts")[1].debit = args["credit"] jv.get("accounts")[1].debit_in_account_currency = args["credit_in_account_currency"]
jv.insert() jv.insert()
jv.submit() jv.submit()
@@ -141,7 +141,8 @@ class TestPaymentTool(unittest.TestCase):
outstanding_entries = get_outstanding_vouchers(json.dumps(args)) outstanding_entries = get_outstanding_vouchers(json.dumps(args))
for d in outstanding_entries: for d in outstanding_entries:
self.assertEquals(flt(d.get("outstanding_amount"), 2), expected_outstanding.get(d.get("voucher_type"))[1]) self.assertEquals(flt(d.get("outstanding_amount"), 2),
expected_outstanding.get(d.get("voucher_type"))[1])
self.check_jv_entries(doc, outstanding_entries, expected_outstanding) self.check_jv_entries(doc, outstanding_entries, expected_outstanding)
@@ -156,11 +157,10 @@ class TestPaymentTool(unittest.TestCase):
paytool.total_payment_amount = 300 paytool.total_payment_amount = 300
new_jv = paytool.make_journal_entry() new_jv = paytool.make_journal_entry()
for jv_entry in new_jv.get("accounts"): for jv_entry in new_jv.get("accounts"):
if paytool.party_account == jv_entry.get("account") and paytool.party == jv_entry.get("party"): if paytool.party_account == jv_entry.get("account") and paytool.party == jv_entry.get("party"):
self.assertEquals(100.00, self.assertEquals(100.00, jv_entry.get("debit_in_account_currency"
jv_entry.get("debit" if paytool.party_type=="Supplier" else "credit")) if paytool.party_type=="Supplier" else "credit_in_account_currency"))
self.assertEquals(jv_entry.reference_name, self.assertEquals(jv_entry.reference_name,
expected_outstanding[jv_entry.reference_type][0]) expected_outstanding[jv_entry.reference_type][0])
@@ -170,4 +170,6 @@ class TestPaymentTool(unittest.TestCase):
def clear_table_entries(self): def clear_table_entries(self):
frappe.db.sql("""delete from `tabGL Entry` where party in ("_Test Customer 3", "_Test Supplier 1")""") frappe.db.sql("""delete from `tabGL Entry` where party in ("_Test Customer 3", "_Test Supplier 1")""")
frappe.db.sql("""delete from `tabSales Order` where customer = "_Test Customer 3" """) frappe.db.sql("""delete from `tabSales Order` where customer = "_Test Customer 3" """)
frappe.db.sql("""delete from `tabSales Invoice` where customer = "_Test Customer 3" """)
frappe.db.sql("""delete from `tabPurchase Order` where supplier = "_Test Supplier 1" """) frappe.db.sql("""delete from `tabPurchase Order` where supplier = "_Test Supplier 1" """)
frappe.db.sql("""delete from `tabPurchase Invoice` where supplier = "_Test Supplier 1" """)

View File

@@ -87,6 +87,7 @@
"in_list_view": 1, "in_list_view": 1,
"label": "Total Amount", "label": "Total Amount",
"no_copy": 0, "no_copy": 0,
"options": "party_account_currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
"read_only": 1, "read_only": 1,
@@ -108,6 +109,7 @@
"in_list_view": 1, "in_list_view": 1,
"label": "Outstanding Amount", "label": "Outstanding Amount",
"no_copy": 0, "no_copy": 0,
"options": "party_account_currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
"read_only": 1, "read_only": 1,
@@ -129,6 +131,7 @@
"in_list_view": 1, "in_list_view": 1,
"label": "Payment Amount", "label": "Payment Amount",
"no_copy": 0, "no_copy": 0,
"options": "party_account_currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
"read_only": 0, "read_only": 0,
@@ -146,7 +149,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 1,
"modified": "2014-09-11 08:55:34.384017", "modified": "2015-08-31 18:58:35.537060",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Payment Tool Detail", "name": "Payment Tool Detail",

View File

@@ -10,11 +10,11 @@ from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journ
class TestPeriodClosingVoucher(unittest.TestCase): class TestPeriodClosingVoucher(unittest.TestCase):
def test_closing_entry(self): def test_closing_entry(self):
make_journal_entry("_Test Account Bank Account - _TC", "Sales - _TC", 400, make_journal_entry("_Test Bank - _TC", "Sales - _TC", 400,
"_Test Cost Center - _TC", submit=True) "_Test Cost Center - _TC", submit=True)
make_journal_entry("_Test Account Cost for Goods Sold - _TC", make_journal_entry("_Test Account Cost for Goods Sold - _TC",
"_Test Account Bank Account - _TC", 600, "_Test Cost Center - _TC", submit=True) "_Test Bank - _TC", 600, "_Test Cost Center - _TC", submit=True)
profit_or_loss = frappe.db.sql("""select sum(ifnull(t1.debit,0))-sum(ifnull(t1.credit,0)) as balance profit_or_loss = frappe.db.sql("""select sum(ifnull(t1.debit,0))-sum(ifnull(t1.credit,0)) as balance
from `tabGL Entry` t1, `tabAccount` t2 from `tabGL Entry` t1, `tabAccount` t2

View File

@@ -622,7 +622,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-08-18 17:49:09.098876", "modified": "2015-09-07 15:51:26",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "POS Profile", "name": "POS Profile",
@@ -650,7 +650,7 @@
}, },
{ {
"amend": 0, "amend": 0,
"apply_user_permissions": 1, "apply_user_permissions": 0,
"cancel": 0, "cancel": 0,
"create": 0, "create": 0,
"delete": 0, "delete": 0,

View File

@@ -41,7 +41,7 @@
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Title", "label": "Title",
"no_copy": 0, "no_copy": 1,
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
@@ -851,7 +851,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-08-13 14:58:29.194326", "modified": "2015-09-11 12:19:52.242771",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Pricing Rule", "name": "Pricing Rule",

View File

@@ -75,8 +75,29 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
me.apply_pricing_rule(); me.apply_pricing_rule();
}) })
}, },
credit_to: function() {
var me = this;
if(this.frm.doc.credit_to) {
me.frm.call({
method: "frappe.client.get_value",
args: {
doctype: "Account",
fieldname: "account_currency",
filters: { name: me.frm.doc.credit_to },
},
callback: function(r, rt) {
if(r.message) {
me.frm.set_value("party_account_currency", r.message.account_currency);
me.set_dynamic_labels();
}
}
});
}
},
write_off_amount: function() { write_off_amount: function() {
this.set_in_company_currency(this.frm.doc, ["write_off_amount"]);
this.calculate_outstanding_amount(); this.calculate_outstanding_amount();
this.frm.refresh_fields(); this.frm.refresh_fields();
}, },

View File

@@ -20,7 +20,7 @@
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Title", "label": "Title",
"no_copy": 0, "no_copy": 1,
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
@@ -576,7 +576,7 @@
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 1, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "items", "fieldname": "items",
@@ -1266,30 +1266,6 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "total_amount_to_pay",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Total Amount To Pay",
"no_copy": 1,
"oldfieldname": "total_amount_to_pay",
"oldfieldtype": "Currency",
"options": "Company:company:default_currency",
"permlevel": 0,
"print_hide": 1,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@@ -1304,7 +1280,7 @@
"no_copy": 1, "no_copy": 1,
"oldfieldname": "total_advance", "oldfieldname": "total_advance",
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
"options": "Company:company:default_currency", "options": "party_account_currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 1, "print_hide": 1,
"read_only": 1, "read_only": 1,
@@ -1328,7 +1304,7 @@
"no_copy": 1, "no_copy": 1,
"oldfieldname": "outstanding_amount", "oldfieldname": "outstanding_amount",
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
"options": "Company:company:default_currency", "options": "party_account_currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 1, "print_hide": 1,
"read_only": 1, "read_only": 1,
@@ -1338,6 +1314,30 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
"collapsible_depends_on": "write_off_amount",
"depends_on": "grand_total",
"fieldname": "write_off",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Write Off",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@@ -1350,7 +1350,7 @@
"in_list_view": 0, "in_list_view": 0,
"label": "Write Off Amount", "label": "Write Off Amount",
"no_copy": 1, "no_copy": 1,
"options": "Company:company:default_currency", "options": "currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 1, "print_hide": 1,
"read_only": 0, "read_only": 0,
@@ -1360,6 +1360,50 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "base_write_off_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Write Off Amount (Company Currency)",
"no_copy": 1,
"options": "Company:company:default_currency",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "column_break_61",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@@ -1406,29 +1450,6 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "against_expense_account",
"fieldtype": "Small Text",
"hidden": 1,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Against Expense Account",
"no_copy": 1,
"oldfieldname": "against_expense_account",
"oldfieldtype": "Small Text",
"permlevel": 0,
"print_hide": 1,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@@ -1748,6 +1769,29 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "party_account_currency",
"fieldtype": "Link",
"hidden": 1,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Party Account Currency",
"no_copy": 1,
"options": "Currency",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@@ -1797,6 +1841,29 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "against_expense_account",
"fieldtype": "Small Text",
"hidden": 1,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Against Expense Account",
"no_copy": 1,
"oldfieldname": "against_expense_account",
"oldfieldtype": "Small Text",
"permlevel": 0,
"print_hide": 1,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@@ -2175,7 +2242,7 @@
"is_submittable": 1, "is_submittable": 1,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-08-27 03:21:24.432106", "modified": "2015-09-30 08:52:56.789115",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Purchase Invoice", "name": "Purchase Invoice",
@@ -2183,7 +2250,7 @@
"permissions": [ "permissions": [
{ {
"amend": 1, "amend": 1,
"apply_user_permissions": 1, "apply_user_permissions": 0,
"cancel": 1, "cancel": 1,
"create": 1, "create": 1,
"delete": 0, "delete": 0,
@@ -2203,7 +2270,7 @@
}, },
{ {
"amend": 0, "amend": 0,
"apply_user_permissions": 1, "apply_user_permissions": 0,
"cancel": 0, "cancel": 0,
"create": 0, "create": 0,
"delete": 0, "delete": 0,
@@ -2223,7 +2290,7 @@
}, },
{ {
"amend": 0, "amend": 0,
"apply_user_permissions": 1, "apply_user_permissions": 0,
"cancel": 0, "cancel": 0,
"create": 0, "create": 0,
"delete": 0, "delete": 0,
@@ -2263,7 +2330,7 @@
}, },
{ {
"amend": 0, "amend": 0,
"apply_user_permissions": 1, "apply_user_permissions": 0,
"cancel": 0, "cancel": 0,
"create": 0, "create": 0,
"delete": 0, "delete": 0,

View File

@@ -10,6 +10,7 @@ import frappe.defaults
from erpnext.controllers.buying_controller import BuyingController from erpnext.controllers.buying_controller import BuyingController
from erpnext.accounts.party import get_party_account, get_due_date from erpnext.accounts.party import get_party_account, get_due_date
from erpnext.accounts.utils import get_account_currency
form_grid_templates = { form_grid_templates = {
"items": "templates/form_grid/item_grid.html" "items": "templates/form_grid/item_grid.html"
@@ -66,7 +67,7 @@ class PurchaseInvoice(BuyingController):
def set_missing_values(self, for_validate=False): def set_missing_values(self, for_validate=False):
if not self.credit_to: if not self.credit_to:
self.credit_to = get_party_account(self.company, self.supplier, "Supplier") self.credit_to = get_party_account("Supplier", self.supplier, self.company)
if not self.due_date: if not self.due_date:
self.due_date = get_due_date(self.posting_date, "Supplier", self.supplier, self.company) self.due_date = get_due_date(self.posting_date, "Supplier", self.supplier, self.company)
@@ -91,7 +92,8 @@ class PurchaseInvoice(BuyingController):
throw(_("Conversion rate cannot be 0 or 1")) throw(_("Conversion rate cannot be 0 or 1"))
def validate_credit_to_acc(self): def validate_credit_to_acc(self):
account = frappe.db.get_value("Account", self.credit_to, ["account_type", "report_type"], as_dict=True) account = frappe.db.get_value("Account", self.credit_to,
["account_type", "report_type", "account_currency"], as_dict=True)
if account.report_type != "Balance Sheet": if account.report_type != "Balance Sheet":
frappe.throw(_("Credit To account must be a Balance Sheet account")) frappe.throw(_("Credit To account must be a Balance Sheet account"))
@@ -99,6 +101,8 @@ class PurchaseInvoice(BuyingController):
if self.supplier and account.account_type != "Payable": if self.supplier and account.account_type != "Payable":
frappe.throw(_("Credit To account must be a Payable account")) frappe.throw(_("Credit To account must be a Payable account"))
self.party_account_currency = account.account_currency
def check_for_stopped_status(self): def check_for_stopped_status(self):
check_list = [] check_list = []
for d in self.get('items'): for d in self.get('items'):
@@ -213,7 +217,7 @@ class PurchaseInvoice(BuyingController):
'party_type': 'Supplier', 'party_type': 'Supplier',
'party': self.supplier, 'party': self.supplier,
'is_advance' : 'Yes', 'is_advance' : 'Yes',
'dr_or_cr' : 'debit', 'dr_or_cr' : 'debit_in_account_currency',
'unadjusted_amt' : flt(d.advance_amount), 'unadjusted_amt' : flt(d.advance_amount),
'allocated_amt' : flt(d.allocated_amount) 'allocated_amt' : flt(d.allocated_amount)
} }
@@ -257,26 +261,32 @@ class PurchaseInvoice(BuyingController):
"party_type": "Supplier", "party_type": "Supplier",
"party": self.supplier, "party": self.supplier,
"against": self.against_expense_account, "against": self.against_expense_account,
"credit": self.total_amount_to_pay, "credit": self.base_grand_total,
"remarks": self.remarks, "credit_in_account_currency": self.base_grand_total \
if self.party_account_currency==self.company_currency else self.grand_total,
"against_voucher": self.return_against if cint(self.is_return) else self.name, "against_voucher": self.return_against if cint(self.is_return) else self.name,
"against_voucher_type": self.doctype, "against_voucher_type": self.doctype,
}) }, self.party_account_currency)
) )
# tax table gl entries # tax table gl entries
valuation_tax = {} valuation_tax = {}
for tax in self.get("taxes"): for tax in self.get("taxes"):
if tax.category in ("Total", "Valuation and Total") and flt(tax.base_tax_amount_after_discount_amount): if tax.category in ("Total", "Valuation and Total") and flt(tax.base_tax_amount_after_discount_amount):
account_currency = get_account_currency(tax.account_head)
dr_or_cr = "debit" if tax.add_deduct_tax == "Add" else "credit"
gl_entries.append( gl_entries.append(
self.get_gl_dict({ self.get_gl_dict({
"account": tax.account_head, "account": tax.account_head,
"against": self.supplier, "against": self.supplier,
"debit": tax.add_deduct_tax == "Add" and tax.base_tax_amount_after_discount_amount or 0, dr_or_cr: tax.base_tax_amount_after_discount_amount,
"credit": tax.add_deduct_tax == "Deduct" and tax.base_tax_amount_after_discount_amount or 0, dr_or_cr + "_in_account_currency": tax.base_tax_amount_after_discount_amount \
"remarks": self.remarks, if account_currency==self.company_currency \
else tax.tax_amount_after_discount_amount,
"cost_center": tax.cost_center "cost_center": tax.cost_center
}) }, account_currency)
) )
# accumulate valuation tax # accumulate valuation tax
@@ -292,14 +302,16 @@ class PurchaseInvoice(BuyingController):
stock_items = self.get_stock_items() stock_items = self.get_stock_items()
for item in self.get("items"): for item in self.get("items"):
if flt(item.base_net_amount): if flt(item.base_net_amount):
account_currency = get_account_currency(item.expense_account)
gl_entries.append( gl_entries.append(
self.get_gl_dict({ self.get_gl_dict({
"account": item.expense_account, "account": item.expense_account,
"against": self.supplier, "against": self.supplier,
"debit": item.base_net_amount, "debit": item.base_net_amount,
"remarks": self.remarks, "debit_in_account_currency": item.base_net_amount \
if account_currency==self.company_currency else item.net_amount,
"cost_center": item.cost_center "cost_center": item.cost_center
}) }, account_currency)
) )
if auto_accounting_for_stock and self.is_opening == "No" and \ if auto_accounting_for_stock and self.is_opening == "No" and \
@@ -352,12 +364,28 @@ class PurchaseInvoice(BuyingController):
# writeoff account includes petty difference in the invoice amount # writeoff account includes petty difference in the invoice amount
# and the amount that is paid # and the amount that is paid
if self.write_off_account and flt(self.write_off_amount): if self.write_off_account and flt(self.write_off_amount):
write_off_account_currency = get_account_currency(self.write_off_account)
gl_entries.append(
self.get_gl_dict({
"account": self.credit_to,
"party_type": "Supplier",
"party": self.supplier,
"against": self.write_off_account,
"debit": self.base_write_off_amount,
"debit_in_account_currency": self.base_write_off_amount \
if self.party_account_currency==self.company_currency else self.write_off_amount,
"against_voucher": self.return_against if cint(self.is_return) else self.name,
"against_voucher_type": self.doctype,
}, self.party_account_currency)
)
gl_entries.append( gl_entries.append(
self.get_gl_dict({ self.get_gl_dict({
"account": self.write_off_account, "account": self.write_off_account,
"against": self.supplier, "against": self.supplier,
"credit": flt(self.write_off_amount), "credit": flt(self.base_write_off_amount),
"remarks": self.remarks, "credit_in_account_currency": self.base_write_off_amount \
if write_off_account_currency==self.company_currency else self.write_off_amount,
"cost_center": self.write_off_cost_center "cost_center": self.write_off_cost_center
}) })
) )

View File

@@ -10,6 +10,7 @@ from frappe.utils import cint
import frappe.defaults import frappe.defaults
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory, \ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory, \
test_records as pr_test_records test_records as pr_test_records
from erpnext.exceptions import InvalidCurrency
test_dependencies = ["Item", "Cost Center"] test_dependencies = ["Item", "Cost Center"]
test_ignore = ["Serial No"] test_ignore = ["Serial No"]
@@ -218,7 +219,8 @@ class TestPurchaseInvoice(unittest.TestCase):
pi.load_from_db() pi.load_from_db()
self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account` self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account`
where reference_type='Purchase Invoice' and reference_name=%s and debit=300""", pi.name)) where reference_type='Purchase Invoice'
and reference_name=%s and debit_in_account_currency=300""", pi.name))
self.assertEqual(pi.outstanding_amount, 1212.30) self.assertEqual(pi.outstanding_amount, 1212.30)
@@ -235,17 +237,17 @@ class TestPurchaseInvoice(unittest.TestCase):
existing_purchase_cost = frappe.db.sql("""select sum(ifnull(base_net_amount, 0)) existing_purchase_cost = frappe.db.sql("""select sum(ifnull(base_net_amount, 0))
from `tabPurchase Invoice Item` where project_name = '_Test Project' and docstatus=1""") from `tabPurchase Invoice Item` where project_name = '_Test Project' and docstatus=1""")
existing_purchase_cost = existing_purchase_cost and existing_purchase_cost[0][0] or 0 existing_purchase_cost = existing_purchase_cost and existing_purchase_cost[0][0] or 0
pi = make_purchase_invoice(currency="USD", conversion_rate=60, project_name="_Test Project") pi = make_purchase_invoice(currency="USD", conversion_rate=60, project_name="_Test Project")
self.assertEqual(frappe.db.get_value("Project", "_Test Project", "total_purchase_cost"), self.assertEqual(frappe.db.get_value("Project", "_Test Project", "total_purchase_cost"),
existing_purchase_cost + 15000) existing_purchase_cost + 15000)
pi1 = make_purchase_invoice(qty=10, project_name="_Test Project") pi1 = make_purchase_invoice(qty=10, project_name="_Test Project")
self.assertEqual(frappe.db.get_value("Project", "_Test Project", "total_purchase_cost"), self.assertEqual(frappe.db.get_value("Project", "_Test Project", "total_purchase_cost"),
existing_purchase_cost + 15500) existing_purchase_cost + 15500)
pi1.cancel() pi1.cancel()
self.assertEqual(frappe.db.get_value("Project", "_Test Project", "total_purchase_cost"), self.assertEqual(frappe.db.get_value("Project", "_Test Project", "total_purchase_cost"),
existing_purchase_cost + 15000) existing_purchase_cost + 15000)
pi.cancel() pi.cancel()
@@ -277,6 +279,55 @@ class TestPurchaseInvoice(unittest.TestCase):
set_perpetual_inventory(0) set_perpetual_inventory(0)
def test_multi_currency_gle(self):
set_perpetual_inventory(0)
pi = make_purchase_invoice(supplier="_Test Supplier USD", credit_to="_Test Payable USD - _TC",
currency="USD", conversion_rate=50)
gl_entries = frappe.db.sql("""select account, account_currency, debit, credit,
debit_in_account_currency, credit_in_account_currency
from `tabGL Entry` where voucher_type='Purchase Invoice' and voucher_no=%s
order by account asc""", pi.name, as_dict=1)
self.assertTrue(gl_entries)
expected_values = {
"_Test Payable USD - _TC": {
"account_currency": "USD",
"debit": 0,
"debit_in_account_currency": 0,
"credit": 12500,
"credit_in_account_currency": 250
},
"_Test Account Cost for Goods Sold - _TC": {
"account_currency": "INR",
"debit": 12500,
"debit_in_account_currency": 12500,
"credit": 0,
"credit_in_account_currency": 0
}
}
for field in ("account_currency", "debit", "debit_in_account_currency", "credit", "credit_in_account_currency"):
for i, gle in enumerate(gl_entries):
self.assertEquals(expected_values[gle.account][field], gle[field])
# Check for valid currency
pi1 = make_purchase_invoice(supplier="_Test Supplier USD", credit_to="_Test Payable USD - _TC",
do_not_save=True)
self.assertRaises(InvalidCurrency, pi1.save)
# cancel
pi.cancel()
gle = frappe.db.sql("""select name from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s""", pi.name)
self.assertFalse(gle)
def make_purchase_invoice(**args): def make_purchase_invoice(**args):
pi = frappe.new_doc("Purchase Invoice") pi = frappe.new_doc("Purchase Invoice")
args = frappe._dict(args) args = frappe._dict(args)

View File

@@ -141,6 +141,9 @@
"supplier": "_Test Supplier", "supplier": "_Test Supplier",
"supplier_name": "_Test Supplier" "supplier_name": "_Test Supplier"
}, },
{ {
"bill_no": "NA", "bill_no": "NA",
"buying_price_list": "_Test Price List", "buying_price_list": "_Test Price List",

View File

@@ -117,7 +117,7 @@
"no_copy": 1, "no_copy": 1,
"oldfieldname": "advance_amount", "oldfieldname": "advance_amount",
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
"options": "Company:company:default_currency", "options": "party_account_currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
"print_width": "100px", "print_width": "100px",
@@ -143,7 +143,7 @@
"no_copy": 1, "no_copy": 1,
"oldfieldname": "allocated_amount", "oldfieldname": "allocated_amount",
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
"options": "Company:company:default_currency", "options": "party_account_currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
"print_width": "100px", "print_width": "100px",
@@ -164,7 +164,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 1,
"modified": "2014-12-25 16:29:15.176476", "modified": "2015-08-25 17:51:30.274069",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Purchase Invoice Advance", "name": "Purchase Invoice Advance",

View File

@@ -504,7 +504,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 1,
"modified": "2015-08-19 12:46:32.687299", "modified": "2015-08-28 02:57:08.769473",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Purchase Taxes and Charges", "name": "Purchase Taxes and Charges",

View File

@@ -8,7 +8,7 @@
"description": "Standard tax template that can be applied to all Purchase Transactions. This template can contain list of tax heads and also other expense heads like \"Shipping\", \"Insurance\", \"Handling\" etc.\n\n#### Note\n\nThe tax rate you define here will be the standard tax rate for all **Items**. If there are **Items** that have different rates, they must be added in the **Item Tax** table in the **Item** master.\n\n#### Description of Columns\n\n1. Calculation Type: \n - This can be on **Net Total** (that is the sum of basic amount).\n - **On Previous Row Total / Amount** (for cumulative taxes or charges). If you select this option, the tax will be applied as a percentage of the previous row (in the tax table) amount or total.\n - **Actual** (as mentioned).\n2. Account Head: The Account ledger under which this tax will be booked\n3. Cost Center: If the tax / charge is an income (like shipping) or expense it needs to be booked against a Cost Center.\n4. Description: Description of the tax (that will be printed in invoices / quotes).\n5. Rate: Tax rate.\n6. Amount: Tax amount.\n7. Total: Cumulative total to this point.\n8. Enter Row: If based on \"Previous Row Total\" you can select the row number which will be taken as a base for this calculation (default is the previous row).\n9. Consider Tax or Charge for: In this section you can specify if the tax / charge is only for valuation (not a part of total) or only for total (does not add value to the item) or for both.\n10. Add or Deduct: Whether you want to add or deduct the tax.", "description": "Standard tax template that can be applied to all Purchase Transactions. This template can contain list of tax heads and also other expense heads like \"Shipping\", \"Insurance\", \"Handling\" etc.\n\n#### Note\n\nThe tax rate you define here will be the standard tax rate for all **Items**. If there are **Items** that have different rates, they must be added in the **Item Tax** table in the **Item** master.\n\n#### Description of Columns\n\n1. Calculation Type: \n - This can be on **Net Total** (that is the sum of basic amount).\n - **On Previous Row Total / Amount** (for cumulative taxes or charges). If you select this option, the tax will be applied as a percentage of the previous row (in the tax table) amount or total.\n - **Actual** (as mentioned).\n2. Account Head: The Account ledger under which this tax will be booked\n3. Cost Center: If the tax / charge is an income (like shipping) or expense it needs to be booked against a Cost Center.\n4. Description: Description of the tax (that will be printed in invoices / quotes).\n5. Rate: Tax rate.\n6. Amount: Tax amount.\n7. Total: Cumulative total to this point.\n8. Enter Row: If based on \"Previous Row Total\" you can select the row number which will be taken as a base for this calculation (default is the previous row).\n9. Consider Tax or Charge for: In this section you can specify if the tax / charge is only for valuation (not a part of total) or only for total (does not add value to the item) or for both.\n10. Add or Deduct: Whether you want to add or deduct the tax.",
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master", "document_type": "Setup",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@@ -21,7 +21,7 @@
"in_filter": 1, "in_filter": 1,
"in_list_view": 0, "in_list_view": 0,
"label": "Title", "label": "Title",
"no_copy": 0, "no_copy": 1,
"oldfieldname": "title", "oldfieldname": "title",
"oldfieldtype": "Data", "oldfieldtype": "Data",
"permlevel": 0, "permlevel": 0,
@@ -176,7 +176,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-05-06 08:52:01.499434", "modified": "2015-09-11 12:19:53.741725",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Purchase Taxes and Charges Template", "name": "Purchase Taxes and Charges Template",

View File

@@ -36,6 +36,11 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
refresh: function(doc, dt, dn) { refresh: function(doc, dt, dn) {
this._super(); this._super();
if(cur_frm.msgbox && cur_frm.msgbox.$wrapper.is(":visible")) {
// hide new msgbox
cur_frm.msgbox.hide();
}
cur_frm.dashboard.reset(); cur_frm.dashboard.reset();
this.frm.toggle_reqd("due_date", !this.frm.doc.is_return); this.frm.toggle_reqd("due_date", !this.frm.doc.is_return);
@@ -146,7 +151,9 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
method: "set_missing_values", method: "set_missing_values",
callback: function(r) { callback: function(r) {
if(!r.exc) { if(!r.exc) {
cur_frm.pos_print_format = r.message.print_format; if(r.message && r.message.print_format) {
cur_frm.pos_print_format = r.message.print_format;
}
cur_frm.doc.__missing_values_set = true; cur_frm.doc.__missing_values_set = true;
me.frm.script_manager.trigger("update_stock"); me.frm.script_manager.trigger("update_stock");
frappe.model.set_default_values(me.frm.doc); frappe.model.set_default_values(me.frm.doc);
@@ -176,6 +183,26 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
}) })
}, },
debit_to: function() {
var me = this;
if(this.frm.doc.debit_to) {
me.frm.call({
method: "frappe.client.get_value",
args: {
doctype: "Account",
fieldname: "account_currency",
filters: { name: me.frm.doc.debit_to },
},
callback: function(r, rt) {
if(r.message) {
me.frm.set_value("party_account_currency", r.message.account_currency);
me.set_dynamic_labels();
}
}
});
}
},
allocated_amount: function() { allocated_amount: function() {
this.calculate_total_advance(); this.calculate_total_advance();
this.frm.refresh_fields(); this.frm.refresh_fields();
@@ -183,10 +210,10 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
write_off_outstanding_amount_automatically: function() { write_off_outstanding_amount_automatically: function() {
if(cint(this.frm.doc.write_off_outstanding_amount_automatically)) { if(cint(this.frm.doc.write_off_outstanding_amount_automatically)) {
frappe.model.round_floats_in(this.frm.doc, ["base_grand_total", "paid_amount"]); frappe.model.round_floats_in(this.frm.doc, ["grand_total", "paid_amount"]);
// this will make outstanding amount 0 // this will make outstanding amount 0
this.frm.set_value("write_off_amount", this.frm.set_value("write_off_amount",
flt(this.frm.doc.base_grand_total - this.frm.doc.paid_amount - this.frm.doc.total_advance, precision("write_off_amount")) flt(this.frm.doc.grand_total - this.frm.doc.paid_amount - this.frm.doc.total_advance, precision("write_off_amount"))
); );
this.frm.toggle_enable("write_off_amount", false); this.frm.toggle_enable("write_off_amount", false);
@@ -199,10 +226,12 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
}, },
write_off_amount: function() { write_off_amount: function() {
this.set_in_company_currency(this.frm.doc, ["write_off_amount"]);
this.write_off_outstanding_amount_automatically(); this.write_off_outstanding_amount_automatically();
}, },
paid_amount: function() { paid_amount: function() {
this.set_in_company_currency(this.frm.doc, ["paid_amount"]);
this.write_off_outstanding_amount_automatically(); this.write_off_outstanding_amount_automatically();
}, },
@@ -395,9 +424,9 @@ cur_frm.cscript.on_submit = function(doc, cdt, cdn) {
}) })
if(cur_frm.doc.is_pos) { if(cur_frm.doc.is_pos) {
frappe.msgprint('<a class="btn btn-primary" \ cur_frm.msgbox = frappe.msgprint('<a class="btn btn-primary" \
onclick="cur_frm.print_preview.printit(true)" style="margin-right: 5px;">Print</a>\ onclick="cur_frm.print_preview.printit(true)" style="margin-right: 5px;">Print</a>\
<a class="btn btn-default" href="#Form/Sales Invoice/New">New</a>'); <a class="btn btn-default" href="#Form/Sales Invoice/New Sales Invoice">New</a>');
} else if(cint(frappe.boot.notification_settings.sales_invoice)) { } else if(cint(frappe.boot.notification_settings.sales_invoice)) {
cur_frm.email_doc(frappe.boot.notification_settings.sales_invoice_message); cur_frm.email_doc(frappe.boot.notification_settings.sales_invoice_message);

View File

@@ -44,7 +44,7 @@
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Title", "label": "Title",
"no_copy": 0, "no_copy": 1,
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
@@ -671,7 +671,7 @@
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 1, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "items", "fieldname": "items",
@@ -1002,7 +1002,7 @@
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 1, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "taxes", "fieldname": "taxes",
@@ -1145,7 +1145,7 @@
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Discount", "label": "Additional Discount",
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
@@ -1448,7 +1448,7 @@
"no_copy": 0, "no_copy": 0,
"oldfieldname": "total_advance", "oldfieldname": "total_advance",
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
"options": "Company:company:default_currency", "options": "party_account_currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 1, "print_hide": 1,
"read_only": 1, "read_only": 1,
@@ -1472,7 +1472,7 @@
"no_copy": 1, "no_copy": 1,
"oldfieldname": "outstanding_amount", "oldfieldname": "outstanding_amount",
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
"options": "Company:company:default_currency", "options": "party_account_currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 1, "print_hide": 1,
"read_only": 1, "read_only": 1,
@@ -1663,7 +1663,7 @@
"no_copy": 1, "no_copy": 1,
"oldfieldname": "paid_amount", "oldfieldname": "paid_amount",
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
"options": "Company:company:default_currency", "options": "currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 1, "print_hide": 1,
"read_only": 0, "read_only": 0,
@@ -1673,6 +1673,29 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "base_paid_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Paid Amount (Company Currency)",
"no_copy": 1,
"options": "Company:company:default_currency",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@@ -1710,7 +1733,7 @@
"in_list_view": 0, "in_list_view": 0,
"label": "Write Off Amount", "label": "Write Off Amount",
"no_copy": 1, "no_copy": 1,
"options": "Company:company:default_currency", "options": "currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 1, "print_hide": 1,
"read_only": 0, "read_only": 0,
@@ -1720,6 +1743,29 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "base_write_off_amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Write Off Amount (Company Currency)",
"no_copy": 1,
"options": "Company:company:default_currency",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@@ -2227,6 +2273,29 @@
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "party_account_currency",
"fieldtype": "Link",
"hidden": 1,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Party Account Currency",
"no_copy": 1,
"options": "Currency",
"permlevel": 0,
"precision": "",
"print_hide": 1,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
@@ -2528,7 +2597,7 @@
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 1,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "sales_team", "fieldname": "sales_team",
@@ -2882,7 +2951,7 @@
"is_submittable": 1, "is_submittable": 1,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-08-27 03:29:14.270731", "modified": "2015-09-30 08:52:53.679566",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Sales Invoice", "name": "Sales Invoice",
@@ -2910,7 +2979,7 @@
}, },
{ {
"amend": 1, "amend": 1,
"apply_user_permissions": 1, "apply_user_permissions": 0,
"cancel": 0, "cancel": 0,
"create": 1, "create": 1,
"delete": 0, "delete": 0,
@@ -2930,7 +2999,7 @@
}, },
{ {
"amend": 0, "amend": 0,
"apply_user_permissions": 1, "apply_user_permissions": 0,
"cancel": 0, "cancel": 0,
"create": 0, "create": 0,
"delete": 0, "delete": 0,

View File

@@ -11,6 +11,7 @@ from erpnext.controllers.stock_controller import update_gl_entries_after
from frappe.model.mapper import get_mapped_doc from frappe.model.mapper import get_mapped_doc
from erpnext.controllers.selling_controller import SellingController from erpnext.controllers.selling_controller import SellingController
from erpnext.accounts.utils import get_account_currency
form_grid_templates = { form_grid_templates = {
"items": "templates/form_grid/item_grid.html" "items": "templates/form_grid/item_grid.html"
@@ -35,6 +36,15 @@ class SalesInvoice(SellingController):
'overflow_type': 'billing' 'overflow_type': 'billing'
}] }]
def set_indicator(self):
"""Set indicator for portal"""
if self.outstanding_amount > 0:
self.indicator_color = "orange"
self.indicator_title = _("Unpaid")
else:
self.indicator_color = "green"
self.indicator_title = _("Paid")
def validate(self): def validate(self):
super(SalesInvoice, self).validate() super(SalesInvoice, self).validate()
self.validate_posting_time() self.validate_posting_time()
@@ -88,13 +98,13 @@ class SalesInvoice(SellingController):
self.update_status_updater_args() self.update_status_updater_args()
self.update_prevdoc_status() self.update_prevdoc_status()
# this sequence because outstanding may get -ve
self.make_gl_entries()
if not self.is_return: if not self.is_return:
self.update_billing_status_for_zero_amount_refdoc("Sales Order") self.update_billing_status_for_zero_amount_refdoc("Sales Order")
self.check_credit_limit() self.check_credit_limit()
# this sequence because outstanding may get -ve
self.make_gl_entries()
if not cint(self.is_pos) == 1 and not self.is_return: if not cint(self.is_pos) == 1 and not self.is_return:
self.update_against_document_in_jv() self.update_against_document_in_jv()
@@ -162,11 +172,22 @@ class SalesInvoice(SellingController):
} }
]) ])
def check_credit_limit(self):
from erpnext.selling.doctype.customer.customer import check_credit_limit
validate_against_credit_limit = False
for d in self.get("items"):
if not (d.sales_order or d.delivery_note):
validate_against_credit_limit = True
break
if validate_against_credit_limit:
check_credit_limit(self.customer, self.company)
def set_missing_values(self, for_validate=False): def set_missing_values(self, for_validate=False):
pos = self.set_pos_fields(for_validate) pos = self.set_pos_fields(for_validate)
if not self.debit_to: if not self.debit_to:
self.debit_to = get_party_account(self.company, self.customer, "Customer") self.debit_to = get_party_account("Customer", self.customer, self.company)
if not self.due_date and self.customer: if not self.due_date and self.customer:
self.due_date = get_due_date(self.posting_date, "Customer", self.customer, self.company) self.due_date = get_due_date(self.posting_date, "Customer", self.customer, self.company)
@@ -260,7 +281,7 @@ class SalesInvoice(SellingController):
'party_type': 'Customer', 'party_type': 'Customer',
'party': self.customer, 'party': self.customer,
'is_advance' : 'Yes', 'is_advance' : 'Yes',
'dr_or_cr' : 'credit', 'dr_or_cr' : 'credit_in_account_currency',
'unadjusted_amt' : flt(d.advance_amount), 'unadjusted_amt' : flt(d.advance_amount),
'allocated_amt' : flt(d.allocated_amount) 'allocated_amt' : flt(d.allocated_amount)
} }
@@ -271,7 +292,8 @@ class SalesInvoice(SellingController):
reconcile_against_document(lst) reconcile_against_document(lst)
def validate_debit_to_acc(self): def validate_debit_to_acc(self):
account = frappe.db.get_value("Account", self.debit_to, ["account_type", "report_type"], as_dict=True) account = frappe.db.get_value("Account", self.debit_to,
["account_type", "report_type", "account_currency"], as_dict=True)
if account.report_type != "Balance Sheet": if account.report_type != "Balance Sheet":
frappe.throw(_("Debit To account must be a Balance Sheet account")) frappe.throw(_("Debit To account must be a Balance Sheet account"))
@@ -279,6 +301,8 @@ class SalesInvoice(SellingController):
if self.customer and account.account_type != "Receivable": if self.customer and account.account_type != "Receivable":
frappe.throw(_("Debit To account must be a Receivable account")) frappe.throw(_("Debit To account must be a Receivable account"))
self.party_account_currency = account.account_currency
def validate_fixed_asset_account(self): def validate_fixed_asset_account(self):
"""Validate Fixed Asset and whether Income Account Entered Exists""" """Validate Fixed Asset and whether Income Account Entered Exists"""
for d in self.get('items'): for d in self.get('items'):
@@ -424,7 +448,7 @@ class SalesInvoice(SellingController):
if flt(self.paid_amount) == 0: if flt(self.paid_amount) == 0:
if self.cash_bank_account: if self.cash_bank_account:
frappe.db.set(self, 'paid_amount', frappe.db.set(self, 'paid_amount',
(flt(self.base_grand_total) - flt(self.write_off_amount))) flt(flt(self.grand_total) - flt(self.write_off_amount), self.precision("paid_amount")))
else: else:
# show message that the amount is not paid # show message that the amount is not paid
frappe.db.set(self,'paid_amount',0) frappe.db.set(self,'paid_amount',0)
@@ -432,6 +456,9 @@ class SalesInvoice(SellingController):
else: else:
frappe.db.set(self,'paid_amount',0) frappe.db.set(self,'paid_amount',0)
frappe.db.set(self, 'base_paid_amount',
flt(self.paid_amount*self.conversion_rate, self.precision("base_paid_amount")))
def check_prev_docstatus(self): def check_prev_docstatus(self):
for d in self.get('items'): for d in self.get('items'):
if d.sales_order and frappe.db.get_value("Sales Order", d.sales_order, "docstatus") != 1: if d.sales_order and frappe.db.get_value("Sales Order", d.sales_order, "docstatus") != 1:
@@ -487,7 +514,7 @@ class SalesInvoice(SellingController):
return gl_entries return gl_entries
def make_customer_gl_entry(self, gl_entries): def make_customer_gl_entry(self, gl_entries):
if self.base_grand_total: if self.grand_total:
gl_entries.append( gl_entries.append(
self.get_gl_dict({ self.get_gl_dict({
"account": self.debit_to, "account": self.debit_to,
@@ -495,37 +522,42 @@ class SalesInvoice(SellingController):
"party": self.customer, "party": self.customer,
"against": self.against_income_account, "against": self.against_income_account,
"debit": self.base_grand_total, "debit": self.base_grand_total,
"remarks": self.remarks, "debit_in_account_currency": self.base_grand_total \
if self.party_account_currency==self.company_currency else self.grand_total,
"against_voucher": self.return_against if cint(self.is_return) else self.name, "against_voucher": self.return_against if cint(self.is_return) else self.name,
"against_voucher_type": self.doctype "against_voucher_type": self.doctype
}) }, self.party_account_currency)
) )
def make_tax_gl_entries(self, gl_entries): def make_tax_gl_entries(self, gl_entries):
for tax in self.get("taxes"): for tax in self.get("taxes"):
if flt(tax.base_tax_amount_after_discount_amount): if flt(tax.base_tax_amount_after_discount_amount):
account_currency = get_account_currency(tax.account_head)
gl_entries.append( gl_entries.append(
self.get_gl_dict({ self.get_gl_dict({
"account": tax.account_head, "account": tax.account_head,
"against": self.customer, "against": self.customer,
"credit": flt(tax.base_tax_amount_after_discount_amount), "credit": flt(tax.base_tax_amount_after_discount_amount),
"remarks": self.remarks, "credit_in_account_currency": flt(tax.base_tax_amount_after_discount_amount) \
if account_currency==self.company_currency else flt(tax.tax_amount_after_discount_amount),
"cost_center": tax.cost_center "cost_center": tax.cost_center
}) }, account_currency)
) )
def make_item_gl_entries(self, gl_entries): def make_item_gl_entries(self, gl_entries):
# income account gl entries # income account gl entries
for item in self.get("items"): for item in self.get("items"):
if flt(item.base_net_amount): if flt(item.base_net_amount):
account_currency = get_account_currency(item.income_account)
gl_entries.append( gl_entries.append(
self.get_gl_dict({ self.get_gl_dict({
"account": item.income_account, "account": item.income_account,
"against": self.customer, "against": self.customer,
"credit": item.base_net_amount, "credit": item.base_net_amount,
"remarks": self.remarks, "credit_in_account_currency": item.base_net_amount \
if account_currency==self.company_currency else item.net_amount,
"cost_center": item.cost_center "cost_center": item.cost_center
}) }, account_currency)
) )
# expense account gl entries # expense account gl entries
@@ -535,6 +567,7 @@ class SalesInvoice(SellingController):
def make_pos_gl_entries(self, gl_entries): def make_pos_gl_entries(self, gl_entries):
if cint(self.is_pos) and self.cash_bank_account and self.paid_amount: if cint(self.is_pos) and self.cash_bank_account and self.paid_amount:
bank_account_currency = get_account_currency(self.cash_bank_account)
# POS, make payment entries # POS, make payment entries
gl_entries.append( gl_entries.append(
self.get_gl_dict({ self.get_gl_dict({
@@ -542,44 +575,50 @@ class SalesInvoice(SellingController):
"party_type": "Customer", "party_type": "Customer",
"party": self.customer, "party": self.customer,
"against": self.cash_bank_account, "against": self.cash_bank_account,
"credit": self.paid_amount, "credit": self.base_paid_amount,
"remarks": self.remarks, "credit_in_account_currency": self.base_paid_amount \
if self.party_account_currency==self.company_currency else self.paid_amount,
"against_voucher": self.return_against if cint(self.is_return) else self.name, "against_voucher": self.return_against if cint(self.is_return) else self.name,
"against_voucher_type": self.doctype, "against_voucher_type": self.doctype,
}) }, self.party_account_currency)
) )
gl_entries.append( gl_entries.append(
self.get_gl_dict({ self.get_gl_dict({
"account": self.cash_bank_account, "account": self.cash_bank_account,
"against": self.customer, "against": self.customer,
"debit": self.paid_amount, "debit": self.base_paid_amount,
"remarks": self.remarks, "debit_in_account_currency": self.base_paid_amount \
}) if bank_account_currency==self.company_currency else self.paid_amount
}, bank_account_currency)
) )
def make_write_off_gl_entry(self, gl_entries): def make_write_off_gl_entry(self, gl_entries):
# write off entries, applicable if only pos # write off entries, applicable if only pos
if self.write_off_account and self.write_off_amount: if self.write_off_account and self.write_off_amount:
write_off_account_currency = get_account_currency(self.write_off_account)
gl_entries.append( gl_entries.append(
self.get_gl_dict({ self.get_gl_dict({
"account": self.debit_to, "account": self.debit_to,
"party_type": "Customer", "party_type": "Customer",
"party": self.customer, "party": self.customer,
"against": self.write_off_account, "against": self.write_off_account,
"credit": self.write_off_amount, "credit": self.base_write_off_amount,
"remarks": self.remarks, "credit_in_account_currency": self.base_write_off_amount \
if self.party_account_currency==self.company_currency else self.write_off_amount,
"against_voucher": self.return_against if cint(self.is_return) else self.name, "against_voucher": self.return_against if cint(self.is_return) else self.name,
"against_voucher_type": self.doctype, "against_voucher_type": self.doctype
}) }, self.party_account_currency)
) )
gl_entries.append( gl_entries.append(
self.get_gl_dict({ self.get_gl_dict({
"account": self.write_off_account, "account": self.write_off_account,
"against": self.customer, "against": self.customer,
"debit": self.write_off_amount, "debit": self.base_write_off_amount,
"remarks": self.remarks, "debit_in_account_currency": self.base_write_off_amount \
if write_off_account_currency==self.company_currency else self.write_off_amount,
"cost_center": self.write_off_cost_center "cost_center": self.write_off_cost_center
}) }, write_off_account_currency)
) )
def get_list_context(context=None): def get_list_context(context=None):

View File

@@ -7,6 +7,7 @@ import unittest, copy
from frappe.utils import nowdate, add_days, flt 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.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.stock.doctype.purchase_receipt.test_purchase_receipt import set_perpetual_inventory
from erpnext.exceptions import InvalidAccountCurrency, InvalidCurrency
class TestSalesInvoice(unittest.TestCase): class TestSalesInvoice(unittest.TestCase):
def make(self): def make(self):
@@ -401,7 +402,7 @@ class TestSalesInvoice(unittest.TestCase):
jv.cancel() jv.cancel()
self.assertEquals(frappe.db.get_value("Sales Invoice", w.name, "outstanding_amount"), 561.8) self.assertEquals(frappe.db.get_value("Sales Invoice", w.name, "outstanding_amount"), 561.8)
def test_sales_invoice_gl_entry_without_aii(self): def test_sales_invoice_gl_entry_without_perpetual_inventory(self):
set_perpetual_inventory(0) set_perpetual_inventory(0)
si = frappe.copy_doc(test_records[1]) si = frappe.copy_doc(test_records[1])
si.insert() si.insert()
@@ -433,7 +434,7 @@ class TestSalesInvoice(unittest.TestCase):
self.assertFalse(gle) self.assertFalse(gle)
def test_pos_gl_entry_with_aii(self): def test_pos_gl_entry_with_perpetual_inventory(self):
set_perpetual_inventory() set_perpetual_inventory()
self.make_pos_profile() self.make_pos_profile()
@@ -442,8 +443,7 @@ class TestSalesInvoice(unittest.TestCase):
pos = copy.deepcopy(test_records[1]) pos = copy.deepcopy(test_records[1])
pos["is_pos"] = 1 pos["is_pos"] = 1
pos["update_stock"] = 1 pos["update_stock"] = 1
# pos["posting_time"] = "12:05" pos["cash_bank_account"] = "_Test Bank - _TC"
pos["cash_bank_account"] = "_Test Account Bank Account - _TC"
pos["paid_amount"] = 600.0 pos["paid_amount"] = 600.0
si = frappe.copy_doc(pos) si = frappe.copy_doc(pos)
@@ -474,7 +474,7 @@ class TestSalesInvoice(unittest.TestCase):
[stock_in_hand, 0.0, abs(sle.stock_value_difference)], [stock_in_hand, 0.0, abs(sle.stock_value_difference)],
[pos["items"][0]["expense_account"], abs(sle.stock_value_difference), 0.0], [pos["items"][0]["expense_account"], abs(sle.stock_value_difference), 0.0],
[si.debit_to, 0.0, 600.0], [si.debit_to, 0.0, 600.0],
["_Test Account Bank Account - _TC", 600.0, 0.0] ["_Test Bank - _TC", 600.0, 0.0]
]) ])
for i, gle in enumerate(sorted(gl_entries, key=lambda gle: gle.account)): for i, gle in enumerate(sorted(gl_entries, key=lambda gle: gle.account)):
@@ -494,7 +494,7 @@ class TestSalesInvoice(unittest.TestCase):
def make_pos_profile(self): def make_pos_profile(self):
pos_profile = frappe.get_doc({ pos_profile = frappe.get_doc({
"cash_bank_account": "_Test Account Bank Account - _TC", "cash_bank_account": "_Test Bank - _TC",
"company": "_Test Company", "company": "_Test Company",
"cost_center": "_Test Cost Center - _TC", "cost_center": "_Test Cost Center - _TC",
"currency": "INR", "currency": "INR",
@@ -513,7 +513,7 @@ class TestSalesInvoice(unittest.TestCase):
if not frappe.db.exists("POS Profile", "_Test POS Profile"): if not frappe.db.exists("POS Profile", "_Test POS Profile"):
pos_profile.insert() pos_profile.insert()
def test_si_gl_entry_with_aii_and_update_stock_with_warehouse_but_no_account(self): def test_si_gl_entry_with_perpetual_inventory_and_update_stock_with_warehouse_but_no_account(self):
set_perpetual_inventory() set_perpetual_inventory()
frappe.delete_doc("Account", "_Test Warehouse No Account - _TC") frappe.delete_doc("Account", "_Test Warehouse No Account - _TC")
@@ -567,7 +567,7 @@ class TestSalesInvoice(unittest.TestCase):
self.assertFalse(gle) self.assertFalse(gle)
set_perpetual_inventory(0) set_perpetual_inventory(0)
def test_sales_invoice_gl_entry_with_aii_no_item_code(self): def test_sales_invoice_gl_entry_with_perpetual_inventory_no_item_code(self):
set_perpetual_inventory() set_perpetual_inventory()
si = frappe.get_doc(test_records[1]) si = frappe.get_doc(test_records[1])
@@ -593,7 +593,7 @@ class TestSalesInvoice(unittest.TestCase):
set_perpetual_inventory(0) set_perpetual_inventory(0)
def test_sales_invoice_gl_entry_with_aii_non_stock_item(self): def test_sales_invoice_gl_entry_with_perpetual_inventory_non_stock_item(self):
set_perpetual_inventory() set_perpetual_inventory()
si = frappe.get_doc(test_records[1]) si = frappe.get_doc(test_records[1])
si.get("items")[0].item_code = "_Test Non Stock Item" si.get("items")[0].item_code = "_Test Non Stock Item"
@@ -660,7 +660,7 @@ class TestSalesInvoice(unittest.TestCase):
where reference_name=%s""", si.name)) where reference_name=%s""", si.name))
self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account` self.assertTrue(frappe.db.sql("""select name from `tabJournal Entry Account`
where reference_name=%s and credit=300""", si.name)) where reference_name=%s and credit_in_account_currency=300""", si.name))
self.assertEqual(si.outstanding_amount, 261.8) self.assertEqual(si.outstanding_amount, 261.8)
@@ -842,6 +842,79 @@ class TestSalesInvoice(unittest.TestCase):
self.assertEquals(si.base_grand_total, 859.44) self.assertEquals(si.base_grand_total, 859.44)
self.assertEquals(si.grand_total, 859.44) self.assertEquals(si.grand_total, 859.44)
def test_multi_currency_gle(self):
set_perpetual_inventory(0)
si = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
currency="USD", conversion_rate=50)
gl_entries = frappe.db.sql("""select account, account_currency, debit, credit,
debit_in_account_currency, credit_in_account_currency
from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s
order by account asc""", si.name, as_dict=1)
self.assertTrue(gl_entries)
expected_values = {
"_Test Receivable USD - _TC": {
"account_currency": "USD",
"debit": 5000,
"debit_in_account_currency": 100,
"credit": 0,
"credit_in_account_currency": 0
},
"Sales - _TC": {
"account_currency": "INR",
"debit": 0,
"debit_in_account_currency": 0,
"credit": 5000,
"credit_in_account_currency": 5000
}
}
for field in ("account_currency", "debit", "debit_in_account_currency", "credit", "credit_in_account_currency"):
for i, gle in enumerate(gl_entries):
self.assertEquals(expected_values[gle.account][field], gle[field])
# cancel
si.cancel()
gle = frappe.db.sql("""select name from `tabGL Entry`
where voucher_type='Sales Invoice' and voucher_no=%s""", si.name)
self.assertFalse(gle)
def test_invalid_currency(self):
# Customer currency = USD
# Transaction currency cannot be INR
si1 = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
do_not_save=True)
self.assertRaises(InvalidCurrency, si1.save)
# Transaction currency cannot be EUR
si2 = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
currency="EUR", conversion_rate=80, do_not_save=True)
self.assertRaises(InvalidCurrency, si2.save)
# Transaction currency only allowed in USD
si3 = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable USD - _TC",
currency="USD", conversion_rate=50)
# Party Account currency must be in USD, as there is existing GLE with USD
si4 = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable - _TC",
currency="USD", conversion_rate=50, do_not_submit=True)
self.assertRaises(InvalidAccountCurrency, si4.submit)
# Party Account currency must be in USD, force customer currency as there is no GLE
si3.cancel()
si5 = create_sales_invoice(customer="_Test Customer USD", debit_to="_Test Receivable - _TC",
currency="USD", conversion_rate=50, do_not_submit=True)
self.assertRaises(InvalidAccountCurrency, si5.submit)
def create_sales_invoice(**args): def create_sales_invoice(**args):
si = frappe.new_doc("Sales Invoice") si = frappe.new_doc("Sales Invoice")
@@ -856,14 +929,15 @@ def create_sales_invoice(**args):
si.is_pos = args.is_pos si.is_pos = args.is_pos
si.is_return = args.is_return si.is_return = args.is_return
si.return_against = args.return_against si.return_against = args.return_against
si.currency="INR" si.currency=args.currency or "INR"
si.conversion_rate = 1 si.conversion_rate = args.conversion_rate or 1
si.append("items", { si.append("items", {
"item_code": args.item or args.item_code or "_Test Item", "item_code": args.item or args.item_code or "_Test Item",
"warehouse": args.warehouse or "_Test Warehouse - _TC", "warehouse": args.warehouse or "_Test Warehouse - _TC",
"qty": args.qty or 1, "qty": args.qty or 1,
"rate": args.rate or 100, "rate": args.rate or 100,
"income_account": "Sales - _TC",
"expense_account": "Cost of Goods Sold - _TC", "expense_account": "Cost of Goods Sold - _TC",
"cost_center": "_Test Cost Center - _TC", "cost_center": "_Test Cost Center - _TC",
"serial_no": args.serial_no "serial_no": args.serial_no

View File

@@ -117,7 +117,7 @@
"no_copy": 1, "no_copy": 1,
"oldfieldname": "advance_amount", "oldfieldname": "advance_amount",
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
"options": "Company:company:default_currency", "options": "party_account_currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
"print_width": "120px", "print_width": "120px",
@@ -143,7 +143,7 @@
"no_copy": 1, "no_copy": 1,
"oldfieldname": "allocated_amount", "oldfieldname": "allocated_amount",
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
"options": "Company:company:default_currency", "options": "party_account_currency",
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
"print_width": "120px", "print_width": "120px",
@@ -164,7 +164,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 1,
"modified": "2014-12-25 16:30:19.446500", "modified": "2015-08-21 16:22:28.866049",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Sales Invoice Advance", "name": "Sales Invoice Advance",

View File

@@ -456,7 +456,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 1,
"modified": "2015-08-19 12:46:33.165519", "modified": "2015-08-28 02:57:00.766305",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Sales Taxes and Charges", "name": "Sales Taxes and Charges",

View File

@@ -5,6 +5,3 @@ cur_frm.cscript.tax_table = "Sales Taxes and Charges";
{% include "public/js/controllers/accounts.js" %} {% include "public/js/controllers/accounts.js" %}
frappe.ui.form.on("Sales Taxes and Charges Template", "onload", function(frm) {
erpnext.add_applicable_territory();
});

View File

@@ -8,7 +8,7 @@
"description": "Standard tax template that can be applied to all Sales Transactions. This template can contain list of tax heads and also other expense / income heads like \"Shipping\", \"Insurance\", \"Handling\" etc.\n\n#### Note\n\nThe tax rate you define here will be the standard tax rate for all **Items**. If there are **Items** that have different rates, they must be added in the **Item Tax** table in the **Item** master.\n\n#### Description of Columns\n\n1. Calculation Type: \n - This can be on **Net Total** (that is the sum of basic amount).\n - **On Previous Row Total / Amount** (for cumulative taxes or charges). If you select this option, the tax will be applied as a percentage of the previous row (in the tax table) amount or total.\n - **Actual** (as mentioned).\n2. Account Head: The Account ledger under which this tax will be booked\n3. Cost Center: If the tax / charge is an income (like shipping) or expense it needs to be booked against a Cost Center.\n4. Description: Description of the tax (that will be printed in invoices / quotes).\n5. Rate: Tax rate.\n6. Amount: Tax amount.\n7. Total: Cumulative total to this point.\n8. Enter Row: If based on \"Previous Row Total\" you can select the row number which will be taken as a base for this calculation (default is the previous row).\n9. Is this Tax included in Basic Rate?: If you check this, it means that this tax will not be shown below the item table, but will be included in the Basic Rate in your main item table. This is useful where you want give a flat price (inclusive of all taxes) price to customers.", "description": "Standard tax template that can be applied to all Sales Transactions. This template can contain list of tax heads and also other expense / income heads like \"Shipping\", \"Insurance\", \"Handling\" etc.\n\n#### Note\n\nThe tax rate you define here will be the standard tax rate for all **Items**. If there are **Items** that have different rates, they must be added in the **Item Tax** table in the **Item** master.\n\n#### Description of Columns\n\n1. Calculation Type: \n - This can be on **Net Total** (that is the sum of basic amount).\n - **On Previous Row Total / Amount** (for cumulative taxes or charges). If you select this option, the tax will be applied as a percentage of the previous row (in the tax table) amount or total.\n - **Actual** (as mentioned).\n2. Account Head: The Account ledger under which this tax will be booked\n3. Cost Center: If the tax / charge is an income (like shipping) or expense it needs to be booked against a Cost Center.\n4. Description: Description of the tax (that will be printed in invoices / quotes).\n5. Rate: Tax rate.\n6. Amount: Tax amount.\n7. Total: Cumulative total to this point.\n8. Enter Row: If based on \"Previous Row Total\" you can select the row number which will be taken as a base for this calculation (default is the previous row).\n9. Is this Tax included in Basic Rate?: If you check this, it means that this tax will not be shown below the item table, but will be included in the Basic Rate in your main item table. This is useful where you want give a flat price (inclusive of all taxes) price to customers.",
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Master", "document_type": "Setup",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
@@ -21,7 +21,7 @@
"in_filter": 1, "in_filter": 1,
"in_list_view": 0, "in_list_view": 0,
"label": "Title", "label": "Title",
"no_copy": 0, "no_copy": 1,
"oldfieldname": "title", "oldfieldname": "title",
"oldfieldtype": "Data", "oldfieldtype": "Data",
"permlevel": 0, "permlevel": 0,
@@ -164,29 +164,6 @@
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"description": "Specify a list of Territories, for which, this Taxes Master is valid",
"fieldname": "territories",
"fieldtype": "Table",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Valid for Territories",
"no_copy": 0,
"options": "Applicable Territory",
"permlevel": 0,
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0
} }
], ],
"hide_heading": 0, "hide_heading": 0,
@@ -198,7 +175,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-05-06 08:51:54.662853", "modified": "2015-09-17 07:09:28.797959",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Sales Taxes and Charges Template", "name": "Sales Taxes and Charges Template",

View File

@@ -5,7 +5,6 @@ from __future__ import unicode_literals
import frappe import frappe
from frappe.model.document import Document from frappe.model.document import Document
from erpnext.controllers.accounts_controller import validate_taxes_and_charges, validate_inclusive_tax from erpnext.controllers.accounts_controller import validate_taxes_and_charges, validate_inclusive_tax
from frappe.utils.nestedset import get_root_of
class SalesTaxesandChargesTemplate(Document): class SalesTaxesandChargesTemplate(Document):
def validate(self): def validate(self):
@@ -20,10 +19,6 @@ def valdiate_taxes_and_charges_template(doc):
where ifnull(is_default,0) = 1 and name != %s and company = %s""".format(doc.doctype), where ifnull(is_default,0) = 1 and name != %s and company = %s""".format(doc.doctype),
(doc.name, doc.company)) (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"): for tax in doc.get("taxes"):
validate_taxes_and_charges(tax) validate_taxes_and_charges(tax)
validate_inclusive_tax(tax, doc) validate_inclusive_tax(tax, doc)

View File

@@ -20,19 +20,7 @@
"rate": 6.36 "rate": 6.36
} }
], ],
"title": "_Test Sales Taxes and Charges Template", "title": "_Test Sales Taxes and Charges Template"
"territories": [
{
"doctype": "Applicable Territory",
"parentfield": "territories",
"territory": "All Territories"
},
{
"doctype": "Applicable Territory",
"parentfield": "territories",
"territory": "_Test Territory Rest Of The World"
}
]
}, },
{ {
"company": "_Test Company", "company": "_Test Company",
@@ -115,14 +103,7 @@
"row_id": 7 "row_id": 7
} }
], ],
"title": "_Test India Tax Master", "title": "_Test India Tax Master"
"territories": [
{
"doctype": "Applicable Territory",
"parentfield": "territories",
"territory": "_Test Territory India"
}
]
}, },
{ {
"company": "_Test Company", "company": "_Test Company",
@@ -145,13 +126,76 @@
"rate": 4 "rate": 4
} }
], ],
"title": "_Test Sales Taxes and Charges Template - Rest of the World", "title": "_Test Sales Taxes and Charges Template - Rest of the World"
"territories": [ },
{
"company": "_Test Company",
"doctype": "Sales Taxes and Charges Template",
"taxes": [
{ {
"doctype": "Applicable Territory", "account_head": "_Test Account VAT - _TC",
"parentfield": "territories", "charge_type": "On Net Total",
"territory": "_Test Territory Rest Of The World" "description": "VAT",
"doctype": "Sales Taxes and Charges",
"parentfield": "taxes",
"rate": 12
},
{
"account_head": "_Test Account Service Tax - _TC",
"charge_type": "On Net Total",
"description": "Service Tax",
"doctype": "Sales Taxes and Charges",
"parentfield": "taxes",
"rate": 4
} }
] ],
"title": "_Test Sales Taxes and Charges Template 1"
},
{
"company": "_Test Company",
"doctype": "Sales Taxes and Charges Template",
"taxes": [
{
"account_head": "_Test Account VAT - _TC",
"charge_type": "On Net Total",
"description": "VAT",
"doctype": "Sales Taxes and Charges",
"parentfield": "taxes",
"rate": 12
},
{
"account_head": "_Test Account Service Tax - _TC",
"charge_type": "On Net Total",
"description": "Service Tax",
"doctype": "Sales Taxes and Charges",
"parentfield": "taxes",
"rate": 4
}
],
"title": "_Test Sales Taxes and Charges Template 2"
},
{
"doctype" : "Sales Taxes and Charges Template",
"title": "_Test Tax 1",
"company": "_Test Company",
"taxes":[{
"charge_type": "Actual",
"account_head": "Sales Expenses - _TC",
"cost_center": "Main - _TC",
"description": "Test Shopping cart taxes with Tax Rule",
"tax_amount": 1000
}]
},
{
"doctype" : "Sales Taxes and Charges Template",
"title": "_Test Tax 2",
"company": "_Test Company",
"taxes":[{
"charge_type": "Actual",
"account_head": "Sales Expenses - _TC",
"cost_center": "Main - _TC",
"description": "Test Shopping cart taxes with Tax Rule",
"tax_amount": 200
}]
} }
] ]

View File

@@ -1,8 +1,3 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt // License: GNU General Public License v3. See license.txt
$.extend(cur_frm.cscript, {
onload: function() {
erpnext.add_applicable_territory();
}
});

View File

@@ -1,388 +1,397 @@
{ {
"allow_copy": 0, "allow_copy": 0,
"allow_import": 0, "allow_import": 0,
"allow_rename": 0, "allow_rename": 0,
"autoname": "field:label", "autoname": "field:label",
"creation": "2013-06-25 11:48:03", "creation": "2013-06-25 11:48:03",
"custom": 0, "custom": 0,
"description": "Specify conditions to calculate shipping amount", "description": "Specify conditions to calculate shipping amount",
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"description": "example: Next Day Shipping", "description": "example: Next Day Shipping",
"fieldname": "label", "fieldname": "label",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 1, "in_list_view": 1,
"label": "Shipping Rule Label", "label": "Shipping Rule Label",
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
"read_only": 0, "read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 1, "reqd": 1,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "column_break_2", "fieldname": "disabled",
"fieldtype": "Column Break", "fieldtype": "Check",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"no_copy": 0, "label": "Disabled",
"permlevel": 0, "no_copy": 0,
"print_hide": 0, "permlevel": 0,
"read_only": 0, "precision": "",
"report_hide": 0, "print_hide": 0,
"reqd": 0, "read_only": 0,
"search_index": 0, "report_hide": 0,
"set_only_once": 0, "reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"default": "Net Total", "default": "Net Total",
"fieldname": "calculate_based_on", "fieldname": "calculate_based_on",
"fieldtype": "Select", "fieldtype": "Select",
"hidden": 1, "hidden": 1,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 1, "in_list_view": 1,
"label": "Calculate Based On", "label": "Calculate Based On",
"no_copy": 0, "no_copy": 0,
"options": "Net Total\nNet Weight", "options": "Net Total\nNet Weight",
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
"read_only": 1, "read_only": 1,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "rule_conditions_section", "depends_on": "eval:!doc.disabled",
"fieldtype": "Section Break", "fieldname": "rule_conditions_section",
"hidden": 0, "fieldtype": "Section Break",
"ignore_user_permissions": 0, "hidden": 0,
"in_filter": 0, "ignore_user_permissions": 0,
"in_list_view": 0, "in_filter": 0,
"label": "Shipping Rule Conditions", "in_list_view": 0,
"no_copy": 0, "label": "Shipping Rule Conditions",
"permlevel": 0, "no_copy": 0,
"print_hide": 0, "permlevel": 0,
"read_only": 0, "print_hide": 0,
"report_hide": 0, "read_only": 0,
"reqd": 0, "report_hide": 0,
"search_index": 0, "reqd": 0,
"set_only_once": 0, "search_index": 0,
"set_only_once": 0,
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "conditions", "fieldname": "conditions",
"fieldtype": "Table", "fieldtype": "Table",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Shipping Rule Conditions", "label": "Shipping Rule Conditions",
"no_copy": 0, "no_copy": 0,
"options": "Shipping Rule Condition", "options": "Shipping Rule Condition",
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
"read_only": 0, "read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 1, "reqd": 1,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "section_break_6", "depends_on": "eval:!doc.disabled",
"fieldtype": "Section Break", "fieldname": "section_break_6",
"hidden": 0, "fieldtype": "Section Break",
"ignore_user_permissions": 0, "hidden": 0,
"in_filter": 0, "ignore_user_permissions": 0,
"in_list_view": 0, "in_filter": 0,
"no_copy": 0, "in_list_view": 0,
"permlevel": 0, "label": "Valid for Countries",
"print_hide": 0, "no_copy": 0,
"read_only": 0, "permlevel": 0,
"report_hide": 0, "print_hide": 0,
"reqd": 0, "read_only": 0,
"search_index": 0, "report_hide": 0,
"set_only_once": 0, "reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"description": "Specify a list of Territories, for which, this Shipping Rule is valid", "fieldname": "worldwide_shipping",
"fieldname": "territories", "fieldtype": "Check",
"fieldtype": "Table", "hidden": 0,
"hidden": 0, "ignore_user_permissions": 0,
"ignore_user_permissions": 0, "in_filter": 0,
"in_filter": 0, "in_list_view": 0,
"in_list_view": 0, "label": "Worldwide Shipping",
"label": "Valid For Territories", "no_copy": 0,
"no_copy": 0, "permlevel": 0,
"options": "Applicable Territory", "precision": "",
"permlevel": 0, "print_hide": 0,
"print_hide": 0, "read_only": 0,
"read_only": 0, "report_hide": 0,
"report_hide": 0, "reqd": 0,
"reqd": 1, "search_index": 0,
"search_index": 0, "set_only_once": 0,
"set_only_once": 0,
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "column_break_8", "depends_on": "eval:!doc.worldwide_shipping",
"fieldtype": "Column Break", "fieldname": "countries",
"hidden": 0, "fieldtype": "Table",
"ignore_user_permissions": 0, "hidden": 0,
"in_filter": 0, "ignore_user_permissions": 0,
"in_list_view": 0, "in_filter": 0,
"no_copy": 0, "in_list_view": 0,
"permlevel": 0, "label": "Valid for Countries",
"print_hide": 0, "no_copy": 0,
"read_only": 0, "options": "Shipping Rule Country",
"report_hide": 0, "permlevel": 0,
"reqd": 0, "precision": "",
"search_index": 0, "print_hide": 0,
"set_only_once": 0, "read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "company", "depends_on": "eval:!doc.disabled",
"fieldtype": "Link", "fieldname": "section_break_10",
"hidden": 0, "fieldtype": "Section Break",
"ignore_user_permissions": 0, "hidden": 0,
"in_filter": 0, "ignore_user_permissions": 0,
"in_list_view": 0, "in_filter": 0,
"label": "Company", "in_list_view": 0,
"no_copy": 0, "no_copy": 0,
"options": "Company", "permlevel": 0,
"permlevel": 0, "print_hide": 0,
"print_hide": 0, "read_only": 0,
"read_only": 0, "report_hide": 0,
"report_hide": 0, "reqd": 0,
"reqd": 1, "search_index": 0,
"search_index": 0, "set_only_once": 0,
"set_only_once": 0,
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "section_break_10", "fieldname": "company",
"fieldtype": "Section Break", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"no_copy": 0, "label": "Company",
"permlevel": 0, "no_copy": 0,
"print_hide": 0, "options": "Company",
"read_only": 0, "permlevel": 0,
"report_hide": 0, "print_hide": 0,
"reqd": 0, "read_only": 0,
"search_index": 0, "report_hide": 0,
"set_only_once": 0, "reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "account", "fieldname": "column_break_12",
"fieldtype": "Link", "fieldtype": "Column Break",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Shipping Account", "no_copy": 0,
"no_copy": 0, "permlevel": 0,
"options": "Account", "print_hide": 0,
"permlevel": 0, "read_only": 0,
"print_hide": 0, "report_hide": 0,
"read_only": 0, "reqd": 0,
"report_hide": 0, "search_index": 0,
"reqd": 1, "set_only_once": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "column_break_12", "fieldname": "account",
"fieldtype": "Column Break", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"no_copy": 0, "label": "Shipping Account",
"permlevel": 0, "no_copy": 0,
"print_hide": 0, "options": "Account",
"read_only": 0, "permlevel": 0,
"report_hide": 0, "print_hide": 0,
"reqd": 0, "read_only": 0,
"search_index": 0, "report_hide": 0,
"set_only_once": 0, "reqd": 1,
"search_index": 0,
"set_only_once": 0,
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "cost_center", "fieldname": "cost_center",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Cost Center", "label": "Cost Center",
"no_copy": 0, "no_copy": 0,
"options": "Cost Center", "options": "Cost Center",
"permlevel": 0, "permlevel": 0,
"print_hide": 0, "print_hide": 0,
"read_only": 0, "read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 1, "reqd": 1,
"search_index": 0, "search_index": 0,
"set_only_once": 0, "set_only_once": 0,
"unique": 0 "unique": 0
} }
], ],
"hide_heading": 0, "hide_heading": 0,
"hide_toolbar": 0, "hide_toolbar": 0,
"icon": "icon-truck", "icon": "icon-truck",
"idx": 1, "idx": 1,
"in_create": 0, "in_create": 0,
"in_dialog": 0, "in_dialog": 0,
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-02-05 05:11:46.634371", "modified": "2015-09-22 08:30:57.226342",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Shipping Rule", "name": "Shipping Rule",
"owner": "Administrator", "owner": "Administrator",
"permissions": [ "permissions": [
{ {
"amend": 0, "amend": 0,
"apply_user_permissions": 1, "apply_user_permissions": 1,
"cancel": 0, "cancel": 0,
"create": 0, "create": 0,
"delete": 0, "delete": 0,
"email": 1, "email": 1,
"export": 0, "export": 0,
"if_owner": 0, "if_owner": 0,
"import": 0, "import": 0,
"permlevel": 0, "permlevel": 0,
"print": 1, "print": 1,
"read": 1, "read": 1,
"report": 1, "report": 1,
"role": "Accounts User", "role": "Accounts User",
"set_user_permissions": 0, "set_user_permissions": 0,
"share": 0, "share": 0,
"submit": 0, "submit": 0,
"write": 0 "write": 0
}, },
{ {
"amend": 0, "amend": 0,
"apply_user_permissions": 1, "apply_user_permissions": 1,
"cancel": 0, "cancel": 0,
"create": 0, "create": 0,
"delete": 0, "delete": 0,
"email": 1, "email": 1,
"export": 0, "export": 0,
"if_owner": 0, "if_owner": 0,
"import": 0, "import": 0,
"permlevel": 0, "permlevel": 0,
"print": 1, "print": 1,
"read": 1, "read": 1,
"report": 1, "report": 1,
"role": "Sales User", "role": "Sales User",
"set_user_permissions": 0, "set_user_permissions": 0,
"share": 0, "share": 0,
"submit": 0, "submit": 0,
"write": 0 "write": 0
}, },
{ {
"amend": 0, "amend": 0,
"apply_user_permissions": 0, "apply_user_permissions": 0,
"cancel": 0, "cancel": 0,
"create": 1, "create": 1,
"delete": 1, "delete": 1,
"email": 1, "email": 1,
"export": 0, "export": 0,
"if_owner": 0, "if_owner": 0,
"import": 0, "import": 0,
"permlevel": 0, "permlevel": 0,
"print": 1, "print": 1,
"read": 1, "read": 1,
"report": 1, "report": 1,
"role": "Accounts Manager", "role": "Accounts Manager",
"set_user_permissions": 0, "set_user_permissions": 0,
"share": 1, "share": 1,
"submit": 0, "submit": 0,
"write": 1 "write": 1
}, },
{ {
"amend": 0, "amend": 0,
"apply_user_permissions": 0, "apply_user_permissions": 0,
"cancel": 0, "cancel": 0,
"create": 1, "create": 1,
"delete": 1, "delete": 1,
"email": 1, "email": 1,
"export": 0, "export": 0,
"if_owner": 0, "if_owner": 0,
"import": 0, "import": 0,
"permlevel": 0, "permlevel": 0,
"print": 1, "print": 1,
"read": 1, "read": 1,
"report": 1, "report": 1,
"role": "Sales Master Manager", "role": "Sales Master Manager",
"set_user_permissions": 0, "set_user_permissions": 0,
"share": 1, "share": 1,
"submit": 0, "submit": 0,
"write": 1 "write": 1
} }
], ],
"read_only": 0, "read_only": 0,
"read_only_onload": 0 "read_only_onload": 0
} }

View File

@@ -22,6 +22,12 @@ class ShippingRule(Document):
self.sort_shipping_rule_conditions() self.sort_shipping_rule_conditions()
self.validate_overlapping_shipping_rule_conditions() self.validate_overlapping_shipping_rule_conditions()
if self.worldwide_shipping:
self.countries = []
elif not len([d.country for d in self.countries if d.country]):
frappe.throw(_("Please specify a country for this Shipping Rule or check Worldwide Shipping"))
def validate_from_to_values(self): def validate_from_to_values(self):
zero_to_values = [] zero_to_values = []

View File

@@ -1,116 +1,100 @@
[ [
{ {
"account": "_Test Account Shipping Charges - _TC", "account": "_Test Account Shipping Charges - _TC",
"calculate_based_on": "Net Total", "calculate_based_on": "Net Total",
"company": "_Test Company", "company": "_Test Company",
"cost_center": "_Test Cost Center - _TC", "cost_center": "_Test Cost Center - _TC",
"doctype": "Shipping Rule", "doctype": "Shipping Rule",
"label": "_Test Shipping Rule", "label": "_Test Shipping Rule",
"name": "_Test Shipping Rule", "name": "_Test Shipping Rule",
"conditions": [ "conditions": [
{ {
"doctype": "Shipping Rule Condition", "doctype": "Shipping Rule Condition",
"from_value": 0, "from_value": 0,
"parentfield": "conditions", "parentfield": "conditions",
"shipping_amount": 50.0, "shipping_amount": 50.0,
"to_value": 100 "to_value": 100
}, },
{ {
"doctype": "Shipping Rule Condition", "doctype": "Shipping Rule Condition",
"from_value": 101, "from_value": 101,
"parentfield": "conditions", "parentfield": "conditions",
"shipping_amount": 100.0, "shipping_amount": 100.0,
"to_value": 200 "to_value": 200
}, },
{ {
"doctype": "Shipping Rule Condition", "doctype": "Shipping Rule Condition",
"from_value": 201, "from_value": 201,
"parentfield": "conditions", "parentfield": "conditions",
"shipping_amount": 0.0 "shipping_amount": 0.0
} }
], ],
"territories": [ "worldwide_shipping": 1
{ },
"doctype": "Applicable Territory",
"parentfield": "territories",
"territory": "_Test Territory"
}
]
},
{ {
"account": "_Test Account Shipping Charges - _TC", "account": "_Test Account Shipping Charges - _TC",
"calculate_based_on": "Net Total", "calculate_based_on": "Net Total",
"company": "_Test Company", "company": "_Test Company",
"cost_center": "_Test Cost Center - _TC", "cost_center": "_Test Cost Center - _TC",
"doctype": "Shipping Rule", "doctype": "Shipping Rule",
"label": "_Test Shipping Rule - India", "label": "_Test Shipping Rule - India",
"name": "_Test Shipping Rule - India", "name": "_Test Shipping Rule - India",
"conditions": [ "conditions": [
{ {
"doctype": "Shipping Rule Condition", "doctype": "Shipping Rule Condition",
"from_value": 0, "from_value": 0,
"parentfield": "conditions", "parentfield": "conditions",
"shipping_amount": 50.0, "shipping_amount": 50.0,
"to_value": 100 "to_value": 100
}, },
{ {
"doctype": "Shipping Rule Condition", "doctype": "Shipping Rule Condition",
"from_value": 101, "from_value": 101,
"parentfield": "conditions", "parentfield": "conditions",
"shipping_amount": 100.0, "shipping_amount": 100.0,
"to_value": 200 "to_value": 200
}, },
{ {
"doctype": "Shipping Rule Condition", "doctype": "Shipping Rule Condition",
"from_value": 201, "from_value": 201,
"parentfield": "conditions", "parentfield": "conditions",
"shipping_amount": 0.0 "shipping_amount": 0.0
} }
], ],
"territories": [ "countries": [
{ {"country": "India"}
"doctype": "Applicable Territory",
"parentfield": "territories",
"territory": "_Test Territory India"
}
] ]
}, },
{ {
"account": "_Test Account Shipping Charges - _TC", "account": "_Test Account Shipping Charges - _TC",
"calculate_based_on": "Net Total", "calculate_based_on": "Net Total",
"company": "_Test Company", "company": "_Test Company",
"cost_center": "_Test Cost Center - _TC", "cost_center": "_Test Cost Center - _TC",
"doctype": "Shipping Rule", "doctype": "Shipping Rule",
"label": "_Test Shipping Rule - Rest of the World", "label": "_Test Shipping Rule - Rest of the World",
"name": "_Test Shipping Rule - Rest of the World", "name": "_Test Shipping Rule - Rest of the World",
"conditions": [ "conditions": [
{ {
"doctype": "Shipping Rule Condition", "doctype": "Shipping Rule Condition",
"from_value": 0, "from_value": 0,
"parentfield": "conditions", "parentfield": "conditions",
"shipping_amount": 500.0, "shipping_amount": 500.0,
"to_value": 1000 "to_value": 1000
}, },
{ {
"doctype": "Shipping Rule Condition", "doctype": "Shipping Rule Condition",
"from_value": 1001, "from_value": 1001,
"parentfield": "conditions", "parentfield": "conditions",
"shipping_amount": 1000.0, "shipping_amount": 1000.0,
"to_value": 2000 "to_value": 2000
}, },
{ {
"doctype": "Shipping Rule Condition", "doctype": "Shipping Rule Condition",
"from_value": 2001, "from_value": 2001,
"parentfield": "conditions", "parentfield": "conditions",
"shipping_amount": 1500.0 "shipping_amount": 1500.0
} }
], ],
"territories": [ "worldwide_shipping": 1
{
"doctype": "Applicable Territory",
"parentfield": "territories",
"territory": "_Test Territory Rest Of The World"
}
]
} }
] ]

View File

@@ -2,26 +2,27 @@
"allow_copy": 0, "allow_copy": 0,
"allow_import": 0, "allow_import": 0,
"allow_rename": 0, "allow_rename": 0,
"creation": "2013-06-20 12:48:38", "creation": "2015-09-17 06:43:22.767534",
"custom": 0, "custom": 0,
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "Other",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"description": "", "fieldname": "country",
"fieldname": "territory",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 1, "in_list_view": 1,
"label": "Territory", "label": "Country",
"no_copy": 0, "no_copy": 0,
"options": "Territory", "options": "Country",
"permlevel": 0, "permlevel": 0,
"precision": "",
"print_hide": 0, "print_hide": 0,
"read_only": 0, "read_only": 0,
"report_hide": 0, "report_hide": 0,
@@ -33,18 +34,20 @@
], ],
"hide_heading": 0, "hide_heading": 0,
"hide_toolbar": 0, "hide_toolbar": 0,
"idx": 1,
"in_create": 0, "in_create": 0,
"in_dialog": 0, "in_dialog": 0,
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 1, "istable": 1,
"modified": "2015-01-01 14:29:58.724652", "modified": "2015-09-17 06:43:22.767534",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Setup", "module": "Accounts",
"name": "Applicable Territory", "name": "Shipping Rule Country",
"name_case": "",
"owner": "Administrator", "owner": "Administrator",
"permissions": [], "permissions": [],
"read_only": 0, "read_only": 0,
"read_only_onload": 0 "read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC"
} }

View File

@@ -1,12 +1,10 @@
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # -*- coding: utf-8 -*-
# MIT License. See license.txt # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt # For license information, please see license.txt
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe import frappe
from frappe.model.document import Document from frappe.model.document import Document
class ApplicableTerritory(Document): class ShippingRuleCountry(Document):
pass pass

View File

@@ -0,0 +1,60 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
cur_frm.add_fetch("customer", "customer_group", "customer_group" );
cur_frm.add_fetch("supplier", "supplier_type", "supplier_type" );
cur_frm.toggle_reqd("sales_tax_template", cur_frm.doc.tax_type=="Sales");
cur_frm.toggle_reqd("purchase_tax_template", cur_frm.doc.tax_type=="Purchase");
frappe.ui.form.on("Tax Rule", "onload", function(frm) {
if(frm.doc.__islocal){
frm.set_value("use_for_shopping_cart", 1);
}
})
frappe.ui.form.on("Tax Rule", "use_for_shopping_cart", function(frm) {
if(!frm.doc.use_for_shopping_cart && (frappe.get_list("Tax Rule", {"use_for_shopping_cart":1}).length == 0)){
frappe.model.get_value("Shopping Cart Settings", "Shopping Cart Settings", "enabled", function(docfield) {
if(docfield.enabled){
frm.set_value("use_for_shopping_cart", 1);
frappe.throw(__("Shopping Cart is enabled"));
}
});
}
})
frappe.ui.form.on("Tax Rule", "customer", function(frm) {
frappe.call({
method:"erpnext.accounts.doctype.tax_rule.tax_rule.get_party_details",
args: {
"party": frm.doc.customer,
"party_type": "customer"
},
callback: function(r) {
if(!r.exc) {
$.each(r.message, function(k, v) {
frm.set_value(k, v);
});
}
}
});
});
frappe.ui.form.on("Tax Rule", "supplier", function(frm) {
frappe.call({
method:"erpnext.accounts.doctype.tax_rule.tax_rule.get_party_details",
args: {
"party": frm.doc.supplier,
"party_type": "supplier"
},
callback: function(r) {
if(!r.exc) {
$.each(r.message, function(k, v) {
frm.set_value(k, v);
});
}
}
});
});

View File

@@ -1,27 +1,30 @@
{ {
"allow_copy": 0, "allow_copy": 0,
"allow_import": 0, "allow_import": 1,
"allow_rename": 0, "allow_rename": 0,
"creation": "2013-04-30 12:58:38", "autoname": "TR.####",
"creation": "2015-08-07 02:33:52.670866",
"custom": 0, "custom": 0,
"description": "System for managing Backups",
"docstatus": 0, "docstatus": 0,
"doctype": "DocType", "doctype": "DocType",
"document_type": "System", "document_type": "Setup",
"fields": [ "fields": [
{ {
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "setup", "default": "Sales",
"fieldtype": "Section Break", "fieldname": "tax_type",
"fieldtype": "Select",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 1,
"label": "Download Backups", "label": "Tax Type",
"no_copy": 0, "no_copy": 0,
"options": "Sales\nPurchase",
"permlevel": 0, "permlevel": 0,
"precision": "",
"print_hide": 0, "print_hide": 0,
"read_only": 0, "read_only": 0,
"report_hide": 0, "report_hide": 0,
@@ -34,13 +37,13 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "current_backups", "fieldname": "use_for_shopping_cart",
"fieldtype": "HTML", "fieldtype": "Check",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Current Backups", "label": "Use for Shopping Cart",
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
@@ -56,56 +59,12 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"description": "", "fieldname": "column_break_1",
"fieldname": "sync_with_dropbox", "fieldtype": "Column Break",
"fieldtype": "Section Break",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Sync with Dropbox",
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "backup_right_now",
"fieldtype": "Button",
"hidden": 1,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Backup Right Now",
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "send_backups_to_dropbox",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Send Backups to Dropbox",
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
@@ -121,18 +80,18 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"depends_on": "send_backups_to_dropbox", "depends_on": "eval:doc.tax_type==\"Sales\"",
"description": "Note: Backups and files are not deleted from Dropbox, you will have to delete them manually.", "fieldname": "sales_tax_template",
"fieldname": "upload_backups_to_dropbox", "fieldtype": "Link",
"fieldtype": "Select",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Upload Backups to Dropbox", "label": "Sales Tax Template",
"no_copy": 0, "no_copy": 0,
"options": "Never\nWeekly\nDaily", "options": "Sales Taxes and Charges Template",
"permlevel": 0, "permlevel": 0,
"precision": "",
"print_hide": 0, "print_hide": 0,
"read_only": 0, "read_only": 0,
"report_hide": 0, "report_hide": 0,
@@ -145,17 +104,18 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"depends_on": "send_backups_to_dropbox", "depends_on": "eval:doc.tax_type==\"Purchase\"",
"description": "Email ids separated by commas.", "fieldname": "purchase_tax_template",
"fieldname": "send_notifications_to", "fieldtype": "Link",
"fieldtype": "Data",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Send Notifications To", "label": "Purchase Tax Template",
"no_copy": 0, "no_copy": 0,
"options": "Purchase Taxes and Charges Template",
"permlevel": 0, "permlevel": 0,
"precision": "",
"print_hide": 0, "print_hide": 0,
"read_only": 0, "read_only": 0,
"report_hide": 0, "report_hide": 0,
@@ -168,123 +128,16 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "dropbox_access_key", "fieldname": "filters",
"fieldtype": "Data",
"hidden": 1,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Dropbox Access Key",
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "dropbox_access_secret",
"fieldtype": "Data",
"hidden": 1,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Dropbox Access Secret",
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "dropbox_access_allowed",
"fieldtype": "Check",
"hidden": 1,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Dropbox Access Allowed",
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"read_only": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "send_backups_to_dropbox",
"fieldname": "allow_dropbox_access",
"fieldtype": "Button",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Allow Dropbox Access",
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"description": "Note: Backups and files are not deleted from Google Drive, you will have to delete them manually.",
"fieldname": "sync_with_gdrive",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hidden": 1,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Sync with Google Drive",
"no_copy": 0,
"permlevel": 0,
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "upload_backups_to_gdrive",
"fieldtype": "Select",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Upload Backups to Google Drive", "label": "Filters",
"no_copy": 0, "no_copy": 0,
"options": "Never\nDaily\nWeekly",
"permlevel": 0, "permlevel": 0,
"precision": "",
"print_hide": 0, "print_hide": 0,
"read_only": 0, "read_only": 0,
"report_hide": 0, "report_hide": 0,
@@ -297,15 +150,18 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "allow_gdrive_access", "depends_on": "eval:doc.tax_type==\"Sales\"",
"fieldtype": "Button", "fieldname": "customer",
"fieldtype": "Link",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Allow Google Drive Access", "label": "Customer",
"no_copy": 0, "no_copy": 0,
"options": "Customer",
"permlevel": 0, "permlevel": 0,
"precision": "",
"print_hide": 0, "print_hide": 0,
"read_only": 0, "read_only": 0,
"report_hide": 0, "report_hide": 0,
@@ -318,15 +174,40 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "verification_code", "depends_on": "eval:doc.tax_type==\"Purchase\"",
"fieldname": "supplier",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Supplier",
"no_copy": 0,
"options": "Supplier",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "billing_city",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Enter Verification Code", "label": "Billing City",
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "permlevel": 0,
"precision": "",
"print_hide": 0, "print_hide": 0,
"read_only": 0, "read_only": 0,
"report_hide": 0, "report_hide": 0,
@@ -339,15 +220,16 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "validate_gdrive", "fieldname": "billing_state",
"fieldtype": "Button", "fieldtype": "Data",
"hidden": 0, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Validate", "label": "Billing State",
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "permlevel": 0,
"precision": "",
"print_hide": 0, "print_hide": 0,
"read_only": 0, "read_only": 0,
"report_hide": 0, "report_hide": 0,
@@ -360,17 +242,19 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "gdrive_access_allowed", "fieldname": "billing_country",
"fieldtype": "Check", "fieldtype": "Link",
"hidden": 1, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Google Drive Access Allowed", "label": "Billing Country",
"no_copy": 0, "no_copy": 0,
"options": "Country",
"permlevel": 0, "permlevel": 0,
"precision": "",
"print_hide": 0, "print_hide": 0,
"read_only": 1, "read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
@@ -381,17 +265,17 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "gdrive_credentials", "fieldname": "column_break_2",
"fieldtype": "Text", "fieldtype": "Column Break",
"hidden": 1, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Credentials",
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "permlevel": 0,
"precision": "",
"print_hide": 0, "print_hide": 0,
"read_only": 1, "read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
@@ -402,17 +286,66 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "database_folder_id", "depends_on": "eval:doc.tax_type==\"Sales\"",
"fieldname": "customer_group",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Customer Group",
"no_copy": 0,
"options": "Customer Group",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"depends_on": "eval:doc.tax_type==\"Purchase\"",
"fieldname": "supplier_type",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Supplier Type",
"no_copy": 0,
"options": "Supplier Type",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "shipping_city",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 1, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Database Folder ID", "label": "Shipping City",
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "permlevel": 0,
"precision": "",
"print_hide": 0, "print_hide": 0,
"read_only": 1, "read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
@@ -423,17 +356,216 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "files_folder_id", "fieldname": "shipping_state",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 1, "hidden": 0,
"ignore_user_permissions": 0, "ignore_user_permissions": 0,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Files Folder ID", "label": "Shipping State",
"no_copy": 0, "no_copy": 0,
"permlevel": 0, "permlevel": 0,
"precision": "",
"print_hide": 0, "print_hide": 0,
"read_only": 1, "read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "shipping_country",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Shipping Country",
"no_copy": 0,
"options": "Country",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "section_break_4",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Validity",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "from_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "From Date",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "column_break_7",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "to_date",
"fieldtype": "Date",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "To Date",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "section_break_6",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"default": "1",
"fieldname": "priority",
"fieldtype": "Int",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Priority",
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "column_break_20",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"fieldname": "company",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 0,
"in_filter": 0,
"in_list_view": 0,
"label": "Company",
"no_copy": 0,
"options": "Company",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"read_only": 0,
"report_hide": 0, "report_hide": 0,
"reqd": 0, "reqd": 0,
"search_index": 0, "search_index": 0,
@@ -443,17 +575,16 @@
], ],
"hide_heading": 0, "hide_heading": 0,
"hide_toolbar": 0, "hide_toolbar": 0,
"icon": "icon-cloud-upload",
"idx": 1,
"in_create": 0, "in_create": 0,
"in_dialog": 0, "in_dialog": 0,
"is_submittable": 0, "is_submittable": 0,
"issingle": 1, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-05-26 04:54:10.193573", "modified": "2015-09-24 07:59:06.502058",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Setup", "module": "Accounts",
"name": "Backup Manager", "name": "Tax Rule",
"name_case": "",
"owner": "Administrator", "owner": "Administrator",
"permissions": [ "permissions": [
{ {
@@ -461,16 +592,16 @@
"apply_user_permissions": 0, "apply_user_permissions": 0,
"cancel": 0, "cancel": 0,
"create": 1, "create": 1,
"delete": 0, "delete": 1,
"email": 1, "email": 1,
"export": 0, "export": 1,
"if_owner": 0, "if_owner": 0,
"import": 0, "import": 0,
"permlevel": 0, "permlevel": 0,
"print": 1, "print": 1,
"read": 1, "read": 1,
"report": 0, "report": 1,
"role": "System Manager", "role": "Accounts Manager",
"set_user_permissions": 0, "set_user_permissions": 0,
"share": 1, "share": 1,
"submit": 0, "submit": 0,
@@ -478,5 +609,7 @@
} }
], ],
"read_only": 0, "read_only": 0,
"read_only_onload": 0 "read_only_onload": 0,
"sort_field": "modified",
"sort_order": "DESC"
} }

View File

@@ -0,0 +1,132 @@
# -*- coding: utf-8 -*-
# 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 import _
from frappe.model.document import Document
from frappe.utils import cstr
class IncorrectCustomerGroup(frappe.ValidationError): pass
class IncorrectSupplierType(frappe.ValidationError): pass
class ConflictingTaxRule(frappe.ValidationError): pass
class TaxRule(Document):
def __setup__(self):
self.flags.ignore_these_exceptions_in_test = [ConflictingTaxRule]
def validate(self):
self.validate_tax_template()
self.validate_date()
self.validate_filters()
def validate_tax_template(self):
if self.tax_type== "Sales":
self.purchase_tax_template = self.supplier = self.supplier_type= None
if self.customer:
self.customer_group = None
else:
self.sales_tax_template= self.customer = self.customer_group= None
if self.supplier:
self.supplier_type = None
if not (self.sales_tax_template or self.purchase_tax_template):
frappe.throw(_("Tax Template is mandatory."))
def validate_date(self):
if self.from_date and self.to_date and self.from_date > self.to_date:
frappe.throw(_("From Date cannot be greater than To Date"))
def validate_filters(self):
filters = {
"tax_type": self.tax_type,
"customer": self.customer,
"customer_group": self.customer_group,
"supplier": self.supplier,
"supplier_type": self.supplier_type,
"billing_city": self.billing_city,
"billing_state": self.billing_state,
"billing_country": self.billing_country,
"shipping_city": self.shipping_city,
"shipping_state": self.shipping_state,
"shipping_country": self.shipping_country,
"company": self.company
}
conds=""
for d in filters:
if conds:
conds += " and "
conds += """ifnull({0}, '') = '{1}'""".format(d, frappe.db.escape(cstr(filters[d])))
if self.from_date and self.to_date:
conds += """ and ((from_date > '{from_date}' and from_date < '{to_date}') or
(to_date > '{from_date}' and to_date < '{to_date}') or
('{from_date}' > from_date and '{from_date}' < to_date) or
('{from_date}' = from_date and '{to_date}' = to_date))""".format(from_date=self.from_date, to_date=self.to_date)
elif self.from_date and not self.to_date:
conds += """ and to_date > '{from_date}'""".format(from_date = self.from_date)
elif self.to_date and not self.from_date:
conds += """ and from_date < '{to_date}'""".format(to_date = self.to_date)
tax_rule = frappe.db.sql("select name, priority \
from `tabTax Rule` where {0} and name != '{1}'".format(conds, self.name), as_dict=1)
if tax_rule:
if tax_rule[0].priority == self.priority:
frappe.throw(_("Tax Rule Conflicts with {0}".format(tax_rule[0].name)), ConflictingTaxRule)
@frappe.whitelist()
def get_party_details(party, party_type, args=None):
out = {}
if args:
billing_filters= {"name": args.get("billing_address")}
shipping_filters= {"name": args.get("shipping_address")}
else:
billing_filters= {party_type: party, "is_primary_address": 1}
shipping_filters= {party_type:party, "is_shipping_address": 1}
billing_address= frappe.get_all("Address", fields=["city", "state", "country"], filters= billing_filters)
shipping_address= frappe.get_all("Address", fields=["city", "state", "country"], filters= shipping_filters)
if billing_address:
out["billing_city"]= billing_address[0].city
out["billing_state"]= billing_address[0].state
out["billing_country"]= billing_address[0].country
if shipping_address:
out["shipping_city"]= shipping_address[0].city
out["shipping_state"]= shipping_address[0].state
out["shipping_country"]= shipping_address[0].country
return out
def get_tax_template(posting_date, args):
"""Get matching tax rule"""
args = frappe._dict(args)
conditions = []
for key, value in args.iteritems():
if key in "use_for_shopping_cart":
conditions.append("use_for_shopping_cart = {0}".format(1 if value else 0))
else:
conditions.append("ifnull({0}, '') in ('', '{1}')".format(key, frappe.db.escape(cstr(value))))
matching = frappe.db.sql("""select * from `tabTax Rule`
where {0}""".format(" and ".join(conditions)), as_dict = True)
if not matching:
return None
for rule in matching:
rule.no_of_keys_matched = 0
for key in args:
if rule.get(key): rule.no_of_keys_matched += 1
rule = sorted(matching, lambda b, a: cmp(a.no_of_keys_matched, b.no_of_keys_matched) or cmp(a.priority, b.priority))[0]
return rule.sales_tax_template or rule.purchase_tax_template

View File

@@ -0,0 +1,28 @@
[
{
"doctype": "Tax Rule",
"tax_type" : "Sales",
"sales_tax_template": "_Test Tax 1",
"use_for_shopping_cart": 1,
"billing_city": "_Test City",
"billing_state": "Test State",
"billing_country": "India",
"shipping_city": "_Test City",
"shipping_country": "India",
"priority": 1,
"company": "_Test Company"
},
{
"doctype": "Tax Rule",
"tax_type" : "Sales",
"sales_tax_template": "_Test Tax 2",
"use_for_shopping_cart": 0,
"billing_city": "_Test City",
"billing_country": "India",
"shipping_city": "_Test City",
"shipping_state": "Test State",
"shipping_country": "India",
"priority": 2,
"company": "_Test Company"
}
]

View File

@@ -0,0 +1,130 @@
# -*- coding: utf-8 -*-
# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
from __future__ import unicode_literals
import frappe
import unittest
from erpnext.accounts.doctype.tax_rule.tax_rule import IncorrectCustomerGroup, IncorrectSupplierType, ConflictingTaxRule, get_tax_template
test_records = frappe.get_test_records('Tax Rule')
class TestTaxRule(unittest.TestCase):
def setUp(self):
frappe.db.sql("delete from `tabTax Rule` where use_for_shopping_cart <> 1")
def test_conflict(self):
tax_rule1 = make_tax_rule(customer= "_Test Customer",
sales_tax_template = "_Test Sales Taxes and Charges Template", priority = 1)
tax_rule1.save()
tax_rule2 = make_tax_rule(customer= "_Test Customer",
sales_tax_template = "_Test Sales Taxes and Charges Template", priority = 1)
self.assertRaises(ConflictingTaxRule, tax_rule2.save)
def test_conflict_with_non_overlapping_dates(self):
tax_rule1 = make_tax_rule(customer= "_Test Customer",
sales_tax_template = "_Test Sales Taxes and Charges Template", priority = 1, from_date = "2015-01-01")
tax_rule1.save()
tax_rule2 = make_tax_rule(customer= "_Test Customer",
sales_tax_template = "_Test Sales Taxes and Charges Template", priority = 1, to_date = "2013-01-01")
tax_rule2.save()
self.assertTrue(tax_rule2.name)
def test_conflict_with_overlapping_dates(self):
tax_rule1 = make_tax_rule(customer= "_Test Customer",
sales_tax_template = "_Test Sales Taxes and Charges Template", priority = 1, from_date = "2015-01-01", to_date = "2015-01-05")
tax_rule1.save()
tax_rule2 = make_tax_rule(customer= "_Test Customer",
sales_tax_template = "_Test Sales Taxes and Charges Template", priority = 1, from_date = "2015-01-03", to_date = "2015-01-09")
self.assertRaises(ConflictingTaxRule, tax_rule2.save)
def test_tax_template(self):
tax_rule = make_tax_rule()
self.assertEquals(tax_rule.purchase_tax_template, None)
def test_select_tax_rule_based_on_customer(self):
make_tax_rule(customer= "_Test Customer",
sales_tax_template = "_Test Sales Taxes and Charges Template", save=1)
make_tax_rule(customer= "_Test Customer 1",
sales_tax_template = "_Test Sales Taxes and Charges Template 1", save=1)
make_tax_rule(customer= "_Test Customer 2",
sales_tax_template = "_Test Sales Taxes and Charges Template 2", save=1)
self.assertEquals(get_tax_template("2015-01-01", {"customer":"_Test Customer 2"}),
"_Test Sales Taxes and Charges Template 2")
def test_select_tax_rule_based_on_better_match(self):
make_tax_rule(customer= "_Test Customer", billing_city = "Test City", billing_state = "Test State",
sales_tax_template = "_Test Sales Taxes and Charges Template", save=1)
make_tax_rule(customer= "_Test Customer", billing_city = "Test City1", billing_state = "Test State",
sales_tax_template = "_Test Sales Taxes and Charges Template 1", save=1)
self.assertEquals(get_tax_template("2015-01-01", {"customer":"_Test Customer", "billing_city": "Test City", "billing_state": "Test State"}),
"_Test Sales Taxes and Charges Template")
def test_select_tax_rule_based_on_state_match(self):
make_tax_rule(customer= "_Test Customer", shipping_state = "Test State",
sales_tax_template = "_Test Sales Taxes and Charges Template", save=1)
make_tax_rule(customer= "_Test Customer", shipping_state = "Test State12",
sales_tax_template = "_Test Sales Taxes and Charges Template 1", priority=2, save=1)
self.assertEquals(get_tax_template("2015-01-01", {"customer":"_Test Customer", "shipping_state": "Test State"}),
"_Test Sales Taxes and Charges Template")
def test_select_tax_rule_based_on_better_priority(self):
make_tax_rule(customer= "_Test Customer", billing_city = "Test City",
sales_tax_template = "_Test Sales Taxes and Charges Template", priority=1, save=1)
make_tax_rule(customer= "_Test Customer", billing_city = "Test City",
sales_tax_template = "_Test Sales Taxes and Charges Template 1", priority=2, save=1)
self.assertEquals(get_tax_template("2015-01-01", {"customer":"_Test Customer", "billing_city": "Test City"}),
"_Test Sales Taxes and Charges Template 1")
def test_select_tax_rule_based_cross_matching_keys(self):
make_tax_rule(customer= "_Test Customer", billing_city = "Test City",
sales_tax_template = "_Test Sales Taxes and Charges Template", save=1)
make_tax_rule(customer= "_Test Customer 1", billing_city = "Test City 1",
sales_tax_template = "_Test Sales Taxes and Charges Template 1", save=1)
self.assertEquals(get_tax_template("2015-01-01", {"customer":"_Test Customer", "billing_city": "Test City 1"}),
None)
def test_select_tax_rule_based_cross_partially_keys(self):
make_tax_rule(customer= "_Test Customer", billing_city = "Test City",
sales_tax_template = "_Test Sales Taxes and Charges Template", save=1)
make_tax_rule(billing_city = "Test City 1",
sales_tax_template = "_Test Sales Taxes and Charges Template 1", save=1)
self.assertEquals(get_tax_template("2015-01-01", {"customer":"_Test Customer", "billing_city": "Test City 1"}),
"_Test Sales Taxes and Charges Template 1")
def make_tax_rule(**args):
args = frappe._dict(args)
tax_rule = frappe.new_doc("Tax Rule")
for key, val in args.iteritems():
if key != "save":
tax_rule.set(key, val)
tax_rule.company = args.company or "_Test Company"
if args.save:
tax_rule.insert()
return tax_rule

View File

@@ -27,14 +27,25 @@ def process_gl_map(gl_map, merge_entries=True):
gl_map = merge_similar_entries(gl_map) gl_map = merge_similar_entries(gl_map)
for entry in gl_map: for entry in gl_map:
# toggle debit, credit if negative entry # toggle debit, credit if negative entry
if flt(entry.debit) < 0: if flt(entry.debit) < 0:
entry.credit = flt(entry.credit) - flt(entry.debit) entry.credit = flt(entry.credit) - flt(entry.debit)
entry.debit = 0.0 entry.debit = 0.0
if flt(entry.debit_in_account_currency) < 0:
entry.credit_in_account_currency = \
flt(entry.credit_in_account_currency) - flt(entry.debit_in_account_currency)
entry.debit_in_account_currency = 0.0
if flt(entry.credit) < 0: if flt(entry.credit) < 0:
entry.debit = flt(entry.debit) - flt(entry.credit) entry.debit = flt(entry.debit) - flt(entry.credit)
entry.credit = 0.0 entry.credit = 0.0
if flt(entry.credit_in_account_currency) < 0:
entry.debit_in_account_currency = \
flt(entry.debit_in_account_currency) - flt(entry.credit_in_account_currency)
entry.credit_in_account_currency = 0.0
return gl_map return gl_map
def merge_similar_entries(gl_map): def merge_similar_entries(gl_map):
@@ -45,7 +56,11 @@ def merge_similar_entries(gl_map):
same_head = check_if_in_list(entry, merged_gl_map) same_head = check_if_in_list(entry, merged_gl_map)
if same_head: if same_head:
same_head.debit = flt(same_head.debit) + flt(entry.debit) same_head.debit = flt(same_head.debit) + flt(entry.debit)
same_head.debit_in_account_currency = \
flt(same_head.debit_in_account_currency) + flt(entry.debit_in_account_currency)
same_head.credit = flt(same_head.credit) + flt(entry.credit) same_head.credit = flt(same_head.credit) + flt(entry.credit)
same_head.credit_in_account_currency = \
flt(same_head.credit_in_account_currency) + flt(entry.credit_in_account_currency)
else: else:
merged_gl_map.append(entry) merged_gl_map.append(entry)

View File

@@ -42,13 +42,13 @@ frappe.pages["Accounts Browser"].on_page_load = function(wrapper){
wrapper.page.add_menu_item(__('New Company'), function() { newdoc('Company'); }, true); wrapper.page.add_menu_item(__('New Company'), function() { newdoc('Company'); }, true);
} }
wrapper.page.set_secondary_action(__('Refresh'), function() { wrapper.page.add_menu_item(__('Refresh'), function() {
wrapper.$company_select.change(); wrapper.$company_select.change();
}); });
wrapper.page.set_primary_action(__('New'), function() { wrapper.page.set_primary_action(__('New'), function() {
erpnext.account_chart && erpnext.account_chart.make_new(); erpnext.account_chart && erpnext.account_chart.make_new();
}); }, "octicon octicon-plus");
// company-select // company-select
wrapper.$company_select = wrapper.page.add_select("Company", []) wrapper.$company_select = wrapper.page.add_select("Company", [])
@@ -121,7 +121,8 @@ erpnext.AccountsChart = Class.extend({
label: __("Add Child"), label: __("Add Child"),
click: function() { click: function() {
me.make_new() me.make_new()
} },
btnClass: "hidden-xs"
}, },
{ {
condition: function(node) { condition: function(node) {
@@ -137,8 +138,8 @@ erpnext.AccountsChart = Class.extend({
"company": me.company "company": me.company
}; };
frappe.set_route("query-report", "General Ledger"); frappe.set_route("query-report", "General Ledger");
} },
btnClass: "hidden-xs"
}, },
{ {
condition: function(node) { return !node.root && me.can_write }, condition: function(node) { return !node.root && me.can_write },
@@ -147,7 +148,8 @@ erpnext.AccountsChart = Class.extend({
frappe.model.rename_doc(me.ctype, node.label, function(new_name) { frappe.model.rename_doc(me.ctype, node.label, function(new_name) {
node.reload(); node.reload();
}); });
} },
btnClass: "hidden-xs"
}, },
{ {
condition: function(node) { return !node.root && me.can_delete }, condition: function(node) { return !node.root && me.can_delete },
@@ -156,14 +158,15 @@ erpnext.AccountsChart = Class.extend({
frappe.model.delete_doc(me.ctype, node.label, function() { frappe.model.delete_doc(me.ctype, node.label, function() {
node.parent.remove(); node.parent.remove();
}); });
} },
btnClass: "hidden-xs"
} }
], ],
onrender: function(node) { onrender: function(node) {
var dr_or_cr = node.data.balance < 0 ? "Cr" : "Dr"; var dr_or_cr = node.data.balance < 0 ? "Cr" : "Dr";
if (me.ctype == 'Account' && node.data && node.data.balance!==undefined) { if (me.ctype == 'Account' && node.data && node.data.balance!==undefined) {
$('<span class="balance-area pull-right text-muted small">' $('<span class="balance-area pull-right text-muted small">'
+ format_currency(Math.abs(node.data.balance), node.data.currency) + format_currency(Math.abs(node.data.balance), node.data.account_currency)
+ " " + dr_or_cr + " " + dr_or_cr
+ '</span>').insertBefore(node.$ul); + '</span>').insertBefore(node.$ul);
} }
@@ -211,7 +214,8 @@ erpnext.AccountsChart = Class.extend({
'Income Account', 'Tax', 'Chargeable', 'Temporary'].join('\n'), 'Income Account', 'Tax', 'Chargeable', 'Temporary'].join('\n'),
description: __("Optional. This setting will be used to filter in various transactions.") }, description: __("Optional. This setting will be used to filter in various transactions.") },
{fieldtype:'Float', fieldname:'tax_rate', label:__('Tax Rate')}, {fieldtype:'Float', fieldname:'tax_rate', label:__('Tax Rate')},
{fieldtype:'Link', fieldname:'warehouse', label:__('Warehouse'), options:"Warehouse"} {fieldtype:'Link', fieldname:'warehouse', label:__('Warehouse'), options:"Warehouse"},
{fieldtype:'Link', fieldname:'account_currency', label:__('Currency'), options:"Currency"}
] ]
}) })

View File

@@ -21,8 +21,7 @@ def get_children():
# root # root
if args['parent'] in ("Accounts", "Cost Centers"): if args['parent'] in ("Accounts", "Cost Centers"):
select_cond = ", root_type, report_type" if args["parent"]=="Accounts" else "" select_cond = ", root_type, report_type, account_currency" if ctype=="Account" else ""
acc = frappe.db.sql(""" select acc = frappe.db.sql(""" select
name as value, is_group as expandable %s name as value, is_group as expandable %s
from `tab%s` from `tab%s`
@@ -35,19 +34,17 @@ def get_children():
sort_root_accounts(acc) sort_root_accounts(acc)
else: else:
# other # other
select_cond = ", account_currency" if ctype=="Account" else ""
acc = frappe.db.sql("""select acc = frappe.db.sql("""select
name as value, is_group as expandable name as value, is_group as expandable %s
from `tab%s` from `tab%s`
where ifnull(parent_%s,'') = %s where ifnull(parent_%s,'') = %s
and docstatus<2 and docstatus<2
order by name""" % (ctype, ctype.lower().replace(' ','_'), '%s'), order by name""" % (select_cond, ctype, ctype.lower().replace(' ','_'), '%s'),
args['parent'], as_dict=1) args['parent'], as_dict=1)
if ctype == 'Account': if ctype == 'Account':
currency = frappe.db.sql("select default_currency from `tabCompany` where name = %s", company)[0][0]
for each in acc: for each in acc:
bal = get_balance_on(each.get("value")) each["balance"] = flt(get_balance_on(each.get("value")))
each["currency"] = currency
each["balance"] = flt(bal)
return acc return acc

View File

@@ -7,9 +7,12 @@ import frappe
import datetime import datetime
from frappe import _, msgprint, scrub from frappe import _, msgprint, scrub
from frappe.defaults import get_user_permissions from frappe.defaults import get_user_permissions
from frappe.utils import add_days, getdate, formatdate, flt, get_first_day, date_diff, nowdate from frappe.utils import add_days, getdate, formatdate, get_first_day, date_diff
from erpnext.utilities.doctype.address.address import get_address_display from erpnext.utilities.doctype.address.address import get_address_display
from erpnext.utilities.doctype.contact.contact import get_contact_details from erpnext.utilities.doctype.contact.contact import get_contact_details
from erpnext.exceptions import InvalidAccountCurrency
class DuplicatePartyAccountError(frappe.ValidationError): pass
@frappe.whitelist() @frappe.whitelist()
def get_party_details(party=None, account=None, party_type="Customer", company=None, def get_party_details(party=None, account=None, party_type="Customer", company=None,
@@ -17,7 +20,7 @@ def get_party_details(party=None, account=None, party_type="Customer", company=N
if not party: if not party:
return {} return {}
if not frappe.db.exists(party_type, party): if not frappe.db.exists(party_type, party):
frappe.throw(_("{0}: {1} does not exists").format(party_type, party)) frappe.throw(_("{0}: {1} does not exists").format(party_type, party))
@@ -26,7 +29,7 @@ def get_party_details(party=None, account=None, party_type="Customer", company=N
def _get_party_details(party=None, account=None, party_type="Customer", company=None, def _get_party_details(party=None, account=None, party_type="Customer", company=None,
posting_date=None, price_list=None, currency=None, doctype=None, ignore_permissions=False): posting_date=None, price_list=None, currency=None, doctype=None, ignore_permissions=False):
out = frappe._dict(set_account_and_due_date(party, account, party_type, company, posting_date, doctype)) out = frappe._dict(set_account_and_due_date(party, account, party_type, company, posting_date, doctype))
party = out[party_type.lower()] party = out[party_type.lower()]
@@ -40,6 +43,7 @@ def _get_party_details(party=None, account=None, party_type="Customer", company=
set_contact_details(out, party, party_type) set_contact_details(out, party, party_type)
set_other_values(out, party, party_type) set_other_values(out, party, party_type)
set_price_list(out, party, party_type, price_list) set_price_list(out, party, party_type, price_list)
out["taxes_and_charges"] = set_taxes(party.name, party_type, posting_date, company, out.customer_group, out.supplier_type)
if not out.get("currency"): if not out.get("currency"):
out["currency"] = currency out["currency"] = currency
@@ -47,9 +51,7 @@ def _get_party_details(party=None, account=None, party_type="Customer", company=
# sales team # sales team
if party_type=="Customer": if party_type=="Customer":
out["sales_team"] = [{ out["sales_team"] = [{
"sales_person": d.sales_person, "sales_person": d.sales_person
"sales_designation": d.sales_designation,
"allocated_percentage": d.allocated_percentage
} for d in party.get("sales_team")] } for d in party.get("sales_team")]
return out return out
@@ -96,11 +98,24 @@ def set_other_values(out, party, party_type):
out[f] = party.get(f) out[f] = party.get(f)
# fields prepended with default in Customer doctype # fields prepended with default in Customer doctype
for f in ['currency', 'taxes_and_charges'] \ for f in ['currency'] \
+ (['sales_partner', 'commission_rate'] if party_type=="Customer" else []): + (['sales_partner', 'commission_rate'] if party_type=="Customer" else []):
if party.get("default_" + f): if party.get("default_" + f):
out[f] = party.get("default_" + f) out[f] = party.get("default_" + f)
def get_default_price_list(party):
"""Return default price list for party (Document object)"""
if party.default_price_list:
return party.default_price_list
if party.doctype == "Customer":
price_list = frappe.db.get_value("Customer Group",
party.customer_group, "default_price_list")
if price_list:
return price_list
return None
def set_price_list(out, party, party_type, given_price_list): def set_price_list(out, party, party_type, given_price_list):
# price list # price list
price_list = filter(None, get_user_permissions().get("Price List", [])) price_list = filter(None, get_user_permissions().get("Price List", []))
@@ -108,11 +123,7 @@ def set_price_list(out, party, party_type, given_price_list):
price_list = price_list[0] if len(price_list)==1 else None price_list = price_list[0] if len(price_list)==1 else None
if not price_list: if not price_list:
price_list = party.default_price_list price_list = get_default_price_list(party)
if not price_list and party_type=="Customer":
price_list = frappe.db.get_value("Customer Group",
party.customer_group, "default_price_list")
if not price_list: if not price_list:
price_list = given_price_list price_list = given_price_list
@@ -131,7 +142,7 @@ def set_account_and_due_date(party, account, party_type, company, posting_date,
} }
if party: if party:
account = get_party_account(company, party, party_type) account = get_party_account(party_type, party, company)
account_fieldname = "debit_to" if party_type=="Customer" else "credit_to" account_fieldname = "debit_to" if party_type=="Customer" else "credit_to"
@@ -142,14 +153,21 @@ def set_account_and_due_date(party, account, party_type, company, posting_date,
} }
return out return out
def get_company_currency():
company_currency = frappe._dict()
for d in frappe.get_all("Company", fields=["name", "default_currency"]):
company_currency.setdefault(d.name, d.default_currency)
return company_currency
@frappe.whitelist() @frappe.whitelist()
def get_party_account(company, party, party_type): def get_party_account(party_type, party, company):
"""Returns the account for the given `party`. """Returns the account for the given `party`.
Will first search in party (Customer / Supplier) record, if not found, Will first search in party (Customer / Supplier) record, if not found,
will search in group (Customer Group / Supplier Type), will search in group (Customer Group / Supplier Type),
finally will return default.""" finally will return default."""
if not company: if not company:
frappe.throw(_("Please select company first.")) frappe.throw(_("Please select a Company"))
if party: if party:
account = frappe.db.get_value("Party Account", account = frappe.db.get_value("Party Account",
@@ -167,6 +185,42 @@ def get_party_account(company, party, party_type):
return account return account
def get_party_account_currency(party_type, party, company):
def generator():
party_account = get_party_account(party_type, party, company)
return frappe.db.get_value("Account", party_account, "account_currency")
return frappe.local_cache("party_account_currency", (party_type, party, company), generator)
def get_party_gle_currency(party_type, party, company):
def generator():
existing_gle_currency = frappe.db.sql("""select account_currency from `tabGL Entry`
where docstatus=1 and company=%(company)s and party_type=%(party_type)s and party=%(party)s
limit 1""", { "company": company, "party_type": party_type, "party": party })
return existing_gle_currency[0][0] if existing_gle_currency else None
return frappe.local_cache("party_gle_currency", (party_type, party, company), generator)
def validate_party_gle_currency(party_type, party, company):
"""Validate party account currency with existing GL Entry's currency"""
party_account_currency = get_party_account_currency(party_type, party, company)
existing_gle_currency = get_party_gle_currency(party_type, party, company)
if existing_gle_currency and party_account_currency != existing_gle_currency:
frappe.throw(_("Accounting Entry for {0}: {1} can only be made in currency: {2}")
.format(party_type, party, existing_gle_currency), InvalidAccountCurrency)
def validate_party_accounts(doc):
companies = []
for account in doc.get("accounts"):
if account.company in companies:
frappe.throw(_("There can only be 1 Account per Company in {0} {1}").format(doc.doctype, doc.name),
DuplicatePartyAccountError)
else:
companies.append(account.company)
@frappe.whitelist() @frappe.whitelist()
def get_due_date(posting_date, party_type, party, company): def get_due_date(posting_date, party_type, party, company):
"""Set Due Date = Posting Date + Credit Days""" """Set Due Date = Posting Date + Credit Days"""
@@ -183,7 +237,7 @@ def get_due_date(posting_date, party_type, party, company):
credit_days = get_credit_days(party_type, party, company) credit_days = get_credit_days(party_type, party, company)
if credit_days: if credit_days:
due_date = add_days(posting_date, credit_days) due_date = add_days(posting_date, credit_days)
return due_date return due_date
def get_credit_days(party_type, party, company): def get_credit_days(party_type, party, company):
@@ -191,30 +245,60 @@ def get_credit_days(party_type, party, company):
if party_type == "Customer": if party_type == "Customer":
credit_days_based_on, credit_days, customer_group = \ credit_days_based_on, credit_days, customer_group = \
frappe.db.get_value(party_type, party, ["credit_days_based_on", "credit_days", "customer_group"]) frappe.db.get_value(party_type, party, ["credit_days_based_on", "credit_days", "customer_group"])
if not credit_days_based_on: if not credit_days_based_on:
credit_days_based_on, credit_days = \ credit_days_based_on, credit_days = \
frappe.db.get_value("Customer Group", customer_group, ["credit_days_based_on", "credit_days"]) \ frappe.db.get_value("Customer Group", customer_group, ["credit_days_based_on", "credit_days"]) \
or frappe.db.get_value("Company", company, ["credit_days_based_on", "credit_days"]) or frappe.db.get_value("Company", company, ["credit_days_based_on", "credit_days"])
return credit_days_based_on, credit_days return credit_days_based_on, credit_days
else: else:
credit_days, supplier_type = frappe.db.get_value(party_type, party, ["credit_days", "supplier_type"]) credit_days, supplier_type = frappe.db.get_value(party_type, party, ["credit_days", "supplier_type"])
if not credit_days: if not credit_days:
credit_days = frappe.db.get_value("Supplier Type", supplier_type, "credit_days") \ credit_days = frappe.db.get_value("Supplier Type", supplier_type, "credit_days") \
or frappe.db.get_value("Company", company, "credit_days") or frappe.db.get_value("Company", company, "credit_days")
return credit_days return credit_days
def validate_due_date(posting_date, due_date, party_type, party, company): def validate_due_date(posting_date, due_date, party_type, party, company):
if getdate(due_date) < getdate(posting_date): if getdate(due_date) < getdate(posting_date):
frappe.throw(_("Due Date cannot be before Posting Date")) frappe.throw(_("Due Date cannot be before Posting Date"))
else: else:
default_due_date = get_due_date(posting_date, party_type, party, company) default_due_date = get_due_date(posting_date, party_type, party, company)
if not default_due_date:
return
if default_due_date != posting_date and getdate(due_date) > getdate(default_due_date): if default_due_date != posting_date and getdate(due_date) > getdate(default_due_date):
is_credit_controller = frappe.db.get_single_value("Accounts Settings", "credit_controller") in frappe.get_roles() is_credit_controller = frappe.db.get_single_value("Accounts Settings", "credit_controller") in frappe.get_roles()
if is_credit_controller: if is_credit_controller:
msgprint(_("Note: Due / Reference Date exceeds allowed customer credit days by {0} day(s)") msgprint(_("Note: Due / Reference Date exceeds allowed customer credit days by {0} day(s)")
.format(date_diff(due_date, default_due_date))) .format(date_diff(due_date, default_due_date)))
else: else:
frappe.throw(_("Due / Reference Date cannot be after {0}").format(formatdate(default_due_date))) frappe.throw(_("Due / Reference Date cannot be after {0}").format(formatdate(default_due_date)))
@frappe.whitelist()
def set_taxes(party, party_type, posting_date, company, customer_group=None, supplier_type=None,
billing_address=None, shipping_address=None, use_for_shopping_cart=None):
from erpnext.accounts.doctype.tax_rule.tax_rule import get_tax_template, get_party_details
args = {
party_type.lower(): party,
"customer_group": customer_group,
"supplier_type": supplier_type,
"company": company
}
if billing_address or shipping_address:
args.update(get_party_details(party, party_type, {"billing_address": billing_address, \
"shipping_address": shipping_address }))
else:
args.update(get_party_details(party, party_type))
if party_type=="Customer":
args.update({"tax_type": "Sales"})
else:
args.update({"tax_type": "Purchase"})
if use_for_shopping_cart:
args.update({"use_for_shopping_cart": use_for_shopping_cart})
return get_tax_template(posting_date, args)

View File

@@ -30,19 +30,47 @@ class ReceivablePayableReport(object):
if args.get("party_type") == "Supplier": if args.get("party_type") == "Supplier":
columns += [_("Bill No") + "::80", _("Bill Date") + ":Date:80"] columns += [_("Bill No") + "::80", _("Bill Date") + ":Date:80"]
columns += [_("Invoiced Amount") + ":Currency:100", _("Paid Amount") + ":Currency:100", for label in ("Invoiced Amount", "Paid Amount", "Outstanding Amount"):
_("Outstanding Amount") + ":Currency:100", _("Age") + ":Int:50", columns.append({
"0-" + str(self.filters.range1) + ":Currency:100", "label": label,
str(self.filters.range1) + "-" + str(self.filters.range2) + ":Currency:100", "fieldtype": "Currency",
str(self.filters.range2) + "-" + str(self.filters.range3) + ":Currency:100", "options": "currency",
str(self.filters.range3) + _("-Above") + ":Currency:100" "width": 120
] })
columns += [_("Age (Days)") + "::80"]
if not "range1" in self.filters:
self.filters["range1"] = "30"
if not "range2" in self.filters:
self.filters["range2"] = "60"
if not "range3" in self.filters:
self.filters["range3"] = "90"
for label in ("0-{range1}".format(**self.filters),
"{range1}-{range2}".format(**self.filters),
"{range2}-{range3}".format(**self.filters),
"{range3}-{above}".format(range3=self.filters.range3, above=_("Above"))):
columns.append({
"label": label,
"fieldtype": "Currency",
"options": "currency",
"width": 120
})
if args.get("party_type") == "Customer": if args.get("party_type") == "Customer":
columns += [_("Territory") + ":Link/Territory:80"] columns += [_("Territory") + ":Link/Territory:80"]
if args.get("party_type") == "Supplier": if args.get("party_type") == "Supplier":
columns += [_("Supplier Type") + ":Link/Supplier Type:80"] columns += [_("Supplier Type") + ":Link/Supplier Type:80"]
columns += [_("Remarks") + "::200"] columns += [
{
"fieldname": "currency",
"label": _("Currency"),
"fieldtype": "Data",
"width": 100,
},
_("Remarks") + "::200"
]
return columns return columns
@@ -55,6 +83,8 @@ class ReceivablePayableReport(object):
future_vouchers = self.get_entries_after(self.filters.report_date, args.get("party_type")) future_vouchers = self.get_entries_after(self.filters.report_date, args.get("party_type"))
company_currency = frappe.db.get_value("Company", self.filters.get("company"), "default_currency")
data = [] data = []
for gle in self.get_entries_till(self.filters.report_date, args.get("party_type")): for gle in self.get_entries_till(self.filters.report_date, args.get("party_type")):
if self.is_receivable_or_payable(gle, dr_or_cr, future_vouchers): if self.is_receivable_or_payable(gle, dr_or_cr, future_vouchers):
@@ -91,10 +121,16 @@ class ReceivablePayableReport(object):
# customer territory / supplier type # customer territory / supplier type
if args.get("party_type") == "Customer": if args.get("party_type") == "Customer":
row += [self.get_territory(gle.party), gle.remarks] row += [self.get_territory(gle.party)]
if args.get("party_type") == "Supplier": if args.get("party_type") == "Supplier":
row += [self.get_supplier_type(gle.party), gle.remarks] row += [self.get_supplier_type(gle.party)]
if self.filters.get(scrub(args.get("party_type"))):
row.append(gle.account_currency)
else:
row.append(company_currency)
row.append(gle.remarks)
data.append(row) data.append(row)
return data return data
@@ -155,7 +191,7 @@ class ReceivablePayableReport(object):
def get_voucher_details(self, party_type): def get_voucher_details(self, party_type):
voucher_details = frappe._dict() voucher_details = frappe._dict()
if party_type == "Customer": if party_type == "Customer":
for si in frappe.db.sql("""select name, due_date for si in frappe.db.sql("""select name, due_date
from `tabSales Invoice` where docstatus=1""", as_dict=1): from `tabSales Invoice` where docstatus=1""", as_dict=1):
@@ -171,17 +207,25 @@ class ReceivablePayableReport(object):
def get_gl_entries(self, party_type): def get_gl_entries(self, party_type):
if not hasattr(self, "gl_entries"): if not hasattr(self, "gl_entries"):
conditions, values = self.prepare_conditions(party_type) conditions, values = self.prepare_conditions(party_type)
self.gl_entries = frappe.db.sql("""select name, posting_date, account, party_type, party, debit, credit,
voucher_type, voucher_no, against_voucher_type, against_voucher from `tabGL Entry` if self.filters.get(scrub(party_type)):
where docstatus < 2 and party_type=%s {0} order by posting_date, party""" select_fields = "debit_in_account_currency as debit, credit_in_account_currency as credit"
.format(conditions), values, as_dict=True) else:
select_fields = "debit, credit"
self.gl_entries = frappe.db.sql("""select name, posting_date, account, party_type, party,
voucher_type, voucher_no, against_voucher_type, against_voucher, account_currency, remarks, {0}
from `tabGL Entry`
where docstatus < 2 and party_type=%s and ifnull(party, '') != '' {1}
order by posting_date, party"""
.format(select_fields, conditions), values, as_dict=True)
return self.gl_entries return self.gl_entries
def prepare_conditions(self, party_type): def prepare_conditions(self, party_type):
conditions = [""] conditions = [""]
values = [party_type] values = [party_type]
party_type_field = scrub(party_type) party_type_field = scrub(party_type)
if self.filters.company: if self.filters.company:
@@ -190,7 +234,7 @@ class ReceivablePayableReport(object):
if self.filters.get(party_type_field): if self.filters.get(party_type_field):
conditions.append("party=%s") conditions.append("party=%s")
values.append(self.filters.get(party_type_field)) values.append(self.filters.get(party_type_field))
return " and ".join(conditions), values return " and ".join(conditions), values

View File

@@ -23,7 +23,8 @@ def execute(filters=None):
total_debit += flt(d[2]) total_debit += flt(d[2])
total_credit += flt(d[3]) total_credit += flt(d[3])
amounts_not_reflected_in_system = frappe.db.sql("""select sum(ifnull(jvd.debit, 0) - ifnull(jvd.credit, 0)) amounts_not_reflected_in_system = frappe.db.sql("""
select sum(ifnull(jvd.debit_in_account_currency, 0) - ifnull(jvd.credit_in_account_currency, 0))
from `tabJournal Entry Account` jvd, `tabJournal Entry` jv from `tabJournal Entry Account` jvd, `tabJournal Entry` jv
where jvd.parent = jv.name and jv.docstatus=1 and jvd.account=%s where jvd.parent = jv.name and jv.docstatus=1 and jvd.account=%s
and jv.posting_date > %s and jv.clearance_date <= %s and ifnull(jv.is_opening, 'No') = 'No' and jv.posting_date > %s and jv.clearance_date <= %s and ifnull(jv.is_opening, 'No') = 'No'
@@ -38,7 +39,7 @@ def execute(filters=None):
data += [ data += [
get_balance_row(_("System Balance"), balance_as_per_system), get_balance_row(_("System Balance"), balance_as_per_system),
[""]*len(columns), [""]*len(columns),
["", '"' + _("Amounts not reflected in bank") + '"', total_debit, total_credit, "", "", "", ""], ["", '"' + _("Amounts not reflected in bank") + '"', total_debit, total_credit, "", "", "", "", ""],
get_balance_row(_("Amounts not reflected in system"), amounts_not_reflected_in_system), get_balance_row(_("Amounts not reflected in system"), amounts_not_reflected_in_system),
[""]*len(columns), [""]*len(columns),
get_balance_row(_("Expected balance as per bank"), bank_bal) get_balance_row(_("Expected balance as per bank"), bank_bal)
@@ -49,13 +50,14 @@ def execute(filters=None):
def get_columns(): def get_columns():
return [_("Posting Date") + ":Date:100", _("Journal Entry") + ":Link/Journal Entry:220", return [_("Posting Date") + ":Date:100", _("Journal Entry") + ":Link/Journal Entry:220",
_("Debit") + ":Currency:120", _("Credit") + ":Currency:120", _("Debit") + ":Currency:120", _("Credit") + ":Currency:120",
_("Against Account") + ":Link/Account:200", _("Reference") + "::100", _("Ref Date") + ":Date:110", _("Clearance Date") + ":Date:110" _("Against Account") + ":Link/Account:200", _("Reference") + "::100",
_("Ref Date") + ":Date:110", _("Clearance Date") + ":Date:110", _("Currency") + ":Link/Currency:70"
] ]
def get_entries(filters): def get_entries(filters):
entries = frappe.db.sql("""select entries = frappe.db.sql("""select
jv.posting_date, jv.name, jvd.debit, jvd.credit, jv.posting_date, jv.name, jvd.debit_in_account_currency, jvd.credit_in_account_currency,
jvd.against_account, jv.cheque_no, jv.cheque_date, jv.clearance_date jvd.against_account, jv.cheque_no, jv.cheque_date, jv.clearance_date, jvd.account_currency
from from
`tabJournal Entry Account` jvd, `tabJournal Entry` jv `tabJournal Entry Account` jvd, `tabJournal Entry` jv
where jvd.parent = jv.name and jv.docstatus=1 where jvd.parent = jv.name and jv.docstatus=1
@@ -68,6 +70,6 @@ def get_entries(filters):
def get_balance_row(label, amount): def get_balance_row(label, amount):
if amount > 0: if amount > 0:
return ["", '"' + label + '"', amount, 0, "", "", "", ""] return ["", '"' + label + '"', amount, 0, "", "", "", "", ""]
else: else:
return ["", '"' + label + '"', 0, abs(amount), "", "", "", ""] return ["", '"' + label + '"', 0, abs(amount), "", "", "", "", ""]

View File

@@ -5,6 +5,7 @@ from __future__ import unicode_literals
import frappe import frappe
from frappe.utils import flt, getdate, cstr from frappe.utils import flt, getdate, cstr
from frappe import _ from frappe import _
from erpnext.accounts.utils import get_account_currency
def execute(filters=None): def execute(filters=None):
account_details = {} account_details = {}
@@ -12,9 +13,12 @@ def execute(filters=None):
account_details.setdefault(acc.name, acc) account_details.setdefault(acc.name, acc)
validate_filters(filters, account_details) validate_filters(filters, account_details)
validate_party(filters) validate_party(filters)
columns = get_columns() filters = set_account_currency(filters)
columns = get_columns(filters)
res = get_result(filters, account_details) res = get_result(filters, account_details)
@@ -44,35 +48,76 @@ def validate_party(filters):
elif not frappe.db.exists(party_type, party): elif not frappe.db.exists(party_type, party):
frappe.throw(_("Invalid {0}: {1}").format(party_type, party)) frappe.throw(_("Invalid {0}: {1}").format(party_type, party))
def get_columns(): def set_account_currency(filters):
return [_("Posting Date") + ":Date:90", _("Account") + ":Link/Account:200", if not (filters.get("account") or filters.get("party")):
_("Debit") + ":Float:100", _("Credit") + ":Float:100", return filters
else:
filters["company_currency"] = frappe.db.get_value("Company", filters.company, "default_currency")
account_currency = None
if filters.get("account"):
account_currency = get_account_currency(filters.account)
elif filters.get("party"):
gle_currency = frappe.db.get_value("GL Entry", {"party_type": filters.party_type,
"party": filters.party, "company": filters.company}, "account_currency")
if gle_currency:
account_currency = gle_currency
else:
account_currency = frappe.db.get_value(filters.party_type, filters.party, "default_currency")
filters["account_currency"] = account_currency or filters.company_currency
if filters.account_currency != filters.company_currency:
filters["show_in_account_currency"] = 1
return filters
def get_columns(filters):
columns = [
_("Posting Date") + ":Date:90", _("Account") + ":Link/Account:200",
_("Debit") + ":Float:100", _("Credit") + ":Float:100"
]
if filters.get("show_in_account_currency"):
columns += [
_("Debit") + " (" + filters.account_currency + ")" + ":Float:100",
_("Credit") + " (" + filters.account_currency + ")" + ":Float:100"
]
columns += [
_("Voucher Type") + "::120", _("Voucher No") + ":Dynamic Link/Voucher Type:160", _("Voucher Type") + "::120", _("Voucher No") + ":Dynamic Link/Voucher Type:160",
_("Against Account") + "::120", _("Party Type") + "::80", _("Party") + "::150", _("Against Account") + "::120", _("Party Type") + "::80", _("Party") + "::150",
_("Cost Center") + ":Link/Cost Center:100", _("Remarks") + "::400"] _("Cost Center") + ":Link/Cost Center:100", _("Remarks") + "::400"
]
return columns
def get_result(filters, account_details): def get_result(filters, account_details):
gl_entries = get_gl_entries(filters) gl_entries = get_gl_entries(filters)
data = get_data_with_opening_closing(filters, account_details, gl_entries) data = get_data_with_opening_closing(filters, account_details, gl_entries)
result = get_result_as_list(data) result = get_result_as_list(data, filters)
return result return result
def get_gl_entries(filters): def get_gl_entries(filters):
select_fields = """, sum(ifnull(debit_in_account_currency, 0)) as debit_in_account_currency,
sum(ifnull(credit_in_account_currency, 0)) as credit_in_account_currency""" \
if filters.get("show_in_account_currency") else ""
group_by_condition = "group by voucher_type, voucher_no, account, cost_center" \ group_by_condition = "group by voucher_type, voucher_no, account, cost_center" \
if filters.get("group_by_voucher") else "group by name" if filters.get("group_by_voucher") else "group by name"
gl_entries = frappe.db.sql("""select posting_date, account, party_type, party, gl_entries = frappe.db.sql("""select posting_date, account, party_type, party,
sum(ifnull(debit, 0)) as debit, sum(ifnull(credit, 0)) as credit, sum(ifnull(debit, 0)) as debit, sum(ifnull(credit, 0)) as credit,
voucher_type, voucher_no, cost_center, remarks, against, is_opening voucher_type, voucher_no, cost_center, remarks, against, is_opening {select_fields}
from `tabGL Entry` from `tabGL Entry`
where company=%(company)s {conditions} where company=%(company)s {conditions}
{group_by_condition} {group_by_condition}
order by posting_date, account"""\ order by posting_date, account"""\
.format(conditions=get_conditions(filters), group_by_condition=group_by_condition), .format(select_fields=select_fields, conditions=get_conditions(filters),
filters, as_dict=1) group_by_condition=group_by_condition), filters, as_dict=1)
return gl_entries return gl_entries
@@ -91,7 +136,7 @@ def get_conditions(filters):
if filters.get("party"): if filters.get("party"):
conditions.append("party=%(party)s") conditions.append("party=%(party)s")
if not (filters.get("account") or filters.get("party") or filters.get("group_by_account")): if not (filters.get("account") or filters.get("party") or filters.get("group_by_account")):
conditions.append("posting_date >=%(from_date)s") conditions.append("posting_date >=%(from_date)s")
@@ -105,35 +150,56 @@ def get_data_with_opening_closing(filters, account_details, gl_entries):
data = [] data = []
gle_map = initialize_gle_map(gl_entries) gle_map = initialize_gle_map(gl_entries)
opening, total_debit, total_credit, gle_map = get_accountwise_gle(filters, gl_entries, gle_map) opening, total_debit, total_credit, opening_in_account_currency, total_debit_in_account_currency, \
total_credit_in_account_currency, gle_map = get_accountwise_gle(filters, gl_entries, gle_map)
# Opening for filtered account # Opening for filtered account
if filters.get("account") or filters.get("party"): if filters.get("account") or filters.get("party"):
data += [get_balance_row(_("Opening"), opening), {}] data += [get_balance_row(_("Opening"), opening, opening_in_account_currency), {}]
for acc, acc_dict in gle_map.items(): if filters.get("group_by_account"):
if acc_dict.entries: for acc, acc_dict in gle_map.items():
# Opening for individual ledger, if grouped by account if acc_dict.entries:
if filters.get("group_by_account"): # Opening for individual ledger, if grouped by account
data.append(get_balance_row(_("Opening"), acc_dict.opening)) data.append(get_balance_row(_("Opening"), acc_dict.opening,
acc_dict.opening_in_account_currency))
data += acc_dict.entries data += acc_dict.entries
# Totals and closing for individual ledger, if grouped by account
account_closing = acc_dict.opening + acc_dict.total_debit - acc_dict.total_credit
account_closing_in_account_currency = acc_dict.opening_in_account_currency \
+ acc_dict.total_debit_in_account_currency - acc_dict.total_credit_in_account_currency
# Totals and closing for individual ledger, if grouped by account
if filters.get("group_by_account"):
data += [{"account": "'" + _("Totals") + "'", "debit": acc_dict.total_debit, data += [{"account": "'" + _("Totals") + "'", "debit": acc_dict.total_debit,
"credit": acc_dict.total_credit}, "credit": acc_dict.total_credit},
get_balance_row(_("Closing (Opening + Totals)"), get_balance_row(_("Closing (Opening + Totals)"),
(acc_dict.opening + acc_dict.total_debit - acc_dict.total_credit)), {}] account_closing, account_closing_in_account_currency), {}]
else:
for gl in gl_entries:
if gl.posting_date >= getdate(filters.from_date) and gl.posting_date <= getdate(filters.to_date):
data.append(gl)
# Total debit and credit between from and to date # Total debit and credit between from and to date
if total_debit or total_credit: if total_debit or total_credit:
data.append({"account": "'" + _("Totals") + "'", "debit": total_debit, "credit": total_credit}) data.append({
"account": "'" + _("Totals") + "'",
"debit": total_debit,
"credit": total_credit,
"debit_in_account_currency": total_debit_in_account_currency,
"credit_in_account_currency": total_credit_in_account_currency
})
# Closing for filtered account # Closing for filtered account
if filters.get("account") or filters.get("party"): if filters.get("account") or filters.get("party"):
closing = opening + total_debit - total_credit
closing_in_account_currency = opening_in_account_currency + \
total_debit_in_account_currency - total_credit_in_account_currency
data.append(get_balance_row(_("Closing (Opening + Totals)"), data.append(get_balance_row(_("Closing (Opening + Totals)"),
(opening + total_debit - total_credit))) closing, closing_in_account_currency))
return data return data
@@ -142,23 +208,38 @@ def initialize_gle_map(gl_entries):
for gle in gl_entries: for gle in gl_entries:
gle_map.setdefault(gle.account, frappe._dict({ gle_map.setdefault(gle.account, frappe._dict({
"opening": 0, "opening": 0,
"opening_in_account_currency": 0,
"entries": [], "entries": [],
"total_debit": 0, "total_debit": 0,
"total_debit_in_account_currency": 0,
"total_credit": 0, "total_credit": 0,
"closing": 0 "total_credit_in_account_currency": 0,
"closing": 0,
"closing_in_account_currency": 0
})) }))
return gle_map return gle_map
def get_accountwise_gle(filters, gl_entries, gle_map): def get_accountwise_gle(filters, gl_entries, gle_map):
opening, total_debit, total_credit = 0, 0, 0 opening, total_debit, total_credit = 0, 0, 0
opening_in_account_currency, total_debit_in_account_currency, total_credit_in_account_currency = 0, 0, 0
from_date, to_date = getdate(filters.from_date), getdate(filters.to_date) from_date, to_date = getdate(filters.from_date), getdate(filters.to_date)
for gle in gl_entries: for gle in gl_entries:
amount = flt(gle.debit, 3) - flt(gle.credit, 3) amount = flt(gle.debit, 3) - flt(gle.credit, 3)
amount_in_account_currency = flt(gle.debit_in_account_currency, 3) - flt(gle.credit_in_account_currency, 3)
if (filters.get("account") or filters.get("party") or filters.get("group_by_account")) \ if (filters.get("account") or filters.get("party") or filters.get("group_by_account")) \
and (gle.posting_date < from_date or cstr(gle.is_opening) == "Yes"): and (gle.posting_date < from_date or cstr(gle.is_opening) == "Yes"):
gle_map[gle.account].opening += amount gle_map[gle.account].opening += amount
if filters.get("show_in_account_currency"):
gle_map[gle.account].opening_in_account_currency += amount_in_account_currency
if filters.get("account") or filters.get("party"): if filters.get("account") or filters.get("party"):
opening += amount opening += amount
if filters.get("show_in_account_currency"):
opening_in_account_currency += amount_in_account_currency
elif gle.posting_date <= to_date: elif gle.posting_date <= to_date:
gle_map[gle.account].entries.append(gle) gle_map[gle.account].entries.append(gle)
gle_map[gle.account].total_debit += flt(gle.debit, 3) gle_map[gle.account].total_debit += flt(gle.debit, 3)
@@ -167,21 +248,43 @@ def get_accountwise_gle(filters, gl_entries, gle_map):
total_debit += flt(gle.debit, 3) total_debit += flt(gle.debit, 3)
total_credit += flt(gle.credit, 3) total_credit += flt(gle.credit, 3)
return opening, total_debit, total_credit, gle_map if filters.get("show_in_account_currency"):
gle_map[gle.account].total_debit_in_account_currency += flt(gle.debit_in_account_currency, 3)
gle_map[gle.account].total_credit_in_account_currency += flt(gle.credit_in_account_currency, 3)
def get_balance_row(label, balance): total_debit_in_account_currency += flt(gle.debit_in_account_currency, 3)
return { total_credit_in_account_currency += flt(gle.credit_in_account_currency, 3)
return opening, total_debit, total_credit, opening_in_account_currency, \
total_debit_in_account_currency, total_credit_in_account_currency, gle_map
def get_balance_row(label, balance, balance_in_account_currency=None):
balance_row = {
"account": "'" + label + "'", "account": "'" + label + "'",
"debit": balance if balance > 0 else 0, "debit": balance if balance > 0 else 0,
"credit": -1*balance if balance < 0 else 0, "credit": -1*balance if balance < 0 else 0
} }
def get_result_as_list(data): if balance_in_account_currency != None:
balance_row.update({
"debit_in_account_currency": balance_in_account_currency if balance_in_account_currency > 0 else 0,
"credit_in_account_currency": -1*balance_in_account_currency if balance_in_account_currency < 0 else 0
})
return balance_row
def get_result_as_list(data, filters):
result = [] result = []
for d in data: for d in data:
result.append([d.get("posting_date"), d.get("account"), d.get("debit"), row = [d.get("posting_date"), d.get("account"), d.get("debit"), d.get("credit")]
d.get("credit"), d.get("voucher_type"), d.get("voucher_no"),
d.get("against"), d.get("party_type"), d.get("party"), if filters.get("show_in_account_currency"):
d.get("cost_center"), d.get("remarks")]) row += [d.get("debit_in_account_currency"), d.get("credit_in_account_currency")]
row += [d.get("voucher_type"), d.get("voucher_no"), d.get("against"),
d.get("party_type"), d.get("party"), d.get("cost_center"), d.get("remarks")
]
result.append(row)
return result return result

View File

@@ -226,7 +226,7 @@ class GrossProfitGenerator(object):
inner join `tabSales Invoice Item` item on item.parent = si.name inner join `tabSales Invoice Item` item on item.parent = si.name
left join `tabSales Team` sales on sales.parent = si.name left join `tabSales Team` sales on sales.parent = si.name
where where
si.docstatus = 1 %s si.docstatus = 1 and si.is_return != 1 %s
order by order by
si.posting_date desc, si.posting_time desc""" % (conditions,), self.filters, as_dict=1) si.posting_date desc, si.posting_time desc""" % (conditions,), self.filters, as_dict=1)

View File

@@ -0,0 +1,59 @@
// Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.query_reports["Trial Balance for Party"] = {
"filters": [
{
"fieldname": "company",
"label": __("Company"),
"fieldtype": "Link",
"options": "Company",
"default": frappe.defaults.get_user_default("company"),
"reqd": 1
},
{
"fieldname": "fiscal_year",
"label": __("Fiscal Year"),
"fieldtype": "Link",
"options": "Fiscal Year",
"default": frappe.defaults.get_user_default("fiscal_year"),
"reqd": 1,
"on_change": function(query_report) {
var fiscal_year = query_report.get_values().fiscal_year;
if (!fiscal_year) {
return;
}
frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) {
var fy = frappe.model.get_doc("Fiscal Year", fiscal_year);
query_report.filters_by_name.from_date.set_input(fy.year_start_date);
query_report.filters_by_name.to_date.set_input(fy.year_end_date);
query_report.trigger_refresh();
});
}
},
{
"fieldname": "from_date",
"label": __("From Date"),
"fieldtype": "Date",
"default": frappe.defaults.get_user_default("year_start_date"),
},
{
"fieldname": "to_date",
"label": __("To Date"),
"fieldtype": "Date",
"default": frappe.defaults.get_user_default("year_end_date"),
},
{
"fieldname":"party_type",
"label": __("Party Type"),
"fieldtype": "Select",
"options": ["Customer", "Supplier"],
"default": "Customer"
},
{
"fieldname": "show_zero_values",
"label": __("Show zero values"),
"fieldtype": "Check"
}
]
}

View File

@@ -0,0 +1,17 @@
{
"add_total_row": 0,
"apply_user_permissions": 1,
"creation": "2015-09-22 10:28:45.762272",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"is_standard": "Yes",
"modified": "2015-09-22 10:28:45.762272",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Trial Balance for Party",
"owner": "Administrator",
"ref_doctype": "GL Entry",
"report_name": "Trial Balance for Party",
"report_type": "Script Report"
}

View File

@@ -0,0 +1,195 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.utils import flt, cint
from erpnext.accounts.report.trial_balance.trial_balance import validate_filters
def execute(filters=None):
validate_filters(filters)
show_party_name = is_party_name_visible(filters)
columns = get_columns(filters, show_party_name)
data = get_data(filters, show_party_name)
return columns, data
def get_data(filters, show_party_name):
party_name_field = "customer_name" if filters.get("party_type")=="Customer" else "supplier_name"
parties = frappe.get_all(filters.get("party_type"), fields = ["name", party_name_field], order_by="name")
opening_balances = get_opening_balances(filters)
balances_within_period = get_balances_within_period(filters)
data = []
total_debit, total_credit = 0, 0
for party in parties:
row = { "party": party.name }
if show_party_name:
row["party_name"] = party.get(party_name_field)
# opening
opening_debit, opening_credit = opening_balances.get(party.name, [0, 0])
row.update({
"opening_debit": opening_debit,
"opening_credit": opening_credit
})
# within period
debit, credit = balances_within_period.get(party.name, [0, 0])
row.update({
"debit": debit,
"credit": credit
})
# totals
total_debit += debit
total_credit += credit
# closing
closing_debit, closing_credit = toggle_debit_credit(opening_debit + debit, opening_credit + credit)
row.update({
"closing_debit": closing_debit,
"closing_credit": closing_credit
})
has_value = False
if (opening_debit or opening_credit or debit or credit or closing_debit or closing_credit):
has_value =True
if cint(filters.show_zero_values) or has_value:
data.append(row)
# Add total row
if total_debit or total_credit:
data.append({
"party": "'" + _("Totals") + "'",
"debit": total_debit,
"credit": total_credit
})
return data
def get_opening_balances(filters):
gle = frappe.db.sql("""
select party, sum(ifnull(debit, 0)) as opening_debit, sum(ifnull(credit, 0)) as opening_credit
from `tabGL Entry`
where company=%(company)s
and ifnull(party_type, '') = %(party_type)s and ifnull(party, '') != ''
and (posting_date < %(from_date)s or ifnull(is_opening, 'No') = 'Yes')
group by party""", {
"company": filters.company,
"from_date": filters.from_date,
"party_type": filters.party_type
}, as_dict=True)
opening = frappe._dict()
for d in gle:
opening_debit, opening_credit = toggle_debit_credit(d.opening_debit, d.opening_credit)
opening.setdefault(d.party, [opening_debit, opening_credit])
return opening
def get_balances_within_period(filters):
gle = frappe.db.sql("""
select party, sum(ifnull(debit, 0)) as debit, sum(ifnull(credit, 0)) as credit
from `tabGL Entry`
where company=%(company)s
and ifnull(party_type, '') = %(party_type)s and ifnull(party, '') != ''
and posting_date >= %(from_date)s and posting_date <= %(to_date)s
and ifnull(is_opening, 'No') = 'No'
group by party""", {
"company": filters.company,
"from_date": filters.from_date,
"to_date": filters.to_date,
"party_type": filters.party_type
}, as_dict=True)
balances_within_period = frappe._dict()
for d in gle:
balances_within_period.setdefault(d.party, [d.debit, d.credit])
return balances_within_period
def toggle_debit_credit(debit, credit):
if flt(debit) > flt(credit):
debit = flt(debit) - flt(credit)
credit = 0.0
else:
credit = flt(credit) - flt(debit)
debit = 0.0
return debit, credit
def get_columns(filters, show_party_name):
columns = [
{
"fieldname": "party",
"label": _(filters.party_type),
"fieldtype": "Link",
"options": filters.party_type,
"width": 200
},
{
"fieldname": "opening_debit",
"label": _("Opening (Dr)"),
"fieldtype": "Currency",
"width": 120
},
{
"fieldname": "opening_credit",
"label": _("Opening (Cr)"),
"fieldtype": "Currency",
"width": 120
},
{
"fieldname": "debit",
"label": _("Debit"),
"fieldtype": "Currency",
"width": 120
},
{
"fieldname": "credit",
"label": _("Credit"),
"fieldtype": "Currency",
"width": 120
},
{
"fieldname": "closing_debit",
"label": _("Closing (Dr)"),
"fieldtype": "Currency",
"width": 120
},
{
"fieldname": "closing_credit",
"label": _("Closing (Cr)"),
"fieldtype": "Currency",
"width": 120
}
]
if show_party_name:
columns.insert(1, {
"fieldname": "party_name",
"label": _(filters.party_type) + " Name",
"fieldtype": "Data",
"width": 200
})
return columns
def is_party_name_visible(filters):
show_party_name = False
if filters.get("party_type") == "Customer":
party_naming_by = frappe.db.get_single_value("Selling Settings", "cust_master_name")
else:
party_naming_by = frappe.db.get_single_value("Buying Settings", "supp_master_name")
if party_naming_by == "Naming Series":
show_party_name = True
return show_party_name

View File

@@ -9,6 +9,9 @@ from frappe import throw, _
from frappe.utils import formatdate from frappe.utils import formatdate
import frappe.desk.reportview import frappe.desk.reportview
# imported to enable erpnext.accounts.utils.get_account_currency
from erpnext.accounts.doctype.account.account import get_account_currency
class FiscalYearError(frappe.ValidationError): pass class FiscalYearError(frappe.ValidationError): pass
class BudgetError(frappe.ValidationError): pass class BudgetError(frappe.ValidationError): pass
@@ -50,7 +53,7 @@ def validate_fiscal_year(date, fiscal_year, label=_("Date"), doc=None):
throw(_("{0} '{1}' not in Fiscal Year {2}").format(label, formatdate(date), fiscal_year)) throw(_("{0} '{1}' not in Fiscal Year {2}").format(label, formatdate(date), fiscal_year))
@frappe.whitelist() @frappe.whitelist()
def get_balance_on(account=None, date=None, party_type=None, party=None): def get_balance_on(account=None, date=None, party_type=None, party=None, in_account_currency=True):
if not account and frappe.form_dict.get("account"): if not account and frappe.form_dict.get("account"):
account = frappe.form_dict.get("account") account = frappe.form_dict.get("account")
if not date and frappe.form_dict.get("date"): if not date and frappe.form_dict.get("date"):
@@ -94,18 +97,27 @@ def get_balance_on(account=None, date=None, party_type=None, party=None):
select name from `tabAccount` ac where ac.name = gle.account select name from `tabAccount` ac where ac.name = gle.account
and ac.lft >= %s and ac.rgt <= %s and ac.lft >= %s and ac.rgt <= %s
)""" % (acc.lft, acc.rgt)) )""" % (acc.lft, acc.rgt))
# If group and currency same as company,
# always return balance based on debit and credit in company currency
if acc.account_currency == frappe.db.get_value("Company", acc.company, "default_currency"):
in_account_currency = False
else: else:
cond.append("""gle.account = "%s" """ % (account.replace('"', '\\"'), )) cond.append("""gle.account = "%s" """ % (account.replace('"', '\\"'), ))
if party_type and party: if party_type and party:
cond.append("""gle.party_type = "%s" and gle.party = "%s" """ % cond.append("""gle.party_type = "%s" and gle.party = "%s" """ %
(party_type.replace('"', '\\"'), party.replace('"', '\\"'))) (party_type.replace('"', '\\"'), party.replace('"', '\\"')))
if account or (party_type and party): if account or (party_type and party):
if in_account_currency:
select_field = "sum(ifnull(debit_in_account_currency, 0)) - sum(ifnull(credit_in_account_currency, 0))"
else:
select_field = "sum(ifnull(debit, 0)) - sum(ifnull(credit, 0))"
bal = frappe.db.sql(""" bal = frappe.db.sql("""
SELECT sum(ifnull(debit, 0)) - sum(ifnull(credit, 0)) SELECT {0}
FROM `tabGL Entry` gle FROM `tabGL Entry` gle
WHERE %s""" % " and ".join(cond))[0][0] WHERE {1}""".format(select_field, " and ".join(cond)))[0][0]
# if bal is None, return 0 # if bal is None, return 0
return flt(bal) return flt(bal)
@@ -186,6 +198,8 @@ def update_against_doc(d, jv_obj):
""" """
jv_detail = jv_obj.get("accounts", {"name": d["voucher_detail_no"]})[0] jv_detail = jv_obj.get("accounts", {"name": d["voucher_detail_no"]})[0]
jv_detail.set(d["dr_or_cr"], d["allocated_amt"]) jv_detail.set(d["dr_or_cr"], d["allocated_amt"])
jv_detail.set('debit' if d['dr_or_cr']=='debit_in_account_currency' else 'credit',
d["allocated_amt"]*flt(jv_detail.exchange_rate))
original_reference_type = jv_detail.reference_type original_reference_type = jv_detail.reference_type
original_reference_name = jv_detail.reference_name original_reference_name = jv_detail.reference_name
@@ -194,21 +208,35 @@ def update_against_doc(d, jv_obj):
jv_detail.set("reference_name", d["against_voucher"]) jv_detail.set("reference_name", d["against_voucher"])
if d['allocated_amt'] < d['unadjusted_amt']: if d['allocated_amt'] < d['unadjusted_amt']:
jvd = frappe.db.sql("""select cost_center, balance, against_account, is_advance jvd = frappe.db.sql("""
from `tabJournal Entry Account` where name = %s""", d['voucher_detail_no']) select cost_center, balance, against_account, is_advance, account_type, exchange_rate
from `tabJournal Entry Account` where name = %s
""", d['voucher_detail_no'], as_dict=True)
amount_in_account_currency = flt(d['unadjusted_amt']) - flt(d['allocated_amt'])
amount_in_company_currency = amount_in_account_currency * flt(jvd[0]['exchange_rate'])
# new entry with balance amount # new entry with balance amount
ch = jv_obj.append("accounts") ch = jv_obj.append("accounts")
ch.account = d['account'] ch.account = d['account']
ch.account_type = jvd[0]['account_type']
ch.exchange_rate = jvd[0]['exchange_rate']
ch.party_type = d["party_type"] ch.party_type = d["party_type"]
ch.party = d["party"] ch.party = d["party"]
ch.cost_center = cstr(jvd[0][0]) ch.cost_center = cstr(jvd[0]["cost_center"])
ch.balance = flt(jvd[0][1]) ch.balance = flt(jvd[0]["balance"])
ch.set(d['dr_or_cr'], flt(d['unadjusted_amt']) - flt(d['allocated_amt']))
ch.set(d['dr_or_cr']== 'debit' and 'credit' or 'debit', 0) ch.set(d['dr_or_cr'], amount_in_account_currency)
ch.against_account = cstr(jvd[0][2]) ch.set('debit' if d['dr_or_cr']=='debit_in_account_currency' else 'credit', amount_in_company_currency)
ch.set('credit_in_account_currency' if d['dr_or_cr']== 'debit_in_account_currency'
else 'debit_in_account_currency', 0)
ch.set('credit' if d['dr_or_cr']== 'debit_in_account_currency' else 'debit', 0)
ch.against_account = cstr(jvd[0]["against_account"])
ch.reference_type = original_reference_type ch.reference_type = original_reference_type
ch.reference_name = original_reference_name ch.reference_name = original_reference_name
ch.is_advance = cstr(jvd[0][3]) ch.is_advance = cstr(jvd[0]["is_advance"])
ch.docstatus = 1 ch.docstatus = 1
# will work as update after submit # will work as update after submit
@@ -273,7 +301,7 @@ def get_stock_and_account_difference(account_list=None, posting_date=None):
and name in (%s)""" % ', '.join(['%s']*len(account_list)), account_list)) and name in (%s)""" % ', '.join(['%s']*len(account_list)), account_list))
for account, warehouse in account_warehouse.items(): for account, warehouse in account_warehouse.items():
account_balance = get_balance_on(account, posting_date) account_balance = get_balance_on(account, posting_date, in_account_currency=False)
stock_value = get_stock_value_on(warehouse, posting_date) stock_value = get_stock_value_on(warehouse, posting_date)
if abs(flt(stock_value) - flt(account_balance)) > 0.005: if abs(flt(stock_value) - flt(account_balance)) > 0.005:
difference.setdefault(account, flt(stock_value) - flt(account_balance)) difference.setdefault(account, flt(stock_value) - flt(account_balance))
@@ -378,7 +406,7 @@ def get_stock_rbnb_difference(posting_date, company):
# Balance as per system # Balance as per system
stock_rbnb_account = "Stock Received But Not Billed - " + frappe.db.get_value("Company", company, "abbr") stock_rbnb_account = "Stock Received But Not Billed - " + frappe.db.get_value("Company", company, "abbr")
sys_bal = get_balance_on(stock_rbnb_account, posting_date) sys_bal = get_balance_on(stock_rbnb_account, posting_date, in_account_currency=False)
# Amount should be credited # Amount should be credited
return flt(stock_rbnb) + flt(sys_bal) return flt(stock_rbnb) + flt(sys_bal)
@@ -393,6 +421,11 @@ def get_outstanding_invoices(amount_query, account, party_type, party):
`tabGL Entry` `tabGL Entry`
where where
account = %s and party_type=%s and party=%s and {amount_query} > 0 account = %s and party_type=%s and party=%s and {amount_query} > 0
and (CASE
WHEN voucher_type = 'Journal Entry'
THEN ifnull(against_voucher, '') = ''
ELSE 1=1
END)
group by voucher_type, voucher_no group by voucher_type, voucher_no
""".format(amount_query = amount_query), (account, party_type, party), as_dict = True) """.format(amount_query = amount_query), (account, party_type, party), as_dict = True)

View File

@@ -157,20 +157,9 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
}, },
add_deduct_tax: function(doc, cdt, cdn) { add_deduct_tax: function(doc, cdt, cdn) {
this.calculate_taxes_and_totals(); this.calculate_taxes_and_totals();
},
calculate_outstanding_amount: function() {
if(this.frm.doc.doctype == "Purchase Invoice" && this.frm.doc.docstatus < 2) {
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"));
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"));
}
}
} }
}); });
cur_frm.add_fetch('project_name', 'cost_center', 'cost_center'); cur_frm.add_fetch('project_name', 'cost_center', 'cost_center');
erpnext.buying.get_default_bom = function(frm) { erpnext.buying.get_default_bom = function(frm) {

View File

@@ -43,7 +43,7 @@
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Title", "label": "Title",
"no_copy": 0, "no_copy": 1,
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
@@ -528,7 +528,7 @@
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 1, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "items", "fieldname": "items",
@@ -1704,7 +1704,7 @@
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 1, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "supplied_items", "fieldname": "supplied_items",
@@ -2032,7 +2032,7 @@
"is_submittable": 1, "is_submittable": 1,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-08-27 06:26:20.233037", "modified": "2015-09-30 08:52:56.137185",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Purchase Order", "name": "Purchase Order",
@@ -2040,7 +2040,7 @@
"permissions": [ "permissions": [
{ {
"amend": 0, "amend": 0,
"apply_user_permissions": 1, "apply_user_permissions": 0,
"cancel": 0, "cancel": 0,
"create": 0, "create": 0,
"delete": 0, "delete": 0,
@@ -2080,7 +2080,7 @@
}, },
{ {
"amend": 1, "amend": 1,
"apply_user_permissions": 1, "apply_user_permissions": 0,
"cancel": 1, "cancel": 1,
"create": 1, "create": 1,
"delete": 1, "delete": 1,
@@ -2100,7 +2100,7 @@
}, },
{ {
"amend": 0, "amend": 0,
"apply_user_permissions": 1, "apply_user_permissions": 0,
"cancel": 0, "cancel": 0,
"create": 0, "create": 0,
"delete": 0, "delete": 0,

View File

@@ -166,7 +166,7 @@ class PurchaseOrder(BuyingController):
self.update_ordered_qty() self.update_ordered_qty()
msgprint(_("Status of {0} {1} is now {2}").format(self.doctype, self.name, status)) msgprint(_("Status of {0} {1} is now {2}").format(self.doctype, self.name, status))
self.notify_modified() self.notify_update()
clear_doctype_notifications(self) clear_doctype_notifications(self)
def on_submit(self): def on_submit(self):

View File

@@ -4,17 +4,17 @@ frappe.listview_settings['Purchase Order'] = {
get_indicator: function(doc) { get_indicator: function(doc) {
if(doc.status==="Stopped") { if(doc.status==="Stopped") {
return [__("Stopped"), "darkgrey", "status,=,Stopped"]; return [__("Stopped"), "darkgrey", "status,=,Stopped"];
} else if(flt(doc.per_received) < 100 && doc.status!=="Stopped") { } else if(flt(doc.per_received, 2) < 100 && doc.status!=="Stopped") {
if(flt(doc.per_billed) < 100) { if(flt(doc.per_billed, 2) < 100) {
return [__("To Receive and Bill"), "orange", return [__("To Receive and Bill"), "orange",
"per_received,<,100|per_billed,<,100|status,!=,Stopped"]; "per_received,<,100|per_billed,<,100|status,!=,Stopped"];
} else { } else {
return [__("To Receive"), "orange", return [__("To Receive"), "orange",
"per_received,<,100|per_billed,=,100|status,!=,Stopped"]; "per_received,<,100|per_billed,=,100|status,!=,Stopped"];
} }
} else if(flt(doc.per_received) == 100 && flt(doc.per_billed) < 100 && doc.status!=="Stopped") { } else if(flt(doc.per_received, 2) == 100 && flt(doc.per_billed, 2) < 100 && doc.status!=="Stopped") {
return [__("To Bill"), "orange", "per_received,=,100|per_billed,<,100|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") { } else if(flt(doc.per_received, 2) == 100 && flt(doc.per_billed, 2) == 100 && doc.status!=="Stopped") {
return [__("Completed"), "green", "per_received,=,100|per_billed,=,100|status,!=,Stopped"]; return [__("Completed"), "green", "per_received,=,100|per_billed,=,100|status,!=,Stopped"];
} }
}, },

View File

@@ -39,16 +39,12 @@ cur_frm.cscript.make_dashboard = function(doc) {
}, },
callback: function(r) { callback: function(r) {
if (in_list(user_roles, "Accounts User") || in_list(user_roles, "Accounts Manager")) { if (in_list(user_roles, "Accounts User") || in_list(user_roles, "Accounts Manager")) {
if(r.message["company_currency"].length == 1) { cur_frm.dashboard.set_headline(
cur_frm.dashboard.set_headline( __("Total Billing This Year: ") + "<b>"
__("Total Billing This Year: ") + "<b>" + format_currency(r.message.billing_this_year, cur_frm.doc.party_account_currency)
+ format_currency(r.message.billing_this_year, r.message.company_currency[0]) + '</b> / <span class="text-muted">' + __("Total Unpaid") + ": <b>"
+ '</b> / <span class="text-muted">' + __("Total Unpaid") + ": <b>" + format_currency(r.message.total_unpaid, cur_frm.doc.party_account_currency)
+ format_currency(r.message.total_unpaid, r.message.company_currency[0]) + '</b></span>');
+ '</b></span>');
} else {
cur_frm.dashboard.set_headline("");
}
} }
cur_frm.dashboard.set_badge_count(r.message); cur_frm.dashboard.set_badge_count(r.message);
} }

View File

@@ -178,7 +178,7 @@
"ignore_user_permissions": 1, "ignore_user_permissions": 1,
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Default Currency", "label": "Billing Currency",
"no_copy": 1, "no_copy": 1,
"options": "Currency", "options": "Currency",
"permlevel": 0, "permlevel": 0,
@@ -389,8 +389,8 @@
"allow_on_submit": 0, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"depends_on": "eval:!doc.__islocal", "depends_on": "",
"description": "Mention if non-standard receivable account applicable", "description": "Mention if non-standard receivable account",
"fieldname": "accounts", "fieldname": "accounts",
"fieldtype": "Table", "fieldtype": "Table",
"hidden": 0, "hidden": 0,
@@ -511,7 +511,7 @@
"is_submittable": 0, "is_submittable": 0,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-08-25 07:14:56.245716", "modified": "2015-09-25 06:34:56.909099",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Supplier", "name": "Supplier",
@@ -579,7 +579,7 @@
}, },
{ {
"amend": 0, "amend": 0,
"apply_user_permissions": 1, "apply_user_permissions": 0,
"cancel": 0, "cancel": 0,
"create": 0, "create": 0,
"delete": 0, "delete": 0,
@@ -619,7 +619,7 @@
}, },
{ {
"amend": 0, "amend": 0,
"apply_user_permissions": 1, "apply_user_permissions": 0,
"cancel": 0, "cancel": 0,
"create": 0, "create": 0,
"delete": 0, "delete": 0,

View File

@@ -8,6 +8,7 @@ from frappe import msgprint, _
from frappe.model.naming import make_autoname from frappe.model.naming import make_autoname
from erpnext.utilities.address_and_contact import load_address_and_contact from erpnext.utilities.address_and_contact import load_address_and_contact
from erpnext.utilities.transaction_base import TransactionBase from erpnext.utilities.transaction_base import TransactionBase
from erpnext.accounts.party import validate_party_accounts
class Supplier(TransactionBase): class Supplier(TransactionBase):
def get_feed(self): def get_feed(self):
@@ -45,6 +46,8 @@ class Supplier(TransactionBase):
if not self.naming_series: if not self.naming_series:
msgprint(_("Series is mandatory"), raise_exception=1) msgprint(_("Series is mandatory"), raise_exception=1)
validate_party_accounts(self)
def get_contacts(self,nm): def get_contacts(self,nm):
if nm: if nm:
contact_details =frappe.db.convert_to_lists(frappe.db.sql("select name, CONCAT(IFNULL(first_name,''),' ',IFNULL(last_name,'')),contact_no,email_id from `tabContact` where supplier = %s", nm)) contact_details =frappe.db.convert_to_lists(frappe.db.sql("select name, CONCAT(IFNULL(first_name,''),' ',IFNULL(last_name,'')),contact_no,email_id from `tabContact` where supplier = %s", nm))
@@ -89,15 +92,17 @@ def get_dashboard_info(supplier):
out[doctype] = frappe.db.get_value(doctype, out[doctype] = frappe.db.get_value(doctype,
{"supplier": supplier, "docstatus": ["!=", 2] }, "count(*)") {"supplier": supplier, "docstatus": ["!=", 2] }, "count(*)")
billing_this_year = frappe.db.sql("""select sum(base_grand_total) billing_this_year = frappe.db.sql("""
from `tabPurchase Invoice` select sum(ifnull(credit_in_account_currency, 0)) - sum(ifnull(debit_in_account_currency, 0))
where supplier=%s and docstatus = 1 and fiscal_year = %s""", from `tabGL Entry`
where voucher_type='Purchase Invoice' and party_type = 'Supplier'
and party=%s and fiscal_year = %s""",
(supplier, frappe.db.get_default("fiscal_year"))) (supplier, frappe.db.get_default("fiscal_year")))
total_unpaid = frappe.db.sql("""select sum(outstanding_amount) total_unpaid = frappe.db.sql("""select sum(outstanding_amount)
from `tabPurchase Invoice` from `tabPurchase Invoice`
where supplier=%s and docstatus = 1""", supplier) where supplier=%s and docstatus = 1""", supplier)
out["billing_this_year"] = billing_this_year[0][0] if billing_this_year else 0 out["billing_this_year"] = billing_this_year[0][0] if billing_this_year else 0
out["total_unpaid"] = total_unpaid[0][0] if total_unpaid else 0 out["total_unpaid"] = total_unpaid[0][0] if total_unpaid else 0

View File

@@ -1,14 +1,21 @@
[ [
{ {
"company": "_Test Company", "doctype": "Supplier",
"doctype": "Supplier", "supplier_name": "_Test Supplier",
"supplier_name": "_Test Supplier",
"supplier_type": "_Test Supplier Type" "supplier_type": "_Test Supplier Type"
}, },
{ {
"company": "_Test Company", "doctype": "Supplier",
"doctype": "Supplier", "supplier_name": "_Test Supplier 1",
"supplier_name": "_Test Supplier 1",
"supplier_type": "_Test Supplier Type" "supplier_type": "_Test Supplier Type"
},
{
"doctype": "Supplier",
"supplier_name": "_Test Supplier USD",
"supplier_type": "_Test Supplier Type",
"accounts": [{
"company": "_Test Company",
"account": "_Test Payable USD - _TC"
}]
} }
] ]

View File

@@ -43,7 +43,7 @@
"in_filter": 0, "in_filter": 0,
"in_list_view": 0, "in_list_view": 0,
"label": "Title", "label": "Title",
"no_copy": 0, "no_copy": 1,
"permlevel": 0, "permlevel": 0,
"precision": "", "precision": "",
"print_hide": 0, "print_hide": 0,
@@ -509,7 +509,7 @@
"unique": 0 "unique": 0
}, },
{ {
"allow_on_submit": 1, "allow_on_submit": 0,
"bold": 0, "bold": 0,
"collapsible": 0, "collapsible": 0,
"fieldname": "items", "fieldname": "items",
@@ -1554,7 +1554,7 @@
"is_submittable": 1, "is_submittable": 1,
"issingle": 0, "issingle": 0,
"istable": 0, "istable": 0,
"modified": "2015-08-27 03:23:35.148488", "modified": "2015-09-30 08:52:51.539634",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Supplier Quotation", "name": "Supplier Quotation",
@@ -1602,7 +1602,7 @@
}, },
{ {
"amend": 1, "amend": 1,
"apply_user_permissions": 1, "apply_user_permissions": 0,
"cancel": 0, "cancel": 0,
"create": 1, "create": 1,
"delete": 0, "delete": 0,
@@ -1622,7 +1622,7 @@
}, },
{ {
"amend": 0, "amend": 0,
"apply_user_permissions": 1, "apply_user_permissions": 0,
"cancel": 0, "cancel": 0,
"create": 0, "create": 0,
"delete": 0, "delete": 0,
@@ -1642,7 +1642,7 @@
}, },
{ {
"amend": 0, "amend": 0,
"apply_user_permissions": 1, "apply_user_permissions": 0,
"cancel": 0, "cancel": 0,
"create": 0, "create": 0,
"delete": 0, "delete": 0,

View File

@@ -0,0 +1,4 @@
- Set default costing rate and billing rate in **Activity Type**
- Task not mandatory in **Time Log** and **Expense Claim**
- **Stock Settings**: Enable/Disable automatic fetching of Serial Nos. based on FIFO
- Fixes in Tree UI for mobile, Gross Profit Report and Credit Limit checking

View File

@@ -0,0 +1 @@
- **[Multi-currency Accounting](https://manual.erpnext.com/contents/accounts/multi-currency-accounting)**: You can now have an Account in a different currency than your Company's currency

View File

@@ -0,0 +1,8 @@
- **Tax Rule:** Define rules to automatically select a Tax Template based on Customer, Supplier, Billing Address or Shipping Address
- Changes to **Shopping Cart:**
- The prices will be based on only a single Price List defined in Shopping Cart Settings. Essentially, it means that your Shopping Cart will be available only in a single currency.
- Shipping Rule will be defined per Country, instead of Territory.
- Taxes will be applied based on the new Tax Rule system, instead of Territory.
- **Important Note:** Your Shopping Cart Settings have been disabled. The new changes require you to review your Price List, Tax Rules and Shipping Rule, update the settings, and then enable Shopping Cart again.
- Enhancements in Customer Portal user interface
- Sales Team is now editable after submission of Sales Order, Sales Invoice and Delivery Note

View File

@@ -0,0 +1,4 @@
- Trial Balance for Customer and Supplier
- Chart of Accounts for Guatemala
- Address and Contact permissions based on Customer and Supplier
- Multi-currency Accounting: Allow Accounts in different currencies for a Customer or Supplier, if they belong to a different Company

View File

@@ -106,6 +106,11 @@ def get_data():
"name": "Accounts Settings", "name": "Accounts Settings",
"description": _("Default settings for accounting transactions.") "description": _("Default settings for accounting transactions.")
}, },
{
"type": "doctype",
"name": "Tax Rule",
"description": _("Tax Rule for transactions.")
},
{ {
"type": "doctype", "type": "doctype",
"name": "Sales Taxes and Charges Template", "name": "Sales Taxes and Charges Template",
@@ -182,6 +187,12 @@ def get_data():
"doctype": "GL Entry", "doctype": "GL Entry",
"is_query_report": True, "is_query_report": True,
}, },
{
"type": "report",
"name": "Trial Balance for Party",
"doctype": "GL Entry",
"is_query_report": True,
},
{ {
"type": "report", "type": "report",
"name": "Gross Profit", "name": "Gross Profit",

View File

@@ -6,16 +6,26 @@ import frappe
from frappe import _, throw from frappe import _, throw
from frappe.utils import today, flt, cint from frappe.utils import today, flt, cint
from erpnext.setup.utils import get_company_currency, get_exchange_rate from erpnext.setup.utils import get_company_currency, get_exchange_rate
from erpnext.accounts.utils import get_fiscal_year, validate_fiscal_year from erpnext.accounts.utils import get_fiscal_year, validate_fiscal_year, get_account_currency
from erpnext.utilities.transaction_base import TransactionBase from erpnext.utilities.transaction_base import TransactionBase
from erpnext.controllers.recurring_document import convert_to_recurring, validate_recurring_document from erpnext.controllers.recurring_document import convert_to_recurring, validate_recurring_document
from erpnext.controllers.sales_and_purchase_return import validate_return from erpnext.controllers.sales_and_purchase_return import validate_return
from erpnext.accounts.party import get_party_account_currency, validate_party_gle_currency
from erpnext.exceptions import CustomerFrozen, InvalidCurrency
force_item_fields = ("item_group", "barcode", "brand", "stock_uom") force_item_fields = ("item_group", "barcode", "brand", "stock_uom")
class CustomerFrozen(frappe.ValidationError): pass
class AccountsController(TransactionBase): class AccountsController(TransactionBase):
def __init__(self, arg1, arg2=None):
super(AccountsController, self).__init__(arg1, arg2)
@property
def company_currency(self):
if not hasattr(self, "__company_currency"):
self.__company_currency = get_company_currency(self.company)
return self.__company_currency
def validate(self): def validate(self):
if self.get("_action") and self._action != "update_after_submit": if self.get("_action") and self._action != "update_after_submit":
self.set_missing_values(for_validate=True) self.set_missing_values(for_validate=True)
@@ -39,6 +49,7 @@ class AccountsController(TransactionBase):
self.validate_enabled_taxes_and_charges() self.validate_enabled_taxes_and_charges()
self.validate_party() self.validate_party()
self.validate_currency()
def on_submit(self): def on_submit(self):
if self.meta.get_field("is_recurring"): if self.meta.get_field("is_recurring"):
@@ -95,8 +106,6 @@ class AccountsController(TransactionBase):
def set_price_list_currency(self, buying_or_selling): def set_price_list_currency(self, buying_or_selling):
if self.meta.get_field("currency"): if self.meta.get_field("currency"):
company_currency = get_company_currency(self.company)
# price list part # price list part
fieldname = "selling_price_list" if buying_or_selling.lower() == "selling" \ fieldname = "selling_price_list" if buying_or_selling.lower() == "selling" \
else "buying_price_list" else "buying_price_list"
@@ -104,22 +113,22 @@ class AccountsController(TransactionBase):
self.price_list_currency = frappe.db.get_value("Price List", self.price_list_currency = frappe.db.get_value("Price List",
self.get(fieldname), "currency") self.get(fieldname), "currency")
if self.price_list_currency == company_currency: if self.price_list_currency == self.company_currency:
self.plc_conversion_rate = 1.0 self.plc_conversion_rate = 1.0
elif not self.plc_conversion_rate: elif not self.plc_conversion_rate:
self.plc_conversion_rate = get_exchange_rate( self.plc_conversion_rate = get_exchange_rate(
self.price_list_currency, company_currency) self.price_list_currency, self.company_currency)
# currency # currency
if not self.currency: if not self.currency:
self.currency = self.price_list_currency self.currency = self.price_list_currency
self.conversion_rate = self.plc_conversion_rate self.conversion_rate = self.plc_conversion_rate
elif self.currency == company_currency: elif self.currency == self.company_currency:
self.conversion_rate = 1.0 self.conversion_rate = 1.0
elif not self.conversion_rate: elif not self.conversion_rate:
self.conversion_rate = get_exchange_rate(self.currency, self.conversion_rate = get_exchange_rate(self.currency,
company_currency) self.company_currency)
def set_missing_item_details(self): def set_missing_item_details(self):
"""set missing item values""" """set missing item values"""
@@ -156,7 +165,10 @@ class AccountsController(TransactionBase):
item.set("discount_percentage", ret.get("discount_percentage")) item.set("discount_percentage", ret.get("discount_percentage"))
if ret.get("pricing_rule_for") == "Price": if ret.get("pricing_rule_for") == "Price":
item.set("pricing_list_rate", ret.get("pricing_list_rate")) item.set("pricing_list_rate", ret.get("pricing_list_rate"))
if item.price_list_rate:
item.rate = flt(item.price_list_rate *
(1.0 - (flt(item.discount_percentage) / 100.0)), item.precision("rate"))
def set_taxes(self): def set_taxes(self):
if not self.meta.get_field("taxes"): if not self.meta.get_field("taxes"):
@@ -187,7 +199,7 @@ class AccountsController(TransactionBase):
if frappe.db.get_value(taxes_and_charges_doctype, self.taxes_and_charges, "disabled"): if frappe.db.get_value(taxes_and_charges_doctype, self.taxes_and_charges, "disabled"):
frappe.throw(_("{0} '{1}' is disabled").format(taxes_and_charges_doctype, self.taxes_and_charges)) frappe.throw(_("{0} '{1}' is disabled").format(taxes_and_charges_doctype, self.taxes_and_charges))
def get_gl_dict(self, args): def get_gl_dict(self, args, account_currency=None):
"""this method populates the common properties of a gl entry record""" """this method populates the common properties of a gl entry record"""
gl_dict = frappe._dict({ gl_dict = frappe._dict({
'company': self.company, 'company': self.company,
@@ -198,13 +210,49 @@ class AccountsController(TransactionBase):
'fiscal_year': self.fiscal_year, 'fiscal_year': self.fiscal_year,
'debit': 0, 'debit': 0,
'credit': 0, 'credit': 0,
'debit_in_account_currency': 0,
'credit_in_account_currency': 0,
'is_opening': self.get("is_opening") or "No", 'is_opening': self.get("is_opening") or "No",
'party_type': None, 'party_type': None,
'party': None 'party': None
}) })
gl_dict.update(args) gl_dict.update(args)
if not account_currency:
account_currency = get_account_currency(gl_dict.account)
if self.doctype != "Journal Entry":
self.validate_account_currency(gl_dict.account, account_currency)
self.set_balance_in_account_currency(gl_dict, account_currency)
return gl_dict return gl_dict
def validate_account_currency(self, account, account_currency=None):
valid_currency = [self.company_currency]
if self.get("currency") and self.currency != self.company_currency:
valid_currency.append(self.currency)
if account_currency not in valid_currency:
frappe.throw(_("Account {0} is invalid. Account Currency must be {1}")
.format(account, _(" or ").join(valid_currency)))
def set_balance_in_account_currency(self, gl_dict, account_currency=None):
if (not self.get("conversion_rate") and account_currency!=self.company_currency):
frappe.throw(_("Account: {0} with currency: {1} can not be selected")
.format(gl_dict.account, account_currency))
gl_dict["account_currency"] = self.company_currency if account_currency==self.company_currency \
else account_currency
# set debit/credit in account currency if not provided
if flt(gl_dict.debit) and not flt(gl_dict.debit_in_account_currency):
gl_dict.debit_in_account_currency = gl_dict.debit if account_currency==self.company_currency \
else flt(gl_dict.debit / (self.get("conversion_rate")), 2)
if flt(gl_dict.credit) and not flt(gl_dict.credit_in_account_currency):
gl_dict.credit_in_account_currency = gl_dict.credit if account_currency==self.company_currency \
else flt(gl_dict.credit / (self.get("conversion_rate")), 2)
def clear_unallocated_advances(self, childtype, parentfield): def clear_unallocated_advances(self, childtype, parentfield):
self.set(parentfield, self.get(parentfield, {"allocated_amount": ["not in", [0, None, ""]]})) self.set(parentfield, self.get(parentfield, {"allocated_amount": ["not in", [0, None, ""]]}))
@@ -217,13 +265,13 @@ class AccountsController(TransactionBase):
# conver sales_order to "Sales Order" # conver sales_order to "Sales Order"
reference_type = against_order_field.replace("_", " ").title() reference_type = against_order_field.replace("_", " ").title()
condition = "" condition = ""
if order_list: if order_list:
in_placeholder = ', '.join(['%s'] * len(order_list)) in_placeholder = ', '.join(['%s'] * len(order_list))
condition = "or (t2.reference_type = '{0}' and ifnull(t2.reference_name, '') in ({1}))"\ condition = "or (t2.reference_type = '{0}' and ifnull(t2.reference_name, '') in ({1}))"\
.format(reference_type, in_placeholder) .format(reference_type, in_placeholder)
res = frappe.db.sql(""" res = frappe.db.sql("""
select select
t1.name as jv_no, t1.remark, t2.{0} as amount, t2.name as jv_detail_no, t1.name as jv_no, t1.remark, t2.{0} as amount, t2.name as jv_detail_no,
@@ -321,9 +369,9 @@ class AccountsController(TransactionBase):
def set_total_advance_paid(self): def set_total_advance_paid(self):
if self.doctype == "Sales Order": if self.doctype == "Sales Order":
dr_or_cr = "credit" dr_or_cr = "credit_in_account_currency"
else: else:
dr_or_cr = "debit" dr_or_cr = "debit_in_account_currency"
advance_paid = frappe.db.sql(""" advance_paid = frappe.db.sql("""
select select
@@ -331,10 +379,9 @@ class AccountsController(TransactionBase):
from from
`tabJournal Entry Account` `tabJournal Entry Account`
where where
reference_type = %s and reference_type = %s and reference_name = %s
reference_name = %s and and docstatus = 1 and is_advance = "Yes"
docstatus = 1 and is_advance = "Yes" """.format(dr_or_cr=dr_or_cr), """.format(dr_or_cr=dr_or_cr), (self.doctype, self.name))
(self.doctype, self.name))
if advance_paid: if advance_paid:
advance_paid = flt(advance_paid[0][0], self.precision("advance_paid")) advance_paid = flt(advance_paid[0][0], self.precision("advance_paid"))
@@ -357,6 +404,13 @@ class AccountsController(TransactionBase):
if frozen_accounts_modifier in frappe.get_roles(): if frozen_accounts_modifier in frappe.get_roles():
return return
party_type, party = self.get_party()
if party_type:
if frappe.db.get_value(party_type, party, "is_frozen"):
frappe.throw("{0} {1} is frozen".format(party_type, party), CustomerFrozen)
def get_party(self):
party_type = None party_type = None
if self.meta.get_field("customer"): if self.meta.get_field("customer"):
party_type = 'Customer' party_type = 'Customer'
@@ -364,10 +418,22 @@ class AccountsController(TransactionBase):
elif self.meta.get_field("supplier"): elif self.meta.get_field("supplier"):
party_type = 'Supplier' party_type = 'Supplier'
if party_type: party = self.get(party_type.lower()) if party_type else None
party = self.get(party_type.lower())
if frappe.db.get_value(party_type, party, "is_frozen"): return party_type, party
frappe.throw("{0} {1} is frozen".format(party_type, party), CustomerFrozen)
def validate_currency(self):
if self.get("currency"):
party_type, party = self.get_party()
if party_type and party:
party_account_currency = get_party_account_currency(party_type, party, self.company)
if (party_account_currency
and party_account_currency != self.company_currency
and self.currency != party_account_currency):
frappe.throw(_("Accounting Entry for {0}: {1} can only be made in currency: {2}")
.format(party_type, party, party_account_currency), InvalidCurrency)
@frappe.whitelist() @frappe.whitelist()
def get_tax_rate(account_head): def get_tax_rate(account_head):

View File

@@ -60,8 +60,8 @@ def validate_item_variant_attributes(item, args):
if not (is_in_range and is_incremental): if not (is_in_range and is_incremental):
frappe.throw(_("Value for Attribute {0} must be within the range of {1} to {2} in the increments of {3}")\ frappe.throw(_("Value for Attribute {0} must be within the range of {1} to {2} in the increments of {3}")\
.format(attribute, from_range, to_range, increment), InvalidItemAttributeValueError) .format(attribute, from_range, to_range, increment), InvalidItemAttributeValueError)
elif value not in attribute_values[attribute]: elif value not in attribute_values.get(attribute, []):
frappe.throw(_("Value {0} for Attribute {1} does not exist in the list of valid Item Attribute Values").format( frappe.throw(_("Value {0} for Attribute {1} does not exist in the list of valid Item Attribute Values").format(
value, attribute)) value, attribute))

View File

@@ -9,6 +9,7 @@ from frappe.utils import nowdate
def get_filters_cond(doctype, filters, conditions): def get_filters_cond(doctype, filters, conditions):
if filters: if filters:
flt = filters
if isinstance(filters, dict): if isinstance(filters, dict):
filters = filters.items() filters = filters.items()
flt = [] flt = []

View File

@@ -4,7 +4,7 @@ import frappe.utils
import frappe.defaults import frappe.defaults
from frappe.utils import add_days, cint, cstr, date_diff, flt, getdate, nowdate, \ from frappe.utils import add_days, cint, cstr, date_diff, flt, getdate, nowdate, \
get_first_day, get_last_day, comma_and get_first_day, get_last_day, comma_and, split_emails
from frappe.model.naming import make_autoname from frappe.model.naming import make_autoname
from frappe import _, msgprint, throw from frappe import _, msgprint, throw
@@ -33,7 +33,7 @@ def manage_recurring_documents(doctype, next_date=None, commit=True):
next_date = next_date or nowdate() next_date = next_date or nowdate()
date_field = date_field_map[doctype] date_field = date_field_map[doctype]
condition = " and ifnull(status, '') != 'Stopped'" if doctype in ("Sales Order", "Purchase Order") else "" condition = " and ifnull(status, '') != 'Stopped'" if doctype in ("Sales Order", "Purchase Order") else ""
recurring_documents = frappe.db.sql("""select name, recurring_id recurring_documents = frappe.db.sql("""select name, recurring_id
@@ -180,8 +180,7 @@ def convert_to_recurring(doc, posting_date):
def validate_notification_email_id(doc): def validate_notification_email_id(doc):
if doc.notification_email_address: if doc.notification_email_address:
email_list = filter(None, [cstr(email).strip() for email in email_list = split_emails(doc.notification_email_address.replace("\n", ""))
doc.notification_email_address.replace("\n", "").split(",")])
from frappe.utils import validate_email_add from frappe.utils import validate_email_add
for email in email_list: for email in email_list:

View File

@@ -32,10 +32,6 @@ class SellingController(StockController):
self.validate_max_discount() self.validate_max_discount()
check_active_sales_items(self) check_active_sales_items(self)
def check_credit_limit(self):
from erpnext.selling.doctype.customer.customer import check_credit_limit
check_credit_limit(self.customer, self.company)
def set_missing_values(self, for_validate=False): def set_missing_values(self, for_validate=False):
super(SellingController, self).set_missing_values(for_validate) super(SellingController, self).set_missing_values(for_validate)

View File

@@ -207,10 +207,10 @@ class StatusUpdater(Document):
# update percent complete in the parent table # update percent complete in the parent table
if args.get('target_parent_field'): if args.get('target_parent_field'):
frappe.db.sql("""update `tab%(target_parent_dt)s` frappe.db.sql("""update `tab%(target_parent_dt)s`
set %(target_parent_field)s = (select sum(if(%(target_ref_field)s > set %(target_parent_field)s = round((select sum(if(%(target_ref_field)s >
ifnull(%(target_field)s, 0), %(target_field)s, ifnull(%(target_field)s, 0), %(target_field)s,
%(target_ref_field)s))/sum(%(target_ref_field)s)*100 %(target_ref_field)s))/sum(%(target_ref_field)s)*100
from `tab%(target_dt)s` where parent="%(name)s") %(set_modified)s from `tab%(target_dt)s` where parent="%(name)s"), 2) %(set_modified)s
where name='%(name)s'""" % args) where name='%(name)s'""" % args)
# update field # update field
@@ -222,7 +222,7 @@ class StatusUpdater(Document):
where name='%(name)s'""" % args) where name='%(name)s'""" % args)
if args.get("set_modified"): if args.get("set_modified"):
frappe.get_doc(args["target_parent_dt"], name).notify_modified() frappe.get_doc(args["target_parent_dt"], name).notify_update()
def update_billing_status_for_zero_amount_refdoc(self, ref_dt): def update_billing_status_for_zero_amount_refdoc(self, ref_dt):
ref_fieldname = ref_dt.lower().replace(" ", "_") ref_fieldname = ref_dt.lower().replace(" ", "_")

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