Compare commits

..

300 Commits

Author SHA1 Message Date
Nabin Hait
d77cd79262 Merge branch 'hotfix' 2019-06-10 19:16:11 +05:30
Nabin Hait
6e97f7fbd8 bumped to version 11.1.37 2019-06-10 19:36:10 +05:50
Deepesh Garg
832884a7ea fix: Total Amount fix in journal entry (#17879) 2019-06-10 17:43:08 +05:30
sahil28297
4bc86c7e9d fix(patch): escape illegal characters to avoid SQL syntax error (#17889) 2019-06-10 17:38:42 +05:30
rohitwaghchaure
cd2938e2d0 fix: accounts receivable for PDC not showing 120 days column (#17892) 2019-06-10 17:38:17 +05:30
rohitwaghchaure
f37a09d9fe Merge pull request #17618 from rohitwaghchaure/user_cogs_only_for_material_issue
feat: set COGS for material issue only
2019-06-10 16:56:34 +05:30
Rohit Waghchaure
978bc1352e stock_entry: set COGS for material issue only 2019-06-07 13:34:59 +05:30
rohitwaghchaure
ab669ad055 Merge pull request #17875 from rohitwaghchaure/salary_slip_update_component_row_error
fix: Not able to save salary slip
2019-06-07 12:58:07 +05:30
Deepesh Garg
443f9ed5f1 Merge pull request #17873 from sagarvora/ewb_fix
fix(regional): remove "Generate" from button text as button now under "Make" heading
2019-06-07 12:43:23 +05:30
Rohit Waghchaure
d72f4d7959 fix: Not able to save salary slip 2019-06-07 11:44:51 +05:30
Sagar Vora
4c23c2ddec fix (regional): remove "Generate" from button text as button now under "Make" heading 2019-06-07 10:37:26 +05:30
Prasann Shah
e0333c0756 [Add] GSTIN filter in HSN summary report (#17836)
* [Add] GSTIN filter in HSN summary report

* [Add] Same filter in GST Itemised Sales Register
2019-06-06 11:22:06 +05:30
Saurabh
066ae71581 Merge pull request #17863 from adityahase/fix-error-report-email-hotfix
fix(error-report): Do not send error reports to support@erpnext.com
2019-06-06 10:36:12 +05:30
Aditya Hase
2898c94f08 fix(error-report): Do not send error reports to support@erpnext.com 2019-06-05 23:31:13 +05:30
Nabin Hait
fc0df994b8 fix: Removed unwanted total row from financial statement and general ledger print (#17859) 2019-06-05 20:29:15 +05:30
Deepesh Garg
c5a4e423c6 fix: NoneType fixes in Lab test (#17856) 2019-06-05 20:23:41 +05:30
Sahil Khan
949f170b48 Merge branch 'hotfix' 2019-06-05 15:15:01 +05:30
Sahil Khan
576e68eef0 bumped to version 11.1.36 2019-06-05 15:35:01 +05:50
Deepesh Garg
63af463a9a fix: Patch for renaming enquiry_from to opportunity_from (#17785)
* fix: Patch for renaming enquiry_from to opportunity_from

* fix: Patch fix

* fix: patch fix

* fix: Use has_column instead of exists

* fix: Handled ifnull
2019-06-05 14:22:59 +05:30
Rohan
b20eda9996 fix(projects): Remove hardcode of expected end date for new Tasks (#17716) 2019-06-05 14:16:57 +05:30
Nabin Hait
88832daae4 feat: Rounding based on settings on salary component (#17851) 2019-06-05 13:20:18 +05:30
rohitwaghchaure
3aa2df29f5 Merge pull request #17850 from deepeshgarg007/italy-xml
fix: Update currency in Italy e-invoice XML
2019-06-05 11:42:37 +05:30
deepeshgarg007
42d0ee7431 fix: Update Italy e-invoice XML 2019-06-05 11:12:32 +05:30
rohitwaghchaure
15e128f321 Merge pull request #17838 from rohitwaghchaure/fix_payment_period_based_on_invoice_date_error
fix: report Payment Period Based On Invoice Date not working
2019-06-03 20:12:01 +05:30
Rohit Waghchaure
56757866f3 fix: report Payment Period Based On Invoice Date not working 2019-06-03 18:20:46 +05:30
Nabin Hait
c3f3c156b1 Merge pull request #17834 from deepeshgarg007/serial-no-fix
fix: Serial no filtering issue for delivered items
2019-06-03 14:25:17 +05:30
Nabin Hait
62e8955b39 Merge pull request #17806 from nabinhait/discount-rounding-adjustment
fix: Rounding adjustment while additional discount amount is applied on grand total
2019-06-03 13:08:57 +05:30
deepeshgarg007
fad3789f64 fix: Pass delivery document no as empty string 2019-06-03 13:04:57 +05:30
deepeshgarg007
a0b85d060b fix: Serial no filtering issue for delivered items 2019-06-03 11:57:00 +05:30
Saurabh
fd5d3f551b Merge pull request #17808 from Alchez/hotfix-sales-analytics-total
fix(selling): Remove Totals row for Sales Analytics
2019-05-31 14:28:10 +05:30
Saurabh
019014070d Merge pull request #17810 from nabinhait/pr-expense-account
fix: Added Expense Account field to push value from PO-PR-PI
2019-05-31 14:18:33 +05:30
Saurabh
97f5dff656 Merge pull request #17812 from rohitwaghchaure/allow_import_for_packing_slip
fix: allow import for packing slip
2019-05-31 14:15:16 +05:30
Deepesh Garg
0d28802485 Merge pull request #17815 from deepeshgarg007/ar_translation
fix: Add translations for missing labels in AR report
2019-05-31 12:37:18 +05:30
Saurabh
254774f7a3 Merge pull request #17817 from rohitwaghchaure/fixed_pos_cash_payment_modal
fix: POS cash mode of payment css
2019-05-31 12:28:53 +05:30
Rohit Waghchaure
6f1e624465 fix: POS cash mode of payment css 2019-05-30 23:19:55 +05:30
deepeshgarg007
2dcd03d17d fix: Label translations 2019-05-30 22:33:02 +05:30
deepeshgarg007
ffcb7aa6ed fix: Add translations for missing labels in AR report 2019-05-30 22:26:52 +05:30
Rohit Waghchaure
9350b5a533 fix: allow import for packing slip 2019-05-30 21:52:13 +05:30
Nabin Hait
8cb2c7a89c fix: Set query for expense account in PO and PR 2019-05-30 18:13:11 +05:30
Nabin Hait
50bdaa6d1a fix: Added Expense Account field to push value from PO-PR-PI 2019-05-30 18:08:22 +05:30
Rohan Bansal
20a4cf1290 fix(selling): Remove Totals row for tree-based Sales Analytics 2019-05-30 17:24:19 +05:30
Suraj Shetty
fc092d0df4 Merge pull request #17807 from deepeshgarg007/gstr-1-condition-fix
fix: GSTR-1 report B2B condition fix
2019-05-30 17:24:04 +05:30
Rushabh Mehta
51bb2909c3 Merge pull request #17790 from deepeshgarg007/customer_lead_dashboard
fix: Customer and Lead dashboard fixes for opportunity and quotation
2019-05-30 17:21:25 +05:30
Suraj Shetty
cab0cd55b3 Merge branch 'hotfix' into gstr-1-condition-fix 2019-05-30 16:38:45 +05:30
deepeshgarg007
4f579639c6 fix: Use AND instead of OR 2019-05-30 16:26:38 +05:30
Nabin Hait
c5b68ef386 fix: Rounding adjustment while additional discount amount is aplied on grand total 2019-05-30 16:05:03 +05:30
deepeshgarg007
95dc4e4dc5 fix: Use make methods to override 2019-05-30 15:28:15 +05:30
Sahil Khan
dc35138356 Merge branch 'hotfix' 2019-05-30 12:09:35 +05:30
Sahil Khan
08617f7700 bumped to version 11.1.35 2019-05-30 12:29:35 +05:50
Anurag Mishra
cfecf3c5b1 fix: Added description if item variants does not have description in map (#17795)
* fix: variant item description based on attribute

* fix: requested changes

* fix: refactor
2019-05-29 17:16:48 +05:30
Shivam Mishra
831100b657 Merge pull request #17792 from adityahase/remove-manifest-hotfix
perf: Remove MANIFEST.in
2019-05-29 15:31:26 +05:30
Aditya Hase
f296a2f58a perf: Remove MANIFEST.in
Faster pip install -e erpnext

https://stackoverflow.com/questions/24727709/do-python-projects-need-a-manifest-in-and-what-should-be-in-it

https://github.com/frappe/frappe/pull/7360
2019-05-29 15:15:05 +05:30
deepeshgarg007
18a0120fc1 fix: Customer and Lead dashboard fixes for opportunity and quotation 2019-05-29 14:20:24 +05:30
Nabin Hait
aae557e8a1 Merge pull request #17779 from sunnyakaxd/patch-1
fix (performance): Dont fetch all orders to update a few
2019-05-29 10:04:37 +05:30
Nabin Hait
0f92c4b85b Merge pull request #17778 from deepeshgarg007/lead_owner
fix: Lead owner efficiency report query and column fixes
2019-05-29 09:57:08 +05:30
deepeshgarg007
217df1761d fix: Styling fixes 2019-05-29 08:10:07 +05:30
Himanshu Mishra
d355741462 Python 2 compatibility -_- 2019-05-28 23:54:03 +05:30
Himanshu Mishra
7b6ed4f16e Update status_updater.py 2019-05-28 23:51:22 +05:30
deepeshgarg007
c5a682e9d9 fix: Lead owner efficiency report query and column fixes 2019-05-28 23:40:05 +05:30
Sahil Khan
b4fa105555 Merge branch 'hotfix' 2019-05-28 14:50:05 +05:30
Sahil Khan
e1aef6f0db bumped to version 11.1.34 2019-05-28 15:10:05 +05:50
Nabin Hait
6f35d2b52d Merge pull request #17764 from nabinhait/salary-slip-tax-calc
fix: Tax calculation on salary slip
2019-05-28 11:16:40 +05:30
rohitwaghchaure
197f57c050 Merge pull request #17766 from rohitwaghchaure/renamed_delayed_item_report
fix: rename delayed order item report
2019-05-27 19:33:41 +05:30
Rohit Waghchaure
c74fa7546b fix: rename delayed order item report 2019-05-27 19:32:43 +05:30
Nabin Hait
233ef8752d Merge pull request #17550 from dimanex/bugfix/17547-Pricing-Rules-are-broken
Pricing Rules are not queried correctly after removing 'customer' and adding 'party_name' in Quotation #17547
2019-05-27 19:24:24 +05:30
Nabin Hait
afd9e7546f Merge branch 'hotfix' into bugfix/17547-Pricing-Rules-are-broken 2019-05-27 19:22:34 +05:30
Nabin Hait
8b1532d691 Merge pull request #17758 from saurabh6790/custom_field_fix_hotfix
fix: add shopify_supplier_id custom field while setting up shopify
2019-05-27 19:12:16 +05:30
Nabin Hait
a887c29e06 Merge pull request #17760 from nabinhait/multi-fixes99
fix: Error due to merging of lead/customer to party_name
2019-05-27 19:11:01 +05:30
Nabin Hait
7114b72be8 fix: Tax calculation on salary slip 2019-05-27 18:42:20 +05:30
Nabin Hait
92ab345daf Merge pull request #17018 from chdecultot/fec_improvement
fix: Addition of third party types in FEC
2019-05-27 17:18:42 +05:30
Nabin Hait
79dd4d707b fix: Error fixed due to merging of lead/customer to party_name 2019-05-27 15:44:12 +05:30
Nabin Hait
a3a733fa91 fix: error because of non-mandatory fields 2019-05-27 15:43:26 +05:30
Nabin Hait
7a9b014f72 fix: Made item code non-mandatory in item 2019-05-27 15:41:06 +05:30
Saurabh
61b4a98356 fix: add shopify_supplier_id custom field while setting up shopify 2019-05-27 15:25:47 +05:30
Don-Leopardo
46db69b304 fix: Make from Lead to Opportunity (#17727)
* fix: lead to oportunity error

* fix: one more missmatched field
2019-05-27 14:22:52 +05:30
Joe Paul
a0be41322d fix(subscription): Remove property decorator (#17395) 2019-05-27 14:11:52 +05:30
Saif
05f29988f4 fix: Status updater for Credit/Debit Note Issued (#16998)
* fix: Status updater for Credit/Debit Note Issued higher precendence than Return

* style: linting
2019-05-27 14:05:14 +05:30
Deepesh Garg
d7903de51c fix: Do not fetch batch items and serialized items in stock reconciliation (#17726) 2019-05-27 13:41:58 +05:30
Kenneth Sequeira
0e31cc9ff8 improve error message (#17745)
Change ```Allocated amount can not greater than unadjusted amount```  to ```Allocated amount cannot be greater than unadjusted amount```

```Allocated amount can not be negative``` to ```Allocated amount cannot be negative```
2019-05-27 13:40:40 +05:30
rohitwaghchaure
32280a11aa fix: customer item code not fetched on selection of the item in the sales order (#17755) 2019-05-27 13:32:44 +05:30
Anurag Mishra
a2f9d2f89c fix: holidays was showing on same date in calendar view (#17753) 2019-05-27 13:20:06 +05:30
Nabin Hait
5fc5355b96 fix: Opening accounting entry for stock entry (#17710)
* fix: Opening accounting entry for stock entry

* test: Add test case for opening stock entry gle

* fix: validate expense account for opening stock entry

* fix: validate expense account for opening stock reco
2019-05-27 11:50:08 +05:30
Deepesh Garg
30c73e6b35 fix: Only show registered customers for GSTR-1 B2B Report (#17719) 2019-05-27 11:48:40 +05:30
Nabin Hait
7a6b88c791 fix: payment reconciliation against expense claim (#17728)
* feat: Added dashboard in expense claim

* fix: payment reconciliation against expense claim
2019-05-27 11:41:05 +05:30
rohitwaghchaure
9c42124fbc Merge pull request #17720 from rohitwaghchaure/reverting_method_to_show_stock_value
fix: warehouse tree view taking lot of time to load
2019-05-23 16:36:34 +05:30
Rohit Waghchaure
0378b15412 fix: warehouse tree view taking lot of time to load 2019-05-23 16:34:32 +05:30
rohitwaghchaure
3f86369604 fix: range 4 for ageing filter added in the AP/AR report (#17714) 2019-05-23 15:27:17 +05:30
rohitwaghchaure
cc99d92116 Merge pull request #17705 from rohitwaghchaure/not_able_to_submit_sales_invoice
fix: not able to submit sales invoice
2019-05-22 22:31:37 +05:30
Rohit Waghchaure
f880975438 fix: not able to submit sales invoice for italy 2019-05-22 22:30:26 +05:30
Saurabh
daa8db4d95 Merge branch 'hotfix' 2019-05-22 22:24:17 +05:30
Saurabh
9ff39e2d1b bumped to version 11.1.33 2019-05-22 22:54:17 +06:00
Saurabh
9d70c5d13f Merge pull request #17704 from rohitwaghchaure/typo_pos_not_working
fix: typo
2019-05-22 22:21:51 +05:30
Rohit Waghchaure
a9a82919e7 fix: typo 2019-05-22 22:15:01 +05:30
Sahil Khan
723ed07642 Merge branch 'hotfix' 2019-05-22 15:54:26 +05:30
Sahil Khan
86b531aea6 bumped to version 11.1.32 2019-05-22 16:14:26 +05:50
rohitwaghchaure
781a420593 Merge pull request #17702 from rohitwaghchaure/not_able_to_submit_sales_invoice_italy_localization
fix: italy localization, not able to submit sales invoice
2019-05-22 15:48:12 +05:30
Rohit Waghchaure
08f709c2a2 fix: italy localization, not able to submit sales invoice 2019-05-22 15:28:47 +05:30
Nabin Hait
a849f6e21e fix: update received qty in PO from PR and PI (#17692)
* fix: Don't set reqd date in PO based on MR, if less than today

* fix: update received qty in PO from PR

* fix: po status

* fix: set schedule date from MR to PO
2019-05-22 15:05:17 +05:30
rohitwaghchaure
6ff8387d56 Merge pull request #17679 from Anurag810/revert_change
fix(Revert): sales order status for order type 'Maintenance
2019-05-22 14:47:47 +05:30
rohitwaghchaure
2af9ff9c33 Merge pull request #17699 from rohitwaghchaure/italian_localization_issue_for_invoices
fix: removed translation for customer type and tax charge type
2019-05-22 13:21:20 +05:30
Rohit Waghchaure
01905cad2f fix: removed translation for customer type and tax charge type 2019-05-22 13:17:46 +05:30
Anurag Mishra
58260e4f0a Merge branch 'hotfix' into revert_change 2019-05-21 16:52:01 +05:30
Anurag Mishra
a01869bb20 style: refactor 2019-05-21 16:50:20 +05:30
Anurag Mishra
3a92615f23 Merge branch 'revert_change' of https://github.com/anurag810/erpnext into revert_change 2019-05-21 14:15:58 +05:30
Sahil Khan
eca8db7405 Merge branch 'hotfix' 2019-05-21 14:11:08 +05:30
Sahil Khan
58c4cfc0d7 bumped to version 11.1.31 2019-05-21 14:31:08 +05:50
Anurag Mishra
ab52a4db75 fix: refactor sales_order_list.js 2019-05-21 13:09:47 +05:30
Saurabh
c8d3a8c0f5 Merge pull request #17686 from rohitwaghchaure/set_default_warehouse_from_the_stock_settings
fix: while making the item, default warehouse not set even if the stock settings has the default warehouse
2019-05-21 12:52:51 +05:30
rohitwaghchaure
aab88eee3e Merge pull request #17644 from nabinhait/bom-item-rate
fix: BOM Item rate based on uom conversion factor and exchange rate
2019-05-21 12:25:19 +05:30
Rushabh Mehta
d9f4c83567 Merge pull request #17539 from adityahase/fix-training-feedback-hotfix
fix(hr): Use event_status instead of status
2019-05-21 12:20:29 +05:30
Rushabh Mehta
b96dd366ce Merge pull request #17641 from nabinhait/gle-opening-stock-reco
fix: GL Entry for opening stock reconciliation
2019-05-21 12:17:04 +05:30
rohitwaghchaure
597ec83af7 Merge pull request #17684 from rohitwaghchaure/item_group_not_disaplying_in_website
fix: item group not disaplying in the website if shopping cart is disabled
2019-05-21 12:15:39 +05:30
Rohit Waghchaure
e802bdd186 fix: while making the item, default warehouse not set even if the stock settings has the warehouse 2019-05-21 12:11:40 +05:30
Rohit Waghchaure
3a949bb298 fix: item group not disaplying in the website if shopping cart is disabled 2019-05-21 10:18:39 +05:30
rohitwaghchaure
1e920dd0d8 Merge pull request #17681 from rohitwaghchaure/expense_claim_reconciliation_issue
fix: payment reconciliation not update the status of the expense claim
2019-05-20 22:41:48 +05:30
Rohit Waghchaure
09536f402e fix: payment reconciliation not update the status of the expense claim 2019-05-20 17:01:22 +05:30
Anurag Mishra
581f26b7a0 Merge branch 'hotfix' into revert_change 2019-05-20 16:22:20 +05:30
Deepesh Garg
0418a2f70a Merge pull request #17677 from rohitwaghchaure/show_opening_entries_gl_report
feat: added checkbox in the filter of general ledger to show opening entries
2019-05-20 15:21:28 +05:30
Saurabh
5e3338744c Merge pull request #17675 from nabinhait/lead-from-email
fix: Person / Org name is not mandatory while creation of lead from email
2019-05-20 14:57:16 +05:30
Anurag Mishra
da3762700c fix(Revert): sales order status for order type 'Maintenance 2019-05-20 12:38:33 +05:30
Rohit Waghchaure
2c9fccd8ba feat: added checkbox in the filter of general ledger to show opening entries 2019-05-20 12:25:53 +05:30
Nabin Hait
286c4fa640 fix: Person / Org name is not mandatory while creation of lead from email 2019-05-20 12:16:34 +05:30
Deepesh Garg
a144e002db Merge pull request #17667 from deepeshgarg007/quotation-qty-fix
fix: Quotation to lead fix while changing item qty
2019-05-18 23:38:24 +05:30
deepeshgarg007
553dabaa08 fix: Pricing rule fix for lead 2019-05-18 21:31:58 +05:30
Anurag Mishra
55d0d32c77 fix: Allow return if delivery note or sales order is required in selling settings(v11) (#17632)
* fix: Allow retrun if delivery note or sales order is required in selling settings

* fix: combined condition

* style: refactor
2019-05-18 13:02:31 +05:30
Deepesh Garg
1f93745eef Merge pull request #17663 from deepeshgarg007/opportunity-list-fixes
fix: Opportunity List view fix
2019-05-17 19:36:08 +05:30
deepeshgarg007
e3a02dd5f6 Merge branch 'hotfix' of https://github.com/frappe/erpnext into opportunity-list-fixes 2019-05-17 19:11:13 +05:30
deepeshgarg007
153733414f fix: Opportunity List view fix 2019-05-17 19:08:24 +05:30
Deepesh Garg
ac535f0ce9 Merge pull request #17661 from deepeshgarg007/quotation_lead_fixes
fix: Quotation to lead fix
2019-05-17 18:35:49 +05:30
deepeshgarg007
3886529787 fix: Get lead details only if lead name entered 2019-05-17 18:10:13 +05:30
Deepesh Garg
d2cd713b89 Merge pull request #17657 from deepeshgarg007/territory_item_code_hotfix
fix: Inactive Sales Item report fixes
2019-05-17 15:41:50 +05:30
Deepesh Garg
6b378e1669 fix: Unable to create item variant based on manufacturer (#17651)
* fix: Unable to create item variant against manufacturer

* fix: Spacing fixes

* fix: Spacing fixes in item.js
2019-05-17 15:39:59 +05:30
deepeshgarg007
a4fc30bbe4 fix: Inactive Sales Item report fixes 2019-05-17 15:39:23 +05:30
rohitwaghchaure
b4b0e4424d Merge pull request #17655 from rohitwaghchaure/not_able_to_make_si_from_dn
fix: not able to make si from dn
2019-05-17 15:29:04 +05:30
Rohit Waghchaure
f3bdcc2a84 fix: not able to make si from dn 2019-05-17 15:28:12 +05:30
Nabin Hait
19901c14c9 fix: Removed unused variable 2019-05-17 14:30:45 +05:30
Deepesh Garg
714d686e50 Merge pull request #17650 from deepeshgarg007/gstr2_fix
fix: GSTR 2 report fix
2019-05-17 11:24:44 +05:30
rohitwaghchaure
094dc1dee6 Merge pull request #17646 from rohitwaghchaure/fixed_bank_reco_internal_transfer
fix: bank reconciliation for internal transfer is not working
2019-05-17 11:20:43 +05:30
deepeshgarg007
cdcf424ba5 Merge branch 'hotfix' of https://github.com/frappe/erpnext into gstr2_fix 2019-05-17 10:30:39 +05:30
deepeshgarg007
2a2b884e32 fix: Return taxable value in get_row_data_for_invoice 2019-05-17 10:20:14 +05:30
Rohit Waghchaure
c1e00f4daa fix: bank reconciliation for internal transfer is not working 2019-05-17 00:01:21 +05:30
Rohan
0a22aab6bb fix(tests): Save batch instead of submitting it (#17636) 2019-05-16 19:45:11 +05:30
Nabin Hait
43c6d1a518 fix: Gte lead details in opportunity (#17633) 2019-05-16 19:35:27 +05:30
Nabin Hait
090219814e fix: BOM Item rate based on uom conversion factor and exchange rate 2019-05-16 19:17:02 +05:30
Nabin Hait
510dc60bf0 fix: GL Entry for opening stock reconciliation 2019-05-16 17:28:39 +05:30
Saurabh
12d520a366 Merge pull request #17562 from fproldan/fix_bundlestock
fix: Available Stock for Packing Items error
2019-05-16 15:40:02 +05:30
Saurabh
a06a527fe6 Merge pull request #17629 from nabinhait/multi-fixes-33
fix: Multiple small fixes
2019-05-16 15:39:31 +05:30
Rushabh Mehta
9a7681535f Merge pull request #17634 from nabinhait/add-multiple-items
feat: Added 'Add Multiple' options in all sales / purchase transactions
2019-05-16 14:56:44 +05:30
Nabin Hait
8e1a612b3b feat: Added 'Add Multiple' options in all sales / purchase transactions 2019-05-16 14:48:10 +05:30
Deepesh Garg
21085bf2be fix: Query fixes in quotation and opportunity (#17619)
* fix: Query fixes for contact person and minor fixes

* fix: Change quotation to quotation_to
2019-05-16 14:11:21 +05:30
Anurag Mishra
8e9413829d fix: variant item description based on attribute (#17627)
* fix: variant item description based on attribute

* fix: requested changes

* style: removed print
2019-05-16 13:57:35 +05:30
Nabin Hait
8208f878ff fix: show tasks in project order by due date and status 2019-05-16 13:43:08 +05:30
Nabin Hait
a7130649cd fix: Get bank account on selection of payment mode in Loan 2019-05-16 13:43:07 +05:30
Nabin Hait
b2d9ffa4ca fix: To allow creation of sales invoice without customer 2019-05-16 13:43:07 +05:30
Himanshu
cc4f13a862 validate: check additional salary component exists (#17530) 2019-05-16 10:34:46 +05:30
Saurabh
60681baf80 Merge branch 'hotfix' into fix_bundlestock 2019-05-15 15:28:58 +05:30
Saurabh
ea6049078a Merge pull request #17620 from rohitwaghchaure/auto_set_the_barcode_if_item_has_one_barcode
fix: set barcode on selection of item if item has one barcode
2019-05-15 15:09:51 +05:30
Rohit Waghchaure
d83e8c56b2 fix: set barcode on selection of item if item has one barcode 2019-05-15 14:10:10 +05:30
Deepesh Garg
e0ade62e38 fix: Status updater fixes in PO and SO and test coverage for return (#17490)
* fix: Status updator fixes in sales and purchase cycle

* fix: Test cases for return in so and po
2019-05-15 12:18:12 +05:30
bghayad
c78b921412 Fix for Chart of Account sorting problem (#17563)
* First Commit from Master

* Fix for CoA sorting problem

* Fixing for CoA sorting problem

* Fix for Chart of Account Sorting Problem
2019-05-15 11:58:59 +05:30
Palash Jhabak
9e9e415c5f fix: Cancelled Lab Tests shouldnt show in SI (#17616)
Cancelled Lab Tests were also showing up in Get Items of SI

fixes issue #17607
2019-05-15 11:37:31 +05:30
Saurabh
0692e5eb78 Merge branch 'hotfix' 2019-05-15 08:07:29 +05:30
Saurabh
3fa5eec07b bumped to version 11.1.30 2019-05-15 08:37:29 +06:00
rohitwaghchaure
ab1bf1af19 fix: not able to create the sales invoice without item code (#17610) 2019-05-15 07:45:47 +05:30
sahil28297
15e0861e82 fix(patch): set attribute to none if it does not exist (#17605) 2019-05-15 07:44:45 +05:30
Prasad Ramesh
aa493a25f8 fix: made Sales Partner Comission report visible in Selling module (#17604) 2019-05-15 07:43:39 +05:30
Saurabh
1c2915f74b fix: reload docs before creating custom fields (#17585) 2019-05-15 07:43:16 +05:30
rohitwaghchaure
391b3b67cb fix: limit offset was missing in the get_delivery_notes_to_be_billed method (#17609) 2019-05-15 07:41:10 +05:30
Nabin Hait
0361c50503 refactor: Payroll processing and tax calculation (#17595)
* refactor: Payroll processing and tax calculation

* fix: payroll test cases

* fix: Codacy fixes

* fix: removed debug mode

* fix: payroll test cases

* Update payroll_period.py
2019-05-15 07:38:57 +05:30
Sahil Khan
517a3071cf Merge branch 'hotfix' 2019-05-14 16:48:26 +05:30
Sahil Khan
0e30e705c5 bumped to version 11.1.29 2019-05-14 17:08:26 +05:50
Deepesh Garg
940df7563b Merge pull request #17575 from deepeshgarg007/quotation-filter-fixes
fix: Standard filter and dashboard fixes in quotation and opportunity
2019-05-14 15:10:41 +05:30
deepeshgarg007
f390872944 Merge branch 'hotfix' of https://github.com/frappe/erpnext into quotation-filter-fixes 2019-05-14 14:58:22 +05:30
deepeshgarg007
cfd18d4e03 fix: Lead and customer dashboard fixes 2019-05-14 14:57:22 +05:30
Deepesh Garg
6c73439e7f Merge pull request #17598 from deepeshgarg007/lead-fixes
fix: Change lead to party_name in Lead
2019-05-14 14:41:49 +05:30
deepeshgarg007
2389e2c438 fix: Change lead to party_name 2019-05-14 14:40:18 +05:30
Deepesh Garg
c2341ca8e3 Merge pull request #17596 from deepeshgarg007/territory_group_fixes_hotfix
fix: Make territory filter mandatory
2019-05-14 14:35:26 +05:30
deepeshgarg007
3b810ea8da fix: Make territory filter mandatory 2019-05-14 14:32:36 +05:30
Deepesh Garg
f85d6aeecd Merge pull request #17589 from deepeshgarg007/inactive_items_row_hotfix
fix: Do not append row if item is sold within days since last ordered
2019-05-14 11:56:41 +05:30
rohitwaghchaure
a13762b05f Merge pull request #17581 from saurabh6790/quoation_creation_fix
fix: validate customer while pulling information
2019-05-14 11:51:15 +05:30
rohitwaghchaure
dd5a0a1f26 Merge pull request #17545 from rohitwaghchaure/incorrect_payment_amount_if_advance_amount_in_si
fix: incorrect payment amount in the payment terms if the sales invoice has the advance amount
2019-05-14 11:14:55 +05:30
deepeshgarg007
a6ad0b0ec8 fix: Do not append row if item is sold within days since last order 2019-05-14 08:49:24 +05:30
Saurabh
077e20f7ae fix: valiadte customer while pulling information 2019-05-13 15:21:25 +05:30
Saurabh
73c6d2e44c Merge pull request #17555 from Alchez/hotfix-lead-import-fix
feat(crm): Allow leads to be imported without person name
2019-05-13 13:07:04 +05:30
Saurabh
d7a13ceb10 Merge pull request #17568 from Mangesh-Khairnar/event-fix
feat(training-event): validate event period
2019-05-13 13:06:17 +05:30
Saurabh
6eadd6657d Merge pull request #17574 from rohitwaghchaure/delayed_orders_summary_report
feat: delayed order summary report
2019-05-13 13:05:40 +05:30
Saurabh
0c890a110d Merge branch 'hotfix' into hotfix-lead-import-fix 2019-05-13 12:07:33 +05:30
Saurabh
0f420baaba Merge branch 'hotfix' into event-fix 2019-05-13 11:46:29 +05:30
Aditya Hase
b32d96fc24 fix(accounts): Add missing field (#17571) 2019-05-13 10:21:41 +05:30
Suraj Shetty
cfc9e18749 Merge pull request #17532 from surajshetty3416/fix-rename-account
fix: Show "Merge Account" button only to users with write access
2019-05-13 10:03:16 +05:30
Deepesh Garg
25d6d08329 Merge pull request #17576 from deepeshgarg007/territory_items_hotfix
fix: Show all territories and items in inactive sales item report
2019-05-12 20:44:06 +05:30
Deepesh Garg
9bf4c754c8 Merge branch 'hotfix' into territory_items_hotfix 2019-05-12 18:40:54 +05:30
deepeshgarg007
79b02db156 fix: Indentation fixes 2019-05-12 18:27:52 +05:30
Rohit Waghchaure
e55dd7233d feat: delayed order summary report 2019-05-12 17:17:55 +05:30
rohitwaghchaure
3ba969ad58 Merge pull request #17573 from adityahase/fix-lead-make-quotation
fix(lead): Map lead name to quotation party_name
2019-05-12 17:10:14 +05:30
rohitwaghchaure
f93cb0a6ba Merge pull request #17567 from rohitwaghchaure/fixed_multiple_bank_reconciliation_issues
fix: fixed multiple bank reconciliation issues
2019-05-12 17:04:58 +05:30
Aditya Hase
e23cfd22ca fix(lead): Map lead name to quotation party_name 2019-05-12 16:20:31 +05:30
Mangesh-Khairnar
0d1b022ea0 feat(training-event): validate event period 2019-05-11 23:30:52 +05:30
deepeshgarg007
aca3772f7d fix: Show all territories and items in inactive sales item report 2019-05-11 21:12:51 +05:30
deepeshgarg007
569815b5ad fix: Standard filter and dashboard fixes in quotation and opportunity 2019-05-11 20:09:29 +05:30
Rohit Waghchaure
80b696ce25 fix: wrong journal entries are showing in the reconcilliation section 2019-05-11 14:58:25 +05:30
rohitwaghchaure
ab7570b7e5 Merge pull request #17558 from rohitwaghchaure/incorrect_stock_balance_showing_in
fix: incorrect stock balance showing in the warehouse tree view
2019-05-11 11:30:24 +05:30
NahuelOperto
c87f8c6f00 fix codecay 2019-05-10 10:46:41 -03:00
Rohit Waghchaure
c5c4de885b fix: bank reconciliation not showing as Settled instead of Reconciled 2019-05-10 19:08:41 +05:30
NahuelOperto
a970bcc56d fix identation 2019-05-10 09:17:52 -03:00
NahuelOperto
5e2d822509 fix error when the product bundle does not have the original item name 2019-05-10 09:00:46 -03:00
Rohit Waghchaure
6be1475882 fix: incorrect stock balance showing in the warehouse tree view 2019-05-10 17:19:50 +05:30
Rohan Bansal
43f05d1de1 feat(crm): Allow leads to be imported without person name 2019-05-10 16:03:16 +05:30
rohitwaghchaure
26bb028ec4 Merge pull request #17549 from sunhoww/pos_batch
fix: POS batch not set correctly
2019-05-10 14:14:44 +05:30
Rohit Waghchaure
1637f0aeaf fix: test cases 2019-05-10 13:42:38 +05:30
Saurabh
353f64caf8 Merge pull request #17541 from rohitwaghchaure/project_update_statue_emails_sending_frequestly
fix: project update status emails are sent frequently
2019-05-10 13:24:24 +05:30
Saurabh
5afb00a7f4 Merge branch 'hotfix' into fix-rename-account 2019-05-10 12:40:00 +05:30
Saurabh
b0ec0f545e Merge pull request #17540 from surajshetty3416/fix-slow-item-search
perf: Index show_in_website field for faster item search
2019-05-10 12:38:06 +05:30
rohitwaghchaure
7088513da9 Update project.py 2019-05-10 12:04:21 +05:30
Mohammad Noureldin
8908d4ee8f 17547:
- Filling in 'customer' arg field if not already set and if 'party_name' is available
- This will fix the logic during which Pricing Rule is constructing the SQL query to list all Pricing Rules related to an item
- The reason to put it in accounts_controller is:
-# This is the palce when args are made before making call which will end using Pricing Rule logic, which means we don't have to go all over the place to add the proper arg field
-# IMHO, it is clean enough in contrast to the case where that code is added to pricing_rule.py for example, cause doing this exposes Pricing Rule to the knowledge about a field called 'party_name' while a Pricing Rule document doesn't have any
2019-05-09 18:21:32 +02:00
Sun Howwrongbum
8133970944 fix: POS batch not set correctly 2019-05-09 20:45:19 +05:30
Rohit Waghchaure
dac7ede911 fix: incorrect payment amount in the payment terms if the sales invoice has the advance amount 2019-05-09 19:50:00 +05:30
Rohit Waghchaure
b052498348 fix: project update status emails sent frequently 2019-05-09 19:04:58 +05:30
Aditya Hase
d62de6c8e5 fix(hr): Use event_status instead of status
Training Feedback DocType has event_status field (not status)
This was broken since PR #10379, PR #17197 made this failure explicit.
2019-05-09 19:01:00 +05:30
Suraj Shetty
d37cc9a5d0 perf: Index show_in_website field for faster item search 2019-05-09 18:49:57 +05:30
Suraj Shetty
c7062d8be7 fix: Check permissions before renaming the account 2019-05-09 14:18:41 +05:30
Suraj Shetty
1435e30ad5 fix: Show "Merge Account" button only to users with write access 2019-05-09 14:16:49 +05:30
rohitwaghchaure
b2ff0bda32 Merge pull request #17526 from rohitwaghchaure/allow_data_import_for_salary_slip
fix: allow data import for salary slip
2019-05-08 22:20:20 +05:30
Rohit Waghchaure
ff42d7b5ed fix: allow data import for salary slip 2019-05-08 20:17:47 +05:30
Anurag Mishra
35a4cae2f6 feat: Adding action Stop or Warn for Quality inspection if it is not submitted
* feat: added action on quality inspection

* feat: added action on quality inspection

* feat: Allow the user to by pass inspection if action is warn ad stop when action is to warn

* patch: for setting default action to 'Stop'

* fix: resolve conflicts

* fix: travis

* Update stock_controller.py
2019-05-08 17:41:04 +05:30
rohitwaghchaure
a76cafecb0 Merge pull request #17521 from rohitwaghchaure/fixed_get_invoiced_item_gross_margin
fix: get totals of gross profit amount on call of the method get_invoiced_item_gross_margin
2019-05-08 16:17:11 +05:30
rohitwaghchaure
715ecc31c8 Merge pull request #17519 from rohitwaghchaure/added_validation_for_stock_entry_purpose
fix: job card issue, added validation in the stock entry
2019-05-08 16:16:39 +05:30
deepeshgarg007
09145674ec bumped to version 11.1.28 2019-05-08 15:58:50 +05:50
Rohit Waghchaure
11eebddb79 fix: get totals of gross profit amount on call of the method get_invoiced_item_gross_margin 2019-05-08 15:31:29 +05:30
Rohit Waghchaure
7d7417af29 fix: job card issue, added validation in the stock entry 2019-05-08 15:07:31 +05:30
rohitwaghchaure
6edb6e0b09 Merge pull request #17506 from Anurag810/rename_column
fix: renamed column avg. buying rate to valuation rate in gross profit report
2019-05-08 14:34:14 +05:30
rohitwaghchaure
e057575661 Merge pull request #17518 from rohitwaghchaure/customer_not_able_to_save
fix: not able to save customer if contribution is not set
2019-05-08 14:33:44 +05:30
Rohit Waghchaure
b7b5eeb92b fix: not able to save customer 2019-05-08 14:28:33 +05:30
rohitwaghchaure
85731053fd Merge pull request #17514 from rohitwaghchaure/pricing_rule_not_working_on_quotation
fix: pricing rule not working properly on the quotation
2019-05-08 11:14:34 +05:30
Rohit Waghchaure
9f3eb9e077 fix: pricing rule not working properly on the quotation 2019-05-08 11:13:56 +05:30
Sahil Khan
5ac8dbfceb Merge branch 'hotfix' 2019-05-07 17:55:28 +05:30
Sahil Khan
f4f2301e5b bumped to version 11.1.27 2019-05-07 18:15:28 +05:50
rohitwaghchaure
7888336524 Merge pull request #17512 from rohitwaghchaure/address_not_set_from_the_lead
fix: address not set on the quotation from the lead
2019-05-07 17:50:09 +05:30
Rohit Waghchaure
b8c2e02c67 fix: address not set on the quotation from the lead 2019-05-07 17:18:00 +05:30
Sahil Khan
a9e9efbd23 Merge branch 'hotfix' 2019-05-07 14:45:45 +05:30
Sahil Khan
2f175e6d85 bumped to version 11.1.26 2019-05-07 15:01:01 +05:50
rohitwaghchaure
df817a858b Merge pull request #17510 from rohitwaghchaure/fixed_lead_dashbord_quotation_redirect
fix: quotation link in the lead dashboard not working and filter from the dashboard was not set in the list view
2019-05-07 14:30:16 +05:30
Rohit Waghchaure
4a73059ed3 fix: quotation link in the lead dashboard not working and filter from the dashboard was not applying 2019-05-07 14:29:39 +05:30
rohitwaghchaure
552615525c Merge pull request #17511 from deepeshgarg007/quotation-refactor-fixes
fix: Dynamic Link fieldname fix in quotation
2019-05-07 14:25:38 +05:30
deepeshgarg007
25e408fd2a fix:Contact person query fix in quotation 2019-05-07 12:43:20 +05:30
rohitwaghchaure
375a6f67ae Merge pull request #17505 from rohitwaghchaure/customer_not_found_in_qtn
fix: not able to make sales order from the lead quotation
2019-05-06 19:26:24 +05:30
Anurag Mishra
01095799e8 fix: renamed column avg. buying rate to valuation rate 2019-05-06 17:41:28 +05:30
Rohit Waghchaure
ead8d82a84 fix: not able to make sales order from the lead quotation 2019-05-06 17:16:57 +05:30
Deepesh Garg
2c229c6403 Merge pull request #17495 from sagarvora/ewb_fixes
fix(regional): imporvements to e-Way Bill JSON generation
2019-05-06 12:31:31 +05:30
Sagar Vora
5fb78a375d fix(regional): imporvements to e-Way Bill JSON generation
- Set pincode correctly in 'Bill To - Ship To' scenario
- Python 2 compatibility (convert to int after rounding)
- Avoid execeeding max character limit in tax amounts by rounding the same to two digits
2019-05-06 11:32:37 +05:30
Tyler Matteson
8fcad571f5 Batch naming series hotfix (#17483)
* fix: not able to make sales order from quotation

* fix: batch naming series unicode() call is py3 incompatible
2019-05-04 23:49:24 +05:30
Himanshu
53d7e667dd fix: patch to remove scheduling tool (#17472)
* fix: patch to remove scheduling tool

* fix: drop table

* patch: delete table if exists

* fix: remove drop table

* use orm
2019-05-04 22:42:35 +05:30
Rohan
7395716476 fix(stock): Allow expired batches to be flushed out of the system (#17477) 2019-05-04 22:40:42 +05:30
Rushabh Mehta
c068b6a885 fix: unlink task rather than deleting it (#17486) 2019-05-04 22:39:00 +05:30
Himanshu Warekar
02d28c5908 fix: use list comprehension 2019-05-04 22:35:08 +05:30
Rohan
90d0d24d1a Update erpnext/selling/doctype/customer/customer.py
Co-Authored-By: hrwX <himanshuwarekar@yahoo.com>
2019-05-04 22:35:08 +05:30
Himanshu Warekar
6aab14f9cf fix: calculate percentage only if sales team exist 2019-05-04 22:35:08 +05:30
Himanshu Warekar
163dbdca34 fix: allocated percentage should be equal to 100 2019-05-04 22:35:08 +05:30
Himanshu Warekar
dcc99a2644 fix: python side validation 2019-05-04 22:35:08 +05:30
Rohit Waghchaure
f5297cf386 fix: not able to make sales order from quotation 2019-05-03 15:52:59 +05:30
Mangesh-Khairnar
9f7fd16882 feat: unlink task from project on task deletion 2019-05-03 13:06:19 +05:30
Nabin Hait
5296ab1f87 Merge pull request #16783 from sahil28297/default_warehouse_for_sales_return
fix: set warehouse from def_warehouse_for_sales_return in sales return
2019-05-03 09:27:04 +05:30
Nabin Hait
e42c14f2cb Merge pull request #17469 from rohitwaghchaure/fixed_sales_order_issue_from_quotation
fix: not able to make sales order from quotation
2019-05-03 09:19:27 +05:30
Rohit Waghchaure
4ef10fd6c3 fix: not able to make sales order from quotation 2019-05-03 01:48:45 +05:30
sahil28297
4c0d0e226b Merge branch 'hotfix' into default_warehouse_for_sales_return 2019-05-03 00:05:22 +05:30
Deepesh Garg
094caaa03e Merge pull request #17465 from deepeshgarg007/inactive-items-fix-hotfix
fix: Inactive sales item report fix
2019-05-02 23:50:57 +05:30
Nabin Hait
ce51553d29 Merge pull request #16907 from sagarvora/ewb
feat(regional 🇮🇳): auto-generate e-Way Bill JSON from Sales Invoice
2019-05-02 21:55:24 +05:30
Nabin Hait
f41fc21274 Merge branch 'hotfix' into ewb 2019-05-02 21:54:38 +05:30
Nabin Hait
1f49b77529 Merge pull request #17460 from nabinhait/project-billing-summary
Refactor: Project billing summary Report
2019-05-02 21:48:35 +05:30
deepeshgarg007
a0012f8c48 fix: Inactive sales item report fix 2019-05-02 19:32:43 +05:30
Nabin Hait
4b1c3ad7ac refactor: Project and billing summary 2019-05-02 18:11:27 +05:30
Anurag Mishra
a063803224 fix: handling case if from date and to date are equal in billing reports 2019-05-02 18:11:27 +05:30
Saurabh
63c7fd90a5 Merge branch 'hotfix' 2019-05-02 16:58:24 +05:30
Saurabh
c9c02c7c85 bumped to version 11.1.25 2019-05-02 17:28:24 +06:00
Nabin Hait
bc7ef1937e Merge pull request #17455 from saurabh6790/multiple_fixes
fix: data pulling based on quotation_to and party_name
2019-05-02 16:01:07 +05:30
Nabin Hait
75b63c5b4c Update set_missing_title_for_quotation.py 2019-05-02 16:00:49 +05:30
Saurabh
625191d20a fix: provision to setup customer name on quotation save and patch for the same 2019-05-02 15:50:01 +05:30
Saurabh
1417c7e828 fix: data pulling based on quotation_to and party_name 2019-05-02 14:12:29 +05:30
Saurabh
a4bbc68945 Merge pull request #17449 from nabinhait/auto-account-creation-company-tree
fix: Validate parent account of child company while creating new account based on parent company
2019-05-02 12:26:23 +05:30
Nabin Hait
2d7a591c61 Merge branch 'hotfix' into auto-account-creation-company-tree 2019-05-02 09:56:53 +05:30
Nabin Hait
448a5e1c9c Merge pull request #17447 from nabinhait/woocommerce-multilingual-fix
fix: Multilingual handling in woocommerce integration
2019-05-01 21:15:07 +05:30
Nabin Hait
7be75adc3f fix: Validate parent account of child company while creating new account based on parent company 2019-05-01 20:26:09 +05:30
Nabin Hait
fafee7cf61 Merge pull request #17442 from nabinhait/work-order-bom-image
feat: Added item image in work order and bom
2019-05-01 19:31:24 +05:30
Nabin Hait
16aa23e454 fix: Multilingual handling in woocommerce integration 2019-05-01 19:17:59 +05:30
Nabin Hait
a4d5c5414d Merge pull request #17440 from nabinhait/pe-ref-exchange-rate
fix: Always fetch exchange rate from ref document
2019-05-01 17:17:46 +05:30
Nabin Hait
b104e3595c feat: Added item image in work order and bom 2019-05-01 16:37:32 +05:30
Deepesh Garg
5a06dd1ed1 Merge pull request #17436 from deepeshgarg007/name_fix_hotfix
fix: Rename Inactive Items report to Inactive Sales Items
2019-05-01 16:29:02 +05:30
Nabin Hait
27fe55efe1 fix: Always fetch exchange rate from ref document 2019-05-01 15:32:30 +05:30
Deepesh Garg
d29fde0bf3 Merge branch 'hotfix' into name_fix_hotfix 2019-05-01 14:27:17 +05:30
deepeshgarg007
8326925fe8 fix: Rename Inactive Items report to Inactive Sales Item 2019-05-01 13:36:08 +05:30
Deepesh Garg
09ac547a80 Merge branch 'hotfix' into ewb 2019-04-30 10:28:57 +05:30
deepeshgarg007
0555223797 Merge branch 'hotfix' of https://github.com/frappe/erpnext into ewb 2019-04-28 23:22:48 +05:30
deepeshgarg007
de93efb304 fix: Added test cases for ewb json creation 2019-04-28 23:14:02 +05:30
deepeshgarg007
6856033d3c fix: Code cleanup and fixes 2019-04-28 23:13:26 +05:30
Sagar Vora
4dacb89da6 fix: test case and semicolons 2019-04-25 21:45:26 +05:30
Sagar Vora
c552d74746 fix: add missing semicolons 2019-04-25 21:45:26 +05:30
Sagar Vora
2697c2d869 feat(regional | India): auto-generate e-Way Bill JSON from Sales Invoice 2019-04-25 21:45:26 +05:30
Nabin Hait
f62e789173 Merge branch 'hotfix' into fec_improvement 2019-04-11 11:46:43 +05:30
Charles-Henri Decultot
91578757ff Addition of third party types in FEC 2019-03-26 08:57:36 +00:00
Sahil Khan
88bd0674ed fix: set warehouse from defalut_warehouse_for_sales_return in sales return 2019-02-26 14:50:20 +05:30
183 changed files with 11147 additions and 8574 deletions

View File

@@ -1,20 +0,0 @@
include MANIFEST.in
include requirements.txt
include *.json
include *.md
include *.py
include *.txt
include .travis.yml
recursive-include erpnext *.txt
recursive-include erpnext *.css
recursive-include erpnext *.csv
recursive-include erpnext *.html
recursive-include erpnext *.ico
recursive-include erpnext *.js
recursive-include erpnext *.json
recursive-include erpnext *.md
recursive-include erpnext *.png
recursive-include erpnext *.py
recursive-include erpnext *.svg
recursive-include erpnext/public *
recursive-exclude * *.pyc

View File

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

View File

@@ -42,15 +42,14 @@ frappe.ui.form.on('Account', {
// show / hide convert buttons
frm.trigger('add_toolbar_buttons');
}
frm.add_custom_button(__('Update Account Name / Number'), function () {
frm.trigger("update_account_number");
});
}
if(!frm.doc.__islocal) {
frm.add_custom_button(__('Merge Account'), function () {
frm.trigger("merge_account");
});
if (frm.has_perm('write')) {
frm.add_custom_button(__('Update Account Name / Number'), function () {
frm.trigger("update_account_number");
});
frm.add_custom_button(__('Merge Account'), function () {
frm.trigger("merge_account");
});
}
}
},
account_type: function (frm) {

View File

@@ -105,20 +105,27 @@ class Account(NestedSet):
descendants = get_descendants_of('Company', self.company)
if not descendants: return
acc_name_map = {}
acc_name = frappe.db.get_value('Account', self.parent_account, "account_name")
parent_acc_name_map = {}
parent_acc_name = frappe.db.get_value('Account', self.parent_account, "account_name")
for d in frappe.db.get_values('Account',
{"company": ["in", descendants], "account_name": acc_name},
{"company": ["in", descendants], "account_name": parent_acc_name},
["company", "name"], as_dict=True):
acc_name_map[d["company"]] = d["name"]
parent_acc_name_map[d["company"]] = d["name"]
if not acc_name_map: return
if not parent_acc_name_map: return
for company in descendants:
if not parent_acc_name_map.get(company):
frappe.throw(_("While creating account for child Company {0}, parent account {1} not found. Please create the parent account in corresponding COA")
.format(company, parent_acc_name))
doc = frappe.copy_doc(self)
doc.flags.ignore_root_company_validation = True
doc.update({"company": company, "account_currency": None,
"parent": acc_name_map[company], "parent_account": acc_name_map[company]})
doc.update({
"company": company,
"account_currency": None,
"parent_account": parent_acc_name_map[company]
})
doc.save()
frappe.msgprint(_("Account {0} is added in the child company {1}")
.format(doc.name, company))
@@ -261,7 +268,7 @@ def update_account_number(name, account_name, account_number=None):
new_name = get_account_autoname(account_number, account_name, account.company)
if name != new_name:
frappe.rename_doc("Account", name, new_name, ignore_permissions=1)
frappe.rename_doc("Account", name, new_name, force=1)
return new_name
@frappe.whitelist()
@@ -280,7 +287,7 @@ def merge_account(old, new, is_group, root_type, company):
frappe.db.set_value("Account", new, "parent_account",
frappe.db.get_value("Account", old, "parent_account"))
frappe.rename_doc("Account", old, new, merge=1, ignore_permissions=1)
frappe.rename_doc("Account", old, new, merge=1, force=1)
return new

View File

@@ -22,7 +22,5 @@ class BankGuarantee(Document):
@frappe.whitelist()
def get_vouchar_detials(column_list, doctype, docname):
print (column_list, doctype, docname)
return frappe.db.sql(''' select {columns} from `tab{doctype}` where name=%s'''
.format(columns=", ".join(json.loads(column_list)), doctype=doctype), docname, as_dict=1)[0]

View File

@@ -121,7 +121,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "Settled",
"default": "Pending",
"fetch_if_empty": 0,
"fieldname": "status",
"fieldtype": "Select",
@@ -755,7 +755,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2019-04-26 14:32:16.437813",
"modified": "2019-05-11 05:27:55.244721",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Transaction",

View File

@@ -36,6 +36,10 @@ class BankTransaction(StatusUpdater):
frappe.db.set_value(self.doctype, self.name, "allocated_amount", 0)
frappe.db.set_value(self.doctype, self.name, "unallocated_amount", abs(flt(self.credit) - flt(self.debit)))
amount = self.debit or self.credit
if amount == self.allocated_amount:
frappe.db.set_value(self.doctype, self.name, "status", "Reconciled")
self.reload()
def clear_linked_payment_entries(self):

View File

@@ -425,8 +425,9 @@ class JournalEntry(AccountsController):
pay_to_recd_from = frappe.db.get_value(d.party_type, d.party,
"customer_name" if d.party_type=="Customer" else "supplier_name")
party_amount += (d.debit_in_account_currency or d.credit_in_account_currency)
party_account_currency = d.account_currency
if pay_to_recd_from and pay_to_recd_from == d.party:
party_amount += (d.debit_in_account_currency or d.credit_in_account_currency)
party_account_currency = d.account_currency
elif frappe.db.get_value("Account", d.account, "account_type") in ["Bank", "Cash"]:
bank_amount += (d.debit_in_account_currency or d.credit_in_account_currency)

View File

@@ -160,7 +160,7 @@ class PaymentEntry(AccountsController):
d.reference_name, self.party_account_currency)
for field, value in iteritems(ref_details):
if not d.get(field) or force:
if field == 'exchange_rate' or not d.get(field) or force:
d.set(field, value)
def validate_payment_type(self):

View File

@@ -167,7 +167,12 @@ def get_pricing_rule_for_item(args):
if args.transaction_type=="selling":
if args.customer and not (args.customer_group and args.territory):
customer = frappe.get_cached_value("Customer", args.customer, ["customer_group", "territory"])
if args.quotation_to and args.quotation_to != 'Customer':
customer = frappe._dict()
else:
customer = frappe.get_cached_value("Customer", args.customer, ["customer_group", "territory"])
if customer:
args.customer_group, args.territory = customer

View File

@@ -100,6 +100,7 @@ class PurchaseInvoice(BuyingController):
self.validate_fixed_asset()
self.create_remarks()
self.set_status()
self.validate_purchase_receipt_if_update_stock()
validate_inter_company_party(self.doctype, self.supplier, self.company, self.inter_company_invoice_reference)
def validate_release_date(self):
@@ -284,7 +285,7 @@ class PurchaseInvoice(BuyingController):
def update_status_updater_args(self):
if cint(self.update_stock):
self.status_updater.extend([{
self.status_updater.append({
'source_dt': 'Purchase Invoice Item',
'target_dt': 'Purchase Order Item',
'join_field': 'po_detail',
@@ -292,28 +293,29 @@ class PurchaseInvoice(BuyingController):
'target_parent_dt': 'Purchase Order',
'target_parent_field': 'per_received',
'target_ref_field': 'qty',
'source_field': 'qty',
'source_field': 'received_qty',
'second_source_dt': 'Purchase Receipt Item',
'second_source_field': 'received_qty',
'second_join_field': 'purchase_order_item',
'percent_join_field':'purchase_order',
# 'percent_join_field': 'prevdoc_docname',
'overflow_type': 'receipt',
'extra_cond': """ and exists(select name from `tabPurchase Invoice`
where name=`tabPurchase Invoice Item`.parent and update_stock = 1)"""
},
{
'source_dt': 'Purchase Invoice Item',
'target_dt': 'Purchase Order Item',
'join_field': 'po_detail',
'target_field': 'returned_qty',
'target_parent_dt': 'Purchase Order',
# 'target_parent_field': 'per_received',
# 'target_ref_field': 'qty',
'source_field': '-1 * qty',
# 'percent_join_field': 'prevdoc_docname',
# 'overflow_type': 'receipt',
'extra_cond': """ and exists (select name from `tabPurchase Invoice`
where name=`tabPurchase Invoice Item`.parent and update_stock=1 and is_return=1)"""
}
])
})
if cint(self.is_return):
self.status_updater.append({
'source_dt': 'Purchase Invoice Item',
'target_dt': 'Purchase Order Item',
'join_field': 'po_detail',
'target_field': 'returned_qty',
'source_field': '-1 * qty',
'second_source_dt': 'Purchase Receipt Item',
'second_source_field': '-1 * qty',
'second_join_field': 'purchase_order_item',
'overflow_type': 'receipt',
'extra_cond': """ and exists (select name from `tabPurchase Invoice`
where name=`tabPurchase Invoice Item`.parent and update_stock=1 and is_return=1)"""
})
def validate_purchase_receipt_if_update_stock(self):
if self.update_stock:
@@ -327,13 +329,13 @@ class PurchaseInvoice(BuyingController):
self.check_prev_docstatus()
self.update_status_updater_args()
self.update_prevdoc_status()
frappe.get_doc('Authorization Control').validate_approving_authority(self.doctype,
self.company, self.base_grand_total)
if not self.is_return:
self.update_against_document_in_jv()
self.update_prevdoc_status()
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
self.update_billing_status_in_pr()
@@ -763,13 +765,13 @@ class PurchaseInvoice(BuyingController):
self.check_for_closed_status()
self.update_status_updater_args()
self.update_prevdoc_status()
if not self.is_return:
from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries
if frappe.db.get_single_value('Accounts Settings', 'unlink_payment_on_cancellation_of_invoice'):
unlink_ref_doc_from_payment_entries(self)
self.update_prevdoc_status()
self.update_billing_status_for_zero_amount_refdoc("Purchase Order")
self.update_billing_status_in_pr()

View File

@@ -6,8 +6,8 @@ frappe.listview_settings['Purchase Invoice'] = {
add_fields: ["supplier", "supplier_name", "base_grand_total", "outstanding_amount", "due_date", "company",
"currency", "is_return", "release_date", "on_hold"],
get_indicator: function(doc) {
if(cint(doc.is_return)==1) {
return [__("Return"), "darkgrey", "is_return,=,Yes"];
if(flt(doc.outstanding_amount) < 0 && doc.docstatus == 1) {
return [__("Debit Note Issued"), "darkgrey", "outstanding_amount,<,0"]
} else if(flt(doc.outstanding_amount) > 0 && doc.docstatus==1) {
if(cint(doc.on_hold) && !doc.release_date) {
return [__("On Hold"), "darkgrey"];
@@ -18,9 +18,9 @@ frappe.listview_settings['Purchase Invoice'] = {
} else {
return [__("Unpaid"), "orange", "outstanding_amount,>,0|due,>=,Today"];
}
} else if(flt(doc.outstanding_amount) < 0 && doc.docstatus == 1) {
return [__("Debit Note Issued"), "darkgrey", "outstanding_amount,<,0"]
}else if(flt(doc.outstanding_amount)==0 && doc.docstatus==1) {
} else if(cint(doc.is_return)) {
return [__("Return"), "darkgrey", "is_return,=,Yes"];
} else if(flt(doc.outstanding_amount)==0 && doc.docstatus==1) {
return [__("Paid"), "green", "outstanding_amount,=,0"];
}
}

View File

@@ -402,9 +402,9 @@ class TestPurchaseInvoice(unittest.TestCase):
pi.save()
pi.submit()
self.assertEqual(pi.payment_schedule[0].payment_amount, 756.15)
self.assertEqual(pi.payment_schedule[0].payment_amount, 606.15)
self.assertEqual(pi.payment_schedule[0].due_date, pi.posting_date)
self.assertEqual(pi.payment_schedule[1].payment_amount, 756.15)
self.assertEqual(pi.payment_schedule[1].payment_amount, 606.15)
self.assertEqual(pi.payment_schedule[1].due_date, add_days(pi.posting_date, 30))
pi.load_from_db()

View File

@@ -0,0 +1,38 @@
frappe.ui.form.on("Sales Invoice", {
setup: function(frm) {
frm.set_query('transporter', function() {
return {
filters: {
'is_transporter': 1
}
};
});
frm.set_query('driver', function(doc) {
return {
filters: {
'transporter': doc.transporter
}
};
});
},
refresh: function(frm) {
if(frm.doc.docstatus == 1 && !frm.is_dirty()
&& !frm.doc.is_return && !frm.doc.ewaybill) {
frm.add_custom_button('e-Way Bill JSON', () => {
var w = window.open(
frappe.urllib.get_full_url(
"/api/method/erpnext.regional.india.utils.generate_ewb_json?"
+ "dt=" + encodeURIComponent(frm.doc.doctype)
+ "&dn=" + encodeURIComponent(frm.doc.name)
)
);
if (!w) {
frappe.msgprint(__("Please enable pop-ups")); return;
}
}, __("Make"));
}
}
});

View File

@@ -0,0 +1,33 @@
var globalOnload = frappe.listview_settings['Sales Invoice'].onload;
frappe.listview_settings['Sales Invoice'].onload = function (doclist) {
// Provision in case onload event is added to sales_invoice.js in future
if (globalOnload) {
globalOnload(doclist);
}
const action = () => {
const selected_docs = doclist.get_checked_items();
const docnames = doclist.get_checked_items(true);
for (let doc of selected_docs) {
if (doc.docstatus !== 1) {
frappe.throw(__("e-Way Bill JSON can only be generated from a submitted document"));
}
}
var w = window.open(
frappe.urllib.get_full_url(
"/api/method/erpnext.regional.india.utils.generate_ewb_json?"
+ "dt=" + encodeURIComponent(doclist.doctype)
+ "&dn=" + encodeURIComponent(docnames)
)
);
if (!w) {
frappe.msgprint(__("Please enable pop-ups")); return;
}
};
doclist.page.add_actions_menu_item(__('Generate e-Way Bill JSON'), action, false);
};

View File

@@ -54,8 +54,8 @@ class SalesInvoice(SellingController):
def set_indicator(self):
"""Set indicator for portal"""
if cint(self.is_return) == 1:
self.indicator_title = _("Return")
if self.outstanding_amount < 0:
self.indicator_title = _("Credit Note Issued")
self.indicator_color = "darkgrey"
elif self.outstanding_amount > 0 and getdate(self.due_date) >= getdate(nowdate()):
self.indicator_color = "orange"
@@ -63,8 +63,8 @@ class SalesInvoice(SellingController):
elif self.outstanding_amount > 0 and getdate(self.due_date) < getdate(nowdate()):
self.indicator_color = "red"
self.indicator_title = _("Overdue")
elif self.outstanding_amount < 0:
self.indicator_title = _("Credit Note Issued")
elif cint(self.is_return) == 1:
self.indicator_title = _("Return")
self.indicator_color = "darkgrey"
else:
self.indicator_color = "green"
@@ -256,7 +256,7 @@ class SalesInvoice(SellingController):
def update_status_updater_args(self):
if cint(self.update_stock):
self.status_updater.extend([{
self.status_updater.append({
'source_dt':'Sales Invoice Item',
'target_dt':'Sales Order Item',
'target_parent_dt':'Sales Order',
@@ -274,21 +274,20 @@ class SalesInvoice(SellingController):
'overflow_type': 'delivery',
'extra_cond': """ and exists(select name from `tabSales Invoice`
where name=`tabSales Invoice Item`.parent and update_stock = 1)"""
},
{
'source_dt': 'Sales Invoice Item',
'target_dt': 'Sales Order Item',
'join_field': 'so_detail',
'target_field': 'returned_qty',
'target_parent_dt': 'Sales Order',
# 'target_parent_field': 'per_delivered',
# 'target_ref_field': 'qty',
'source_field': '-1 * qty',
# 'percent_join_field': 'sales_order',
# 'overflow_type': 'delivery',
'extra_cond': """ and exists (select name from `tabSales Invoice` where name=`tabSales Invoice Item`.parent and update_stock=1 and is_return=1)"""
}
])
})
if cint(self.is_return):
self.status_updater.append({
'source_dt': 'Sales Invoice Item',
'target_dt': 'Sales Order Item',
'join_field': 'so_detail',
'target_field': 'returned_qty',
'target_parent_dt': 'Sales Order',
'source_field': '-1 * qty',
'second_source_dt': 'Delivery Note Item',
'second_source_field': '-1 * qty',
'second_join_field': 'so_detail',
'extra_cond': """ and exists (select name from `tabSales Invoice` where name=`tabSales Invoice Item`.parent and update_stock=1 and is_return=1)"""
})
def check_credit_limit(self):
from erpnext.selling.doctype.customer.customer import check_credit_limit
@@ -504,12 +503,15 @@ class SalesInvoice(SellingController):
def so_dn_required(self):
"""check in manage account if sales order / delivery note required or not."""
if self.is_return:
return
dic = {'Sales Order':['so_required', 'is_pos'],'Delivery Note':['dn_required', 'update_stock']}
for i in dic:
if frappe.db.get_single_value('Selling Settings', dic[i][0]) == 'Yes':
for d in self.get('items'):
if frappe.get_cached_value('Item', d.item_code, 'is_stock_item') == 1 \
and not d.get(i.lower().replace(' ','_')) and not self.get(dic[i][1]):
is_stock_item = frappe.get_cached_value('Item', d.item_code, 'is_stock_item')
if (d.item_code and is_stock_item == 1\
and not d.get(i.lower().replace(' ','_')) and not self.get(dic[i][1])):
msgprint(_("{0} is mandatory for Item {1}").format(i,d.item_code), raise_exception=1)
@@ -1167,6 +1169,8 @@ class SalesInvoice(SellingController):
self.set_missing_values(for_validate = True)
def validate_inter_company_party(doctype, party, company, inter_company_invoice_reference):
if not party:
return
if doctype == "Sales Invoice":
partytype, ref_partytype, internal = "Customer", "Supplier", "is_internal_customer"
ref_doc = "Purchase Invoice"

View File

@@ -6,16 +6,16 @@ frappe.listview_settings['Sales Invoice'] = {
add_fields: ["customer", "customer_name", "base_grand_total", "outstanding_amount", "due_date", "company",
"currency", "is_return"],
get_indicator: function(doc) {
if(cint(doc.is_return)==1) {
return [__("Return"), "darkgrey", "is_return,=,Yes"];
} else if(flt(doc.outstanding_amount)==0) {
return [__("Paid"), "green", "outstanding_amount,=,0"]
} else if(flt(doc.outstanding_amount) < 0) {
if(flt(doc.outstanding_amount) < 0) {
return [__("Credit Note Issued"), "darkgrey", "outstanding_amount,<,0"]
}else if (flt(doc.outstanding_amount) > 0 && doc.due_date >= frappe.datetime.get_today()) {
} else if (flt(doc.outstanding_amount) > 0 && doc.due_date >= frappe.datetime.get_today()) {
return [__("Unpaid"), "orange", "outstanding_amount,>,0|due_date,>,Today"]
} else if (flt(doc.outstanding_amount) > 0 && doc.due_date < frappe.datetime.get_today()) {
return [__("Overdue"), "red", "outstanding_amount,>,0|due_date,<=,Today"]
} else if(cint(doc.is_return)) {
return [__("Return"), "darkgrey", "is_return,=,Yes"];
} else if(flt(doc.outstanding_amount)==0) {
return [__("Paid"), "green", "outstanding_amount,=,0"]
}
},
right_column: "grand_total"

View File

@@ -18,6 +18,8 @@ from erpnext.accounts.doctype.account.test_account import get_inventory_account,
from erpnext.controllers.taxes_and_totals import get_itemised_tax_breakup_data
from erpnext.stock.doctype.item.test_item import create_item
from six import iteritems
from erpnext.regional.india.utils import get_ewb_data
class TestSalesInvoice(unittest.TestCase):
def make(self):
w = frappe.copy_doc(test_records[0])
@@ -1611,6 +1613,110 @@ class TestSalesInvoice(unittest.TestCase):
self.assertEqual(expected_gle[i][2], gle.credit)
self.assertEqual(getdate(expected_gle[i][3]), gle.posting_date)
def test_eway_bill_json(self):
if not frappe.db.exists('Address', '_Test Address for Eway bill-Billing'):
address = frappe.get_doc({
"address_line1": "_Test Address Line 1",
"address_title": "_Test Address for Eway bill",
"address_type": "Billing",
"city": "_Test City",
"state": "Test State",
"country": "India",
"doctype": "Address",
"is_primary_address": 1,
"phone": "+91 0000000000",
"gstin": "27AAECE4835E1ZR",
"gst_state": "Maharashtra",
"gst_state_number": "27",
"pincode": "401108"
}).insert()
address.append("links", {
"link_doctype": "Company",
"link_name": "_Test Company"
})
address.save()
if not frappe.db.exists('Address', '_Test Customer-Address for Eway bill-Shipping'):
address = frappe.get_doc({
"address_line1": "_Test Address Line 1",
"address_title": "_Test Customer-Address for Eway bill",
"address_type": "Shipping",
"city": "_Test City",
"state": "Test State",
"country": "India",
"doctype": "Address",
"is_primary_address": 1,
"phone": "+91 0000000000",
"gst_state": "Maharashtra",
"gst_state_number": "27",
"pincode": "410038"
}).insert()
address.append("links", {
"link_doctype": "Customer",
"link_name": "_Test Customer"
})
address.save()
gst_settings = frappe.get_doc("GST Settings")
gst_account = frappe.get_all(
"GST Account",
fields=["cgst_account", "sgst_account", "igst_account"],
filters = {"company": "_Test Company"})
if not gst_account:
gst_settings.append("gst_accounts", {
"company": "_Test Company",
"cgst_account": "CGST - _TC",
"sgst_account": "SGST - _TC",
"igst_account": "IGST - _TC",
})
gst_settings.save()
si = create_sales_invoice(do_not_save =1, rate = '60000')
si.distance = 2000
si.company_address = "_Test Address for Eway bill-Billing"
si.customer_address = "_Test Customer-Address for Eway bill-Shipping"
si.vehicle_no = "KA12KA1234"
si.append("taxes", {
"charge_type": "On Net Total",
"account_head": "CGST - _TC",
"cost_center": "Main - _TC",
"description": "CGST @ 9.0",
"rate": 9
})
si.append("taxes", {
"charge_type": "On Net Total",
"account_head": "SGST - _TC",
"cost_center": "Main - _TC",
"description": "SGST @ 9.0",
"rate": 9
})
si.submit()
data = get_ewb_data("Sales Invoice", si.name)
self.assertEqual(data['version'], '1.0.1118')
self.assertEqual(data['billLists'][0]['fromGstin'], '27AAECE4835E1ZR')
self.assertEqual(data['billLists'][0]['fromTrdName'], '_Test Company')
self.assertEqual(data['billLists'][0]['toTrdName'], '_Test Customer')
self.assertEqual(data['billLists'][0]['vehicleType'], 'R')
self.assertEqual(data['billLists'][0]['totalValue'], 60000)
self.assertEqual(data['billLists'][0]['cgstValue'], 5400)
self.assertEqual(data['billLists'][0]['sgstValue'], 5400)
self.assertEqual(data['billLists'][0]['vehicleNo'], 'KA12KA1234')
self.assertEqual(data['billLists'][0]['itemList'][0]['taxableAmount'], 60000)
def create_sales_invoice(**args):
si = frappe.new_doc("Sales Invoice")
args = frappe._dict(args)

View File

@@ -314,13 +314,11 @@ class Subscription(Document):
self.save()
@property
def is_postpaid_to_invoice(self):
return getdate(nowdate()) > getdate(self.current_invoice_end) or \
(getdate(nowdate()) >= getdate(self.current_invoice_end) and getdate(self.current_invoice_end) == getdate(self.current_invoice_start)) and \
not self.has_outstanding_invoice()
@property
def is_prepaid_to_invoice(self):
if not self.generate_invoice_at_period_start:
return False
@@ -340,7 +338,7 @@ class Subscription(Document):
2. Change the `Subscription` status to 'Past Due Date'
3. Change the `Subscription` status to 'Cancelled'
"""
if self.is_postpaid_to_invoice or self.is_prepaid_to_invoice:
if self.is_postpaid_to_invoice() or self.is_prepaid_to_invoice():
self.generate_invoice()
if self.current_invoice_is_past_due():
self.status = 'Past Due Date'

View File

@@ -39,7 +39,9 @@ def add_payment_to_transaction(transaction, payment_entry, gl_entry):
"payment_entry": payment_entry.name,
"allocated_amount": allocated_amount
})
transaction.save()
transaction.update_allocations()
@frappe.whitelist()
def get_linked_payments(bank_transaction):
@@ -56,7 +58,11 @@ def get_linked_payments(bank_transaction):
return check_amount_vs_description(amount_matching, description_matching)
elif description_matching:
return sorted(description_matching, key = lambda x: x["posting_date"], reverse=True)
description_matching = filter(lambda x: not x.get('clearance_date'), description_matching)
if not description_matching:
return []
return sorted(list(description_matching), key = lambda x: x["posting_date"], reverse=True)
else:
return []
@@ -71,7 +77,7 @@ def check_matching_amount(bank_account, company, transaction):
payment_entries = frappe.get_all("Payment Entry", fields=["'Payment Entry' as doctype", "name", "paid_amount", "payment_type", "reference_no", "reference_date",
"party", "party_type", "posting_date", "{0}".format(currency_field)], filters=[["paid_amount", "like", "{0}%".format(amount)],
["docstatus", "=", "1"], ["payment_type", "=", payment_type], ["ifnull(clearance_date, '')", "=", ""], ["{0}".format(account_from_to), "=", "{0}".format(bank_account)]])
["docstatus", "=", "1"], ["payment_type", "=", [payment_type, "Internal Transfer"]], ["ifnull(clearance_date, '')", "=", ""], ["{0}".format(account_from_to), "=", "{0}".format(bank_account)]])
if transaction.credit > 0:
journal_entries = frappe.db.sql("""
@@ -97,7 +103,8 @@ def check_matching_amount(bank_account, company, transaction):
journal_entries = frappe.db.sql("""
SELECT
'Journal Entry' as doctype, je.name, je.posting_date, je.cheque_no as reference_no,
je.pay_to_recd_from as party, je.cheque_date as reference_date, jea.credit_in_account_currency as paid_amount
jea.account_currency as currency, je.pay_to_recd_from as party, je.cheque_date as reference_date,
jea.credit_in_account_currency as paid_amount
FROM
`tabJournal Entry Account` as jea
JOIN
@@ -107,12 +114,17 @@ def check_matching_amount(bank_account, company, transaction):
WHERE
(je.clearance_date is null or je.clearance_date='0000-00-00')
AND
jea.account = %s
jea.account = %(bank_account)s
AND
jea.credit_in_account_currency like %s
jea.credit_in_account_currency like %(txt)s
AND
je.docstatus = 1
""", (bank_account, amount), as_dict=True)
""", {
'bank_account': bank_account,
'txt': '%%%s%%' % amount
}, as_dict=True)
frappe.errprint(journal_entries)
if transaction.credit > 0:
sales_invoices = frappe.db.sql("""
@@ -213,9 +225,14 @@ def get_matching_descriptions_data(company, transaction):
company_currency = get_company_currency(company)
for key, value in iteritems(links):
if key == "Payment Entry":
data.extend(frappe.get_all("Payment Entry", filters=[["name", "in", value]], fields=["'Payment Entry' as doctype", "posting_date", "party", "reference_no", "reference_date", "paid_amount", "paid_to_account_currency as currency"]))
data.extend(frappe.get_all("Payment Entry", filters=[["name", "in", value]],
fields=["'Payment Entry' as doctype", "posting_date", "party", "reference_no",
"reference_date", "paid_amount", "paid_to_account_currency as currency", "clearance_date"]))
if key == "Journal Entry":
journal_entries = frappe.get_all("Journal Entry", filters=[["name", "in", value]], fields=["name", "'Journal Entry' as doctype", "posting_date", "pay_to_recd_from as party", "cheque_no as reference_no", "cheque_date as reference_date", "total_credit as paid_amount"])
journal_entries = frappe.get_all("Journal Entry", filters=[["name", "in", value]],
fields=["name", "'Journal Entry' as doctype", "posting_date",
"pay_to_recd_from as party", "cheque_no as reference_no", "cheque_date as reference_date",
"total_credit as paid_amount", "clearance_date"])
for journal_entry in journal_entries:
journal_entry_accounts = frappe.get_all("Journal Entry Account", filters={"parenttype": journal_entry["doctype"], "parent": journal_entry["name"]}, fields=["account_currency"])
journal_entry["currency"] = journal_entry_accounts[0]["account_currency"] if journal_entry_accounts else company_currency
@@ -236,6 +253,9 @@ def check_amount_vs_description(amount_matching, description_matching):
if description_matching:
for am_match in amount_matching:
for des_match in description_matching:
if des_match.get("clearance_date"):
continue
if am_match["party"] == des_match["party"]:
if am_match not in result:
result.append(am_match)

View File

@@ -1957,6 +1957,12 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
}],
function(values){
me.item_batch_no[me.items[0].item_code] = values.batch;
const item = me.frm.doc.items.find(
({ item_code }) => item_code === me.items[0].item_code
);
if (item) {
item.batch_no = values.batch;
}
},
__('Select Batch No'))
}

View File

@@ -44,7 +44,7 @@ def _get_party_details(party=None, account=None, party_type="Customer", company=
frappe.throw(_("Not permitted for {0}").format(party), frappe.PermissionError)
party = frappe.get_doc(party_type, party)
currency = party.default_currency if party.default_currency else get_company_currency(company)
currency = party.default_currency if party.get("default_currency") else get_company_currency(company)
out["taxes_and_charges"] = set_taxes(party.name, party_type, posting_date, company, out.customer_group, out.supplier_group)
out["payment_terms_template"] = get_pyt_term_template(party.name, party_type, company)
@@ -140,7 +140,7 @@ def set_other_values(out, party, party_type):
def get_default_price_list(party):
"""Return default price list for party (Document object)"""
if party.default_price_list:
if party.get("default_price_list"):
return party.default_price_list
if party.doctype == "Customer":

View File

@@ -44,6 +44,13 @@ frappe.query_reports["Accounts Payable"] = {
"default": "90",
"reqd": 1
},
{
"fieldname":"range4",
"label": __("Ageing Range 4"),
"fieldtype": "Int",
"default": "120",
"reqd": 1
},
{
"fieldname":"finance_book",
"label": __("Finance Book"),

View File

@@ -44,6 +44,13 @@ frappe.query_reports["Accounts Payable Summary"] = {
"default": "90",
"reqd": 1
},
{
"fieldname":"range4",
"label": __("Ageing Range 4"),
"fieldtype": "Int",
"default": "120",
"reqd": 1
},
{
"fieldname":"finance_book",
"label": __("Finance Book"),

View File

@@ -46,6 +46,8 @@
var range2 = report.columns[12].label;
var range3 = report.columns[13].label;
var range4 = report.columns[14].label;
var range5 = report.columns[15].label;
var range6 = report.columns[16].label;
%}
{% if(balance_row) { %}
<table class="table table-bordered table-condensed">
@@ -56,8 +58,10 @@
<col style="width: 18mm;">
<col style="width: 18mm;">
<col style="width: 18mm;">
<col style="width: 18mm;">
<col style="width: 18mm;">
<col style="width: 18mm;">
<col style="width: 18mm;">
<col style="width: 18mm;">
<col style="width: 18mm;">
</colgroup>
<thead>
@@ -67,16 +71,20 @@
<th>{%= __(range2) %}</th>
<th>{%= __(range3) %}</th>
<th>{%= __(range4) %}</th>
<th>{%= __(range5) %}</th>
<th>{%= __(range6) %}</th>
<th>{%= __("Total") %}</th>
</tr>
</thead>
<tbody>
<tr>
<td>{%= __("Total Outstanding") %}</td>
<td class="text-right">{%= format_currency(balance_row[range1]) %}</td>
<td class="text-right">{%= format_number(balance_row[range1], null, 2) %}</td>
<td class="text-right">{%= format_currency(balance_row[range2]) %}</td>
<td class="text-right">{%= format_currency(balance_row[range3]) %}</td>
<td class="text-right">{%= format_currency(balance_row[range4]) %}</td>
<td class="text-right">{%= format_currency(balance_row[range5]) %}</td>
<td class="text-right">{%= format_currency(balance_row[range6]) %}</td>
<td class="text-right">
{%= format_currency(flt(balance_row[("outstanding_amount")]), data[data.length-1]["currency"]) %}
</td>
@@ -86,6 +94,8 @@
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td class="text-right">
{%= format_currency(flt(balance_row[("pdc/lc_amount")]), data[data.length-1]["currency"]) %}
</td>
@@ -95,6 +105,8 @@
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th class="text-right">
{%= format_currency(flt(balance_row[("outstanding_amount")]-balance_row[("pdc/lc_amount")]), data[data.length-1]["currency"]) %}</th>
</tr>
@@ -111,10 +123,10 @@
<th style="width: 4%">{%= __("Age (Days)") %}</th>
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person_in_print) { %}
<th style="width: 16%">{%= __("Reference") %}</th>
<th style="width: 14%">{%= __("Reference") %}</th>
<th style="width: 10%">{%= __("Sales Person") %}</th>
{% } else { %}
<th style="width: 26%">{%= __("Reference") %}</th>
<th style="width: 24%">{%= __("Reference") %}</th>
{% } %}
{% if(!filters.show_pdc_in_print) { %}
<th style="width: 20%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
@@ -127,7 +139,7 @@
<th style="width: 10%; text-align: right">{%= __("Outstanding Amount") %}</th>
{% if(filters.show_pdc_in_print) { %}
{% if(report.report_name === "Accounts Receivable") { %}
<th style="width: 10%">{%= __("Customer LPO No.") %}</th>
<th style="width: 12%">{%= __("Customer LPO No.") %}</th>
{% } %}
<th style="width: 10%">{%= __("PDC/LC Ref") %}</th>
<th style="width: 10%">{%= __("PDC/LC Amount") %}</th>

View File

@@ -44,6 +44,13 @@ frappe.query_reports["Accounts Receivable"] = {
"default": "90",
"reqd": 1
},
{
"fieldname":"range4",
"label": __("Ageing Range 4"),
"fieldtype": "Int",
"default": "120",
"reqd": 1
},
{
"fieldname":"finance_book",
"label": __("Finance Book"),

View File

@@ -68,13 +68,13 @@ class ReceivablePayableReport(object):
if self.filters.based_on_payment_terms:
columns.append({
"label": "Payment Term",
"label": _("Payment Term"),
"fieldname": "payment_term",
"fieldtype": "Data",
"width": 120
})
columns.append({
"label": "Invoice Grand Total",
"label": _("Invoice Grand Total"),
"fieldname": "invoice_grand_total",
"fieldtype": "Currency",
"options": "currency",
@@ -83,7 +83,7 @@ class ReceivablePayableReport(object):
for label in ("Invoiced Amount", "Paid Amount", credit_or_debit_note, "Outstanding Amount"):
columns.append({
"label": label,
"label": _(label),
"fieldname": frappe.scrub(label),
"fieldtype": "Currency",
"options": "currency",
@@ -100,11 +100,14 @@ class ReceivablePayableReport(object):
self.filters["range2"] = "60"
if not "range3" in self.filters:
self.filters["range3"] = "90"
if not "range4" in self.filters:
self.filters["range4"] = "120"
for label in ("0-{range1}".format(range1=self.filters["range1"]),
"{range1}-{range2}".format(range1=cint(self.filters["range1"])+ 1, range2=self.filters["range2"]),
"{range2}-{range3}".format(range2=cint(self.filters["range2"])+ 1, range3=self.filters["range3"]),
"{range3}-{above}".format(range3=cint(self.filters["range3"])+ 1, above=_("Above"))):
"{range3}-{range4}".format(range3=cint(self.filters["range3"])+ 1, range4=self.filters["range4"]),
"{range4}-{above}".format(range4=cint(self.filters["range4"])+ 1, above=_("Above"))):
columns.append({
"label": label,
"fieldname":label,
@@ -328,18 +331,17 @@ class ReceivablePayableReport(object):
entry_date = gle.posting_date
row += get_ageing_data(cint(self.filters.range1), cint(self.filters.range2),
cint(self.filters.range3), self.age_as_on, entry_date, outstanding_amount)
cint(self.filters.range3), cint(self.filters.range4), self.age_as_on, entry_date, outstanding_amount)
# issue 6371-Ageing buckets should not have amounts if due date is not reached
if self.filters.ageing_based_on == "Due Date" \
and getdate(due_date) > getdate(self.filters.report_date):
row[-1]=row[-2]=row[-3]=row[-4]=0
row[-1]=row[-2]=row[-3]=row[-4]=row[-5]=0
if self.filters.ageing_based_on == "Supplier Invoice Date" \
and getdate(bill_date) > getdate(self.filters.report_date):
row[-1]=row[-2]=row[-3]=row[-4]=0
row[-1]=row[-2]=row[-3]=row[-4]=row[-5]=0
if self.filters.get(scrub(args.get("party_type"))):
row.append(gle.account_currency)
@@ -585,13 +587,13 @@ class ReceivablePayableReport(object):
return payment_term_map
def get_chart_data(self, columns, data):
ageing_columns = columns[self.ageing_col_idx_start : self.ageing_col_idx_start+4]
ageing_columns = columns[self.ageing_col_idx_start : self.ageing_col_idx_start+5]
rows = []
for d in data:
rows.append(
{
'values': d[self.ageing_col_idx_start : self.ageing_col_idx_start+4]
'values': d[self.ageing_col_idx_start : self.ageing_col_idx_start+5]
}
)
@@ -610,21 +612,22 @@ def execute(filters=None):
}
return ReceivablePayableReport(filters).run(args)
def get_ageing_data(first_range, second_range, third_range, age_as_on, entry_date, outstanding_amount):
# [0-30, 30-60, 60-90, 90-above]
outstanding_range = [0.0, 0.0, 0.0, 0.0]
def get_ageing_data(first_range, second_range, third_range,
fourth_range, age_as_on, entry_date, outstanding_amount):
# [0-30, 30-60, 60-90, 90-120, 120-above]
outstanding_range = [0.0, 0.0, 0.0, 0.0, 0.0]
if not (age_as_on and entry_date):
return [0] + outstanding_range
age = (getdate(age_as_on) - getdate(entry_date)).days or 0
index = None
for i, days in enumerate([first_range, second_range, third_range]):
for i, days in enumerate([first_range, second_range, third_range, fourth_range]):
if age <= days:
index = i
break
if index is None: index = 3
if index is None: index = 4
outstanding_range[index] = outstanding_amount
return [age] + outstanding_range

View File

@@ -44,6 +44,13 @@ frappe.query_reports["Accounts Receivable Summary"] = {
"default": "90",
"reqd": 1
},
{
"fieldname":"range4",
"label": __("Ageing Range 4"),
"fieldtype": "Int",
"default": "120",
"reqd": 1
},
{
"fieldname":"finance_book",
"label": __("Finance Book"),

View File

@@ -82,8 +82,15 @@ class AccountsReceivableSummary(ReceivablePayableReport):
"width": 160
},
{
"label": _(str(self.filters.range3) + _("-Above")),
"fieldname": scrub(str(self.filters.range3) + _("-Above")),
"label": _(str(self.filters.range3) + "-" + str(self.filters.range4)),
"fieldname": scrub(str(self.filters.range3) + "-" + str(self.filters.range4)),
"fieldtype": "Currency",
"options": "currency",
"width": 160
},
{
"label": _(str(self.filters.range4) + _("-Above")),
"fieldname": scrub(str(self.filters.range4) + _("-Above")),
"fieldtype": "Currency",
"options": "currency",
"width": 160
@@ -152,7 +159,7 @@ class AccountsReceivableSummary(ReceivablePayableReport):
row += [
party_dict.invoiced_amt, paid_amt, party_dict.credit_amt, party_dict.outstanding_amt,
party_dict.range1, party_dict.range2, party_dict.range3, party_dict.range4,
party_dict.range1, party_dict.range2, party_dict.range3, party_dict.range4, party_dict.range5
]
if args.get("party_type") == "Customer":
@@ -178,6 +185,7 @@ class AccountsReceivableSummary(ReceivablePayableReport):
"range2": 0,
"range3": 0,
"range4": 0,
"range5": 0,
"sales_person": []
})
)
@@ -209,7 +217,7 @@ class AccountsReceivableSummary(ReceivablePayableReport):
cols += ["bill_no", "bill_date"]
cols += ["invoiced_amt", "paid_amt", "credit_amt",
"outstanding_amt", "age", "range1", "range2", "range3", "range4", "currency", "pdc/lc_date", "pdc/lc_ref",
"outstanding_amt", "age", "range1", "range2", "range3", "range4", "range5", "currency", "pdc/lc_date", "pdc/lc_ref",
"pdc/lc_amount"]
if args.get("party_type") == "Supplier":

View File

@@ -39,7 +39,7 @@
</tr>
</thead>
<tbody>
{% for(var j=0, k=data.length; j<k; j++) { %}
{% for(var j=0, k=data.length-1; j<k; j++) { %}
{%
var row = data[j];
var row_class = data[j].parent_account ? "" : "financial-statements-important";

View File

@@ -321,7 +321,10 @@ def sort_accounts(accounts, is_root=False, key="name"):
"""Sort root types as Asset, Liability, Equity, Income, Expense"""
def compare_accounts(a, b):
if is_root:
if re.split('\W+', a[key])[0].isdigit():
# if chart of accounts is numbered, then sort by number
return cmp(a[key], b[key])
elif is_root:
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":
@@ -330,10 +333,6 @@ def sort_accounts(accounts, is_root=False, key="name"):
return -1
if a.root_type == "Income" and b.root_type == "Expense":
return -1
else:
if re.split('\W+', a[key])[0].isdigit():
# if chart of accounts is numbered, then sort by number
return cmp(a[key], b[key])
return 1
accounts.sort(key = functools.cmp_to_key(compare_accounts))

View File

@@ -33,7 +33,7 @@
</tr>
</thead>
<tbody>
{% for(var i=0, l=data.length; i<l; i++) { %}
{% for(var i=0, l=data.length-1; i<l; i++) { %}
<tr>
{% if(data[i].posting_date) { %}
<td>{%= frappe.datetime.str_to_user(data[i].posting_date) %}</td>

View File

@@ -211,6 +211,11 @@ frappe.query_reports["General Ledger"] = {
"label": __("Currency"),
"fieldtype": "Select",
"options": erpnext.get_presentation_currency_list()
},
{
"fieldname": "show_opening_entries",
"label": __("Show Opening Entries"),
"fieldtype": "Check"
}
]
}

View File

@@ -283,7 +283,8 @@ def get_accountwise_gle(filters, gl_entries, gle_map):
from_date, to_date = getdate(filters.from_date), getdate(filters.to_date)
for gle in gl_entries:
if gle.posting_date < from_date or cstr(gle.is_opening) == "Yes":
if (gle.posting_date < from_date or
(cstr(gle.is_opening) == "Yes" and not filters.get("show_opening_entries"))):
update_value_in_dict(gle_map[gle.get(group_by)].totals, 'opening', gle)
update_value_in_dict(totals, 'opening', gle)

View File

@@ -65,7 +65,7 @@ def get_columns(group_wise_columns, filters):
"warehouse": _("Warehouse") + ":Link/Warehouse",
"qty": _("Qty") + ":Float",
"base_rate": _("Avg. Selling Rate") + ":Currency/currency",
"buying_rate": _("Avg. Buying Rate") + ":Currency/currency",
"buying_rate": _("Valuation Rate") + ":Currency/currency",
"base_amount": _("Selling Amount") + ":Currency/currency",
"buying_amount": _("Buying Amount") + ":Currency/currency",
"gross_profit": _("Gross Profit") + ":Currency/currency",

View File

@@ -2,8 +2,15 @@
// For license information, please see license.txt
/* eslint-disable */
frappe.query_reports["Inactive Items"] = {
frappe.query_reports["Inactive Sales Items"] = {
"filters": [
{
fieldname: "territory",
label: __("Territory"),
fieldtype: "Link",
options: "Territory",
reqd: 1,
},
{
fieldname: "item",
label: __("Item"),

View File

@@ -1,21 +1,20 @@
{
"add_total_row": 0,
"creation": "2019-04-16 16:05:00.647308",
"creation": "2019-05-01 12:59:52.018850",
"disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"letter_head": "Test Letter Head 1",
"modified": "2019-04-16 16:06:33.630043",
"modified": "2019-05-01 13:00:26.545278",
"modified_by": "Administrator",
"module": "Stock",
"name": "Inactive Items",
"module": "Accounts",
"name": "Inactive Sales Items",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "Sales Invoice",
"report_name": "Inactive Items",
"report_name": "Inactive Sales Items",
"report_type": "Script Report",
"roles": [
{

View File

@@ -7,13 +7,11 @@ from frappe.utils import getdate, add_days, today, cint
from frappe import _
def execute(filters=None):
columns = get_columns()
data = get_data(filters)
return columns, data
def get_columns():
columns = [
{
"fieldname": "territory",
@@ -30,7 +28,7 @@ def get_columns():
"width": 150
},
{
"fieldname": "item_name",
"fieldname": "item",
"fieldtype": "Link",
"options": "Item",
"label": "Item",
@@ -74,59 +72,68 @@ def get_columns():
def get_data(filters):
data = []
items = get_items(filters)
territories = get_territories(filters)
sales_invoice_data = get_sales_details(filters)
for item in items:
if sales_invoice_data.get(item.name):
item_obj = sales_invoice_data[item.name]
if item_obj.days_since_last_order > cint(filters['days']):
row = {
"territory": item_obj.territory,
"item_group": item_obj.item_group,
"item": item_obj.name,
"item_name": item_obj.item_name,
"customer": item_obj.customer,
"last_order_date": item_obj.last_order_date,
"qty": item_obj.qty,
"days_since_last_order": item_obj.days_since_last_order
}
data.append(row)
else:
for territory in territories:
for item in items:
row = {
"territory": territory.name,
"item_group": item.item_group,
"item": item.name,
"item": item.item_code,
"item_name": item.item_name
}
if sales_invoice_data.get((territory.name,item.item_code)):
item_obj = sales_invoice_data[(territory.name,item.item_code)]
if item_obj.days_since_last_order > cint(filters['days']):
row.update({
"territory": item_obj.territory,
"customer": item_obj.customer,
"last_order_date": item_obj.last_order_date,
"qty": item_obj.qty,
"days_since_last_order": item_obj.days_since_last_order
})
else:
continue
data.append(row)
return data
def get_sales_details(filters):
data = []
item_details_map = {}
date_field = "s.transaction_date" if filters["based_on"] == "Sales Order" else "s.posting_date"
sales_data = frappe.db.sql("""
select s.territory, s.customer, si.item_group, si.item_name, si.qty, {date_field} as last_order_date,
select s.territory, s.customer, si.item_group, si.item_code, si.qty, {date_field} as last_order_date,
DATEDIFF(CURDATE(), {date_field}) as days_since_last_order
from `tab{doctype}` s, `tab{doctype} Item` si
where s.name = si.parent and s.docstatus = 1
group by si.name order by days_since_last_order """ #nosec
order by days_since_last_order """ #nosec
.format(date_field = date_field, doctype = filters['based_on']), as_dict=1)
for d in sales_data:
item_details_map.setdefault(d.item_name, d)
item_details_map.setdefault((d.territory,d.item_code), d)
return item_details_map
def get_items(filters):
def get_territories(filters):
filter_dict = {}
if filters.get("territory"):
filter_dict.update({'name': filters['territory']})
territories = frappe.get_all("Territory", fields=["name"], filters=filter_dict)
return territories
def get_items(filters):
filters_dict = {
"disabled": 0,
"is_stock_item": 1
@@ -142,7 +149,6 @@ def get_items(filters):
"name": filters["item"]
})
items = frappe.get_all("Item", fields=["name", "item_group", "item_name"], filters=filters_dict, order_by="name")
items = frappe.get_all("Item", fields=["name", "item_group", "item_name", "item_code"], filters=filters_dict, order_by="name")
return items

View File

@@ -102,7 +102,9 @@ def get_conditions(filters):
("customer", " and `tabSales Invoice`.customer = %(customer)s"),
("item_code", " and `tabSales Invoice Item`.item_code = %(item_code)s"),
("from_date", " and `tabSales Invoice`.posting_date>=%(from_date)s"),
("to_date", " and `tabSales Invoice`.posting_date<=%(to_date)s")):
("to_date", " and `tabSales Invoice`.posting_date<=%(to_date)s"),
("company_gstin", " and `tabSales Invoice`.company_gstin = %(company_gstin)s"),
("invoice_type", " and `tabSales Invoice`.invoice_type = %(invoice_type)s")):
if filters.get(opts[0]):
conditions += opts[1]

View File

@@ -18,22 +18,22 @@ def execute(filters=None):
data = []
for d in entries:
invoice = invoice_details.get(d.against_voucher) or frappe._dict()
if d.reference_type=="Purchase Invoice":
payment_amount = flt(d.debit) or -1 * flt(d.credit)
else:
payment_amount = flt(d.credit) or -1 * flt(d.debit)
row = [d.voucher_type, d.voucher_no, d.party_type, d.party, d.posting_date, d.against_voucher,
row = [d.voucher_type, d.voucher_no, d.party_type, d.party, d.posting_date, d.against_voucher,
invoice.posting_date, invoice.due_date, d.debit, d.credit, d.remarks]
if d.against_voucher:
row += get_ageing_data(30, 60, 90, d.posting_date, invoice.posting_date, payment_amount)
row += get_ageing_data(30, 60, 90, 120, d.posting_date, invoice.posting_date, payment_amount)
else:
row += ["", "", "", "", ""]
if invoice.due_date:
row.append((getdate(d.posting_date) - getdate(invoice.due_date)).days or 0)
data.append(row)
return columns, data
@@ -48,19 +48,19 @@ def get_columns(filters):
return [
_("Payment Document") + ":: 100",
_("Payment Entry") + ":Dynamic Link/"+_("Payment Document")+":140",
_("Party Type") + "::100",
_("Party Type") + "::100",
_("Party") + ":Dynamic Link/Party Type:140",
_("Posting Date") + ":Date:100",
_("Invoice") + (":Link/Purchase Invoice:130" if filters.get("payment_type") == "Outgoing" else ":Link/Sales Invoice:130"),
_("Invoice Posting Date") + ":Date:130",
_("Payment Due Date") + ":Date:130",
_("Debit") + ":Currency:120",
_("Invoice Posting Date") + ":Date:130",
_("Payment Due Date") + ":Date:130",
_("Debit") + ":Currency:120",
_("Credit") + ":Currency:120",
_("Remarks") + "::150",
_("Remarks") + "::150",
_("Age") +":Int:40",
"0-30:Currency:100",
"30-60:Currency:100",
"60-90:Currency:100",
"0-30:Currency:100",
"30-60:Currency:100",
"60-90:Currency:100",
_("90-Above") + ":Currency:100",
_("Delay in payment (Days)") + "::150"
]
@@ -79,21 +79,21 @@ def get_conditions(filters):
if filters.party:
conditions.append("party=%(party)s")
if filters.party_type:
conditions.append("against_voucher_type=%(reference_type)s")
filters["reference_type"] = "Sales Invoice" if filters.party_type=="Customer" else "Purchase Invoice"
if filters.get("from_date"):
conditions.append("posting_date >= %(from_date)s")
if filters.get("to_date"):
conditions.append("posting_date <= %(to_date)s")
return "and " + " and ".join(conditions) if conditions else ""
def get_entries(filters):
return frappe.db.sql("""select
return frappe.db.sql("""select
voucher_type, voucher_no, party_type, party, posting_date, debit, credit, remarks, against_voucher
from `tabGL Entry`
where company=%(company)s and voucher_type in ('Journal Entry', 'Payment Entry') {0}

View File

@@ -137,7 +137,7 @@ def get_appropriate_company(filters):
return company
@frappe.whitelist()
def get_invoiced_item_gross_margin(sales_invoice=None, item_code=None, company=None):
def get_invoiced_item_gross_margin(sales_invoice=None, item_code=None, company=None, with_item_data=False):
from erpnext.accounts.report.gross_profit.gross_profit import GrossProfitGenerator
sales_invoice = sales_invoice or frappe.form_dict.get('sales_invoice')
@@ -152,5 +152,8 @@ def get_invoiced_item_gross_margin(sales_invoice=None, item_code=None, company=N
}
gross_profit_data = GrossProfitGenerator(filters)
result = gross_profit_data.grouped_data
if not with_item_data:
result = sum([d.gross_profit for d in result])
return gross_profit_data.grouped_data
return result

View File

@@ -333,6 +333,9 @@ def reconcile_against_document(args):
doc = frappe.get_doc(d.voucher_type, d.voucher_no)
doc.make_gl_entries(cancel = 0, adv_adj =1)
if d.voucher_type in ('Payment Entry', 'Journal Entry'):
doc.update_expense_claim()
def check_if_advance_entry_modified(args):
"""
check if there is already a voucher reference
@@ -375,9 +378,9 @@ def check_if_advance_entry_modified(args):
def validate_allocated_amount(args):
if args.get("allocated_amount") < 0:
throw(_("Allocated amount can not be negative"))
throw(_("Allocated amount cannot be negative"))
elif args.get("allocated_amount") > args.get("unadjusted_amount"):
throw(_("Allocated amount can not greater than unadjusted amount"))
throw(_("Allocated amount cannot be greater than unadjusted amount"))
def update_reference_in_journal_entry(d, jv_obj):
"""

View File

@@ -33,6 +33,14 @@ frappe.ui.form.on("Purchase Order", {
}
}
});
frm.set_query("expense_account", "items", function() {
return {
query: "erpnext.controllers.queries.get_expense_account",
filters: {'company': frm.doc.company}
}
});
},
refresh: function(frm) {

View File

@@ -8,7 +8,9 @@ import frappe.defaults
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
from frappe.utils import flt, add_days, nowdate, getdate
from erpnext.stock.doctype.item.test_item import make_item
from erpnext.buying.doctype.purchase_order.purchase_order import (make_purchase_receipt, make_purchase_invoice, make_rm_stock_entry as make_subcontract_transfer_entry)
from erpnext.buying.doctype.purchase_order.purchase_order \
import (make_purchase_receipt, make_purchase_invoice as make_pi_from_po, make_rm_stock_entry as make_subcontract_transfer_entry)
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice as make_pi_from_pr
from erpnext.stock.doctype.material_request.test_material_request import make_material_request
from erpnext.stock.doctype.material_request.material_request import make_purchase_order
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
@@ -62,7 +64,7 @@ class TestPurchaseOrder(unittest.TestCase):
frappe.db.set_value('Item', '_Test Item', 'tolerance', 50)
pi = make_purchase_invoice(po.name)
pi = make_pi_from_po(po.name)
pi.update_stock = 1
pi.items[0].qty = 12
pi.insert()
@@ -89,7 +91,7 @@ class TestPurchaseOrder(unittest.TestCase):
create_pr_against_po(po.name)
make_purchase_invoice(po.name)
make_pi_from_po(po.name)
existing_ordered_qty = get_ordered_qty()
existing_requested_qty = get_requested_qty()
@@ -108,14 +110,85 @@ class TestPurchaseOrder(unittest.TestCase):
self.assertEqual(po.get("items")[0].amount, 1400)
self.assertEqual(get_ordered_qty(), existing_ordered_qty + 3)
def test_update_qty(self):
po = create_purchase_order()
pr = make_pr_against_po(po.name, 2)
po.load_from_db()
self.assertEqual(po.get("items")[0].received_qty, 2)
# Check received_qty after making PI from PR without update_stock checked
pi1 = make_pi_from_pr(pr.name)
pi1.get("items")[0].qty = 2
pi1.insert()
pi1.submit()
po.load_from_db()
self.assertEqual(po.get("items")[0].received_qty, 2)
# Check received_qty after making PI from PO with update_stock checked
pi2 = make_pi_from_po(po.name)
pi2.set("update_stock", 1)
pi2.get("items")[0].qty = 3
pi2.insert()
pi2.submit()
po.load_from_db()
self.assertEqual(po.get("items")[0].received_qty, 5)
# Check received_qty after making PR from PO
pr = make_pr_against_po(po.name, 1)
po.load_from_db()
self.assertEqual(po.get("items")[0].received_qty, 6)
def test_return_against_purchase_order(self):
po = create_purchase_order()
pr = make_pr_against_po(po.name, 6)
po.load_from_db()
self.assertEqual(po.get("items")[0].received_qty, 6)
pi2 = make_pi_from_po(po.name)
pi2.set("update_stock", 1)
pi2.get("items")[0].qty = 3
pi2.insert()
pi2.submit()
po.load_from_db()
self.assertEqual(po.get("items")[0].received_qty, 9)
# Make return purchase receipt, purchase invoice and check quantity
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt \
import make_purchase_receipt as make_purchase_receipt_return
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice \
import make_purchase_invoice as make_purchase_invoice_return
pr1 = make_purchase_receipt_return(is_return=1, return_against=pr.name, qty=-3, do_not_submit=True)
pr1.items[0].purchase_order = po.name
pr1.items[0].purchase_order_item = po.items[0].name
pr1.submit()
pi1= make_purchase_invoice_return(is_return=1, return_against=pi2.name, qty=-1, update_stock=1, do_not_submit=True)
pi1.items[0].purchase_order = po.name
pi1.items[0].po_detail = po.items[0].name
pi1.submit()
po.load_from_db()
self.assertEqual(po.get("items")[0].received_qty, 5)
def test_make_purchase_invoice(self):
po = create_purchase_order(do_not_submit=True)
self.assertRaises(frappe.ValidationError, make_purchase_invoice, po.name)
self.assertRaises(frappe.ValidationError, make_pi_from_po, po.name)
po.submit()
pi = make_purchase_invoice(po.name)
pi = make_pi_from_po(po.name)
self.assertEqual(pi.doctype, "Purchase Invoice")
self.assertEqual(len(pi.get("items", [])), 1)
@@ -123,7 +196,7 @@ class TestPurchaseOrder(unittest.TestCase):
def test_make_purchase_invoice_with_terms(self):
po = create_purchase_order(do_not_save=True)
self.assertRaises(frappe.ValidationError, make_purchase_invoice, po.name)
self.assertRaises(frappe.ValidationError, make_pi_from_po, po.name)
po.update(
{"payment_terms_template": "_Test Payment Term Template"}
@@ -136,7 +209,7 @@ class TestPurchaseOrder(unittest.TestCase):
self.assertEqual(getdate(po.payment_schedule[0].due_date), getdate(po.transaction_date))
self.assertEqual(po.payment_schedule[1].payment_amount, 2500.0)
self.assertEqual(getdate(po.payment_schedule[1].due_date), add_days(getdate(po.transaction_date), 30))
pi = make_purchase_invoice(po.name)
pi = make_pi_from_po(po.name)
pi.save()
self.assertEqual(pi.doctype, "Purchase Invoice")
@@ -274,7 +347,7 @@ class TestPurchaseOrder(unittest.TestCase):
self.assertTrue(po.get('payment_schedule'))
pi = make_purchase_invoice(po.name)
pi = make_pi_from_po(po.name)
self.assertFalse(pi.get('payment_schedule'))
@@ -285,7 +358,7 @@ class TestPurchaseOrder(unittest.TestCase):
po.submit()
self.assertTrue(po.get('payment_schedule'))
pi = make_purchase_invoice(po.name)
pi = make_pi_from_po(po.name)
pi.insert()
self.assertTrue(pi.get('payment_schedule'))
@@ -365,7 +438,7 @@ class TestPurchaseOrder(unittest.TestCase):
self.assertEquals(bin7.reserved_qty_for_sub_contract, bin2.reserved_qty_for_sub_contract - 6)
# Make Purchase Invoice
pi = make_purchase_invoice(po.name)
pi = make_pi_from_po(po.name)
pi.update_stock = 1
pi.supplier_warehouse = "_Test Warehouse 1 - _TC"
pi.insert()
@@ -469,6 +542,13 @@ class TestPurchaseOrder(unittest.TestCase):
self.assertEquals(se_items, supplied_items)
update_backflush_based_on("BOM")
def make_pr_against_po(po, received_qty=0):
pr = make_purchase_receipt(po)
pr.get("items")[0].qty = received_qty or 5
pr.insert()
pr.submit()
return pr
def make_subcontracted_item(item_code):
from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom

View File

@@ -291,6 +291,12 @@ def get_data():
"name": "Customers Without Any Sales Transactions",
"doctype": "Customer"
},
{
"type": "report",
"is_query_report": True,
"name": "Sales Partners Commission",
"doctype": "Customer"
}
]
},
{

View File

@@ -237,6 +237,10 @@ class AccountsController(TransactionBase):
document_type = "{} Item".format(self.doctype)
parent_dict.update({"document_type": document_type})
# party_name field used for customer in quotation
if self.doctype == "Quotation" and self.quotation_to == "Customer" and parent_dict.get("party_name"):
parent_dict.update({"customer": parent_dict.get("party_name")})
for item in self.get("items"):
if item.get("item_code"):
args = parent_dict.copy()
@@ -344,7 +348,7 @@ class AccountsController(TransactionBase):
'fiscal_year': fiscal_year,
'voucher_type': self.doctype,
'voucher_no': self.name,
'remarks': self.get("remarks"),
'remarks': self.get("remarks") or self.get("remark"),
'debit': 0,
'credit': 0,
'debit_in_account_currency': 0,
@@ -763,6 +767,9 @@ class AccountsController(TransactionBase):
if self.doctype in ("Sales Invoice", "Purchase Invoice"):
grand_total = grand_total - flt(self.write_off_amount)
if self.get("total_advance"):
grand_total -= self.get("total_advance")
if not self.get("payment_schedule"):
if self.get("payment_terms_template"):
data = get_payment_terms(self.payment_terms_template, posting_date, grand_total)
@@ -808,6 +815,9 @@ class AccountsController(TransactionBase):
total = flt(total, self.precision("grand_total"))
grand_total = flt(self.get("rounded_total") or self.grand_total, self.precision('grand_total'))
if self.get("total_advance"):
grand_total -= self.get("total_advance")
if self.doctype in ("Sales Invoice", "Purchase Invoice"):
grand_total = grand_total - flt(self.write_off_amount)
if total != grand_total:

View File

@@ -287,14 +287,14 @@ def copy_attributes_to_variant(item, variant):
variant.set(field.fieldname, item.get(field.fieldname))
variant.variant_of = item.name
if 'description' in allow_fields:
variant.has_variants = 0
if 'description' not in allow_fields:
if not variant.description:
variant.description = ""
variant.description = ""
if item.variant_based_on=='Item Attribute':
if variant.attributes:
attributes_description = ""
attributes_description = item.description + " "
for d in variant.attributes:
attributes_description += "<div>" + d.attribute + ": " + cstr(d.attribute_value) + "</div>"

View File

@@ -254,11 +254,13 @@ def get_delivery_notes_to_be_billed(doctype, txt, searchfield, start, page_len,
and return_against in (select name from `tabDelivery Note` where per_billed < 100)
)
)
%(mcond)s order by `tabDelivery Note`.`%(key)s` asc
%(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,
"page_len": page_len,
"txt": "%(txt)s"
}, {"txt": ("%%%s%%" % txt)}, as_dict=as_dict)

View File

@@ -205,11 +205,14 @@ def get_already_returned_items(doc):
def make_return_doc(doctype, source_name, target_doc=None):
from frappe.model.mapper import get_mapped_doc
company = frappe.db.get_value("Delivery Note", source_name, "company")
default_warehouse_for_sales_return = frappe.db.get_value("Company", company, "default_warehouse_for_sales_return")
def set_missing_values(source, target):
doc = frappe.get_doc(target)
doc.is_return = 1
doc.return_against = source.name
doc.ignore_pricing_rule = 1
doc.set_warehouse = ""
if doctype == "Sales Invoice":
doc.is_pos = source.is_pos
@@ -277,12 +280,16 @@ def make_return_doc(doctype, source_name, target_doc=None):
target_doc.so_detail = source_doc.so_detail
target_doc.si_detail = source_doc.si_detail
target_doc.expense_account = source_doc.expense_account
if default_warehouse_for_sales_return:
target_doc.warehouse = default_warehouse_for_sales_return
elif doctype == "Sales Invoice":
target_doc.sales_order = source_doc.sales_order
target_doc.delivery_note = source_doc.delivery_note
target_doc.so_detail = source_doc.so_detail
target_doc.dn_detail = source_doc.dn_detail
target_doc.expense_account = source_doc.expense_account
if default_warehouse_for_sales_return:
target_doc.warehouse = default_warehouse_for_sales_return
def update_terms(source_doc, target_doc, source_parent):
target_doc.payment_amount = -source_doc.payment_amount

View File

@@ -34,8 +34,8 @@ status_map = {
],
"Sales Order": [
["Draft", None],
["To Deliver and Bill", "eval:self.per_delivered < 100 and self.per_billed < 100 and self.docstatus == 1 and self.order_type in ['Sales', 'Shopping Cart']"],
["To Bill", "eval:self.per_delivered == 100 or self.order_type == 'Maintenance' and self.per_billed < 100 and self.docstatus == 1"],
["To Deliver and Bill", "eval:self.per_delivered < 100 and self.per_billed < 100 and self.docstatus == 1"],
["To Bill", "eval:self.per_delivered == 100 and self.per_billed < 100 and self.docstatus == 1"],
["To Deliver", "eval:self.per_delivered < 100 and self.per_billed == 100 and self.docstatus == 1"],
["Completed", "eval:self.per_delivered == 100 and self.per_billed == 100 and self.docstatus == 1"],
["Completed", "eval:self.order_type == 'Maintenance' and self.per_billed == 100 and self.docstatus == 1"],
@@ -45,9 +45,9 @@ status_map = {
"Sales Invoice": [
["Draft", None],
["Submitted", "eval:self.docstatus==1"],
["Paid", "eval:self.outstanding_amount==0 and self.docstatus==1"],
["Return", "eval:self.is_return==1 and self.docstatus==1"],
["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})"],
["Credit Note Issued", "eval:self.outstanding_amount < 0 and self.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"],
@@ -55,9 +55,9 @@ status_map = {
"Purchase Invoice": [
["Draft", None],
["Submitted", "eval:self.docstatus==1"],
["Paid", "eval:self.outstanding_amount==0 and self.docstatus==1"],
["Return", "eval:self.is_return==1 and self.docstatus==1"],
["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})"],
["Debit Note Issued", "eval:self.outstanding_amount < 0 and self.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"],
@@ -316,19 +316,27 @@ class StatusUpdater(Document):
.format(frappe.db.escape(frappe.session.user))
def update_billing_status_for_zero_amount_refdoc(self, ref_dt):
ref_fieldname = ref_dt.lower().replace(" ", "_")
zero_amount_refdoc = []
all_zero_amount_refdoc = frappe.db.sql_list("""select name from `tab%s`
where docstatus=1 and base_net_total = 0""" % ref_dt)
ref_fieldname = frappe.scrub(ref_dt)
for item in self.get("items"):
if item.get(ref_fieldname) \
and item.get(ref_fieldname) in all_zero_amount_refdoc \
and item.get(ref_fieldname) not in zero_amount_refdoc:
zero_amount_refdoc.append(item.get(ref_fieldname))
ref_docs = [item.get(ref_fieldname) for item in (self.get('items') or []) if item.get(ref_fieldname)]
if not ref_docs:
return
if zero_amount_refdoc:
self.update_billing_status(zero_amount_refdoc, ref_dt, ref_fieldname)
zero_amount_refdocs = frappe.db.sql_list("""
SELECT
name
from
`tab{ref_dt}`
where
docstatus = 1
and base_net_total = 0
and name in %(ref_docs)s
""".format(ref_dt=ref_dt), {
'ref_docs': ref_docs
})
if zero_amount_refdocs:
self.update_billing_status(zero_amount_refdocs, ref_dt, ref_fieldname)
def update_billing_status(self, zero_amount_refdoc, ref_dt, ref_fieldname):
for ref_dn in zero_amount_refdoc:

View File

@@ -80,6 +80,7 @@ class StockController(AccountsController):
"cost_center": item_row.cost_center,
"remarks": self.get("remarks") or "Accounting Entry for Stock",
"debit": flt(sle.stock_value_difference, 2),
"is_opening": item_row.get("is_opening") or self.get("is_opening") or "No",
}, warehouse_account[sle.warehouse]["account_currency"]))
# to target warehouse / expense account
@@ -89,7 +90,8 @@ class StockController(AccountsController):
"cost_center": item_row.cost_center,
"remarks": self.get("remarks") or "Accounting Entry for Stock",
"credit": flt(sle.stock_value_difference, 2),
"project": item_row.get("project") or self.get("project")
"project": item_row.get("project") or self.get("project"),
"is_opening": item_row.get("is_opening") or self.get("is_opening") or "No"
}))
elif sle.warehouse not in warehouse_with_no_account:
warehouse_with_no_account.append(sle.warehouse)
@@ -123,8 +125,17 @@ class StockController(AccountsController):
def get_voucher_details(self, default_expense_account, default_cost_center, sle_map):
if self.doctype == "Stock Reconciliation":
return [frappe._dict({ "name": voucher_detail_no, "expense_account": default_expense_account,
"cost_center": default_cost_center }) for voucher_detail_no, sle in sle_map.items()]
reconciliation_purpose = frappe.db.get_value(self.doctype, self.name, "purpose")
is_opening = "Yes" if reconciliation_purpose == "Opening Stock" else "No"
details = []
for voucher_detail_no in sle_map:
details.append(frappe._dict({
"name": voucher_detail_no,
"expense_account": default_expense_account,
"cost_center": default_cost_center,
"is_opening": is_opening
}))
return details
else:
details = self.get("items")
@@ -351,10 +362,12 @@ class StockController(AccountsController):
frappe.throw(_("Row {0}: Quality Inspection rejected for item {1}")
.format(d.idx, d.item_code), QualityInspectionRejectedError)
elif qa_required :
frappe.msgprint(_("Quality Inspection required for Item {0}").format(d.item_code))
if self.docstatus==1:
raise QualityInspectionRequiredError
action = frappe.get_doc('Stock Settings').action_if_quality_inspection_is_not_submitted
if self.docstatus==1 and action == 'Stop':
frappe.throw(_("Quality Inspection required for Item {0} to submit").format(frappe.bold(d.item_code)),
exc=QualityInspectionRequiredError)
else:
frappe.msgprint(_("Create Quality Inspection for Item {0}").format(frappe.bold(d.item_code)))
def update_blanket_order(self):
blanket_orders = list(set([d.blanket_order for d in self.items if d.blanket_order]))

View File

@@ -390,7 +390,7 @@ class calculate_taxes_and_totals(object):
net_total += item.net_amount
# discount amount rounding loss adjustment if no taxes
if (not taxes or self.doc.apply_discount_on == "Net Total") \
if (self.doc.apply_discount_on == "Net Total" or not taxes or total_for_discount_amount==self.doc.net_total) \
and i == len(self.doc.get("items")) - 1:
discount_amount_loss = flt(self.doc.net_total - net_total - self.doc.discount_amount,
self.doc.precision("net_total"))
@@ -608,7 +608,7 @@ def get_itemised_tax_breakup_data(doc):
return itemised_tax, itemised_taxable_amount
def get_itemised_tax(taxes):
def get_itemised_tax(taxes, with_tax_account=False):
itemised_tax = {}
for tax in taxes:
if getattr(tax, "category", None) and tax.category=="Valuation":
@@ -633,6 +633,9 @@ def get_itemised_tax(taxes):
tax_amount = tax_amount
))
if with_tax_account:
itemised_tax[item_code][tax.description].tax_account = tax.account_head
return itemised_tax
def get_itemised_taxable_amount(items):

View File

@@ -5,79 +5,91 @@ frappe.provide("erpnext");
cur_frm.email_field = "email_id";
erpnext.LeadController = frappe.ui.form.Controller.extend({
setup: function() {
this.frm.fields_dict.customer.get_query = function(doc, cdt, cdn) {
return { query: "erpnext.controllers.queries.customer_query" } }
setup: function () {
this.frm.make_methods = {
'Quotation': () => erpnext.utils.create_new_doc('Quotation', {
'quotation_to': this.frm.doc.doctype,
'party_name': this.frm.doc.name
}),
'Opportunity': () => erpnext.utils.create_new_doc('Opportunity', {
'opportunity_from': this.frm.doc.doctype,
'party_name': this.frm.doc.name
})
}
this.frm.fields_dict.customer.get_query = function (doc, cdt, cdn) {
return { query: "erpnext.controllers.queries.customer_query" }
}
this.frm.toggle_reqd("lead_name", !this.frm.doc.organization_lead);
},
onload: function() {
if(cur_frm.fields_dict.lead_owner.df.options.match(/^User/)) {
cur_frm.fields_dict.lead_owner.get_query = function(doc, cdt, cdn) {
onload: function () {
if (cur_frm.fields_dict.lead_owner.df.options.match(/^User/)) {
cur_frm.fields_dict.lead_owner.get_query = function (doc, cdt, cdn) {
return { query: "frappe.core.doctype.user.user.user_query" }
}
}
if(cur_frm.fields_dict.contact_by.df.options.match(/^User/)) {
cur_frm.fields_dict.contact_by.get_query = function(doc, cdt, cdn) {
return { query: "frappe.core.doctype.user.user.user_query" } }
if (cur_frm.fields_dict.contact_by.df.options.match(/^User/)) {
cur_frm.fields_dict.contact_by.get_query = function (doc, cdt, cdn) {
return { query: "frappe.core.doctype.user.user.user_query" }
}
}
},
refresh: function() {
refresh: function () {
var doc = this.frm.doc;
erpnext.toggle_naming_series();
frappe.dynamic_link = {doc: doc, fieldname: 'name', doctype: 'Lead'}
frappe.dynamic_link = { doc: doc, fieldname: 'name', doctype: 'Lead' }
if(!doc.__islocal && doc.__onload && !doc.__onload.is_customer) {
if (!doc.__islocal && doc.__onload && !doc.__onload.is_customer) {
this.frm.add_custom_button(__("Customer"), this.create_customer, __("Make"));
this.frm.add_custom_button(__("Opportunity"), this.create_opportunity, __("Make"));
this.frm.add_custom_button(__("Quotation"), this.make_quotation, __("Make"));
}
if(!this.frm.doc.__islocal) {
if (!this.frm.doc.__islocal) {
frappe.contacts.render_address_and_contact(cur_frm);
} else {
frappe.contacts.clear_address_and_contact(cur_frm);
}
},
create_customer: function() {
create_customer: function () {
frappe.model.open_mapped_doc({
method: "erpnext.crm.doctype.lead.lead.make_customer",
frm: cur_frm
})
},
create_opportunity: function() {
create_opportunity: function () {
frappe.model.open_mapped_doc({
method: "erpnext.crm.doctype.lead.lead.make_opportunity",
frm: cur_frm
})
},
make_quotation: function() {
make_quotation: function () {
frappe.model.open_mapped_doc({
method: "erpnext.crm.doctype.lead.lead.make_quotation",
frm: cur_frm
})
},
organization_lead: function() {
if (this.frm.doc.organization_lead == 1) {
this.frm.set_df_property('company_name', 'reqd', 1);
} else {
this.frm.set_df_property('company_name', 'reqd', 0);
}
organization_lead: function () {
this.frm.toggle_reqd("lead_name", !this.frm.doc.organization_lead);
this.frm.toggle_reqd("company_name", this.frm.doc.organization_lead);
},
company_name: function() {
company_name: function () {
if (this.frm.doc.organization_lead == 1) {
this.frm.set_value("lead_name", this.frm.doc.company_name);
}
},
contact_date: function() {
contact_date: function () {
if (this.frm.doc.contact_date) {
let d = moment(this.frm.doc.contact_date);
d.add(1, "hours");
@@ -86,4 +98,4 @@ erpnext.LeadController = frappe.ui.form.Controller.extend({
}
});
$.extend(cur_frm.cscript, new erpnext.LeadController({frm: cur_frm}));
$.extend(cur_frm.cscript, new erpnext.LeadController({ frm: cur_frm }));

View File

@@ -141,7 +141,7 @@
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 1,
"reqd": 0,
"search_index": 1,
"set_only_once": 0,
"translatable": 0,
@@ -499,7 +499,7 @@
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Lead Owner",
"length": 0,
@@ -1389,7 +1389,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-10-02 09:12:23.415379",
"modified": "2019-05-10 03:22:57.283628",
"modified_by": "Administrator",
"module": "CRM",
"name": "Lead",

View File

@@ -102,14 +102,18 @@ class Lead(SellingController):
def has_lost_quotation(self):
return frappe.db.get_value("Quotation", {
"lead": self.name,
"party_name": self.name,
"docstatus": 1,
"status": "Lost"
})
def set_lead_name(self):
if not self.lead_name:
frappe.db.set_value("Lead", self.name, "lead_name", self.company_name)
# Check for leads being created through data import
if not self.company_name and not self.flags.ignore_mandatory:
frappe.throw(_("A Lead requires either a person's name or an organization's name"))
self.lead_name = self.company_name
@frappe.whitelist()
def make_customer(source_name, target_doc=None):
@@ -146,8 +150,8 @@ def make_opportunity(source_name, target_doc=None):
"doctype": "Opportunity",
"field_map": {
"campaign_name": "campaign",
"doctype": "enquiry_from",
"name": "lead",
"doctype": "opportunity_from",
"name": "party_name",
"lead_name": "contact_display",
"company_name": "customer_name",
"email_id": "contact_email",
@@ -163,7 +167,7 @@ def make_quotation(source_name, target_doc=None):
{"Lead": {
"doctype": "Quotation",
"field_map": {
"name": "lead"
"name": "party_name"
}
}}, target_doc)
target_doc.quotation_to = "Lead"
@@ -225,4 +229,4 @@ def make_lead_from_communication(communication, ignore_communication_links=False
lead_name = lead.name
link_communication_to_document(doc, "Lead", lead_name, ignore_communication_links)
return lead_name
return lead_name

View File

@@ -4,6 +4,13 @@ from frappe import _
def get_data():
return {
'fieldname': 'lead',
'non_standard_fieldnames': {
'Quotation': 'party_name',
'Opportunity': 'party_name'
},
'dynamic_links': {
'party_name': ['Lead', 'quotation_to']
},
'transactions': [
{
'items': ['Opportunity', 'Quotation']

View File

@@ -20,10 +20,22 @@ frappe.ui.form.on("Opportunity", {
});
},
onload_post_render: function(frm) {
frm.get_field("items").grid.set_multiple_add("item_code", "qty");
},
party_name: function(frm) {
frm.toggle_display("contact_info", frm.doc.party_name);
if (frm.doc.opportunity_from == "Customer") {
frm.trigger('set_contact_link');
erpnext.utils.get_party_details(frm);
} else if (frm.doc.opportunity_from == "Lead") {
erpnext.utils.map_current_doc({
method: "erpnext.crm.doctype.lead.lead.make_opportunity",
source_name: frm.doc.party_name,
frm: frm
});
}
},
@@ -82,9 +94,9 @@ frappe.ui.form.on("Opportunity", {
set_contact_link: function(frm) {
if(frm.doc.opportunity_from == "Customer" && frm.doc.party_name) {
frappe.dynamic_link = {doc: frm.doc, fieldname: 'customer', doctype: 'Customer'}
frappe.dynamic_link = {doc: frm.doc, fieldname: 'party_name', doctype: 'Customer'}
} else if(frm.doc.opportunity_from == "Lead" && frm.doc.party_name) {
frappe.dynamic_link = {doc: frm.doc, fieldname: 'lead', doctype: 'Lead'}
frappe.dynamic_link = {doc: frm.doc, fieldname: 'party_name', doctype: 'Lead'}
}
},
@@ -138,12 +150,14 @@ erpnext.crm.Opportunity = frappe.ui.form.Controller.extend({
};
});
$.each([["lead", "lead"],
["customer", "customer"],
["contact_person", "contact_query"]],
function(i, opts) {
me.frm.set_query(opts[0], erpnext.queries[opts[1]]);
});
me.frm.set_query('contact_person', erpnext.queries['contact_query'])
if (me.frm.doc.opportunity_from == "Lead") {
me.frm.set_query('party_name', erpnext.queries['lead']);
}
else if (me.frm.doc.opportunity_from == "Cuatomer") {
me.frm.set_query('party_name', erpnext.queries['customer']);
}
},
create_quotation: function() {
@@ -156,11 +170,6 @@ erpnext.crm.Opportunity = frappe.ui.form.Controller.extend({
$.extend(cur_frm.cscript, new erpnext.crm.Opportunity({frm: cur_frm}));
cur_frm.cscript.onload_post_render = function(doc, cdt, cdn) {
if(doc.opportunity_from == 'Lead' && doc.party_name)
cur_frm.cscript.lead(doc, cdt, cdn);
}
cur_frm.cscript.item_code = function(doc, cdt, cdn) {
var d = locals[cdt][cdn];
if (d.item_code) {
@@ -179,15 +188,6 @@ cur_frm.cscript.item_code = function(doc, cdt, cdn) {
}
}
cur_frm.cscript.lead = function(doc, cdt, cdn) {
cur_frm.toggle_display("contact_info", doc.party_name);
erpnext.utils.map_current_doc({
method: "erpnext.crm.doctype.lead.lead.make_opportunity",
source_name: cur_frm.doc.party_name,
frm: cur_frm
});
}
cur_frm.cscript['Declare Opportunity Lost'] = function() {
var dialog = new frappe.ui.Dialog({
title: __("Set as Lost"),

View File

@@ -99,7 +99,7 @@
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"in_standard_filter": 1,
"label": "Opportunity From",
"length": 0,
"no_copy": 0,
@@ -136,7 +136,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 1,
"label": "Customer/Lead",
"label": "Party",
"length": 0,
"no_copy": 0,
"oldfieldname": "customer",
@@ -878,7 +878,7 @@
"collapsible": 1,
"collapsible_depends_on": "next_contact_by",
"columns": 0,
"depends_on": "eval:doc.lead || doc.customer",
"depends_on": "eval:doc.party_name",
"fetch_if_empty": 0,
"fieldname": "contact_info",
"fieldtype": "Section Break",
@@ -912,7 +912,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.customer || doc.lead",
"depends_on": "eval:doc.party_name",
"fetch_if_empty": 0,
"fieldname": "customer_address",
"fieldtype": "Link",
@@ -1083,7 +1083,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.lead || doc.customer",
"depends_on": "eval:doc.party_name",
"fetch_if_empty": 0,
"fieldname": "contact_person",
"fieldtype": "Link",
@@ -1150,7 +1150,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.lead || doc.customer",
"depends_on": "eval:doc.party_name",
"fetch_if_empty": 0,
"fieldname": "contact_email",
"fieldtype": "Data",
@@ -1183,7 +1183,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.lead || doc.customer",
"depends_on": "eval:doc.party_name",
"fetch_if_empty": 0,
"fieldname": "contact_mobile",
"fieldtype": "Small Text",
@@ -1468,7 +1468,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2019-04-25 18:55:43.874656",
"modified": "2019-05-17 19:03:32.740910",
"modified_by": "Administrator",
"module": "CRM",
"name": "Opportunity",

View File

@@ -17,5 +17,13 @@ frappe.listview_settings['Opportunity'] = {
listview.page.add_menu_item(__("Set as Closed"), function() {
listview.call_for_selected_items(method, {"status": "Closed"});
});
listview.page.fields_dict.opportunity_from.get_query = function() {
return {
"filters": {
"name": ["in", ["Customer", "Lead"]],
}
};
};
}
};

View File

@@ -11,7 +11,7 @@ def execute(filters=None):
columns=get_columns("Campaign Name")
data=get_lead_data(filters or {}, "Campaign Name")
return columns, data
def get_columns(based_on):
return [
{
@@ -69,21 +69,21 @@ def get_columns(based_on):
"width": 100
}
]
def get_lead_data(filters, based_on):
based_on_field = frappe.scrub(based_on)
conditions = get_filter_conditions(filters)
lead_details = frappe.db.sql("""
select {based_on_field}, name
from `tabLead`
where {based_on_field} is not null and {based_on_field} != '' {conditions}
from `tabLead`
where {based_on_field} is not null and {based_on_field} != '' {conditions}
""".format(based_on_field=based_on_field, conditions=conditions), filters, as_dict=1)
lead_map = frappe._dict()
for d in lead_details:
lead_map.setdefault(d.get(based_on_field), []).append(d.name)
data = []
for based_on_value, leads in lead_map.items():
row = {
@@ -94,42 +94,42 @@ def get_lead_data(filters, based_on):
row["opp_count"] = get_lead_opp_count(leads)
row["order_count"] = get_quotation_ordered_count(leads)
row["order_value"] = get_order_amount(leads) or 0
row["opp_lead"] = flt(row["opp_count"]) / flt(row["lead_count"] or 1.0) * 100.0
row["quot_lead"] = flt(row["quot_count"]) / flt(row["lead_count"] or 1.0) * 100.0
row["order_quot"] = flt(row["order_count"]) / flt(row["quot_count"] or 1.0) * 100.0
data.append(row)
return data
def get_filter_conditions(filters):
conditions=""
if filters.from_date:
conditions += " and date(creation) >= %(from_date)s"
if filters.to_date:
conditions += " and date(creation) <= %(to_date)s"
return conditions
def get_lead_quotation_count(leads):
return frappe.db.sql("""select count(name) from `tabQuotation`
where lead in (%s)""" % ', '.join(["%s"]*len(leads)), tuple(leads))[0][0]
return frappe.db.sql("""select count(name) from `tabQuotation`
where quotation_to = 'Lead' and party_name in (%s)""" % ', '.join(["%s"]*len(leads)), tuple(leads))[0][0] #nosec
def get_lead_opp_count(leads):
return frappe.db.sql("""select count(name) from `tabOpportunity`
where lead in (%s)""" % ', '.join(["%s"]*len(leads)), tuple(leads))[0][0]
return frappe.db.sql("""select count(name) from `tabOpportunity`
where opportunity_from = 'Lead' and party_name in (%s)""" % ', '.join(["%s"]*len(leads)), tuple(leads))[0][0]
def get_quotation_ordered_count(leads):
return frappe.db.sql("""select count(name)
from `tabQuotation` where status = 'Ordered'
and lead in (%s)""" % ', '.join(["%s"]*len(leads)), tuple(leads))[0][0]
return frappe.db.sql("""select count(name)
from `tabQuotation` where status = 'Ordered' and quotation_to = 'Lead'
and party_name in (%s)""" % ', '.join(["%s"]*len(leads)), tuple(leads))[0][0]
def get_order_amount(leads):
return frappe.db.sql("""select sum(base_net_amount)
return frappe.db.sql("""select sum(base_net_amount)
from `tabSales Order Item`
where prevdoc_docname in (
select name from `tabQuotation` where status = 'Ordered'
and lead in (%s)
select name from `tabQuotation` where status = 'Ordered'
and quotation_to = 'Lead' and party_name in (%s)
)""" % ', '.join(["%s"]*len(leads)), tuple(leads))[0][0]

View File

@@ -11,16 +11,61 @@ def execute(filters=None):
columns=get_columns()
data=get_lead_data(filters, "Lead Owner")
return columns, data
def get_columns():
return [
_("Lead Owner") + ":Data:130",
_("Lead Count") + ":Int:80",
_("Opp Count") + ":Int:80",
_("Quot Count") + ":Int:80",
_("Order Count") + ":Int:100",
_("Order Value") + ":Float:100",
_("Opp/Lead %") + ":Float:100",
_("Quot/Lead %") + ":Float:100",
_("Order/Quot %") + ":Float:100"
{
"fieldname": "lead_owner",
"label": _("Lead Owner"),
"fieldtype": "Data",
"width": "130"
},
{
"fieldname": "lead_count",
"label": _("Lead Count"),
"fieldtype": "Int",
"width": "80"
},
{
"fieldname": "opp_count",
"label": _("Opp Count"),
"fieldtype": "Int",
"width": "80"
},
{
"fieldname": "quot_count",
"label": _("Quot Count"),
"fieldtype": "Int",
"width": "80"
},
{
"fieldname": "order_count",
"label": _("Order Count"),
"fieldtype": "Int",
"width": "100"
},
{
"fieldname": "order_value",
"label": _("Order Value"),
"fieldtype": "Float",
"width": "100"
},
{
"fieldname": "opp_lead",
"label": _("Opp/Lead %"),
"fieldtype": "Float",
"width": "100"
},
{
"fieldname": "quot_lead",
"label": _("Quot/Lead %"),
"fieldtype": "Float",
"width": "100"
},
{
"fieldname": "order_quot",
"label": _("Order/Quot %"),
"fieldtype": "Float",
"width": "100"
}
]

View File

@@ -35,14 +35,14 @@ def get_data(filters):
for lead in frappe.get_all('Lead', fields = ['name', 'lead_name', 'company_name'], filters=lead_filters):
data = frappe.db.sql("""
select
`tabCommunication`.reference_doctype, `tabCommunication`.reference_name,
select
`tabCommunication`.reference_doctype, `tabCommunication`.reference_name,
`tabCommunication`.content, `tabCommunication`.communication_date
from
from
(
(select name, lead from `tabOpportunity` where lead = %(lead)s)
union
(select name, lead from `tabQuotation` where lead = %(lead)s)
(select name, party_name as lead from `tabOpportunity` where opportunity_from='Lead' and party_name = %(lead)s)
union
(select name, party_name as lead from `tabQuotation` where quotation_to = 'Lead' and party_name = %(lead)s)
union
(select name, lead from `tabIssue` where lead = %(lead)s and status!='Closed')
union

View File

@@ -186,7 +186,7 @@ def link_item(item_data,item_status):
item.item_name = str(item_data.get("name"))
item.item_code = "woocommerce - " + str(item_data.get("product_id"))
item.woocommerce_id = str(item_data.get("product_id"))
item.item_group = "WooCommerce Products"
item.item_group = _("WooCommerce Products")
item.stock_uom = woocommerce_settings.uom or _("Nos")
item.save()
frappe.db.commit()

View File

@@ -107,10 +107,18 @@ def get_series():
def setup_custom_fields():
custom_fields = {
"Customer": [dict(fieldname='shopify_customer_id', label='Shopify Customer Id',
fieldtype='Data', insert_after='series', read_only=1, print_hide=1)],
"Address": [dict(fieldname='shopify_address_id', label='Shopify Address Id',
fieldtype='Data', insert_after='fax', read_only=1, print_hide=1)],
"Customer": [
dict(fieldname='shopify_customer_id', label='Shopify Customer Id',
fieldtype='Data', insert_after='series', read_only=1, print_hide=1)
],
"Supplier": [
dict(fieldname='shopify_supplier_id', label='Shopify Supplier Id',
fieldtype='Data', insert_after='supplier_name', read_only=1, print_hide=1)
],
"Address": [
dict(fieldname='shopify_address_id', label='Shopify Address Id',
fieldtype='Data', insert_after='fax', read_only=1, print_hide=1)
],
"Item": [
dict(fieldname='shopify_variant_id', label='Shopify Variant Id',
fieldtype='Data', insert_after='item_code', read_only=1, print_hide=1),
@@ -119,16 +127,20 @@ def setup_custom_fields():
dict(fieldname='shopify_description', label='Shopify Description',
fieldtype='Text Editor', insert_after='description', read_only=1, print_hide=1)
],
"Sales Order": [dict(fieldname='shopify_order_id', label='Shopify Order Id',
fieldtype='Data', insert_after='title', read_only=1, print_hide=1)],
"Sales Order": [
dict(fieldname='shopify_order_id', label='Shopify Order Id',
fieldtype='Data', insert_after='title', read_only=1, print_hide=1)
],
"Delivery Note":[
dict(fieldname='shopify_order_id', label='Shopify Order Id',
fieldtype='Data', insert_after='title', read_only=1, print_hide=1),
dict(fieldname='shopify_fulfillment_id', label='Shopify Fulfillment Id',
fieldtype='Data', insert_after='title', read_only=1, print_hide=1)
],
"Sales Invoice": [dict(fieldname='shopify_order_id', label='Shopify Order Id',
fieldtype='Data', insert_after='title', read_only=1, print_hide=1)]
"Sales Invoice": [
dict(fieldname='shopify_order_id', label='Shopify Order Id',
fieldtype='Data', insert_after='title', read_only=1, print_hide=1)
]
}
create_custom_fields(custom_fields)

View File

@@ -4,8 +4,8 @@
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.utils.nestedset import get_root_of
from frappe.model.document import Document
from six.moves.urllib.parse import urlparse
@@ -62,10 +62,10 @@ class WoocommerceSettings(Document):
custom.read_only = 1
custom.save()
if not frappe.get_value("Item Group",{"name": "WooCommerce Products"}):
if not frappe.get_value("Item Group",{"name": _("WooCommerce Products")}):
item_group = frappe.new_doc("Item Group")
item_group.item_group_name = "WooCommerce Products"
item_group.parent_item_group = _("All Item Groups")
item_group.item_group_name = _("WooCommerce Products")
item_group.parent_item_group = get_root_of("Item Group")
item_group.save()
@@ -83,7 +83,7 @@ class WoocommerceSettings(Document):
for name in email_names:
frappe.delete_doc("Custom Field",name)
frappe.delete_doc("Item Group","WooCommerce Products")
frappe.delete_doc("Item Group", _("WooCommerce Products"))
frappe.db.commit()

View File

@@ -300,11 +300,15 @@ def insert_lab_test_to_medical_record(doc):
elif doc.special_test_items:
item = doc.special_test_items[0]
table_row = item.lab_test_particulars +" "+ item.result_value
if item.lab_test_particulars and item.result_value:
table_row = item.lab_test_particulars +" "+ item.result_value
elif doc.sensitivity_test_items:
item = doc.sensitivity_test_items[0]
table_row = item.antibiotic +" "+ item.antibiotic_sensitivity
if item.antibiotic and item.antibiotic_sensitivity:
table_row = item.antibiotic +" "+ item.antibiotic_sensitivity
if table_row:
subject += "<br/>"+table_row

View File

@@ -82,7 +82,7 @@ def get_healthcare_services_to_invoice(patient):
'service': service_item, 'rate': practitioner_charge,
'income_account': income_account})
lab_tests = frappe.get_list("Lab Test", {'patient': patient.name, 'invoiced': False})
lab_tests = frappe.get_list("Lab Test", {'patient': patient.name, 'invoiced': False, 'docstatus': 1})
if lab_tests:
for lab_test in lab_tests:
lab_test_obj = frappe.get_doc("Lab Test", lab_test['name'])

View File

@@ -13,8 +13,6 @@ source_link = "https://github.com/frappe/erpnext"
develop_version = '12.x.x-develop'
error_report_email = "support@erpnext.com"
app_include_js = "assets/js/erpnext.min.js"
app_include_css = "assets/css/erpnext.css"
web_include_js = "assets/js/erpnext-web.min.js"

View File

@@ -3,13 +3,8 @@
frappe.ui.form.on('Additional Salary', {
setup: function(frm) {
frm.set_query("salary_component", function() {
return {
filters: {
is_additional_component: true
}
};
});
frm.add_fetch("salary_component", "deduct_full_tax_on_selected_payroll_date", "deduct_full_tax_on_selected_payroll_date");
frm.set_query("employee", function() {
return {
filters: {

View File

@@ -1,5 +1,6 @@
{
"allow_copy": 0,
"allow_events_in_timeline": 0,
"allow_guest_to_view": 0,
"allow_import": 1,
"allow_rename": 0,
@@ -21,6 +22,7 @@
"collapsible": 0,
"columns": 0,
"default": "",
"fetch_if_empty": 0,
"fieldname": "naming_series",
"fieldtype": "Select",
"hidden": 0,
@@ -54,6 +56,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "employee",
"fieldtype": "Link",
"hidden": 0,
@@ -87,6 +90,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "salary_component",
"fieldtype": "Link",
"hidden": 0,
@@ -120,6 +124,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "amount",
"fieldtype": "Currency",
"hidden": 0,
@@ -153,6 +158,7 @@
"collapsible": 0,
"columns": 0,
"default": "1",
"fetch_if_empty": 0,
"fieldname": "overwrite_salary_structure_amount",
"fieldtype": "Check",
"hidden": 0,
@@ -185,6 +191,40 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "deduct_full_tax_on_selected_payroll_date",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Deduct Full Tax on Selected Payroll Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_5",
"fieldtype": "Column Break",
"hidden": 0,
@@ -217,6 +257,8 @@
"collapsible": 0,
"columns": 0,
"default": "",
"description": "Date on which this component is applied",
"fetch_if_empty": 0,
"fieldname": "payroll_date",
"fieldtype": "Date",
"hidden": 0,
@@ -250,6 +292,7 @@
"collapsible": 0,
"columns": 0,
"fetch_from": "employee.employee_name",
"fetch_if_empty": 0,
"fieldname": "employee_name",
"fieldtype": "Data",
"hidden": 0,
@@ -284,6 +327,7 @@
"collapsible": 0,
"columns": 0,
"fetch_from": "employee.department",
"fetch_if_empty": 0,
"fieldname": "department",
"fieldtype": "Link",
"hidden": 0,
@@ -317,6 +361,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "company",
"fieldtype": "Link",
"hidden": 0,
@@ -350,6 +395,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "salary_slip",
"fieldtype": "Link",
"hidden": 0,
@@ -384,6 +430,7 @@
"collapsible": 0,
"columns": 0,
"fetch_from": "salary_component.type",
"fetch_if_empty": 0,
"fieldname": "type",
"fieldtype": "Data",
"hidden": 0,
@@ -417,6 +464,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "amended_from",
"fieldtype": "Link",
"hidden": 0,
@@ -453,7 +501,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-08-21 14:44:22.921926",
"modified": "2019-05-09 19:53:37.475839",
"modified_by": "Administrator",
"module": "HR",
"name": "Additional Salary",

View File

@@ -9,6 +9,12 @@ from frappe import _
from frappe.utils import getdate, date_diff
class AdditionalSalary(Document):
def before_insert(self):
if frappe.db.exists("Additional Salary", {"employee": self.employee, "salary_component": self.salary_component,
"amount": self.amount, "payroll_date": self.payroll_date, "company": self.company}):
frappe.throw(_("Additional Salary Component Exists."))
def validate(self):
self.validate_dates()
if self.amount < 0:
@@ -35,7 +41,8 @@ class AdditionalSalary(Document):
@frappe.whitelist()
def get_additional_salary_component(employee, start_date, end_date):
additional_components = frappe.db.sql("""
select salary_component, sum(amount) as amount, overwrite_salary_structure_amount from `tabAdditional Salary`
select salary_component, sum(amount) as amount, overwrite_salary_structure_amount, deduct_full_tax_on_selected_payroll_date
from `tabAdditional Salary`
where employee=%(employee)s
and docstatus = 1
and payroll_date between %(from_date)s and %(to_date)s
@@ -48,16 +55,20 @@ def get_additional_salary_component(employee, start_date, end_date):
}, as_dict=1)
additional_components_list = []
component_fields = ["depends_on_payment_days", "salary_component_abbr", "is_tax_applicable", "variable_based_on_taxable_salary", 'type']
for d in additional_components:
component = frappe.get_doc("Salary Component", d.salary_component)
struct_row = {'salary_component': d.salary_component}
for field in ["depends_on_payment_days", "abbr", "is_tax_applicable", "variable_based_on_taxable_salary", "is_additional_component"]:
struct_row[field] = component.get(field)
struct_row = frappe._dict({'salary_component': d.salary_component})
component = frappe.get_all("Salary Component", filters={'name': d.salary_component}, fields=component_fields)
if component:
struct_row.update(component[0])
additional_components_list.append({
struct_row['deduct_full_tax_on_selected_payroll_date'] = d.deduct_full_tax_on_selected_payroll_date
struct_row['is_additional_component'] = 1
additional_components_list.append(frappe._dict({
'amount': d.amount,
'type': component.type,
'type': component[0].type,
'struct_row': struct_row,
'overwrite': d.overwrite_salary_structure_amount
})
'overwrite': d.overwrite_salary_structure_amount,
}))
return additional_components_list

View File

@@ -3,13 +3,18 @@
frappe.ui.form.on('Employee Benefit Application', {
setup: function(frm) {
frm.set_query("earning_component", "employee_benefits", function() {
return {
query : "erpnext.hr.doctype.employee_benefit_application.employee_benefit_application.get_earning_components",
filters: {date: frm.doc.date, employee: frm.doc.employee}
};
});
if(!frm.doc.employee || !frm.doc.date) {
frappe.throw(__("Please select Employee and Date first"));
} else {
frm.set_query("earning_component", "employee_benefits", function() {
return {
query : "erpnext.hr.doctype.employee_benefit_application.employee_benefit_application.get_earning_components",
filters: {date: frm.doc.date, employee: frm.doc.employee}
};
});
}
},
employee: function(frm) {
var method, args;
if(frm.doc.employee && frm.doc.date && frm.doc.payroll_period){

View File

@@ -7,7 +7,7 @@ import frappe
from frappe import _
from frappe.utils import date_diff, getdate, rounded, add_days, cstr, cint, flt
from frappe.model.document import Document
from erpnext.hr.doctype.payroll_period.payroll_period import get_payroll_period_days
from erpnext.hr.doctype.payroll_period.payroll_period import get_payroll_period_days, get_period_factor
from erpnext.hr.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure
from erpnext.hr.utils import get_sal_slip_total_benefit_given, get_holidays_for_employee, get_previous_claimed_amount
@@ -131,7 +131,7 @@ def get_max_benefits_remaining(employee, on_date, payroll_period):
salary_component = frappe.get_doc("Salary Component", sal_struct_row.salary_component)
if salary_component.depends_on_payment_days == 1 and salary_component.pay_against_benefit_claim != 1:
have_depends_on_payment_days = True
benefit_amount = get_benefit_pro_rata_ratio_amount(sal_struct, salary_component.max_benefit_amount)
benefit_amount = get_benefit_amount_based_on_pro_rata(sal_struct, salary_component.max_benefit_amount)
amount_per_day = benefit_amount / payroll_period_days
per_day_amount_total += amount_per_day
@@ -167,16 +167,14 @@ def calculate_lwp(employee, start_date, holidays, working_days):
lwp = cint(leave[0][1]) and (lwp + 0.5) or (lwp + 1)
return lwp
def get_benefit_component_amount(employee, start_date, end_date, struct_row, sal_struct, period_length, frequency):
payroll_period, period_factor, actual_payroll_days = get_payroll_period_days(start_date, end_date, employee)
def get_benefit_component_amount(employee, start_date, end_date, salary_component, sal_struct, payroll_frequency, payroll_period):
if not payroll_period:
frappe.msgprint(_("Start and end dates not in a valid Payroll Period, cannot calculate {0}.")
.format(struct_row.salary_component))
frappe.msgprint(_("Start and end dates not in a valid Payroll Period, cannot calculate {0}")
.format(salary_component))
return False
# Considering there is only one application for a year
benefit_application_name = frappe.db.sql("""
benefit_application = frappe.db.sql("""
select name
from `tabEmployee Benefit Application`
where
@@ -185,69 +183,58 @@ def get_benefit_component_amount(employee, start_date, end_date, struct_row, sal
and docstatus = 1
""", {
'employee': employee,
'payroll_period': payroll_period
'payroll_period': payroll_period.name
})
if frappe.db.get_value("Salary Component", struct_row.salary_component, "depends_on_payment_days") != 1:
if frequency == "Monthly" and actual_payroll_days in range(360, 370):
period_length = 1
period_factor = 12
current_benefit_amount = 0.0
component_max_benefit, depends_on_payment_days = frappe.db.get_value("Salary Component",
salary_component, ["max_benefit_amount", "depends_on_payment_days"])
if period_factor:
# If there is application for benefit then fetch the amount from the application.
# else Split the max benefits to the pro-rata components with the ratio of their max_benefit_amount
if benefit_application_name:
benefit_application = frappe.get_doc("Employee Benefit Application", benefit_application_name[0][0])
return get_benefit_amount(benefit_application, struct_row, period_factor, period_length)
if benefit_application:
benefit_amount = frappe.db.get_value("Employee Benefit Application Detail",
{"parent": benefit_application[0][0], "earning_component": salary_component}, "amount")
elif component_max_benefit:
benefit_amount = get_benefit_amount_based_on_pro_rata(sal_struct, component_max_benefit)
# TODO: Check if there is benefit claim for employee then pro-rata divide the rest of amount (Late Benefit Application)
else:
component_max = frappe.db.get_value("Salary Component", struct_row.salary_component, "max_benefit_amount")
if component_max:
benefit_amount = get_benefit_pro_rata_ratio_amount(sal_struct, component_max)
return get_amount(period_factor, benefit_amount, period_length)
return False
current_benefit_amount = 0
if benefit_amount:
total_sub_periods = get_period_factor(employee,
start_date, end_date, payroll_frequency, payroll_period, depends_on_payment_days)[0]
def get_benefit_pro_rata_ratio_amount(sal_struct, component_max):
total_pro_rata_max = 0
current_benefit_amount = benefit_amount / total_sub_periods
return current_benefit_amount
def get_benefit_amount_based_on_pro_rata(sal_struct, component_max_benefit):
max_benefits_total = 0
benefit_amount = 0
for sal_struct_row in sal_struct.get("earnings"):
pay_against_benefit_claim, max_benefit_amount = frappe.db.get_value("Salary Component",
sal_struct_row.salary_component, ["pay_against_benefit_claim", "max_benefit_amount"])
if sal_struct_row.is_flexible_benefit == 1 and pay_against_benefit_claim != 1:
total_pro_rata_max += max_benefit_amount
if total_pro_rata_max > 0:
benefit_amount = component_max * sal_struct.max_benefits / total_pro_rata_max
if benefit_amount > component_max:
benefit_amount = component_max
for d in sal_struct.get("earnings"):
if d.is_flexible_benefit == 1:
component = frappe.db.get_value("Salary Component", d.salary_component, ["max_benefit_amount", "pay_against_benefit_claim"], as_dict=1)
if not component.pay_against_benefit_claim:
max_benefits_total += component.max_benefit_amount
if max_benefits_total > 0:
benefit_amount = sal_struct.max_benefits * component.max_benefit_amount / max_benefits_total
if benefit_amount > component_max_benefit:
benefit_amount = component_max_benefit
return benefit_amount
def get_benefit_amount(application, struct_row, period_factor, period_length):
amount = 0
for employee_benefit in application.employee_benefits:
if employee_benefit.earning_component == struct_row.salary_component:
amount += get_amount(period_factor, employee_benefit.amount, period_length)
return amount if amount > 0 else False
def get_amount(period_factor, amount, period_length):
amount_per_day = amount / period_factor
total_amount = amount_per_day * period_length
return total_amount
def get_earning_components(doctype, txt, searchfield, start, page_len, filters):
if len(filters) < 2:
return {}
employee = filters['employee']
date = filters['date']
salary_structure = get_assigned_salary_structure(employee, date)
salary_structure = get_assigned_salary_structure(filters['employee'], filters['date'])
if salary_structure:
query = """select salary_component from `tabSalary Detail` where parent = '{salary_structure}'
and is_flexible_benefit = 1
order by name"""
return frappe.db.sql(query.format(**{
"salary_structure": salary_structure
}))
return {}
return frappe.db.sql("""
select salary_component
from `tabSalary Detail`
where parent = %s and is_flexible_benefit = 1
order by name
""", salary_structure)
else:
frappe.throw(_("Salary Structure not found for employee {0} and date {1}")
.format(filters['employee'], filters['date']))

View File

@@ -5,9 +5,11 @@
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.utils import flt
from frappe.model.document import Document
from erpnext.hr.doctype.employee_benefit_application.employee_benefit_application import get_max_benefits
from erpnext.hr.utils import get_payroll_period, get_previous_claimed_amount
from erpnext.hr.utils import get_previous_claimed_amount
from erpnext.hr.doctype.payroll_period.payroll_period import get_payroll_period
from erpnext.hr.doctype.salary_structure_assignment.salary_structure_assignment import get_assigned_salary_structure
class EmployeeBenefitClaim(Document):
@@ -97,31 +99,28 @@ def get_benefit_pro_rata_ratio_amount(employee, on_date, sal_struct):
benefit_amount_total += benefit_amount
return benefit_amount_total
def get_benefit_claim_amount(employee, start_date, end_date, salary_component):
query = """select claimed_amount from `tabEmployee Benefit Claim`
where employee=%(employee)s
and docstatus = 1 and pay_against_benefit_claim = 1
def get_benefit_claim_amount(employee, start_date, end_date, salary_component=None):
query = """
select sum(claimed_amount)
from `tabEmployee Benefit Claim`
where
employee=%(employee)s
and docstatus = 1
and pay_against_benefit_claim = 1
and claim_date between %(start_date)s and %(end_date)s
"""
if not start_date:
query += "and claim_date <= %(end_date)s"
else:
query += "and (claim_date between %(start_date)s and %(end_date)s)"
if salary_component:
query += "and earning_component = %(earning_component)s"
query += " and earning_component = %(earning_component)s"
benefit_claim_details = frappe.db.sql(query, {
claimed_amount = flt(frappe.db.sql(query, {
'employee': employee,
'start_date': start_date,
'end_date': end_date,
'earning_component': salary_component
}, as_dict = True)
if benefit_claim_details:
claimed_amount = 0
for claim_detail in benefit_claim_details:
claimed_amount += claim_detail.claimed_amount
return claimed_amount
return False
})[0][0])
return claimed_amount
def get_total_benefit_dispensed(employee, sal_struct, sal_slip_start_date, payroll_period):
pro_rata_amount = 0
@@ -140,11 +139,11 @@ def get_total_benefit_dispensed(employee, sal_struct, sal_slip_start_date, payro
else:
pro_rata_amount = get_benefit_pro_rata_ratio_amount(employee, sal_slip_start_date, sal_struct)
claimed_amount += get_benefit_claim_amount(employee, payroll_period.start_date, payroll_period.end_date, False)
claimed_amount += get_benefit_claim_amount(employee, payroll_period.start_date, payroll_period.end_date)
return claimed_amount + pro_rata_amount
def get_last_payroll_period_benefits(employee, sal_slip_start_date, sal_slip_end_date, current_flexi_amount, payroll_period, sal_struct):
def get_last_payroll_period_benefits(employee, sal_slip_start_date, sal_slip_end_date, payroll_period, sal_struct):
max_benefits = get_max_benefits(employee, payroll_period.end_date)
if not max_benefits:
max_benefits = 0

View File

@@ -446,6 +446,72 @@
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "other_incomes_section",
"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,
"label": "Other Incomes",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "income_from_other_sources",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Income From Other Sources",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
@@ -458,7 +524,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2019-04-25 16:38:05.847925",
"modified": "2019-05-11 16:13:50.472670",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee Tax Exemption Declaration",

View File

@@ -25,7 +25,8 @@ class EmployeeTaxExemptionDeclaration(Document):
filters = {
"employee": self.employee,
"payroll_period": self.payroll_period,
"name": ["!=", self.name]
"name": ["!=", self.name],
"docstatus": ["!=", 2]
}
)
if duplicate:

View File

@@ -448,6 +448,72 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "other_incomes_section",
"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,
"label": "Other Incomes",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "income_from_other_sources",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Income From Other Sources",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
@@ -558,7 +624,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2019-04-25 17:06:36.569549",
"modified": "2019-05-13 12:17:18.045171",
"modified_by": "Administrator",
"module": "HR",
"name": "Employee Tax Exemption Proof Submission",

View File

@@ -219,7 +219,8 @@ frappe.ui.form.on("Expense Claim", {
frm.fields_dict["cost_center"].get_query = function() {
return {
filters: {
"company": frm.doc.company
"company": frm.doc.company,
"is_group": 0
}
};
};
@@ -230,7 +231,9 @@ frappe.ui.form.on("Expense Claim", {
return {
filters: {
"report_type": "Balance Sheet",
"account_type": "Payable"
"account_type": "Payable",
"company": frm.doc.company,
"is_group": 0
}
};
};

View File

@@ -0,0 +1,20 @@
from __future__ import unicode_literals
from frappe import _
def get_data():
return {
'fieldname': 'reference_name',
'internal_links': {
'Employee Advance': ['advances', 'employee_advance']
},
'transactions': [
{
'label': _('Payment'),
'items': ['Payment Entry', 'Journal Entry']
},
{
'label': _('Reference'),
'items': ['Employee Advance']
},
]
}

View File

@@ -79,6 +79,7 @@ def get_events(start, end, filters=None):
filters.append(['Holiday', 'holiday_date', '>', getdate(start)])
if end:
filters.append(['Holiday', 'holiday_date', '<', getdate(end)])
return frappe.get_list('Holiday List',
fields=['name', '`tabHoliday`.holiday_date', '`tabHoliday`.description', '`tabHoliday List`.color'],
filters = filters,

View File

@@ -3,8 +3,8 @@
frappe.views.calendar["Holiday List"] = {
field_map: {
"start": "from_date",
"end": "to_date",
"start": "holiday_date",
"end": "holiday_date",
"id": "name",
"title": "description",
"allDay": "allDay"

View File

@@ -146,7 +146,7 @@ frappe.ui.form.on('Loan', {
'payment_date': payment.payment_date,
'principal_amount': payment.principal_amount,
'interest_amount': payment.interest_amount,
'total_payment': payment.total_payment
'total_payment': payment.total_payment
});
dialog.fields_dict.payments.grid.refresh();
$(dialog.wrapper.find(".grid-buttons")).hide();
@@ -172,18 +172,20 @@ frappe.ui.form.on('Loan', {
},
mode_of_payment: function (frm) {
frappe.call({
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.get_bank_cash_account",
args: {
"mode_of_payment": frm.doc.mode_of_payment,
"company": frm.doc.company
},
callback: function (r, rt) {
if (r.message) {
frm.set_value("payment_account", r.message.account);
if (frm.doc.mode_of_payment && frm.doc.company) {
frappe.call({
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.get_bank_cash_account",
args: {
"mode_of_payment": frm.doc.mode_of_payment,
"company": frm.doc.company
},
callback: function (r, rt) {
if (r.message) {
frm.set_value("payment_account", r.message.account);
}
}
}
});
});
}
},
loan_application: function (frm) {

View File

@@ -539,14 +539,15 @@ def submit_salary_slips_for_employees(payroll_entry, salary_slips, publish_progr
payroll_entry.email_salary_slip(submitted_ss)
payroll_entry.db_set("salary_slips_submitted", 1)
payroll_entry.notify_update()
payroll_entry.db_set("salary_slips_submitted", 1)
payroll_entry.notify_update()
if not submitted_ss and not not_submitted_ss:
frappe.msgprint(_("No salary slip found to submit for the above selected criteria OR salary slip already submitted"))
if not_submitted_ss:
frappe.msgprint(_("Could not submit some Salary Slips"))
def get_payroll_entries_for_jv(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql("""
select name from `tabPayroll Entry`

View File

@@ -5,7 +5,7 @@
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.utils import date_diff, getdate, formatdate, cint
from frappe.utils import date_diff, getdate, formatdate, cint, month_diff, flt
from frappe.model.document import Document
from erpnext.hr.utils import get_holidays_for_employee
@@ -48,12 +48,13 @@ class PayrollPeriod(Document):
def get_payroll_period_days(start_date, end_date, employee):
company = frappe.db.get_value("Employee", employee, "company")
payroll_period = frappe.db.sql("""
select name, start_date, end_date from `tabPayroll Period`
where company=%(company)s
and (
(%(start_date)s between start_date and end_date)
and (%(end_date)s between start_date and end_date)
)""", {
select name, start_date, end_date
from `tabPayroll Period`
where
company=%(company)s
and %(start_date)s between start_date and end_date
and %(end_date)s between start_date and end_date
""", {
'company': company,
'start_date': start_date,
'end_date': end_date
@@ -67,3 +68,38 @@ def get_payroll_period_days(start_date, end_date, employee):
working_days -= len(holidays)
return payroll_period[0][0], working_days, actual_no_of_days
return False, False, False
def get_payroll_period(from_date, to_date, company):
payroll_period = frappe.db.sql("""
select name, start_date, end_date
from `tabPayroll Period`
where start_date<=%s and end_date>= %s and company=%s
""", (from_date, to_date, company), as_dict=1)
return payroll_period[0] if payroll_period else None
def get_period_factor(employee, start_date, end_date, payroll_frequency, payroll_period, depends_on_payment_days=0):
# TODO if both deduct checked update the factor to make tax consistent
period_start, period_end = payroll_period.start_date, payroll_period.end_date
joining_date, relieving_date = frappe.db.get_value("Employee", employee, ["date_of_joining", "relieving_date"])
if getdate(joining_date) > getdate(period_start):
period_start = joining_date
if relieving_date and getdate(relieving_date) < getdate(period_end):
period_end = relieving_date
total_sub_periods, remaining_sub_periods = 0.0, 0.0
if payroll_frequency == "Monthly" and not depends_on_payment_days:
total_sub_periods = month_diff(payroll_period.end_date, payroll_period.start_date)
remaining_sub_periods = month_diff(period_end, start_date)
else:
salary_days = date_diff(end_date, start_date) + 1
days_in_payroll_period = date_diff(payroll_period.end_date, payroll_period.start_date) + 1
total_sub_periods = flt(days_in_payroll_period) / flt(salary_days)
remaining_days_in_payroll_period = date_diff(period_end, start_date) + 1
remaining_sub_periods = flt(remaining_days_in_payroll_period) / flt(salary_days)
return total_sub_periods, remaining_sub_periods

View File

@@ -24,14 +24,6 @@ frappe.ui.form.on('Salary Component', {
is_flexible_benefit: function(frm) {
if(frm.doc.is_flexible_benefit){
set_value_for_condition_and_formula(frm);
frm.set_value("is_additional_component", 0);
frm.set_value("formula", '');
frm.set_value("amount", 0);
}
},
is_additional_component: function(frm) {
if(frm.doc.is_additional_component){
frm.set_value("is_flexible_benefit", 0);
frm.set_value("formula", '');
frm.set_value("amount", 0);
}

View File

@@ -122,18 +122,17 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.is_flexible_benefit != 1",
"fetch_if_empty": 0,
"fieldname": "is_additional_component",
"fieldtype": "Check",
"fieldname": "description",
"fieldtype": "Small Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_list_view": 1,
"in_standard_filter": 0,
"label": "Is Additional Component",
"label": "Description",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -156,11 +155,9 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "1",
"depends_on": "eval:doc.type == \"Earning\"",
"fetch_if_empty": 0,
"fieldname": "is_tax_applicable",
"fieldtype": "Check",
"fieldname": "column_break_4",
"fieldtype": "Column Break",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@@ -168,7 +165,6 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Is Tax Applicable",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -259,8 +255,10 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "1",
"depends_on": "eval:doc.type == \"Earning\"",
"fetch_if_empty": 0,
"fieldname": "do_not_include_in_total",
"fieldname": "is_tax_applicable",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
@@ -269,7 +267,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Do not include in total",
"label": "Is Tax Applicable",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -292,40 +290,9 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "is_tax_applicable",
"fetch_if_empty": 0,
"fieldname": "column_break_4",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "disabled",
"fieldname": "deduct_full_tax_on_selected_payroll_date",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
@@ -334,7 +301,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Disabled",
"label": "Deduct Full Tax on Selected Payroll Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -358,16 +325,16 @@
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "description",
"fieldtype": "Small Text",
"fieldname": "round_to_the_nearest_integer",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 1,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Description",
"label": "Round to the Nearest Integer",
"length": 0,
"no_copy": 0,
"permlevel": 0,
@@ -424,7 +391,73 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.type==\"Earning\" && doc.is_additional_component != 1 && doc.statistical_component!=1",
"fetch_if_empty": 0,
"fieldname": "do_not_include_in_total",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Do Not Include in Total",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "disabled",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Disabled",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.type==\"Earning\" && doc.statistical_component!=1",
"fetch_if_empty": 0,
"fieldname": "flexible_benefits",
"fieldtype": "Section Break",
@@ -458,7 +491,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.is_additional_component != 1",
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "is_flexible_benefit",
"fieldtype": "Check",
@@ -855,6 +888,40 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.amount_based_on_formula!==1",
"fetch_if_empty": 0,
"fieldname": "amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Amount",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
@@ -896,7 +963,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.amount_based_on_formula!==0",
"depends_on": "amount_based_on_formula",
"fetch_if_empty": 0,
"fieldname": "formula",
"fieldtype": "Code",
@@ -923,40 +990,6 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"depends_on": "eval:doc.amount_based_on_formula!==1",
"fetch_if_empty": 0,
"fieldname": "amount",
"fieldtype": "Currency",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Amount",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 0,
"print_hide_if_no_value": 0,
"read_only": 0,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
@@ -1035,7 +1068,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2019-04-16 19:08:55.323567",
"modified": "2019-06-05 11:19:15.926608",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Component",

View File

@@ -35,7 +35,6 @@
"salary_component": "Leave Encashment",
"type": "Earning",
"is_payable": 1,
"is_tax_applicable": 1,
"is_additional_component": 1
"is_tax_applicable": 1
}
]

View File

@@ -19,7 +19,5 @@ def create_salary_component(component_name, **args):
"salary_component": component_name,
"type": args.get("type") or "Earning",
"is_payable": args.get("is_payable") or 1,
"is_tax_applicable": args.get("is_tax_applicable") or 1,
"is_additional_component": args.get("is_additional_component") or 1
"is_tax_applicable": args.get("is_tax_applicable") or 1
}).insert()

View File

@@ -218,41 +218,6 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "",
"fetch_from": "salary_component.is_additional_component",
"fetch_if_empty": 0,
"fieldname": "is_additional_component",
"fieldtype": "Check",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Is Additional Component",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
@@ -323,6 +288,39 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "deduct_full_tax_on_selected_payroll_date",
"fieldtype": "Check",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Deduct Full Tax on Selected Payroll Date",
"length": 0,
"no_copy": 0,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
@@ -566,6 +564,41 @@
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "",
"fetch_from": "",
"fetch_if_empty": 0,
"fieldname": "additional_amount",
"fieldtype": "Currency",
"hidden": 1,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Additional Amount",
"length": 0,
"no_copy": 1,
"permlevel": 0,
"precision": "",
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
@@ -713,7 +746,7 @@
"issingle": 0,
"istable": 1,
"max_attachments": 0,
"modified": "2019-04-16 19:09:31.726597",
"modified": "2019-05-11 17:33:08.508653",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Detail",

View File

@@ -11,7 +11,7 @@ frappe.ui.form.on("Salary Slip", {
{fieldname: 'salary_component', columns: 6},
{fieldname: 'amount', columns: 4}
];
})
});
frm.fields_dict["timesheets"].grid.get_field("time_sheet").get_query = function(){
return {
@@ -19,31 +19,39 @@ frappe.ui.form.on("Salary Slip", {
employee: frm.doc.employee
}
}
}
};
frm.set_query("salary_component", "earnings", function() {
return {
filters: {
type: "earning"
}
}
})
});
frm.set_query("salary_component", "deductions", function() {
return {
filters: {
type: "deduction"
}
}
})
});
frm.set_query("employee", function() {
return{
query: "erpnext.controllers.queries.employee_query"
}
});
},
start_date: function(frm, dt, dn){
start_date: function(frm){
if(frm.doc.start_date){
frm.trigger("set_end_date");
}
},
end_date: function(frm, dt, dn) {
get_emp_and_leave_details(frm.doc, dt, dn);
end_date: function(frm) {
frm.events.get_emp_and_leave_details(frm);
},
set_end_date: function(frm){
@@ -70,43 +78,54 @@ frappe.ui.form.on("Salary Slip", {
refresh: function(frm) {
frm.trigger("toggle_fields")
frm.trigger("toggle_reqd_fields")
var salary_detail_fields = ["formula", "abbr", "statistical_component", "is_tax_applicable",
"is_flexible_benefit", "variable_based_on_taxable_salary", "is_additional_component"]
var salary_detail_fields = ["formula", "abbr", "statistical_component", "variable_based_on_taxable_salary"];
cur_frm.fields_dict['earnings'].grid.set_column_disp(salary_detail_fields,false);
cur_frm.fields_dict['deductions'].grid.set_column_disp(salary_detail_fields,false);
},
salary_slip_based_on_timesheet: function(frm, dt, dn) {
salary_slip_based_on_timesheet: function(frm) {
frm.trigger("toggle_fields");
get_emp_and_leave_details(frm.doc, dt, dn);
frm.events.get_emp_and_leave_details(frm);
},
payroll_frequency: function(frm, dt, dn) {
payroll_frequency: function(frm) {
frm.trigger("toggle_fields");
frm.set_value('end_date', '');
},
employee: function(frm, dt, dn) {
get_emp_and_leave_details(frm.doc, dt, dn);
employee: function(frm) {
frm.events.get_emp_and_leave_details(frm);
},
leave_without_pay: function(frm){
if (frm.doc.employee && frm.doc.start_date && frm.doc.end_date) {
return frappe.call({
method: 'process_salary_based_on_leave',
doc: frm.doc,
args: {"lwp": frm.doc.leave_without_pay},
callback: function(r, rt) {
frm.refresh();
}
});
}
},
toggle_fields: function(frm) {
frm.toggle_display(['hourly_wages', 'timesheets'],
cint(frm.doc.salary_slip_based_on_timesheet)==1);
frm.toggle_display(['hourly_wages', 'timesheets'], cint(frm.doc.salary_slip_based_on_timesheet)===1);
frm.toggle_display(['payment_days', 'total_working_days', 'leave_without_pay'],
frm.doc.payroll_frequency!="");
}
})
frappe.ui.form.on('Salary Detail', {
earnings_remove: function(frm, dt, dn) {
calculate_all(frm.doc, dt, dn);
},
deductions_remove: function(frm, dt, dn) {
calculate_all(frm.doc, dt, dn);
get_emp_and_leave_details: function(frm) {
return frappe.call({
method: 'get_emp_and_leave_details',
doc: frm.doc,
callback: function(r, rt) {
frm.refresh();
}
});
}
})
@@ -119,133 +138,29 @@ frappe.ui.form.on('Salary Slip Timesheet', {
}
});
// Get leave details
//---------------------------------------------------------------------
var get_emp_and_leave_details = function(doc, dt, dn) {
return frappe.call({
method: 'get_emp_and_leave_details',
doc: locals[dt][dn],
callback: function(r, rt) {
cur_frm.refresh();
calculate_all(doc, dt, dn);
}
});
}
cur_frm.cscript.leave_without_pay = function(doc,dt,dn){
if (doc.employee && doc.start_date && doc.end_date) {
return $c_obj(doc, 'get_leave_details', {"lwp": doc.leave_without_pay}, function(r, rt) {
var doc = locals[dt][dn];
cur_frm.refresh();
calculate_all(doc, dt, dn);
});
}
}
var calculate_all = function(doc, dt, dn) {
calculate_earning_total(doc, dt, dn);
calculate_ded_total(doc, dt, dn);
calculate_net_pay(doc, dt, dn);
}
cur_frm.cscript.amount = function(doc,dt,dn){
var child = locals[dt][dn];
if(!doc.salary_structure){
frappe.model.set_value(dt,dn, "default_amount", child.amount)
}
calculate_all(doc, dt, dn);
}
cur_frm.cscript.depends_on_payment_days = function(doc,dt,dn){
calculate_earning_total(doc, dt, dn, true);
calculate_ded_total(doc, dt, dn, true);
calculate_net_pay(doc, dt, dn);
refresh_many(['amount','gross_pay', 'rounded_total', 'net_pay', 'loan_repayment']);
};
// Calculate earning total
// ------------------------------------------------------------------------
var calculate_earning_total = function(doc, dt, dn, reset_amount) {
var tbl = doc.earnings || [];
var total_earn = 0;
for(var i = 0; i < tbl.length; i++){
if(cint(tbl[i].depends_on_payment_days) == 1) {
tbl[i].amount = Math.round(tbl[i].default_amount)*(flt(doc.payment_days) /
cint(doc.total_working_days)*100)/100;
} else if(reset_amount && tbl[i].default_amount) {
tbl[i].amount = tbl[i].default_amount;
}
if(!tbl[i].do_not_include_in_total) {
total_earn += flt(tbl[i].amount);
}
}
doc.gross_pay = total_earn;
refresh_many(['earnings', 'amount','gross_pay']);
}
// Calculate deduction total
// ------------------------------------------------------------------------
var calculate_ded_total = function(doc, dt, dn, reset_amount) {
var tbl = doc.deductions || [];
var total_ded = 0;
for(var i = 0; i < tbl.length; i++){
if(cint(tbl[i].depends_on_payment_days) == 1) {
tbl[i].amount = Math.round(tbl[i].default_amount)*(flt(doc.payment_days)/cint(doc.total_working_days)*100)/100;
} else if(reset_amount && tbl[i].default_amount) {
tbl[i].amount = tbl[i].default_amount;
}
if(!tbl[i].do_not_include_in_total) {
total_ded += flt(tbl[i].amount);
}
}
doc.total_deduction = total_ded;
refresh_many(['deductions', 'total_deduction']);
}
var calculate_net_pay = function(doc, dt, dn) {
doc.net_pay = flt(doc.gross_pay) - flt(doc.total_deduction);
doc.rounded_total = Math.round(doc.net_pay);
refresh_many(['net_pay', 'rounded_total']);
}
cur_frm.cscript.validate = function(doc, dt, dn) {
calculate_all(doc, dt, dn);
}
cur_frm.fields_dict.employee.get_query = function(doc,cdt,cdn) {
return{
query: "erpnext.controllers.queries.employee_query"
}
}
// calculate total working hours, earnings based on hourly wages and totals
// ------------------------------------------------------------------------
var total_work_hours = function(frm, dt, dn) {
frm.set_value('total_working_hours', 0);
var total_working_hours = 0.0;
$.each(frm.doc["timesheets"] || [], function(i, timesheet) {
frm.doc.total_working_hours += timesheet.working_hours;
total_working_hours += timesheet.working_hours;
});
frm.refresh_field('total_working_hours');
frm.set_value('total_working_hours', total_working_hours);
var wages_amount = frm.doc.total_working_hours * frm.doc.hour_rate;
frappe.db.get_value('Salary Structure', {'name': frm.doc.salary_structure}, 'salary_component', (r) => {
frm.set_value('gross_pay', 0);
var gross_pay = 0.0;
$.each(frm.doc["earnings"], function(i, earning) {
if (earning.salary_component == r.salary_component) {
earning.amount = wages_amount;
frm.refresh_fields('earnings');
}
frm.doc.gross_pay += earning.amount;
gross_pay += earning.amount;
});
frm.set_value('gross_pay', gross_pay);
frm.refresh_field('gross_pay');
calculate_net_pay(frm.doc, dt, dn);
frm.doc.net_pay = flt(frm.doc.gross_pay) - flt(frm.doc.total_deduction);
frm.doc.rounded_total = Math.round(frm.doc.net_pay);
refresh_many(['net_pay', 'rounded_total']);
});
}

View File

@@ -21,6 +21,7 @@
"collapsible": 0,
"columns": 0,
"default": "Today",
"fetch_if_empty": 0,
"fieldname": "posting_date",
"fieldtype": "Date",
"hidden": 0,
@@ -53,6 +54,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "employee",
"fieldtype": "Link",
"hidden": 0,
@@ -88,6 +90,7 @@
"collapsible": 0,
"columns": 0,
"fetch_from": "employee.employee_name",
"fetch_if_empty": 0,
"fieldname": "employee_name",
"fieldtype": "Read Only",
"hidden": 0,
@@ -123,6 +126,7 @@
"collapsible": 0,
"columns": 0,
"fetch_from": "employee.department",
"fetch_if_empty": 0,
"fieldname": "department",
"fieldtype": "Link",
"hidden": 0,
@@ -159,6 +163,7 @@
"columns": 0,
"depends_on": "eval:doc.designation",
"fetch_from": "employee.designation",
"fetch_if_empty": 0,
"fieldname": "designation",
"fieldtype": "Read Only",
"hidden": 0,
@@ -194,6 +199,7 @@
"collapsible": 0,
"columns": 0,
"fetch_from": "employee.branch",
"fetch_if_empty": 0,
"fieldname": "branch",
"fieldtype": "Link",
"hidden": 0,
@@ -228,6 +234,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break1",
"fieldtype": "Column Break",
"hidden": 0,
@@ -260,6 +267,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "status",
"fieldtype": "Select",
"hidden": 0,
@@ -293,6 +301,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "journal_entry",
"fieldtype": "Link",
"hidden": 0,
@@ -326,6 +335,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "payroll_entry",
"fieldtype": "Link",
"hidden": 0,
@@ -359,6 +369,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "company",
"fieldtype": "Link",
"hidden": 0,
@@ -391,6 +402,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "letter_head",
"fieldtype": "Link",
"hidden": 0,
@@ -423,6 +435,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "section_break_10",
"fieldtype": "Section Break",
"hidden": 0,
@@ -455,6 +468,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "salary_slip_based_on_timesheet",
"fieldtype": "Check",
"hidden": 0,
@@ -489,6 +503,7 @@
"collapsible": 0,
"columns": 0,
"default": "",
"fetch_if_empty": 0,
"fieldname": "start_date",
"fieldtype": "Date",
"hidden": 0,
@@ -523,6 +538,7 @@
"columns": 0,
"default": "",
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "end_date",
"fieldtype": "Date",
"hidden": 0,
@@ -555,6 +571,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_15",
"fieldtype": "Column Break",
"hidden": 0,
@@ -587,6 +604,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "salary_structure",
"fieldtype": "Link",
"hidden": 0,
@@ -622,6 +640,7 @@
"columns": 0,
"default": "",
"depends_on": "eval:(!doc.salary_slip_based_on_timesheet)",
"fetch_if_empty": 0,
"fieldname": "payroll_frequency",
"fieldtype": "Select",
"hidden": 0,
@@ -656,6 +675,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "total_working_days",
"fieldtype": "Float",
"hidden": 0,
@@ -690,6 +710,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "leave_without_pay",
"fieldtype": "Float",
"hidden": 0,
@@ -724,6 +745,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "payment_days",
"fieldtype": "Float",
"hidden": 0,
@@ -758,6 +780,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "hourly_wages",
"fieldtype": "Section Break",
"hidden": 0,
@@ -791,6 +814,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "timesheets",
"fieldtype": "Table",
"hidden": 0,
@@ -824,6 +848,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_20",
"fieldtype": "Column Break",
"hidden": 0,
@@ -855,6 +880,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "total_working_hours",
"fieldtype": "Float",
"hidden": 0,
@@ -887,6 +913,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "hour_rate",
"fieldtype": "Currency",
"hidden": 0,
@@ -921,6 +948,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "section_break_26",
"fieldtype": "Section Break",
"hidden": 0,
@@ -953,6 +981,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "bank_name",
"fieldtype": "Data",
"hidden": 0,
@@ -986,6 +1015,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "bank_account_no",
"fieldtype": "Data",
"hidden": 0,
@@ -1019,71 +1049,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "column_break_01",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "amended_from",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Amended From",
"length": 0,
"no_copy": 1,
"oldfieldname": "amended_from",
"oldfieldtype": "Data",
"options": "Salary Slip",
"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,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "section_break_32",
"fieldtype": "Section Break",
"hidden": 0,
@@ -1115,6 +1081,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "deduct_tax_for_unclaimed_employee_benefits",
"fieldtype": "Check",
"hidden": 0,
@@ -1147,6 +1114,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "deduct_tax_for_unsubmitted_tax_exemption_proof",
"fieldtype": "Check",
"hidden": 0,
@@ -1179,6 +1147,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "earning_deduction",
"fieldtype": "Section Break",
"hidden": 0,
@@ -1211,6 +1180,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "earning",
"fieldtype": "Column Break",
"hidden": 0,
@@ -1245,6 +1215,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "",
"fetch_if_empty": 0,
"fieldname": "earnings",
"fieldtype": "Table",
"hidden": 0,
@@ -1279,6 +1250,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "deduction",
"fieldtype": "Column Break",
"hidden": 0,
@@ -1312,6 +1284,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "deductions",
"fieldtype": "Table",
"hidden": 0,
@@ -1346,6 +1319,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "totals",
"fieldtype": "Section Break",
"hidden": 0,
@@ -1378,6 +1352,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "gross_pay",
"fieldtype": "Currency",
"hidden": 0,
@@ -1412,6 +1387,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_25",
"fieldtype": "Column Break",
"hidden": 0,
@@ -1442,6 +1418,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "total_deduction",
"fieldtype": "Currency",
"hidden": 0,
@@ -1477,6 +1454,7 @@
"collapsible": 0,
"columns": 0,
"depends_on": "total_loan_repayment",
"fetch_if_empty": 0,
"fieldname": "loan_repayment",
"fieldtype": "Section Break",
"hidden": 0,
@@ -1509,6 +1487,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "loans",
"fieldtype": "Table",
"hidden": 0,
@@ -1542,6 +1521,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "section_break_43",
"fieldtype": "Section Break",
"hidden": 0,
@@ -1574,6 +1554,7 @@
"collapsible": 0,
"columns": 0,
"default": "0",
"fetch_if_empty": 0,
"fieldname": "total_principal_amount",
"fieldtype": "Currency",
"hidden": 0,
@@ -1608,6 +1589,7 @@
"collapsible": 0,
"columns": 0,
"default": "0",
"fetch_if_empty": 0,
"fieldname": "total_interest_amount",
"fieldtype": "Currency",
"hidden": 0,
@@ -1641,6 +1623,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_45",
"fieldtype": "Column Break",
"hidden": 0,
@@ -1673,6 +1656,7 @@
"collapsible": 0,
"columns": 0,
"default": "0",
"fetch_if_empty": 0,
"fieldname": "total_loan_repayment",
"fieldtype": "Currency",
"hidden": 0,
@@ -1706,6 +1690,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "net_pay_info",
"fieldtype": "Section Break",
"hidden": 0,
@@ -1739,6 +1724,7 @@
"collapsible": 0,
"columns": 0,
"description": "Gross Pay - Total Deduction - Loan Repayment",
"fetch_if_empty": 0,
"fieldname": "net_pay",
"fieldtype": "Currency",
"hidden": 0,
@@ -1773,6 +1759,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "column_break_53",
"fieldtype": "Column Break",
"hidden": 0,
@@ -1804,6 +1791,7 @@
"bold": 1,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "rounded_total",
"fieldtype": "Currency",
"hidden": 0,
@@ -1836,6 +1824,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "section_break_55",
"fieldtype": "Section Break",
"hidden": 0,
@@ -1868,6 +1857,7 @@
"collapsible": 0,
"columns": 0,
"description": "Net Pay (in words) will be visible once you save the Salary Slip.",
"fetch_if_empty": 0,
"fieldname": "total_in_words",
"fieldtype": "Data",
"hidden": 0,
@@ -1893,6 +1883,41 @@
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "amended_from",
"fieldtype": "Link",
"hidden": 0,
"ignore_user_permissions": 1,
"ignore_xss_filter": 0,
"in_filter": 0,
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
"label": "Amended From",
"length": 0,
"no_copy": 1,
"oldfieldname": "amended_from",
"oldfieldtype": "Data",
"options": "Salary Slip",
"permlevel": 0,
"print_hide": 1,
"print_hide_if_no_value": 0,
"read_only": 1,
"remember_last_selected_value": 0,
"report_hide": 0,
"reqd": 0,
"search_index": 0,
"set_only_once": 0,
"translatable": 0,
"unique": 0
}
],
"has_web_view": 0,
@@ -1906,7 +1931,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2019-02-18 18:54:36.161027",
"modified": "2019-05-13 13:10:14.524119",
"modified_by": "Administrator",
"module": "HR",
"name": "Salary Slip",

File diff suppressed because it is too large Load Diff

View File

@@ -45,12 +45,12 @@ class TestSalarySlip(unittest.TestCase):
self.assertEqual(ss.total_working_days, no_of_days[0])
self.assertEqual(ss.payment_days, no_of_days[0])
self.assertEqual(ss.earnings[0].amount, 25000)
self.assertEqual(ss.earnings[0].amount, 50000)
self.assertEqual(ss.earnings[1].amount, 3000)
self.assertEqual(ss.deductions[0].amount, 5000)
self.assertEqual(ss.deductions[1].amount, 5000)
self.assertEqual(ss.gross_pay, 40500)
self.assertEqual(ss.net_pay, 29918)
self.assertEqual(ss.gross_pay, 78000)
self.assertEqual(ss.net_pay, 67418.0)
def test_salary_slip_with_holidays_excluded(self):
no_of_days = self.get_no_of_days()
@@ -64,13 +64,13 @@ class TestSalarySlip(unittest.TestCase):
self.assertEqual(ss.total_working_days, no_of_days[0] - no_of_days[1])
self.assertEqual(ss.payment_days, no_of_days[0] - no_of_days[1])
self.assertEqual(ss.earnings[0].amount, 25000)
self.assertEqual(ss.earnings[0].default_amount, 25000)
self.assertEqual(ss.earnings[0].amount, 50000)
self.assertEqual(ss.earnings[0].default_amount, 50000)
self.assertEqual(ss.earnings[1].amount, 3000)
self.assertEqual(ss.deductions[0].amount, 5000)
self.assertEqual(ss.deductions[1].amount, 5000)
self.assertEqual(ss.gross_pay, 40500)
self.assertEqual(ss.net_pay, 29918)
self.assertEqual(ss.gross_pay, 78000)
self.assertEqual(ss.net_pay, 67418.0)
def test_payment_days(self):
no_of_days = self.get_no_of_days()
@@ -211,7 +211,7 @@ class TestSalarySlip(unittest.TestCase):
tax_paid = get_tax_paid_in_period(employee)
# total taxable income 586000, 250000 @ 5%, 86000 @ 20% ie. 12500 + 17200
annual_tax = 29700
annual_tax = 113568
try:
self.assertEqual(tax_paid, annual_tax)
except AssertionError:
@@ -250,7 +250,7 @@ class TestSalarySlip(unittest.TestCase):
# total taxable income 416000, 166000 @ 5% ie. 8300
try:
self.assertEqual(tax_paid, 8300)
self.assertEqual(tax_paid, 88608)
except AssertionError:
print("\nSalary Slip - Tax calculation failed on following case\n", data, "\n")
raise
@@ -265,7 +265,7 @@ class TestSalarySlip(unittest.TestCase):
# total taxable income 566000, 250000 @ 5%, 66000 @ 20%, 12500 + 13200
tax_paid = get_tax_paid_in_period(employee)
try:
self.assertEqual(tax_paid, 25700)
self.assertEqual(tax_paid, 121211)
except AssertionError:
print("\nSalary Slip - Tax calculation failed on following case\n", data, "\n")
raise
@@ -365,7 +365,7 @@ def make_earning_salary_component(setup=False, test_tax=False):
"salary_component": 'Basic Salary',
"abbr":'BS',
"condition": 'base > 10000',
"formula": 'base*.5',
"formula": 'base',
"type": "Earning",
"amount_based_on_formula": 1
},
@@ -386,7 +386,6 @@ def make_earning_salary_component(setup=False, test_tax=False):
{
"salary_component": "Leave Encashment",
"abbr": 'LE',
"is_additional_component": 1,
"type": "Earning"
}
]
@@ -398,7 +397,8 @@ def make_earning_salary_component(setup=False, test_tax=False):
"is_flexible_benefit": 1,
"type": "Earning",
"pay_against_benefit_claim": 1,
"max_benefit_amount": 100000
"max_benefit_amount": 100000,
"depends_on_payment_days": 0
},
{
"salary_component": "Medical Allowance",
@@ -409,9 +409,8 @@ def make_earning_salary_component(setup=False, test_tax=False):
"max_benefit_amount": 15000
},
{
"salary_component": "Perfomance Bonus",
"salary_component": "Performance Bonus",
"abbr": 'B',
"is_additional_component": 1,
"type": "Earning"
}
])
@@ -442,7 +441,10 @@ def make_deduction_salary_component(setup=False, test_tax=False):
"abbr":'T',
"formula": 'base*.1',
"type": "Deduction",
"amount_based_on_formula": 1
"amount_based_on_formula": 1,
"depends_on_payment_days": 0,
"variable_based_on_taxable_salary": 1,
"round_to_the_nearest_integer": 1
}
]
if not test_tax:
@@ -452,7 +454,8 @@ def make_deduction_salary_component(setup=False, test_tax=False):
"condition": 'employment_type=="Intern"',
"formula": 'base*.1',
"type": "Deduction",
"amount_based_on_formula": 1
"amount_based_on_formula": 1,
"round_to_the_nearest_integer": 1
})
if setup or test_tax:
make_salary_component(data, test_tax)
@@ -512,21 +515,23 @@ def create_tax_slab(payroll_period):
{
"from_amount": 250000,
"to_amount": 500000,
"percent_deduction": 5
"percent_deduction": 5.2,
"condition": "annual_taxable_earning > 500000"
},
{
"from_amount": 500000,
"from_amount": 500001,
"to_amount": 1000000,
"percent_deduction": 20
"percent_deduction": 20.8
},
{
"from_amount": 1000000,
"percent_deduction": 30
"from_amount": 1000001,
"percent_deduction": 31.2
}
]
payroll_period.taxable_salary_slabs = []
for item in data:
payroll_period.append("taxable_salary_slabs", item)
payroll_period.standard_tax_exemption_amount = 52500
payroll_period.save()
def create_salary_slips_for_payroll_period(employee, salary_structure, payroll_period, deduct_random=True):
@@ -559,7 +564,7 @@ def create_additional_salary(employee, payroll_period, amount):
"doctype": "Additional Salary",
"employee": employee,
"company": erpnext.get_default_company(),
"salary_component": "Perfomance Bonus",
"salary_component": "Performance Bonus",
"payroll_date": salary_date,
"amount": amount,
"type": "Earning"

View File

@@ -147,7 +147,8 @@ frappe.ui.form.on('Salary Structure', {
source_name: frm.doc.name,
employee: values.employee,
as_print: 1,
print_format: print_format
print_format: print_format,
for_preview: 1
},
callback: function(r) {
var new_window = window.open();

View File

@@ -137,7 +137,7 @@ def get_existing_assignments(employees, salary_structure,from_date):
return salary_structures_assignments
@frappe.whitelist()
def make_salary_slip(source_name, target_doc = None, employee = None, as_print = False, print_format = None):
def make_salary_slip(source_name, target_doc = None, employee = None, as_print = False, print_format = None, for_preview=0):
def postprocess(source, target):
if employee:
employee_details = frappe.db.get_value("Employee", employee,
@@ -147,7 +147,7 @@ def make_salary_slip(source_name, target_doc = None, employee = None, as_print =
target.branch = employee_details.branch
target.designation = employee_details.designation
target.department = employee_details.department
target.run_method('process_salary_structure')
target.run_method('process_salary_structure', for_preview=for_preview)
doc = get_mapped_doc("Salary Structure", source_name, {
"Salary Structure": {

View File

@@ -39,18 +39,19 @@ class TestSalaryStructure(unittest.TestCase):
holiday_list.save()
def test_amount_totals(self):
frappe.db.set_value("HR Settings", None, "include_holidays_in_total_working_days", 0)
sal_slip = frappe.get_value("Salary Slip", {"employee_name":"test_employee_2@salary.com"})
if not sal_slip:
sal_slip = make_employee_salary_slip("test_employee_2@salary.com", "Monthly", "Salary Structure Sample")
self.assertEqual(sal_slip.get("salary_structure"), 'Salary Structure Sample')
self.assertEqual(sal_slip.get("earnings")[0].amount, 25000)
self.assertEqual(sal_slip.get("earnings")[0].amount, 50000)
self.assertEqual(sal_slip.get("earnings")[1].amount, 3000)
self.assertEqual(sal_slip.get("earnings")[2].amount, 12500)
self.assertEqual(sal_slip.get("gross_pay"), 40500)
self.assertEqual(sal_slip.get("earnings")[2].amount, 25000)
self.assertEqual(sal_slip.get("gross_pay"), 78000)
self.assertEqual(sal_slip.get("deductions")[0].amount, 5000)
self.assertEqual(sal_slip.get("deductions")[1].amount, 5000)
self.assertEqual(sal_slip.get("total_deduction"), 10000)
self.assertEqual(sal_slip.get("net_pay"), 30500)
self.assertEqual(sal_slip.get("net_pay"), 68000)
def test_whitespaces_in_formula_conditions_fields(self):
salary_structure = make_salary_structure("Salary Structure Sample", "Monthly", dont_submit=True)

View File

@@ -5,9 +5,19 @@
from __future__ import unicode_literals
import frappe
from frappe.model.document import Document
from frappe import _
from frappe.utils import time_diff_in_seconds
from erpnext.hr.doctype.employee.employee import get_employee_emails
class TrainingEvent(Document):
def validate(self):
self.set_employee_emails()
self.validate_period()
def set_employee_emails(self):
self.employee_emails = ', '.join(get_employee_emails([d.employee
for d in self.employees]))
def validate_period(self):
if time_diff_in_seconds(self.end_time, self.start_time) <= 0:
frappe.throw(_('End time cannot be before start time'))

View File

@@ -15,11 +15,11 @@ class TrainingFeedback(Document):
def on_submit(self):
training_event = frappe.get_doc("Training Event", self.training_event)
status = None
event_status = None
for e in training_event.employees:
if e.employee == self.employee:
status = 'Feedback Submitted'
event_status = 'Feedback Submitted'
break
if status:
frappe.db.set_value("Training Event", self.training_event, "status", status)
if event_status:
frappe.db.set_value("Training Event", self.training_event, "event_status", event_status)

View File

@@ -35,6 +35,8 @@ def execute(filters=None):
for d in ded_types:
row.append(ss_ded_map.get(ss.name, {}).get(d))
row.append(ss.total_loan_repayment)
row += [ss.total_deduction, ss.net_pay]
data.append(row)
@@ -67,7 +69,7 @@ def get_columns(salary_slips):
columns = columns + [(e + ":Currency:120") for e in salary_components[_("Earning")]] + \
[_("Gross Pay") + ":Currency:120"] + [(d + ":Currency:120") for d in salary_components[_("Deduction")]] + \
[_("Total Deduction") + ":Currency:120", _("Net Pay") + ":Currency:120"]
[_("Loan Repayment") + ":Currency:120", _("Total Deduction") + ":Currency:120", _("Net Pay") + ":Currency:120"]
return columns, salary_components[_("Earning")], salary_components[_("Deduction")]

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