Compare commits

..

223 Commits

Author SHA1 Message Date
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
c9b6a07732 [fix] Remove recurring id on amendment only if it is same as amended_from 2016-11-16 18:43:44 +05:30
295 changed files with 54019 additions and 44546 deletions

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

@@ -2,7 +2,8 @@
from __future__ import unicode_literals
import frappe
__version__ = '8.0.22'
__version__ = '8.0.46'
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, currency=self.account_currency),
fmt_money(stock_balance, currency=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

@@ -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 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,7 @@ 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.customer = get_customer_id(doc)
si_doc.due_date = doc.get('posting_date')
submit_invoice(si_doc, name, doc)
name_list.append(name)
@@ -310,30 +328,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_contact(data, name)
make_address(data, name)
customer_list.append(name)
frappe.db.commit()
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')
@@ -348,6 +390,7 @@ def make_contact(args,customer):
{'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')
}
@@ -357,8 +400,8 @@ def make_contact(args,customer):
doc = frappe.get_doc('Contact', name)
doc.update(args)
doc.is_primary_contact = 1
if not name:
doc.first_name = customer
doc.append('links',{
'link_doctype': 'Customer',
'link_name': customer

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 () {
@@ -347,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: []
})
},
@@ -389,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();
@@ -693,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'].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>')
@@ -709,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
@@ -770,6 +783,32 @@ 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"] : '',
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;
},
@@ -788,12 +827,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',
@@ -856,10 +895,15 @@ 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()
@@ -872,12 +916,19 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
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() {
@@ -887,6 +938,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);
@@ -908,12 +960,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)
},
@@ -927,26 +980,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) {
@@ -1165,6 +1198,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)
}
@@ -1373,7 +1407,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);
@@ -1593,8 +1626,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: {
@@ -1604,12 +1640,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

@@ -3,6 +3,7 @@
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)
@@ -277,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

@@ -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

@@ -64,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))

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

@@ -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

@@ -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,6 +33,7 @@ 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 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

@@ -391,4 +391,11 @@ 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.disable_instructor_role
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

@@ -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,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

View File

@@ -0,0 +1,9 @@
# 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("Customer")
frappe.db.sql(""" update `tabCustomer` set customer_pos_id = name """)

View File

@@ -0,0 +1,38 @@
# 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():
if frappe.db.table_exists("Student Batch"):
student_batches = frappe.db.sql('''select name from `tabStudent Batch`''', as_dict=1)
for student_batch in student_batches:
if frappe.db.exists("Student Group", student_batch.get("name")):
student_group = frappe.get_doc("Student Group", student_batch.get("name"))
if frappe.db.table_exists("Student Batch Student"):
current_student_list = frappe.db.sql_list('''select student from `tabStudent Group Student`
where parent=%s''', (student_group.name))
batch_student_list = frappe.db.sql_list('''select student from `tabStudent Batch Student`
where parent=%s''', (student_group.name))
student_list = list(set(batch_student_list)-set(current_student_list))
if student_list:
student_group.extend("students", [{"student":d} for d in student_list])
if frappe.db.table_exists("Student Batch Instructor"):
current_instructor_list = frappe.db.sql_list('''select instructor from `tabStudent Group Instructor`
where parent=%s''', (student_group.name))
batch_instructor_list = frappe.db.sql_list('''select instructor from `tabStudent Batch Instructor`
where parent=%s''', (student_group.name))
instructor_list = list(set(batch_instructor_list)-set(current_instructor_list))
if instructor_list:
student_group.extend("instructors", [{"instructor":d} for d in instructor_list])
student_group.save()

View File

@@ -4,14 +4,17 @@ def set_default_role(doc, method):
'''Set customer, supplier, student based on email'''
if frappe.flags.setting_role or frappe.flags.in_migrate:
return
roles = frappe.get_roles(doc.name)
contact_name = frappe.get_value('Contact', dict(email_id=doc.email))
if contact_name:
contact = frappe.get_doc('Contact', contact_name)
for link in contact.links:
frappe.flags.setting_role = True
if link.link_doctype=='Customer':
if link.link_doctype=='Customer' and 'Customer' not in roles:
doc.add_roles('Customer')
elif link.link_doctype=='Supplier':
elif link.link_doctype=='Supplier' and 'Supplier' not in roles:
doc.add_roles('Supplier')
elif frappe.get_value('Student', dict(student_email_id=doc.email)):
elif frappe.get_value('Student', dict(student_email_id=doc.email)) and 'Student' not in roles:
doc.add_roles('Student')

View File

@@ -216,9 +216,19 @@ class Project(Document):
# duplicated project
dependency_map = {}
for task in self.tasks:
name, depends_on_tasks = frappe.db.get_value(
'Task', { "subject": task.title, "project": self.copied_from }, ['name', 'depends_on_tasks']
_task = frappe.db.get_value(
'Task',
{"subject": task.title, "project": self.copied_from},
['name', 'depends_on_tasks'],
as_dict=True
)
if _task is None:
continue
name = _task.name
depends_on_tasks = _task.depends_on_tasks
depends_on_tasks = [x for x in depends_on_tasks.split(',') if x]
dependency_map[task.title] = [ x['subject'] for x in frappe.get_list(
'Task Depends On', {"parent": name}, ['subject'])]

View File

@@ -368,7 +368,8 @@ def get_events(start, end, filters=None):
conditions = get_conditions(filters)
return frappe.db.sql("""select `tabTimesheet Detail`.name as name,
`tabTimesheet Detail`.docstatus as status, `tabTimesheet Detail`.parent as parent,
from_time as start_date, hours, activity_type, project, to_time as end_date,
from_time as start_date, hours, activity_type,
`tabTimesheet Detail`.project, to_time as end_date,
CONCAT(`tabTimesheet Detail`.parent, ' (', ROUND(hours,2),' hrs)') as title
from `tabTimesheet Detail`, `tabTimesheet`
where `tabTimesheet Detail`.parent = `tabTimesheet`.name
@@ -382,9 +383,13 @@ def get_events(start, end, filters=None):
def get_conditions(filters):
conditions = []
abbr = {'employee': 'tabTimesheet', 'project': 'tabTimesheet Detail'}
for key in filters:
if filters.get(key):
conditions.append("`%s`.%s = '%s'"%(abbr.get(key), key, filters.get(key)))
if frappe.get_meta("Timesheet").has_field(key):
dt = 'tabTimesheet'
elif frappe.get_meta("Timesheet Detail").has_field(key):
dt = 'tabTimesheet Detail'
conditions.append("`%s`.%s = '%s'"%(dt, key, filters.get(key)))
return " and {}".format(" and ".join(conditions)) if conditions else ""

View File

@@ -270,6 +270,11 @@ body[data-route="pos"] .item-cart-items {
border: 1px solid #d1d8dd;
border-top: none;
}
@media (max-width: 767px) {
body[data-route="pos"] .item-cart-items {
height: 30vh;
}
}
body[data-route="pos"] .no-items-message {
min-height: 200px;
display: flex;

View File

@@ -5,17 +5,17 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
setup: function() {},
apply_pricing_rule_on_item: function(item){
if(item.margin_type == "Percentage"){
item.total_margin = flt(item.price_list_rate)
item.rate_with_margin = flt(item.price_list_rate)
+ flt(item.price_list_rate) * ( flt(item.margin_rate_or_amount) / 100);
} else {
item.total_margin = flt(item.price_list_rate) + flt(item.margin_rate_or_amount);
item.rate_with_margin = flt(item.price_list_rate) + flt(item.margin_rate_or_amount);
}
item.rate = flt(item.total_margin , precision("rate", item));
item.rate = flt(item.rate_with_margin , precision("rate", item));
if(item.discount_percentage){
discount_value = flt(item.total_margin) * flt(item.discount_percentage) / 100;
item.rate = flt((item.total_margin) - (discount_value), precision('rate', item));
discount_value = flt(item.rate_with_margin) * flt(item.discount_percentage) / 100;
item.rate = flt((item.rate_with_margin) - (discount_value), precision('rate', item));
}
},
@@ -594,6 +594,8 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
paid_amount += data.amount;
base_paid_amount += data.base_amount;
})
} else if(!this.frm.doc.is_return){
this.frm.doc.payments = [];
}
this.frm.doc.paid_amount = flt(paid_amount, precision("paid_amount"));
@@ -602,11 +604,18 @@ erpnext.taxes_and_totals = erpnext.payments.extend({
calculate_change_amount: function(){
this.frm.doc.change_amount = 0.0;
if(this.frm.doc.paid_amount > this.frm.doc.grand_total && !this.frm.doc.is_return){
this.frm.doc.change_amount = flt(this.frm.doc.paid_amount - this.frm.doc.grand_total +
this.frm.doc.write_off_amount, precision("change_amount"));
this.frm.doc.base_change_amount = flt(this.frm.doc.base_paid_amount - this.frm.doc.base_grand_total +
this.frm.doc.base_write_off_amount, precision("base_change_amount"));
this.frm.doc.base_change_amount = 0.0;
if(this.frm.doc.paid_amount > this.frm.doc.grand_total && !this.frm.doc.is_return) {
var payment_types = $.map(this.frm.doc.payments, function(d) { return d.type });
if (in_list(payment_types, 'Cash')) {
this.frm.doc.change_amount = flt(this.frm.doc.paid_amount - this.frm.doc.grand_total +
this.frm.doc.write_off_amount, precision("change_amount"));
this.frm.doc.base_change_amount = flt(this.frm.doc.base_paid_amount -
this.frm.doc.base_grand_total + this.frm.doc.base_write_off_amount,
precision("base_change_amount"));
}
}
},

View File

@@ -6,12 +6,31 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
this._super();
frappe.ui.form.on(this.frm.doctype + " Item", "rate", function(frm, cdt, cdn) {
var item = frappe.get_doc(cdt, cdn);
var has_margin_field = frappe.meta.has_field(cdt, 'margin_type');
frappe.model.round_floats_in(item, ["rate", "price_list_rate"]);
if(item.price_list_rate) {
item.discount_percentage = flt((1 - item.rate / item.price_list_rate) * 100.0, precision("discount_percentage", item));
if(item.rate > item.price_list_rate && has_margin_field) {
// if rate is greater than price_list_rate, set margin
// or set discount
item.discount_percentage = 0;
item.margin_type = 'Amount';
item.margin_rate_or_amount = flt(item.rate - item.price_list_rate,
precision("margin_rate_or_amount", item));
item.rate_with_margin = item.rate;
} else {
item.discount_percentage = flt((1 - item.rate / item.price_list_rate) * 100.0,
precision("discount_percentage", item));
item.margin_type = '';
item.margin_rate_or_amount = 0;
item.rate_with_margin = 0;
}
} else {
item.discount_percentage = 0.0;
item.margin_type = '';
item.margin_rate_or_amount = 0;
item.rate_with_margin = 0;
}
cur_frm.cscript.set_gross_profit(item);
@@ -79,6 +98,13 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
frm.cscript.calculate_taxes_and_totals();
});
var me = this;
if(this.frm.fields_dict["items"].grid.get_field('batch_no')) {
this.frm.set_query("batch_no", "items", function(doc, cdt, cdn) {
return me.set_query_for_batch(doc, cdt, cdn)
});
}
},
onload: function() {
var me = this;
@@ -500,6 +526,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
},
get_exchange_rate: function(transaction_date, from_currency, to_currency, callback) {
if (!transaction_date || !from_currency || !to_currency) return;
return frappe.call({
method: "erpnext.setup.utils.get_exchange_rate",
args: {
@@ -563,14 +590,14 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
}
},
conversion_factor: function(doc, cdt, cdn) {
conversion_factor: function(doc, cdt, cdn, dont_fetch_price_list_rate) {
if(frappe.meta.get_docfield(cdt, "stock_qty", cdn)) {
var item = frappe.get_doc(cdt, cdn);
frappe.model.round_floats_in(item, ["qty", "conversion_factor"]);
item.stock_qty = flt(item.qty * item.conversion_factor, precision("stock_qty", item));
refresh_field("stock_qty", item.name, item.parentfield);
this.toggle_conversion_factor(item);
this.apply_price_list(item, true);
if(!dont_fetch_price_list_rate) this.apply_price_list(item, true);
}
},
@@ -581,7 +608,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
},
qty: function(doc, cdt, cdn) {
this.conversion_factor(doc, cdt, cdn);
this.conversion_factor(doc, cdt, cdn, true);
this.apply_pricing_rule(frappe.get_doc(cdt, cdn), true);
},
set_dynamic_labels: function() {
@@ -762,7 +790,6 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
if(calculate_taxes_and_totals) me.calculate_taxes_and_totals();
return;
}
return this.frm.call({
method: "erpnext.accounts.doctype.pricing_rule.pricing_rule.apply_pricing_rule",
args: { args: args },
@@ -959,7 +986,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
var valid = true;
$.each(["company", "customer"], function(i, fieldname) {
if(frappe.meta.has_field(me.frm.doc.doctype, fieldname && me.frm.doc.doctype != "Purchase Order")) {
if(frappe.meta.has_field(me.frm.doc.doctype, fieldname) && me.frm.doc.doctype != "Purchase Order") {
if (!me.frm.doc[fieldname]) {
msgprint(__("Please specify") + ": " +
frappe.meta.get_label(me.frm.doc.doctype, fieldname, me.frm.doc.name) +
@@ -973,20 +1000,12 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
get_terms: function() {
var me = this;
if(this.frm.doc.tc_name) {
return frappe.call({
method: 'erpnext.setup.doctype.terms_and_conditions.terms_and_conditions.get_terms_and_conditions',
args: {
template_name: this.frm.doc.tc_name,
doc: this.frm.doc
},
callback: function(r) {
if(!r.exc) {
me.frm.set_value("terms", r.message);
}
}
});
}
erpnext.utils.get_terms(this.frm.doc.tc_name, this.frm.doc, function(r) {
if(!r.exc) {
me.frm.set_value("terms", r.message);
}
});
},
taxes_and_charges: function() {
@@ -1117,4 +1136,32 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
return method
},
});
set_query_for_batch: function(doc, cdt, cdn) {
// Show item's batches in the dropdown of batch no
var me = this;
var item = frappe.get_doc(cdt, cdn);
if(!item.item_code) {
frappe.throw(__("Please enter Item Code to get batch no"));
} else if (doc.doctype == "Purchase Receipt" ||
(doc.doctype == "Purchase Invoice" && doc.update_stock)) {
return {
filters: {'item': item.item_code}
}
} else {
filters = {
'item_code': item.item_code,
'posting_date': me.frm.doc.posting_date || frappe.datetime.nowdate(),
}
if(item.warehouse) filters["warehouse"] = item.warehouse
return {
query : "erpnext.controllers.queries.get_batch_no",
filters: filters
}
}
},
});

View File

@@ -1,20 +1,20 @@
<div class="pos_payment row">
<div class="row" style="padding: 0px 30px;">
<h3>Total Amount: <span class="label label-default" style="font-size:20px;padding:5px">{%= format_currency(grand_total, currency) %}</span></h3>
<h3>{{ __("Total Amount") }}: <span class="label label-default" style="font-size:20px;padding:5px">{%= format_currency(grand_total, currency) %}</span></h3>
</div>
<div class="row amount-row">
<div class="col-xs-6 col-sm-3 text-center">
<p class="amount-label"> Paid <h3 class="paid_amount">{%= format_currency(paid_amount, currency) %}</h3></p>
<p class="amount-label"> {{ __("Paid") }} <h3 class="paid_amount">{%= format_currency(paid_amount, currency) %}</h3></p>
</div>
<div class="col-xs-6 col-sm-3 text-center">
<p class="amount-label"> Outstanding <h3 class="outstanding_amount">{%= format_currency(outstanding_amount, currency) %} </h3></p>
<p class="amount-label"> {{ __("Outstanding") }} <h3 class="outstanding_amount">{%= format_currency(outstanding_amount, currency) %} </h3></p>
</div>
<div class="col-xs-6 col-sm-3 text-center">
<p class="amount-label"> Change <input class="form-control text-right change_amount bold" type="text" idx="change_amount" value="{{format_number(change_amount, null, 2)}}">
<p class="amount-label"> {{ __("Change") }} <input class="form-control text-right change_amount bold" type="text" idx="change_amount" value="{{format_number(change_amount, null, 2)}}">
</p>
</div>
<div class="col-xs-6 col-sm-3 text-center">
<p class="amount-label"> Write off <input class="form-control text-right write_off_amount bold" type="text" idx="write_off_amount" value="{{format_number(write_off_amount, null, 2)}}">
<p class="amount-label"> {{ __("Write off") }} <input class="form-control text-right write_off_amount bold" type="text" idx="write_off_amount" value="{{format_number(write_off_amount, null, 2)}}">
</p>
</div>
</div>
@@ -33,7 +33,7 @@
</div>
{% } %}
<div class="row">
<button type="button" class="btn btn-default delete-btn">Del</button>
<button type="button" class="btn btn-default delete-btn">{{ __("Del") }}</button>
<button type="button" class="btn btn-default pos-keyboard-key">0</button>
<button type="button" class="btn btn-default pos-keyboard-key">.</button>
</div>

View File

@@ -17,7 +17,7 @@
<div class="no-items-message text-extra-muted">
<span class="text-center">
<i class="fa fa-2x fa-shopping-cart"></i>
<p>Tap items to add them here</p>
<p>{{ __("Tap items to add them here") }}</p>
</span>
</div>
<div class="items">
@@ -73,14 +73,14 @@
{% for(var j=i*3; j
<(i+1)*3; j++) { %} <button type="button" class="btn btn-default numeric-keypad" val="{{j+1}}">{{j+1}}</button>
{% } %}
<button type="button" id="pos-item-{{ chartData[i].toLowerCase() }}" class="btn text-center btn-default numeric-keypad pos-operation">{{ chartData[i] }}</button>
<button type="button" {% if(!allow_user_to_edit_rate && chartData[i] == __("Price")) { %} disabled {% } %} id="pos-item-{{ chartData[i].toLowerCase() }}" class="btn text-center btn-default numeric-keypad pos-operation">{{ __(chartData[i]) }}</button>
</div>
{% } %}
<div class="row text-right">
<button type="button" class="btn btn-default numeric-keypad numeric-del">Del</button>
<button type="button" class="btn btn-default numeric-keypad numeric-del">{{ __("Del") }}</button>
<button type="button" class="btn btn-default numeric-keypad" val="0">0</button>
<button type="button" class="btn btn-default numeric-keypad" val=".">.</button>
<button type="button" class="btn btn-primary numeric-keypad pos-pay">Pay</button>
<button type="button" class="btn btn-primary numeric-keypad pos-pay">{{ __("Pay") }}</button>
</div>
</div>
</div>
@@ -97,7 +97,7 @@
<div class="no-items-message text-extra-muted">
<span class="text-center">
<i class="fa fa-2x fa-user"></i>
<p>No Customers yet!</p>
<p>{{ __("No Customers yet!") }}</p>
</span>
</div>
</div>
@@ -112,7 +112,7 @@
<div class="pos-list-row pos-bill-header text-muted h6">
<div class="cell subject search-item-group">
<div class="dropdown">
<a class="text-muted dropdown-toggle" data-toggle="dropdown"><span class="dropdown-text">All Item Groups</span><i class="caret"></i></a>
<a class="text-muted dropdown-toggle" data-toggle="dropdown"><span class="dropdown-text">{{ __("All Item Groups") }}</span><i class="caret"></i></a>
<ul class="dropdown-menu">
</ul>
</div>
@@ -120,8 +120,8 @@
<div class="cell search-item"></div>
</div>
<div class="app-listing item-list image-view-container">
</div>
</div>
</div>
</div>
</div>

View File

@@ -1,7 +1,7 @@
<div class="pos-item-wrapper image-view-item" data-item-code="{{item_code}}">
<div class="image-view-header doclist-row">
<div class="list-value">
<a class="grey list-id" data-name="{{item_code}}" title="{{ item_name || item_code}}">{{item_name || item_code}}<br>({{item_stock}})</a>
<a class="grey list-id" data-name="{{item_code}}" title="{{ item_name || item_code}}">{{item_name || item_code}}<br>({{ __(item_stock) }})</a>
</div>
</div>
<div class="image-view-body">

View File

@@ -1,22 +1,22 @@
<div class="pos-selected-item-action" data-item-code="{%= item_code %}">
<div class="pos-list-row">
<div class="cell">Quantity:</div>
<div class="cell">{{ __("Quantity") }}:</div>
<input type="tel" class="form-control cell pos-item-qty" value="{%= qty %}"/>
</div>
<div class="pos-list-row">
<div class="cell">Price List Rate:</div>
<div class="cell">{{ __("Price List Rate") }}:</div>
<input type="tel" class="form-control cell" disabled value="{%= price_list_rate %}"/>
</div>
<div class="pos-list-row">
<div class="cell">Discount:</div>
<div class="cell">{{ __("Discount") }}:</div>
<input type="tel" class="form-control cell pos-item-disc" value="{%= discount_percentage %}">
</div>
<div class="pos-list-row">
<div class="cell">Price:</div>
<input type="tel" class="form-control cell pos-item-price" value="{%= rate %}"/>
<div class="cell">{{ __("Price") }}:</div>
<input type="tel" class="form-control cell pos-item-price" {% if !allow_user_to_edit_rate %} disabled {% endif %} value="{%= rate %}"/>
</div>
<div class="pos-list-row">
<div class="cell">Amount:</div>
<div class="cell">{{ __("Amount") }}:</div>
<input type="tel" class="form-control cell pos-amount" value="{%= amount %}"/>
</div>
</div>

View File

@@ -3,7 +3,7 @@
<label>
<input
type="checkbox"
data-idx="{{idx}}"
data-group_roll_number="{{group_roll_number}}"
data-student="{{student}}"
data-student-name="{{student_name}}"
class="students-check"
@@ -11,7 +11,7 @@
checked
{% endif %}
>
{{ idx }} - {{ student_name }}
{{ group_roll_number }} - {{ student_name }}
</label>
</div>
</div>

View File

@@ -104,6 +104,21 @@ $.extend(erpnext.utils, {
}
}
refresh_field(table_fieldname);
},
get_terms: function(tc_name, doc, callback) {
if(tc_name) {
return frappe.call({
method: 'erpnext.setup.doctype.terms_and_conditions.terms_and_conditions.get_terms_and_conditions',
args: {
template_name: tc_name,
doc: doc
},
callback: function(r) {
callback(r)
}
});
}
}
});
@@ -114,63 +129,68 @@ erpnext.utils.map_current_doc = function(opts) {
}
}
var _map = function() {
// remove first item row if empty
if($.isArray(cur_frm.doc.items) && cur_frm.doc.items.length > 0) {
// remove first item row if empty
if(!cur_frm.doc.items[0].item_code) {
cur_frm.doc.items = cur_frm.doc.items.splice(1);
}
// find the doctype of the items table
var items_doctype = frappe.meta.get_docfield(cur_frm.doctype, 'items').options;
// find the link fieldname from items table for the given
// source_doctype
var link_fieldname = null;
frappe.get_meta(items_doctype).fields.forEach(function(d) {
frappe.get_meta(items_doctype).fields.forEach(function(d) {
if(d.options===opts.source_doctype) link_fieldname = d.fieldname; });
// search in existing items if the source_name is already set and full qty fetched
var already_set = false;
var item_qty_map = {};
$.each(cur_frm.doc.items, function(i, d) {
if(d[link_fieldname]==opts.source_name) {
already_set = true;
if (item_qty_map[d.item_code])
item_qty_map[d.item_code] += flt(d.qty);
else
item_qty_map[d.item_code] = flt(d.qty);
}
});
if(already_set) {
frappe.model.with_doc(opts.source_doctype, opts.source_name, function(r) {
var source_doc = frappe.model.get_doc(opts.source_doctype, opts.source_name);
$.each(source_doc.items || [], function(i, row) {
if(row.qty > flt(item_qty_map[row.item_code])) {
already_set = false;
return false;
}
})
})
if(already_set) {
frappe.msgprint(__("You have already selected items from {0} {1}",
[opts.source_doctype, opts.source_name]));
return;
}
$.each(cur_frm.doc.items, function(i, d) {
opts.source_name.forEach(function(src) {
if(d[link_fieldname]==src) {
already_set = true;
if (item_qty_map[d.item_code])
item_qty_map[d.item_code] += flt(d.qty);
else
item_qty_map[d.item_code] = flt(d.qty);
}
});
});
if(already_set) {
opts.source_name.forEach(function(src) {
frappe.model.with_doc(opts.source_doctype, src, function(r) {
var source_doc = frappe.model.get_doc(opts.source_doctype, src);
$.each(source_doc.items || [], function(i, row) {
if(row.qty > flt(item_qty_map[row.item_code])) {
already_set = false;
return false;
}
})
})
if(already_set) {
frappe.msgprint(__("You have already selected items from {0} {1}",
[opts.source_doctype, src]));
return;
}
})
}
}
return frappe.call({
// Sometimes we hit the limit for URL length of a GET request
// as we send the full target_doc. Hence this is a POST request.
type: "POST",
method: opts.method,
method: 'frappe.model.mapper.map_docs',
args: {
"source_name": opts.source_name,
"target_doc": cur_frm.doc
"method": opts.method,
"source_names": opts.source_name,
"target_doc": cur_frm.doc,
},
callback: function(r) {
if(!r.exc) {
@@ -181,29 +201,26 @@ erpnext.utils.map_current_doc = function(opts) {
});
}
if(opts.source_doctype) {
var d = new frappe.ui.Dialog({
title: __("Get From ") + __(opts.source_doctype),
fields: [
{
fieldtype: "Link",
label: __(opts.source_doctype),
fieldname: opts.source_doctype,
options: opts.source_doctype,
get_query: opts.get_query,
reqd:1
},
]
var d = new frappe.ui.form.MultiSelectDialog({
doctype: opts.source_doctype,
target: opts.target,
date_field: opts.date_field || undefined,
setters: opts.setters,
get_query: opts.get_query,
action: function(selections, args) {
let values = selections;
if(values.length === 0){
frappe.msgprint(__("Please select Quotations"))
return;
}
opts.source_name = values;
opts.setters = args;
d.dialog.hide();
_map();
},
});
d.set_primary_action(__('Get Items'), function() {
var values = d.get_values();
if(!values)
return;
opts.source_name = values[opts.source_doctype];
d.hide();
_map();
})
d.show();
} else if(opts.source_name) {
opts.source_name = [opts.source_name];
_map();
}
}

View File

@@ -321,6 +321,10 @@ body[data-route="pos"] {
overflow: auto;
border: 1px solid @border-color;
border-top: none;
@media (max-width: @screen-xs) {
height: 30vh;
}
}
.no-items-message {

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