Compare commits

..

256 Commits

Author SHA1 Message Date
Nabin Hait
d59df5dd5d Merge branch 'hotfix' 2017-06-12 13:50:11 +05:30
Nabin Hait
d3e1afa2f5 bumped to version 8.0.47 2017-06-12 14:20:11 +06:00
Makarand Bauskar
13239339a6 Merge pull request #9224 from rohitwaghchaure/pos_due_date_issue
[fix] Due Date cannot be before Posting Date during POS records syncing
2017-06-12 13:23:11 +05:30
Rohit Waghchaure
782d477be0 [fix] Due Date cannot be before Posting Date 2017-06-12 12:35:40 +05:30
rohitwaghchaure
f9fe5787a2 Merge pull request #9218 from rohitwaghchaure/pos_mobile_search
[minor] added mobile no to search customer in POS
2017-06-11 17:07:05 +05:30
Rohit Waghchaure
c8490626f9 [minor] added mobile no to search customer in POS 2017-06-10 12:21:37 +05:30
rohitwaghchaure
97fd21a01d [Fix] Accounts receivable summary report not loading data because credit note column was missing (#9191)
* [Fix] Accounts receivable summary report not loading data because credit note column was missing

* Create accounts_receivable_summary.py
2017-06-09 14:05:10 +05:30
Makarand Bauskar
665311b38a [hotfix] replaced __ call with _ in project_timesheets.html (#9198) 2017-06-09 14:03:20 +05:30
Nabin Hait
89f7dd3c98 Merge branch 'hotfix' 2017-06-08 10:34:45 +05:30
Nabin Hait
72f7a1a748 bumped to version 8.0.46 2017-06-08 11:04:44 +06:00
rohitwaghchaure
1607125815 [minor] POS customer creation was disabled in offline mode (#9169) 2017-06-07 20:06:58 +05:30
rohitwaghchaure
6b95210c19 [Fix] User not able to view projects from the portal (#9173) 2017-06-07 20:03:37 +05:30
tundebabzy
98f8da1294 hides payments field when is_pos is checked (#9174) 2017-06-07 20:01:51 +05:30
tundebabzy
f3be8e0da6 removes check for condition - status == "Submitted" (#9176) 2017-06-07 19:59:33 +05:30
Nabin Hait
d428ec1a9e Merge branch 'hotfix' 2017-06-07 12:12:11 +05:30
Nabin Hait
d0e530c63d bumped to version 8.0.45 2017-06-07 12:42:11 +06:00
tundebabzy
f981eee221 fixes issue #9013: Salary calculation Error. (#9044)
* fixes issue #9013:
Salary calculation Error.

* adds test case for scenario as in #9013
2017-06-07 12:05:00 +05:30
rohitwaghchaure
d2be55b2e0 [Fix] Orders not creating from the POS for new customer if customer name is based on naming series (#8933)
* [Fix] Orders not creating from the POS for new customer if customer name is based on naming series

* [fix] Disabled price field in POS, if Allow User to Edit Rate field is disabled in POS profile

* added customer name in the search bar of the customer

* search customer by phone number and email id in the POS
2017-06-07 12:04:01 +05:30
tundebabzy
99b734bfd7 Fix #4587: Status does not display "Pending" in report filter (#9104)
* adds Material Request to `status_map`

* updates eval condition for Partially Ordered in Material Request map

* changes material_request doctype to include "pending", "ordered", "partially ordered", "issued", "transferred" as options

* adds more options to `validate_status`

* adds `set_status` just before saving

* adds `check_for_closed_status` in `before_cancel`

* adds patch to convert status to material request specific status

* adds stricter status update conditions

* changes `update_status` to me `set_status`

* adds checker such that draft status can only change to pending

* renames `check_draft_status` to `status_can_change`

* adds Cancelled to Material Request map

* makes `status_can_change` block any attempt to change a cancelled document

* adds more test cases

* updates what `set_status` checks for before adding comment

* adds patch to rename the present material request status
2017-06-07 12:02:07 +05:30
Manas Solanki
ba7ed4626c fetch query for assessment group (#9120) 2017-06-07 11:57:11 +05:30
Nabin Hait
17378e8a29 Added a new column for Credit/Debit Note in AR/AP report (#9123) 2017-06-07 11:56:53 +05:30
Manas Solanki
b76b4f40a0 Added mode of transport in program enrollment (#9124) 2017-06-07 11:56:26 +05:30
Prateeksha Singh
3a18dea8a4 [fix][multiselect] don't show completed PRECs for a PINV (#9125) 2017-06-07 11:55:25 +05:30
Makarand Bauskar
db8363e9eb [minor] check serial no filedtype before updating (#9126)
* Update serial_no.py

Takes care of Different FieldTypes

* [minor] check serial no filedtype before updating
2017-06-07 11:54:26 +05:30
Manas Solanki
214e6906e5 Addition of field 'group based on' in the student attendance tool (#9152) 2017-06-07 11:53:41 +05:30
Manas Solanki
39adfb52f3 Naming in the assessment plan (#9153) 2017-06-07 11:52:36 +05:30
rohitwaghchaure
ce6e621c18 [fix] Not able to cancel the invoice if it's linked with the timesheet (#9157) 2017-06-07 11:52:04 +05:30
Manas Solanki
2a0d650735 fix in the report (#9167) 2017-06-07 11:29:27 +05:30
Makarand Bauskar
c196d740e5 [minor] added filter for batch no field in purchase transaction (#9038)
* [minor] added filter for batch no field in purchase transaction

* [minor] filter the batch based on item_code instead of get_batch_no method

* [minor] filter based on item_code for Purchase Invoice with update stock
2017-06-02 17:28:40 +05:30
Nabin Hait
5f98dddd0c Merge branch 'hotfix' 2017-06-02 14:05:53 +05:30
Nabin Hait
52e443cfc8 bumped to version 8.0.44 2017-06-02 14:35:53 +06:00
Nabin Hait
ae07f22d8b Fixed syntax error 2017-06-02 13:57:16 +05:30
Nabin Hait
a171d5e6ed Merge branch 'hotfix' 2017-06-01 19:09:36 +05:30
Nabin Hait
1091a25491 bumped to version 8.0.43 2017-06-01 19:39:36 +06:00
rohitwaghchaure
7f2513f7a1 [Fix] Employees working on a holiday report's date filter not working (#9108) 2017-06-01 18:57:34 +05:30
Makarand Bauskar
0b8f920e22 [minor] install node v7 (#9109) 2017-06-01 18:56:39 +05:30
Nabin Hait
e4b3a67e74 Rate validation in return entry 2017-06-01 16:47:45 +05:30
Manas Solanki
f02c82aeca Fixes in the patch (#9027)
* fix in the patch

* new patch for student groups

* changes in the merge student batch patch
2017-06-01 13:08:27 +05:30
tundebabzy
cf92be88b7 [minor] fixes typo (#9093) 2017-05-31 13:21:12 +05:30
Nabin Hait
38fae6a240 Merge branch 'hotfix' 2017-05-30 22:52:38 +05:30
Nabin Hait
f2c374f524 bumped to version 8.0.42 2017-05-30 23:22:38 +06:00
Saurabh
9a342c6401 Merge pull request #9090 from mbauskar/hotfix
[minor] fixed a unexpected token error for opportunity
2017-05-30 22:42:31 +05:30
mbauskar
26044f436a [minor] fixed a unexpected token error for opportunity 2017-05-30 22:28:08 +05:30
Nabin Hait
f6c396f176 Merge branch 'hotfix' 2017-05-30 15:59:04 +05:30
Nabin Hait
93b5367485 bumped to version 8.0.41 2017-05-30 16:29:04 +06:00
Manas Solanki
5995010007 Correction in the validation message (#9079) 2017-05-30 15:56:48 +05:30
bcornwellmott
0d38a6b495 Fix grammar error in HR Training Results (#9076)
* Fix Typo in message

Issue #9074

* Added  condition for notifying user of grade.
2017-05-30 15:56:02 +05:30
Manas Solanki
a85b68ee77 link for timesheet, fix https://github.com/frappe/erpnext/issues/9042 (#9071) 2017-05-30 15:55:00 +05:30
Manas Solanki
ca31425bb4 field for the naming series should have no copy attribute (#9067) 2017-05-30 15:36:50 +05:30
Manas Solanki
253c53cf53 updating serial no status from scheduler #9061 (#9066) 2017-05-30 15:35:58 +05:30
Nabin Hait
ac3b2aa913 Set change amount automatically only if it is a cash transaction (#9065) 2017-05-30 15:35:01 +05:30
Nabin Hait
5d7e8d9e83 Minor fix in timesheet (#9063) 2017-05-30 15:34:35 +05:30
Nabin Hait
6a5cf672c1 Fixed opportunity status (#9062) 2017-05-30 15:34:20 +05:30
Manas Solanki
5f7b88d9c3 set student roll no in backend if not given (#9039) 2017-05-30 15:33:50 +05:30
Nabin Hait
465d8352aa Merge branch 'hotfix' 2017-05-26 21:35:42 +05:30
Nabin Hait
38ada81487 bumped to version 8.0.40 2017-05-26 22:05:41 +06:00
Prateeksha Singh
e38c70c8bc [fix] uppercase filter frappe/erpnext#8996 (#9026) 2017-05-26 21:34:18 +05:30
Makarand Bauskar
157c334737 [minor] added get_terms methods to erpnext.utils so that it can be used in non transactional documents (#9037) 2017-05-26 21:32:33 +05:30
Nabin Hait
4b12896941 Party Dashboard: Consider any random company if default company not set (#9046) 2017-05-26 21:25:36 +05:30
Sagar Vora
7b0c682635 [fix] add round off difference to last row in landed cost voucher (#8989)
* [fix] add round off difference to last row in landed cost voucher

* Add test case for odd numbers

* Add assertEquals to verify applicable charges

* Use make_purchase_receipt, move round off logic

* Allow rounding difference

* Specify cost center to pass test
2017-05-26 14:58:54 +05:30
Nabin Hait
d45a036b5e Merge branch 'hotfix' 2017-05-25 14:19:50 +05:30
Nabin Hait
d7d9cd2938 bumped to version 8.0.39 2017-05-25 14:49:50 +06:00
Nabin Hait
7e7dc0f254 Update party.py 2017-05-25 14:18:20 +05:30
Ayush Shukla
e9cf1aba77 added float and rebase with hotfix (#9019) 2017-05-25 14:14:55 +05:30
Manas Solanki
c2bf50042f validate email id only if provided (#9020) 2017-05-25 14:14:42 +05:30
Nabin Hait
ca89b6f59c Merge branch 'hotfix' 2017-05-24 18:38:33 +05:30
Nabin Hait
12ec71781d bumped to version 8.0.38 2017-05-24 19:08:33 +06:00
Manas Solanki
659a225f24 allow multiple items in purchase receipt (#8997) 2017-05-24 17:49:52 +05:30
CH
d0ebd7f7c2 POS translatable fields correction 2017-05-24 17:45:46 +05:30
Nabin Hait
adfe84b04f Revert "Website Specification Labls should not be capitalised by default (#8798)"
This reverts commit 331566d612.
2017-05-24 17:33:07 +05:30
Nabin Hait
7916590528 Revert "Update lead.py (#8789)"
This reverts commit 9283377f87.
2017-05-24 17:33:07 +05:30
Nabin Hait
fdce7a0dc3 Revert "link the different doctype in the school module (#8844)"
This reverts commit c320537e4c.
2017-05-24 17:33:07 +05:30
Nabin Hait
b3d4326dcc Revert "Prompt for mandatory batch number in POS (#8928)"
This reverts commit ce9ac7885e.
2017-05-24 17:33:07 +05:30
Nabin Hait
54fcf85317 Revert "Documentation for Allow Login using Mobile Number (#8918)"
This reverts commit 818ad397c8.
2017-05-24 17:33:07 +05:30
Nabin Hait
689da20e7b Revert "View Attachments in portal (#8830)"
This reverts commit 7e661437b1.
2017-05-24 17:33:07 +05:30
Nabin Hait
eb92d907bc Revert "Added flt to convert string to float"
This reverts commit bf4915b285.
2017-05-24 17:33:07 +05:30
Nabin Hait
0cd792ebb2 Revert "Update taxes_and_totals.py"
This reverts commit e4afab7fb0.
2017-05-24 17:33:07 +05:30
Rushabh Mehta
e4afab7fb0 Update taxes_and_totals.py 2017-05-24 17:29:23 +05:30
Ayush AS
bf4915b285 Added flt to convert string to float 2017-05-24 17:29:23 +05:30
Charles-Henri Decultot
7e661437b1 View Attachments in portal (#8830)
* View Attachments in portal

* HTML beautify correction

* Move option to shopping cart settings

* new field in shopping cart settings for public attachments display in portal
2017-05-24 17:29:23 +05:30
Gaurav Naik
818ad397c8 Documentation for Allow Login using Mobile Number (#8918) 2017-05-24 17:29:23 +05:30
Charles-Henri Decultot
ce9ac7885e Prompt for mandatory batch number in POS (#8928)
* Prompt for mandatory batch number in POS

* Whitespaces correction
2017-05-24 17:29:23 +05:30
Manas Solanki
c320537e4c link the different doctype in the school module (#8844) 2017-05-24 17:29:23 +05:30
Abdulla P I
9283377f87 Update lead.py (#8789)
* Update lead.py

Import datetime to compare next contact date with now()

* Update lead.py

* Update lead.py

As per the suggestion from Nabin,instead of Datetime,getdate and nowdate from frappe.utils imported
2017-05-24 17:29:23 +05:30
KanchanChauhan
331566d612 Website Specification Labls should not be capitalised by default (#8798) 2017-05-24 17:29:23 +05:30
mbauskar
ff70f38eac [minor] patch to delete the schools deprecated doctypes 2017-05-24 17:13:16 +05:30
mbauskar
d1552f97e4 [minor] moved a comment in item.js 2017-05-24 17:13:16 +05:30
mbauskar
baa4afd67b [minor] removed the Grade Interval and Grading Structure doctype 2017-05-24 17:13:16 +05:30
Nabin Hait
2f0c34385b Check if doctype exists before renaming 2017-05-24 17:12:07 +05:30
Nabin Hait
258f7da778 Paid amount + Write Off Amount can not be greater than Grand Total in return POS 2017-05-24 17:12:07 +05:30
Nabin Hait
799a9cabe7 Can't change valuation method in stock settings, if there are transactions against some items which does not have it's own valuation method 2017-05-24 17:11:48 +05:30
Nabin Hait
24f0b13b22 Total unpaid amount in party is based on GLE 2017-05-24 16:46:56 +05:30
Nabin Hait
2d79a641c2 Annual billing amount in party dashboard based on grand total 2017-05-24 16:46:56 +05:30
Nabin Hait
7a9bd41a72 Improved currency exchange rate message 2017-05-24 16:18:36 +05:30
Nabin Hait
cf1e0508af Merge branch 'hotfix' 2017-05-24 09:07:26 +05:30
Nabin Hait
88906cfc63 bumped to version 8.0.37 2017-05-24 09:37:26 +06:00
Nabin Hait
e72c98dafb On change of item rate, set margin amount on price list rate, instead of margin percentage (#8988) 2017-05-24 09:01:44 +05:30
Nabin Hait
f8b412ba98 Merge branch 'hotfix' 2017-05-23 17:48:42 +05:30
Nabin Hait
c8a25b6dac bumped to version 8.0.36 2017-05-23 18:18:42 +06:00
Makarand Bauskar
724cc35421 [minor] added columns to pos print_template (#8978) 2017-05-23 17:43:42 +05:30
Nabin Hait
67ae3a7f22 Merge branch 'hotfix' 2017-05-23 17:16:19 +05:30
Nabin Hait
8854f8bd50 bumped to version 8.0.35 2017-05-23 17:46:19 +06:00
Manas Solanki
fe05645051 fix issue when image not saved after attaching (#8957) 2017-05-23 17:13:26 +05:30
Faris Ansari
29d64cae34 POS print format fix (#8973) 2017-05-23 17:12:51 +05:30
Nabin Hait
84e50fe371 Merge branch 'hotfix' 2017-05-23 12:04:06 +05:30
Nabin Hait
2ef20a968c bumped to version 8.0.34 2017-05-23 12:34:05 +06:00
Nabin Hait
fdc7d7f3de Check for active quotations before declaring it as lost (#8969) 2017-05-23 12:02:34 +05:30
Manas Solanki
a68fff470c minor fix in student group (#8959) 2017-05-23 11:39:53 +05:30
Manas Solanki
552f7ab678 fix in the patch for merging the student batch and student group (#8961) 2017-05-23 11:38:57 +05:30
Faris Ansari
2424aa73d7 Fix translated string in jinja (#8956) 2017-05-22 13:09:36 +05:30
Rushabh Mehta
50835cbaa0 Merge branch 'hotfix' 2017-05-19 19:24:42 +05:30
Rushabh Mehta
f3a318921f bumped to version 8.0.33 2017-05-19 19:54:42 +06:00
rohitwaghchaure
529f5ce503 [fix] Not able to change the status as Lost in opportunity (#8938) 2017-05-19 18:56:54 +05:30
rohitwaghchaure
177a26849d [fix] Trial balance not working (#8937) 2017-05-19 18:56:25 +05:30
Rushabh Mehta
d144a4c621 Merge branch 'hotfix' 2017-05-19 18:33:55 +05:30
Rushabh Mehta
d2484ece24 bumped to version 8.0.32 2017-05-19 19:03:54 +06:00
Rushabh Mehta
04ea4910f0 [rename] zh-tw -> zh-TW 2017-05-19 18:27:53 +05:30
Rushabh Mehta
65a27395fe [minor] remove student batch name from activation.py 2017-05-19 14:53:32 +05:30
Nabin Hait
91dcd8d952 Merge branch 'hotfix' 2017-05-19 14:12:28 +05:30
Nabin Hait
4d2d996782 bumped to version 8.0.31 2017-05-19 14:42:28 +06:00
Nabin Hait
47afc55939 Merge branch 'master' of github.com:frappe/erpnext into hotfix 2017-05-19 14:10:53 +05:30
Nabin Hait
8c72fa2e4f Merge branch 'develop' 2017-05-19 14:09:00 +05:30
Nabin Hait
0096240a65 bumped to version 8.0.30 2017-05-19 14:38:59 +06:00
Makarand Bauskar
864f134610 [minor] removed the da-DK.csv translations (#8930) 2017-05-19 14:04:52 +05:30
rohitwaghchaure
81fcbbefe5 Merge pull request #8929 from rohitwaghchaure/pos_transalate_issue
[fix] _ is not defined while accessing the POS
2017-05-19 13:23:27 +05:30
Rohit Waghchaure
8a1c58c3c3 [fix] _ is not defined while accessing the POS 2017-05-19 13:01:32 +05:30
Ayush Shukla
ae696d005a Changed report_hide=0 for reference_name (#8902) 2017-05-19 12:38:08 +05:30
Prateeksha Singh
89cec18467 set variant name based on template name (frappe/erpnext#6367) (#8920) 2017-05-19 12:35:36 +05:30
Rushabh Mehta
4bcc2316a9 Don't update variant when template is updated (#8922)
* [fix] allow editing of variant properties, reverting earlier fix

* [minor] dont automatically update variants when the template is saved
2017-05-19 12:33:00 +05:30
Nabin Hait
012c9a0916 Set default account in mode-of-payment Cash, only if default cash account exists (#8909) 2017-05-19 12:30:58 +05:30
Frappe PR Bot
c080479f1f [translation] translation updates (#8927) 2017-05-19 12:30:04 +05:30
tundebabzy
a109141624 Issue 8842 (#8869)
* adds test that confirms #8842

* fixes #8842 and adds tests

* fixes new test case

* adds test for `encode_company_abbr`

* adds six as a requirement

* fixes six.moves.range import

* fixes duplicate company used in test

* fresh commit of test

* fixes failing test - company not saving

* fixes failing test

* Revert "adds six as a requirement"

This reverts commit 80454d98dc.

* replaces whitespace indentation with tabs
2017-05-19 11:42:45 +05:30
Prateeksha Singh
064530d11f [minor] remove order_type as setter in multiselect (#8906) 2017-05-19 11:09:12 +05:30
Rushabh Mehta
6799e27390 [fix] chart of accounts root sorting, #8784, #8897 (#8904) 2017-05-18 16:21:21 +05:30
Rushabh Mehta
c6d75eb942 [fix] allow editing of variant properties, reverting earlier fix (#8905) 2017-05-18 16:21:01 +05:30
Nabin Hait
83f8344e62 Merge branch 'master' of github.com:frappe/erpnext into hotfix 2017-05-18 14:06:37 +05:30
Nabin Hait
ee59ecd396 Merge branch 'hotfix' of github.com:frappe/erpnext into hotfix 2017-05-18 14:06:06 +05:30
gmplab
7607cc9dfa try to fix #997 ,when cart is disabled, it will not show error and it will dedirect to 'contact' page (#8636) 2017-05-18 12:41:19 +05:30
Frappe PR Bot
32e453b8eb [translation] translation updates (#8867) 2017-05-18 12:35:26 +05:30
Nabin Hait
b32cf35fe5 Merge branch 'develop' 2017-05-18 12:01:54 +05:30
Nabin Hait
9422f69fdf bumped to version 8.0.29 2017-05-18 12:31:53 +06:00
Nabin Hait
61ac683847 Fixed chart of accounts for Indonesia (#8900) 2017-05-18 11:58:38 +05:30
Nabin Hait
8fac2ad183 Don't set batch nos automatically on saving, if already set and validate qty with batch (#8887) 2017-05-18 11:54:24 +05:30
Omar Jaber
52e1ba7714 update throw message in maintenance_schedule.py (#8896)
updating Throw message
2017-05-18 11:53:26 +05:30
Nabin Hait
08ea710c8f Minor fix in creating chart of accounts based on existing (#8890) 2017-05-17 19:43:12 +05:30
Nabin Hait
4b544347a9 Hide 'Edit Posting Time' from printing (#8888) 2017-05-17 19:42:38 +05:30
Francisco Roldán
85bfc05efa Fix missing translations (#8871)
* Fix missing translations

* Fix
2017-05-17 19:42:04 +05:30
Nabin Hait
e4f80a6eaa Open lost opportunity again if quotation made against it (#8854) 2017-05-17 19:41:39 +05:30
Rushabh Mehta
f69ffeb0b4 [fix] if rate is greater than price_list_rate, set margin instead of discount. Fixes frappe/erpnext#6468 (#8856) 2017-05-17 19:40:40 +05:30
Faris Ansari
f7a9023fda Show empty state when no students are found in student attendance tool (#8862)
- fixes #8839
2017-05-17 19:37:31 +05:30
Julian Robbins
3d6567411d Spelling improvmement (#8866) 2017-05-17 19:36:15 +05:30
Felipe Orellana
0952df29dc Update product.py (#8877)
Hi!

If template_item_code is None on line 71-72 the query will return ALL "Item Price" records irrespective of item_code, potentially causing the wrong price list rate to be used.

Noticed this behavior when fetching product price for a group of items without "variant_of" data.
2017-05-17 19:20:01 +05:30
Ayush Shukla
76d965f340 [minor]-Changed receivable to payable (#8880) 2017-05-17 19:18:47 +05:30
Ayush Shukla
c55b805b66 [minor] process_payroll_create salary fixed, fixes frappe/erpnext#8853 (#8881)
* [minor] process_payroll_create salary fixed

* [minor] indentation fix
2017-05-17 19:18:26 +05:30
tundebabzy
cf7df5d106 fixes #8883 (#8885) 2017-05-17 19:17:04 +05:30
Sagar Vora
3b04b030eb Fix cost center not getting pulled in DN and SINV (#8878)
* Fixes for getting selling cost center instead of company's default cost center.

* fix error in update_item
2017-05-17 19:16:27 +05:30
Nabin Hait
67ddcf9d32 Merge branch 'develop' 2017-05-17 13:54:04 +05:30
Nabin Hait
def2e4678c bumped to version 8.0.28 2017-05-17 14:24:04 +06:00
Nabin Hait
1e2d7b3519 Filters for multiselect dialog for DN/PR in Sales/Purchase Invoice (#8882) 2017-05-17 13:52:21 +05:30
Nabin Hait
3bd15fb19e Merge branch 'develop' 2017-05-16 13:39:42 +05:30
Nabin Hait
a282c13d84 bumped to version 8.0.27 2017-05-16 14:09:41 +06:00
rohitwaghchaure
a689432a6e [fix] Can't update naming series if format has year,month (#8836) 2017-05-16 12:47:34 +05:30
rohitwaghchaure
3da400b532 [fix] Due Date cannot be before Posting Date during amendement of an invoice (#8841) 2017-05-16 12:43:18 +05:30
Nabin Hait
8a27cf3785 Fix status of invoices with negative outstanding, if no return entry (#8829)
* Move allowance field in Item to the first section to apply it for both stock and non-stock items

* Fix status of invoices with negative outstanding, if no return entry

* get_value included in safe_eval
2017-05-16 12:43:00 +05:30
rohitwaghchaure
73456ac81a [fix] Payments on the invoice is showing even if IS POS option is disabled (#8802) 2017-05-16 11:29:57 +05:30
Saurabh
aa1be1ce92 [documentaion] stripe documentation (#8780) 2017-05-16 09:30:07 +05:30
rohitwaghchaure
654f186f95 [fix] Apply pricing rule on the item based on quantity as per stock uom (#8792) 2017-05-16 08:54:29 +05:30
rohitwaghchaure
09483d3c0f [fix] Numeric keypad not displaying after creation of new customer (#8797) 2017-05-16 08:51:24 +05:30
Makarand Bauskar
5886aafbae [minor] removed cur_frm and added frappe.ui.form.on (#8803) 2017-05-16 08:35:39 +05:30
Charles-Henri Decultot
156eef1907 Description correction (#8811) 2017-05-16 08:34:33 +05:30
Faris Ansari
74e2e4672c Fix for duplicated project dependencies (#8817)
- skip when task is not found
2017-05-16 08:32:45 +05:30
Saurabh
f4ee682400 [fix] pass company filter as string in get_value (#8823) 2017-05-16 08:30:27 +05:30
Makarand Bauskar
475d140b5c [minor] made items table mandatory for quotation (#8825) 2017-05-16 08:26:21 +05:30
Ishan Loya
bdb2f95957 Show duplicate button in BOM unless in draft stage (#8826) 2017-05-16 08:25:07 +05:30
Makarand Bauskar
3e2c9d00f1 [minor] Corrected field label & placed Expense Claim button in Next Step (#8828) 2017-05-16 08:22:45 +05:30
rohitwaghchaure
d678809939 [fix] Party balance field showing wrong currency symbol (#8832) 2017-05-16 08:06:45 +05:30
Manas Solanki
59472e5449 Changes in the student group creation tool (#8833) 2017-05-16 08:05:14 +05:30
Manas Solanki
c5f79d2ec4 Fix for the student batch-wise attendance report (#8834) 2017-05-16 08:00:24 +05:30
Makarand Bauskar
b0df661e81 [minor] set set_posting_time to 1 while data import (#8835) 2017-05-16 07:59:58 +05:30
Rushabh Mehta
939db36ad4 [fix] copy item variant values on save and make non no_copy fields as not editable. fixes frappe/erpnext#4253 (#8837) 2017-05-16 07:42:44 +05:30
Faris Ansari
4fcd8a6db3 Fix sales funnel legend position (Fixes #5073) (#8838) 2017-05-16 07:40:26 +05:30
Faris Ansari
769b6bada8 [POS] Add Pay menu item in mobile view (#8801) (#8845) 2017-05-16 07:31:10 +05:30
Faris Ansari
125996e21c Increase item cart height in mobile view (Fixes #8827) (#8846) 2017-05-16 07:29:15 +05:30
Nabin Hait
7bf810300e Fixed #8822, active students patch 2017-05-15 11:42:37 +05:30
Prateeksha Singh
edeb4dc7e0 Multiselect dialog for getting items (#8583)
* Concatenate docnames and send to mapper

* Multiselect with checkboxes

* set setters as separate filters

* Map filter fields to target_doc

* Get items from quotation (in SO) working

* [major] Set dialog setters for all forms

* Add date field

* Specify non-default date_fields

* [minor] add test_mapper.py

* [minor] remove cur_frm

* [minor][fix] test

* [minor] fix test with make_test_records
2017-05-15 11:32:06 +05:30
Julian Robbins
62e3e75555 Addition of Imap to description of usable mail accounts (#8806) 2017-05-13 09:55:23 +05:30
Saurabh
154385db1b Merge branch 'develop' 2017-05-13 07:42:03 +05:30
Saurabh
82e303882e bumped to version 8.0.26 2017-05-13 08:12:03 +06:00
Saurabh
b679533f81 Merge pull request #8815 from saurabh6790/activation_status_fix
[fix] rename Student Batch to Student Batch Name in activation check
2017-05-13 07:08:02 +05:30
Saurabh
45540569ff [fix] rename Student Batch to Student Batch Name in activation check 2017-05-13 06:30:41 +05:30
Makarand Bauskar
d4e15ca359 [minor] create student batch if does not exists (#8781)
* [minor] create student batch if does not exists

* [fix] minor fixes in if condition
2017-05-12 11:23:58 +05:30
Makarand Bauskar
e8270fe21c [fix] minor fix in get_context for item variants (#8791) 2017-05-12 11:23:23 +05:30
Saurabh
62c7deee00 [fix] don't allow string values in limits to avoid sql injection (#8779) 2017-05-11 18:03:12 +05:30
Nabin Hait
fcefc601e0 Merge branch 'develop' 2017-05-11 17:06:56 +05:30
Nabin Hait
b579fd7ada bumped to version 8.0.25 2017-05-11 17:36:56 +06:00
Nabin Hait
4f5ad50ecf Item Rate in Sales Return must be less than or equal to reference doc (#8775)
* Item Rate in Sales Return must be less than or equal to reference doc

* Fix as rate field is not there in Packed Item
2017-05-11 16:44:19 +05:30
Nabin Hait
5ad6126832 On saving of Accounts Settings, only save enabled warehouses (#8772) 2017-05-11 13:34:04 +05:30
Nabin Hait
62d27ab7d3 Reload doctype bewfore renaming margin fields (#8771) 2017-05-11 13:26:55 +05:30
KanchanChauhan
f74010d379 Calculations based on working and payment days should be made if salary structure exists (#8770) 2017-05-11 11:42:35 +05:30
Makarand Bauskar
0e4c5c9efb [minor] renamed a Total Margin field to Rate With Margin (#8720)
* [minor] renamed a Total Margin field to Rate With Margin

* [minor] minor fixes in field lable
2017-05-11 11:40:02 +05:30
Faris Ansari
734d7795f8 Show party name in General Ledger print report (#8760) 2017-05-11 11:38:30 +05:30
Makarand Bauskar
464f108586 [minor] added currency in Totals for Trial Balance report (#8757)
* [minor] added currency in Totals for Trial Balance report

* [minor] used erpnext.get_default_currency to fetch the company currency
2017-05-10 19:40:36 +05:30
KanchanChauhan
323e46ba5a Added parent filter to the Salary Structure Employee since it was getting base and variable value from first found Salary Structure Employee (#8756) 2017-05-10 17:46:32 +05:30
Makarand Bauskar
62414565b1 [minor] added the address_html field in company doctype (#8754) 2017-05-10 14:52:03 +05:30
Manas Solanki
426b8a14fd Merge the student group and student batch (#8663)
* deleted student batch creation tool

* Patch for deleting the doctype and config

* Changes in the student attendance

* Patch for renaming the student batch as student group

* Changes in the student attendance

* Change in the student attendance reports

- Absent student report
- Student Batch-wise attendance
- Student monthly attendance sheet

* Changes in the patch

* Changes in the course schedule

* Changes in the course scheduling tool

* Change in the assessment plan

* Changes in the assessment result tool

* Cleanup

* Changes in the api.py

* create student group from student batch

* delete student batch

* add patch

* remove student batch from config/schools.py

* Delete the depricated doctype with patch

* Changes in patch

* Changes as per PR
2017-05-09 15:32:52 +05:30
Nabin Hait
0d0d3bacd7 Merge branch 'develop' 2017-05-09 15:13:32 +05:30
Nabin Hait
70e1778712 bumped to version 8.0.24 2017-05-09 15:43:32 +06:00
Nabin Hait
4e6ff8ccd8 Ignore permission for Bin in warehouse query (#8742) 2017-05-09 15:09:10 +05:30
Rushabh Mehta
95233a62d7 [minor] item route item_name + random string 2017-05-09 07:48:41 +05:30
Nabin Hait
226ea7db88 Filter issue on timesheet calendar view (#8730) 2017-05-08 17:34:07 +05:30
Saurabh
9df45bbc42 [fix] Reference Document is required to create Payment Request (#8729)
* [fix] Referene Document is required to create Payment Request

* Update payment_request.py
2017-05-08 17:24:23 +05:30
Rushabh Mehta
a6bc583daf [minor] item route is item name + code 2017-05-08 11:35:40 +05:30
Nabin Hait
3f7fff04f4 Merge branch 'develop' 2017-05-06 13:40:52 +05:30
Nabin Hait
b5a9822fff bumped to version 8.0.23 2017-05-06 14:10:52 +06:00
Nabin Hait
17179ee83a minor syntax fix 2017-05-06 13:23:12 +05:30
Ishan Loya
120ee275cc Fix typo (#8719) 2017-05-06 12:48:22 +05:30
Makarand Bauskar
486045e1d1 [minor] check if date of joining is available before creating Salary Slip (#8700) 2017-05-06 12:47:42 +05:30
Nabin Hait
3257aeeb55 Price list rate fix multiple uom and validation with prev doc (#8718) 2017-05-06 12:47:14 +05:30
Nabin Hait
7228e1af6e Customer/Supplier Name in sales/purchase analytics (#8717) 2017-05-06 12:45:37 +05:30
Nabin Hait
6e7407962f Get exchange rate only if date and currency is present (#8712)
* Get exchange rate only if date and currency is present

* Update transaction.js
2017-05-06 12:45:16 +05:30
Rushabh Mehta
b7e740ff45 [minor] ignore permissions for bin on warehouse_query (#8713) 2017-05-06 12:44:03 +05:30
Dominik Ottenbreit
51cd2df763 Fix Romanian Translation (#8706) 2017-05-05 17:30:45 +05:30
Pawan Mehta
cac94b9bd5 [fix] #8373 (#8707) 2017-05-05 17:29:57 +05:30
Manas Solanki
65c8466622 check for mandatory field before frappe.call (#8705) 2017-05-05 17:28:09 +05:30
Nabin Hait
42343bbc2c Selling price validation against last purchase rate / valuation rate (#8702) 2017-05-05 17:23:17 +05:30
KanchanChauhan
ed56b8afd8 Website route for item based on name not item name (#8682) 2017-05-05 11:56:55 +05:30
Makarand Bauskar
6b3bc8a8e1 [hotfix] fixed the recursion error while saving the User (#8696) 2017-05-05 11:53:00 +05:30
Makarand Bauskar
c4ec937835 [hotfix] fixed object has no attribute 'delivered_by_supplier' (#8699) 2017-05-05 11:52:17 +05:30
Nabin Hait
d2a966eef3 Update general_ledger.py 2017-05-05 10:41:16 +05:30
rohitwaghchaure
6324987c15 [fix] Company's abbreviation change will not update the warehouse (#8685) 2017-05-04 16:12:29 +05:30
Nabin Hait
c9bfb64cd9 Merge branch 'develop' 2017-05-04 12:20:31 +05:30
Nabin Hait
8470b39d4c bumped to version 8.0.22 2017-05-04 12:50:31 +06:00
Nabin Hait
932423ecba Balance Sheet always shows accumulated values from previous fiscal year (#8668) 2017-05-04 12:12:29 +05:30
Faris Ansari
1c09a991f3 Change beta_version to 8.x-beta (#8673)
* Change beta_version to 8.x-beta

* change to 8.x.x-beta
2017-05-04 12:12:14 +05:30
Nabin Hait
fcc0246b38 Get mobile nos for customer contact (#8674) 2017-05-04 12:11:48 +05:30
Nabin Hait
3ce41d6b1f Fixed related to auto fetching batch nos and date valiation in salary structure (#8666)
* Fixed related to auto fetching batch nos and date valiation in salary structure

* Update get_item_details.py
2017-05-03 18:22:24 +05:30
Javier Wong
470535ae9b Removed set_default_roles
set_default_roles was removed in 7fff0908a4

It is not required anymore. It currently breaks the patch.
2017-05-03 16:44:14 +05:30
rohitwaghchaure
a1923574c6 Merge pull request #8657 from rohitwaghchaure/pos_email_id_issue
[fix] Contact not creating if only email id has entered in the POS
2017-05-03 12:56:13 +05:30
Nabin Hait
ef027e9030 Correct args to the fmt_money function 2017-05-03 11:51:47 +05:30
Aditya Duggal
bd8c7d683b Website related fields now consider show_variant_in_website field as well
-Resolves issue #8635
2017-05-03 10:14:09 +05:30
Rohit Waghchaure
fd37516165 [fix] Contact not creating if only email id has entered in the POS 2017-05-02 18:27:09 +05:30
Nabin Hait
504eba7fb2 Test case fixed for Employee Loan Application 2017-05-02 18:19:48 +05:30
Nabin Hait
e15721df48 Caluculate total interest and payable amount in Loan Application if fixed amount per period 2017-05-02 18:19:48 +05:30
mbauskar
c482aeda1a [fixes] added missing get_linked_material_requests method to buying utils 2017-05-02 14:05:54 +05:30
rohitwaghchaure
8811cc6b5b Merge pull request #8651 from rohitwaghchaure/pos_default_customer_issue
[fix] Default pos profile's customer not working for the POS
2017-05-02 13:18:40 +05:30
Rohit Waghchaure
61165127fc [fix] Default pos profile's customer not working for the POS 2017-05-02 13:04:08 +05:30
Nabin Hait
d71a1c5e67 Merge branch 'develop' 2017-05-02 09:58:48 +05:30
Nabin Hait
fb73621db2 bumped to version 8.0.21 2017-05-02 10:28:48 +06:00
Rohit Waghchaure
3df7eef6cc [fix] Unable to create an asset due to rounding issue 2017-05-02 09:49:02 +05:30
akshay
b674d27285 Test Cleanup 2017-05-02 09:48:51 +05:30
akshay
949fbc559e Serial No Rename 2017-05-02 09:48:51 +05:30
Nabin Hait
1117125841 Update domainify.py 2017-05-01 11:40:13 +05:30
mbauskar
e94c6e7b8e [minor] disabled the instructor role if the company domain is not Education 2017-05-01 11:40:13 +05:30
Rushabh Mehta
c75becc42c [minor] README.md and CONTRIBUTING.md 2017-04-28 16:09:57 +05:30
Rushabh Mehta
ed5543adda [minor] README.md and CONTRIBUTING.md 2017-04-28 16:07:36 +05:30
Nabin Hait
c9b6a07732 [fix] Remove recurring id on amendment only if it is same as amended_from 2016-11-16 18:43:44 +05:30
314 changed files with 54365 additions and 44666 deletions

View File

@@ -1,67 +1,36 @@
## General Overview
### Introduction (first timers)
There are three branches where all the work happens:
Thank you for your interest in raising an Issue with ERPNext. An Issue could mean a bug report or a request for a missing feature. By raising a bug report, you are contributing to the development of ERPNext and this is the first step of participating in the community. Bug reports are very helpful for developers as they quickly fix the issue before other users start facing it.
* **master** - This is the production / stable branch for releases.
* **develop** - This is bleeding edge with features and fixes. Non critical bug fixes and new features go here. All updates to master also get pushed to develop.
* **hotfix** - Urgent bug fixes go here. This is merged into master for releases.
Feature requests are also a great way to take the product forward. New ideas can come in any user scenario and the issue list also acts a roadmap of future features.
## Release Cycles
When you are raising an Issue, you should keep a few things in mind. Remember that the developer does not have access to your machine so you must give all the information you can while raising an Issue. If you are suggesting a feature, you should be very clear about what you want.
Usually, hotfix / develop is pushed to master roughly every week.
The Issue list is not the right place to ask a question or start a general discussion. If you want to do that , then the right place is the forum [https://discuss.erpnext.com](https://discuss.erpnext.com).
If we are close to a major release, then all bugfixes get pushed to hotfix and a release is done every week or as necessary.
### Reply and Closing Policy
***
If your issue is not clear or does not meet the guidelines, then it will be closed. If it is closed, please supply the information asked and re-open it.
### General Issue Guidelines
## Contributing
1. **Search existing Issues:** Before raising a Issue, search if it has been raised before. Maybe add a 👍 or give additional help by creating a mockup if it is not already created.
1. **Report each issue separately:** Don't club multiple, unreleated issues in one note.
1. **Brief:** Please don't include long explanations. Use screenshots and bullet points instead of descriptive paragraphs.
Contributing to ERPNext is not very different from the usual Pull Request workflow on GitHub.
### Bug Report Guidelines
### Prerequisites :
1. **Steps to Reproduce:** The bug report must have a list of steps needed to reproduce a bug. If we cannot reproduce it, then we cannot solve it.
1. **Version Number:** Please add the version number in your report. Often a bug is fixed in the latest version
1. **Clear Title:** Add a clear subject to your bug report like "Unable to submit Purchase Order without Basic Rate" instead of just "Cannot Submit"
1. **Screenshots:** Screenshots are a great way of communicating the issues. Try adding annotations or using LiceCAP to take a screencast in `gif`.
* You need to know [Git and Github basics](https://try.github.io/levels/1/challenges/1)
* You need to have a Fork of the [ERPNext repo](https://github.com/frappe/erpnext) in your personal Github account
* You need to add a [remote](#glossary) for your Forked repository. `git remote add origin [your-erpnext-repo-url]`
### Feature Request Guidelines
### The Process:
1. **Clarity:** Clearly specify how do you want the feature to behave. Don't just say "I would like multiple PDF formats", say that "Ability to add multiple print formats for customers with different languages".
1. **Solution:** Try and identify how the feature should look like.
1. **Mockups:** Mockups are a great way to explain your requirement.
1. Make sure you're in the right branch. **develop** for adding features / fixing issues and **hotfix** for urgent bug fixes
2. Make your changes
3. Create and checkout a new branch for the changes you've made. `git checkout -b [branch-name]`
4. Add and commit your changes `git commit -am "[commit-message]"
5. If you have been working on sometime for a long time, you should [rebase](#glossary) your branch with main develop branch. `git pull upstream develop --rebase` where `upstream` is the remote name of our repo
6. Now, push your changes to your fork. `git push origin [branch-name]`
If you rebased your commits, you will have to [force push](http://vignette2.wikia.nocookie.net/starwars/images/e/ea/Yodapush.png/revision/latest?cb=20130205190454) `git push origin [branch-name] --force`
7. You should now be able to see your pushed branch on Github, now create a pull request against the branch that you want to merge to.
8. Wait for us to review it
### What if my Issue is closed
### Your Pull Request Should have
1. Clear explanation of the use case
1. Screenshots / Screecast GIF
1. Test Cases (if applicable)
1. Update to documentation
### Common Problems:
* During rebase you might face _merge conflicts_. A merge conflict occurs when you have made changes to the same file that someone else has, in the commits you're pulling. You need to resolve these conflicts by picking which code you want to keep, yours or theirs. You can use `git mergetool` for help.
* Sometimes you don't have a local branch to which you want to make changes to. In that case you first run `git fetch` followed by `git checkout --track -b upstream/[branch-name]`
### Good practices:
* You should rebase your branch with the branch you plan to make a Pull Request (PR) to as often as you can.
* Your commit messages should be precise and explain exactly what the commit does. Same goes for the Pull Request title.
* When making a PR make sure that all your code is committed properly by checking the diffs.
* If you're working on different things at the same time, make sure you make separate branches for each.
* Don't create new DocTypes unless absolutely necessary. If you find that there is a another DocType with a similar functionality, then please try and extend that functionality.
* DRY. Don't Repeat Yourself. Before writing up a similar function /feature make sure it doesn't exist in the codebase already.
* Tabs, not spaces.
### Glossary
* remote - A remote is a connection to a Github repo. You should have two remotes, one that points to your repo and one to ours.
* rebase - When you rebase a branch, you pull commits from your remote branch and move your commits on top of it. This allows you to update your branch with the latest changes without losing your changes.
Don't worry, take the feedback, supply the correct information and re-open it!

View File

@@ -13,6 +13,7 @@ before_install:
install:
- sudo apt-get purge -y mysql-common mysql-server mysql-client
- nvm install v7.10.0
# - wget https://raw.githubusercontent.com/frappe/bench/master/install_scripts/setup_frappe.sh
# - sudo bash setup_frappe.sh --skip-setup-bench --mysql-root-password travis --bench-branch develop
- wget https://raw.githubusercontent.com/frappe/bench/master/playbooks/install.py

View File

@@ -40,7 +40,8 @@ The ERPNext code is licensed as GNU General Public License (v3) and the Document
## Contributing
1. [Pull Request Requirements](https://github.com/frappe/erpnext/wiki/Pull-Request-Guidelines)
1. [Issue Guidelines](https://github.com/frappe/erpnext/wiki/Issue-Guidelines)
1. [Pull Request Requirements](https://github.com/frappe/erpnext/wiki/Contribution-Guidelines)
1. [Translations](https://translate.erpnext.com)
1. [Chart of Accounts](https://charts.erpnext.com)

View File

@@ -2,7 +2,8 @@
from __future__ import unicode_literals
import frappe
__version__ = '8.0.20'
__version__ = '8.0.47'
def get_default_company(user=None):
'''Get default company for user'''
@@ -19,12 +20,14 @@ def get_default_company(user=None):
return default_company
def get_default_currency():
'''Returns the currency of the default company'''
company = get_default_company()
if company:
return frappe.db.get_value('Company', company, 'default_currency')
def get_company_currency(company):
'''Returns the default company currency'''
if not frappe.flags.company_currency:
@@ -33,11 +36,13 @@ def get_company_currency(company):
frappe.flags.company_currency[company] = frappe.db.get_value('Company', company, 'default_currency')
return frappe.flags.company_currency[company]
def set_perpetual_inventory(enable=1):
accounts_settings = frappe.get_doc("Accounts Settings")
accounts_settings.auto_accounting_for_stock = enable
accounts_settings.save()
def encode_company_abbr(name, company):
'''Returns name encoded with company abbreviation'''
company_abbr = frappe.db.get_value("Company", company, "abbr")
@@ -46,4 +51,6 @@ def encode_company_abbr(name, company):
if parts[-1].lower() != company_abbr.lower():
parts.append(company_abbr)
return " - ".join([parts[0], company_abbr])
return " - ".join(parts)

View File

@@ -188,9 +188,9 @@ class Account(Document):
account_balance = get_balance_on(self.name)
if account_balance != stock_balance:
frappe.throw(_('Account balance ({0}) and stock value ({1}) must be same')\
.format(fmt_money(account_balance, self.account_currency),
fmt_money(stock_balance, self.account_currency)))
frappe.throw(_('Account balance ({0}) for {1} and stock value ({2}) for warehouse {3} must be same')
.format(fmt_money(account_balance, currency=self.account_currency), self.name,
fmt_money(stock_balance, currency=self.account_currency), self.warehouse))
elif self.warehouse:
self.warehouse = None

View File

@@ -125,13 +125,14 @@ def get_account_tree_from_existing_company(existing_company):
account_tree = {}
# fill in tree starting with root accounts (those with no parent)
build_account_tree(account_tree, None, all_accounts)
if all_accounts:
build_account_tree(account_tree, None, all_accounts)
return account_tree
def build_account_tree(tree, parent, all_accounts):
# find children
parent_account = parent.name if parent else None
children = [acc for acc in all_accounts if acc.parent_account == parent_account]
parent_account = parent.name if parent else ""
children = [acc for acc in all_accounts if cstr(acc.parent_account) == parent_account]
# if no children, but a group account
if not children and parent.is_group:

View File

@@ -22,20 +22,10 @@
},
"1120.000 Bank ": {
"1121.000 Bank Rupiah": {
"1121.0010 Bank 1": {
"account_type": "Bank"
},
"1121.0020 Bank 2": {
"account_type": "Bank"
}
"is_group": 1
},
"1122.000 Bank Other Currency": {
"1122.0010 Bank 1": {
"account_type": "Bank"
},
"1122.0020 Bank 2": {
"account_type": "Bank"
}
"is_group": 1
},
"account_type": "Bank"
},

View File

@@ -21,10 +21,13 @@ class AccountsSettings(Document):
company.save()
# 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
where disabled=0""", as_dict=1)
warehouse_with_no_company = [d.name for d in warehouse_list if not d.company]
if warehouse_with_no_company:
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:
wh_doc = frappe.get_doc("Warehouse", wh.name)
wh_doc.flags.ignore_permissions = True

View File

@@ -45,7 +45,7 @@ frappe.ui.form.on('Asset', {
erpnext.asset.scrap_asset(frm);
});
frm.add_custom_button("Sale Asset", function() {
frm.add_custom_button("Sell Asset", function() {
erpnext.asset.make_sales_invoice(frm);
});

View File

@@ -114,8 +114,17 @@ class Asset(Document):
def set_accumulated_depreciation(self):
accumulated_depreciation = flt(self.opening_accumulated_depreciation)
for d in self.get("schedules"):
accumulated_depreciation += flt(d.depreciation_amount, d.precision("depreciation_amount"))
value_after_depreciation = flt(self.value_after_depreciation)
for i, d in enumerate(self.get("schedules")):
depreciation_amount = flt(d.depreciation_amount, d.precision("depreciation_amount"))
value_after_depreciation -= flt(depreciation_amount)
if i==len(self.get("schedules"))-1 and self.depreciation_method == "Straight Line":
depreciation_amount += flt(value_after_depreciation - flt(self.expected_value_after_useful_life),
d.precision("depreciation_amount"))
d.depreciation_amount = depreciation_amount
accumulated_depreciation += d.depreciation_amount
d.accumulated_depreciation_amount = flt(accumulated_depreciation, d.precision("accumulated_depreciation_amount"))
def get_depreciation_amount(self, depreciable_value):

View File

@@ -5,7 +5,7 @@ from __future__ import unicode_literals
import frappe
import unittest
from frappe.utils import cstr, nowdate, getdate
from frappe.utils import cstr, nowdate, getdate, flt
from erpnext.accounts.doctype.asset.depreciation import post_depreciation_entries, scrap_asset, restore_asset
from erpnext.accounts.doctype.asset.asset import make_sales_invoice, make_purchase_invoice
@@ -243,6 +243,23 @@ class TestAsset(unittest.TestCase):
self.assertEqual(frappe.db.get_value("Asset", "Macbook Pro 1", "status"), "Partially Depreciated")
def test_asset_expected_value_after_useful_life(self):
asset = frappe.get_doc("Asset", "Macbook Pro 1")
asset.depreciation_method = "Straight Line"
asset.is_existing_asset = 1
asset.total_number_of_depreciations = 400
asset.gross_purchase_amount = 16866177.00
asset.expected_value_after_useful_life = 500000
asset.save()
accumulated_depreciation_after_full_schedule = \
max([d.accumulated_depreciation_amount for d in asset.get("schedules")])
asset_value_after_full_schedule = (flt(asset.gross_purchase_amount) -
flt(accumulated_depreciation_after_full_schedule))
self.assertTrue(asset.expected_value_after_useful_life >= asset_value_after_full_schedule)
def tearDown(self):
asset = frappe.get_doc("Asset", "Macbook Pro 1")

View File

@@ -147,6 +147,7 @@ frappe.ui.form.on('Payment Entry', {
var currency_field = (frm.doc.payment_type=="Receive") ? "paid_from_account_currency" : "paid_to_account_currency"
frm.set_df_property("total_allocated_amount", "options", currency_field);
frm.set_df_property("unallocated_amount", "options", currency_field);
frm.set_df_property("party_balance", "options", currency_field);
frm.set_currency_labels(["total_amount", "outstanding_amount", "allocated_amount"],
party_account_currency, "references");

View File

@@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "naming_series:",
@@ -13,6 +14,7 @@
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -43,6 +45,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -72,6 +75,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -103,6 +107,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -132,6 +137,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -161,6 +167,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -189,6 +196,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -219,6 +227,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -249,6 +258,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -280,6 +290,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -311,6 +322,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -341,6 +353,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -369,6 +382,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -398,6 +412,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -428,6 +443,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -457,6 +473,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -486,6 +503,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -516,6 +534,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -546,6 +565,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -576,6 +596,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -605,6 +626,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -635,6 +657,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -658,13 +681,14 @@
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 1,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -694,17 +718,17 @@
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"in_create": 1,
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-02-23 05:03:37.464863",
"modified": "2017-05-18 12:18:39.187714",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Request",

View File

@@ -14,9 +14,14 @@ from frappe.integrations.utils import get_payment_gateway_controller
class PaymentRequest(Document):
def validate(self):
self.validate_reference_document()
self.validate_payment_request()
self.validate_currency()
def validate_reference_document(self):
if not self.reference_doctype or not self.reference_name:
frappe.throw(_("To create a Payment Request reference document is required"))
def validate_payment_request(self):
if frappe.db.get_value("Payment Request", {"reference_name": self.reference_name,
"name": ("!=", self.name), "status": ("not in", ["Initiated", "Paid"]), "docstatus": 1}, "name"):

View File

@@ -285,9 +285,10 @@ def get_pricing_rules(args):
def filter_pricing_rules(args, pricing_rules):
# filter for qty
stock_qty = args.get('qty') * args.get('conversion_factor', 1)
if pricing_rules:
pricing_rules = filter(lambda x: (flt(args.get("qty"))>=flt(x.min_qty)
and (flt(args.get("qty"))<=x.max_qty if x.max_qty else True)), pricing_rules)
pricing_rules = filter(lambda x: (flt(stock_qty)>=flt(x.min_qty)
and (flt(stock_qty)<=x.max_qty if x.max_qty else True)), pricing_rules)
# add variant_of property in pricing rule
for p in pricing_rules:

View File

@@ -5,6 +5,9 @@
from __future__ import unicode_literals
import unittest
import frappe
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
from erpnext.stock.get_item_details import get_item_details
from frappe import MandatoryError
class TestPricingRule(unittest.TestCase):
def test_pricing_rule_for_discount(self):
@@ -203,3 +206,46 @@ class TestPricingRule(unittest.TestCase):
details = get_item_details(args)
self.assertEquals(details.get("discount_percentage"), 17.5)
def test_pricing_rule_for_stock_qty(self):
frappe.db.sql("delete from `tabPricing Rule`")
test_record = {
"doctype": "Pricing Rule",
"title": "_Test Pricing Rule",
"apply_on": "Item Code",
"item_code": "_Test Item",
"selling": 1,
"price_or_discount": "Discount Percentage",
"price": 0,
"min_qty": 5,
"max_qty": 7,
"discount_percentage": 17.5,
"company": "_Test Company"
}
frappe.get_doc(test_record.copy()).insert()
if not frappe.db.get_value('UOM Conversion Detail',
{'parent': '_Test Item', 'uom': 'box'}):
item = frappe.get_doc('Item', '_Test Item')
item.append('uoms', {
'uom': 'Box',
'conversion_factor': 5
})
item.save(ignore_permissions=True)
# With pricing rule
so = make_sales_order(item_code="_Test Item", qty=1, uom="Box", do_not_submit=True)
so.items[0].price_list_rate = 100
so.submit()
so = frappe.get_doc('Sales Order', so.name)
self.assertEquals(so.items[0].discount_percentage, 17.5)
self.assertEquals(so.items[0].rate, 82.5)
# Without pricing rule
so = make_sales_order(item_code="_Test Item", qty=2, uom="Box", do_not_submit=True)
so.items[0].price_list_rate = 100
so.submit()
so = frappe.get_doc('Sales Order', so.name)
self.assertEquals(so.items[0].discount_percentage, 0)
self.assertEquals(so.items[0].rate, 100)

View File

@@ -49,29 +49,38 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
}
if(doc.docstatus===0) {
cur_frm.add_custom_button(__('Purchase Order'), function() {
var me = this;
this.frm.add_custom_button(__('Purchase Order'), function() {
erpnext.utils.map_current_doc({
method: "erpnext.buying.doctype.purchase_order.purchase_order.make_purchase_invoice",
source_doctype: "Purchase Order",
target: me.frm,
setters: {
supplier: me.frm.doc.supplier || undefined,
},
get_query_filters: {
supplier: cur_frm.doc.supplier || undefined,
docstatus: 1,
status: ["!=", "Closed"],
per_billed: ["<", 99.99],
company: cur_frm.doc.company
company: me.frm.doc.company
}
})
}, __("Get items from"));
cur_frm.add_custom_button(__('Purchase Receipt'), function() {
this.frm.add_custom_button(__('Purchase Receipt'), function() {
erpnext.utils.map_current_doc({
method: "erpnext.stock.doctype.purchase_receipt.purchase_receipt.make_purchase_invoice",
source_doctype: "Purchase Receipt",
target: me.frm,
date_field: "posting_date",
setters: {
supplier: me.frm.doc.supplier || undefined,
},
get_query_filters: {
supplier: cur_frm.doc.supplier || undefined,
docstatus: 1,
status: ["!=", "Closed"],
company: cur_frm.doc.company
status: ["not in", ["Closed", "Completed"]],
company: me.frm.doc.company,
is_return: 0
}
})
}, __("Get items from"));
@@ -120,7 +129,7 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
hide_fields(this.frm.doc);
if(cint(this.frm.doc.is_paid)) {
if(!this.frm.doc.company) {
cur_frm.set_value("is_paid", 0)
this.frm.set_value("is_paid", 0)
msgprint(__("Please specify Company to proceed"));
}
}

View File

@@ -13,6 +13,7 @@
"editable_grid": 0,
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
@@ -43,6 +44,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -74,6 +76,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -105,6 +108,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
@@ -136,6 +140,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -166,6 +171,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -195,6 +201,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -224,6 +231,7 @@
"width": "50%"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -253,6 +261,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -284,6 +293,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -315,6 +325,7 @@
"width": "100px"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -334,7 +345,7 @@
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
@@ -345,6 +356,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -376,6 +388,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -406,6 +419,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -437,6 +451,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -465,6 +480,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -495,6 +511,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -525,6 +542,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -555,6 +573,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -586,6 +605,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -615,6 +635,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -644,6 +665,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -673,6 +695,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -702,6 +725,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -730,6 +754,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -758,6 +783,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -786,6 +812,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -814,6 +841,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -845,6 +873,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -874,6 +903,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -903,6 +933,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -934,6 +965,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -966,6 +998,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -993,6 +1026,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1022,6 +1056,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1051,6 +1086,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1080,6 +1116,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1108,6 +1145,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1138,6 +1176,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1168,6 +1207,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1199,6 +1239,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1226,6 +1267,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1256,6 +1298,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1288,6 +1331,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1315,6 +1359,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1345,6 +1390,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1376,6 +1422,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1406,6 +1453,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1437,6 +1485,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1468,6 +1517,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1497,6 +1547,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1527,6 +1578,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1558,6 +1610,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1589,6 +1642,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1620,6 +1674,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1648,6 +1703,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1679,6 +1735,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1710,6 +1767,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1740,6 +1798,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -1770,6 +1829,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1801,6 +1861,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1831,6 +1892,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1859,6 +1921,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1888,6 +1951,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1918,6 +1982,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1946,6 +2011,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1977,6 +2043,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -2008,6 +2075,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -2037,6 +2105,7 @@
"width": "50%"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -2068,6 +2137,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -2098,6 +2168,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -2129,6 +2200,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -2160,6 +2232,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -2191,6 +2264,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -2221,6 +2295,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -2251,6 +2326,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -2279,6 +2355,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -2310,6 +2387,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -2340,6 +2418,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -2371,6 +2450,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -2400,6 +2480,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -2430,6 +2511,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -2458,6 +2540,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -2488,6 +2571,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -2518,6 +2602,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -2549,6 +2634,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -2579,6 +2665,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -2610,6 +2697,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -2640,6 +2728,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -2669,6 +2758,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -2697,6 +2787,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -2726,6 +2817,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -2757,6 +2849,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -2789,6 +2882,7 @@
"width": "50px"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -2819,6 +2913,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -2848,6 +2943,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
@@ -2878,6 +2974,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
@@ -2909,6 +3006,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -2938,6 +3036,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -2968,6 +3067,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -3000,6 +3100,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -3030,6 +3131,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -3063,6 +3165,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -3093,6 +3196,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -3120,6 +3224,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -3151,6 +3256,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -3181,6 +3287,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -3212,6 +3319,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -3243,6 +3351,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
@@ -3273,6 +3382,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
@@ -3304,6 +3414,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
@@ -3334,6 +3445,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
@@ -3364,6 +3476,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
@@ -3394,6 +3507,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
@@ -3425,6 +3539,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
@@ -3455,6 +3570,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
@@ -3485,6 +3601,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -3513,6 +3630,7 @@
"width": "50%"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -3543,6 +3661,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -3573,6 +3692,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
@@ -3603,6 +3723,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -3646,7 +3767,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2017-04-10 12:05:28.082020",
"modified": "2017-05-17 10:35:40.729350",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice",

View File

@@ -204,7 +204,7 @@ class PurchaseInvoice(BuyingController):
if frappe.db.get_value("Buying Settings", None, "po_required") == 'Yes':
for d in self.get('items'):
if not d.purchase_order:
throw(_("Purchse Order number required for Item {0}").format(d.item_code))
throw(_("Purchase Order number required for Item {0}").format(d.item_code))
def pr_required(self):
stock_items = self.get_stock_items()

View File

@@ -35,6 +35,7 @@ def get_pos_data():
'item_groups': get_item_groups(pos_profile),
'customers': customers,
'address': get_customers_address(customers),
'contacts': get_contacts(customers),
'serial_no_data': get_serial_no_data(pos_profile, doc.company),
'batch_no_data': get_batch_no_data(),
'tax_data': get_item_tax_data(),
@@ -160,7 +161,7 @@ def get_item_groups(pos_profile):
item_group_dict[data.name] = [data.lft, data.rgt]
return item_group_dict
def get_customers_list(pos_profile):
def get_customers_list(pos_profile={}):
cond = "1=1"
customer_groups = []
if pos_profile.get('customer_groups'):
@@ -170,7 +171,7 @@ def get_customers_list(pos_profile):
cond = "customer_group in (%s)"%(', '.join(['%s']*len(customer_groups)))
return frappe.db.sql(""" select name, customer_name, customer_group,
territory from tabCustomer where disabled = 0
territory, customer_pos_id from tabCustomer where disabled = 0
and {cond}""".format(cond=cond), tuple(customer_groups), as_dict=1) or {}
def get_customers_address(customers):
@@ -183,13 +184,29 @@ def get_customers_address(customers):
email_id, phone, fax, pincode from `tabAddress` where is_primary_address =1 and name in
(select parent from `tabDynamic Link` where link_doctype = 'Customer' and link_name = %s
and parenttype = 'Address')""", data.name, as_dict=1)
if address:
address_data = address[0]
address_data.update({'full_name': data.customer_name})
customer_address[data.name] = address_data
address_data = {}
if address: address_data = address[0]
address_data.update({'full_name': data.customer_name, 'customer_pos_id': data.customer_pos_id})
customer_address[data.name] = address_data
return customer_address
def get_contacts(customers):
customer_contact = {}
if isinstance(customers, basestring):
customers = [frappe._dict({'name': customers})]
for data in customers:
contact = frappe.db.sql(""" select email_id, phone, mobile_no from `tabContact`
where is_primary_contact =1 and name in
(select parent from `tabDynamic Link` where link_doctype = 'Customer' and link_name = %s
and parenttype = 'Contact')""", data.name, as_dict=1)
if contact:
customer_contact[data.name] = contact[0]
return customer_contact
def get_child_nodes(group_type, root):
lft, rgt = frappe.db.get_value(group_type, root, ["lft", "rgt"])
return frappe.db.sql(""" Select name, lft, rgt from `tab{tab}` where
@@ -294,7 +311,7 @@ def make_invoice(doc_list={}, email_queue_list={}, customers_list={}):
if isinstance(customers_list, basestring):
customers_list = json.loads(customers_list)
customers = make_customer_and_address(customers_list)
customers_list = make_customer_and_address(customers_list)
name_list = []
for docs in doc_list:
for name, doc in docs.items():
@@ -303,6 +320,8 @@ def make_invoice(doc_list={}, email_queue_list={}, customers_list={}):
si_doc = frappe.new_doc('Sales Invoice')
si_doc.offline_pos_name = name
si_doc.update(doc)
si_doc.set_posting_time = 1
si_doc.customer = get_customer_id(doc)
si_doc.due_date = doc.get('posting_date')
submit_invoice(si_doc, name, doc)
name_list.append(name)
@@ -310,28 +329,54 @@ def make_invoice(doc_list={}, email_queue_list={}, customers_list={}):
name_list.append(name)
email_queue = make_email_queue(email_queue_list)
customers = get_customers_list()
return {
'invoice': name_list,
'email_queue': email_queue,
'customers': customers
'customers': customers_list,
'synced_customers_list': customers,
'synced_address': get_customers_address(customers),
'synced_contacts': get_contacts(customers)
}
def validate_records(doc):
validate_item(doc)
def make_customer_and_address(customers):
customer_list = []
for name, data in customers.items():
if not frappe.db.exists('Customer', name):
name = add_customer(name)
data = json.loads(data)
make_address(data, name)
customer_list.append(name)
return customer_list
def get_customer_id(doc, customer=None):
cust_id = None
if doc.get('customer_pos_id'):
cust_id = frappe.db.get_value('Customer',
{'customer_pos_id': doc.get('customer_pos_id')}, 'name')
def add_customer(name):
if not cust_id:
customer = customer or doc.get('customer')
if frappe.db.exists('Customer', customer):
cust_id = customer
else:
cust_id = add_customer(doc)
return cust_id
def make_customer_and_address(customers):
customers_list = []
for customer, data in customers.items():
data = json.loads(data)
cust_id = get_customer_id(data, customer)
if not cust_id:
cust_id = add_customer(data)
else:
frappe.db.set_value("Customer", cust_id, "customer_name", data.get('full_name'))
make_contact(data, cust_id)
make_address(data, cust_id)
customers_list.append(customer)
frappe.db.commit()
return customers_list
def add_customer(data):
customer_doc = frappe.new_doc('Customer')
customer_doc.customer_name = name
customer_doc.customer_name = data.get('full_name') or data.get('customer')
customer_doc.customer_pos_id = data.get('customer_pos_id')
customer_doc.customer_type = 'Company'
customer_doc.customer_group = frappe.db.get_single_value('Selling Settings', 'customer_group')
customer_doc.territory = frappe.db.get_single_value('Selling Settings', 'territory')
@@ -340,6 +385,30 @@ def add_customer(name):
frappe.db.commit()
return customer_doc.name
def make_contact(args,customer):
if args.get('email_id') or args.get('phone'):
name = frappe.db.get_value('Dynamic Link',
{'link_doctype': 'Customer', 'link_name': customer, 'parenttype': 'Contact'}, 'parent')
args = {
'first_name': args.get('full_name'),
'email_id': args.get('email_id'),
'phone': args.get('phone')
}
doc = frappe.new_doc('Contact')
if name:
doc = frappe.get_doc('Contact', name)
doc.update(args)
doc.is_primary_contact = 1
if not name:
doc.append('links',{
'link_doctype': 'Customer',
'link_name': customer
})
doc.save(ignore_permissions=True)
def make_address(args, customer):
if not args.get('address_line1'): return

View File

@@ -112,33 +112,44 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
},
sales_order_btn: function() {
this.$sales_order_btn = cur_frm.add_custom_button(__('Sales Order'),
var me = this;
this.$sales_order_btn = this.frm.add_custom_button(__('Sales Order'),
function() {
erpnext.utils.map_current_doc({
method: "erpnext.selling.doctype.sales_order.sales_order.make_sales_invoice",
source_doctype: "Sales Order",
target: me.frm,
setters: {
customer: me.frm.doc.customer || undefined,
},
get_query_filters: {
docstatus: 1,
status: ["!=", "Closed"],
per_billed: ["<", 99.99],
customer: cur_frm.doc.customer || undefined,
company: cur_frm.doc.company
company: me.frm.doc.company
}
})
}, __("Get items from"));
},
delivery_note_btn: function() {
this.$delivery_note_btn = cur_frm.add_custom_button(__('Delivery Note'),
var me = this;
this.$delivery_note_btn = this.frm.add_custom_button(__('Delivery Note'),
function() {
erpnext.utils.map_current_doc({
method: "erpnext.stock.doctype.delivery_note.delivery_note.make_sales_invoice",
source_doctype: "Delivery Note",
target: me.frm,
date_field: "posting_date",
setters: {
customer: me.frm.doc.customer || undefined
},
get_query: function() {
var filters = {
company: cur_frm.doc.company
docstatus: 1,
company: me.frm.doc.company
};
if(cur_frm.doc.customer) filters["customer"] = cur_frm.doc.customer;
if(me.frm.doc.customer) filters["customer"] = me.frm.doc.customer;
return {
query: "erpnext.controllers.queries.get_delivery_notes_to_be_billed",
filters: filters
@@ -288,6 +299,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
this.calculate_write_off_amount()
}else {
this.frm.set_value("change_amount", 0.0)
this.frm.set_value("base_change_amount", 0.0)
}
this.frm.refresh_fields();

File diff suppressed because it is too large Load Diff

View File

@@ -17,6 +17,7 @@ from erpnext.stock.doctype.delivery_note.delivery_note import update_billed_amou
from erpnext.projects.doctype.timesheet.timesheet import get_projectwise_timesheet_data
from erpnext.accounts.doctype.asset.depreciation \
import get_disposal_account_and_cost_center, get_gl_entries_on_asset_disposal
from erpnext.stock.doctype.batch.batch import set_batch_nos
form_grid_templates = {
"items": "templates/form_grid/item_grid.html"
@@ -52,7 +53,7 @@ class SalesInvoice(SellingController):
def validate(self):
super(SalesInvoice, self).validate()
self.validate_posting_time()
self.validate_auto_set_posting_time()
self.so_dn_required()
self.validate_proj_cust()
self.validate_with_previous_doc()
@@ -78,6 +79,10 @@ class SalesInvoice(SellingController):
if not self.is_opening:
self.is_opening = 'No'
if self._action != 'submit' and self.update_stock and not self.is_return:
set_batch_nos(self, 'warehouse', True)
self.set_against_income_account()
self.validate_c_form()
@@ -87,7 +92,7 @@ class SalesInvoice(SellingController):
self.set_billing_hours_and_amount()
self.update_timesheet_billing_for_project()
self.set_status()
def before_save(self):
set_account_for_mode_of_payment(self)
@@ -238,7 +243,6 @@ class SalesInvoice(SellingController):
(not self.project and not data.sales_invoice) or \
(not sales_invoice and data.sales_invoice == self.name):
data.sales_invoice = sales_invoice
if self.project: return
def on_update(self):
self.set_paid_amount()
@@ -330,7 +334,7 @@ class SalesInvoice(SellingController):
frappe.throw(_("Debit To account must be a Receivable account"))
self.party_account_currency = account.account_currency
def clear_unallocated_mode_of_payments(self):
self.set("payments", self.get("payments", {"amount": ["not in", [0, None, ""]]}))
@@ -341,13 +345,23 @@ class SalesInvoice(SellingController):
super(SalesInvoice, self).validate_with_previous_doc({
"Sales Order": {
"ref_dn_field": "sales_order",
"compare_fields": [["customer", "="], ["company", "="], ["project", "="],
["currency", "="]],
"compare_fields": [["customer", "="], ["company", "="], ["project", "="], ["currency", "="]]
},
"Sales Order Item": {
"ref_dn_field": "so_detail",
"compare_fields": [["item_code", "="], ["uom", "="], ["conversion_factor", "="]],
"is_child_table": True,
"allow_duplicate_prev_row_id": True
},
"Delivery Note": {
"ref_dn_field": "delivery_note",
"compare_fields": [["customer", "="], ["company", "="], ["project", "="],
["currency", "="]],
"compare_fields": [["customer", "="], ["company", "="], ["project", "="], ["currency", "="]]
},
"Delivery Note Item": {
"ref_dn_field": "dn_detail",
"compare_fields": [["item_code", "="], ["uom", "="], ["conversion_factor", "="]],
"is_child_table": True,
"allow_duplicate_prev_row_id": True
},
})
@@ -368,6 +382,12 @@ class SalesInvoice(SellingController):
def add_remarks(self):
if not self.remarks: self.remarks = 'No Remarks'
def validate_auto_set_posting_time(self):
# Don't auto set the posting date and time if invoice is amended
if self.is_new() and self.amended_from:
self.set_posting_time = 1
self.validate_posting_time()
def so_dn_required(self):
"""check in manage account if sales order / delivery note required or not."""
@@ -390,10 +410,10 @@ class SalesInvoice(SellingController):
throw(_("Customer {0} does not belong to project {1}").format(self.customer,self.project))
def validate_pos(self):
if flt(self.paid_amount) + flt(self.write_off_amount) \
- flt(self.grand_total) > 1/(10**(self.precision("grand_total") + 1)) and self.is_return:
frappe.throw(_("""Paid amount + Write Off Amount can not be greater than Grand Total"""))
if self.is_return:
if flt(self.paid_amount) + flt(self.write_off_amount) - flt(self.grand_total) < \
1/(10**(self.precision("grand_total") + 1)):
frappe.throw(_("Paid amount + Write Off Amount can not be greater than Grand Total"))
def validate_item_code(self):
for d in self.get('items'):
@@ -462,13 +482,14 @@ class SalesInvoice(SellingController):
self.set('packed_items', [])
def set_billing_hours_and_amount(self):
for timesheet in self.timesheets:
ts_doc = frappe.get_doc('Timesheet', timesheet.time_sheet)
if not timesheet.billing_hours and ts_doc.total_billable_hours:
timesheet.billing_hours = ts_doc.total_billable_hours
if not self.project:
for timesheet in self.timesheets:
ts_doc = frappe.get_doc('Timesheet', timesheet.time_sheet)
if not timesheet.billing_hours and ts_doc.total_billable_hours:
timesheet.billing_hours = ts_doc.total_billable_hours
if not timesheet.billing_amount and ts_doc.total_billable_amount:
timesheet.billing_amount = ts_doc.total_billable_amount
if not timesheet.billing_amount and ts_doc.total_billable_amount:
timesheet.billing_amount = ts_doc.total_billable_amount
def update_timesheet_billing_for_project(self):
if not self.timesheets and self.project:
@@ -535,7 +556,7 @@ class SalesInvoice(SellingController):
def make_gl_entries(self, gl_entries=None, repost_future_gle=True, from_repost=False):
if not self.grand_total:
return
if not gl_entries:
gl_entries = self.get_gl_entries()
@@ -681,7 +702,7 @@ class SalesInvoice(SellingController):
else payment_mode.amount
}, payment_mode_account_currency)
)
def make_gle_for_change_amount(self, gl_entries):
if cint(self.is_pos) and self.change_amount:
if self.account_for_change_amount:
@@ -698,7 +719,7 @@ class SalesInvoice(SellingController):
"against_voucher_type": self.doctype
}, self.party_account_currency)
)
gl_entries.append(
self.get_gl_dict({
"account": self.account_for_change_amount,
@@ -708,7 +729,7 @@ class SalesInvoice(SellingController):
)
else:
frappe.throw(_("Select change amount account"), title="Mandatory Field")
def make_write_off_gl_entry(self, gl_entries):
# write off entries, applicable if only pos
if self.write_off_account and self.write_off_amount:
@@ -792,7 +813,7 @@ def make_delivery_note(source_name, target_doc=None):
def update_item(source_doc, target_doc, source_parent):
target_doc.qty = flt(source_doc.qty) - flt(source_doc.delivered_qty)
target_doc.stock_qty = target_doc.qty * flt(source_doc.conversion_factor)
target_doc.base_amount = target_doc.qty * flt(source_doc.base_rate)
target_doc.amount = target_doc.qty * flt(source_doc.rate)
@@ -810,7 +831,8 @@ def make_delivery_note(source_name, target_doc=None):
"parent": "against_sales_invoice",
"serial_no": "serial_no",
"sales_order": "against_sales_order",
"so_detail": "so_detail"
"so_detail": "so_detail",
"cost_center": "cost_center"
},
"postprocess": update_item,
"condition": lambda doc: doc.delivered_by_supplier!=1
@@ -839,4 +861,4 @@ def make_sales_return(source_name, target_doc=None):
def set_account_for_mode_of_payment(self):
for data in self.payments:
if not data.account:
data.account = get_bank_cash_account(data.mode_of_payment, self.company).get("account")
data.account = get_bank_cash_account(data.mode_of_payment, self.company).get("account")

View File

@@ -13,6 +13,7 @@
"editable_grid": 1,
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -41,6 +42,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
@@ -72,6 +74,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -99,6 +102,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -129,6 +133,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -157,6 +162,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -186,6 +192,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -218,6 +225,7 @@
"width": "200px"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -246,6 +254,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -276,6 +285,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -305,6 +315,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -333,6 +344,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
@@ -363,6 +375,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -392,6 +405,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -419,6 +433,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -449,6 +464,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -478,6 +494,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -507,6 +524,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -535,6 +553,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -566,6 +585,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -597,6 +617,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -626,65 +647,7 @@
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "price_list_rate",
"fieldname": "discount_percentage",
"fieldtype": "Percent",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Discount on Price List Rate (%)",
"length": 0,
"no_copy": 0,
"oldfieldname": "adj_rate",
"oldfieldtype": "Float",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_19",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -716,6 +679,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -746,12 +710,13 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.margin_type && doc.price_list_rate",
"fieldname": "total_margin",
"depends_on": "eval:doc.margin_type && doc.price_list_rate && doc.margin_rate_or_amount",
"fieldname": "rate_with_margin",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
@@ -760,7 +725,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Total Margin",
"label": "Rate With Margin",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -776,6 +741,69 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_19",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "price_list_rate",
"fieldname": "discount_percentage",
"fieldtype": "Percent",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Discount (%) on Price List Rate with Margin",
"length": 0,
"no_copy": 0,
"oldfieldname": "adj_rate",
"oldfieldtype": "Float",
"permlevel": 0,
"precision": "2",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -803,6 +831,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
@@ -834,6 +863,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -865,6 +895,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -892,6 +923,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -923,6 +955,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -954,6 +987,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -983,6 +1017,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1011,6 +1046,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1041,6 +1077,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1071,6 +1108,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1099,6 +1137,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1129,6 +1168,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1159,6 +1199,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -1189,6 +1230,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1218,6 +1260,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -1246,6 +1289,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1279,6 +1323,7 @@
"width": "120px"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1309,6 +1354,7 @@
"width": "120px"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1336,6 +1382,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1370,6 +1417,7 @@
"width": "120px"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -1400,6 +1448,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1431,6 +1480,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1461,6 +1511,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1492,6 +1543,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1521,6 +1573,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1548,6 +1601,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1577,6 +1631,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1607,6 +1662,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1639,6 +1695,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1669,6 +1726,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1699,6 +1757,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
@@ -1730,6 +1789,7 @@
"width": "150px"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
@@ -1760,6 +1820,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -1789,6 +1850,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1820,6 +1882,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1850,6 +1913,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1878,6 +1942,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1909,6 +1974,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1939,6 +2005,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1969,6 +2036,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -1998,6 +2066,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -2028,6 +2097,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -2056,6 +2126,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
@@ -2094,7 +2165,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2017-04-19 11:53:26.682964",
"modified": "2017-05-10 17:14:42.681757",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Item",

View File

@@ -123,10 +123,10 @@ def round_off_debit_credit(gl_map):
debit_credit_diff = flt(debit_credit_diff, precision)
if gl_map[0]["voucher_type"] == "Journal Entry":
if gl_map[0]["voucher_type"] in ("Journal Entry", "Payment Entry"):
allowance = 5.0 / (10**precision)
else:
allowance = 1
allowance = .5
if abs(debit_credit_diff) >= allowance:
frappe.throw(_("Debit and Credit not equal for {0} #{1}. Difference is {2}.")

View File

@@ -20,6 +20,7 @@ frappe.pages['pos'].refresh = function (wrapper) {
erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
init: function (wrapper) {
this.page_len = 20;
this.freeze = false;
this.page = wrapper.page;
this.wrapper = $(wrapper).find('.page-content');
this.set_indicator();
@@ -72,14 +73,23 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
onload: function () {
var me = this;
this.get_data_from_server(function () {
me.make_control();
me.create_new();
});
},
make_menu_list: function () {
var me = this;
this.page.clear_menu();
// for mobile
this.page.add_menu_item(__("Pay"), function () {
me.validate();
me.update_paid_amount_status(true);
me.create_invoice();
me.make_payment();
}).addClass('visible-xs');
this.page.add_menu_item(__("New Sales Invoice"), function () {
me.save_previous_entry();
me.create_new();
@@ -88,7 +98,6 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
this.page.add_menu_item(__("Sync Master Data"), function () {
me.get_data_from_server(function () {
me.load_data(false);
me.make_customer();
me.make_item_list();
me.set_missing_values();
})
@@ -303,6 +312,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
this.serial_no_data = r.message.serial_no_data;
this.batch_no_data = r.message.batch_no_data;
this.tax_data = r.message.tax_data;
this.contacts = r.message.contacts;
this.address = r.message.address || {};
this.price_list_data = r.message.price_list_data;
this.bin_data = r.message.bin_data;
@@ -312,7 +322,6 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
this.default_customer = r.message.default_customer || null;
this.print_settings = locals[":Print Settings"]["Print Settings"];
this.letter_head = (this.pos_profile_data.length > 0) ? frappe.boot.letter_heads[this.pos_profile_data[letter_head]] : {};
this.make_control()
},
save_previous_entry: function () {
@@ -327,6 +336,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
this.name = null;
this.load_data(true);
this.setup();
this.set_default_customer()
},
load_data: function (load_doc) {
@@ -346,8 +356,14 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
this.print_template_data = frappe.render_template("print_template", {
content: this.print_template,
title: "POS", base_url: frappe.urllib.get_base_url(), print_css: frappe.boot.print_css,
print_settings: this.print_settings, header: this.letter_head.header, footer: this.letter_head.footer
title: "POS",
base_url: frappe.urllib.get_base_url(),
print_css: frappe.boot.print_css,
print_settings: this.print_settings,
header: this.letter_head.header,
footer: this.letter_head.footer,
landscape: false,
columns: []
})
},
@@ -360,6 +376,16 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
}
},
set_default_customer: function() {
if (this.default_customer && !this.frm.doc.customer) {
this.party_field.$input.val(this.default_customer);
this.frm.doc.customer = this.default_customer;
this.numeric_keypad.show();
this.toggle_list_customer(false)
this.toggle_item_cart(true)
}
},
set_transaction_defaults: function (party) {
var me = this;
this.party = party;
@@ -378,6 +404,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
this.frm = {}
this.frm.doc = this.doc
this.set_transaction_defaults("Customer");
this.frm.doc["allow_user_to_edit_rate"] = this.pos_profile_data["allow_user_to_edit_rate"] ? true : false,
this.wrapper.html(frappe.render_template("pos", this.frm.doc));
this.make_search();
this.make_customer();
@@ -675,11 +702,6 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
me.toggle_delete_button();
}
if (this.default_customer && !this.frm.doc.customer) {
this.party_field.$input.val(this.default_customer);
this.frm.doc.customer = this.default_customer;
}
this.party_field.awesomeplete =
new Awesomplete(this.party_field.$input.get(0), {
minChars: 0,
@@ -687,15 +709,28 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
autoFirst: true,
list: [],
filter: function (item, input) {
var value = item.value.toLowerCase();
if (value.indexOf('is_action') !== -1 ||
value.indexOf(input) !== -1) {
if (item.value.includes('is_action')) {
return true;
}
input = input.toLowerCase();
item = this.get_item(item.value);
var searchtext =
Object.keys(item)
.filter(key => ['customer_name', 'customer_group', 'value', 'label', 'email_id', 'phone', 'mobile_no'].includes(key))
.map(key => item[key])
.join(" ")
.toLowerCase();
return searchtext.includes(input)
},
item: function (item, input) {
var d = item;
var d = this.get_item(item.value);
var html = "<span>" + __(d.label || d.value) + "</span>";
if(d.customer_name) {
html += '<br><span class="text-muted ellipsis">' + __(d.customer_name) + '</span>';
}
return $('<li></li>')
.data('item.autocomplete', d)
.html('<a><p>' + html + '</p></a>')
@@ -703,28 +738,12 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
}
});
this.customers_mapper = this.customers.map(function (c) {
return {
label: c.name,
value: c.name,
customer_group: c.customer_group,
territory: c.territory
}
});
this.customers_mapper.push({
label: "<span class='text-primary link-option'>"
+ "<i class='fa fa-plus' style='margin-right: 5px;'></i> "
+ __("Create a new Customer")
+ "</span>",
value: 'is_action',
action: me.add_customer
});
this.prepare_customer_mapper()
this.autocomplete_customers();
this.party_field.$input
.on('input', function (e) {
me.party_field.awesomeplete.list = this.customers_mapper;
me.party_field.awesomeplete.list = me.customers_mapper;
})
.on('awesomplete-select', function (e) {
var customer = me.party_field.awesomeplete
@@ -764,6 +783,33 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
});
},
prepare_customer_mapper: function() {
var me = this;
this.customers_mapper = this.customers.map(function (c) {
contact = me.contacts[c.name];
return {
label: c.name,
value: c.name,
customer_name: c.customer_name,
customer_group: c.customer_group,
territory: c.territory,
phone: contact ? contact["phone"] : '',
mobile_no: contact ? contact["mobile_no"] : '',
email_id: contact ? contact["email_id"] : ''
}
});
this.customers_mapper.push({
label: "<span class='text-primary link-option'>"
+ "<i class='fa fa-plus' style='margin-right: 5px;'></i> "
+ __("Create a new Customer")
+ "</span>",
value: 'is_action',
action: me.add_customer
});
},
autocomplete_customers: function() {
this.party_field.awesomeplete.list = this.customers_mapper;
},
@@ -782,12 +828,12 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
add_customer: function() {
this.frm.doc.customer = "";
this.update_customer(true)
this.update_customer(true);
this.numeric_keypad.show();
},
update_customer: function (new_customer) {
var me = this;
if (!this.connection_status) return;
this.customer_doc = new frappe.ui.Dialog({
'title': 'Customer',
@@ -850,27 +896,40 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
"label": __("ZIP Code"),
"fieldname": "pincode",
"fieldtype": "Data"
},
{
"label": __("Customer POS Id"),
"fieldname": "customer_pos_id",
"fieldtype": "Data",
"hidden": 1
}
]
})
this.customer_doc.show()
this.render_address_data()
this.customer_doc.set_primary_action(__("Save"), function () {
me.make_offline_customer(new_customer);
me.pos_bill.show();
me.list_customers.hide();
});
},
render_address_data: function() {
var me = this;
this.address_data = this.address[this.frm.doc.customer];
this.customer_doc.set_values(this.address_data)
this.address_data = this.address[this.frm.doc.customer] || {};
if(!this.address_data.email_id || !this.address_data.phone) {
this.address_data = this.contacts[this.frm.doc.customer];
}
this.customer_doc.set_values(this.address_data)
if(!this.customer_doc.fields_dict.full_name.$input.val()) {
this.customer_doc.set_value("full_name", this.frm.doc.customer)
}
if(!this.customer_doc.fields_dict.customer_pos_id.value) {
this.customer_doc.set_value("customer_pos_id", $.now())
}
},
get_address_from_localstorage: function() {
@@ -880,6 +939,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
make_offline_customer: function(new_customer) {
this.frm.doc.customer = this.frm.doc.customer || this.customer_doc.get_values().full_name;
this.frm.doc.customer_pos_id = this.customer_doc.fields_dict.customer_pos_id.value;
this.customer_details = this.get_customers_details();
this.customer_details[this.frm.doc.customer] = this.get_prompt_details();
this.party_field.$input.val(this.frm.doc.customer);
@@ -901,12 +961,13 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
});
}
this.address[this.frm.doc.customer] = this.customer_doc.get_values();
this.address[this.frm.doc.customer] = JSON.parse(this.get_prompt_details())
},
get_prompt_details: function() {
this.prompt_details = this.customer_doc.get_values();
this.prompt_details['country'] = this.pos_profile_data.country;
this.prompt_details['customer_pos_id'] = this.customer_doc.fields_dict.customer_pos_id.value;
return JSON.stringify(this.prompt_details)
},
@@ -920,26 +981,6 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
this.numeric_keypad.show();
},
get_customers: function (key) {
var me = this;
key = key.toLowerCase().trim()
var re = new RegExp('%', 'g');
var reg = new RegExp(key.replace(re, '\\w*\\s*[a-zA-Z0-9]*'))
if (key) {
return $.grep(this.customers, function (data) {
if (reg.test(data.name.toLowerCase())
|| reg.test(data.customer_name.toLowerCase())
|| (data.customer_group && reg.test(data.customer_group.toLowerCase()))) {
return data
}
})
} else {
customers = this.customers.sort(function (a, b) { return a.idx < b.idx })
return customers.slice(0, 20)
}
},
make_item_list: function () {
var me = this;
if (!this.price_list) {
@@ -1158,6 +1199,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
this.child_doc = this.get_child_item(this.item_code);
$(this.wrapper).find('.selected-item').empty();
if(this.child_doc.length) {
this.child_doc[0]["allow_user_to_edit_rate"] = this.pos_profile_data["allow_user_to_edit_rate"] ? true : false,
this.selected_row = $(frappe.render_template("pos_selected_item", this.child_doc[0]))
$(this.wrapper).find('.selected-item').html(this.selected_row)
}
@@ -1366,7 +1408,6 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
actual_qty: me.actual_qty_dict[d.item_code] || 0.0,
projected_qty: d.projected_qty,
rate: format_currency(d.rate, me.frm.doc.currency),
enabled: me.pos_profile_data["allow_user_to_edit_rate"] ? true : false,
amount: format_currency(d.amount, me.frm.doc.currency),
selected_class: (me.item_code == d.item_code) ? "active" : ""
})).appendTo($items);
@@ -1586,8 +1627,11 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
this.si_docs = this.get_submitted_invoice() || [];
this.email_queue_list = this.get_email_queue() || {};
this.customers_list = this.get_customers_details() || {};
if(this.customer_doc) {
this.freeze = this.customer_doc.display
}
if (this.si_docs.length || this.email_queue_list || this.customers_list) {
if ((this.si_docs.length || this.email_queue_list || this.customers_list) && !this.freeze) {
frappe.call({
method: "erpnext.accounts.doctype.sales_invoice.pos.make_invoice",
args: {
@@ -1597,12 +1641,17 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
},
callback: function (r) {
if (r.message) {
me.customers = r.message.synced_customers_list;
me.address = r.message.synced_address;
me.contacts = r.message.synced_contacts;
me.removed_items = r.message.invoice;
me.removed_email = r.message.email_queue
me.removed_customers = r.message.customers
me.remove_doc_from_localstorage();
me.remove_email_queue_from_localstorage();
me.remove_customer_from_localstorage();
me.prepare_customer_mapper()
me.autocomplete_customers()
}
}
})

View File

@@ -7,10 +7,13 @@ import frappe
import datetime
from frappe import _, msgprint, scrub
from frappe.defaults import get_user_permissions
from frappe.utils import add_days, getdate, formatdate, get_first_day, date_diff, add_years, get_timestamp
from frappe.utils import add_days, getdate, formatdate, get_first_day, date_diff, \
add_years, get_timestamp, nowdate, flt
from frappe.geo.doctype.address.address import get_address_display, get_default_address
from frappe.email.doctype.contact.contact import get_contact_details, get_default_contact
from erpnext.exceptions import PartyFrozen, InvalidCurrency, PartyDisabled, InvalidAccountCurrency
from erpnext.accounts.utils import get_fiscal_year
from erpnext import get_default_currency
class DuplicatePartyAccountError(frappe.ValidationError): pass
@@ -359,4 +362,39 @@ def get_timeline_data(doctype, name):
timestamp = get_timestamp(date)
out.update({ timestamp: count })
return out
return out
def get_dashboard_info(party_type, party):
current_fiscal_year = get_fiscal_year(nowdate(), as_dict=True)
company = frappe.db.get_default("company") or frappe.get_all("Company")[0].name
party_account_currency = get_party_account_currency(party_type, party, company)
company_default_currency = get_default_currency() \
or frappe.db.get_value('Company', company, 'default_currency')
if party_account_currency==company_default_currency:
total_field = "base_grand_total"
else:
total_field = "grand_total"
doctype = "Sales Invoice" if party_type=="Customer" else "Purchase Invoice"
billing_this_year = frappe.db.sql("""
select sum({0})
from `tab{1}`
where {2}=%s and docstatus=1 and posting_date between %s and %s
""".format(total_field, doctype, party_type.lower()),
(party, current_fiscal_year.year_start_date, current_fiscal_year.year_end_date))
total_unpaid = frappe.db.sql("""
select sum(debit_in_account_currency) - sum(credit_in_account_currency)
from `tabGL Entry`
where party_type = %s and party=%s""", (party_type, party))
info = {}
info["billing_this_year"] = flt(billing_this_year[0][0]) if billing_this_year else 0
info["currency"] = party_account_currency
info["total_unpaid"] = flt(total_unpaid[0][0]) if total_unpaid else 0
if party_type == "Supplier":
info["total_unpaid"] = -1 * info["total_unpaid"]
return info

View File

@@ -7,10 +7,10 @@
"docstatus": 0,
"doctype": "Print Format",
"font": "Default",
"html": "<style>\n\t.print-format table, .print-format tr, \n\t.print-format td, .print-format div, .print-format p {\n\t\tfont-family: Monospace;\n\t\tline-height: 200%;\n\t\tvertical-align: middle;\n\t}\n\t@media screen {\n\t\t.print-format {\n\t\t\twidth: 4in;\n\t\t\tpadding: 0.25in;\n\t\t\tmin-height: 8in;\n\t\t}\n\t}\n</style>\n\n<p class=\"text-center\">\n\t{{ company }}<br>\n\t{{ __(\"POS No : \") }} {{ offline_pos_name }}<br>\n</p>\n<p>\n\t<b>{{ __(\"Date\") }}:</b> {{ dateutil.global_date_format(posting_date) }}<br>\n</p>\n\n<hr>\n<table class=\"table table-condensed cart no-border\">\n\t<thead>\n\t\t<tr>\n\t\t\t<th width=\"50%\">{{ __(\"Item\") }}</b></th>\n\t\t\t<th width=\"25%\" class=\"text-right\">{{ __(\"Qty\") }}</th>\n\t\t\t<th width=\"25%\" class=\"text-right\">{{ __(\"Amount\") }}</th>\n\t\t</tr>\n\t</thead>\n\t<tbody>\n\t\t{% for item in items %}\n\t\t<tr>\n\t\t\t<td>\n\t\t\t\t{{ item.item_name }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">{{ format_number(item.qty, null,precision(\"difference\")) }}<br>@ {{ format_currency(item.rate, currency) }}</td>\n\t\t\t<td class=\"text-right\">{{ format_currency(item.amount, currency) }}</td>\n\t\t</tr>\n\t\t{% endfor %}\n\t</tbody>\n</table>\n\n<table class=\"table table-condensed no-border\">\n\t<tbody>\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t{{ __(\"Net Total\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ format_currency(total, currency) }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{% for row in taxes %}\n\t\t{% if not row.included_in_print_rate %}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t{{ row.description }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ format_currency(row.tax_amount, currency) }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{% endif %}\n\t\t{% endfor %}\n\t\t{% if discount_amount %}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t{{ __(\"Discount\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ format_currency(discount_amount, currency) }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{% endif %}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ __(\"Grand Total\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ format_currency(grand_total, currency) }}\n\t\t\t</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ __(\"Paid Amount\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ format_currency(paid_amount, currency) }}\n\t\t\t</td>\n\t\t</tr>\n\t</tbody>\n</table>\n\n\n<hr>\n<p>{{ terms }}</p>\n<p class=\"text-center\">{{ __(\"Thank you, please visit again.\") }}</p>",
"html": "<style>\n\t.print-format table, .print-format tr, \n\t.print-format td, .print-format div, .print-format p {\n\t\tfont-family: Monospace;\n\t\tline-height: 200%;\n\t\tvertical-align: middle;\n\t}\n\t@media screen {\n\t\t.print-format {\n\t\t\twidth: 4in;\n\t\t\tpadding: 0.25in;\n\t\t\tmin-height: 8in;\n\t\t}\n\t}\n</style>\n\n<p class=\"text-center\">\n\t{{ company }}<br>\n\t{{ __(\"POS No : \") }} {{ offline_pos_name }}<br>\n</p>\n<p>\n\t<b>{{ __(\"Customer\") }}:</b> {{ customer }}<br>\n</p>\n\n<p>\n\t<b>{{ __(\"Date\") }}:</b> {{ dateutil.global_date_format(posting_date) }}<br>\n</p>\n\n<hr>\n<table class=\"table table-condensed cart no-border\">\n\t<thead>\n\t\t<tr>\n\t\t\t<th width=\"50%\">{{ __(\"Item\") }}</b></th>\n\t\t\t<th width=\"25%\" class=\"text-right\">{{ __(\"Qty\") }}</th>\n\t\t\t<th width=\"25%\" class=\"text-right\">{{ __(\"Amount\") }}</th>\n\t\t</tr>\n\t</thead>\n\t<tbody>\n\t\t{% for item in items %}\n\t\t<tr>\n\t\t\t<td>\n\t\t\t\t{{ item.item_name }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">{{ format_number(item.qty, null,precision(\"difference\")) }}<br>@ {{ format_currency(item.rate, currency) }}</td>\n\t\t\t<td class=\"text-right\">{{ format_currency(item.amount, currency) }}</td>\n\t\t</tr>\n\t\t{% endfor %}\n\t</tbody>\n</table>\n\n<table class=\"table table-condensed no-border\">\n\t<tbody>\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t{{ __(\"Net Total\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ format_currency(total, currency) }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{% for row in taxes %}\n\t\t{% if not row.included_in_print_rate %}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 70%\">\n\t\t\t\t{{ row.description }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ format_currency(row.tax_amount, currency) }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{% endif %}\n\t\t{% endfor %}\n\t\t{% if discount_amount %}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t{{ __(\"Discount\") }}\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ format_currency(discount_amount, currency) }}\n\t\t\t</td>\n\t\t</tr>\n\t\t{% endif %}\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ __(\"Grand Total\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ format_currency(grand_total, currency) }}\n\t\t\t</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td class=\"text-right\" style=\"width: 75%\">\n\t\t\t\t<b>{{ __(\"Paid Amount\") }}</b>\n\t\t\t</td>\n\t\t\t<td class=\"text-right\">\n\t\t\t\t{{ format_currency(paid_amount, currency) }}\n\t\t\t</td>\n\t\t</tr>\n\t</tbody>\n</table>\n\n\n<hr>\n<p>{{ terms }}</p>\n<p class=\"text-center\">{{ __(\"Thank you, please visit again.\") }}</p>",
"idx": 0,
"line_breaks": 0,
"modified": "2017-04-19 13:28:05.129504",
"modified": "2017-05-19 14:36:04.740728",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Point of Sale",

View File

@@ -33,7 +33,9 @@ class ReceivablePayableReport(object):
if args.get("party_type") == "Supplier":
columns += [_("Bill No") + "::80", _("Bill Date") + ":Date:80"]
for label in ("Invoiced Amount", "Paid Amount", "Outstanding Amount"):
credit_or_debit_note = "Credit Note" if args.get("party_type") == "Customer" else "Debit Note"
for label in ("Invoiced Amount", "Paid Amount", credit_or_debit_note, "Outstanding Amount"):
columns.append({
"label": label,
"fieldtype": "Currency",
@@ -95,13 +97,14 @@ class ReceivablePayableReport(object):
self.filters["company"] = frappe.db.get_single_value('Global Defaults', 'default_company')
company_currency = frappe.db.get_value("Company", self.filters.get("company"), "default_currency")
return_entries = self.get_return_entries(args.get("party_type"))
data = []
for gle in self.get_entries_till(self.filters.report_date, args.get("party_type")):
if self.is_receivable_or_payable(gle, dr_or_cr, future_vouchers):
outstanding_amount = flt(self.get_outstanding_amount(gle,
self.filters.report_date, dr_or_cr), currency_precision)
outstanding_amount, credit_note_amount = self.get_outstanding_amount(gle,
self.filters.report_date, dr_or_cr, return_entries, currency_precision)
if abs(outstanding_amount) > 0.1/10**currency_precision:
row = [gle.posting_date, gle.party]
@@ -123,8 +126,8 @@ class ReceivablePayableReport(object):
# invoiced and paid amounts
invoiced_amount = gle.get(dr_or_cr) if (gle.get(dr_or_cr) > 0) else 0
paid_amt = invoiced_amount - outstanding_amount
row += [invoiced_amount, paid_amt, outstanding_amount]
paid_amt = invoiced_amount - outstanding_amount - credit_note_amount
row += [invoiced_amount, paid_amt, credit_note_amount, outstanding_amount]
# ageing data
entry_date = due_date if self.filters.ageing_based_on == "Due Date" else gle.posting_date
@@ -132,7 +135,8 @@ class ReceivablePayableReport(object):
cint(self.filters.range3), self.age_as_on, entry_date, outstanding_amount)
# issue 6371-Ageing buckets should not have amounts if due date is not reached
if self.filters.ageing_based_on == "Due Date" and getdate(due_date) > getdate(self.filters.report_date):
if self.filters.ageing_based_on == "Due Date" \
and getdate(due_date) > getdate(self.filters.report_date):
row[-1]=row[-2]=row[-3]=row[-4]=0
if self.filters.get(scrub(args.get("party_type"))):
@@ -175,14 +179,28 @@ class ReceivablePayableReport(object):
# entries adjusted with future vouchers
((gle.against_voucher_type, gle.against_voucher) in future_vouchers)
)
def get_return_entries(self, party_type):
doctype = "Sales Invoice" if party_type=="Customer" else "Purchase Invoice"
return [d.name for d in frappe.get_all(doctype, filters={"is_return": 1, "docstatus": 1})]
def get_outstanding_amount(self, gle, report_date, dr_or_cr):
payment_amount = 0.0
def get_outstanding_amount(self, gle, report_date, dr_or_cr, return_entries, currency_precision):
payment_amount, credit_note_amount = 0.0, 0.0
reverse_dr_or_cr = "credit" if dr_or_cr=="debit" else "debit"
for e in self.get_gl_entries_for(gle.party, gle.party_type, gle.voucher_type, gle.voucher_no):
if getdate(e.posting_date) <= report_date and e.name!=gle.name:
payment_amount += (flt(e.credit if gle.party_type == "Customer" else e.debit) - flt(e.get(dr_or_cr)))
return flt(gle.get(dr_or_cr)) - flt(gle.credit if gle.party_type == "Customer" else gle.debit) - payment_amount
amount = flt(e.get(reverse_dr_or_cr)) - flt(e.get(dr_or_cr))
if e.voucher_no not in return_entries:
payment_amount += amount
else:
credit_note_amount += amount
outstanding_amount = flt((flt(gle.get(dr_or_cr)) - flt(gle.get(reverse_dr_or_cr)) \
- payment_amount - credit_note_amount), currency_precision)
credit_note_amount = flt(credit_note_amount, currency_precision)
return outstanding_amount, credit_note_amount
def get_party_name(self, party_type, party_name):
return self.get_party_map(party_type).get(party_name, {}).get("customer_name" if party_type == "Customer" else "supplier_name") or ""

View File

@@ -17,9 +17,11 @@ class AccountsReceivableSummary(ReceivablePayableReport):
if party_naming_by == "Naming Series":
columns += [ args.get("party_type") + " Name::140"]
credit_debit_label = _("Credit Note Amt") if args.get('party_type') == 'Customer' else _("Debit Note Amt")
columns += [
_("Total Invoiced Amt") + ":Currency/currency:140",
_("Total Paid Amt") + ":Currency/currency:140",
credit_debit_label + ":Currency/currency:140",
_("Total Outstanding Amt") + ":Currency/currency:160",
"0-" + str(self.filters.range1) + ":Currency/currency:100",
str(self.filters.range1) + "-" + str(self.filters.range2) + ":Currency/currency:100",
@@ -56,7 +58,7 @@ class AccountsReceivableSummary(ReceivablePayableReport):
row += [self.get_party_name(args.get("party_type"), party)]
row += [
party_dict.invoiced_amt, party_dict.paid_amt, party_dict.outstanding_amt,
party_dict.invoiced_amt, party_dict.paid_amt, party_dict.credit_amt, party_dict.outstanding_amt,
party_dict.range1, party_dict.range2, party_dict.range3, party_dict.range4,
]
@@ -77,6 +79,7 @@ class AccountsReceivableSummary(ReceivablePayableReport):
frappe._dict({
"invoiced_amt": 0,
"paid_amt": 0,
"credit_amt": 0,
"outstanding_amt": 0,
"range1": 0,
"range2": 0,
@@ -104,7 +107,7 @@ class AccountsReceivableSummary(ReceivablePayableReport):
if args.get("party_type") == "Supplier":
cols += ["bill_no", "bill_date"]
cols += ["invoiced_amt", "paid_amt",
cols += ["invoiced_amt", "paid_amt", "credit_amt",
"outstanding_amt", "age", "range1", "range2", "range3", "range4", "currency"]
if args.get("party_type") == "Supplier":

View File

@@ -7,7 +7,8 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() {
frappe.query_reports["Balance Sheet"]["filters"].push({
"fieldname": "accumulated_values",
"label": __("Accumulated Values"),
"fieldtype": "Check"
"fieldtype": "Check",
"default": 1
});
});

View File

@@ -9,22 +9,19 @@ from erpnext.accounts.report.financial_statements import (get_period_list, get_c
def execute(filters=None):
period_list = get_period_list(filters.from_fiscal_year, filters.to_fiscal_year,
filters.periodicity, filters.accumulated_values, filters.company)
filters.periodicity, company=filters.company)
asset = get_data(filters.company, "Asset", "Debit", period_list,
only_current_fiscal_year=False, filters=filters,
accumulated_values=filters.accumulated_values,
ignore_closing_entries=True, ignore_accumulated_values_for_fy=True)
accumulated_values=filters.accumulated_values)
liability = get_data(filters.company, "Liability", "Credit", period_list,
only_current_fiscal_year=False, filters=filters,
accumulated_values=filters.accumulated_values,
ignore_closing_entries=True, ignore_accumulated_values_for_fy=True)
accumulated_values=filters.accumulated_values)
equity = get_data(filters.company, "Equity", "Credit", period_list,
only_current_fiscal_year=False, filters=filters,
accumulated_values=filters.accumulated_values,
ignore_closing_entries=True, ignore_accumulated_values_for_fy=True)
accumulated_values=filters.accumulated_values)
provisional_profit_loss, total_credit = get_provisional_profit_loss(asset, liability, equity,
period_list, filters.company)
@@ -114,7 +111,8 @@ def check_opening_balance(asset, liability, equity):
opening_balance -= flt(liability[0].get("opening_balance", 0), float_precision)
if equity:
opening_balance -= flt(equity[0].get("opening_balance", 0), float_precision)
opening_balance = flt(opening_balance, float_precision)
if opening_balance:
return _("Previous Financial Year is not closed"),opening_balance
return None,None

View File

@@ -3,11 +3,15 @@
from __future__ import unicode_literals
import frappe
import re
from frappe import _
from frappe.utils import (flt, getdate, get_first_day, get_last_day, date_diff,
add_months, add_days, formatdate, cint)
from erpnext.accounts.utils import get_fiscal_year
def get_period_list(from_fiscal_year, to_fiscal_year, periodicity, accumulated_values=False, company=None):
def get_period_list(from_fiscal_year, to_fiscal_year, periodicity, accumulated_values=False,
company=None, reset_period_on_fy_change=True):
"""Get a list of dict {"from_date": from_date, "to_date": to_date, "key": key, "label": label}
Periodicity can be (Yearly, Quarterly, Monthly)"""
@@ -49,7 +53,8 @@ def get_period_list(from_fiscal_year, to_fiscal_year, periodicity, accumulated_v
# if a fiscal year ends before a 12 month period
period.to_date = year_end_date
period.to_date_fiscal_year = get_date_fiscal_year(period.to_date, company)
period.to_date_fiscal_year = get_fiscal_year(period.to_date, company=company)[0]
period.from_date_fiscal_year_start_date = get_fiscal_year(period.from_date, company=company)[1]
period_list.append(period)
@@ -65,7 +70,10 @@ def get_period_list(from_fiscal_year, to_fiscal_year, periodicity, accumulated_v
if not accumulated_values:
label = get_label(periodicity, opts["from_date"], opts["to_date"])
else:
label = get_label(periodicity, period_list[0]["from_date"], opts["to_date"])
if reset_period_on_fy_change:
label = get_label(periodicity, opts.from_date_fiscal_year_start_date, opts["to_date"])
else:
label = get_label(periodicity, period_list[0].from_date, opts["to_date"])
opts.update({
"key": key.replace(" ", "_").replace("-", "_"),
@@ -150,10 +158,6 @@ def calculate_values(accounts_by_name, gl_entries_by_account, period_list, accum
if entry.posting_date < period_list[0].year_start_date:
d["opening_balance"] = d.get("opening_balance", 0.0) + flt(entry.debit) - flt(entry.credit)
def get_date_fiscal_year(date, company):
from erpnext.accounts.utils import get_fiscal_year
return get_fiscal_year(date, company=company)[0]
def accumulate_values_into_parents(accounts, accounts_by_name, period_list, accumulated_values):
"""accumulate children's values in parent accounts"""
for d in reversed(accounts):
@@ -274,6 +278,9 @@ def sort_root_accounts(roots):
"""Sort root types as Asset, Liability, Equity, Income, Expense"""
def compare_roots(a, b):
if a.value and re.split('\W+', a.value)[0].isdigit():
# if chart of accounts is numbered, then sort by number
return cmp(a.value, b.value)
if a.report_type != b.report_type and a.report_type == "Balance Sheet":
return -1
if a.root_type != b.root_type and a.root_type == "Asset":

View File

@@ -5,7 +5,15 @@
</div>
{% } %}
<h2 class="text-center">{%= __("Statement of Account") %}</h2>
<h4 class="text-center">{%= (filters.party || filters.account) && ((filters.party || filters.account) + ", ") || "" %} {%= filters.company %}</h4>
<h4 class="text-center">
{% if (filters.party_name) { %}
{%= filters.party_name %}
{% } else if (filters.party) { %}
{%= filters.party %}
{% } else if (filters.account) { %}
{%= filters.account %}
{% } %}
</h4>
<h5 class="text-center">
{%= dateutil.str_to_user(filters.from_date) %}
{%= __("to") %}

View File

@@ -74,8 +74,27 @@ frappe.query_reports["General Ledger"] = {
frappe.throw(__("Please select Party Type first"));
}
return party_type;
},
change: function() {
var party_type = frappe.query_report_filters_by_name.party_type.get_value();
var party = frappe.query_report_filters_by_name.party.get_value();
if(!party_type || !party) {
frappe.query_report_filters_by_name.party_name.set_value("");
return;
}
var fieldname = party_type.toLowerCase() + "_name";
frappe.db.get_value(party_type, party, fieldname, function(value) {
frappe.query_report_filters_by_name.party_name.set_value(value[fieldname]);
});
}
},
{
"fieldname":"party_name",
"label": __("Party Name"),
"fieldtype": "Data",
"hidden": 1
},
{
"fieldname":"group_by_voucher",
"label": __("Group by Voucher"),

View File

@@ -20,7 +20,7 @@ def execute(filters=None):
invoice_expense_map, invoice_tax_map = get_invoice_tax_map(invoice_list,
invoice_expense_map, expense_accounts)
invoice_po_pr_map = get_invoice_po_pr_map(invoice_list)
supplier_details = get_supplier_deatils(invoice_list)
supplier_details = get_supplier_details(invoice_list)
company_currency = frappe.db.get_value("Company", filters.company, "default_currency")
@@ -205,7 +205,7 @@ def get_account_details(invoice_list):
return account_map
def get_supplier_deatils(invoice_list):
def get_supplier_details(invoice_list):
supplier_details = {}
suppliers = list(set([inv.supplier for inv in invoice_list]))
for supp in frappe.db.sql("""select name, supplier_type from `tabSupplier`

View File

@@ -2,7 +2,7 @@
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
import frappe, erpnext
from frappe import _
from frappe.utils import flt, getdate, formatdate, cstr
from erpnext.accounts.report.financial_statements \
@@ -53,6 +53,7 @@ def validate_filters(filters):
def get_data(filters):
accounts = frappe.db.sql("""select name, parent_account, account_name, root_type, report_type, lft, rgt
from `tabAccount` where company=%s order by lft""", filters.company, as_dict=True)
company_currency = erpnext.get_company_currency(filters.company)
if not accounts:
return None
@@ -69,10 +70,10 @@ def get_data(filters):
opening_balances = get_opening_balances(filters)
total_row = calculate_values(accounts, gl_entries_by_account, opening_balances, filters)
total_row = calculate_values(accounts, gl_entries_by_account, opening_balances, filters, company_currency)
accumulate_values_into_parents(accounts, accounts_by_name)
data = prepare_data(accounts, filters, total_row, parent_children_map)
data = prepare_data(accounts, filters, total_row, parent_children_map, company_currency)
data = filter_out_zero_value_rows(data, parent_children_map,
show_zero_values=filters.get("show_zero_values"))
@@ -119,7 +120,7 @@ def get_rootwise_opening_balances(filters, report_type):
return opening
def calculate_values(accounts, gl_entries_by_account, opening_balances, filters):
def calculate_values(accounts, gl_entries_by_account, opening_balances, filters, company_currency):
init = {
"opening_debit": 0.0,
"opening_credit": 0.0,
@@ -137,7 +138,8 @@ def calculate_values(accounts, gl_entries_by_account, opening_balances, filters)
"credit": 0.0,
"parent_account": None,
"indent": 0,
"has_value": True
"has_value": True,
"currency": company_currency
}
for d in accounts:
@@ -164,9 +166,8 @@ def accumulate_values_into_parents(accounts, accounts_by_name):
for key in value_fields:
accounts_by_name[d.parent_account][key] += d[key]
def prepare_data(accounts, filters, total_row, parent_children_map):
def prepare_data(accounts, filters, total_row, parent_children_map, company_currency):
data = []
company_currency = frappe.db.get_value("Company", filters.company, "default_currency")
for d in accounts:
has_value = False

View File

@@ -11,7 +11,6 @@ from frappe.utils import formatdate, get_number_format_info
# imported to enable erpnext.accounts.utils.get_account_currency
from erpnext.accounts.doctype.account.account import get_account_currency
from erpnext.accounts.report.financial_statements import sort_root_accounts
class FiscalYearError(frappe.ValidationError): pass
@@ -652,6 +651,8 @@ def get_companies():
@frappe.whitelist()
def get_children():
from erpnext.accounts.report.financial_statements import sort_root_accounts
args = frappe.local.form_dict
doctype, company = args['doctype'], args['company']
fieldname = frappe.db.escape(doctype.lower().replace(' ','_'))

View File

@@ -153,30 +153,37 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
},
add_from_mappers: function() {
cur_frm.add_custom_button(__('Material Request'),
var me = this;
this.frm.add_custom_button(__('Material Request'),
function() {
erpnext.utils.map_current_doc({
method: "erpnext.stock.doctype.material_request.material_request.make_purchase_order",
source_doctype: "Material Request",
target: me.frm,
setters: {
company: me.frm.doc.company
},
get_query_filters: {
material_request_type: "Purchase",
docstatus: 1,
status: ["!=", "Stopped"],
per_ordered: ["<", 99.99],
company: cur_frm.doc.company
}
})
}, __("Add items from"));
cur_frm.add_custom_button(__('Supplier Quotation'),
this.frm.add_custom_button(__('Supplier Quotation'),
function() {
erpnext.utils.map_current_doc({
method: "erpnext.buying.doctype.supplier_quotation.supplier_quotation.make_purchase_order",
source_doctype: "Supplier Quotation",
target: me.frm,
setters: {
company: me.frm.doc.company
},
get_query_filters: {
docstatus: 1,
status: ["!=", "Stopped"],
company: cur_frm.doc.company
}
})
}, __("Add items from"));

View File

@@ -59,7 +59,8 @@ class PurchaseOrder(BuyingController):
},
"Supplier Quotation Item": {
"ref_dn_field": "supplier_quotation_item",
"compare_fields": [["rate", "="], ["project", "="], ["item_code", "="]],
"compare_fields": [["rate", "="], ["project", "="], ["item_code", "="],
["uom", "="], ["conversion_factor", "="]],
"is_child_table": True
}
})

View File

@@ -48,7 +48,7 @@ frappe.ui.form.on("Request for Quotation",{
});
});
}
},
make_suppplier_quotation: function(frm) {
@@ -124,24 +124,28 @@ frappe.ui.form.on("Request for Quotation Supplier",{
erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.extend({
refresh: function() {
var me = this;
this._super();
if (this.frm.doc.docstatus===0) {
cur_frm.add_custom_button(__('Material Request'),
this.frm.add_custom_button(__('Material Request'),
function() {
erpnext.utils.map_current_doc({
method: "erpnext.stock.doctype.material_request.material_request.make_request_for_quotation",
source_doctype: "Material Request",
target: me.frm,
setters: {
company: me.frm.doc.company
},
get_query_filters: {
material_request_type: "Purchase",
docstatus: 1,
status: ["!=", "Stopped"],
per_ordered: ["<", 99.99],
company: cur_frm.doc.company
per_ordered: ["<", 99.99]
}
})
}, __("Get items from"));
// Get items from open Material Requests based on supplier
cur_frm.add_custom_button(__('Possible Supplier'), function() {
this.frm.add_custom_button(__('Possible Supplier'), function() {
// Create a dialog window for the user to pick their supplier
var d = new frappe.ui.Dialog({
title: __('Select Possible Supplier'),
@@ -150,32 +154,35 @@ erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.e
{fieldname: 'ok_button', fieldtype:'Button', label:'Get Items from Material Requests'},
]
});
// On the user clicking the ok button
d.fields_dict.ok_button.input.onclick = function() {
var btn = d.fields_dict.ok_button.input;
var v = d.get_values();
if(v) {
$(btn).set_working();
erpnext.utils.map_current_doc({
method: "erpnext.buying.doctype.request_for_quotation.request_for_quotation.get_item_from_material_requests_based_on_supplier",
source_name: v.supplier,
target: me.frm,
setters: {
company: me.frm.doc.company
},
get_query_filters: {
material_request_type: "Purchase",
docstatus: 1,
status: ["!=", "Stopped"],
per_ordered: ["<", 99.99],
company: cur_frm.doc.company
per_ordered: ["<", 99.99]
}
});
$(btn).done_working();
d.hide();
}
}
}
d.show();
}, __("Get items from"));
}
},

View File

@@ -15,6 +15,7 @@
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -45,6 +46,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -76,6 +78,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
@@ -106,6 +109,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -136,6 +140,37 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "tax_id",
"fieldtype": "Data",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Tax ID",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -165,6 +200,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -193,6 +229,7 @@
"width": "50%"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -224,6 +261,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -254,6 +292,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 1,
"collapsible": 0,
@@ -284,6 +323,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -313,6 +353,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -342,6 +383,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -370,6 +412,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -399,6 +442,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -428,6 +472,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -458,6 +503,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -487,6 +533,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -518,6 +565,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -546,6 +594,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -574,6 +623,7 @@
"width": "50%"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -602,6 +652,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -631,12 +682,13 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "",
"description": "Mention if non-standard receivable account",
"description": "Mention if non-standard payable account",
"fieldname": "accounts",
"fieldtype": "Table",
"hidden": 0,
@@ -662,6 +714,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -692,6 +745,7 @@
"width": "50%"
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -722,6 +776,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -753,6 +808,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -794,7 +850,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-03-14 17:04:17.785461",
"modified": "2017-05-17 12:21:21.218428",
"modified_by": "Administrator",
"module": "Buying",
"name": "Supplier",

View File

@@ -6,11 +6,9 @@ import frappe
import frappe.defaults
from frappe import msgprint, _
from frappe.model.naming import make_autoname
from frappe.geo.address_and_contact import (load_address_and_contact,
delete_contact_and_address)
from frappe.geo.address_and_contact import load_address_and_contact, delete_contact_and_address
from erpnext.utilities.transaction_base import TransactionBase
from erpnext.accounts.party import validate_party_accounts, get_timeline_data # keep this
from erpnext.accounts.party import validate_party_accounts, get_dashboard_info, get_timeline_data # keep this
class Supplier(TransactionBase):
def get_feed(self):
@@ -22,22 +20,7 @@ class Supplier(TransactionBase):
self.load_dashboard_info()
def load_dashboard_info(self):
billing_this_year = frappe.db.sql("""
select sum(credit_in_account_currency) - sum(debit_in_account_currency)
from `tabGL Entry`
where voucher_type='Purchase Invoice' and party_type = 'Supplier'
and party=%s and fiscal_year = %s""",
(self.name, frappe.db.get_default("fiscal_year")))
total_unpaid = frappe.db.sql("""select sum(outstanding_amount)
from `tabPurchase Invoice`
where supplier=%s and docstatus = 1""", self.name)
info = {}
info["billing_this_year"] = billing_this_year[0][0] if billing_this_year else 0
info["total_unpaid"] = total_unpaid[0][0] if total_unpaid else 0
info = get_dashboard_info(self.doctype, self.name)
self.set_onload('dashboard_info', info)
def autoname(self):

View File

@@ -14,6 +14,7 @@ frappe.ui.form.on('Suppier Quotation', {
erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.extend({
refresh: function() {
var me = this;
this._super();
if (this.frm.doc.docstatus === 1) {
cur_frm.add_custom_button(__("Purchase Order"), this.make_purchase_order,
@@ -24,18 +25,21 @@ erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.ext
}
else if (this.frm.doc.docstatus===0) {
cur_frm.add_custom_button(__('Material Request'),
this.frm.add_custom_button(__('Material Request'),
function() {
erpnext.utils.map_current_doc({
method: "erpnext.stock.doctype.material_request.material_request.make_supplier_quotation",
source_doctype: "Material Request",
target: me.frm,
setters: {
company: me.frm.doc.company
},
get_query_filters: {
material_request_type: "Purchase",
docstatus: 1,
status: ["!=", "Stopped"],
per_ordered: ["<", 99.99],
company: cur_frm.doc.company
per_ordered: ["<", 99.99]
}
})
}, __("Get items from"));

View File

@@ -35,11 +35,7 @@ erpnext.PurchaseAnalytics = frappe.views.TreeGridReport.extend({
item_key: "supplier",
parent_field: "parent_supplier_type",
formatter: function(item) {
// return repl('<a href="#Report/stock-invoices/customer=%(enc_value)s">%(value)s</a>', {
// value: item.name,
// enc_value: encodeURIComponent(item.name)
// });
return item.name;
return item.supplier_name ? item.supplier_name + " (" + item.name + ")" : item.name;
}
},
"Supplier": {
@@ -47,7 +43,7 @@ erpnext.PurchaseAnalytics = frappe.views.TreeGridReport.extend({
show: false,
item_key: "supplier",
formatter: function(item) {
return item.name;
return item.supplier_name ? item.supplier_name + " (" + item.name + ")" : item.name;
}
},
"Item Group": {

View File

@@ -5,6 +5,7 @@ from __future__ import unicode_literals
import frappe
from frappe.utils import flt, cstr, cint
from frappe import _
import json
from erpnext.stock.doctype.item.item import get_last_purchase_details
from erpnext.stock.doctype.item.item import validate_end_of_life
@@ -63,7 +64,7 @@ def validate_for_items(doc):
validate_end_of_life(d.item_code, item.end_of_life, item.disabled)
# validate stock item
if item.is_stock_item==1 and d.qty and not d.warehouse and not d.delivered_by_supplier:
if item.is_stock_item==1 and d.qty and not d.warehouse and not d.get("delivered_by_supplier"):
frappe.throw(_("Warehouse is mandatory for stock Item {0} in row {1}").format(d.item_code, d.idx))
items.append(cstr(d.item_code))
@@ -78,3 +79,25 @@ def check_for_closed_status(doctype, docname):
if status == "Closed":
frappe.throw(_("{0} {1} status is {2}").format(doctype, docname, status), frappe.InvalidStatusError)
@frappe.whitelist()
def get_linked_material_requests(items):
items = json.loads(items)
mr_list = []
for item in items:
material_request = frappe.db.sql("""SELECT distinct mr.name AS mr_name,
(mr_item.qty - mr_item.ordered_qty) AS qty,
mr_item.item_code AS item_code,
mr_item.name AS mr_item
FROM `tabMaterial Request` mr, `tabMaterial Request Item` mr_item
WHERE mr.name = mr_item.parent
AND mr_item.item_code = %(item)s
AND mr.material_request_type = 'Purchase'
AND mr.per_ordered < 99.99
AND mr.docstatus = 1
AND mr.status != 'Stopped'
ORDER BY mr_item.item_code ASC""",{"item": item}, as_dict=1)
if material_request:
mr_list.append(material_request)
return mr_list

View File

@@ -18,10 +18,6 @@ def get_data():
"type": "doctype",
"name": "Student Log"
},
{
"type": "doctype",
"name": "Student Batch"
},
{
"type": "doctype",
"name": "Student Group"
@@ -58,10 +54,6 @@ def get_data():
{
"type": "doctype",
"name": "Program Enrollment Tool"
},
{
"type": "doctype",
"name": "Student Batch Creation Tool"
}
]
},

View File

@@ -54,7 +54,7 @@ class AccountsController(TransactionBase):
self.validate_currency()
if self.meta.get_field("is_recurring"):
if self.amended_from and self.recurring_id:
if self.amended_from and self.recurring_id == self.amended_from:
self.recurring_id = None
if not self.get("__islocal"):
validate_recurring_document(self)

View File

@@ -165,7 +165,7 @@ def create_variant(item, args):
variant.set("attributes", variant_attributes)
copy_attributes_to_variant(template, variant)
make_variant_item_code(template.item_code, variant)
make_variant_item_code(template.item_code, template.item_name, variant)
return variant
@@ -194,7 +194,7 @@ def copy_attributes_to_variant(item, variant):
for d in variant.attributes:
variant.description += "<p>" + d.attribute + ": " + cstr(d.attribute_value) + "</p>"
def make_variant_item_code(template_item_code, variant):
def make_variant_item_code(template_item_code, template_item_name, variant):
"""Uses template's item code and abbreviations to make variant's item code"""
if variant.item_code:
return
@@ -220,6 +220,4 @@ def make_variant_item_code(template_item_code, variant):
if abbreviations:
variant.item_code = "{0}-{1}".format(template_item_code, "-".join(abbreviations))
if variant.item_code:
variant.item_name = variant.item_code
variant.item_name = "{0}-{1}".format(template_item_name, "-".join(abbreviations))

View File

@@ -222,19 +222,21 @@ def get_project_name(doctype, txt, searchfield, start, page_len, filters):
"_txt": txt.replace('%', '')
})
def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql("""select `tabDelivery Note`.name, `tabDelivery Note`.customer_name
def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len, filters, as_dict):
return frappe.db.sql("""
select `tabDelivery Note`.name, `tabDelivery Note`.customer, `tabDelivery Note`.posting_date
from `tabDelivery Note`
where `tabDelivery Note`.`%(key)s` like %(txt)s and
`tabDelivery Note`.docstatus = 1 and status not in ("Stopped", "Closed") %(fcond)s
`tabDelivery Note`.docstatus = 1 and `tabDelivery Note`.is_return = 0
and status not in ("Stopped", "Closed") %(fcond)s
and (`tabDelivery Note`.per_billed < 100 or `tabDelivery Note`.grand_total = 0)
%(mcond)s order by `tabDelivery Note`.`%(key)s` asc
limit %(start)s, %(page_len)s""" % {
"key": searchfield,
"fcond": get_filters_cond(doctype, filters, []),
"mcond": get_match_cond(doctype),
"start": "%(start)s", "page_len": "%(page_len)s", "txt": "%(txt)s"
}, { "start": start, "page_len": page_len, "txt": ("%%%s%%" % txt) })
""" % {
"key": searchfield,
"fcond": get_filters_cond(doctype, filters, []),
"mcond": get_match_cond(doctype),
"txt": "%(txt)s"
}, { "txt": ("%%%s%%" % txt) }, as_dict=as_dict)
def get_batch_no(doctype, txt, searchfield, start, page_len, filters):
cond = ""
@@ -360,7 +362,8 @@ def warehouse_query(doctype, txt, searchfield, start, page_len, filters):
sub_query = """ select round(`tabBin`.actual_qty, 2) from `tabBin`
where `tabBin`.warehouse = `tabWarehouse`.name
{bin_conditions} """.format(
bin_conditions=get_filters_cond(doctype, filter_dict.get("Bin"), bin_conditions))
bin_conditions=get_filters_cond(doctype, filter_dict.get("Bin"),
bin_conditions, ignore_permissions=True))
response = frappe.db.sql("""select `tabWarehouse`.name,
CONCAT_WS(" : ", "Actual Qty", ifnull( ({sub_query}), 0) ) as actual_qty

View File

@@ -53,8 +53,8 @@ def validate_returned_items(doc):
valid_items = frappe._dict()
select_fields = "item_code, qty, parenttype" if doc.doctype=="Purchase Invoice" \
else "item_code, qty, serial_no, batch_no, parenttype"
select_fields = "item_code, qty, rate, parenttype" if doc.doctype=="Purchase Invoice" \
else "item_code, qty, rate, serial_no, batch_no, parenttype"
if doc.doctype in ['Purchase Invoice', 'Purchase Receipt']:
select_fields += ",rejected_qty, received_qty"
@@ -82,10 +82,15 @@ def validate_returned_items(doc):
else:
ref = valid_items.get(d.item_code, frappe._dict())
validate_quantity(doc, d, ref, valid_items, already_returned_items)
if ref.batch_no and d.batch_no not in ref.batch_no:
if ref.rate and doc.doctype in ("Delivery Note", "Sales Invoice") and flt(d.rate) > ref.rate:
frappe.throw(_("Row # {0}: Rate cannot be greater than the rate used in {1} {2}")
.format(d.idx, doc.doctype, doc.return_against))
elif ref.batch_no and d.batch_no not in ref.batch_no:
frappe.throw(_("Row # {0}: Batch No must be same as {1} {2}")
.format(d.idx, doc.doctype, doc.return_against))
elif ref.serial_no:
if not d.serial_no:
frappe.throw(_("Row # {0}: Serial No is mandatory").format(d.idx))
@@ -131,6 +136,7 @@ def get_ref_item_dict(valid_items, ref_item_row):
valid_items.setdefault(ref_item_row.item_code, frappe._dict({
"qty": 0,
"rate": 0,
"rejected_qty": 0,
"received_qty": 0,
"serial_no": [],
@@ -138,6 +144,8 @@ def get_ref_item_dict(valid_items, ref_item_row):
}))
item_dict = valid_items[ref_item_row.item_code]
item_dict["qty"] += ref_item_row.qty
if ref_item_row.get("rate", 0) > item_dict["rate"]:
item_dict["rate"] = ref_item_row.get("rate", 0)
if ref_item_row.parenttype in ['Purchase Invoice', 'Purchase Receipt']:
item_dict["received_qty"] += ref_item_row.received_qty

View File

@@ -170,7 +170,7 @@ class SellingController(StockController):
def validate_selling_price(self):
def throw_message(item_name, rate, ref_rate_field):
frappe.throw(_("""Selling price for item {0} is lower than its {1}. Selling price should be atleast {2}""")
frappe.throw(_("""Selling rate for item {0} is lower than its {1}. Selling rate should be atleast {2}""")
.format(item_name, ref_rate_field, rate))
if not frappe.db.get_single_value("Selling Settings", "validate_selling_price"):
@@ -178,18 +178,19 @@ class SellingController(StockController):
for it in self.get("items"):
last_purchase_rate, is_stock_item = frappe.db.get_value("Item", it.item_code, ["last_purchase_rate", "is_stock_item"])
if flt(it.base_rate) < flt(last_purchase_rate):
throw_message(it.item_name, last_purchase_rate, "last purchase rate")
last_purchase_rate_in_sales_uom = last_purchase_rate / (it.conversion_factor or 1)
if flt(it.base_rate) < flt(last_purchase_rate_in_sales_uom):
throw_message(it.item_name, last_purchase_rate_in_sales_uom, "last purchase rate")
last_valuation_rate = frappe.db.sql("""
SELECT valuation_rate FROM `tabStock Ledger Entry` WHERE item_code = %s
AND warehouse = %s AND valuation_rate > 0
ORDER BY posting_date DESC, posting_time DESC, name DESC LIMIT 1
""", (it.item_code, it.warehouse))
if is_stock_item and flt(it.base_rate) < flt(last_valuation_rate):
throw_message(it.name, last_valuation_rate, "valuation rate")
if last_valuation_rate:
last_valuation_rate_in_sales_uom = last_valuation_rate[0][0] / (it.conversion_factor or 1)
if is_stock_item and flt(it.base_rate) < flt(last_valuation_rate_in_sales_uom):
throw_message(it.name, last_valuation_rate_in_sales_uom, "valuation rate")
def get_item_list(self):

View File

@@ -19,10 +19,10 @@ status_map = {
["Converted", "has_customer"],
],
"Opportunity": [
["Quotation", "has_quotation"],
["Converted", "has_ordered_quotation"],
["Lost", "eval:self.status=='Lost'"],
["Lost", "has_lost_quotation"],
["Quotation", "has_active_quotation"],
["Converted", "has_ordered_quotation"],
["Closed", "eval:self.status=='Closed'"]
],
"Quotation": [
@@ -46,8 +46,8 @@ status_map = {
["Draft", None],
["Submitted", "eval:self.docstatus==1"],
["Return", "eval:self.is_return==1 and self.docstatus==1"],
["Credit Note Issued", "eval:self.outstanding_amount < 0 and self.docstatus==1"],
["Paid", "eval:self.outstanding_amount==0 and self.docstatus==1 and self.is_return==0"],
["Paid", "eval:self.outstanding_amount<=0 and self.docstatus==1 and self.is_return==0"],
["Credit Note Issued", "eval:self.outstanding_amount < 0 and self.docstatus==1 and self.is_return==0 and get_value('Sales Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1})"],
["Unpaid", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1"],
["Overdue", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1"],
["Cancelled", "eval:self.docstatus==2"],
@@ -56,8 +56,8 @@ status_map = {
["Draft", None],
["Submitted", "eval:self.docstatus==1"],
["Return", "eval:self.is_return==1 and self.docstatus==1"],
["Debit Note Issued", "eval:self.outstanding_amount < 0 and self.docstatus==1"],
["Paid", "eval:self.outstanding_amount==0 and self.docstatus==1 and self.is_return==0"],
["Paid", "eval:self.outstanding_amount<=0 and self.docstatus==1 and self.is_return==0"],
["Debit Note Issued", "eval:self.outstanding_amount < 0 and self.docstatus==1 and self.is_return==0 and get_value('Purchase Invoice', {'is_return': 1, 'return_against': self.name, 'docstatus': 1})"],
["Unpaid", "eval:self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()) and self.docstatus==1"],
["Overdue", "eval:self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()) and self.docstatus==1"],
["Cancelled", "eval:self.docstatus==2"],
@@ -85,6 +85,16 @@ status_map = {
["Completed", "eval:self.per_billed == 100 and self.docstatus == 1"],
["Cancelled", "eval:self.docstatus==2"],
["Closed", "eval:self.status=='Closed'"],
],
"Material Request": [
["Draft", None],
["Stopped", "eval:self.status == 'Stopped'"],
["Cancelled", "eval:self.docstatus == 2"],
["Pending", "eval:self.status != 'Stopped' and self.per_ordered == 0 and self.docstatus == 1"],
["Partially Ordered", "eval:self.status != 'Stopped' and self.per_ordered < 100 and self.per_ordered > 0 and self.docstatus == 1"],
["Ordered", "eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Purchase'"],
["Transferred", "eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Material Transfer'"],
["Issued", "eval:self.status != 'Stopped' and self.per_ordered == 100 and self.docstatus == 1 and self.material_request_type == 'Material Issue'"]
]
}
@@ -119,14 +129,16 @@ class StatusUpdater(Document):
self.status = s[0]
break
elif s[1].startswith("eval:"):
if frappe.safe_eval(s[1][5:], None, { "self": self.as_dict(), "getdate": getdate, "nowdate": nowdate }):
if frappe.safe_eval(s[1][5:], None, { "self": self.as_dict(), "getdate": getdate,
"nowdate": nowdate, "get_value": frappe.db.get_value }):
self.status = s[0]
break
elif getattr(self, s[1])():
self.status = s[0]
break
if self.status != _status and self.status not in ("Submitted", "Cancelled"):
if self.status != _status and self.status not in ("Cancelled", "Partially Ordered",
"Ordered", "Issued", "Transferred"):
self.add_comment("Label", _(self.status))
if update:

View File

@@ -59,10 +59,10 @@ class calculate_taxes_and_totals(object):
(1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))
if item.doctype in ['Quotation Item', 'Sales Order Item', 'Delivery Note Item', 'Sales Invoice Item']:
item.total_margin = self.calculate_margin(item)
item.rate_with_margin = self.calculate_margin(item)
item.rate = flt(item.total_margin * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))\
if item.total_margin > 0 else item.rate
item.rate = flt(item.rate_with_margin * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))\
if item.rate_with_margin > 0 else item.rate
item.net_rate = item.rate
item.amount = flt(item.rate * item.qty, item.precision("amount"))
@@ -440,16 +440,16 @@ class calculate_taxes_and_totals(object):
self.doc.conversion_rate, self.doc.precision("grand_total")) - self.doc.total_advance
- flt(self.doc.base_write_off_amount), self.doc.precision("grand_total"))
if self.doc.doctype == "Sales Invoice":
if self.doc.doctype == "Sales Invoice":
self.doc.round_floats_in(self.doc, ["paid_amount"])
paid_amount = self.doc.paid_amount \
if self.doc.party_account_currency == self.doc.currency else self.doc.base_paid_amount
change_amount = self.doc.change_amount \
if self.doc.party_account_currency == self.doc.currency else self.doc.base_change_amount
self.calculate_write_off_amount()
self.calculate_change_amount()
paid_amount = self.doc.paid_amount \
if self.doc.party_account_currency == self.doc.currency else self.doc.base_paid_amount
change_amount = self.doc.change_amount \
if self.doc.party_account_currency == self.doc.currency else self.doc.base_change_amount
self.doc.outstanding_amount = flt(total_amount_to_pay - flt(paid_amount) +
flt(change_amount), self.doc.precision("outstanding_amount"))
@@ -462,9 +462,12 @@ class calculate_taxes_and_totals(object):
if self.doc.is_pos:
for payment in self.doc.get('payments'):
payment.base_amount = flt(payment.amount * self.doc.conversion_rate)
payment.amount = flt(payment.amount)
payment.base_amount = payment.amount * flt(self.doc.conversion_rate)
paid_amount += payment.amount
base_paid_amount += payment.base_amount
elif not self.doc.is_return:
self.doc.set('payments', [])
self.doc.paid_amount = flt(paid_amount, self.doc.precision("paid_amount"))
self.doc.base_paid_amount = flt(base_paid_amount, self.doc.precision("base_paid_amount"))
@@ -472,7 +475,9 @@ class calculate_taxes_and_totals(object):
def calculate_change_amount(self):
self.doc.change_amount = 0.0
self.doc.base_change_amount = 0.0
if self.doc.paid_amount > self.doc.grand_total:
if self.doc.paid_amount > self.doc.grand_total and not self.doc.is_return \
and any([d.type == "Cash" for d in self.doc.payments]):
self.doc.change_amount = flt(self.doc.paid_amount - self.doc.grand_total +
self.doc.write_off_amount, self.doc.precision("change_amount"))
@@ -487,7 +492,7 @@ class calculate_taxes_and_totals(object):
self.doc.precision("base_write_off_amount"))
def calculate_margin(self, item):
total_margin = 0.0
rate_with_margin = 0.0
if item.price_list_rate:
if item.pricing_rule and not self.doc.ignore_pricing_rule:
pricing_rule = frappe.get_doc('Pricing Rule', item.pricing_rule)
@@ -496,6 +501,6 @@ class calculate_taxes_and_totals(object):
if item.margin_type and item.margin_rate_or_amount:
margin_value = item.margin_rate_or_amount if item.margin_type == 'Amount' else flt(item.price_list_rate) * flt(item.margin_rate_or_amount) / 100
total_margin = flt(item.price_list_rate) + flt(margin_value)
rate_with_margin = flt(item.price_list_rate) + flt(margin_value)
return total_margin
return rate_with_margin

View File

@@ -0,0 +1,73 @@
from __future__ import unicode_literals
import unittest
import frappe
import random, json
import frappe.utils
from frappe.utils import nowdate
from frappe.model import mapper
from frappe.test_runner import make_test_records
class TestMapper(unittest.TestCase):
def test_map_docs(self):
'''Test mapping of multiple source docs on a single target doc'''
make_test_records("Item")
items = frappe.get_all("Item", fields = ["name", "item_code"], filters = {'is_sales_item': 1, 'has_variants': 0})
customers = frappe.get_all("Customer")
if items and customers:
# Make source docs (quotations) and a target doc (sales order)
customer = random.choice(customers).name
qtn1, item_list_1 = self.make_quotation(items, customer)
qtn2, item_list_2 = self.make_quotation(items, customer)
so, item_list_3 = self.make_sales_order()
# Map source docs to target with corresponding mapper method
method = "erpnext.selling.doctype.quotation.quotation.make_sales_order"
updated_so = mapper.map_docs(method, json.dumps([qtn1.name, qtn2.name]), so)
# Assert that all inserted items are present in updated sales order
src_items = item_list_1 + item_list_2 + item_list_3
self.assertEqual(set([d.item_code for d in src_items]),
set([d.item_code for d in updated_so.items]))
def get_random_items(self, items, limit):
'''Get a number of random items from a list of given items'''
random_items = []
for i in range(0, limit):
random_items.append(random.choice(items))
return random_items
def make_quotation(self, items, customer):
item_list = self.get_random_items(items, 3)
qtn = frappe.get_doc({
"doctype": "Quotation",
"quotation_to": "Customer",
"customer": customer,
"order_type": "Sales"
})
for item in item_list:
qtn.append("items", {"qty": "2", "item_code": item.item_code})
qtn.submit()
return qtn, item_list
def make_sales_order(self):
item = frappe.get_doc({
"base_amount": 1000.0,
"base_rate": 100.0,
"description": "CPU",
"doctype": "Sales Order Item",
"item_code": "_Test Item Home Desktop 100",
"item_name": "CPU",
"parentfield": "items",
"qty": 10.0,
"rate": 100.0,
"warehouse": "_Test Warehouse - _TC",
"stock_uom": "_Test UOM",
"conversion_factor": 1.0,
"uom": "_Test UOM"
})
so = frappe.get_doc(frappe.get_test_records('Sales Order')[0])
so.insert(ignore_permissions=True)
return so, [item]

View File

@@ -43,7 +43,8 @@ class Lead(SellingController):
if self.email_id == self.contact_by:
frappe.throw(_("Next Contact By cannot be same as the Lead Email Address"))
self.image = has_gravatar(self.email_id)
if self.is_new() or not self.image:
self.image = has_gravatar(self.email_id)
if self.contact_date and self.contact_date < now():
frappe.throw(_("Next Contact Date cannot be in the past"))

View File

@@ -35,6 +35,7 @@ frappe.ui.form.on("Opportunity", {
var doc = frm.doc;
frm.events.enquiry_from(frm);
frm.trigger('set_contact_link');
erpnext.toggle_naming_series();
if(!doc.__islocal && doc.status!=="Lost") {
if(doc.with_items){
@@ -54,6 +55,20 @@ frappe.ui.form.on("Opportunity", {
cur_frm.cscript['Declare Opportunity Lost']);
}
}
if(!frm.doc.__islocal && frm.perm[0].write && frm.doc.docstatus==0) {
if(frm.doc.status==="Open") {
frm.add_custom_button(__("Close"), function() {
frm.set_value("status", "Closed");
frm.save();
});
} else {
frm.add_custom_button(__("Reopen"), function() {
frm.set_value("status", "Open");
frm.save();
});
}
}
},
set_contact_link: function(frm) {
@@ -122,25 +137,6 @@ erpnext.crm.Opportunity = frappe.ui.form.Controller.extend({
$.extend(cur_frm.cscript, new erpnext.crm.Opportunity({frm: cur_frm}));
cur_frm.cscript.refresh = function(doc, cdt, cdn) {
erpnext.toggle_naming_series();
var frm = cur_frm;
if(!doc.__islocal && frm.perm[0].write && doc.docstatus==0) {
if(frm.doc.status==="Open") {
frm.add_custom_button(__("Close"), function() {
frm.set_value("status", "Closed");
frm.save();
});
} else {
frm.add_custom_button(__("Reopen"), function() {
frm.set_value("status", "Open");
frm.save();
});
}
}
}
cur_frm.cscript.onload_post_render = function(doc, cdt, cdn) {
if(doc.enquiry_from == 'Lead' && doc.lead)
cur_frm.cscript.lead(doc, cdt, cdn);

View File

@@ -31,7 +31,6 @@ class Opportunity(TransactionBase):
if not self.enquiry_from:
frappe.throw(_("Opportunity From field is mandatory"))
self.set_status()
self.validate_item_details()
self.validate_uom_is_integer("uom", "qty")
self.validate_lead_cust()
@@ -75,7 +74,7 @@ class Opportunity(TransactionBase):
self.lead = lead_name
def declare_enquiry_lost(self,arg):
if not self.has_quotation():
if not self.has_active_quotation():
frappe.db.set(self, 'status', 'Lost')
frappe.db.set(self, 'order_lost_reason', arg)
else:
@@ -84,20 +83,31 @@ class Opportunity(TransactionBase):
def on_trash(self):
self.delete_events()
def has_quotation(self):
return frappe.db.get_value("Quotation Item", {"prevdoc_docname": self.name, "docstatus": 1})
def has_active_quotation(self):
return frappe.db.sql("""
select q.name
from `tabQuotation` q, `tabQuotation Item` qi
where q.name = qi.parent and q.docstatus=1 and qi.prevdoc_docname =%s
and q.status not in ('Lost', 'Closed')""", self.name)
def has_ordered_quotation(self):
return frappe.db.sql("""select q.name from `tabQuotation` q, `tabQuotation Item` qi
where q.name = qi.parent and q.docstatus=1 and qi.prevdoc_docname =%s and q.status = 'Ordered'""", self.name)
return frappe.db.sql("""
select q.name
from `tabQuotation` q, `tabQuotation Item` qi
where q.name = qi.parent and q.docstatus=1 and qi.prevdoc_docname =%s
and q.status = 'Ordered'""", self.name)
def has_lost_quotation(self):
return frappe.db.sql("""
lost_quotation = frappe.db.sql("""
select q.name
from `tabQuotation` q, `tabQuotation Item` qi
where q.name = qi.parent and q.docstatus=1
and qi.prevdoc_docname =%s and q.status = 'Lost'
""", self.name)
if lost_quotation:
if self.has_active_quotation():
return False
return True
def validate_cust_name(self):
if self.customer:

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

View File

@@ -64,7 +64,7 @@ On submission of the invoice, the "Fixed Asset Account" will be debited and paya
## Sell an Asset
To sale an asset, open the asset record and create a Sales Invoice by clicking on "Sale Asset". On submission of the Sales Invoice, following entries will take place:
To sell an asset, open the asset record and create a Sales Invoice by clicking on "Sell Asset". On submission of the Sales Invoice, following entries will take place:
- "Receivable Account" (Debtors) will be debited by the sales amount.
- "Fixed Asset Account" will be credited by the purchase amount of asset.

View File

@@ -1,6 +1,6 @@
People who assist you in getting business are termed as Sales Partners. Sales Partners can be represented by different names in ERPNext. You can call them Channel Partner, Distributor, Dealer, Agent, Retailer, Implementation Partner, Reseller etc.
For each Sales Partner, you can define commission offer to them. When Sales Partner is selected in transactions, there commission is calculated over Net Total of Sales Order/Invoice or Delivery Note.
For each Sales Partner, you can define commission offer to them. When Sales Partner is selected in transactions, their commission is calculated over Net Total of Sales Order/Invoice or Delivery Note.
You can track Sales Personwise commission in the report under Selling module.

View File

@@ -8,7 +8,7 @@ You can manage multiple incoming and outgoing Email Accounts in ERPNext. There h
ERPNext will create templates for a bunch of email accounts by default. Not all of them are enabled. To enable them, you must set your account details.
There are 2 types of email accounts, outgoing and incoming. Outgoing email accounts use an SMTP service to send emails and emails are retrived from your inbox using a POP service. Most email providers such as GMail, Outlook or Yahoo provide these services.
There are 2 types of email accounts, outgoing and incoming. Outgoing email accounts use an SMTP service to send emails and emails are retrived from your inbox using a IMAP or POP service. Most email providers such as GMail, Outlook or Yahoo provide these services.
<img class="screenshot" alt="Defining Criteria" src="{{docs_base_url}}/assets/img/setup/email/email-account-list.png">
@@ -28,7 +28,7 @@ To setup an incoming Email Account, check on **Enable Incoming** and set your PO
### How ERPNext handles replies
In ERPNext when you send an email to a contact like a customer, the sender will be the user who sent the email. In the **Reply-To** property, the Email Address will be of the default incoming account (like `replies@yourcompany.com`). ERPNext will automatically extract these emails from the incoming account and tag it to the relvant communication
In ERPNext when you send an email to a contact like a customer, the sender will be the user who sent the email. In the **Reply-To** property, the Email Address will be of the default incoming account (like `replies@yourcompany.com`). ERPNext will automatically extract these emails from the incoming account and tag it to the relevant communication
### Notification for unreplied messages

View File

@@ -0,0 +1,30 @@
#Setting up Stripe
To setup Stripe,
`Explore > Integrations > Stripe Settings`
#### Setup Stripe
To enable Stripe payment service, you need to configure parameters like Publishable Key, Secret Key
<img class="screenshot" alt="Razorpay Settings" src="{{docs_base_url}}/assets/img/setup/integrations/stripe_setting.png">
On enabling service, the system will create Payment Gateway record and Account head in chart of account with account type as Bank.
<img class="screenshot" alt="Stripe COA" src="{{docs_base_url}}/assets/img/setup/integrations/stripe_coa.png">
Also it will create Payment Gateway Account entry. Payment Gateway Account is configuration hub from this you can set account head from existing COA, default Payment Request email body template.
<img class="screenshot" alt="Payment Gateway Account" src="{{docs_base_url}}/assets/img/setup/integrations/payment_gateway_account_stripe.png">
After configuring Payment Gateway Account your system is able to accept online payments.
####Supporting transaction currencies
"AED", "ALL", "ANG", "ARS", "AUD", "AWG", "BBD", "BDT", "BIF", "BMD", "BND",
"BOB", "BRL", "BSD", "BWP", "BZD", "CAD", "CHF", "CLP", "CNY", "COP", "CRC", "CVE", "CZK", "DJF",
"DKK", "DOP", "DZD", "EGP", "ETB", "EUR", "FJD", "FKP", "GBP", "GIP", "GMD", "GNF", "GTQ", "GYD",
"HKD", "HNL", "HRK", "HTG", "HUF", "IDR", "ILS", "INR", "ISK", "JMD", "JPY", "KES", "KHR", "KMF",
"KRW", "KYD", "KZT", "LAK", "LBP", "LKR", "LRD", "MAD", "MDL", "MNT", "MOP", "MRO", "MUR", "MVR",
"MWK", "MXN", "MYR", "NAD", "NGN", "NIO", "NOK", "NPR", "NZD", "PAB", "PEN", "PGK", "PHP", "PKR",
"PLN", "PYG", "QAR", "RUB", "SAR", "SBD", "SCR", "SEK", "SGD", "SHP", "SLL", "SOS", "STD", "SVC",
"SZL", "THB", "TOP", "TTD", "TWD", "TZS", "UAH", "UGX", "USD", "UYU", "UZS", "VND", "VUV", "WST",
"XAF", "XOF", "XPF", "YER", "ZAR"

View File

@@ -12,7 +12,7 @@ app_email = "info@erpnext.com"
app_license = "GNU General Public License (v3)"
source_link = "https://github.com/frappe/erpnext"
develop_version = '8.0.0-beta'
develop_version = '8.x.x-beta'
error_report_email = "support@erpnext.com"
@@ -178,7 +178,8 @@ scheduler_events = {
"erpnext.hr.doctype.employee.employee.send_birthday_reminders",
"erpnext.projects.doctype.task.task.set_tasks_as_overdue",
"erpnext.accounts.doctype.asset.depreciation.post_depreciation_entries",
'erpnext.hr.doctype.daily_work_summary_settings.daily_work_summary_settings.send_summary'
"erpnext.hr.doctype.daily_work_summary_settings.daily_work_summary_settings.send_summary",
"erpnext.stock.doctype.serial_no.serial_no.update_maintenance_status"
]
}

View File

@@ -79,7 +79,7 @@ frappe.ui.form.on('Employee Loan', {
},
employee_loan_application: function(frm) {
return frm.call({
return frappe.call({
method: "erpnext.hr.doctype.employee_loan.employee_loan.get_employee_loan_application",
args: {
"employee_loan_application": frm.doc.employee_loan_application

View File

@@ -14,6 +14,7 @@
"engine": "InnoDB",
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -44,6 +45,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -74,6 +76,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -104,6 +107,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -134,6 +138,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -162,6 +167,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -192,6 +198,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -222,10 +229,12 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "Sanctioned",
"fieldname": "status",
"fieldtype": "Select",
"hidden": 0,
@@ -237,7 +246,7 @@
"in_standard_filter": 0,
"label": "Status",
"length": 0,
"no_copy": 0,
"no_copy": 1,
"options": "Sanctioned\nPartially Disbursed\nFully Disbursed\nRepaid/Closed",
"permlevel": 0,
"precision": "",
@@ -252,6 +261,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -281,6 +291,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -310,6 +321,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -340,6 +352,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -371,6 +384,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -400,6 +414,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -428,6 +443,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -459,6 +475,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -490,6 +507,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -522,6 +540,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -551,6 +570,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -581,6 +601,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -611,6 +632,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -639,6 +661,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -669,6 +692,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -699,6 +723,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -728,6 +753,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -758,6 +784,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -787,6 +814,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -818,6 +846,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -846,6 +875,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -877,6 +907,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -916,7 +947,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-03-30 12:59:40.650035",
"modified": "2017-05-02 13:52:30.884154",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee Loan",

View File

@@ -132,7 +132,7 @@ def get_monthly_repayment_amount(repayment_method, loan_amount, rate_of_interest
def get_employee_loan_application(employee_loan_application):
employee_loan = frappe.get_doc("Employee Loan Application", employee_loan_application)
if employee_loan:
return employee_loan
return employee_loan.as_dict()
@frappe.whitelist()
def make_jv_entry(employee_loan, company, employee_loan_account, employee, loan_amount, payment_account):

View File

@@ -5,7 +5,7 @@
from __future__ import unicode_literals
import frappe, math
from frappe import _
from frappe.utils import flt
from frappe.utils import flt, rounded
from frappe.model.mapper import get_mapped_doc
from frappe.model.document import Document
@@ -35,9 +35,21 @@ class EmployeeLoanApplication(Document):
else:
self.repayment_periods = self.loan_amount / self.repayment_amount
self.total_payable_amount = self.repayment_amount * self.repayment_periods
self.total_payable_interest = self.total_payable_amount - self.loan_amount
self.calculate_payable_amount()
def calculate_payable_amount(self):
balance_amount = self.loan_amount
self.total_payable_amount = 0
self.total_payable_interest = 0
while(balance_amount > 0):
interest_amount = rounded(balance_amount * flt(self.rate_of_interest) / (12*100))
balance_amount = rounded(balance_amount + interest_amount - self.repayment_amount)
self.total_payable_interest += interest_amount
self.total_payable_amount = self.loan_amount + self.total_payable_interest
@frappe.whitelist()
def make_employee_loan(source_name, target_doc = None):
doclist = get_mapped_doc("Employee Loan Application", source_name, {

View File

@@ -39,13 +39,13 @@ class TestEmployeeLoanApplication(unittest.TestCase):
def test_loan_totals(self):
loan_application = frappe.get_doc("Employee Loan Application", {"employee":self.employee})
self.assertEquals(loan_application.repayment_amount, 11445)
self.assertEquals(loan_application.total_payable_interest, 24680)
self.assertEquals(loan_application.total_payable_amount, 274680)
self.assertEquals(loan_application.total_payable_interest, 24657)
self.assertEquals(loan_application.total_payable_amount, 274657)
loan_application.repayment_method = "Repay Fixed Amount per Period"
loan_application.repayment_amount = 15000
loan_application.save()
self.assertEquals(loan_application.repayment_periods, 18)
self.assertEquals(loan_application.total_payable_interest, 20000)
self.assertEquals(loan_application.total_payable_amount, 270000)
self.assertEquals(loan_application.total_payable_interest, 18506)
self.assertEquals(loan_application.total_payable_amount, 268506)

View File

@@ -11,6 +11,7 @@ from erpnext.accounts.party import get_party_account
from erpnext.accounts.general_ledger import make_gl_entries
from erpnext.accounts.doctype.sales_invoice.sales_invoice import get_bank_cash_account
from erpnext.controllers.accounts_controller import AccountsController
from frappe.utils.csvutils import getlink
class InvalidExpenseApproverError(frappe.ValidationError): pass
@@ -146,7 +147,7 @@ class ExpenseClaim(AccountsController):
frappe.throw(_("Cost center is required to book an expense claim"))
if not self.payable_account:
frappe.throw(_("Please set default payable account in the employee {0}").format(self.employee))
frappe.throw(_("Please set default payable account for the company {0}").format(getlink("Company",self.company)))
if self.is_paid:
if not self.mode_of_payment:

View File

@@ -27,7 +27,8 @@ class JobApplicant(Document):
def validate(self):
self.check_email_id_is_unique()
validate_email_add(self.email_id, True)
if self.email_id:
validate_email_add(self.email_id, True)
if not self.applicant_name and self.email_id:
guess = self.email_id.split('@')[0]

View File

@@ -5,8 +5,10 @@ frappe.provide("erpnext.offer_letter");
frappe.ui.form.on("Offer Letter", {
select_terms: function(frm) {
frappe.model.get_value("Terms and Conditions", frm.doc.select_terms, "terms", function(value) {
frm.set_value("terms", value.terms);
erpnext.utils.get_terms(frm.doc.select_terms, frm.doc, function(r) {
if(!r.exc) {
me.frm.set_value("terms", r.message);
}
});
},

View File

@@ -112,14 +112,14 @@ class ProcessPayroll(Document):
def create_log(self, ss_list):
if not ss_list:
if not ss_list or len(ss_list) < 1:
log = "<p>" + _("No employee for the above selected criteria OR salary slip already created") + "</p>"
else:
log = frappe.render_template("templates/includes/salary_slip_log.html",
dict(ss_list=ss_list,
keys=sorted(ss_list[0].keys()),
title=_('Created Salary Slips')))
return log
return log
def get_sal_slip_list(self, ss_status, as_dict=False):
"""

View File

@@ -91,7 +91,7 @@ class SalarySlip(TransactionBase):
frappe.throw(_("Name error: {0}".format(err)))
except SyntaxError as err:
frappe.throw(_("Syntax error in formula or condition: {0}".format(err)))
except Exception, e:
except Exception as e:
frappe.throw(_("Error in formula or condition: {0}".format(e)))
raise
@@ -99,7 +99,8 @@ class SalarySlip(TransactionBase):
'''Returns data for evaluating formula'''
data = frappe._dict()
data.update(frappe.get_doc("Salary Structure Employee", {"employee": self.employee}).as_dict())
data.update(frappe.get_doc("Salary Structure Employee",
{"employee": self.employee, "parent": self.salary_structure}).as_dict())
data.update(frappe.get_doc("Employee", self.employee).as_dict())
data.update(self.as_dict())
@@ -321,15 +322,26 @@ class SalarySlip(TransactionBase):
def sum_components(self, component_type, total_field):
joining_date, relieving_date = frappe.db.get_value("Employee", self.employee,
["date_of_joining", "relieving_date"])
if not relieving_date:
relieving_date = getdate(self.end_date)
for d in self.get(component_type):
if ((cint(d.depends_on_lwp) == 1 and not self.salary_slip_based_on_timesheet) or\
getdate(self.start_date) < joining_date or getdate(self.end_date) > relieving_date):
if not joining_date:
frappe.throw(_("Please set the Date Of Joining for employee {0}").format(frappe.bold(self.employee_name)))
d.amount = rounded((flt(d.default_amount) * flt(self.payment_days)
/ cint(self.total_working_days)), self.precision("amount", component_type))
for d in self.get(component_type):
if (self.salary_structure and
cint(d.depends_on_lwp) and
(not
self.salary_slip_based_on_timesheet or
getdate(self.start_date) < joining_date or
getdate(self.end_date) > relieving_date
)):
d.amount = rounded(
(flt(d.default_amount) * flt(self.payment_days)
/ cint(self.total_working_days)), self.precision("amount", component_type)
)
elif not self.payment_days and not self.salary_slip_based_on_timesheet:
d.amount = 0
elif not d.amount:

View File

@@ -33,7 +33,8 @@ class SalaryStructure(Document):
for employee in self.get('employees'):
joining_date, relieving_date = frappe.db.get_value("Employee", employee.employee,
["date_of_joining", "relieving_date"])
if employee.from_date and getdate(employee.from_date) < joining_date:
if employee.from_date and joining_date and getdate(employee.from_date) < joining_date:
frappe.throw(_("From Date {0} for Employee {1} cannot be before employee's joining Date {2}")
.format(employee.from_date, employee.employee, joining_date))

View File

@@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 1,
"autoname": "field:event_name",
@@ -12,6 +13,7 @@
"editable_grid": 1,
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -27,7 +29,7 @@
"in_standard_filter": 0,
"label": "Event Name",
"length": 0,
"no_copy": 0,
"no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 0,
@@ -41,6 +43,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
@@ -71,6 +74,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -99,6 +103,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -129,6 +134,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -157,6 +163,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -186,6 +193,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -216,6 +224,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -244,6 +253,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -274,6 +284,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -303,6 +314,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -331,6 +343,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -361,6 +374,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -390,6 +404,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -418,6 +433,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -447,6 +463,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -476,6 +493,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -504,6 +522,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -533,6 +552,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -562,6 +582,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -592,6 +613,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
@@ -622,6 +644,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -651,17 +674,17 @@
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-02-17 16:51:35.141403",
"modified": "2017-05-29 06:13:38.411039",
"modified_by": "Administrator",
"module": "HR",
"name": "Training Event",

View File

@@ -13,7 +13,9 @@ class TrainingResult(Document):
def send_result(self):
for emp in self.employees:
message = "Thank You for attending {0}. You grade is {1}".format(self.training_event, emp.grade)
message = "Thank You for attending {0}.".format(self.training_event)
if emp.grade:
message = message + "Your grade: {0}".format(emp.grade)
frappe.sendmail(frappe.db.get_value("Employee", emp.employee, "company_email"), \
subject=_("{0} Results".format(self.training_event)), \
content=message)

View File

@@ -16,7 +16,15 @@ frappe.ui.form.on("Vehicle Log", {
}
})
}
if(frm.doc.docstatus == 1) {
frm.add_custom_button(__('Expense Claim'), function() {
frm.events.expense_claim(frm)
}, __("Make"));
frm.page.set_inner_btn_group_as_primary(__("Make"));
}
},
expense_claim: function(frm){
frappe.call({
method: "erpnext.hr.doctype.vehicle_log.vehicle_log.make_expense_claim",

View File

@@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_guest_to_view": 0,
"allow_import": 0,
"allow_rename": 0,
"autoname": "naming_series:",
@@ -12,6 +13,7 @@
"editable_grid": 1,
"fields": [
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -41,6 +43,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -71,6 +74,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -101,6 +105,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -131,6 +136,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -159,6 +165,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -187,6 +194,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -216,6 +224,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -245,6 +254,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -274,6 +284,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -303,6 +314,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -332,6 +344,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -361,6 +374,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -390,6 +404,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -419,6 +434,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -447,6 +463,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -477,6 +494,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -506,6 +524,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 1,
@@ -519,7 +538,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Service_Details",
"label": "Service Details",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -535,6 +554,7 @@
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -565,64 +585,7 @@
"unique": 0
},
{
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "section_break_20",
"fieldtype": "Section Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_on_submit": 1,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.docstatus==1",
"fieldname": "expense_claim",
"fieldtype": "Button",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Make Expense Claim",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
@@ -652,17 +615,17 @@
"unique": 0
}
],
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"idx": 0,
"image_view": 0,
"in_create": 0,
"in_dialog": 0,
"is_submittable": 1,
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2017-02-17 16:53:17.975663",
"modified": "2017-05-15 13:17:59.575317",
"modified_by": "Administrator",
"module": "HR",
"name": "Vehicle Log",

View File

@@ -25,11 +25,10 @@ def get_columns():
]
def get_employees(filters):
holiday_filter = {"holiday_date": (">=", filters.from_date),
"holiday_date": ("<=", filters.to_date)}
holiday_filter = [["holiday_date", ">=", filters.from_date], ["holiday_date", "<=", filters.to_date]]
if filters.holiday_list:
holiday_filter["parent"] = filters.holiday_list
holiday_filter.append(["parent", "=", filters.holiday_list])
holidays = frappe.get_all("Holiday", fields=["holiday_date", "description"],
filters=holiday_filter)

View File

@@ -2,7 +2,7 @@
{%= frappe.boot.letter_heads[filters.letter_head || frappe.defaults.get_default("letter_head")] %}
</div>
<h2 class="text-center">{%= __(report.report_name) %}</h2>
<h5 class="text-center">From {%= filters.date_range[0] %} to {%= filters.date_range[1] %}</h5>
<h5 class="text-center">{{ __("From") }} {%= filters.date_range[0] %} {{ __("to") }} {%= filters.date_range[1] %}</h5>
<hr>
<table class="table table-bordered">
<thead>
@@ -34,4 +34,4 @@
{% } %}
</tbody>
</table>
<p class="text-right text-muted">Printed On {%= dateutil.str_to_user(dateutil.get_datetime_as_string()) %}</p>
<p class="text-right text-muted">{{ __("Printed On") }} {%= dateutil.str_to_user(dateutil.get_datetime_as_string()) %}</p>

View File

@@ -33,10 +33,12 @@ erpnext.maintenance.MaintenanceSchedule = frappe.ui.form.Controller.extend({
erpnext.utils.map_current_doc({
method: "erpnext.selling.doctype.sales_order.sales_order.make_maintenance_schedule",
source_doctype: "Sales Order",
target: me.frm,
setters: {
customer: me.frm.doc.customer || undefined
},
get_query_filters: {
docstatus: 1,
order_type: me.frm.doc.order_type,
customer: me.frm.doc.customer || undefined,
company: me.frm.doc.company
}
});

View File

@@ -163,7 +163,7 @@ class MaintenanceSchedule(TransactionBase):
`tabMaintenance Schedule Item` msi where msi.parent=ms.name and
msi.sales_order=%s and ms.docstatus=1""", d.sales_order)
if chk:
throw(_("Maintenance Schedule {0} exists against {0}").format(chk[0][0], d.sales_order))
throw(_("Maintenance Schedule {0} exists against {1}").format(chk[0][0], d.sales_order))
def validate(self):
self.validate_maintenance_detail()

View File

@@ -27,41 +27,53 @@ erpnext.maintenance.MaintenanceVisit = frappe.ui.form.Controller.extend({
refresh: function() {
frappe.dynamic_link = {doc: this.frm.doc, fieldname: 'customer', doctype: 'Customer'}
var me = this;
if (this.frm.doc.docstatus===0) {
cur_frm.add_custom_button(__('Maintenance Schedule'),
this.frm.add_custom_button(__('Maintenance Schedule'),
function() {
erpnext.utils.map_current_doc({
method: "erpnext.maintenance.doctype.maintenance_schedule.maintenance_schedule.make_maintenance_visit",
source_doctype: "Maintenance Schedule",
target: me.frm,
setters: {
customer: me.frm.doc.customer || undefined,
},
get_query_filters: {
docstatus: 1,
customer: cur_frm.doc.customer || undefined,
company: cur_frm.doc.company
company: me.frm.doc.company
}
})
}, __("Get items from"));
cur_frm.add_custom_button(__('Warranty Claim'),
this.frm.add_custom_button(__('Warranty Claim'),
function() {
erpnext.utils.map_current_doc({
method: "erpnext.support.doctype.warranty_claim.warranty_claim.make_maintenance_visit",
source_doctype: "Warranty Claim",
target: me.frm,
date_field: "complaint_date",
setters: {
customer: me.frm.doc.customer || undefined,
},
get_query_filters: {
status: ["in", "Open, Work in Progress"],
customer: cur_frm.doc.customer || undefined,
company: cur_frm.doc.company
company: me.frm.doc.company
}
})
}, __("Get items from"));
cur_frm.add_custom_button(__('Sales Order'),
this.frm.add_custom_button(__('Sales Order'),
function() {
erpnext.utils.map_current_doc({
method: "erpnext.selling.doctype.sales_order.sales_order.make_maintenance_visit",
source_doctype: "Sales Order",
target: me.frm,
setters: {
customer: me.frm.doc.customer || undefined,
},
get_query_filters: {
docstatus: 1,
order_type: cur_frm.doc.order_type,
customer: cur_frm.doc.customer || undefined,
company: cur_frm.doc.company
company: me.frm.doc.company,
order_type: me.frm.doc.order_type,
}
})
}, __("Get items from"));

View File

@@ -33,9 +33,7 @@ frappe.ui.form.on("BOM", {
});
}
if(frm.doc.docstatus==2) {
// show duplicate button when BOM is cancelled,
// its not very intuitive
if(frm.doc.docstatus!=0) {
frm.add_custom_button(__("Duplicate"), function() {
frm.copy_doc();
});

View File

@@ -20,6 +20,7 @@ from erpnext.manufacturing.doctype.manufacturing_settings.manufacturing_settings
from erpnext.stock.stock_balance import get_planned_qty, update_bin_qty
from erpnext.manufacturing.doctype.bom.bom import get_bom_items_as_dict
from erpnext.stock.utils import get_bin
from frappe.utils.csvutils import getlink
class OverProductionError(frappe.ValidationError): pass
class StockOverProductionError(frappe.ValidationError): pass
@@ -311,7 +312,7 @@ class ProductionOrder(Document):
if timesheet and timesheet.get("time_logs"):
timesheet.save()
timesheets.append(timesheet.name)
timesheets.append(getlink("Timesheet", timesheet.name))
self.planned_end_date = self.operations[-1].planned_end_time
if timesheets:

View File

@@ -390,4 +390,12 @@ erpnext.patches.v8_0.enable_booking_asset_depreciation_automatically
erpnext.patches.v8_0.set_project_copied_from
erpnext.patches.v8_0.update_status_as_paid_for_completed_expense_claim
erpnext.patches.v7_2.stock_uom_in_selling
erpnext.patches.v8_0.revert_manufacturers_table_from_item
erpnext.patches.v8_0.revert_manufacturers_table_from_item
erpnext.patches.v8_0.disable_instructor_role
erpnext.patches.v8_0.merge_student_batch_and_student_group
erpnext.patches.v8_0.rename_total_margin_to_rate_with_margin # 11-05-2017
erpnext.patches.v8_0.fix_status_for_invoices_with_negative_outstanding
erpnext.patches.v8_0.make_payments_table_blank_for_non_pos_invoice
erpnext.patches.v8_0.delete_schools_depricated_doctypes
erpnext.patches.v8_0.update_customer_pos_id
erpnext.patches.v8_0.rename_items_in_status_field_of_material_request

View File

@@ -13,7 +13,6 @@ def execute():
# set customer, supplier roles
for c in frappe.get_all('Contact', fields=['user'], filters={'ifnull(user, "")': ('!=', '')}):
user = frappe.get_doc('User', c.user)
user.set_default_roles()
user.flags.ignore_validate = True
user.flags.ignore_mandatory = True
user.save()

View File

@@ -1,7 +1,5 @@
import frappe
def execute():
frappe.reload_doc('schools', 'doctype', 'student_batch_student')
frappe.reload_doc('schools', 'doctype', 'student_group_student')
frappe.db.sql("update `tabStudent Batch Student` set active=1")
frappe.db.sql("update `tabStudent Group Student` set active=1")

View File

@@ -3,26 +3,32 @@ from frappe.model.utils.rename_field import rename_field
def execute():
#Rename Grading Structure to Grading Scale
frappe.rename_doc("DocType", "Grading Structure", "Grading Scale", force=True)
frappe.rename_doc("DocType", "Grade Interval", "Grading Scale Interval", force=True)
if not frappe.db.exists("DocType", "Grading Scale"):
frappe.rename_doc("DocType", "Grading Structure", "Grading Scale", force=True)
if not frappe.db.exists("DocType", "Grading Scale Interval"):
frappe.rename_doc("DocType", "Grade Interval", "Grading Scale Interval", force=True)
frappe.reload_doc("schools", "doctype", "grading_scale_interval")
rename_field("Grading Scale Interval", "to_score", "threshold")
if "to_score" in frappe.db.get_table_columns("Grading Scale Interval"):
rename_field("Grading Scale Interval", "to_score", "threshold")
frappe.rename_doc("DocType", "Assessment", "Assessment Plan", force=True)
if not frappe.db.exists("DocType", "Assessment Plan"):
frappe.rename_doc("DocType", "Assessment", "Assessment Plan", force=True)
#Rename Assessment Results
frappe.reload_doc("schools", "doctype", "assessment_plan")
rename_field("Assessment Plan", "grading_structure", "grading_scale")
if "grading_structure" in frappe.db.get_table_columns("Assessment Plan"):
rename_field("Assessment Plan", "grading_structure", "grading_scale")
frappe.reload_doc("schools", "doctype", "assessment_result")
frappe.reload_doc("schools", "doctype", "assessment_result_detail")
frappe.reload_doc("schools", "doctype", "assessment_criteria")
for assessment in frappe.get_all("Assessment Plan", fields=["name", "grading_scale"], filters = [["docstatus", "!=", 2 ]]):
print assessment
for stud_result in frappe.db.sql("select * from `tabAssessment Result` where parent= %s", assessment.name, as_dict=True):
for assessment in frappe.get_all("Assessment Plan",
fields=["name", "grading_scale"], filters = [["docstatus", "!=", 2 ]]):
for stud_result in frappe.db.sql("select * from `tabAssessment Result` where parent= %s",
assessment.name, as_dict=True):
if stud_result.result:
assessment_result = frappe.new_doc("Assessment Result")
assessment_result.student = stud_result.student

View File

@@ -0,0 +1,14 @@
# Copyright (c) 2017, Web Notes Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
""" delete doctypes """
if frappe.db.exists("DocType", "Grading Structure"):
frappe.delete_doc("DocType", "Grading Structure", force=1)
if frappe.db.exists("DocType", "Grade Interval"):
frappe.delete_doc("DocType", "Grade Interval", force=1)

View File

@@ -0,0 +1,17 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
"""
disable the instructor role for companies with domain other than
Education.
"""
domains = frappe.db.sql_list("select domain from tabCompany")
if "Education" not in domains:
role = frappe.get_doc("Role", "Instructor")
role.disabled = 1
role.save(ignore_permissions=True)

View File

@@ -0,0 +1,23 @@
# Copyright (c) 2017, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
for dt, status in [["Sales Invoice", "Credit Note Issued"], ["Purchase Invoice", "Debit Note Issued"]]:
invoices = frappe.db.sql("""
select name
from `tab{0}`
where
status = %s
and outstanding_amount < 0
and docstatus=1
and is_return=0
""".format(dt), status)
for inv in invoices:
return_inv = frappe.db.sql("""select name from `tab{0}`
where is_return=1 and return_against=%s and docstatus=1""".format(dt), inv[0])
if not return_inv:
frappe.db.sql("update `tab{0}` set status='Paid' where name = %s".format(dt), inv[0])

View File

@@ -0,0 +1,15 @@
# Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
def execute():
frappe.reload_doctype('Sales Invoice')
frappe.db.sql("""
delete from
`tabSales Invoice Payment`
where
parent in (select name from `tabSales Invoice` where is_pos = 0)
""")

View File

@@ -0,0 +1,67 @@
# Copyright (c) 2017, Frappe and Contributors
# License: GNU General Public License v3. See license.txt
from __future__ import unicode_literals
import frappe
from frappe.model.utils.rename_field import *
from frappe.model.mapper import get_mapped_doc
def execute():
# for converting student batch into student group
for doctype in ["Student Group", "Student Group Student", "Student Group Instructor", "Student Attendance"]:
frappe.reload_doc("schools", "doctype", doctype)
if frappe.db.table_exists("Student Batch"):
student_batches = frappe.db.sql('''select name as student_group_name, student_batch_name as batch,
program, academic_year, academic_term from `tabStudent Batch`''', as_dict=1)
for student_batch in student_batches:
# create student batch name if does not exists !!
if student_batch.get("batch") and not frappe.db.exists("Student Batch Name", student_batch.get("batch")):
frappe.get_doc({
"doctype": "Student Batch Name",
"batch_name": student_batch.get("batch")
}).insert(ignore_permissions=True)
student_batch.update({"doctype":"Student Group", "group_based_on": "Batch"})
doc = frappe.get_doc(student_batch)
if frappe.db.sql("SHOW COLUMNS FROM `tabStudent Batch Student` LIKE 'active'"):
cond = ", active"
else:
cond = " "
student_list = frappe.db.sql('''select student, student_name {cond} from `tabStudent Batch Student`
where parent=%s'''.format(cond=cond), (doc.student_group_name), as_dict=1)
if student_list:
for i, student in enumerate(student_list):
student.update({"group_roll_number": i+1})
doc.extend("students", student_list)
instructor_list = frappe.db.sql('''select instructor, instructor_name from `tabStudent Batch Instructor`
where parent=%s''', (doc.student_group_name), as_dict=1)
if instructor_list:
doc.extend("instructors", instructor_list)
doc.save()
# delete the student batch and child-table
if frappe.db.table_exists("Student Batch"):
frappe.delete_doc("DocType", "Student Batch", force=1)
if frappe.db.table_exists("Student Batch Student"):
frappe.delete_doc("DocType", "Student Batch Student", force=1)
if frappe.db.table_exists("Student Batch Instructor"):
frappe.delete_doc("DocType", "Student Batch Instructor", force=1)
# delete the student batch creation tool
if frappe.db.table_exists("Student Batch Creation Tool"):
frappe.delete_doc("DocType", "Student Batch Creation Tool", force=1)
# delete the student batch creation tool
if frappe.db.table_exists("Attendance Tool Student"):
frappe.delete_doc("DocType", "Attendance Tool Student", force=1)
# change the student batch to student group in the student attendance
table_columns = frappe.db.get_table_columns("Student Attendance")
if "student_batch" in table_columns:
rename_field("Student Attendance", "student_batch", "student_group")

View File

@@ -0,0 +1,25 @@
from __future__ import unicode_literals
import frappe
def execute():
frappe.db.sql(
"""
UPDATE `tabMaterial Request`
SET status = CASE
WHEN docstatus = 2 THEN 'Cancelled'
WHEN docstatus = 0 THEN 'Draft'
ELSE CASE
WHEN status = 'Stopped' THEN 'Stopped'
WHEN status != 'Stopped' AND per_ordered = 0 THEN 'Pending'
WHEN per_ordered < 100 AND per_ordered > 0 AND status != 'Stopped'
THEN 'Partially Ordered'
WHEN per_ordered = 100 AND material_request_type = 'Purchase'
AND status != 'Stopped' THEN 'Ordered'
WHEN per_ordered = 100 AND material_request_type = 'Material Transfer'
AND status != 'Stopped' THEN 'Transferred'
WHEN per_ordered = 100 AND material_request_type = 'Material Issue'
AND status != 'Stopped' THEN 'Issued'
END
END
"""
)

View File

@@ -0,0 +1,24 @@
from __future__ import unicode_literals
import frappe
from frappe.model.utils.rename_field import rename_field
def execute():
"""
Rename Total Margin field to Rate With Margin in
"Sales Order Item", "Sales Invoice Item", "Delivery Note Item",
"Quotation Item"
"""
for d in ("Sales Order Item", "Sales Invoice Item",
"Delivery Note Item", "Quotation Item"):
frappe.reload_doctype(d)
rename_field_if_exists(d, "total_margin", "rate_with_margin")
def rename_field_if_exists(doctype, old_fieldname, new_fieldname):
try:
rename_field(doctype, old_fieldname, new_fieldname)
except Exception, e:
if e.args[0] != 1054:
raise

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