Compare commits

...

354 Commits

Author SHA1 Message Date
Nabin Hait
927d14e021 fix: merge conflict 2021-06-21 15:48:15 +05:30
Nabin Hait
7bb5313190 bumped to version 11.1.69 2021-06-21 16:04:04 +05:50
Ivan Ray Altomera
1d6bb3953d fix: Customer Ledger Summary report not working on python 3 (#23957) 2021-01-04 11:54:19 +05:30
rohitwaghchaure
78d0a11d09 Merge pull request #23327 from iRaySpace/stock-controller-fix
fix(stock_controller): precision based on `debit_in_account_currency`
2020-10-12 17:38:00 +05:30
Ivan Ray Altomera
ccf62886e4 fix(stock_controller): precision based on debit_in_account_currency 2020-09-15 15:36:31 +08:00
rohitwaghchaure
40747e1b49 Merge pull request #22459 from britlog/version-11-hotfix
fix product_info
2020-06-26 15:55:24 +05:30
britlog
87f8d66b79 fix product_info 2020-06-25 16:18:58 +02:00
Revant Nandgaonkar
d075399ecd ci: github release action for v11 (#22408) 2020-06-24 10:45:18 +05:30
mergify[bot]
c4ff9c72dd fix:status error in purchase invoice (#22390)
(cherry picked from commit 8a1f2ed65a)

Co-authored-by: Kenneth Sequeira <seq.kenneth@gmail.com>
2020-06-23 10:35:18 +05:30
Mangesh-Khairnar
97ccb0eec2 fix: update shopify api version (#22311) 2020-06-18 15:50:16 +05:30
mergify[bot]
2382312d41 fix: fetch tax lines within the shipping lines (#22138) (#22140)
(cherry picked from commit 3031128167)

Co-authored-by: Mangesh-Khairnar <mkhairnar10@gmail.com>
2020-06-07 18:59:14 +05:30
rohitwaghchaure
348d10cad8 Merge pull request #22078 from rohitwaghchaure/show-disabled-items-stock-balance-v11-hotfix
fix: show disabled items in the stock balance
2020-06-02 12:41:24 +05:30
Rohit Waghchaure
47f030b1bf fix: show disabled items in the stock balance 2020-06-02 12:39:51 +05:30
Sahil Khan
7e4f8fa04d Merge branch 'v11-pre-release' into version-11 2020-06-01 15:39:40 +05:30
Sahil Khan
652181600d bumped to version 11.1.77 2020-06-01 15:59:40 +05:50
Sahil Khan
e4f300c903 Merge branch 'version-11-hotfix' of https://github.com/frappe/erpnext into v11-pre-release 2020-06-01 15:38:31 +05:30
Mangesh-Khairnar
d839c46f10 fix: make transaction date of the oldest transaction as the last integration date (#22015)
* fix: make transaction date of the oldest transaction as the last integration date

* fix: only save end date when transactions are returned
2020-05-29 10:16:59 +05:30
Suraj Shetty
6d45dfd6e8 Merge pull request #21217 from gavindsouza/freeze-v11 2020-05-26 11:49:41 +05:30
Sahil Khan
5caa40857d Merge branch 'v11-pre-release' into version-11 2020-05-21 14:55:08 +05:30
Sahil Khan
78b7f69e0c bumped to version 11.1.76 2020-05-21 15:15:08 +05:50
Sahil Khan
8d9b58d801 Merge branch 'version-11-hotfix' of https://github.com/frappe/erpnext into v11-pre-release 2020-05-21 14:52:13 +05:30
sahil28297
a611cc5916 fix(patch): use translated string while setting notification template (#21680) 2020-05-11 19:28:30 +05:30
sahil28297
c6466c7d97 fix(item): patch to rename duplicate item_code values to name (#21621) 2020-05-07 12:11:42 +05:30
Sahil Khan
b35db7e819 Merge branch 'v11-pre-release' into version-11 2020-04-22 12:20:15 +05:30
Sahil Khan
44875482ac bumped to version 11.1.75 2020-04-22 12:40:15 +05:50
Sahil Khan
f06cc233a8 Merge branch 'version-11-hotfix' of https://github.com/frappe/erpnext into v11-pre-release 2020-04-22 11:55:01 +05:30
Sun Howwrongbum
fdd93cba03 fix: alter 'other_charges_calculation' to Long Text (#21304) 2020-04-16 22:45:12 +05:30
Gavin D'souza
10f53f04d7 chore: pinned python requirements
dependencies from pip freeze of version-11 production environment
2020-04-09 12:26:32 +05:30
Sahil Khan
884dc28af6 Merge branch 'v11-pre-release' into version-11 2020-03-17 12:12:23 +05:30
Sahil Khan
a506930b63 bumped to version 11.1.74 2020-03-17 12:32:23 +05:50
Sahil Khan
f6836c1d89 Merge branch 'version-11-hotfix' of https://github.com/frappe/erpnext into v11-pre-release 2020-03-17 12:06:42 +05:30
Don-Leopardo
3e2fe12c9d Fix: sql injection (#20816) 2020-03-16 22:36:19 +05:30
Saqib
5b2ca9e133 fix: is_pos gets reset on making s_inv from s_ord (#20888)
* s_inv has is_pos default set as 1
2020-03-16 17:50:35 +05:30
Chinmay Pai
f8dc7b32d5 fix: use ERPNext in welcome email when default company is not set (#20837)
Signed-off-by: Chinmay D. Pai <chinmaydpai@gmail.com>
2020-03-16 13:50:09 +05:30
RJPvT
0393cb5c43 fix: smaller then instead of bigger then :-( dumb mistake (#20693)
* fix: smaller then instead of bigger then :-( dumb mistake

* fix: comment on get_all

* Update erpnext/projects/doctype/task/task.py

Co-Authored-By: Himanshu <himanshuwarekar@yahoo.com>

* Update erpnext/projects/doctype/task/task.py

Co-Authored-By: Himanshu <himanshuwarekar@yahoo.com>

Co-authored-by: Himanshu <himanshuwarekar@yahoo.com>
2020-02-26 12:49:48 +05:30
Sahil Khan
4206b52bdc Merge branch 'v11-pre-release' into version-11 2020-02-20 18:03:05 +05:30
Sahil Khan
9e66c86d6f bumped to version 11.1.73 2020-02-20 18:23:05 +05:50
Sahil Khan
ec46b4d4c6 Merge branch 'version-11-hotfix' of https://github.com/frappe/erpnext into v11-pre-release 2020-02-20 18:00:23 +05:30
Mathieu Brunot
39c71e26b9 chore(ci-coverage): Pin coverage 4.5.4 #20646 (#20649)
* chore(ci-coverage): Pin coveralls 4.5.4 #20646

Signed-off-by: mathieu.brunot <mathieu.brunot@monogramm.io>

* chore: Fix coverage

Signed-off-by: mathieu.brunot <mathieu.brunot@monogramm.io>
2020-02-20 12:51:32 +05:30
Deepesh Garg
77285317c0 Merge pull request #20665 from marination/stock-settings-query-v11-hotfix
fix: Set Query on warehouse fields in Stock Settings
2020-02-19 18:59:37 +05:30
marination
a95c09bef6 fix: Server side validation for Warehouses 2020-02-19 11:30:47 +05:30
marination
6b901d0c1a fix: Set Query on warehouse fields in Stock Settings 2020-02-18 21:51:00 +05:30
rohitwaghchaure
8a7ccd9874 Merge pull request #20656 from marination/precision-mr-item-v11-hotfix
fix: Stock Quantity not calculated on client side in Material Request Items.
2020-02-18 12:34:45 +05:30
marination
2f2e748b7a fix: Stock Quantity not calculated on client side in Material Request Items. 2020-02-18 11:19:37 +05:30
Deepesh Garg
549ab68eb0 Merge pull request #20503 from sunhoww/patch-4
fix: [v11] negative stock error at landed cost voucher
2020-02-02 17:35:54 +05:30
Sun Howwrongbum
0b9bf4d0ac fix: negative stock error at landed cost voucher 2020-02-02 16:45:57 +05:30
Nabin Hait
1c5df5dede cannot close task if dependent task are not closed / cancelled (#20433) 2020-01-28 14:08:31 +05:30
Deepesh Garg
41d2357dc4 Merge pull request #20442 from fproldan/fix_sqlinjection_hotfix_v11
fix: SQL Injection in get_product_list_for_group method
2020-01-28 09:17:39 +05:30
NahuelOperto
af79714f40 Fix sql injection 2020-01-27 08:55:35 -03:00
Marica
8a5f31da34 fix: Applied query on PO Supplier popup field in Material Request. (#20233) 2020-01-15 19:24:39 +05:30
Sahil Khan
47a7e3422b Merge branch 'v11-pre-release' into version-11 2020-01-14 14:19:35 +05:30
Sahil Khan
9efb087e22 bumped to version 11.1.72 2020-01-14 14:39:35 +05:50
Sahil Khan
3d58dd6aa5 Merge branch 'v11-pre-release' of https://github.com/frappe/erpnext into v11-pre-release 2020-01-14 12:49:02 +05:30
Sahil Khan
ac7966cd7e bumped to version 11.1.71 2020-01-14 12:47:21 +05:30
sahil28297
61ddb508d4 fix(init): bump version to 11.1.70 2020-01-14 12:47:21 +05:30
Marica
d973fb8823 fix: Added description and title to supplier selection popup in Material Request (#20181)
* fix: Added description and title to supplier selection popup in Material Request

* Update material_request.js

cleanup

Co-authored-by: rohitwaghchaure <rohitw1991@gmail.com>
2020-01-07 13:04:56 +05:30
Marica
a649414419 fix: Remove unnecessary Duplicate Entry Error (#20122) 2019-12-27 16:53:00 +05:30
rohitwaghchaure
270f23b966 Merge pull request #20080 from nabinhait/rounding-adjustment
fix: rounding adjustment while both inclusive tax and discount amount present
2019-12-27 11:52:24 +05:30
Nabin Hait
4d745a39e0 fix: rounding adjustment while both inclusive tax and discount amount present 2019-12-25 13:59:24 +05:30
Don-Leopardo
5e0b5cb283 perf: Asset Depreciations and Balances report (#18455) 2019-12-24 17:35:57 +05:30
Sun Howwrongbum
69358cfd40 feat: consider expiry_date during Batch queries (#19972) 2019-12-24 12:30:20 +05:30
RJPvT
ad1ba2bb76 fix: sc object not loaded (#19892)
fix for object not loaded and errors in auto-generate :

Traceback (most recent call last):
  File "/home/frappe/frappe-bench/apps/frappe/frappe/utils/background_jobs.py", line 103, in execute_job
    method(**kwargs)
  File "/home/frappe/frappe-bench/apps/erpnext/erpnext/buying/doctype/supplier_scorecard/supplier_scorecard.py", line 141, in refresh_scorecards
    sc.save()
TypeError: 'NoneType' object is not callable
2019-12-24 12:20:41 +05:30
DeeMysterio
c44abd2f78 fix(employee onboarding): stop showing irrelevant job offer links for a job applicant (#20055) 2019-12-23 19:02:59 +05:30
Deepesh Garg
0bcada8f3c Merge pull request #20016 from marination/valuation_rate_company_v11_hotfix
fix: Company None not found in get_valuation_rate
2019-12-20 11:56:24 +05:30
marination
a6b110a7d3 fix: Company None not found in get_valuation_rate 2019-12-19 14:50:17 +05:30
Sahil Khan
b4ce43306b Merge branch 'v11-pre-release' into version-11 2019-12-18 13:59:59 +05:30
Sahil Khan
85a9f55fa7 bumped to version 11.1.71 2019-12-18 14:19:59 +05:50
Suraj Shetty
596c0d2060 fix(init): bump version to 11.1.70 (#19986)
fix(init): bump version to 11.1.70
2019-12-18 13:58:01 +05:30
sahil28297
723421f375 fix(init): bump version to 11.1.70 2019-12-18 13:56:34 +05:30
Deepesh Garg
f9297416f2 fix: Append expense account only if expense account exists (#19882) 2019-12-10 12:14:58 +05:30
Nabin Hait
a8d2916e61 Merge branch 'version-11' into version-11-hotfix 2019-12-09 18:08:05 +05:30
Deepesh Garg
0f70f20ec5 Merge pull request #19866 from nextchamp-saqib/ux-fix
fix: tax templates from all companies fetching in receipt
2019-12-09 17:59:44 +05:30
thefalconx33
73d9d8fec9 fix: tax templates from all companies fetching in receipt 2019-12-09 13:59:50 +05:30
Deepesh Garg
c4756e938f fix: Rounding Adjustment GL entry fix (#19840)
* fix: Rounding Adjustment GL entry fix

* fix: Spacing in tab

* fix: Comment fix
2019-12-09 13:03:18 +05:30
Deepesh Garg
faa5d90f1b fix: NoneType' object has no attribute '__getitem__' (#19861) 2019-12-09 11:28:38 +05:30
rohitwaghchaure
3b37a109c5 Merge pull request #19852 from rohitwaghchaure/fixed_timesheet_overlap_issue_v11_hotfix
fix: timesheet overlap error
2019-12-07 14:10:35 +05:30
Rohit Waghchaure
ab4aa53f52 fix: timesheet overlap error 2019-12-07 13:28:09 +05:30
Nabin Hait
3483426fe7 optimize: Optimization of Receivable report filtered based on sales person (#19795) 2019-12-04 15:30:48 +05:30
Deepesh Garg
3039094737 fix: Party name field in trial balacne for party report (#19791) 2019-12-03 16:30:16 +05:30
Nabin Hait
e21602d432 Update accounts_receivable_summary.py 2019-12-03 16:22:22 +05:30
Marica
6067ef3c49 fix: Item qty cannot be zero in Purchase Receipt (#19781) 2019-12-03 12:45:28 +05:30
Deepesh Garg
b78128a7fe Merge pull request #19767 from deepeshgarg007/avaiable_stock_for_v11
fix: Available stock for packing item report
2019-12-02 11:57:07 +05:30
deepeshgarg007
c8cbb87168 fix: Available stock for packing item report 2019-12-01 22:17:46 +05:30
Deepesh Garg
aed8aeb83a fix: Account type in Handling Difference in Inventory account (#19675)
* fix: Account type in Handling Difference in Inventory account

* fix: Add Stock Adjustment account

* fix: Rename account to stock adjustment
2019-11-28 18:29:26 +05:30
Saqib
fa47445e11 fix: due date before posting date for items added to cart yesterday (#19680) 2019-11-28 18:27:55 +05:30
Marica
0220acf100 fix: Changed type of column 'serial_no' in Stock Ledger Entry (#19705) 2019-11-28 18:20:57 +05:30
Marica
0fe8f8e6d9 fix: get_batch_qty_and_serial_no() requires argument 'stock_qty' (#19699) 2019-11-27 15:51:12 +05:30
rohitwaghchaure
f87b766a26 Merge pull request #19688 from deepeshgarg007/sales_invoice_serial_no
fix: Serial no validation against sales invoice
2019-11-27 11:59:46 +05:30
deepeshgarg007
2262e5d443 fix: Validation msg 2019-11-26 16:15:05 +05:30
deepeshgarg007
b9cad8d891 fix: Serial no validation against sales invoice 2019-11-26 15:27:54 +05:30
Marica
dafdc6035a fix: Validation Error message on Prepared Report. (#19640)
Give the user the reason why he has to use filters.
2019-11-22 14:38:53 +05:30
Marica
c8369c10ff fix: Get Current Stock button in Purhase Receipt not working (#19653)
- Current Stock visible in grid view within Supplied Items table of Purchase Receipt
2019-11-22 13:33:11 +05:30
Deepesh Garg
08b8ed5ca6 Merge pull request #19635 from ruchamahabal/fix_cash_entry_v11
fix(minor): default Cash Entry account not getting fetched in Journal Entry (v11)
2019-11-20 22:02:48 +05:30
Rucha Mahabal
6cefdb40d1 fix(Journal Entry): default Cash Entry account not getting fetched 2019-11-20 20:13:04 +05:30
Rohit Waghchaure
3ad65ec57d fix: code cleanup 2019-11-20 13:24:53 +05:30
rohitwaghchaure
364332bcfe Update queries.py 2019-11-20 13:24:44 +05:30
Rohit Waghchaure
9965f443ee fix: not able to select item in sales order 2019-11-20 13:24:34 +05:30
rohitwaghchaure
d87d644dc2 Merge pull request #19621 from rohitwaghchaure/not_able_to_select_item_in_the_sales_order_hotfix
fix: not able to select item in sales order
2019-11-19 19:40:29 +05:30
rohitwaghchaure
eeab3128aa Merge pull request #19623 from rohitwaghchaure/code_cleanup_sales_invoice_credit_note_hotfix
fix: code cleanup
2019-11-19 19:39:36 +05:30
Rohit Waghchaure
7b2cec8da2 fix: code cleanup 2019-11-19 19:27:31 +05:30
rohitwaghchaure
6e3cbd8698 Update queries.py 2019-11-19 19:26:31 +05:30
Rohit Waghchaure
b5e1f846ce fix: not able to select item in sales order 2019-11-19 19:08:44 +05:30
Sahil Khan
f06b31aab4 Merge branch 'v11-pre-release' into version-11 2019-11-18 18:09:02 +05:30
Sahil Khan
f8502317a9 bumped to version 11.1.68 2019-11-18 18:29:02 +05:50
rohitwaghchaure
9c7a33405d Merge pull request #19024 from Alchez/v11-work-order-bugs
fix: Work Order operating cost re-calculation on client-side (v11)
2019-11-15 11:18:39 +05:30
Nabin Hait
0ee46a624c Merge branch 'version-11-hotfix' into v11-pre-release 2019-11-14 18:05:47 +05:30
Nabin Hait
5485d4c435 fix: One serial no can be tagged in multiple invoices if used against different items (#19581) 2019-11-14 13:28:29 +05:30
RJPvT
fb2d4884b3 fix: allow pending within review date (#19577)
* fix: allow pending within review date

referenced - fix: allow Pending on review date #19497
explanation for closing not valid

* fix: codacy
2019-11-14 10:10:02 +05:30
Nabin Hait
3b38a9e418 Accounts Receivable report fixes (#19575)
* fix: Set due date in accounts receivable based on payment terms

* fix: Show AR summary based on outstanding
2019-11-13 18:49:45 +05:30
Deepesh Garg
437d3e37d6 Merge pull request #19566 from deepeshgarg007/balance_sheet_filter
fix: Accumulated Values filter disappearing
2019-11-13 17:47:05 +05:30
Rohan
1b9a55e766 Merge branch 'version-11-hotfix' into v11-work-order-bugs 2019-11-13 13:08:59 +05:30
deepeshgarg007
a5353a8d04 fix: Accumulated Values filter disappearing 2019-11-13 12:46:19 +05:30
RJPvT
e1bc0a1028 fix: user with invoice role not able to submit (#19555)
fix for a user not having timesheet submit privelige (e.g. a shared timesheet from another department) not being able to submit because of linked fields
2019-11-13 10:47:40 +05:30
Deepesh Garg
92116abd92 Merge pull request #19548 from DeeMysterio/v11-emp-err
fix(employee): show only active employees in the error while marking …
2019-11-11 21:47:58 +05:30
Diksha
e5731b3d0f fix(employee): show only active employees in the error while marking reporting to employee as left 2019-11-11 19:08:48 +05:30
rohitwaghchaure
a36c4326c3 Merge pull request #19449 from surajshetty3416/account-head-fix
fix: not able to change the account type in parent company account head
2019-11-11 15:13:07 +05:30
sahil28297
5a0a4bdcff fix(cart): return rule instead of rule_label_map (#19521)
* fix(cart): return rule instead of rule_label_map

* fix(cart): remove get_value for rule_label_map as well
2019-11-11 11:15:29 +05:30
Nabin Hait
d2dc889849 fix(trial balance): Show opening and closing of group account in single column (#19511) 2019-11-11 10:59:30 +05:30
rohitwaghchaure
dbcdf7e225 minor: added description, uom fields in material request plan item table (#19464)
* fix: added description, uom fields in material request plan item table

* Update material_request_plan_item.json

* Update production_plan.js
2019-11-11 10:57:04 +05:30
rohitwaghchaure
7fdef64152 fix: incorrect balance qty in stock ledger if batch filter set (#19480) 2019-11-08 14:50:12 +05:30
Deepesh Garg
b2be301988 Merge pull request #19529 from deepeshgarg007/gst_check_digit_msg_v11
fix: GSTIN validation msg fix
2019-11-07 23:05:07 +05:30
deepeshgarg007
68db5a6792 fix: GSTIN validation msg fix 2019-11-07 21:58:56 +05:30
Marica
f232965392 fix[minor]: Payment Entry status patch (#19520) 2019-11-07 18:04:16 +05:30
Marica
2ff7197752 fix: Added 'status' field in Payment Entry (#19513) 2019-11-06 19:16:11 +05:30
Anurag Mishra
1d3b5b0437 fix: packed items child table reset on amending docs (#19158) 2019-11-06 16:56:14 +05:30
Nabin Hait
8280d32967 fix: incorrect number of entries while making deferred revenue entry (#19512) 2019-11-06 15:33:36 +05:30
Rohan
4a79072c71 Merge branch 'version-11-hotfix' into v11-work-order-bugs 2019-11-06 11:39:11 +05:30
Deepesh Garg
721a00cd22 Merge pull request #19489 from DeeMysterio/v11-delivery-dn
fix(sales order): rename delivery to delivery note on sales order mak…
2019-11-04 13:16:50 +05:30
Diksha
8ce7893bf0 fix(sales order): rename delivery to delivery note on sales order make button 2019-11-04 12:19:23 +05:30
Deepesh Garg
0f87282aef Merge pull request #19482 from MaxMorais/patch-9
fix: Wrong datafield type in Task
2019-11-03 17:12:15 +05:30
Deepesh Garg
de51217a80 Merge pull request #19485 from gavindsouza/sales-register-tax-v11
fix: calculate total tax in sales register report
2019-11-03 17:09:30 +05:30
Gavin D'souza
bc01dafbba fix: calculate total tax in sales register report 2019-11-03 14:02:52 +05:30
Maxwell Morais
a985bfc29a Wrong datafield type in Task
The field `depends_on_tasks` is set as `Data`, but it stores a list of strings joined by comma, so with more than 5 tasks, we reach the field size limit of 144 chars and data get trunked, what make us lose data!
2019-11-01 18:31:56 -03:00
Deepesh Garg
08dbb13451 Merge pull request #19424 from marination/search_field
fix: Search field entries included in Item Link field query
2019-11-01 15:40:14 +05:30
rohitwaghchaure
170dd37b86 fix: purchase order issue, margin_rate_or_amount not there in the purchase documents (#19465) 2019-10-31 15:56:05 +05:30
gavin
b1744972e0 Merge pull request #19451 from gavindsouza/hooks-update-v11
chore: moves email digest to long job from regular
2019-10-31 14:34:58 +05:30
marination
7aa993b14b fix: Fetching catched meta and removed description fetch from Search Fields
Description is conditionally fetched and also used in WHERE clause, that is maintained.
Improved naming
2019-10-30 18:39:35 +05:30
gavin
ad89cb0ef3 Merge branch 'version-11-hotfix' into hooks-update-v11 2019-10-30 17:52:26 +05:30
rohitwaghchaure
a185b51f0d Merge pull request #19447 from rohitwaghchaure/not_able_to_select_project_in_work_order_v11
fix: not able to select the project in the work order
2019-10-30 16:32:48 +05:30
gavin
1ff81bca7d Merge branch 'version-11-hotfix' into hooks-update-v11 2019-10-30 15:47:52 +05:30
Gavin D'souza
b02db3c3e8 chore: moves email digest to long job from regular 2019-10-30 15:20:43 +05:30
Suraj Shetty
d1d835347e fix: Pass parent_acc_name 2019-10-30 14:45:11 +05:30
Rohit Waghchaure
ba245553f0 fix: not able to change the account type in parent company account head 2019-10-30 14:39:03 +05:30
Rohit Waghchaure
112902a9c4 fix: not able to select the project in the work order 2019-10-30 14:27:18 +05:30
Anurag Mishra
3f4cdcc7a4 fix: On Specific case if no item code in name (#19420) 2019-10-30 14:18:20 +05:30
rohitwaghchaure
91f81b07d9 fix: not able to select the zero qty batch while making the sales return entry (#19436) 2019-10-30 13:30:28 +05:30
Deepesh Garg
66e92041b9 Merge pull request #19403 from Mangesh-Khairnar/fix-dn-status-11
fix: sync delivery note status in both list view and form view
2019-10-29 14:18:23 +05:30
Rohan
f2dc89c7a0 Merge branch 'version-11-hotfix' into v11-work-order-bugs 2019-10-29 11:25:40 +05:30
marination
10b226af10 fix: Search field entries included in Item Link field query 2019-10-28 16:18:47 +05:30
Deepesh Garg
115887a886 fix: Better validation msg for difference account in Stock Entry (#19401)
* fix: Better validation msg for difference account

* fix: Make primary info bold
2019-10-28 12:07:57 +05:30
Suraj Shetty
a1f5c22da7 fix: Overwrite default cost center if item has default cost center set. (#19406) 2019-10-28 12:03:58 +05:30
Deepesh Garg
712c01d5bd Merge pull request #19394 from 0Pranav/patch-3
fix: setting incorrect field for party bank account
2019-10-25 12:25:33 +05:30
Mangesh-Khairnar
bd4e296336 fix: sync delivery note status in both list view and form view 2019-10-24 16:20:10 +05:30
Pranav Nachnekar
b52d81ef6a fix: setting incorrect field for party bank account 2019-10-23 15:50:00 +00:00
Shivam Mishra
15432c76a4 fix: icon for opportunity (#19381) 2019-10-23 11:07:33 +05:30
Rohan
dd3cd6e2ba Merge branch 'version-11-hotfix' into v11-work-order-bugs 2019-10-22 17:11:39 +05:30
Sahil Khan
5889a29656 Merge branch 'v11-pre-release' into version-11 2019-10-22 17:03:12 +05:30
Sahil Khan
0e7ca6ada7 bumped to version 11.1.67 2019-10-22 17:23:12 +05:50
Anurag Mishra
5a2ea528e8 fix: if naming if item-code and index both are same (#19372) 2019-10-22 11:46:42 +05:30
Saurabh
8957093439 fix: Don't show make jv button if equity or liability account and asset account not specified (#19349) 2019-10-21 16:02:47 +05:30
Nabin Hait
507c435dee Reorder level report fix v11 (#19367)
* fix: Fixed consumed qty based on Stock Ledger Entry

* Update itemwise_recommended_reorder_level.py
2019-10-21 16:01:51 +05:30
Deepesh Garg
8889edad72 Merge pull request #19365 from deepeshgarg007/sales_return_print_fix
fix: Positive qty in sales return print
2019-10-21 15:23:34 +05:30
Nabin Hait
e27d1aebd5 fix: Positive qty in sales return print 2019-10-21 14:28:33 +05:30
Deepesh Garg
ef567eac37 fix: Item Price changes are not persistent after updating cost on submitted BOM (#19357) 2019-10-21 13:27:34 +05:30
rohitwaghchaure
f8e0a2204e Merge pull request #19331 from rohitwaghchaure/minor_loan_not_able_to_make_jv
fix: not able to save journal entry for disbursement amount
2019-10-16 19:02:09 +05:30
Rohit Waghchaure
baecc7fd31 fix: not able to save journal entry for disbursement amount 2019-10-16 18:40:27 +05:30
Rohan
529b465337 Merge branch 'version-11-hotfix' into v11-work-order-bugs 2019-10-16 15:11:45 +05:30
Deepesh Garg
675e2670b2 fix: Show Create Loan button only if loan is not created (#19291) 2019-10-14 15:50:19 +05:30
Deepesh Garg
3919fd10ad Merge pull request #19289 from deepeshgarg007/chart_fix_analytics_v11
fix: Chart fix in Analytics report when based on item
2019-10-13 09:33:16 +05:30
deepeshgarg007
b4e61e19bb fix: Chart fix in Analytics report 2019-10-12 23:11:28 +05:30
Rohan
85be3ec796 Merge branch 'version-11-hotfix' into v11-work-order-bugs 2019-10-11 12:50:28 +05:30
Marica
aa3d6ed11e fix: Margin and Discount percentage set correctly via 'Update Items' (#19173) 2019-10-11 11:52:29 +05:30
Nabin Hait
382735ad2f fix: Set incoming rate for standalone sales return (#19264) 2019-10-10 16:40:10 +05:30
Deepesh Garg
8d42b75e29 Merge pull request #19254 from Thunderbottom/e-invoice-disc-fix-v11
chore: check if discount_percentage exists
2019-10-06 23:47:18 +05:30
Chinmay D. Pai
7e03e046ec chore: check if discount_percentage exists
fixes TypeError: '>' not supported between instances of 'NoneType' and 'float'

Signed-off-by: Chinmay D. Pai <chinmaydpai@gmail.com>
2019-10-05 10:20:19 +05:30
Sahil Khan
bfddc8aba1 Merge branch 'version-11-hotfix' into version-11 2019-10-04 13:43:32 +05:30
Sahil Khan
d0dafcc46f bumped to version 11.1.66 2019-10-04 14:03:32 +05:50
Rohan
43a0161257 Merge branch 'version-11-hotfix' into v11-work-order-bugs 2019-10-04 13:13:06 +05:30
gavin
f8996b64f1 fix: report ~ lead conversion time (#19234) 2019-10-03 11:37:41 +05:30
Rucha Mahabal
867443ea6d fix: produced_qty field hidden and not updated in sales order item (v11) (#19214)
* fix: produced_qty field hidden and not updated in sales order item

* fix: codacy review
2019-09-30 20:04:21 +05:30
Rohan
acdc230ef3 fix: set no-copy on some contract fields (#19207) 2019-09-30 20:04:03 +05:30
DeeMysterio
86232e4449 fix(purchase receipt): rename return/invoice to Purchase return/invoice (#19213) 2019-09-30 16:59:08 +05:30
rohitwaghchaure
78c7f03731 fix: mismatch stock value between stock balance report and stock in hand in trial balance (#19203) 2019-09-30 15:18:38 +05:30
Marica
65b99a5109 fix: Make '% Completed' 100% in Projects if no Tasks and Status is Completed (#19176) 2019-09-30 15:12:10 +05:30
rohitwaghchaure
8ec290d5ec fix: group by customer filter shows incorrect value in buying amount column for the gross profit report (#18998) 2019-09-30 10:28:10 +05:30
Nabin Hait
65a259eb81 refactor: Reposting utility of Stock ledger (#19153) 2019-09-26 16:44:02 +05:30
Sahil Khan
47420578ae Merge branch 'version-11-hotfix' into version-11 2019-09-26 13:55:31 +05:30
Sahil Khan
35d8cd9f10 bumped to version 11.1.65 2019-09-26 14:15:31 +05:50
Rucha Mahabal
1e55ac6a4f fix(Report): Sales Register (#19170) 2019-09-25 16:03:09 +05:30
Rohan
ce60dc3ea3 Merge branch 'version-11-hotfix' into v11-work-order-bugs 2019-09-25 14:45:21 +05:30
Nabin Hait
ff7a5d18d1 fix: Handling payments against credit/debit notes and party currency (#19152) 2019-09-24 19:52:06 +05:30
Rucha Mahabal
5a15dcc060 fix(Stock): item variant description (#19135) 2019-09-24 19:16:43 +05:30
Sahil Khan
cec6c87e09 Merge branch 'version-11-hotfix' into version-11 2019-09-24 18:14:44 +05:30
Sahil Khan
b2870b9426 bumped to version 11.1.64 2019-09-24 18:34:44 +05:50
rohitwaghchaure
e27600066c Merge pull request #19145 from rohitwaghchaure/fixed_imponibileimporto_for_the_previous_row_total
fix: ImponibileImporto not getting calculated properly
2019-09-24 18:11:16 +05:30
Nabin Hait
5b8c624b34 Merge branch 'version-11-hotfix' into fixed_imponibileimporto_for_the_previous_row_total 2019-09-24 11:46:24 +05:30
Anurag Mishra
9e8531d68e fix: provided delete permission (#19142) 2019-09-24 09:55:01 +05:30
Mangesh-Khairnar
ebeb137351 chore: remove unlinked letter head references (#19139) 2019-09-24 09:52:13 +05:30
Rohit Waghchaure
8c7d28a41a fix: ImponibileImporto not getting calculated properly 2019-09-23 17:39:55 +05:30
rohitwaghchaure
d90b80af86 Merge pull request #19128 from rohitwaghchaure/removed_mandatory_property_for_address_field_in_quick_entry_v11_hotfix
fix: removed mandatory property for address field in quick entry
2019-09-23 14:48:41 +05:30
Anurag Mishra
b62e17f7f2 feat: missmatching amount in GST Sales report and itemised sales report (#19119) 2019-09-20 22:59:26 +05:30
Sahil Khan
5169dc1566 Merge branch 'version-11-hotfix' into version-11 2019-09-20 15:13:04 +05:30
Sahil Khan
d3afecd1d9 bumped to version 11.1.63 2019-09-20 15:33:04 +05:50
Rohit Waghchaure
ec56984e12 fix: removed mandatory property for address field in quick entry 2019-09-20 12:20:37 +05:30
Marica
1c26623f22 fix: Report 'Payment Period based On Invoice Date' (#19124) 2019-09-20 11:40:17 +05:30
Anurag Mishra
02cb668f22 fix: dashboard + button not working (#19095) 2019-09-19 18:12:57 +05:30
rohitwaghchaure
bd749dce18 Merge pull request #19113 from rohitwaghchaure/fixed_get_bin_details_and_serial_nos_arugument_passing_issue_v11
fix: get_bin_details_and_serial_nos() takes at least 3 arguments (4 g…
2019-09-19 18:01:16 +05:30
Rohit Waghchaure
737a504a35 fix: get_bin_details_and_serial_nos() takes at least 3 arguments (4 given) 2019-09-19 17:11:14 +05:30
rohitwaghchaure
c719f842bc fix: set stock adjustment account for the raw materials instead of COGS (#19089) 2019-09-19 11:16:19 +05:30
Rohan
0d19139acf fix: only set times if job card is filled (#19106) 2019-09-18 20:06:48 +05:30
rohitwaghchaure
80973bb8de fix: not able to export accounts receivable summary report in excel (#19099) 2019-09-18 20:03:30 +05:30
Suraj Shetty
a9a7e84873 fix: Permission issue in Total Stock Summary report (#19103)
Data in "Total Stock Summary" report were not getting filtered
based on applied user permissions because some link fields had
wrong options
2019-09-18 20:01:54 +05:30
Rohan
b23da53fee Merge branch 'version-11-hotfix' into v11-work-order-bugs 2019-09-18 13:24:25 +05:30
Sahil Khan
78776c42fe Merge branch 'version-11-hotfix' into version-11 2019-09-17 17:00:32 +05:30
Sahil Khan
2d9908d22f bumped to version 11.1.62 2019-09-17 17:20:32 +05:50
rohitwaghchaure
3b0695e0ca Merge pull request #19086 from rohitwaghchaure/offline_pos_currency_conversion_issue_for_v11_hotfix
fix: plc conversion issue for offline pos
2019-09-17 13:33:17 +05:30
Rohit Waghchaure
a9695f04de fix: plc conversion issue for offline pos 2019-09-17 13:25:16 +05:30
Jamsheer PP
a2398775cd fix: Asset Maintenance - TypeError - Version 11 (#19069)
* fix: Asset Maintenance - TypeError

* Update asset_maintenance.py
2019-09-16 19:42:38 +05:30
rohitwaghchaure
f2544ec7f2 Merge pull request #19067 from rohitwaghchaure/fixed_decimal_point_issue_for_e_invoice
fix: Decimal point issue for e-invoice
2019-09-16 14:51:50 +05:30
Rohit Waghchaure
778d7b9cbd fix: Decimal point issue for e-invoice 2019-09-16 14:49:30 +05:30
rohitwaghchaure
2a3c62e7f6 Merge pull request #19060 from rohitwaghchaure/user_can_edit_rate_and_discount_in_offline_pos
fix: user can able to change rate and discount even if they don't have permission
2019-09-16 11:50:33 +05:30
Rohit Waghchaure
5c576e4365 fix: user can able to change rate and discount even if they don't have permission 2019-09-16 11:14:33 +05:30
rohitwaghchaure
c57753a234 fix: for pos, paid amount has not considered the tax amount due to which outstadning amount showing for the pos invoices (#19038) 2019-09-13 15:49:11 +05:30
Sun Howwrongbum
7cabcdd9a2 fix: precision rounding issue during pos return validation (#19034) 2019-09-13 11:08:02 +05:30
Saurabh
2b0c845cb8 fix: if id not found then compare item name to avoid duplicate item creation (#19002) 2019-09-12 19:23:24 +05:30
Suraj Shetty
71e51714f2 fix: Naming series check to avoid duplicate entry error (#19016)
* fix: Naming series check to avoid duplicate key error

* fix: Check for existence of naming series
2019-09-12 19:18:40 +05:30
Rohan
d37a30fc47 fix: operating cost calculation in JS 2019-09-12 16:12:48 +05:30
Anurag Mishra
62767ced3e feat: added date filter based on billing date and based date (#19013) 2019-09-12 13:48:18 +05:30
Himanshu
cf71643685 fix(Issue): track issue split from (#18995)
* fix: track issue split from

* fix: comment on issue split

* Update issue.py
2019-09-11 19:20:47 +05:30
Nabin Hait
d225f5993b Show draft future payments as well 2019-09-11 18:40:31 +05:30
Mangesh-Khairnar
3dc8941be7 fix(purchase-invoice): Update paid amount on creation of debit note (#18833)
* fix(purchase-invoice): set paid amount for purchase return

* fix(purchase-invoice): remove payment schedule on creation of debit note

* Update accounts_controller.py
2019-09-11 10:50:13 +05:30
rohitwaghchaure
dbebec8fcc fix: Quotation Trends report not working for filter group by customer (#18987) 2019-09-10 19:18:45 +05:30
Sahil Khan
4111965d2e Merge branch 'version-11-hotfix' into version-11 2019-09-10 14:32:30 +05:30
Sahil Khan
3ac54a0ba6 bumped to version 11.1.61 2019-09-10 14:52:29 +05:50
Nabin Hait
143f007272 fix: set raw material's batch based on main item's batch only if RM has batch no (#18978) 2019-09-10 14:07:37 +05:30
Suraj Shetty
3fc10a8da1 fix: Make address fields mandatory (#18980) 2019-09-10 14:07:01 +05:30
Mangesh-Khairnar
ed1e28b8fa fix(packing-slip): add a more descriptive message (#18982) 2019-09-10 13:21:19 +05:30
Deepesh Garg
71145c699f Merge pull request #18973 from Anurag810/sales_invoice_print_format_fixes_v11
fix: sales invoice return print format overriding the meta default print format
2019-09-09 18:35:49 +05:30
Anurag Mishra
ea6d90988f fix: sales invoice return print format overideing the meta default print format 2019-09-09 16:05:23 +05:30
Faris Ansari
6efe4acb1d fix: Set Price List in case of User Permissions (#18968)
- Pick Price List if there is one User Permission record
2019-09-09 14:28:12 +05:30
Rohan
ad2365502f fix: only select serial no that are present in the selected batch (#18726) 2019-09-09 13:49:02 +05:30
rohitwaghchaure
9c428ebfba fix: not able to save item because price list has disabled (#18965) 2019-09-09 13:20:15 +05:30
rohitwaghchaure
b53e4404dc fix: '>' not supported between instances of 'int' and 'str' (#18966) 2019-09-09 13:19:22 +05:30
rohitwaghchaure
0bc849975c Merge pull request #18951 from rohitwaghchaure/fixed_decimal_point_issue_for_e_invoicing
fix: PrezzoUnitario decimal issue
2019-09-06 18:47:59 +05:30
rohitwaghchaure
690139ca11 fix: circular dependency during asset cancellation (#18952) 2019-09-06 18:39:25 +05:30
Rohit Waghchaure
c0a21ad96a fix: PrezzoUnitario decimal issue 2019-09-06 16:30:05 +05:30
rohitwaghchaure
8c9fd62775 fix: ImponibileImporto for On Previous Row Total (#18947) 2019-09-06 12:14:45 +05:30
Sahil Khan
0be054ae0f Merge branch 'version-11-hotfix' into version-11 2019-09-05 17:25:12 +05:30
Sahil Khan
912cf9d555 bumped to version 11.1.60 2019-09-05 17:45:11 +05:50
Nabin Hait
7fe0a2023a fix: Print/PDF of AR/AP report after refactoring (#18930) 2019-09-05 16:43:06 +05:30
Mohammad Noureldin
cf191c0483 18861: Updated .gitignore (#18862) 2019-09-05 15:15:59 +05:30
Ernesto Ruiz
bb8d9f2a57 fix: Add transtlation function to strings (#18806)
* fix: Add transtlation function to strings

fix: Add transtlation function to strings

* fix: Add transtlation function to strings

fix: Add transtlation function to strings
2019-09-05 15:04:42 +05:30
Bassam Ramadan
fa4577d18a Update journal_entry.js (#18922)
adding a missed semicolom
2019-09-05 14:55:58 +05:30
Rohan
993521d365 fix: pull project from task (#18775) 2019-09-05 14:53:48 +05:30
Rohan
3372b7411d fix: error while trying to get directions (#18826) 2019-09-05 14:51:04 +05:30
rohitwaghchaure
5dbfeb8557 fix: not able to create invoice against patient (#18857) 2019-09-05 14:48:34 +05:30
rohitwaghchaure
f5d2337176 fix: mismatch between warehouse tree value and warehouse based stock balance report value (#18877) 2019-09-05 14:47:10 +05:30
Faris Ansari
67aa7b8735 fix: Honor Shopping Cart Price List (#18884) 2019-09-05 12:19:35 +05:30
Anurag Mishra
aea67c9843 fix: added missing positional argument (#18897) 2019-09-05 12:18:01 +05:30
Rohan
f64aaf5e1c fix: attribute error when trying to fetch items (#18899) 2019-09-05 12:16:44 +05:30
Deepesh Garg
00f6fd827e fix: Add UOM in anlytics report when viewing based on item (#18901) 2019-09-05 12:15:25 +05:30
Sahil Khan
ea2cdf8c12 Merge branch 'version-11-hotfix' into version-11 2019-09-03 16:30:00 +05:30
Sahil Khan
29729c8d31 bumped to version 11.1.59 2019-09-03 16:50:00 +05:50
rohitwaghchaure
536e24c6a1 Merge pull request #18908 from rohitwaghchaure/update_qty_even_after_submission_of_the_sales_transaction
fix: update actual qty even after submission of the sales transactions
2019-09-03 15:59:26 +05:30
Rohit Waghchaure
f178dc3b26 fix: update actual qty even after submission of the sales transactions 2019-09-03 15:57:53 +05:30
Mangesh-Khairnar
bc2f311a41 fix(purchase-invoice): add rounded total property setter for purchase invoice (#18842) 2019-09-03 14:18:31 +05:30
Deepesh Garg
5c28aba872 fix: Default message from Payment Gateway Account not fetching (#18849) 2019-09-03 14:17:15 +05:30
Rohan
f800050b71 fix: include start and end date for contract status (#18865) 2019-09-03 14:16:33 +05:30
Nabin Hait
fea996303f fix: Test case for Accounts Receivable based on payment terms (#18896) 2019-09-03 13:54:40 +05:30
rohitwaghchaure
833e017111 Merge pull request #18886 from rohitwaghchaure/fix_incorrect_stock_valuation_calculate_when_qty_from_negative_to_stock
fix: incorrect stock value difference when stock move from negative to positive
2019-09-02 23:19:56 +05:30
Nabin Hait
fb4f73605a refactor: Accounts Receivable / Payable report (#18890) 2019-09-02 22:36:20 +05:30
Mangesh-Khairnar
94dfcce434 fix: loan application (#18881)
* fix: loan application

* Update loan_application.py
2019-09-02 15:59:07 +05:30
Rohit Waghchaure
18e5bc1976 fix: incorrect stock value difference when stock move from negative to positive 2019-09-02 11:57:54 +05:30
Saurabh
7e54e6acb8 Merge branch 'version-11-hotfix' into version-11 2019-08-29 20:11:51 +05:30
Saurabh
0e52951db8 bumped to version 11.1.58 2019-08-29 20:41:51 +06:00
rohitwaghchaure
1b050e2dfd Merge pull request #18869 from rohitwaghchaure/not_able_to_save_sales_order
fix: not able to save sales order
2019-08-29 20:02:41 +05:30
Rohit Waghchaure
5674e19e71 fix: not able to save sales order 2019-08-29 20:00:45 +05:30
Saurabh
c49735ed15 Merge branch 'version-11-hotfix' into version-11 2019-08-29 15:08:47 +05:30
Saurabh
d1c467e49c bumped to version 11.1.57 2019-08-29 15:38:47 +06:00
rohitwaghchaure
e14df31c05 Merge pull request #18864 from rohitwaghchaure/v11_optimized_code_to_get_items_for_pos
fix: optimized the code to fix slow loading of items in the POS
2019-08-29 12:22:30 +05:30
rohitwaghchaure
9ff9f21af0 Merge pull request #18856 from rohitwaghchaure/incorrect_valuation_rate_calculated_for_serial_no
fix: incorrect valuation rate calculated because of string replacement issue
2019-08-29 12:13:24 +05:30
Saurabh
eba361b27d Merge branch 'version-11-hotfix' into v11_optimized_code_to_get_items_for_pos 2019-08-29 12:05:44 +05:30
Rohit Waghchaure
cc960806a9 Optimized the code to fix slow loading of items in the POS 2019-08-29 11:46:55 +05:30
rohitwaghchaure
75c47d64ff Merge pull request #18824 from rohitwaghchaure/fix_actual_qty_showing_different_in_the_report_and_form
fix: Actual qty in the sales order showing different on the sales ord…
2019-08-29 09:23:32 +05:30
Rohit Waghchaure
300307b22f fix: incorrect valuation rate calculated because of string replacement issue 2019-08-28 16:56:07 +05:30
Mangesh-Khairnar
43ebcb5876 fix: update show in website on disabling item (#18832) 2019-08-26 10:27:21 +05:30
bghayad
f254d8c5ee Adding query for item field for some stock reports (#18457) 2019-08-23 13:00:39 +05:30
Nabin Hait
8811a8c9b6 Translated payment type 2019-08-23 12:48:43 +05:30
Ernesto Ruiz
4f1b88ee0f fix: fix: Add transtlation function to strings (#18801)
fix: Add transtlation function to strings
2019-08-23 12:31:11 +05:30
Ernesto Ruiz
51a7aa9c76 Fix: Add translation function to string (#18802)
Fix: Add translation function to string
2019-08-23 12:28:50 +05:30
Sun Howwrongbum
33ce5ce048 fix: pos return validation (#18810) 2019-08-23 12:25:18 +05:30
Sagar Vora
b4a570242e fix: incorrect value being passed to get_taxes function (#18509) 2019-08-23 12:12:26 +05:30
Rohit Waghchaure
481977d368 fix: Actual qty in the sales order showing different on the sales order form and the standard sales order report 2019-08-23 11:49:26 +05:30
Deepesh Garg
479b2bbbd9 fix: Duplicate items check in sales Invoice (#18662)
* fix: Duplicate items check in sales Invoice

* fix: Commonified function for duplicate items check in selling controller

* fix: Set valuation rate and transaction date in child tabel

* fix: Code cleanup
2019-08-23 11:42:49 +05:30
rohitwaghchaure
4faf57869d fix: POS Sync Issue (#18808) 2019-08-23 11:13:54 +05:30
Mangesh-Khairnar
33eb38cc1b fix: fetch capital work in progress as expense account (#18781) 2019-08-21 14:49:14 +05:30
Nabin Hait
c6f72f7c90 fix: Single gl entry should only be considered once either in opening or closing entry (#18793) 2019-08-21 14:47:25 +05:30
rohitwaghchaure
d082a12411 Merge pull request #18798 from scmmishra/shopping-cart
fix: shopping cart item availability
2019-08-21 08:59:37 +05:30
Shivam Mishra
096de8a0d5 fix: shopping cart item availability 2019-08-20 19:18:45 +05:30
Sahil Khan
5638d63866 Merge branch 'version-11-hotfix' into version-11 2019-08-20 15:58:20 +05:30
Sahil Khan
a621466219 bumped to version 11.1.56 2019-08-20 16:18:20 +05:50
rohitwaghchaure
6155114bfd fix: group by voucher consolidated showing incorrect data for deferred entries (#18778) 2019-08-20 12:35:38 +05:30
Mangesh-Khairnar
2ca33d4f51 Merge pull request #18782 from deepeshgarg007/test_case_fix_v11
fix: Failing sales and purchase return test cases
2019-08-19 19:21:05 +05:30
deepeshgarg007
f1618af76c fix: Syntax error 2019-08-19 18:53:11 +05:30
deepeshgarg007
9f7098115b fix: Failing sales and purchase return test cases 2019-08-19 17:53:47 +05:30
Deepesh Garg
55b8b4e374 fix: Travis (#18773) 2019-08-19 12:56:49 +05:30
Deepesh Garg
7cb8c51b2d fix: Party dashboard heatmap not capturing sales, purchase and other activities (#18752) 2019-08-19 11:49:47 +05:30
Anurag Mishra
311fe5b3dc fix: removed filters(not required) (#18728) 2019-08-19 10:30:44 +05:30
Anurag Mishra
269868130f fix: validated cost center in financial_statement (#18732)
* fix: validated cost center in financial_statement

* Update financial_statements.py
2019-08-19 10:27:53 +05:30
Mangesh-Khairnar
736eed01c2 fix: notify update on status change (#18764) 2019-08-19 10:20:48 +05:30
Mangesh-Khairnar
e014b898bd fix: valuation rate in stock ledger (#18743)
* fix: valuation rate in stock ledger

* test: allow zero valuation rate for items
2019-08-19 10:04:34 +05:30
Sahil Khan
1eb31210e0 Merge branch 'version-11-hotfix' into version-11 2019-08-16 17:32:19 +05:30
Sahil Khan
8d72aa99aa bumped to version 11.1.55 2019-08-16 17:52:19 +05:50
Deepesh Garg
3a4503b7a1 Merge pull request #18760 from deepeshgarg007/chart_of_accounts_fix
fix: Symbol fix in chart of accounts
2019-08-16 15:48:34 +05:30
Mangesh-Khairnar
01952bbc15 Merge pull request #18714 from Mangesh-Khairnar/quality-inspection-fix
fix(quality-inspection): fetch all items for inspection type in process
2019-08-16 13:54:31 +05:30
Mangesh-Khairnar
1ca6365dee Merge branch 'version-11-hotfix' into quality-inspection-fix 2019-08-16 13:54:11 +05:30
deepeshgarg007
a7a810a25f fix: Remove extra space 2019-08-16 12:58:24 +05:30
Suraj Shetty
e62fba062e fix: Check if account passed is accessible under Payment Entry (#18706)
* fix: Check if account passed is accessible under Payment Entry

* fix: Comment description

* fix: Ignore account permission check

* fix: Re-organise code
2019-08-14 14:47:24 +05:30
Mangesh-Khairnar
60b7acc9b0 fix(payroll-entry): commit submitted salary slip check onchange (#18695)
* fix(payroll-entry): commit submitted salary slip check onchange

* fix: track submitted ss via flags
2019-08-14 14:39:33 +05:30
Deepesh Garg
d7ddb0c14e Merge pull request #18718 from Anurag810/company_fix_v11
fix: removed hard coded string
2019-08-14 12:12:03 +05:30
Anurag Mishra
8d84ffb276 fix: removed hard coded string 2019-08-13 19:47:20 +05:30
Mangesh-Khairnar
784c3333d7 fix(quality-inspection): fetch all items for inspection type in process 2019-08-13 17:04:29 +05:30
Sahil Khan
2227f54e66 Merge branch 'version-11-hotfix' into version-11 2019-08-13 14:41:58 +05:30
Sahil Khan
c55657dca5 bumped to version 11.1.54 2019-08-13 15:01:58 +05:50
Anurag Mishra
b6b2c48340 fix: condition (#18708) 2019-08-13 12:53:17 +05:30
Suraj Shetty
582c86a7b6 fix: Filters for portal quotation list (#18704)
* fix: Filters for portal quotation list

* fix: Remove unwanted import
2019-08-12 17:33:04 +05:30
rohitwaghchaure
76668e9004 Merge pull request #18701 from rohitwaghchaure/reconciled_entry_still_has_no_clearance_date
fix: reconciled entry has not clearance date set
2019-08-12 15:34:05 +05:30
Rohit Waghchaure
d0426a6127 fix: reconciled entry has not clearance date set 2019-08-12 14:56:57 +05:30
DeeMysterio
4252575e84 fix(delivery note): change the text invoice to sales invoice on make button (#18665) 2019-08-12 13:03:39 +05:30
Anurag Mishra
f6dcd8ac91 payment ammount valication for pos invoiceds (#18664) 2019-08-12 12:10:48 +05:30
Andrew McLeod
6a9fb51dc5 fix: Python3 urllib use in item_group.py (now uses six.moves) (#18605) 2019-08-12 12:07:07 +05:30
Anurag Mishra
70b93e2cd4 fix: condition (#18667) 2019-08-12 11:46:47 +05:30
rohitwaghchaure
f4493674f7 Merge pull request #18680 from Anurag810/get-item-from-product-bundle-v11
fix: get item from product bundle
2019-08-09 15:17:07 +05:30
Anurag Mishra
dd5a818d6a fix: get item from product bundle 2019-08-09 13:35:01 +05:30
rohitwaghchaure
e78dfb5df6 Merge pull request #18668 from rohitwaghchaure/removed_stock_item_condition_for_sub_contract
fix: allow to subcontract service raw materials
2019-08-09 12:42:01 +05:30
Rohit Waghchaure
429d0dcda0 fix: allow to subcotract service raw materials 2019-08-08 18:19:24 +05:30
rohitwaghchaure
eb174e1afa fix: incorrect value of ImponibileImporto in the xml invoice (#18655) 2019-08-08 17:42:11 +05:30
rohitwaghchaure
4a6b5367ca fix: not able to transfer raw materials for subcontracted items (#18649) 2019-08-08 17:41:33 +05:30
Mangesh-Khairnar
3675e616bc fix: accounting period (#18476)
* fix: accounting period

* test: accounting period

* fix: account period creation

* fix: remove status field from accounting period
2019-08-08 17:38:56 +05:30
Deepesh Garg
f9d3df9531 fix: Changes in print format due to attribute name changes in frappe (#18638) 2019-08-08 15:52:00 +05:30
Aditya Hase
081d312a11 fix(quickbooks): Do not build global search for QuickBooks Migrator (#18626) 2019-08-08 15:47:22 +05:30
Shivam Mishra
355e832b02 fix: icon for opportunity (#18651) 2019-08-08 13:18:46 +05:30
Deepesh Garg
6b65e2b168 fix: Show Cr or Dr symbol in chart of accounts based on balance (#18652)
* fix: Show Cr or Dr symbol in chart of accounts based on balance

* Update account_tree.js
2019-08-08 13:16:35 +05:30
Sahil Khan
57041084d1 Merge branch 'version-11-hotfix' into version-11 2019-08-07 16:17:49 +05:30
Sahil Khan
cf4c7b406a bumped to version 11.1.53 2019-08-07 16:37:49 +05:50
Anurag Mishra
edb3cdbb59 fix: query (#18465) 2019-08-05 10:27:48 +05:30
Mangesh-Khairnar
55025d4dc9 fix(sales-order): update items (#18537)
* fix(sales-order): update items

* fix: minor changes
2019-08-05 10:16:10 +05:30
Deepesh Garg
6f7a5e525d fix: Make conversion rate optional for non itemized items (#18538) 2019-08-05 10:14:37 +05:30
Deepesh Garg
342c77c851 fix: Customer price list not honored in shopping cart (#18555) 2019-08-05 10:11:07 +05:30
Suraj Shetty
94f4d81b70 fix: Quotation list in customer portal (#18580)
* fix: Quotation list in customer portal

Customers were not able to see their quotations because of
 the recent customer field removal from quotation

* fix: Remove duplicate code
2019-08-05 10:08:01 +05:30
Mangesh-Khairnar
74f31c6677 fix(employee-advance): update employee advance on change in expense claim (#18589)
* fix(employee-advance): update employee advance on rejection/cancellation of expense claim

* fix(expense-claim): display appropriate buttons only if linked transactions are valid

* Update employee_advance.py
2019-08-05 10:03:32 +05:30
Deepesh Garg
7589b9f822 Merge pull request #18575 from deepeshgarg007/taxes_and_totals_v11
fix: Error handling in taxes and totals
2019-08-03 18:27:52 +05:30
deepeshgarg007
b761169976 fix: Error handling in taxes and totals 2019-08-03 13:40:37 +05:30
Suraj Shetty
537c3a79ab fix: Show correct label instead of showing undefined (#18561) 2019-08-02 21:46:11 +05:30
193 changed files with 8020 additions and 6999 deletions

14
.github/workflows/docker-release.yml vendored Normal file
View File

@@ -0,0 +1,14 @@
name: Trigger Docker build on release
on:
release:
types: [released]
jobs:
curl:
runs-on: ubuntu-latest
container:
image: alpine:latest
steps:
- name: curl
run: |
apk add curl bash
curl -s -X POST -H "Content-Type: application/json" -H "Accept: application/json" -H "Travis-API-Version: 3" -H "Authorization: token ${{ secrets.TRAVIS_CI_TOKEN }}" -d '{"request":{"branch":"master"}}' https://api.travis-ci.com/repo/frappe%2Ffrappe_docker/requests

3
.gitignore vendored
View File

@@ -13,4 +13,5 @@ erpnext/docs/current
__pycache__
*~
.vscode/
node_modules/
node_modules/
.idea/

View File

@@ -76,5 +76,6 @@ install:
- bench --site test_site reinstall --yes
after_script:
- pip install coverage==4.5.4
- pip install python-coveralls
- coveralls -b apps/erpnext -d ../../sites/.coverage

View File

@@ -5,7 +5,7 @@ import frappe
from erpnext.hooks import regional_overrides
from frappe.utils import getdate
__version__ = '11.1.52'
__version__ = '11.1.77'
def get_default_company(user=None):
'''Get default company for user'''
@@ -144,4 +144,4 @@ def is_member():
last_membership = get_last_membership()
if last_membership and getdate(last_membership.to_date) > getdate():
return True
return False
return False

View File

@@ -174,6 +174,8 @@ def make_gl_entries(doc, credit_account, debit_account, against,
# GL Entry for crediting the amount in the deferred expense
from erpnext.accounts.general_ledger import make_gl_entries
if amount == 0: return
gl_entries = []
gl_entries.append(
doc.get_gl_dict({

View File

@@ -100,7 +100,10 @@ class Account(NestedSet):
if ancestors:
if frappe.get_value("Company", self.company, "allow_account_creation_against_child_company"):
return
frappe.throw(_("Please add the account to root level Company - %s" % ancestors[0]))
if not frappe.db.get_value("Account",
{'account_name': self.account_name, 'company': ancestors[0]}, 'name'):
frappe.throw(_("Please add the account to root level Company - %s" % ancestors[0]))
else:
descendants = get_descendants_of('Company', self.company)
if not descendants: return
@@ -114,21 +117,7 @@ class Account(NestedSet):
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_account": parent_acc_name_map[company]
})
doc.save()
frappe.msgprint(_("Account {0} is added in the child company {1}")
.format(doc.name, company))
self.create_account_for_child_company(parent_acc_name_map, descendants, parent_acc_name)
def validate_group_or_ledger(self):
if self.get("__islocal"):
@@ -170,6 +159,49 @@ class Account(NestedSet):
if frappe.db.get_value("GL Entry", {"account": self.name}):
frappe.throw(_("Currency can not be changed after making entries using some other currency"))
def create_account_for_child_company(self, parent_acc_name_map, descendants, parent_acc_name):
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))
filters = {
"account_name": self.account_name,
"company": company
}
if self.account_number:
filters["account_number"] = self.account_number
child_account = frappe.db.get_value("Account", filters, 'name')
if not child_account:
doc = frappe.copy_doc(self)
doc.flags.ignore_root_company_validation = True
doc.update({
"company": company,
# parent account's currency should be passed down to child account's curreny
# if it is None, it picks it up from default company currency, which might be unintended
"account_currency": self.account_currency,
"parent_account": parent_acc_name_map[company]
})
doc.save()
frappe.msgprint(_("Account {0} is added in the child company {1}")
.format(doc.name, company))
elif child_account:
# update the parent company's value in child companies
doc = frappe.get_doc("Account", child_account)
parent_value_changed = False
for field in ['account_type', 'account_currency',
'freeze_account', 'balance_must_be']:
if doc.get(field) != self.get(field):
parent_value_changed = True
doc.set(field, self.get(field))
if parent_value_changed:
doc.save()
def convert_group_to_ledger(self):
if self.check_if_child_exists():
throw(_("Account with child nodes cannot be converted to ledger"))

View File

@@ -121,7 +121,11 @@ frappe.treeview_settings["Account"] = {
},
onrender: function(node) {
if(frappe.boot.user.can_read.indexOf("GL Entry") !== -1){
var dr_or_cr = in_list(["Liability", "Income", "Equity"], node.data.root_type) ? "Cr" : "Dr";
// show Dr if positive since balance is calculated as debit - credit else show Cr
let balance = node.data.balance_in_account_currency || node.data.balance;
let dr_or_cr = balance > 0 ? "Dr": "Cr";
if (node.data && node.data.balance!==undefined) {
$('<span class="balance-area pull-right text-muted small">'
+ (node.data.balance_in_account_currency ?

View File

@@ -1,465 +1,466 @@
{
"country_code": "ae",
"name": "U.A.E - Chart of Accounts",
"country_code": "ae",
"name": "U.A.E - Chart of Accounts",
"tree": {
"Assets": {
"Current Assets": {
"Accounts Receivable": {
"Corporate Credit Cards": {
"account_type": "Receivable"
},
},
"Other Receivable": {
"Accrued Rebates Due from Suppliers": {
"account_type": "Receivable"
},
"Accured Income from Suppliers": {
},
"Accrued Income from Suppliers": {
"account_type": "Receivable"
},
},
"Other Debtors": {
"account_type": "Receivable"
},
},
"account_type": "Receivable"
},
},
"Post Dated Cheques Received": {
"account_type": "Receivable"
},
},
"Staff Receivable": {
"account_type": "Receivable"
},
},
"Trade Receivable": {
"account_type": "Receivable"
},
},
"Trade in Opening Fees": {
"account_type": "Receivable"
},
},
"account_type": "Receivable"
},
},
"Cash in Hand & Banks": {
"Banks": {
"Bank Margin On LC & LG": {},
"Banks Blocked Deposits": {},
"Banks Call Deposit Accounts": {},
"Bank Margin On LC & LG": {},
"Banks Blocked Deposits": {},
"Banks Call Deposit Accounts": {},
"Banks Current Accounts": {
"account_type": "Bank"
},
},
"account_type": "Bank"
},
},
"Cash in Hand": {
"Cash in Safe": {
"Main Safe": {
"account_type": "Cash"
},
},
"Main Safe - Foreign Currency": {
"account_type": "Cash"
}
},
},
"Petty Cash": {
"Petty Cash - Admininistration": {
"account_type": "Cash"
},
},
"Petty Cash - Others": {
"account_type": "Cash"
}
},
},
"account_type": "Cash"
},
},
"Cash in Transit": {
"Credit Cards": {
"Gateway Credit Cards": {
"account_type": "Bank"
},
},
"Manual Visa & Master Cards": {
"account_type": "Bank"
},
},
"PayPal Account": {
"account_type": "Bank"
},
},
"Visa & Master Credit Cards": {
"account_type": "Bank"
}
}
}
},
},
"Inventory": {
"Consigned Stock": {
"Handling Difference in Inventory": {
"account_type": "Stock Adjustment"
},
"Items Delivered to Customs on temprary Base": {}
},
"Handling Difference in Inventory": {},
"Items Delivered to Customs on temporary Base": {}
},
"Stock in Hand": {
"account_type": "Stock"
}
},
"Perliminary and Preoperating Expenses": {
},
"Preliminary and Preoperating Expenses": {
"Preoperating Expenses": {}
},
},
"Prepayments & Deposits": {
"Deposits": {
"Deposit - Office Rent": {},
"Deposit Others": {},
"Deposit to Immigration (Visa)": {},
"Deposit - Office Rent": {},
"Deposit Others": {},
"Deposit to Immigration (Visa)": {},
"Deposits - Customs": {}
},
},
"Prepaid Taxes": {
"Sales Taxes Receivables": {},
"Sales Taxes Receivables": {},
"Withholding Tax Receivables": {}
},
},
"Prepayments": {
"Other Prepayments": {},
"PrePaid Advertisement Expenses": {},
"Prepaid Bank Guarantee": {},
"Prepaid Consultancy Fees": {},
"Prepaid Employees Housing": {},
"Prepaid Finance charge for Loans": {},
"Prepaid Legal Fees": {},
"Prepaid License Fees": {},
"Prepaid Life Insurance": {},
"Prepaid Maintenance": {},
"Prepaid Medical Insurance": {},
"Prepaid Office Rent": {},
"Prepaid Other Insurance": {},
"Prepaid Schooling Fees": {},
"Prepaid Site Hosting Fees": {},
"Other Prepayments": {},
"PrePaid Advertisement Expenses": {},
"Prepaid Bank Guarantee": {},
"Prepaid Consultancy Fees": {},
"Prepaid Employees Housing": {},
"Prepaid Finance charge for Loans": {},
"Prepaid Legal Fees": {},
"Prepaid License Fees": {},
"Prepaid Life Insurance": {},
"Prepaid Maintenance": {},
"Prepaid Medical Insurance": {},
"Prepaid Office Rent": {},
"Prepaid Other Insurance": {},
"Prepaid Schooling Fees": {},
"Prepaid Site Hosting Fees": {},
"Prepaid Sponsorship Fees": {}
}
}
},
},
"Long Term Assets": {
"Fixed Assets": {
"Accumulated Depreciation": {
"Acc. Depreciation of Motor Vehicles": {
"account_type": "Accumulated Depreciation"
},
},
"Acc. Deprn.Computer Hardware & Software": {
"account_type": "Accumulated Depreciation"
},
},
"Acc.Deprn.of Furniture & Office Equipment": {
"account_type": "Accumulated Depreciation"
},
},
"Amortisation on Leasehold Improvement": {
"account_type": "Accumulated Depreciation"
},
},
"account_type": "Accumulated Depreciation"
},
},
"Fixed Assets (Cost Price)": {
"Computer Hardware & Software": {
"account_type": "Fixed Asset"
},
},
"Furniture and Equipment": {
"account_type": "Fixed Asset"
},
"Leasehold Improvement": {},
"Motor Vehicules": {
},
"Leasehold Improvement": {},
"Motor Vehicles": {
"account_type": "Fixed Asset"
},
"Work In Progrees": {},
},
"Work In Progress": {},
"account_type": "Fixed Asset"
}
},
},
"Intangible Assets": {
"Computer Card Renewal": {},
"Dispoal of Outlets": {},
"Computer Card Renewal": {},
"Disposal of Outlets": {},
"Registration of Trademarks": {}
},
"Intercompany Accounts": {},
},
"Intercompany Accounts": {},
"Investments": {
"Investments in Subsidiaries": {}
}
},
},
"root_type": "Asset"
},
},
"Closing And Temporary Accounts": {
"Closing Accounts": {
"Closing Account": {}
},
},
"root_type": "Liability"
},
},
"Expenses": {
"Commercial Expenses": {
"Consultancy Fees": {},
"Consultancy Fees": {},
"Provision for Doubtful Debts": {}
},
},
"Cost of Sale": {
"Cost Of Goods Sold": {
"Cost Of Goods Sold I/C Sales": {},
"Cost Of Goods Sold I/C Sales": {},
"Cost of Goods Sold in Trading": {
"account_type": "Cost of Goods Sold"
},
},
"account_type": "Cost of Goods Sold"
},
},
"Expenses Included In Valuation": {
"account_type": "Expenses Included In Valuation"
},
"Stock Adjustment": {
"account_type": "Stock Adjustment"
}
},
},
"Depreciation": {
"Depreciation & Amortization": {
"Amortization on Leasehold Improvement": {},
"Amortization on Leasehold Improvement": {},
"Depreciation Of Computer Hard & Soft": {
"account_type": "Depreciation"
},
},
"Depreciation Of Furniture & Office Equipment\n\t\t\t": {
"account_type": "Depreciation"
},
},
"Depreciation Of Motor Vehicles": {
"account_type": "Depreciation"
}
}
},
},
"Direct Expenses": {
"Financial Charges": {
"Air Miles Card Charges": {},
"Amex Credit Cards Charges": {},
"Bank Finance & Loan Charges": {},
"Credit Card Charges": {},
"Credit Card Swipe Charges": {},
"Air Miles Card Charges": {},
"Amex Credit Cards Charges": {},
"Bank Finance & Loan Charges": {},
"Credit Card Charges": {},
"Credit Card Swipe Charges": {},
"PayPal Charges": {}
}
},
},
"MISC Charges": {
"Other Charges": {
"Captial Loss": {
"Disposal of Business Branch": {},
"Loss On Fixed Assets Disposal": {},
"Capital Loss": {
"Disposal of Business Branch": {},
"Loss On Fixed Assets Disposal": {},
"Loss on Difference on Exchange": {}
},
},
"Other Non Operating Exp": {
"Other Non Operating Expenses": {}
},
},
"Previous Year Adjustments": {
"Previous Year Adjustments Account": {}
},
},
"Royalty Fees": {
"Royalty to Parent Co.": {}
},
},
"Tax / Zakat Expenses": {
"Income Tax": {
"account_type": "Tax"
},
"Zakat": {},
},
"Zakat": {},
"account_type": "Tax"
}
}
},
},
"Share Resources": {
"Share Resource Expenses Account": {}
},
},
"Store Operating Expenses": {
"Selling, General & Admin Expenses": {
"Advertising Expenses": {
"Other - Advertising Expenses": {}
},
},
"Bank & Finance Charges": {
"Other Bank Charges": {}
},
},
"Communications": {
"Courrier": {},
"Others - Communication": {},
"Telephone": {},
"Courier": {},
"Others - Communication": {},
"Telephone": {},
"Web Site Hosting Fees": {}
},
},
"Office & Various Expenses": {
"Cleaning": {},
"Convoyance Expenses": {},
"Gifts & Donations": {},
"Insurance": {},
"Kitchen and Buffet Expenses": {},
"Maintenance": {},
"Others - Office Various Expenses": {},
"Security & Guard": {},
"Stationary From Suppliers": {},
"Stationary Out Of Stock": {},
"Subscriptions": {},
"Training": {},
"Cleaning": {},
"Conveyance Expenses": {},
"Gifts & Donations": {},
"Insurance": {},
"Kitchen and Buffet Expenses": {},
"Maintenance": {},
"Others - Office Various Expenses": {},
"Security & Guard": {},
"Stationary From Suppliers": {},
"Stationary Out Of Stock": {},
"Subscriptions": {},
"Training": {},
"Vehicle Expenses": {}
},
},
"Personnel Cost": {
"Basic Salary": {},
"End Of Service Indemnity": {},
"Housing Allowance": {},
"Leave Salary": {},
"Leave Ticket": {},
"Life Insurance": {},
"Medical Insurance": {},
"Personnel Cost Others": {},
"Sales Commission": {},
"Staff School Allowances": {},
"Transportation Allowance": {},
"Uniform": {},
"Basic Salary": {},
"End Of Service Indemnity": {},
"Housing Allowance": {},
"Leave Salary": {},
"Leave Ticket": {},
"Life Insurance": {},
"Medical Insurance": {},
"Personnel Cost Others": {},
"Sales Commission": {},
"Staff School Allowances": {},
"Transportation Allowance": {},
"Uniform": {},
"Visa Expenses": {}
},
},
"Professional & Legal Fees": {
"Audit Fees": {},
"Legal fees": {},
"Others - Professional Fees": {},
"Sponsorship Fees": {},
"Audit Fees": {},
"Legal fees": {},
"Others - Professional Fees": {},
"Sponsorship Fees": {},
"Trade License Fees": {}
},
},
"Provision & Write Off": {
"Amortisation of Preoperating Expenses": {},
"Cash Shortage": {},
"Others - Provision & Write off": {},
"Write Off Inventory": {},
"Amortisation of Preoperating Expenses": {},
"Cash Shortage": {},
"Others - Provision & Write off": {},
"Write Off Inventory": {},
"Write Off Receivables & Payables": {}
},
},
"Rent Expenses": {
"Office Rent": {},
"Office Rent": {},
"Warehouse Rent": {}
},
},
"Travel Expenses": {
"Air tickets": {},
"Hotel": {},
"Meals": {},
"Others": {},
"Air tickets": {},
"Hotel": {},
"Meals": {},
"Others": {},
"Per Diem": {}
},
},
"Utilities": {
"Other Utility Cahrges": {},
"Other Utility Cahrges": {},
"Water & Electricity": {}
}
}
},
},
"root_type": "Expense"
},
},
"Liabilities": {
"Current Liabilities": {
"Accounts Payable": {
"Payables": {
"Advance Paybale to Suppliers": {
"account_type": "Payable"
},
},
"Consigned Payable": {
"account_type": "Payable"
},
},
"Other Payable": {
"account_type": "Payable"
},
},
"Post Dated Cheques Paid": {
"account_type": "Payable"
},
"Staff Payable": {},
},
"Staff Payable": {},
"Suppliers Price Protection": {
"account_type": "Payable"
},
},
"Trade Payable": {
"account_type": "Payable"
},
},
"account_type": "Payable"
}
},
},
"Accruals & Provisions": {
"Accruals": {
"Accrued Personnel Cost": {
"Accrued - Commissions": {},
"Accrued - Leave Salary": {},
"Accrued - Leave Tickets": {},
"Accrued - Salaries": {},
"Accrued Other Personnel Cost": {},
"Accrued Salaries Increment": {},
"Accrued - Commissions": {},
"Accrued - Leave Salary": {},
"Accrued - Leave Tickets": {},
"Accrued - Salaries": {},
"Accrued Other Personnel Cost": {},
"Accrued Salaries Increment": {},
"Accrued-Staff Bonus": {}
}
},
},
"Accrued Expenses": {
"Accrued Other Expenses": {
"Accrued - Audit Fees": {},
"Accrued - Office Rent": {},
"Accrued - Sponsorship": {},
"Accrued - Telephone": {},
"Accrued - Utilities": {},
"Accrued - Audit Fees": {},
"Accrued - Office Rent": {},
"Accrued - Sponsorship": {},
"Accrued - Telephone": {},
"Accrued - Utilities": {},
"Accrued Others": {}
}
},
},
"Other Current Liabilities": {
"Accrued Dubai Customs": {},
"Deferred income": {},
"Accrued Dubai Customs": {},
"Deferred income": {},
"Shipping & Handling": {}
},
},
"Provisions": {
"Tax Payables": {
"Income Tax Payable": {},
"Sales Tax Payable": {},
"Income Tax Payable": {},
"Sales Tax Payable": {},
"Withholding Tax Payable": {}
}
},
},
"Short Term Loan": {}
},
},
"Duties and Taxes": {
"account_type": "Tax",
"account_type": "Tax",
"is_group": 1
},
},
"Reservations & Credit Notes": {
"Credit Notes": {
"Credit Notes to Customers": {},
"Credit Notes to Customers": {},
"Reservations": {}
}
},
},
"Stock Liabilities": {
"Stock Received But Not Billed": {
"account_type": "Stock Received But Not Billed"
}
},
},
"Unearned Income": {}
},
},
"Long Term Liabilities": {
"Long Term Loans & Provisions": {}
},
},
"root_type": "Liability"
},
},
"Revenue": {
"Direct Revenue": {
"Other Direct Revenue": {
"Other Revenue - Operating": {
"Advertising Income": {},
"Branding Income": {},
"Early Setmt Margin from Suppliers": {},
"Marketing Rebate from Suppliers": {},
"Rebate from Suppliers": {},
"Service Income": {},
"Advertising Income": {},
"Branding Income": {},
"Early Setmt Margin from Suppliers": {},
"Marketing Rebate from Suppliers": {},
"Rebate from Suppliers": {},
"Service Income": {},
"Space Rental Income": {}
}
}
},
},
"Indirect Revenue": {
"Other Indirect Revenue": {
"Capital Gain": {},
"Excess In Till": {},
"Gain On Difference Of Exchange": {},
"Management Consultancy Fees": {},
"Capital Gain": {},
"Excess In Till": {},
"Gain On Difference Of Exchange": {},
"Management Consultancy Fees": {},
"Other Income": {}
},
},
"Other Revenue - Non Operating": {
"Interest Revenue": {},
"Interest from FD": {},
"Products Listing Fees from Suppliers": {},
"Interest Revenue": {},
"Interest from FD": {},
"Products Listing Fees from Suppliers": {},
"Trade Opening Fees from suppliers": {}
}
},
},
"Sales": {
"Sales from Other Regions": {
"Sales from Other Region": {}
},
},
"Sales of same region": {
"Management Consultancy Fees 1": {},
"Sales Account": {},
"Management Consultancy Fees 1": {},
"Sales Account": {},
"Sales of I/C": {}
}
},
},
"root_type": "Income"
},
},
"Share Holder Equity": {
"Capital": {
"Contributed Capital": {},
"Share Capital": {},
"Shareholders Current A/c": {},
"Sub Ordinated Loan": {},
"Contributed Capital": {},
"Share Capital": {},
"Shareholders Current A/c": {},
"Sub Ordinated Loan": {},
"Treasury Stocks": {}
},
},
"Retained Earnings": {
"Current Year Results": {},
"Dividends Paid": {},
"Current Year Results": {},
"Dividends Paid": {},
"Previous Years Results": {}
},
"account_type": "Equity",
},
"account_type": "Equity",
"root_type": "Equity"
}
}

View File

@@ -167,39 +167,7 @@
"set_only_once": 0,
"translatable": 0,
"unique": 0
},
{
"allow_bulk_edit": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"fieldname": "status",
"fieldtype": "Select",
"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": "Status",
"length": 0,
"no_copy": 0,
"options": "Open\nClosed",
"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_on_submit": 0,
@@ -273,7 +241,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2018-04-13 19:14:47.593753",
"modified": "2019-08-01 19:14:47.593753",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounting Period",

View File

@@ -4,8 +4,10 @@
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.model.document import Document
from frappe import _
class OverlapError(frappe.ValidationError): pass
class AccountingPeriod(Document):
def validate(self):
@@ -34,12 +36,13 @@ class AccountingPeriod(Document):
}, as_dict=True)
if len(existing_accounting_period) > 0:
frappe.throw(_("Accounting Period overlaps with {0}".format(existing_accounting_period[0].get("name"))))
frappe.throw(_("Accounting Period overlaps with {0}")
.format(existing_accounting_period[0].get("name")), OverlapError)
def get_doctypes_for_closing(self):
docs_for_closing = []
#if not self.closed_documents or len(self.closed_documents) == 0:
doctypes = ["Sales Invoice", "Purchase Invoice", "Journal Entry", "Payroll Entry", "Bank Reconciliation", "Asset", "Purchase Order", "Sales Order", "Leave Application", "Leave Allocation", "Stock Entry"]
doctypes = ["Sales Invoice", "Purchase Invoice", "Journal Entry", "Payroll Entry", "Bank Reconciliation",
"Asset", "Purchase Order", "Sales Order", "Leave Application", "Leave Allocation", "Stock Entry"]
closed_doctypes = [{"document_type": doctype, "closed": 1} for doctype in doctypes]
for closed_doctype in closed_doctypes:
docs_for_closing.append(closed_doctype)
@@ -52,4 +55,4 @@ class AccountingPeriod(Document):
self.append('closed_documents', {
"document_type": doctype_for_closing.document_type,
"closed": doctype_for_closing.closed
})
})

View File

@@ -5,23 +5,42 @@ from __future__ import unicode_literals
import frappe
import unittest
from frappe.utils import nowdate, add_months
from erpnext.accounts.general_ledger import ClosedAccountingPeriod
from erpnext.accounts.doctype.accounting_period.accounting_period import OverlapError
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
# class TestAccountingPeriod(unittest.TestCase):
# def test_overlap(self):
# ap1 = create_accounting_period({"start_date":"2018-04-01", "end_date":"2018-06-30", "company":"Wind Power LLC"})
# ap1.save()
# ap2 = create_accounting_period({"start_date":"2018-06-30", "end_date":"2018-07-10", "company":"Wind Power LLC"})
# self.assertRaises(frappe.OverlapError, accounting_period_2.save())
#
# def tearDown(self):
# pass
#
#
# def create_accounting_period(**args):
# accounting_period = frappe.new_doc("Accounting Period")
# accounting_period.start_date = args.start_date or frappe.utils.datetime.date(2018, 4, 1)
# accounting_period.end_date = args.end_date or frappe.utils.datetime.date(2018, 6, 30)
# accounting_period.company = args.company
# accounting_period.period_name = "_Test_Period_Name_1"
#
# return accounting_period
class TestAccountingPeriod(unittest.TestCase):
def test_overlap(self):
ap1 = create_accounting_period(start_date = "2018-04-01",
end_date = "2018-06-30", company = "Wind Power LLC")
ap1.save()
ap2 = create_accounting_period(start_date = "2018-06-30",
end_date = "2018-07-10", company = "Wind Power LLC", period_name = "Test Accounting Period 1")
self.assertRaises(OverlapError, ap2.save)
def test_accounting_period(self):
ap1 = create_accounting_period(period_name = "Test Accounting Period 2")
ap1.save()
doc = create_sales_invoice(do_not_submit=1, cost_center = "_Test Company - _TC", warehouse = "Stores - _TC")
self.assertRaises(ClosedAccountingPeriod, doc.submit)
def tearDown(self):
for d in frappe.get_all("Accounting Period"):
frappe.delete_doc("Accounting Period", d.name)
def create_accounting_period(**args):
args = frappe._dict(args)
accounting_period = frappe.new_doc("Accounting Period")
accounting_period.start_date = args.start_date or nowdate()
accounting_period.end_date = args.end_date or add_months(nowdate(), 1)
accounting_period.company = args.company or "_Test Company"
accounting_period.period_name =args.period_name or "_Test_Period_Name_1"
accounting_period.append("closed_documents", {
"document_type": 'Sales Invoice', "closed": 1
})
return accounting_period

View File

@@ -6,6 +6,7 @@ from __future__ import unicode_literals
import frappe, json
from frappe.model.document import Document
from frappe import _
from frappe.desk.search import sanitize_searchfield
class BankGuarantee(Document):
def validate(self):
@@ -22,5 +23,8 @@ class BankGuarantee(Document):
@frappe.whitelist()
def get_vouchar_detials(column_list, doctype, docname):
column_list = json.loads(column_list)
for col in column_list:
sanitize_searchfield(col)
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

@@ -50,7 +50,7 @@ class BankTransaction(StatusUpdater):
if paid_amount and allocated_amount:
if flt(allocated_amount[0]["allocated_amount"]) > flt(paid_amount):
frappe.throw(_("The total allocated amount ({0}) is greated than the paid amount ({1}).".format(flt(allocated_amount[0]["allocated_amount"]), flt(paid_amount))))
elif flt(allocated_amount[0]["allocated_amount"]) == flt(paid_amount):
else:
if payment_entry.payment_document in ["Payment Entry", "Journal Entry", "Purchase Invoice", "Expense Claim"]:
self.clear_simple_entry(payment_entry)

View File

@@ -92,10 +92,10 @@ frappe.ui.form.on("Journal Entry", {
multi_currency: function(frm) {
erpnext.journal_entry.toggle_fields_based_on_currency(frm);
},
posting_date: function(frm) {
if(!frm.doc.multi_currency || !frm.doc.posting_date) return;
$.each(frm.doc.accounts || [], function(i, row) {
erpnext.journal_entry.set_exchange_rate(frm, row.doctype, row.name);
})
@@ -167,7 +167,7 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
filters: {
'account': row.account
}
}
};
});
me.frm.set_query("reference_name", "accounts", function(doc, cdt, cdn) {
@@ -234,7 +234,7 @@ erpnext.accounts.JournalEntry = frappe.ui.form.Controller.extend({
out.filters.push([jvd.reference_type, "per_billed", "<", 100]);
}
if(jvd.party_type && jvd.party) {
var party_field = "";
if(jvd.reference_type.indexOf("Sales")===0) {
@@ -382,7 +382,7 @@ cur_frm.cscript.voucher_type = function(doc, cdt, cdn) {
});
refresh_field("accounts");
}
if((!(doc.accounts || []).length) || ((doc.accounts || []).length==1 && !doc.accounts[0].account)) {
if(in_list(["Bank Entry", "Cash Entry"], doc.voucher_type)) {
return frappe.call({
@@ -390,7 +390,7 @@ cur_frm.cscript.voucher_type = function(doc, cdt, cdn) {
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_default_bank_cash_account",
args: {
"account_type": (doc.voucher_type=="Bank Entry" ?
"Bank" : (doc.voucher_type=="Cash" ? "Cash" : null)),
"Bank" : (doc.voucher_type=="Cash Entry" ? "Cash" : null)),
"company": doc.company
},
callback: function(r) {
@@ -442,7 +442,7 @@ frappe.ui.form.on("Journal Entry Account", {
account: function(frm, dt, dn) {
erpnext.journal_entry.set_account_balance(frm, dt, dn);
},
debit_in_account_currency: function(frm, cdt, cdn) {
erpnext.journal_entry.set_exchange_rate(frm, cdt, cdn);
},

View File

@@ -294,7 +294,7 @@ frappe.ui.form.on('Payment Entry', {
() => {
frm.set_party_account_based_on_party = false;
if (r.message.bank_account) {
frm.set_value("bank_account", r.message.bank_account);
frm.set_value("party_bank_account", r.message.bank_account);
}
}
]);

View File

@@ -1791,6 +1791,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": "Draft",
"fetch_if_empty": 0,
"fieldname": "status",
"fieldtype": "Select",
"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": "Status",
"length": 0,
"no_copy": 0,
"options": "\nDraft\nSubmitted\nCancelled",
"permlevel": 0,
"precision": "",
"print_hide": 0,
"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,
@@ -2205,7 +2240,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2019-03-27 17:39:54.163016",
"modified": "2019-11-06 15:15:45.223497",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Entry",

View File

@@ -60,6 +60,7 @@ class PaymentEntry(AccountsController):
self.validate_duplicate_entry()
self.validate_allocated_amount()
self.ensure_supplier_is_not_blocked()
self.set_status()
def on_submit(self):
self.setup_party_account_field()
@@ -69,6 +70,7 @@ class PaymentEntry(AccountsController):
self.update_outstanding_amounts()
self.update_advance_paid()
self.update_expense_claim()
self.set_status()
def on_cancel(self):
@@ -78,6 +80,7 @@ class PaymentEntry(AccountsController):
self.update_advance_paid()
self.update_expense_claim()
self.delink_advance_entry_references()
self.set_status()
def update_outstanding_amounts(self):
self.set_missing_ref_details(force=True)
@@ -274,6 +277,14 @@ class PaymentEntry(AccountsController):
frappe.throw(_("Against Journal Entry {0} does not have any unmatched {1} entry")
.format(d.reference_name, dr_or_cr))
def set_status(self):
if self.docstatus == 2:
self.status = 'Cancelled'
elif self.docstatus == 1:
self.status = 'Submitted'
else:
self.status = 'Draft'
def set_amounts(self):
self.set_amounts_in_company_currency()
self.set_total_allocated_amount()
@@ -613,13 +624,18 @@ def get_orders_to_be_billed(posting_date, party_type, party, party_account_curre
orders = []
if voucher_type:
ref_field = "base_grand_total" if party_account_currency == company_currency else "grand_total"
if party_account_currency == company_currency:
grand_total_field = "base_grand_total"
rounded_total_field = "base_rounded_total"
else:
grand_total_field = "grand_total"
rounded_total_field = "rounded_total"
orders = frappe.db.sql("""
select
name as voucher_no,
{ref_field} as invoice_amount,
({ref_field} - advance_paid) as outstanding_amount,
if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) as invoice_amount,
(if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) - advance_paid) as outstanding_amount,
transaction_date as posting_date
from
`tab{voucher_type}`
@@ -627,17 +643,18 @@ def get_orders_to_be_billed(posting_date, party_type, party, party_account_curre
{party_type} = %s
and docstatus = 1
and ifnull(status, "") != "Closed"
and {ref_field} > advance_paid
and if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) > advance_paid
and abs(100 - per_billed) > 0.01
{condition}
order by
transaction_date, name
""".format(**{
"ref_field": ref_field,
"rounded_total_field": rounded_total_field,
"grand_total_field": grand_total_field,
"voucher_type": voucher_type,
"party_type": scrub(party_type),
"condition": condition
}), party, as_dict=True)
}), (party), as_dict=True)
order_list = []
for d in orders:
@@ -713,9 +730,23 @@ def get_party_details(company, party_type, party, date, cost_center=None):
@frappe.whitelist()
def get_account_details(account, date, cost_center=None):
frappe.has_permission('Payment Entry', throw=True)
# to check if the passed account is accessible if the reference doctype is Payment Entry
account_list = frappe.get_list('Account', {
'name': account
}, reference_doctype='Payment Entry', limit=1)
# There might be some user permissions which will allow account under certain doctypes
# except for Payment Entry, only in such case we should throw permission error
if not account_list:
frappe.throw(_('Account: {0} is not permitted under Payment Entry').format(account))
account_balance = get_balance_on(account, date, cost_center=cost_center,
ignore_account_permission=True)
return frappe._dict({
"account_currency": get_account_currency(account),
"account_balance": get_balance_on(account, date, cost_center=cost_center),
"account_balance": account_balance,
"account_type": frappe.db.get_value("Account", account, "account_type")
})

View File

@@ -1,7 +1,6 @@
cur_frm.add_fetch("payment_gateway", "payment_account", "payment_account")
cur_frm.add_fetch("payment_gateway", "payment_gateway", "payment_gateway")
cur_frm.add_fetch("payment_gateway", "message", "message")
cur_frm.add_fetch("payment_gateway", "payment_url_message", "payment_url_message")
cur_frm.add_fetch("payment_gateway_account", "payment_account", "payment_account")
cur_frm.add_fetch("payment_gateway_account", "payment_gateway", "payment_gateway")
cur_frm.add_fetch("payment_gateway_account", "message", "message")
frappe.ui.form.on("Payment Request", "onload", function(frm, dt, dn){
if (frm.doc.reference_doctype) {

View File

@@ -2454,7 +2454,7 @@
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "other_charges_calculation",
"fieldtype": "Text",
"fieldtype": "Long Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@@ -4903,7 +4903,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2019-04-22 12:45:49.728359",
"modified": "2020-04-16 19:04:18.599264",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice",

View File

@@ -250,7 +250,7 @@ class PurchaseInvoice(BuyingController):
def set_against_expense_account(self):
against_accounts = []
for item in self.get("items"):
if item.expense_account not in against_accounts:
if item.expense_account and (item.expense_account not in against_accounts):
against_accounts.append(item.expense_account)
self.against_expense_account = ",".join(against_accounts)
@@ -363,7 +363,7 @@ class PurchaseInvoice(BuyingController):
update_outstanding = "No" if (cint(self.is_paid) or self.write_off_account) else "Yes"
make_gl_entries(gl_entries, cancel=(self.docstatus == 2),
update_outstanding=update_outstanding, merge_entries=False)
update_outstanding=update_outstanding, merge_entries=False, from_repost=from_repost)
if update_outstanding == "No":
update_outstanding_amt(self.credit_to, "Supplier", self.supplier,
@@ -750,7 +750,11 @@ class PurchaseInvoice(BuyingController):
)
def make_gle_for_rounding_adjustment(self, gl_entries):
if self.rounding_adjustment:
# if rounding adjustment in small and conversion rate is also small then
# base_rounding_adjustment may become zero due to small precision
# eg: rounding_adjustment = 0.01 and exchange rate = 0.05 and precision of base_rounding_adjustment is 2
# then base_rounding_adjustment becomes zero and error is thrown in GL Entry
if self.rounding_adjustment and self.base_rounding_adjustment:
round_off_account, round_off_cost_center = \
get_round_off_account_and_cost_center(self.company)

View File

@@ -16,7 +16,7 @@ frappe.listview_settings['Purchase Invoice'] = {
} else if(frappe.datetime.get_diff(doc.due_date) < 0) {
return [__("Overdue"), "red", "outstanding_amount,>,0|due_date,<,Today"];
} else {
return [__("Unpaid"), "orange", "outstanding_amount,>,0|due,>=,Today"];
return [__("Unpaid"), "orange", "outstanding_amount,>,0|due_date,>=,Today"];
}
} else if(cint(doc.is_return)) {
return [__("Return"), "darkgrey", "is_return,=,Yes"];

View File

@@ -41,6 +41,8 @@ def get_pos_data():
items_list = get_items_list(pos_profile, doc.company)
customers = get_customers_list(pos_profile)
doc.plc_conversion_rate = update_plc_conversion_rate(doc, pos_profile)
return {
'doc': doc,
'default_customer': pos_profile.get('customer'),
@@ -53,7 +55,7 @@ def get_pos_data():
'batch_no_data': get_batch_no_data(),
'barcode_data': get_barcode_data(items_list),
'tax_data': get_item_tax_data(),
'price_list_data': get_price_list_data(doc.selling_price_list),
'price_list_data': get_price_list_data(doc.selling_price_list, doc.plc_conversion_rate),
'customer_wise_price_list': get_customer_wise_price_list(),
'bin_data': get_bin_data(pos_profile),
'pricing_rules': get_pricing_rule_data(doc),
@@ -62,6 +64,15 @@ def get_pos_data():
'meta': get_meta()
}
def update_plc_conversion_rate(doc, pos_profile):
conversion_rate = 1.0
price_list_currency = frappe.get_cached_value("Price List", doc.selling_price_list, "currency")
if pos_profile.get("currency") != price_list_currency:
conversion_rate = get_exchange_rate(price_list_currency,
pos_profile.get("currency"), nowdate(), args="for_selling") or 1.0
return conversion_rate
def get_meta():
doctype_meta = {
@@ -317,14 +328,14 @@ def get_item_tax_data():
return itemwise_tax
def get_price_list_data(selling_price_list):
def get_price_list_data(selling_price_list, conversion_rate):
itemwise_price_list = {}
price_lists = frappe.db.sql("""Select ifnull(price_list_rate, 0) as price_list_rate,
item_code from `tabItem Price` ip where price_list = %(price_list)s""",
{'price_list': selling_price_list}, as_dict=1)
for item in price_lists:
itemwise_price_list[item.item_code] = item.price_list_rate
itemwise_price_list[item.item_code] = item.price_list_rate * conversion_rate
return itemwise_price_list
@@ -432,7 +443,6 @@ def get_customer_id(doc, customer=None):
return cust_id
def make_customer_and_address(customers):
customers_list = []
for customer, data in iteritems(customers):
@@ -449,7 +459,6 @@ def make_customer_and_address(customers):
frappe.db.commit()
return customers_list
def add_customer(data):
customer = data.get('full_name') or data.get('customer')
if frappe.db.exists("Customer", customer.strip()):
@@ -466,21 +475,18 @@ def add_customer(data):
frappe.db.commit()
return customer_doc.name
def get_territory(data):
if data.get('territory'):
return data.get('territory')
return frappe.db.get_single_value('Selling Settings','territory') or _('All Territories')
def get_customer_group(data):
if data.get('customer_group'):
return data.get('customer_group')
return frappe.db.get_single_value('Selling Settings', 'customer_group') or frappe.db.get_value('Customer Group', {'is_group': 0}, 'name')
def make_contact(args, customer):
if args.get('email_id') or args.get('phone'):
name = frappe.db.get_value('Dynamic Link',
@@ -506,7 +512,6 @@ def make_contact(args, customer):
doc.flags.ignore_mandatory = True
doc.save(ignore_permissions=True)
def make_address(args, customer):
if not args.get('address_line1'):
return
@@ -521,7 +526,10 @@ def make_address(args, customer):
address = frappe.get_doc('Address', name)
else:
address = frappe.new_doc('Address')
address.country = frappe.get_cached_value('Company', args.get('company'), 'country')
if args.get('company'):
address.country = frappe.get_cached_value('Company',
args.get('company'), 'country')
address.append('links', {
'link_doctype': 'Customer',
'link_name': customer
@@ -533,7 +541,6 @@ def make_address(args, customer):
address.flags.ignore_mandatory = True
address.save(ignore_permissions=True)
def make_email_queue(email_queue):
name_list = []
for key, data in iteritems(email_queue):
@@ -550,7 +557,6 @@ def make_email_queue(email_queue):
return name_list
def validate_item(doc):
for item in doc.get('items'):
if not frappe.db.exists('Item', item.get('item_code')):
@@ -569,7 +575,6 @@ def validate_item(doc):
item_doc.save(ignore_permissions=True)
frappe.db.commit()
def submit_invoice(si_doc, name, doc, name_list):
try:
si_doc.insert()
@@ -585,7 +590,6 @@ def submit_invoice(si_doc, name, doc, name_list):
return name_list
def save_invoice(doc, name, name_list):
try:
if not frappe.db.exists('Sales Invoice', {'offline_pos_name': name}):

View File

@@ -141,7 +141,7 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
cur_frm.meta._default_print_format = cur_frm.meta.default_print_format;
cur_frm.meta.default_print_format = cur_frm.pos_print_format;
}
} else if(cur_frm.doc.is_return) {
} else if(cur_frm.doc.is_return && !cur_frm.meta.default_print_format) {
if(cur_frm.return_print_format) {
cur_frm.meta._default_print_format = cur_frm.meta.default_print_format;
cur_frm.meta.default_print_format = cur_frm.return_print_format;

View File

@@ -2544,7 +2544,7 @@
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "other_charges_calculation",
"fieldtype": "Text",
"fieldtype": "Long Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@@ -5816,7 +5816,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2019-04-22 12:45:41.109345",
"modified": "2020-04-16 19:05:00.498772",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",

View File

@@ -78,6 +78,7 @@ class SalesInvoice(SellingController):
self.so_dn_required()
self.validate_proj_cust()
self.validate_pos_return()
self.validate_with_previous_doc()
self.validate_uom_is_integer("stock_uom", "stock_qty")
self.validate_uom_is_integer("uom", "qty")
@@ -199,6 +200,16 @@ class SalesInvoice(SellingController):
if "Healthcare" in active_domains:
manage_invoice_submit_cancel(self, "on_submit")
def validate_pos_return(self):
if self.is_pos and self.is_return:
total_amount_in_payments = 0
for payment in self.payments:
total_amount_in_payments += payment.amount
invoice_total = self.rounded_total or self.grand_total
if flt(total_amount_in_payments, self.precision("grand_total")) < invoice_total:
frappe.throw(_("Total payments amount can't be greater than {}".format(-invoice_total)))
def validate_pos_paid_amount(self):
if len(self.payments) == 0 and self.is_pos:
frappe.throw(_("At least one mode of payment is required for POS invoice."))
@@ -339,7 +350,7 @@ class SalesInvoice(SellingController):
timesheet.calculate_percentage_billed()
timesheet.flags.ignore_validate_update_after_submit = True
timesheet.set_status()
timesheet.save()
timesheet.save(ignore_permissions=True)
def update_time_sheet_detail(self, timesheet, args, sales_invoice):
for data in timesheet.time_logs:
@@ -688,7 +699,7 @@ class SalesInvoice(SellingController):
cint(self.redeem_loyalty_points)) else "Yes"
make_gl_entries(gl_entries, cancel=(self.docstatus == 2),
update_outstanding=update_outstanding, merge_entries=False)
update_outstanding=update_outstanding, merge_entries=False, from_repost=from_repost)
if update_outstanding == "No":
from erpnext.accounts.doctype.gl_entry.gl_entry import update_outstanding_amt
@@ -933,7 +944,7 @@ class SalesInvoice(SellingController):
)
def make_gle_for_rounding_adjustment(self, gl_entries):
if flt(self.rounding_adjustment, self.precision("rounding_adjustment")):
if flt(self.rounding_adjustment, self.precision("rounding_adjustment")) and self.base_rounding_adjustment:
round_off_account, round_off_cost_center = \
get_round_off_account_and_cost_center(self.company)
@@ -981,10 +992,8 @@ class SalesInvoice(SellingController):
continue
for serial_no in item.serial_no.split("\n"):
if serial_no and frappe.db.exists('Serial No', serial_no):
sno = frappe.get_doc('Serial No', serial_no)
sno.sales_invoice = invoice
sno.db_update()
if serial_no and frappe.db.get_value('Serial No', serial_no, 'item_code') == item.item_code:
frappe.db.set_value('Serial No', serial_no, 'sales_invoice', invoice)
def validate_serial_numbers(self):
"""
@@ -1030,12 +1039,18 @@ class SalesInvoice(SellingController):
continue
for serial_no in item.serial_no.split("\n"):
sales_invoice = frappe.db.get_value("Serial No", serial_no, "sales_invoice")
if sales_invoice and self.name != sales_invoice:
sales_invoice_company = frappe.db.get_value("Sales Invoice", sales_invoice, "company")
serial_no_details = frappe.db.get_value("Serial No", serial_no,
["sales_invoice", "item_code"], as_dict=1)
if not serial_no_details:
continue
if serial_no_details.sales_invoice and serial_no_details.item_code == item.item_code \
and self.name != serial_no_details.sales_invoice:
sales_invoice_company = frappe.db.get_value("Sales Invoice", serial_no_details.sales_invoice, "company")
if sales_invoice_company == self.company:
frappe.throw(_("Serial Number: {0} is already referenced in Sales Invoice: {1}"
.format(serial_no, sales_invoice)))
.format(serial_no, serial_no_details.sales_invoice)))
def update_project(self):
if self.project:

View File

@@ -16,7 +16,7 @@ frappe.ui.form.on('Share Transfer', {
};
};
});
if (frm.doc.docstatus == 1) {
if (frm.doc.docstatus == 1 && frm.doc.equity_or_liability_account && frm.doc.asset_account ) {
frm.add_custom_button(__('Make Journal Entry'), function () {
erpnext.share_transfer.make_jv(frm);
});
@@ -92,6 +92,7 @@ erpnext.share_transfer.make_jv = function (frm) {
debit_applicant_type = "Shareholder";
debit_applicant = frm.doc.from_shareholder;
}
frappe.call({
args: {
"company": frm.doc.company,

View File

@@ -292,11 +292,14 @@ def make_jv_entry( company, account, amount, payment_account,\
"party_type": debit_applicant_type,
"party": debit_applicant,
})
account_amt_list.append({
"account": payment_account,
"credit_in_account_currency": amount,
"party_type": credit_applicant_type,
"party": credit_applicant,
})
journal_entry.set("accounts", account_amt_list)
return journal_entry.as_dict()

View File

@@ -9,11 +9,13 @@ from frappe.model.meta import get_field_precision
from erpnext.accounts.doctype.budget.budget import validate_expense_against_budget
class ClosedAccountingPeriod(frappe.ValidationError): pass
class StockAccountInvalidTransaction(frappe.ValidationError): pass
def make_gl_entries(gl_map, cancel=False, adv_adj=False, merge_entries=True, update_outstanding='Yes', from_repost=False):
if gl_map:
if not cancel:
validate_accounting_period(gl_map)
gl_map = process_gl_map(gl_map, merge_entries)
if gl_map and len(gl_map) > 1:
save_entries(gl_map, adv_adj, update_outstanding, from_repost)
@@ -22,6 +24,27 @@ def make_gl_entries(gl_map, cancel=False, adv_adj=False, merge_entries=True, upd
else:
delete_gl_entries(gl_map, adv_adj=adv_adj, update_outstanding=update_outstanding)
def validate_accounting_period(gl_map):
accounting_periods = frappe.db.sql(""" SELECT
ap.name as name
FROM
`tabAccounting Period` ap, `tabClosed Document` cd
WHERE
ap.name = cd.parent
AND ap.company = %(company)s
AND cd.closed = 1
AND cd.document_type = %(voucher_type)s
AND %(date)s between ap.start_date and ap.end_date
""", {
'date': gl_map[0].posting_date,
'company': gl_map[0].company,
'voucher_type': gl_map[0].voucher_type
}, as_dict=1)
if accounting_periods:
frappe.throw(_("You can't create accounting entries in the closed accounting period {0}")
.format(accounting_periods[0].name), ClosedAccountingPeriod)
def process_gl_map(gl_map, merge_entries=True):
if merge_entries:
gl_map = merge_similar_entries(gl_map)

View File

@@ -1625,7 +1625,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
setTimeout(function () {
w.print();
w.close();
}, 1000)
}, 1000);
},
submit_invoice: function () {
@@ -1682,6 +1682,12 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
$(this.wrapper).find('.pos-bill').css('pointer-events', pointer_events);
$(this.wrapper).find('.pos-items-section').css('pointer-events', pointer_events);
this.set_primary_action();
$(this.wrapper).find('#pos-item-disc').prop('disabled',
this.pos_profile_data.allow_user_to_edit_discount ? false : true);
$(this.wrapper).find('#pos-item-price').prop('disabled',
this.pos_profile_data.allow_user_to_edit_rate ? false : true);
},
create_invoice: function () {
@@ -1699,13 +1705,6 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
if (this.frm.doc.offline_pos_name
&& in_list(existing_pos_list, this.frm.doc.offline_pos_name)) {
this.update_invoice()
//to retrieve and set the default payment
invoice_data[this.frm.doc.offline_pos_name] = this.frm.doc;
invoice_data[this.frm.doc.offline_pos_name].payments[0].amount = this.frm.doc.net_total
invoice_data[this.frm.doc.offline_pos_name].payments[0].base_amount = this.frm.doc.net_total
this.frm.doc.paid_amount = this.frm.doc.net_total
this.frm.doc.outstanding_amount = 0
} else if(!this.frm.doc.offline_pos_name) {
this.frm.doc.offline_pos_name = frappe.datetime.now_datetime();
this.frm.doc.posting_date = frappe.datetime.get_today();
@@ -1762,6 +1761,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
this.si_docs = this.get_submitted_invoice() || [];
this.email_queue_list = this.get_email_queue() || {};
this.customers_list = this.get_customers_details() || {};
if(this.customer_doc) {
this.freeze = this.customer_doc.display
}
@@ -1906,7 +1906,7 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({
serial_no = me.item_serial_no[key][0];
}
if (this.items[0].has_serial_no && serial_no == "") {
if (this.items && this.items[0].has_serial_no && serial_no == "") {
this.refresh();
frappe.throw(__(repl("Error: Serial no is mandatory for item %(item)s", {
'item': this.items[0].item_code

View File

@@ -53,7 +53,7 @@ def _get_party_details(party=None, account=None, party_type="Customer", company=
set_other_values(out, party, party_type)
set_price_list(out, party, party_type, price_list, pos_profile)
out["taxes_and_charges"] = set_taxes(party.name, party_type, posting_date, company, out.customer_group, out.supplier_type)
out["taxes_and_charges"] = set_taxes(party.name, party_type, posting_date, company, out.customer_group, out.supplier_group)
if fetch_payment_terms_template:
out["payment_terms_template"] = get_pyt_term_template(party.name, party_type, company)
@@ -155,7 +155,7 @@ def set_price_list(out, party, party_type, given_price_list, pos=None):
# price list
price_list = get_permitted_documents('Price List')
if price_list:
if price_list and len(price_list) == 1:
price_list = price_list[0]
elif pos and party_type == 'Customer':
customer_price_list = frappe.get_value('Customer', party.name, 'default_price_list')
@@ -450,7 +450,9 @@ def get_timeline_data(doctype, name):
# fetch and append data from Activity Log
data += frappe.db.sql("""select {fields}
from `tabActivity Log`
where reference_doctype="{doctype}" and reference_name="{name}"
where (reference_doctype="{doctype}" and reference_name="{name}")
or (timeline_doctype in ("{doctype}") and timeline_name="{name}")
or (reference_doctype in ("Quotation", "Opportunity") and timeline_name="{name}")
and status!='Success' and creation > {after}
{group_by} order by creation desc
""".format(doctype=frappe.db.escape(doctype), name=frappe.db.escape(name), fields=fields,
@@ -586,4 +588,4 @@ def get_partywise_advanced_payment_amount(party_type, posting_date = None):
.format(("credit") if party_type == "Customer" else "debit", cond) , party_type)
if data:
return frappe._dict(data)
return frappe._dict(data)

View File

@@ -4,12 +4,12 @@
{%- macro render_currency(df, doc) -%}
<div class="row {% if df.bold %}important{% endif %} data-field">
<div class="col-xs-{{ "9" if df.fieldtype=="Check" else "5" }}
{%- if doc._align_labels_right %} text-right{%- endif -%}">
{%- if doc.align_labels_right %} text-right{%- endif -%}">
<label>{{ _(df.label) }}</label>
</div>
<div class="col-xs-{{ "3" if df.fieldtype=="Check" else "7" }} value">
{% if doc.get(df.fieldname) != None -%}
{{ frappe.utils.fmt_money((doc[df.fieldname])|int|abs, currency=doc.currency) }}
{{ frappe.utils.fmt_money((doc[df.fieldname])|abs, currency=doc.currency) }}
{% endif %}
</div>
</div>
@@ -23,10 +23,10 @@
{%- for charge in data -%}
{%- if (charge.tax_amount or doc.flags.print_taxes_with_zero_amount) and (not charge.included_in_print_rate or doc.flags.show_inclusive_tax_in_print) -%}
<div class="row">
<div class="col-xs-5 {%- if doc._align_labels_right %} text-right{%- endif -%}">
<div class="col-xs-5 {%- if doc.align_labels_right %} text-right{%- endif -%}">
<label>{{ charge.get_formatted("description") }}</label></div>
<div class="col-xs-7 text-right">
{{ frappe.utils.fmt_money((charge.tax_amount)|int|abs, currency=doc.currency) }}
{{ frappe.utils.fmt_money((charge.tax_amount)|abs, currency=doc.currency) }}
</div>
</div>
{%- endif -%}
@@ -65,8 +65,10 @@
{% for tdf in visible_columns %}
{% if not d.flags.compact_item_print or tdf.fieldname in doc.get(df.fieldname)[0].flags.compact_item_fields %}
<td class="{{ get_align_class(tdf) }}" {{ fieldmeta(df) }}>
{% if tdf.fieldtype == 'Currency' %}
<div class="value">{{ frappe.utils.fmt_money((d[tdf.fieldname])|int|abs, currency=doc.currency) }}</div></td>
{% if tdf.fieldname == 'qty' %}
<div class="value">{{ (d[tdf.fieldname])|abs }}</div></td>
{% elif tdf.fieldtype == 'Currency' %}
<div class="value">{{ frappe.utils.fmt_money((d[tdf.fieldname])|abs, currency=doc.currency) }}</div></td>
{% else %}
<div class="value">{{ print_value(tdf, d, doc, visible_columns) }}</div></td>
{% endif %}
@@ -103,8 +105,8 @@
{% for section in page %}
<div class="row section-break">
{% if section.columns.fields %}
{%- if doc._line_breaks and loop.index != 1 -%}<hr>{%- endif -%}
{%- if doc._show_section_headings and section.label and section.has_data -%}
{%- if doc.print_line_breaks and loop.index != 1 -%}<hr>{%- endif -%}
{%- if doc.print_section_headings and section.label and section.has_data -%}
<h4 class='col-sm-12'>{{ _(section.label) }}</h4>
{% endif %}
{%- endif -%}
@@ -117,7 +119,7 @@
{{ render_currency(df, doc) }}
{% elif df.fieldtype =='Table' %}
{{ render_table(df, doc)}}
{% elif doc[df.fieldname] %}
{% elif doc[df.fieldname] and df.fieldname != 'total_qty' %}
{{ render_field(df, doc) }}
{% endif %}
{% endfor %}

View File

@@ -1,275 +1,269 @@
<style>
.print-format {
padding: 4mm;
font-size: 8.0pt !important;
}
.print-format td {
vertical-align:middle !important;
}
</style>
.print-format {
padding: 4mm;
font-size: 8.0pt !important;
}
.print-format td {
vertical-align:middle !important;
}
</style>
<h2 class="text-center" style="margin-top:0">{%= __(report.report_name) %}</h2>
<h4 class="text-center">
{% if (filters.customer_name) { %}
{%= filters.customer_name %}
{% } else { %}
{%= filters.customer || filters.supplier %}
{% } %}
</h4>
<h6 class="text-center">
{% if (filters.tax_id) { %}
{%= __("Tax Id: ")%} {%= filters.tax_id %}
<h2 class="text-center" style="margin-top:0">{%= __(report.report_name) %}</h2>
<h4 class="text-center">
{% if (filters.customer_name) { %}
{%= filters.customer_name %}
{% } else { %}
{%= filters.customer || filters.supplier %}
{% } %}
</h6>
<h5 class="text-center">
{%= __(filters.ageing_based_on) %}
{%= __("Until") %}
{%= frappe.datetime.str_to_user(filters.report_date) %}
</h5>
</h4>
<h6 class="text-center">
{% if (filters.tax_id) { %}
{%= __("Tax Id: ")%} {%= filters.tax_id %}
{% } %}
</h6>
<h5 class="text-center">
{%= __(filters.ageing_based_on) %}
{%= __("Until") %}
{%= frappe.datetime.str_to_user(filters.report_date) %}
</h5>
<div class="clearfix">
<div class="pull-left">
{% if(filters.payment_terms) { %}
<strong>{%= __("Payment Terms") %}:</strong> {%= filters.payment_terms %}
{% } %}
<div class="clearfix">
<div class="pull-left">
{% if(filters.payment_terms) { %}
<strong>{%= __("Payment Terms") %}:</strong> {%= filters.payment_terms %}
{% } %}
</div>
<div class="pull-right">
{% if(filters.credit_limit) { %}
<strong>{%= __("Credit Limit") %}:</strong> {%= format_currency(filters.credit_limit) %}
{% } %}
</div>
</div>
<div class="pull-right">
{% if(filters.credit_limit) { %}
<strong>{%= __("Credit Limit") %}:</strong> {%= format_currency(filters.credit_limit) %}
{% if(filters.show_future_payments) { %}
{% var balance_row = data.slice(-1).pop();
var range1 = report.columns[11].label;
var range2 = report.columns[12].label;
var range3 = report.columns[13].label;
var range4 = report.columns[14].label;
var range5 = report.columns[15].label;
%}
{% if(balance_row) { %}
<table class="table table-bordered table-condensed">
<caption class="text-right">(Amount in {%= data[0]["currency"] || "" %})</caption>
<colgroup>
<col style="width: 30mm;">
<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>
<tr>
<th>{%= __(" ") %}</th>
<th>{%= __(range1) %}</th>
<th>{%= __(range2) %}</th>
<th>{%= __(range3) %}</th>
<th>{%= __(range4) %}</th>
<th>{%= __(range5) %}</th>
<th>{%= __("Total") %}</th>
</tr>
</thead>
<tbody>
<tr>
<td>{%= __("Total Outstanding") %}</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(flt(balance_row["outstanding"]), data[data.length-1]["currency"]) %}
</td>
</tr>
<td>{%= __("Future Payments") %}</td>
<td></td>
<td></td>
<td></td>
<td></td>
<td></td>
<td class="text-right">
{%= format_currency(flt(balance_row[("future_amount")]), data[data.length-1]["currency"]) %}
</td>
<tr class="cvs-footer">
<th class="text-left">{%= __("Cheques Required") %}</th>
<th></th>
<th></th>
<th></th>
<th></th>
<th></th>
<th class="text-right">
{%= format_currency(flt(balance_row["outstanding"] - balance_row[("future_amount")]), data[data.length-1]["currency"]) %}</th>
</tr>
</tbody>
</table>
{% } %}
{% } %}
</div>
</div>
{% if(filters.show_pdc_in_print) { %}
{% var balance_row = data.slice(-1).pop();
var range1 = report.columns[11].label;
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">
<caption class="text-right">(Amount in {%= data[0][__("currency")] || "" %})</caption>
<colgroup>
<col style="width: 30mm;">
<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>
<table class="table table-bordered">
<thead>
<tr>
<th>{%= __(" ") %}</th>
<th>{%= __(range1) %}</th>
<th>{%= __(range2) %}</th>
<th>{%= __(range3) %}</th>
<th>{%= __(range4) %}</th>
<th>{%= __(range5) %}</th>
<th>{%= __(range6) %}</th>
<th>{%= __("Total") %}</th>
{% if(report.report_name === "Accounts Receivable" || report.report_name === "Accounts Payable") { %}
<th style="width: 10%">{%= __("Date") %}</th>
<th style="width: 4%">{%= __("Age (Days)") %}</th>
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person) { %}
<th style="width: 14%">{%= __("Reference") %}</th>
<th style="width: 10%">{%= __("Sales Person") %}</th>
{% } else { %}
<th style="width: 24%">{%= __("Reference") %}</th>
{% } %}
{% if(!filters.show_future_payments) { %}
<th style="width: 20%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
{% } %}
<th style="width: 10%; text-align: right">{%= __("Invoiced Amount") %}</th>
{% if(!filters.show_future_payments) { %}
<th style="width: 10%; text-align: right">{%= __("Paid Amount") %}</th>
<th style="width: 10%; text-align: right">{%= report.report_name === "Accounts Receivable" ? __('Credit Note') : __('Debit Note') %}</th>
{% } %}
<th style="width: 10%; text-align: right">{%= __("Outstanding Amount") %}</th>
{% if(filters.show_future_payments) { %}
{% if(report.report_name === "Accounts Receivable") { %}
<th style="width: 12%">{%= __("Customer LPO No.") %}</th>
{% } %}
<th style="width: 10%">{%= __("Future Payment Ref") %}</th>
<th style="width: 10%">{%= __("Future Payment Amount") %}</th>
<th style="width: 10%">{%= __("Remaining Balance") %}</th>
{% } %}
{% } else { %}
<th style="width: 40%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
<th style="width: 15%">{%= __("Total Invoiced Amount") %}</th>
<th style="width: 15%">{%= __("Total Paid Amount") %}</th>
<th style="width: 15%">{%= report.report_name === "Accounts Receivable Summary" ? __('Credit Note Amount') : __('Debit Note Amount') %}</th>
<th style="width: 15%">{%= __("Total Outstanding Amount") %}</th>
{% } %}
</tr>
</thead>
<tbody>
<tr>
<td>{%= __("Total Outstanding") %}</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>
</tr>
<td>{%= __("PDC/LC") %}</td>
<td></td>
<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>
<tr class="cvs-footer">
<th class="text-left">{%= __("Cheques Required") %}</th>
<th></th>
<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>
</tbody>
</table>
{% } %}
{% } %}
<table class="table table-bordered">
<thead>
<tr>
{% if(report.report_name === "Accounts Receivable" || report.report_name === "Accounts Payable") { %}
<th style="width: 10%">{%= __("Date") %}</th>
<th style="width: 4%">{%= __("Age (Days)") %}</th>
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person_in_print) { %}
<th style="width: 14%">{%= __("Reference") %}</th>
<th style="width: 10%">{%= __("Sales Person") %}</th>
{% } else { %}
<th style="width: 24%">{%= __("Reference") %}</th>
{% } %}
{% if(!filters.show_pdc_in_print) { %}
<th style="width: 20%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
{% } %}
<th style="width: 10%; text-align: right">{%= __("Invoiced Amount") %}</th>
{% if(!filters.show_pdc_in_print) { %}
<th style="width: 10%; text-align: right">{%= __("Paid Amount") %}</th>
<th style="width: 10%; text-align: right">{%= report.report_name === "Accounts Receivable" ? __('Credit Note') : __('Debit Note') %}</th>
{% } %}
<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: 12%">{%= __("Customer LPO No.") %}</th>
{% } %}
<th style="width: 10%">{%= __("PDC/LC Ref") %}</th>
<th style="width: 10%">{%= __("PDC/LC Amount") %}</th>
<th style="width: 10%">{%= __("Remaining Balance") %}</th>
{% } %}
{% } else { %}
<th style="width: 40%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
<th style="width: 15%">{%= __("Total Invoiced Amount") %}</th>
<th style="width: 15%">{%= __("Total Paid Amount") %}</th>
<th style="width: 15%">{%= report.report_name === "Accounts Receivable Summary" ? __('Credit Note Amount') : __('Debit Note Amount') %}</th>
<th style="width: 15%">{%= __("Total Outstanding Amount") %}</th>
{% } %}
</tr>
</thead>
<tbody>
{% for(var i=0, l=data.length; i<l; i++) { %}
<tr>
{% if(report.report_name === "Accounts Receivable" || report.report_name === "Accounts Payable") { %}
{% if(data[i][__("Customer")] || data[i][__("Supplier")]) { %}
<td>{%= frappe.datetime.str_to_user(data[i]["posting_date"]) %}</td>
<td style="text-align: right">{%= data[i][__("Age (Days)")] %}</td>
<td>
{% if(!filters.show_pdc_in_print) { %}
{%= data[i]["voucher_type"] %}
<br>
{% } %}
{%= data[i]["voucher_no"] %}
</td>
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person_in_print) { %}
<td>{%= data[i]["sales_person"] %}</td>
{% } %}
{% if(!filters.show_pdc_in_print) { %}
<td>
{% if(!(filters.customer || filters.supplier)) { %}
{%= data[i][__("Customer")] || data[i][__("Supplier")] %}
{% if(data[i][__("Customer Name")] && data[i][__("Customer Name")] != data[i][__("Customer")]) { %}
<br> {%= data[i][__("Customer Name")] %}
{% } else if(data[i][__("Supplier Name")] != data[i][__("Supplier")]) { %}
<br> {%= data[i][__("Supplier Name")] %}
{% for(var i=0, l=data.length; i<l; i++) { %}
<tr>
{% if(report.report_name === "Accounts Receivable" || report.report_name === "Accounts Payable") { %}
{% if(data[i]["party"]) { %}
<td>{%= frappe.datetime.str_to_user(data[i]["posting_date"]) %}</td>
<td style="text-align: right">{%= data[i]["age"] %}</td>
<td>
{% if(!filters.show_future_payments) { %}
{%= data[i]["voucher_type"] %}
<br>
{% } %}
{%= data[i]["voucher_no"] %}
</td>
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person) { %}
<td>{%= data[i]["sales_person"] %}</td>
{% } %}
<div>
{% if data[i][__("Remarks")] %}
{%= __("Remarks") %}:
{%= data[i][__("Remarks")] %}
{% } %}
</div>
</td>
{% } %}
<td style="text-align: right">
{%= format_currency(data[i]["invoiced_amount"], data[i]["currency"]) %}</td>
{% if(!filters.show_pdc_in_print) { %}
<td style="text-align: right">
{%= format_currency(data[i]["paid_amount"], data[i]["currency"]) %}</td>
<td style="text-align: right">
{%= report.report_name === "Accounts Receivable" ? format_currency(data[i]["credit_note"], data[i]["currency"]) : format_currency(data[i]["debit_note"], data[i]["currency"]) %}</td>
{% } %}
<td style="text-align: right">
{%= format_currency(data[i]["outstanding_amount"], data[i]["currency"]) %}</td>
{% if(filters.show_pdc_in_print) { %}
{% if(report.report_name === "Accounts Receivable") { %}
<td style="text-align: right">
{%= data[i]["po_no"] %}</td>
{% } %}
<td style="text-align: right">{%= data[i][("pdc/lc_ref")] %}</td>
<td style="text-align: right">{%= format_currency(data[i][("pdc/lc_amount")], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= format_currency(data[i][("remaining_balance")], data[i]["currency"]) %}</td>
{% } %}
{% } else { %}
<td></td>
{% if(!filters.show_pdc_in_print) { %}
<td></td>
{% } %}
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person_in_print) { %}
<td></td>
{% } %}
<td></td>
<td style="text-align: right"><b>{%= __("Total") %}</b></td>
<td style="text-align: right">
{%= format_currency(data[i]["invoiced_amount"], data[i]["currency"] ) %}</td>
{% if(!filters.show_pdc_in_print) { %}
<td style="text-align: right">
{%= format_currency(data[i]["paid_amount"], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= report.report_name === "Accounts Receivable" ? format_currency(data[i]["credit_note"], data[i]["currency"]) : format_currency(data[i]["debit_note"], data[i]["currency"]) %} </td>
{% } %}
<td style="text-align: right">
{%= format_currency(data[i]["outstanding_amount"], data[i]["currency"]) %}</td>
{% if(filters.show_pdc_in_print) { %}
{% if(report.report_name === "Accounts Receivable") { %}
<td style="text-align: right">
{%= data[i][__("Customer LPO")] %}</td>
{% } %}
<td style="text-align: right">{%= data[i][("pdc/lc_ref")] %}</td>
<td style="text-align: right">{%= format_currency(data[i][("pdc/lc_amount")], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= format_currency(data[i][("remaining_balance")], data[i]["currency"]) %}</td>
{% } %}
{% } %}
{% } else { %}
{% if(data[i][__("Customer")] || data[i][__("Supplier")]|| "&nbsp;") { %}
{% if((data[i][__("Customer")] || data[i][__("Supplier")]) != __("'Total'")) { %}
{% if(!filters.show_future_payments) { %}
<td>
{% if(!(filters.customer || filters.supplier)) { %}
{%= data[i][__("Customer")] || data[i][__("Supplier")] %}
{% if(data[i][__("Customer Name")] && data[i][__("Customer Name")] != data[i][__("Customer")]) { %}
<br> {%= data[i][__("Customer Name")] %}
{% } else if(data[i][__("Supplier Name")] != data[i][__("Supplier")]) { %}
<br> {%= data[i][__("Supplier Name")] %}
{%= data[i]["party"] %}
{% if(data[i]["customer_name"] && data[i]["customer_name"] != data[i]["party"]) { %}
<br> {%= data[i]["customer_name"] %}
{% } else if(data[i]["supplier_name"] != data[i]["party"]) { %}
<br> {%= data[i]["supplier_name"] %}
{% } %}
{% } %}
<br>{%= __("Remarks") %}:
{%= data[i][__("Remarks")] %}
<div>
{% if data[i]["remarks"] %}
{%= __("Remarks") %}:
{%= data[i]["remarks"] %}
{% } %}
</div>
</td>
{% } %}
<td style="text-align: right">
{%= format_currency(data[i]["invoiced"], data[i]["currency"]) %}</td>
{% if(!filters.show_future_payments) { %}
<td style="text-align: right">
{%= format_currency(data[i]["paid"], data[i]["currency"]) %}</td>
<td style="text-align: right">
{%= format_currency(data[i]["credit_note"], data[i]["currency"]) %}</td>
{% } %}
<td style="text-align: right">
{%= format_currency(data[i]["outstanding"], data[i]["currency"]) %}</td>
{% if(filters.show_future_payments) { %}
{% if(report.report_name === "Accounts Receivable") { %}
<td style="text-align: right">
{%= data[i]["po_no"] %}</td>
{% } %}
<td style="text-align: right">{%= data[i]["future_ref"] %}</td>
<td style="text-align: right">{%= format_currency(data[i]["future_amount"], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= format_currency(data[i]["remaining_balance"], data[i]["currency"]) %}</td>
{% } %}
{% } else { %}
<td><b>{%= __("Total") %}</b></td>
<td></td>
{% if(!filters.show_future_payments) { %}
<td></td>
{% } %}
{% if(report.report_name === "Accounts Receivable" && filters.show_sales_person) { %}
<td></td>
{% } %}
<td></td>
<td style="text-align: right"><b>{%= __("Total") %}</b></td>
<td style="text-align: right">
{%= format_currency(data[i]["invoiced"], data[i]["currency"] ) %}</td>
{% if(!filters.show_future_payments) { %}
<td style="text-align: right">
{%= format_currency(data[i]["paid"], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= format_currency(data[i]["credit_note"], data[i]["currency"]) %} </td>
{% } %}
<td style="text-align: right">
{%= format_currency(data[i]["outstanding"], data[i]["currency"]) %}</td>
{% if(filters.show_future_payments) { %}
{% if(report.report_name === "Accounts Receivable") { %}
<td style="text-align: right">
{%= data[i]["po_no"] %}</td>
{% } %}
<td style="text-align: right">{%= data[i]["future_ref"] %}</td>
<td style="text-align: right">{%= format_currency(data[i]["future_amount"], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= format_currency(data[i]["remaining_balance"], data[i]["currency"]) %}</td>
{% } %}
{% } %}
{% } else { %}
{% if(data[i]["party"]|| "&nbsp;") { %}
{% if((data[i]["party"]) != __("'Total'")) { %}
<td>
{% if(!(filters.customer || filters.supplier)) { %}
{%= data[i]["party"] %}
{% if(data[i]["customer_name"] && data[i]["customer_name"] != data[i]["party"]) { %}
<br> {%= data[i]["customer_name"] %}
{% } else if(data[i]["supplier_name"] != data[i]["party"]) { %}
<br> {%= data[i]["supplier_name"] %}
{% } %}
{% } %}
<br>{%= __("Remarks") %}:
{%= data[i]["remarks"] %}
</td>
{% } else { %}
<td><b>{%= __("Total") %}</b></td>
{% } %}
<td style="text-align: right">{%= format_currency(data[i]["invoiced"], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= format_currency(data[i]["paid"], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= format_currency(data[i]["credit_note"], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= format_currency(data[i]["outstanding"], data[i]["currency"]) %}</td>
{% } %}
<td style="text-align: right">{%= format_currency(data[i][("total_invoiced_amt")], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= format_currency(data[i][("total_paid_amt")], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= report.report_name === "Accounts Receivable Summary" ? format_currency(data[i][__("credit_note_amt")], data[i]["currency"]) : format_currency(data[i][__("debit_note_amt")], data[i]["currency"]) %}</td>
<td style="text-align: right">{%= format_currency(data[i][("total_outstanding_amt")], data[i]["currency"]) %}</td>
{% } %}
</tr>
{% } %}
</tr>
{% } %}
</tbody>
</table>
<p class="text-right text-muted">{{ __("Printed On ") }}{%= frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string()) %}</p>
</tbody>
</table>
<p class="text-right text-muted">{{ __("Printed On ") }}{%= frappe.datetime.str_to_user(frappe.datetime.get_datetime_as_string()) %}</p>

View File

@@ -115,13 +115,18 @@ frappe.query_reports["Accounts Receivable"] = {
"fieldtype": "Check",
},
{
"fieldname":"show_pdc_in_print",
"label": __("Show PDC in Print"),
"fieldname":"show_future_payments",
"label": __("Show Future Payments"),
"fieldtype": "Check",
},
{
"fieldname":"show_sales_person_in_print",
"label": __("Show Sales Person in Print"),
"fieldname":"show_delivery_notes",
"label": __("Show Delivery Notes"),
"fieldtype": "Check",
},
{
"fieldname":"show_sales_person",
"label": __("Show Sales Person"),
"fieldtype": "Check",
},
{

View File

@@ -14,33 +14,44 @@ class TestAccountsReceivable(unittest.TestCase):
filters = {
'company': '_Test Company 2',
'based_on_payment_terms': 1
'based_on_payment_terms': 1,
'report_date': today(),
'range1': 30,
'range2': 60,
'range3': 90,
'range4': 120
}
# check invoice grand total and invoiced column's value for 3 payment terms
name = make_sales_invoice()
report = execute(filters)
expected_data = [[100,30], [100,50], [100,20]]
expected_data = [[100, 30], [100, 50], [100, 20]]
self.assertEqual(expected_data[0], report[1][0][7:9])
self.assertEqual(expected_data[1], report[1][1][7:9])
self.assertEqual(expected_data[2], report[1][2][7:9])
for i in range(3):
row = report[1][i-1]
self.assertEqual(expected_data[i-1], [row.invoice_grand_total, row.invoiced])
# check invoice grand total, invoiced, paid and outstanding column's value after payment
make_payment(name)
report = execute(filters)
expected_data_after_payment = [[100,50], [100,20]]
expected_data_after_payment = [[100, 50, 10, 40], [100, 20, 0, 20]]
self.assertEqual(expected_data_after_payment[0], report[1][0][7:9])
self.assertEqual(expected_data_after_payment[1], report[1][1][7:9])
for i in range(2):
row = report[1][i-1]
self.assertEqual(expected_data_after_payment[i-1],
[row.invoice_grand_total, row.invoiced, row.paid, row.outstanding])
# check invoice grand total, invoiced, paid and outstanding column's value after credit note
make_credit_note(name)
report = execute(filters)
expected_data_after_credit_note = [[100,100,30,100,-30]]
self.assertEqual(expected_data_after_credit_note[0], report[1][0][7:12])
expected_data_after_credit_note = [100, 0, 0, 40, -40]
row = report[1][0]
self.assertEqual(expected_data_after_credit_note,
[row.invoice_grand_total, row.invoiced, row.paid, row.credit_note, row.outstanding])
def make_sales_invoice():
frappe.set_user("Administrator")
@@ -64,7 +75,7 @@ def make_sales_invoice():
return si.name
def make_payment(docname):
pe = get_payment_entry("Sales Invoice", docname, bank_account="Cash - _TC2", party_amount=30)
pe = get_payment_entry("Sales Invoice", docname, bank_account="Cash - _TC2", party_amount=40)
pe.paid_from = "Debtors - _TC2"
pe.insert()
pe.submit()

View File

@@ -3,236 +3,11 @@
from __future__ import unicode_literals
import frappe
from frappe import _, scrub
from frappe.utils import flt
from frappe import _
from frappe.utils import flt, cint
from erpnext.accounts.party import get_partywise_advanced_payment_amount
from erpnext.accounts.report.accounts_receivable.accounts_receivable import ReceivablePayableReport
from six import iteritems
from six.moves import zip
class AccountsReceivableSummary(ReceivablePayableReport):
def run(self, args):
party_naming_by = frappe.db.get_value(args.get("naming_by")[0], None, args.get("naming_by")[1])
return self.get_columns(party_naming_by, args), self.get_data(party_naming_by, args)
def get_columns(self, party_naming_by, args):
columns = [_(args.get("party_type")) + ":Link/" + args.get("party_type") + ":200"]
if party_naming_by == "Naming Series":
columns += [ args.get("party_type") + " Name::140"]
credit_debit_label = "Credit Note Amt" if args.get('party_type') == 'Customer' else "Debit Note Amt"
columns += [{
"label": _("Advance Amount"),
"fieldname": "advance_amount",
"fieldtype": "Currency",
"options": "currency",
"width": 100
},{
"label": _("Total Invoiced Amt"),
"fieldname": "total_invoiced_amt",
"fieldtype": "Currency",
"options": "currency",
"width": 100
},
{
"label": _("Total Paid Amt"),
"fieldname": "total_paid_amt",
"fieldtype": "Currency",
"options": "currency",
"width": 100
}]
columns += [
{
"label": _(credit_debit_label),
"fieldname": scrub(credit_debit_label),
"fieldtype": "Currency",
"options": "currency",
"width": 140
},
{
"label": _("Total Outstanding Amt"),
"fieldname": "total_outstanding_amt",
"fieldtype": "Currency",
"options": "currency",
"width": 160
},
{
"label": _("0-" + str(self.filters.range1)),
"fieldname": scrub("0-" + str(self.filters.range1)),
"fieldtype": "Currency",
"options": "currency",
"width": 160
},
{
"label": _(str(self.filters.range1) + "-" + str(self.filters.range2)),
"fieldname": scrub(str(self.filters.range1) + "-" + str(self.filters.range2)),
"fieldtype": "Currency",
"options": "currency",
"width": 160
},
{
"label": _(str(self.filters.range2) + "-" + str(self.filters.range3)),
"fieldname": scrub(str(self.filters.range2) + "-" + str(self.filters.range3)),
"fieldtype": "Currency",
"options": "currency",
"width": 160
},
{
"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
}
]
if args.get("party_type") == "Customer":
columns += [{
"label": _("Territory"),
"fieldname": "territory",
"fieldtype": "Link",
"options": "Territory",
"width": 80
},
{
"label": _("Customer Group"),
"fieldname": "customer_group",
"fieldtype": "Link",
"options": "Customer Group",
"width": 80
},
{
"label": _("Sales Person"),
"fieldtype": "Data",
"fieldname": "sales_person",
"width": 120,
}]
if args.get("party_type") == "Supplier":
columns += [{
"label": _("Supplier Group"),
"fieldname": "supplier_group",
"fieldtype": "Link",
"options": "Supplier Group",
"width": 80
}]
columns.append({
"fieldname": "currency",
"label": _("Currency"),
"fieldtype": "Link",
"options": "Currency",
"width": 80
})
return columns
def get_data(self, party_naming_by, args):
data = []
partywise_total = self.get_partywise_total(party_naming_by, args)
partywise_advance_amount = get_partywise_advanced_payment_amount(args.get("party_type"),
self.filters.get("report_date")) or {}
for party, party_dict in iteritems(partywise_total):
row = [party]
if party_naming_by == "Naming Series":
row += [self.get_party_name(args.get("party_type"), party)]
row += [partywise_advance_amount.get(party, 0)]
paid_amt = 0
if party_dict.paid_amt > 0:
paid_amt = flt(party_dict.paid_amt - partywise_advance_amount.get(party, 0))
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.range5
]
if args.get("party_type") == "Customer":
row += [self.get_territory(party), self.get_customer_group(party), ", ".join(set(party_dict.sales_person))]
if args.get("party_type") == "Supplier":
row += [self.get_supplier_group(party)]
row.append(party_dict.currency)
data.append(row)
return data
def get_partywise_total(self, party_naming_by, args):
party_total = frappe._dict()
for d in self.get_voucherwise_data(party_naming_by, args):
party_total.setdefault(d.party,
frappe._dict({
"invoiced_amt": 0,
"paid_amt": 0,
"credit_amt": 0,
"outstanding_amt": 0,
"range1": 0,
"range2": 0,
"range3": 0,
"range4": 0,
"range5": 0,
"sales_person": []
})
)
for k in list(party_total[d.party]):
if k not in ["currency", "sales_person"]:
party_total[d.party][k] += flt(d.get(k, 0))
party_total[d.party].currency = d.currency
if d.sales_person:
party_total[d.party].sales_person.append(d.sales_person)
return party_total
def get_voucherwise_data(self, party_naming_by, args):
voucherwise_data = ReceivablePayableReport(self.filters).run(args)[1]
cols = ["posting_date", "party"]
if party_naming_by == "Naming Series":
cols += ["party_name"]
if args.get("party_type") == 'Customer':
cols += ["contact"]
cols += ["voucher_type", "voucher_no", "due_date"]
if args.get("party_type") == "Supplier":
cols += ["bill_no", "bill_date"]
cols += ["invoiced_amt", "paid_amt", "credit_amt",
"outstanding_amt", "age", "range1", "range2", "range3", "range4", "range5", "currency", "pdc/lc_date", "pdc/lc_ref",
"pdc/lc_amount"]
if args.get("party_type") == "Supplier":
cols += ["supplier_group", "remarks"]
if args.get("party_type") == "Customer":
cols += ["po_no", "do_no", "territory", "customer_group", "sales_person", "remarks"]
return self.make_data_dict(cols, voucherwise_data)
def make_data_dict(self, cols, data):
data_dict = []
for d in data:
data_dict.append(frappe._dict(zip(cols, d)))
return data_dict
def execute(filters=None):
args = {
@@ -241,3 +16,123 @@ def execute(filters=None):
}
return AccountsReceivableSummary(filters).run(args)
class AccountsReceivableSummary(ReceivablePayableReport):
def run(self, args):
self.party_type = args.get('party_type')
self.party_naming_by = frappe.db.get_value(args.get("naming_by")[0], None, args.get("naming_by")[1])
self.get_columns()
self.get_data(args)
return self.columns, self.data
def get_data(self, args):
self.data = []
self.receivables = ReceivablePayableReport(self.filters).run(args)[1]
self.get_party_total(args)
party_advance_amount = get_partywise_advanced_payment_amount(self.party_type,
self.filters.report_date) or {}
for party, party_dict in iteritems(self.party_total):
if party_dict.outstanding == 0:
continue
row = frappe._dict()
row.party = party
if self.party_naming_by == "Naming Series":
row.party_name = frappe.get_cached_value(self.party_type,
party, frappe.scrub(self.party_type) + "_name")
row.update(party_dict)
# Advance against party
row.advance = party_advance_amount.get(party, 0)
# In AR/AP, advance shown in paid columns,
# but in summary report advance shown in separate column
row.paid -= row.advance
self.data.append(row)
def get_party_total(self, args):
self.party_total = frappe._dict()
for d in self.receivables:
self.init_party_total(d)
# Add all amount columns
for k in list(self.party_total[d.party]):
if k not in ["currency", "sales_person"]:
self.party_total[d.party][k] += d.get(k, 0.0)
# set territory, customer_group, sales person etc
self.set_party_details(d)
def init_party_total(self, row):
self.party_total.setdefault(row.party, frappe._dict({
"invoiced": 0.0,
"paid": 0.0,
"credit_note": 0.0,
"outstanding": 0.0,
"range1": 0.0,
"range2": 0.0,
"range3": 0.0,
"range4": 0.0,
"range5": 0.0,
"sales_person": []
}))
def set_party_details(self, row):
self.party_total[row.party].currency = row.currency
for key in ('territory', 'customer_group', 'supplier_group'):
if row.get(key):
self.party_total[row.party][key] = row.get(key)
if row.sales_person:
self.party_total[row.party].sales_person.append(row.sales_person)
def get_columns(self):
self.columns = []
self.add_column(label=_(self.party_type), fieldname='party',
fieldtype='Link', options=self.party_type, width=180)
if self.party_naming_by == "Naming Series":
self.add_column(_('{0} Name').format(self.party_type),
fieldname = 'party_name', fieldtype='Data')
credit_debit_label = "Credit Note" if self.party_type == 'Customer' else "Debit Note"
self.add_column(_('Advance Amount'), fieldname='advance')
self.add_column(_('Invoiced Amount'), fieldname='invoiced')
self.add_column(_('Paid Amount'), fieldname='paid')
self.add_column(_(credit_debit_label), fieldname='credit_note')
self.add_column(_('Outstanding Amount'), fieldname='outstanding')
self.setup_ageing_columns()
if self.party_type == "Customer":
self.add_column(label=_('Territory'), fieldname='territory', fieldtype='Link',
options='Territory')
self.add_column(label=_('Customer Group'), fieldname='customer_group', fieldtype='Link',
options='Customer Group')
if self.filters.show_sales_person:
self.add_column(label=_('Sales Person'), fieldname='sales_person', fieldtype='Data')
else:
self.add_column(label=_('Supplier Group'), fieldname='supplier_group', fieldtype='Link',
options='Supplier Group')
self.add_column(label=_('Currency'), fieldname='currency', fieldtype='Link',
options='Currency', width=80)
def setup_ageing_columns(self):
for i, label in enumerate(["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}-{range4}".format(range3=cint(self.filters["range3"])+ 1, range4=self.filters["range4"]),
"{range4}-{above}".format(range4=cint(self.filters["range4"])+ 1, above=_("Above"))]):
self.add_column(label=label, fieldname='range' + str(i+1))

View File

@@ -4,126 +4,141 @@
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.utils import formatdate, getdate, flt, add_days
from frappe.utils import formatdate, flt, add_days
def execute(filters=None):
filters.day_before_from_date = add_days(filters.from_date, -1)
columns, data = get_columns(filters), get_data(filters)
return columns, data
def get_data(filters):
data = []
asset_categories = get_asset_categories(filters)
assets = get_assets(filters)
asset_costs = get_asset_costs(assets, filters)
asset_depreciations = get_accumulated_depreciations(assets, filters)
for asset_category in asset_categories:
row = frappe._dict()
row.asset_category = asset_category
row.update(asset_costs.get(asset_category))
# row.asset_category = asset_category
row.update(asset_category)
row.cost_as_on_to_date = (flt(row.cost_as_on_from_date) + flt(row.cost_of_new_purchase) -
flt(row.cost_of_sold_asset) - flt(row.cost_of_scrapped_asset))
row.update(next(asset for asset in assets if asset["asset_category"] == asset_category.get("asset_category", "")))
row.accumulated_depreciation_as_on_to_date = (flt(row.accumulated_depreciation_as_on_from_date) +
flt(row.depreciation_amount_during_the_period) - flt(row.depreciation_eliminated))
row.net_asset_value_as_on_from_date = (flt(row.cost_as_on_from_date) -
flt(row.accumulated_depreciation_as_on_from_date))
row.net_asset_value_as_on_to_date = (flt(row.cost_as_on_to_date) -
flt(row.accumulated_depreciation_as_on_to_date))
row.cost_as_on_to_date = (flt(row.cost_as_on_from_date) + flt(row.cost_of_new_purchase)
- flt(row.cost_of_sold_asset) - flt(row.cost_of_scrapped_asset))
row.update(asset_depreciations.get(asset_category))
row.accumulated_depreciation_as_on_to_date = (flt(row.accumulated_depreciation_as_on_from_date) +
flt(row.depreciation_amount_during_the_period) - flt(row.depreciation_eliminated))
row.net_asset_value_as_on_from_date = (flt(row.cost_as_on_from_date) -
flt(row.accumulated_depreciation_as_on_from_date))
row.net_asset_value_as_on_to_date = (flt(row.cost_as_on_to_date) -
flt(row.accumulated_depreciation_as_on_to_date))
data.append(row)
return data
def get_asset_categories(filters):
return frappe.db.sql_list("""
select distinct asset_category from `tabAsset`
where docstatus=1 and company=%s and purchase_date <= %s
""", (filters.company, filters.to_date))
return frappe.db.sql("""
SELECT asset_category,
ifnull(sum(case when purchase_date < %(from_date)s then
case when ifnull(disposal_date, 0) = 0 or disposal_date >= %(from_date)s then
gross_purchase_amount
else
0
end
else
0
end), 0) as cost_as_on_from_date,
ifnull(sum(case when purchase_date >= %(from_date)s then
gross_purchase_amount
else
0
end), 0) as cost_of_new_purchase,
ifnull(sum(case when ifnull(disposal_date, 0) != 0
and disposal_date >= %(from_date)s
and disposal_date <= %(to_date)s then
case when status = "Sold" then
gross_purchase_amount
else
0
end
else
0
end), 0) as cost_of_sold_asset,
ifnull(sum(case when ifnull(disposal_date, 0) != 0
and disposal_date >= %(from_date)s
and disposal_date <= %(to_date)s then
case when status = "Scrapped" then
gross_purchase_amount
else
0
end
else
0
end), 0) as cost_of_scrapped_asset
from `tabAsset`
where docstatus=1 and company=%(company)s and purchase_date <= %(to_date)s
group by asset_category
""", {"to_date": filters.to_date, "from_date": filters.from_date, "company": filters.company}, as_dict=1)
def get_assets(filters):
return frappe.db.sql("""
select name, asset_category, purchase_date, gross_purchase_amount, disposal_date, status
from `tabAsset`
where docstatus=1 and company=%s and purchase_date <= %s""",
(filters.company, filters.to_date), as_dict=1)
def get_asset_costs(assets, filters):
asset_costs = frappe._dict()
for d in assets:
asset_costs.setdefault(d.asset_category, frappe._dict({
"cost_as_on_from_date": 0,
"cost_of_new_purchase": 0,
"cost_of_sold_asset": 0,
"cost_of_scrapped_asset": 0
}))
costs = asset_costs[d.asset_category]
if getdate(d.purchase_date) < getdate(filters.from_date):
if not d.disposal_date or getdate(d.disposal_date) >= getdate(filters.from_date):
costs.cost_as_on_from_date += flt(d.gross_purchase_amount)
else:
costs.cost_of_new_purchase += flt(d.gross_purchase_amount)
if d.disposal_date and getdate(d.disposal_date) >= getdate(filters.from_date) \
and getdate(d.disposal_date) <= getdate(filters.to_date):
if d.status == "Sold":
costs.cost_of_sold_asset += flt(d.gross_purchase_amount)
elif d.status == "Scrapped":
costs.cost_of_scrapped_asset += flt(d.gross_purchase_amount)
return asset_costs
def get_accumulated_depreciations(assets, filters):
asset_depreciations = frappe._dict()
for d in assets:
asset = frappe.get_doc("Asset", d.name)
if d.asset_category in asset_depreciations:
asset_depreciations[d.asset_category]['accumulated_depreciation_as_on_from_date'] += asset.opening_accumulated_depreciation
else:
asset_depreciations.setdefault(d.asset_category, frappe._dict({
"accumulated_depreciation_as_on_from_date": asset.opening_accumulated_depreciation,
"depreciation_amount_during_the_period": 0,
"depreciation_eliminated_during_the_period": 0
}))
SELECT results.asset_category,
sum(results.accumulated_depreciation_as_on_from_date) as accumulated_depreciation_as_on_from_date,
sum(results.depreciation_eliminated_during_the_period) as depreciation_eliminated_during_the_period,
sum(results.depreciation_amount_during_the_period) as depreciation_amount_during_the_period
from (SELECT a.asset_category,
ifnull(sum(a.opening_accumulated_depreciation +
case when ds.schedule_date < %(from_date)s and
(ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s) then
ds.depreciation_amount
else
0
end), 0) as accumulated_depreciation_as_on_from_date,
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and a.disposal_date >= %(from_date)s
and a.disposal_date <= %(to_date)s and ds.schedule_date <= a.disposal_date then
ds.depreciation_amount
else
0
end), 0) as depreciation_eliminated_during_the_period,
depr = asset_depreciations[d.asset_category]
ifnull(sum(case when ds.schedule_date >= %(from_date)s and ds.schedule_date <= %(to_date)s
and (ifnull(a.disposal_date, 0) = 0 or ds.schedule_date <= a.disposal_date) then
ds.depreciation_amount
else
0
end), 0) as depreciation_amount_during_the_period
from `tabAsset` a, `tabDepreciation Schedule` ds
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and a.name = ds.parent
group by a.asset_category
union
SELECT a.asset_category,
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0
and (a.disposal_date < %(from_date)s or a.disposal_date > %(to_date)s) then
0
else
a.opening_accumulated_depreciation
end), 0) as accumulated_depreciation_as_on_from_date,
ifnull(sum(case when a.disposal_date >= %(from_date)s and a.disposal_date <= %(to_date)s then
a.opening_accumulated_depreciation
else
0
end), 0) as depreciation_eliminated_during_the_period,
0 as depreciation_amount_during_the_period
from `tabAsset` a
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s
and not exists(select * from `tabDepreciation Schedule` ds where a.name = ds.parent)
group by a.asset_category) as results
group by results.asset_category
""", {"to_date": filters.to_date, "from_date": filters.from_date, "company": filters.company}, as_dict=1)
if not asset.schedules: # if no schedule,
if asset.disposal_date:
# and disposal is NOT within the period, then opening accumulated depreciation not included
if getdate(asset.disposal_date) < getdate(filters.from_date) or getdate(asset.disposal_date) > getdate(filters.to_date):
asset_depreciations[d.asset_category]['accumulated_depreciation_as_on_from_date'] = 0
# if no schedule, and disposal is within period, accumulated dep is the amount eliminated
if getdate(asset.disposal_date) >= getdate(filters.from_date) and getdate(asset.disposal_date) <= getdate(filters.to_date):
depr.depreciation_eliminated_during_the_period += asset.opening_accumulated_depreciation
for schedule in asset.get("schedules"):
if getdate(schedule.schedule_date) < getdate(filters.from_date):
if not asset.disposal_date or getdate(asset.disposal_date) >= getdate(filters.from_date):
depr.accumulated_depreciation_as_on_from_date += flt(schedule.depreciation_amount)
elif getdate(schedule.schedule_date) <= getdate(filters.to_date):
if not asset.disposal_date:
depr.depreciation_amount_during_the_period += flt(schedule.depreciation_amount)
else:
if getdate(schedule.schedule_date) <= getdate(asset.disposal_date):
depr.depreciation_amount_during_the_period += flt(schedule.depreciation_amount)
if asset.disposal_date and getdate(asset.disposal_date) >= getdate(filters.from_date) and getdate(asset.disposal_date) <= getdate(filters.to_date):
if getdate(schedule.schedule_date) <= getdate(asset.disposal_date):
depr.depreciation_eliminated_during_the_period += flt(schedule.depreciation_amount)
return asset_depreciations
def get_columns(filters):
return [
{

View File

@@ -2,7 +2,7 @@
// License: GNU General Public License v3. See license.txt
frappe.require("assets/erpnext/js/financial_statements.js", function() {
frappe.query_reports["Balance Sheet"] = erpnext.financial_statements;
frappe.query_reports["Balance Sheet"] = $.extend({}, erpnext.financial_statements);
frappe.query_reports["Balance Sheet"]["filters"].push({
"fieldname": "accumulated_values",

View File

@@ -135,11 +135,11 @@ def get_chart_data(filters, columns, asset, liability, equity):
datasets = []
if asset_data:
datasets.append({'name':'Assets', 'values': asset_data})
datasets.append({'name':_('Assets'), 'values': asset_data})
if liability_data:
datasets.append({'name':'Liabilities', 'values': liability_data})
datasets.append({'name':_('Liabilities'), 'values': liability_data})
if equity_data:
datasets.append({'name':'Equity', 'values': equity_data})
datasets.append({'name':_('Equity'), 'values': equity_data})
chart = {
"data": {
@@ -153,4 +153,4 @@ def get_chart_data(filters, columns, asset, liability, equity):
else:
chart["type"] = "line"
return chart
return chart

View File

@@ -130,7 +130,7 @@ def get_cash_flow_data(fiscal_year, companies, filters):
section_data.append(net_profit_loss)
for account in cash_flow_account['account_types']:
account_data = get_account_type_based_data(account['account_type'], companies, fiscal_year)
account_data = get_account_type_based_data(account['account_type'], companies, fiscal_year, filters)
account_data.update({
"account_name": account['label'],
"account": account['label'],
@@ -148,12 +148,12 @@ def get_cash_flow_data(fiscal_year, companies, filters):
return data
def get_account_type_based_data(account_type, companies, fiscal_year):
def get_account_type_based_data(account_type, companies, fiscal_year, filters):
data = {}
total = 0
for company in companies:
amount = get_account_type_based_gl_data(company,
fiscal_year.year_start_date, fiscal_year.year_end_date, account_type)
fiscal_year.year_start_date, fiscal_year.year_end_date, account_type, filters)
if amount and account_type == "Depreciation":
amount *= -1

View File

@@ -286,14 +286,14 @@ class PartyLedgerSummaryReport(object):
if parties and accounts:
if len(parties) == 1:
party = parties.keys()[0]
party = list(parties.keys())[0]
for account, amount in iteritems(accounts):
self.party_adjustment_accounts.add(account)
self.party_adjustment_details.setdefault(party, {})
self.party_adjustment_details[party].setdefault(account, 0)
self.party_adjustment_details[party][account] += amount
elif len(accounts) == 1 and not has_irrelevant_entry:
account = accounts.keys()[0]
account = list(accounts.keys())[0]
self.party_adjustment_accounts.add(account)
for party, amount in iteritems(parties):
self.party_adjustment_details.setdefault(party, {})

View File

@@ -407,9 +407,12 @@ def get_cost_centers_with_children(cost_centers):
all_cost_centers = []
for d in cost_centers:
lft, rgt = frappe.db.get_value("Cost Center", d, ["lft", "rgt"])
children = frappe.get_all("Cost Center", filters={"lft": [">=", lft], "rgt": ["<=", rgt]})
all_cost_centers += [c.name for c in children]
if frappe.db.exists("Cost Center", d):
lft, rgt = frappe.db.get_value("Cost Center", d, ["lft", "rgt"])
children = frappe.get_all("Cost Center", filters={"lft": [">=", lft], "rgt": ["<=", rgt]})
all_cost_centers += [c.name for c in children]
else:
frappe.throw(_("Cost Center: {0} does not exist".format(d)))
return list(set(all_cost_centers))

View File

@@ -121,19 +121,11 @@ def get_gl_entries(filters):
select_fields = """, debit, credit, debit_in_account_currency,
credit_in_account_currency """
group_by_statement = ''
order_by_statement = "order by posting_date, account"
if filters.get("group_by") == _("Group by Voucher"):
order_by_statement = "order by posting_date, voucher_type, voucher_no"
if filters.get("group_by") == _("Group by Voucher (Consolidated)"):
group_by_statement = "group by voucher_type, voucher_no, account, cost_center"
select_fields = """, sum(debit) as debit, sum(credit) as credit,
sum(debit_in_account_currency) as debit_in_account_currency,
sum(credit_in_account_currency) as credit_in_account_currency"""
if filters.get("include_default_book_entries"):
filters['company_fb'] = frappe.db.get_value("Company",
filters.get("company"), 'default_finance_book')
@@ -146,11 +138,10 @@ def get_gl_entries(filters):
against_voucher_type, against_voucher, account_currency,
remarks, against, is_opening {select_fields}
from `tabGL Entry`
where company=%(company)s {conditions} {group_by_statement}
where company=%(company)s {conditions}
{order_by_statement}
""".format(
select_fields=select_fields, conditions=get_conditions(filters),
group_by_statement=group_by_statement,
order_by_statement=order_by_statement
),
filters, as_dict=1)
@@ -187,7 +178,8 @@ def get_conditions(filters):
if not (filters.get("account") or filters.get("party") or
filters.get("group_by") in ["Group by Account", "Group by Party"]):
conditions.append("posting_date >=%(from_date)s")
conditions.append("posting_date <=%(to_date)s")
conditions.append("(posting_date <=%(to_date)s or is_opening = 'Yes')")
if filters.get("project"):
conditions.append("project in %(project)s")
@@ -281,6 +273,7 @@ def initialize_gle_map(gl_entries, filters):
def get_accountwise_gle(filters, gl_entries, gle_map):
totals = get_totals_dict()
entries = []
consolidated_gle = OrderedDict()
group_by = group_by_field(filters.get('group_by'))
def update_value_in_dict(data, key, gle):
@@ -306,11 +299,19 @@ def get_accountwise_gle(filters, gl_entries, gle_map):
if filters.get("group_by") != _('Group by Voucher (Consolidated)'):
gle_map[gle.get(group_by)].entries.append(gle)
else:
entries.append(gle)
key = (gle.get("voucher_type"), gle.get("voucher_no"),
gle.get("account"), gle.get("cost_center"))
if key not in consolidated_gle:
consolidated_gle.setdefault(key, gle)
else:
update_value_in_dict(consolidated_gle, key, gle)
update_value_in_dict(gle_map[gle.get(group_by)].totals, 'closing', gle)
update_value_in_dict(totals, 'closing', gle)
for key, value in consolidated_gle.items():
entries.append(value)
return totals, entries
def get_result_as_list(data, filters):

View File

@@ -154,28 +154,31 @@ class GrossProfitGenerator(object):
def get_average_rate_based_on_group_by(self):
# sum buying / selling totals for group
for key in list(self.grouped):
for i, row in enumerate(self.grouped[key]):
if row.parent in self.returned_invoices \
and row.item_code in self.returned_invoices[row.parent]:
returned_item_rows = self.returned_invoices[row.parent][row.item_code]
for returned_item_row in returned_item_rows:
row.qty += returned_item_row.qty
row.base_amount += flt(returned_item_row.base_amount, self.currency_precision)
row.buying_amount = flt(row.qty * row.buying_rate, self.currency_precision)
if i==0:
new_row = row
elif self.filters.get("group_by") != "Invoice":
new_row.qty += row.qty
new_row.buying_amount += flt(row.buying_amount, self.currency_precision)
new_row.base_amount += flt(row.base_amount, self.currency_precision)
if self.filters.get("group_by") == "Invoice" and (row.qty or row.base_amount):
self.grouped_data_based_on_group_by(row)
if self.filters.get("group_by") != "Invoice":
for i, row in enumerate(self.grouped[key]):
if i==0:
new_row = row
else:
new_row.qty += row.qty
new_row.buying_amount += flt(row.buying_amount, self.currency_precision)
new_row.base_amount += flt(row.base_amount, self.currency_precision)
new_row = self.set_average_rate(new_row)
self.grouped_data.append(new_row)
else:
for i, row in enumerate(self.grouped[key]):
if row.parent in self.returned_invoices \
and row.item_code in self.returned_invoices[row.parent]:
returned_item_rows = self.returned_invoices[row.parent][row.item_code]
for returned_item_row in returned_item_rows:
row.qty += returned_item_row.qty
row.base_amount += flt(returned_item_row.base_amount, self.currency_precision)
row.buying_amount = flt(row.qty * row.buying_rate, self.currency_precision)
if row.qty or row.base_amount:
row = self.set_average_rate(row)
self.grouped_data.append(row)
self.grouped_data_based_on_group_by(new_row)
def grouped_data_based_on_group_by(self, row):
row = self.set_average_rate(row)
self.grouped_data.append(row)
def set_average_rate(self, new_row):
new_row.gross_profit = flt(new_row.base_amount - new_row.buying_amount, self.currency_precision)
@@ -204,10 +207,7 @@ class GrossProfitGenerator(object):
.setdefault(inv.item_code, []).append(inv)
def skip_row(self, row, product_bundles):
if self.filters.get("group_by") != "Invoice":
if not row.get(scrub(self.filters.get("group_by", ""))):
return True
elif row.get("is_return") == 1:
if row.get("is_return") == 1:
return True
def get_buying_amount_from_product_bundle(self, row, product_bundle):

View File

@@ -17,7 +17,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
filters.update({"from_date": filters.get("date_range") and filters.get("date_range")[0], "to_date": filters.get("date_range") and filters.get("date_range")[1]})
columns = get_columns(additional_table_columns)
company_currency = erpnext.get_company_currency(filters.get('company'))
company_currency = frappe.get_cached_value('Company', filters.get("company"), "default_currency")
item_list = get_items(filters, additional_query_columns)
if item_list:

View File

@@ -27,8 +27,8 @@ frappe.query_reports["Payment Period Based On Invoice Date"] = {
fieldname:"payment_type",
label: __("Payment Type"),
fieldtype: "Select",
options: "Incoming\nOutgoing",
default: "Incoming"
options: __("Incoming")+"\n"+__("Outgoing"),
default: __("Incoming")
},
{
"fieldname":"party_type",

View File

@@ -4,11 +4,13 @@
from __future__ import unicode_literals
import frappe
from frappe import _
from erpnext.accounts.report.accounts_receivable.accounts_receivable import get_ageing_data
from erpnext.accounts.report.accounts_receivable.accounts_receivable import ReceivablePayableReport
from frappe.utils import getdate, flt
def execute(filters=None):
if not filters: filters = {}
if not filters:
filters = {}
validate_filters(filters)
columns = get_columns(filters)
@@ -19,18 +21,29 @@ def execute(filters=None):
for d in entries:
invoice = invoice_details.get(d.against_voucher) or frappe._dict()
if d.reference_type=="Purchase Invoice":
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,
invoice.posting_date, invoice.due_date, d.debit, d.credit, d.remarks]
d.update({
"range1": 0,
"range2": 0,
"range3": 0,
"range4": 0,
"outstanding": payment_amount
})
if d.against_voucher:
row += get_ageing_data(30, 60, 90, 120, d.posting_date, invoice.posting_date, payment_amount)
else:
row += ["", "", "", "", ""]
ReceivablePayableReport(filters).get_ageing_data(invoice.posting_date, d)
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,
d.age, d.range1, d.range2, d.range3, d.range4
]
if invoice.due_date:
row.append((getdate(d.posting_date) - getdate(invoice.due_date)).days or 0)
@@ -39,8 +52,8 @@ def execute(filters=None):
return columns, data
def validate_filters(filters):
if (filters.get("payment_type") == "Incoming" and filters.get("party_type") == "Supplier") or \
(filters.get("payment_type") == "Outgoing" and filters.get("party_type") == "Customer"):
if (filters.get("payment_type") == _("Incoming") and filters.get("party_type") == "Supplier") or \
(filters.get("payment_type") == _("Outgoing") and filters.get("party_type") == "Customer"):
frappe.throw(_("{0} payment entries can not be filtered by {1}")\
.format(filters.payment_type, filters.party_type))
@@ -51,7 +64,7 @@ def get_columns(filters):
_("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") + (":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",
@@ -69,7 +82,7 @@ def get_conditions(filters):
conditions = []
if not filters.party_type:
if filters.payment_type == "Outgoing":
if filters.payment_type == _("Outgoing"):
filters.party_type = "Supplier"
else:
filters.party_type = "Customer"
@@ -101,7 +114,7 @@ def get_entries(filters):
def get_invoice_posting_date_map(filters):
invoice_details = {}
dt = "Sales Invoice" if filters.get("payment_type") == "Incoming" else "Purchase Invoice"
dt = "Sales Invoice" if filters.get("payment_type") == _("Incoming") else "Purchase Invoice"
for t in frappe.db.sql("select name, posting_date, due_date from `tab{0}`".format(dt), as_dict=1):
invoice_details[t.name] = t

View File

@@ -4,6 +4,7 @@
from __future__ import unicode_literals
import frappe
from frappe.utils import flt
from frappe.model.meta import get_field_precision
from frappe import msgprint, _
def execute(filters=None):
@@ -67,7 +68,8 @@ def _execute(filters, additional_table_columns=None, additional_query_columns=No
total_tax = 0
for tax_acc in tax_accounts:
if tax_acc not in income_accounts:
tax_amount = flt(invoice_tax_map.get(inv.name, {}).get(tax_acc))
tax_amount_precision = get_field_precision(frappe.get_meta("Sales Taxes and Charges").get_field("tax_amount"), currency=company_currency) or 2
tax_amount = flt(invoice_tax_map.get(inv.name, {}).get(tax_acc), tax_amount_precision)
total_tax += tax_amount
row.append(tax_amount)

View File

@@ -5,9 +5,8 @@
"docstatus": 0,
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"letter_head": "Capital Traders",
"modified": "2018-12-12 05:10:02.987274",
"is_standard": "Yes",
"modified": "2019-02-12 05:10:02.987274",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Supplier Ledger Summary",

View File

@@ -6,8 +6,7 @@
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"letter_head": "Gadgets International",
"modified": "2018-08-21 11:25:00.551823",
"modified": "2018-09-21 11:25:00.551823",
"modified_by": "Administrator",
"module": "Accounts",
"name": "TDS Computation Summary",

View File

@@ -6,8 +6,7 @@
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"letter_head": "Gadgets International",
"modified": "2018-08-21 11:33:40.804532",
"modified": "2019-09-24 13:46:16.473711",
"modified_by": "Administrator",
"module": "Accounts",
"name": "TDS Payable Monthly",

View File

@@ -75,8 +75,7 @@ def get_data(filters):
accumulate_values_into_parents(accounts, accounts_by_name)
data = prepare_data(accounts, filters, total_row, parent_children_map, company_currency)
data = filter_out_zero_value_rows(data, parent_children_map,
show_zero_values=filters.get("show_zero_values"))
data = filter_out_zero_value_rows(data, parent_children_map, show_zero_values=filters.get("show_zero_values"))
return data
@@ -175,33 +174,11 @@ def calculate_values(accounts, gl_entries_by_account, opening_balances, filters,
d["closing_debit"] = d["opening_debit"] + d["debit"]
d["closing_credit"] = d["opening_credit"] + d["credit"]
total_row["debit"] += d["debit"]
total_row["credit"] += d["credit"]
if d["root_type"] == "Asset" or d["root_type"] == "Equity" or d["root_type"] == "Expense":
d["opening_debit"] -= d["opening_credit"]
d["closing_debit"] -= d["closing_credit"]
prepare_opening_closing(d)
# For opening
check_opening_closing_has_negative_value(d, "opening_debit", "opening_credit")
# For closing
check_opening_closing_has_negative_value(d, "closing_debit", "closing_credit")
if d["root_type"] == "Liability" or d["root_type"] == "Income":
d["opening_credit"] -= d["opening_debit"]
d["closing_credit"] -= d["closing_debit"]
# For opening
check_opening_closing_has_negative_value(d, "opening_credit", "opening_debit")
# For closing
check_opening_closing_has_negative_value(d, "closing_credit", "closing_debit")
total_row["opening_debit"] += d["opening_debit"]
total_row["closing_debit"] += d["closing_debit"]
total_row["opening_credit"] += d["opening_credit"]
total_row["closing_credit"] += d["closing_credit"]
for field in value_fields:
total_row[field] += d[field]
return total_row
@@ -215,6 +192,10 @@ def prepare_data(accounts, filters, total_row, parent_children_map, company_curr
data = []
for d in accounts:
# Prepare opening closing for group account
if parent_children_map.get(d.account):
prepare_opening_closing(d)
has_value = False
row = {
"account": d.name,
@@ -301,11 +282,16 @@ def get_columns():
}
]
def check_opening_closing_has_negative_value(d, dr_or_cr, switch_to_column):
# If opening debit has negetive value then move it to opening credit and vice versa.
def prepare_opening_closing(row):
dr_or_cr = "debit" if row["root_type"] in ["Asset", "Equity", "Expense"] else "credit"
reverse_dr_or_cr = "credit" if dr_or_cr == "debit" else "debit"
if d[dr_or_cr] < 0:
d[switch_to_column] = abs(d[dr_or_cr])
d[dr_or_cr] = 0.0
else:
d[switch_to_column] = 0.0
for col_type in ["opening", "closing"]:
valid_col = col_type + "_" + dr_or_cr
reverse_col = col_type + "_" + reverse_dr_or_cr
row[valid_col] -= row[reverse_col]
if row[valid_col] < 0:
row[reverse_col] = abs(row[valid_col])
row[valid_col] = 0.0
else:
row[reverse_col] = 0.0

View File

@@ -18,14 +18,17 @@ def execute(filters=None):
return columns, data
def get_data(filters, show_party_name):
party_name_field = "{0}_name".format(frappe.scrub(filters.get('party_type')))
if filters.get('party_type') in ('Customer', 'Supplier', 'Employee', 'Member'):
party_name_field = "{0}_name".format(frappe.scrub(filters.get('party_type')))
if filters.get('party_type') == 'Student':
party_name_field = 'first_name'
elif filters.get('party_type') == 'Shareholder':
party_name_field = 'title'
else:
party_name_field = 'name'
party_filters = {"name": filters.get("party")} if filters.get("party") else {}
parties = frappe.get_all(filters.get("party_type"), fields = ["name", party_name_field],
parties = frappe.get_all(filters.get("party_type"), fields = ["name", party_name_field],
filters = party_filters, order_by="name")
company_currency = frappe.get_cached_value('Company', filters.company, "default_currency")
opening_balances = get_opening_balances(filters)
@@ -70,7 +73,7 @@ def get_data(filters, show_party_name):
# totals
for col in total_row:
total_row[col] += row.get(col)
row.update({
"currency": company_currency
})
@@ -78,7 +81,7 @@ def get_data(filters, show_party_name):
has_value = False
if (opening_debit or opening_credit or debit or credit or closing_debit or closing_credit):
has_value =True
if cint(filters.show_zero_values) or has_value:
data.append(row)
@@ -94,9 +97,9 @@ def get_data(filters, show_party_name):
def get_opening_balances(filters):
gle = frappe.db.sql("""
select party, sum(debit) as opening_debit, sum(credit) as opening_credit
select party, sum(debit) as opening_debit, sum(credit) as opening_credit
from `tabGL Entry`
where company=%(company)s
where company=%(company)s
and ifnull(party_type, '') = %(party_type)s and ifnull(party, '') != ''
and (posting_date < %(from_date)s or ifnull(is_opening, 'No') = 'Yes')
group by party""", {
@@ -114,11 +117,11 @@ def get_opening_balances(filters):
def get_balances_within_period(filters):
gle = frappe.db.sql("""
select party, sum(debit) as debit, sum(credit) as credit
select party, sum(debit) as debit, sum(credit) as credit
from `tabGL Entry`
where company=%(company)s
where company=%(company)s
and ifnull(party_type, '') = %(party_type)s and ifnull(party, '') != ''
and posting_date >= %(from_date)s and posting_date <= %(to_date)s
and posting_date >= %(from_date)s and posting_date <= %(to_date)s
and ifnull(is_opening, 'No') = 'No'
group by party""", {
"company": filters.company,

View File

@@ -84,7 +84,8 @@ def validate_fiscal_year(date, fiscal_year, company, label="Date", doc=None):
throw(_("{0} '{1}' not in Fiscal Year {2}").format(label, formatdate(date), fiscal_year))
@frappe.whitelist()
def get_balance_on(account=None, date=None, party_type=None, party=None, company=None, in_account_currency=True, cost_center=None):
def get_balance_on(account=None, date=None, party_type=None, party=None, company=None,
in_account_currency=True, cost_center=None, ignore_account_permission=False):
if not account and frappe.form_dict.get("account"):
account = frappe.form_dict.get("account")
if not date and frappe.form_dict.get("date"):
@@ -140,7 +141,8 @@ def get_balance_on(account=None, date=None, party_type=None, party=None, company
if account:
if not frappe.flags.ignore_account_permission:
if not (frappe.flags.ignore_account_permission
or ignore_account_permission):
acc.check_permission("read")
if report_type == 'Profit and Loss':

View File

@@ -286,12 +286,6 @@ class Asset(AccountsController):
if self.status not in ("Submitted", "Partially Depreciated", "Fully Depreciated"):
frappe.throw(_("Asset cannot be cancelled, as it is already {0}").format(self.status))
if self.purchase_invoice:
frappe.throw(_("Please cancel Purchase Invoice {0} first").format(self.purchase_invoice))
if self.purchase_receipt:
frappe.throw(_("Please cancel Purchase Receipt {0} first").format(self.purchase_receipt))
def delete_depreciation_entries(self):
for d in self.get("schedules"):
if d.journal_entry:

View File

@@ -56,8 +56,7 @@ def assign_tasks(asset_maintenance_name, assign_to_member, maintenance_task, nex
def calculate_next_due_date(periodicity, start_date = None, end_date = None, last_completion_date = None, next_due_date = None):
if not start_date and not last_completion_date:
start_date = frappe.utils.now()
if last_completion_date and (last_completion_date > start_date or not start_date):
if last_completion_date and ((start_date and last_completion_date > start_date) or not start_date):
start_date = last_completion_date
if periodicity == 'Daily':
next_due_date = add_days(start_date, 1)
@@ -115,4 +114,4 @@ def get_maintenance_log(asset_name):
select maintenance_status, count(asset_name) as count, asset_name
from `tabAsset Maintenance Log`
where asset_name=%s group by maintenance_status""",
(asset_name), as_dict=1)
(asset_name), as_dict=1)

View File

@@ -10,7 +10,8 @@ frappe.ui.form.on("Purchase Order", {
frm.custom_make_buttons = {
'Purchase Receipt': 'Receipt',
'Purchase Invoice': 'Invoice',
'Stock Entry': 'Material to Supplier'
'Stock Entry': 'Material to Supplier',
'Payment Entry': 'Payment'
}
frm.set_query("reserve_warehouse", "supplied_items", function() {

View File

@@ -2176,7 +2176,7 @@
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "other_charges_calculation",
"fieldtype": "Text",
"fieldtype": "Long Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@@ -3948,7 +3948,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2019-06-24 20:55:03.466766",
"modified": "2020-04-16 18:54:54.840653",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order",

View File

@@ -472,7 +472,7 @@ def make_rm_stock_entry(purchase_order, rm_items):
'from_warehouse': rm_item_data["warehouse"],
'stock_uom': rm_item_data["stock_uom"],
'main_item_code': rm_item_data["item_code"],
'allow_alternative_item': item_wh[rm_item_code].get('allow_alternative_item')
'allow_alternative_item': item_wh.get(rm_item_code, {}).get('allow_alternative_item')
}
}
stock_entry.add_to_stock_entry_detail(items_dict)

View File

@@ -1395,7 +1395,7 @@
"collapsible": 0,
"columns": 0,
"fieldname": "other_charges_calculation",
"fieldtype": "Text",
"fieldtype": "Long Text",
"hidden": 0,
"ignore_user_permissions": 0,
"ignore_xss_filter": 0,
@@ -2845,7 +2845,7 @@
"istable": 0,
"max_attachments": 0,
"menu_index": 0,
"modified": "2019-01-07 16:52:01.505553",
"modified": "2020-04-16 19:05:41.924303",
"modified_by": "Administrator",
"module": "Buying",
"name": "Supplier Quotation",

View File

@@ -138,7 +138,7 @@ def refresh_scorecards():
# Check to see if any new scorecard periods are created
if make_all_scorecards(sc.name) > 0:
# Save the scorecard to update the score and standings
sc.save()
frappe.get_doc('Supplier Scorecard', sc.name).save()
@frappe.whitelist()

View File

@@ -30,7 +30,9 @@ def update_last_purchase_rate(doc, is_submit):
# for it to be considered for latest purchase rate
if flt(d.conversion_factor):
last_purchase_rate = flt(d.base_rate) / flt(d.conversion_factor)
else:
# Check if item code is present
# Conversion factor should not be mandatory for non itemized items
elif d.item_code:
frappe.throw(_("UOM Conversion factor is required in row {0}").format(d.idx))
# update last purchsae rate
@@ -84,13 +86,13 @@ def get_linked_material_requests(items):
items = json.loads(items)
mr_list = []
for item in items:
material_request = frappe.db.sql("""SELECT distinct mr.name AS mr_name,
(mr_item.qty - mr_item.ordered_qty) AS qty,
material_request = frappe.db.sql("""SELECT distinct mr.name AS mr_name,
(mr_item.qty - mr_item.ordered_qty) AS qty,
mr_item.item_code AS item_code,
mr_item.name AS mr_item
mr_item.name AS mr_item
FROM `tabMaterial Request` mr, `tabMaterial Request Item` mr_item
WHERE mr.name = mr_item.parent
AND mr_item.item_code = %(item)s
AND mr_item.item_code = %(item)s
AND mr.material_request_type = 'Purchase'
AND mr.per_ordered < 99.99
AND mr.docstatus = 1
@@ -98,6 +100,6 @@ def get_linked_material_requests(items):
ORDER BY mr_item.item_code ASC""",{"item": item}, as_dict=1)
if material_request:
mr_list.append(material_request)
return mr_list

View File

@@ -87,7 +87,7 @@ class AccountsController(TransactionBase):
self.validate_currency()
if self.doctype == 'Purchase Invoice':
self.validate_paid_amount()
self.calculate_paid_amount()
if self.doctype in ['Purchase Invoice', 'Sales Invoice']:
pos_check_field = "is_pos" if self.doctype=="Sales Invoice" else "is_paid"
@@ -131,22 +131,23 @@ class AccountsController(TransactionBase):
else:
df.set("print_hide", 1)
def validate_paid_amount(self):
def calculate_paid_amount(self):
if hasattr(self, "is_pos") or hasattr(self, "is_paid"):
is_paid = self.get("is_pos") or self.get("is_paid")
if cint(is_paid) == 1:
if flt(self.paid_amount) == 0 and flt(self.outstanding_amount) > 0:
if self.cash_bank_account:
self.paid_amount = flt(flt(self.outstanding_amount), self.precision("paid_amount"))
self.base_paid_amount = flt(self.paid_amount * self.conversion_rate,
self.precision("base_paid_amount"))
else:
# show message that the amount is not paid
self.paid_amount = 0
frappe.throw(
_("Note: Payment Entry will not be created since 'Cash or Bank Account' was not specified"))
else:
frappe.db.set(self, 'paid_amount', 0)
if is_paid:
if not self.cash_bank_account:
# show message that the amount is not paid
frappe.throw(_("Note: Payment Entry will not be created since 'Cash or Bank Account' was not specified"))
if cint(self.is_return) and (self.grand_total > self.paid_amount):
self.paid_amount = flt(flt(self.grand_total), self.precision("paid_amount"))
elif not flt(self.paid_amount) and flt(self.outstanding_amount) > 0:
self.paid_amount = flt(flt(self.outstanding_amount), self.precision("paid_amount"))
self.base_paid_amount = flt(self.paid_amount * self.conversion_rate,
self.precision("base_paid_amount"))
def set_missing_values(self, for_validate=False):
if frappe.flags.in_test:
@@ -374,9 +375,10 @@ class AccountsController(TransactionBase):
return gl_dict
def validate_qty_is_not_zero(self):
for item in self.items:
if not item.qty:
frappe.throw(_("Item quantity can not be zero"))
if self.doctype != "Purchase Receipt":
for item in self.items:
if not item.qty:
frappe.throw(_("Item quantity can not be zero"))
def validate_account_currency(self, account, account_currency=None):
valid_currency = [self.company_currency]
@@ -1096,6 +1098,8 @@ def get_supplier_block_status(party_name):
@frappe.whitelist()
def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name):
data = json.loads(trans_items)
sales_doctypes = ['Sales Order', 'Sales Invoice', 'Delivery Note', 'Quotation']
for d in data:
child_item = frappe.get_doc(parent_doctype + ' Item', d.get("docname"))
@@ -1112,6 +1116,28 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name):
.format(child_item.idx, child_item.item_code))
else:
child_item.rate = flt(d.get("rate"))
if flt(child_item.price_list_rate):
if flt(child_item.rate) > flt(child_item.price_list_rate):
# if rate is greater than price_list_rate, set margin
# or set discount
child_item.discount_percentage = 0
if parent_doctype in sales_doctypes:
child_item.margin_type = "Amount"
child_item.margin_rate_or_amount = flt(child_item.rate - child_item.price_list_rate,
child_item.precision("margin_rate_or_amount"))
child_item.rate_with_margin = child_item.rate
else:
child_item.discount_percentage = flt((1 - flt(child_item.rate) / flt(child_item.price_list_rate)) * 100.0,
child_item.precision("discount_percentage"))
child_item.discount_amount = flt(
child_item.price_list_rate) - flt(child_item.rate)
if parent_doctype in sales_doctypes:
child_item.margin_type = ""
child_item.margin_rate_or_amount = 0
child_item.rate_with_margin = 0
child_item.flags.ignore_validate_update_after_submit = True
child_item.save()

View File

@@ -337,7 +337,7 @@ class BuyingController(StockController):
if self.doctype in ["Purchase Receipt", "Purchase Invoice"]:
rm.consumed_qty = required_qty
rm.description = bom_item.description
if item.batch_no and not rm.batch_no:
if item.batch_no and frappe.db.get_value("Item", rm.rm_item_code, "has_batch_no") and not rm.batch_no:
rm.batch_no = item.batch_no
# get raw materials rate
@@ -395,7 +395,9 @@ class BuyingController(StockController):
def set_qty_as_per_stock_uom(self):
for d in self.get("items"):
if d.meta.get_field("stock_qty"):
if not d.conversion_factor:
# Check if item code is present
# Conversion factor should not be mandatory for non itemized items
if not d.conversion_factor and d.item_code:
frappe.throw(_("Row {0}: Conversion Factor is mandatory").format(d.idx))
d.stock_qty = flt(d.qty) * flt(d.conversion_factor)
@@ -513,10 +515,15 @@ class BuyingController(StockController):
for d in self.get('supplied_items'):
# negative quantity is passed, as raw material qty has to be decreased
# when PR is submitted and it has to be increased when PR is cancelled
incoming_rate = 0
if self.is_return and self.return_against and self.docstatus==1:
incoming_rate = self.get_incoming_rate_for_sales_return(d.rm_item_code, self.return_against)
sl_entries.append(self.get_sl_entries(d, {
"item_code": d.rm_item_code,
"warehouse": self.supplier_warehouse,
"actual_qty": -1*flt(d.consumed_qty),
"incoming_rate": incoming_rate
}))
def on_submit(self):
@@ -715,7 +722,7 @@ def get_items_from_bom(item_code, bom, exploded_item=1):
where
t2.parent = t1.name and t1.item = %s
and t1.docstatus = 1 and t1.is_active = 1 and t1.name = %s
and t2.item_code = t3.name and t3.is_stock_item = 1""".format(doctype),
and t2.item_code = t3.name""".format(doctype),
(item_code, bom), as_dict=1)
if not bom_items:

View File

@@ -292,6 +292,7 @@ def copy_attributes_to_variant(item, variant):
if not variant.description:
variant.description = ""
else:
if item.variant_based_on=='Item Attribute':
if variant.attributes:
attributes_description = item.description + " "
@@ -299,7 +300,7 @@ def copy_attributes_to_variant(item, variant):
attributes_description += "<div>" + d.attribute + ": " + cstr(d.attribute_value) + "</div>"
if attributes_description not in variant.description:
variant.description += attributes_description
variant.description = attributes_description
def make_variant_item_code(template_item_code, template_item_name, variant):
"""Uses template's item code and abbreviations to make variant's item code"""

View File

@@ -152,6 +152,24 @@ def tax_account_query(doctype, txt, searchfield, start, page_len, filters):
def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False):
conditions = []
#Get searchfields from meta and use in Item Link field query
meta = frappe.get_meta("Item", cached=True)
searchfields = meta.get_search_fields()
if "description" in searchfields:
searchfields.remove("description")
columns = ''
extra_searchfields = [field for field in searchfields
if field not in ["name", "item_group", "description"]]
if extra_searchfields:
columns = ", " + ", ".join(extra_searchfields)
searchfields = searchfields + [field for field in[searchfield or "name", "item_code", "item_group", "item_name"]
if not field in searchfields]
searchfields = " or ".join([field + " like %(txt)s" for field in searchfields])
description_cond = ''
if frappe.db.count('Item', cache=True) < 50000:
# scan description only if items are less than 50000
@@ -162,17 +180,14 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals
concat(substr(tabItem.item_name, 1, 40), "..."), item_name) as item_name,
tabItem.item_group,
if(length(tabItem.description) > 40, \
concat(substr(tabItem.description, 1, 40), "..."), description) as decription
concat(substr(tabItem.description, 1, 40), "..."), description) as description
{columns}
from tabItem
where tabItem.docstatus < 2
and tabItem.has_variants=0
and tabItem.disabled=0
and (tabItem.end_of_life > %(today)s or ifnull(tabItem.end_of_life, '0000-00-00')='0000-00-00')
and (tabItem.`{key}` LIKE %(txt)s
or tabItem.item_code LIKE %(txt)s
or tabItem.item_group LIKE %(txt)s
or tabItem.item_name LIKE %(txt)s
or tabItem.item_code IN (select parent from `tabItem Barcode` where barcode LIKE %(txt)s)
and ({scond} or tabItem.item_code IN (select parent from `tabItem Barcode` where barcode LIKE %(txt)s)
{description_cond})
{fcond} {mcond}
order by
@@ -182,6 +197,8 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals
name, item_name
limit %(start)s, %(page_len)s """.format(
key=searchfield,
columns=columns,
scond=searchfields,
fcond=get_filters_cond(doctype, filters, conditions).replace('%', '%%'),
mcond=get_match_cond(doctype).replace('%', '%%'),
description_cond = description_cond),
@@ -280,22 +297,32 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters):
"page_len": page_len
}
having_clause = "having sum(sle.actual_qty) > 0"
if filters.get("is_return"):
having_clause = ""
if args.get('warehouse'):
batch_nos = frappe.db.sql("""select sle.batch_no, round(sum(sle.actual_qty),2), sle.stock_uom, concat('MFG-',batch.manufacturing_date), concat('EXP-',batch.expiry_date)
from `tabStock Ledger Entry` sle
INNER JOIN `tabBatch` batch on sle.batch_no = batch.name
where
batch.disabled = 0
and sle.item_code = %(item_code)s
and sle.warehouse = %(warehouse)s
and (sle.batch_no like %(txt)s
or batch.manufacturing_date like %(txt)s)
and batch.docstatus < 2
{0}
{match_conditions}
group by batch_no having sum(sle.actual_qty) > 0
order by batch.expiry_date, sle.batch_no desc
limit %(start)s, %(page_len)s""".format(cond, match_conditions=get_match_cond(doctype)), args)
batch_nos = frappe.db.sql("""select sle.batch_no, round(sum(sle.actual_qty),2), sle.stock_uom,
concat('MFG-',batch.manufacturing_date), concat('EXP-',batch.expiry_date)
from `tabStock Ledger Entry` sle
INNER JOIN `tabBatch` batch on sle.batch_no = batch.name
where
batch.disabled = 0
and sle.item_code = %(item_code)s
and sle.warehouse = %(warehouse)s
and (sle.batch_no like %(txt)s
or batch.expiry_date like %(txt)s
or batch.manufacturing_date like %(txt)s)
and batch.docstatus < 2
{cond}
{match_conditions}
group by batch_no {having_clause}
order by batch.expiry_date, sle.batch_no desc
limit %(start)s, %(page_len)s""".format(
cond=cond,
match_conditions=get_match_cond(doctype),
having_clause = having_clause
), args)
return batch_nos
else:
@@ -303,6 +330,7 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters):
where batch.disabled = 0
and item = %(item_code)s
and (name like %(txt)s
or expiry_date like %(txt)s
or manufacturing_date like %(txt)s)
and docstatus < 2
{0}
@@ -371,7 +399,7 @@ def get_expense_account(doctype, txt, searchfield, start, page_len, filters):
return frappe.db.sql("""select tabAccount.name from `tabAccount`
where (tabAccount.report_type = "Profit and Loss"
or tabAccount.account_type in ("Expense Account", "Fixed Asset", "Temporary", "Asset Received But Not Billed"))
or tabAccount.account_type in ("Expense Account", "Fixed Asset", "Temporary", "Asset Received But Not Billed", "Capital Work in Progress"))
and tabAccount.is_group=0
and tabAccount.docstatus!=2
and tabAccount.{key} LIKE %(txt)s

View File

@@ -18,34 +18,31 @@ def validate_return(doc):
validate_returned_items(doc)
def validate_return_against(doc):
filters = {"doctype": doc.doctype, "docstatus": 1, "company": doc.company}
if doc.meta.get_field("customer") and doc.customer:
filters["customer"] = doc.customer
elif doc.meta.get_field("supplier") and doc.supplier:
filters["supplier"] = doc.supplier
if not frappe.db.exists(filters):
if not frappe.db.exists(doc.doctype, doc.return_against):
frappe.throw(_("Invalid {0}: {1}")
.format(doc.meta.get_label("return_against"), doc.return_against))
else:
ref_doc = frappe.get_doc(doc.doctype, doc.return_against)
# validate posting date time
return_posting_datetime = "%s %s" % (doc.posting_date, doc.get("posting_time") or "00:00:00")
ref_posting_datetime = "%s %s" % (ref_doc.posting_date, ref_doc.get("posting_time") or "00:00:00")
party_type = "customer" if doc.doctype in ("Sales Invoice", "Delivery Note") else "supplier"
if get_datetime(return_posting_datetime) < get_datetime(ref_posting_datetime):
frappe.throw(_("Posting timestamp must be after {0}").format(format_datetime(ref_posting_datetime)))
if ref_doc.company == doc.company and ref_doc.get(party_type) == doc.get(party_type) and ref_doc.docstatus == 1:
# validate posting date time
return_posting_datetime = "%s %s" % (doc.posting_date, doc.get("posting_time") or "00:00:00")
ref_posting_datetime = "%s %s" % (ref_doc.posting_date, ref_doc.get("posting_time") or "00:00:00")
# validate same exchange rate
if doc.conversion_rate != ref_doc.conversion_rate:
frappe.throw(_("Exchange Rate must be same as {0} {1} ({2})")
.format(doc.doctype, doc.return_against, ref_doc.conversion_rate))
if get_datetime(return_posting_datetime) < get_datetime(ref_posting_datetime):
frappe.throw(_("Posting timestamp must be after {0}").format(format_datetime(ref_posting_datetime)))
# validate update stock
if doc.doctype == "Sales Invoice" and doc.update_stock and not ref_doc.update_stock:
frappe.throw(_("'Update Stock' can not be checked because items are not delivered via {0}")
.format(doc.return_against))
# validate same exchange rate
if doc.conversion_rate != ref_doc.conversion_rate:
frappe.throw(_("Exchange Rate must be same as {0} {1} ({2})")
.format(doc.doctype, doc.return_against, ref_doc.conversion_rate))
# validate update stock
if doc.doctype == "Sales Invoice" and doc.update_stock and not ref_doc.update_stock:
frappe.throw(_("'Update Stock' can not be checked because items are not delivered via {0}")
.format(doc.return_against))
def validate_returned_items(doc):
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
@@ -75,7 +72,7 @@ def validate_returned_items(doc):
items_returned = False
for d in doc.get("items"):
if d.item_code and (flt(d.qty) < 0 or d.get('received_qty') < 0):
if d.item_code and (flt(d.qty) < 0 or flt(d.get('received_qty')) < 0):
if d.item_code not in valid_items:
frappe.throw(_("Row # {0}: Returned Item {1} does not exists in {2} {3}")
.format(d.idx, d.item_code, doc.doctype, doc.return_against))
@@ -249,6 +246,8 @@ def make_return_doc(doctype, source_name, target_doc=None):
elif doc.doctype == 'Purchase Invoice':
doc.paid_amount = -1 * source.paid_amount
doc.base_paid_amount = -1 * source.base_paid_amount
doc.payment_terms_template = ''
doc.payment_schedule = []
if doc.get("is_return") and hasattr(doc, "packed_items"):
for d in doc.get("packed_items"):

View File

@@ -45,6 +45,7 @@ class SellingController(StockController):
self.set_gross_profit()
set_default_income_account_for_item(self)
self.set_customer_address()
self.validate_for_duplicate_items()
def set_missing_values(self, for_validate=False):
@@ -305,8 +306,9 @@ class SellingController(StockController):
if flt(d.conversion_factor)==0.0:
d.conversion_factor = get_conversion_factor(d.item_code, d.uom).get("conversion_factor") or 1.0
return_rate = 0
if cint(self.is_return) and self.return_against and self.docstatus==1:
return_rate = self.get_incoming_rate_for_sales_return(d.item_code, self.return_against)
if cint(self.is_return) and self.docstatus==1:
return_rate = self.get_incoming_rate_for_sales_return(d.item_code,
d.warehouse, self.return_against)
# On cancellation or if return entry submission, make stock ledger entry for
# target warehouse first, to update serial no values properly
@@ -381,6 +383,34 @@ class SellingController(StockController):
if self.get(address_field):
self.set(address_display_field, get_address_display(self.get(address_field)))
def validate_for_duplicate_items(self):
check_list, chk_dupl_itm = [], []
if cint(frappe.db.get_single_value("Selling Settings", "allow_multiple_items")):
return
for d in self.get('items'):
if self.doctype == "Sales Invoice":
e = [d.item_code, d.description, d.warehouse, d.sales_order or d.delivery_note, d.batch_no or '']
f = [d.item_code, d.description, d.sales_order or d.delivery_note]
elif self.doctype == "Delivery Note":
e = [d.item_code, d.description, d.warehouse, d.against_sales_order or d.against_sales_invoice, d.batch_no or '']
f = [d.item_code, d.description, d.against_sales_order or d.against_sales_invoice]
elif self.doctype in ["Sales Order", "Quotation"]:
e = [d.item_code, d.description, d.warehouse, '']
f = [d.item_code, d.description]
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1:
if e in check_list:
frappe.throw(_("Note: Item {0} entered multiple times").format(d.item_code))
else:
check_list.append(e)
else:
if f in chk_dupl_itm:
frappe.throw(_("Note: Item {0} entered multiple times").format(d.item_code))
else:
chk_dupl_itm.append(f)
def validate_items(self):
# validate items to see if they have is_sales_item enabled
from erpnext.controllers.buying_controller import validate_item_type

View File

@@ -54,6 +54,7 @@ class StockController(AccountsController):
gl_list = []
warehouse_with_no_account = []
precision = frappe.get_precision('GL Entry', 'debit_in_account_currency')
for item_row in voucher_details:
sle_list = sle_map.get(item_row.name)
if sle_list:
@@ -79,7 +80,7 @@ class StockController(AccountsController):
"against": item_row.expense_account,
"cost_center": item_row.cost_center,
"remarks": self.get("remarks") or "Accounting Entry for Stock",
"debit": flt(sle.stock_value_difference, 2),
"debit": flt(sle.stock_value_difference, precision),
"is_opening": item_row.get("is_opening") or self.get("is_opening") or "No",
}, warehouse_account[sle.warehouse]["account_currency"]))
@@ -89,7 +90,7 @@ class StockController(AccountsController):
"against": warehouse_account[sle.warehouse]["account"],
"cost_center": item_row.cost_center,
"remarks": self.get("remarks") or "Accounting Entry for Stock",
"credit": flt(sle.stock_value_difference, 2),
"credit": flt(sle.stock_value_difference, precision),
"project": item_row.get("project") or self.get("project"),
"is_opening": item_row.get("is_opening") or self.get("is_opening") or "No"
}))
@@ -299,7 +300,7 @@ class StockController(AccountsController):
return serialized_items
def get_incoming_rate_for_sales_return(self, item_code, against_document):
def get_incoming_rate_for_sales_return(self, item_code, warehouse, against_document):
incoming_rate = 0.0
if against_document and item_code:
incoming_rate = frappe.db.sql("""select abs(stock_value_difference / actual_qty)
@@ -308,6 +309,9 @@ class StockController(AccountsController):
and item_code = %s limit 1""",
(self.doctype, against_document, item_code))
incoming_rate = incoming_rate[0][0] if incoming_rate else 0.0
else:
incoming_rate = get_valuation_rate(item_code, warehouse,
self.doctype, against_document, company=self.company, currency=self.currency)
return incoming_rate

View File

@@ -77,7 +77,7 @@ class calculate_taxes_and_totals(object):
item.net_rate = item.rate
if not item.qty and self.doc.is_return:
if not item.qty and self.doc.get("is_return"):
item.amount = flt(-1 * item.rate, item.precision("amount"))
else:
item.amount = flt(item.rate * item.qty, item.precision("amount"))
@@ -305,11 +305,19 @@ class calculate_taxes_and_totals(object):
last_tax = self.doc.get("taxes")[-1]
non_inclusive_tax_amount = sum([flt(d.tax_amount_after_discount_amount)
for d in self.doc.get("taxes") if not d.included_in_print_rate])
diff = self.doc.total + non_inclusive_tax_amount \
- flt(last_tax.total, last_tax.precision("total"))
# If discount amount applied, deduct the discount amount
# because self.doc.total is always without discount, but last_tax.total is after discount
if self.discount_amount_applied and self.doc.discount_amount:
diff -= flt(self.doc.discount_amount)
diff = flt(diff, self.doc.precision("rounding_adjustment"))
if diff and abs(diff) <= (5.0 / 10**last_tax.precision("tax_amount")):
self.doc.rounding_adjustment = flt(flt(self.doc.rounding_adjustment) +
flt(diff), self.doc.precision("rounding_adjustment"))
self.doc.rounding_adjustment = diff
def calculate_totals(self):
self.doc.grand_total = flt(self.doc.get("taxes")[-1].total) + flt(self.doc.rounding_adjustment) \

View File

@@ -39,7 +39,6 @@ def validate_filters(filters):
frappe.throw(_("'Based On' and 'Group By' can not be same"))
def get_data(filters, conditions):
data = []
inc, cond= '',''
query_details = conditions["based_on_select"] + conditions["period_wise_select"]
@@ -47,13 +46,17 @@ def get_data(filters, conditions):
posting_date = 't1.transaction_date'
if conditions.get('trans') in ['Sales Invoice', 'Purchase Invoice', 'Purchase Receipt', 'Delivery Note']:
posting_date = 't1.posting_date'
if filters.period_based_on:
posting_date = 't1.'+filters.period_based_on
if conditions["based_on_select"] in ["t1.project,", "t2.project,"]:
cond = ' and '+ conditions["based_on_select"][:-1] +' IS Not NULL'
if conditions.get('trans') in ['Sales Order', 'Purchase Order']:
cond += " and t1.status != 'Closed'"
if conditions.get('trans') == 'Quotation' and filters.get("group_by") == 'Customer':
cond += " and t1.quotation_to = 'Customer'"
year_start_date, year_end_date = frappe.db.get_value("Fiscal Year",
filters.get('fiscal_year'), ["year_start_date", "year_end_date"])
@@ -64,7 +67,7 @@ def get_data(filters, conditions):
if filters.get("group_by") == 'Item':
sel_col = 't2.item_code'
elif filters.get("group_by") == 'Customer':
sel_col = 't1.customer'
sel_col = 't1.party_name' if conditions.get('trans') == 'Quotation' else 't1.customer'
elif filters.get("group_by") == 'Supplier':
sel_col = 't1.supplier'
@@ -225,7 +228,7 @@ def based_wise_columns_query(based_on, trans):
elif based_on == "Customer":
based_on_details["based_on_cols"] = ["Customer:Link/Customer:120", "Territory:Link/Territory:120"]
based_on_details["based_on_select"] = "t1.customer_name, t1.territory, "
based_on_details["based_on_group_by"] = 't1.customer'
based_on_details["based_on_group_by"] = 't1.party_name' if trans == 'Quotation' else 't1.customer'
based_on_details["addl_tables"] = ''
elif based_on == "Customer Group":

View File

@@ -21,42 +21,45 @@ def get_list_context(context=None):
def get_transaction_list(doctype, txt=None, filters=None, limit_start=0, limit_page_length=20, order_by="modified"):
user = frappe.session.user
key = None
ignore_permissions = False
if not filters: filters = []
if doctype == 'Supplier Quotation':
filters.append((doctype, "docstatus", "<", 2))
filters.append((doctype, 'docstatus', '<', 2))
else:
filters.append((doctype, "docstatus", "=", 1))
filters.append((doctype, 'docstatus', '=', 1))
if (user != "Guest" and is_website_user()) or doctype == 'Request for Quotation':
if (user != 'Guest' and is_website_user()) or doctype == 'Request for Quotation':
parties_doctype = 'Request for Quotation Supplier' if doctype == 'Request for Quotation' else doctype
# find party for this contact
customers, suppliers = get_customers_suppliers(parties_doctype, user)
if not customers and not suppliers: return []
key, parties = get_party_details(customers, suppliers)
if doctype == 'Request for Quotation':
return rfq_transaction_list(parties_doctype, doctype, parties, limit_start, limit_page_length)
filters.append((doctype, key, "in", parties))
if key:
return post_process(doctype, get_list_for_transactions(doctype, txt,
filters=filters, fields="name",limit_start=limit_start,
limit_page_length=limit_page_length,ignore_permissions=True,
order_by="modified desc"))
if customers:
if doctype == 'Quotation':
filters.append(('quotation_to', '=', 'Customer'))
filters.append(('party_name', 'in', customers))
else:
filters.append(('customer', 'in', customers))
elif suppliers:
filters.append(('supplier', 'in', suppliers))
else:
return []
return post_process(doctype, get_list_for_transactions(doctype, txt, filters, limit_start, limit_page_length,
fields="name", order_by="modified desc"))
if doctype == 'Request for Quotation':
parties = customers or suppliers
return rfq_transaction_list(parties_doctype, doctype, parties, limit_start, limit_page_length)
# Since customers and supplier do not have direct access to internal doctypes
ignore_permissions = True
transactions = get_list_for_transactions(doctype, txt, filters, limit_start, limit_page_length,
fields='name', ignore_permissions=ignore_permissions, order_by='modified desc')
return post_process(doctype, transactions)
def get_list_for_transactions(doctype, txt, filters, limit_start, limit_page_length=20,
ignore_permissions=False,fields=None, order_by=None):
ignore_permissions=False, fields=None, order_by=None):
""" Get List of transactions like Invoices, Orders """
from frappe.www.list import get_list
meta = frappe.get_meta(doctype)
@@ -77,22 +80,12 @@ def get_list_for_transactions(doctype, txt, filters, limit_start, limit_page_len
if or_filters:
for r in frappe.get_list(doctype, fields=fields,filters=filters, or_filters=or_filters,
limit_start=limit_start, limit_page_length=limit_page_length,
limit_start=limit_start, limit_page_length=limit_page_length,
ignore_permissions=ignore_permissions, order_by=order_by):
data.append(r)
return data
def get_party_details(customers, suppliers):
if customers:
key, parties = "customer", customers
elif suppliers:
key, parties = "supplier", suppliers
else:
key, parties = "customer", []
return key, parties
def rfq_transaction_list(parties_doctype, doctype, parties, limit_start, limit_page_length):
data = frappe.db.sql("""select distinct parent as name, supplier from `tab{doctype}`
where supplier = '{supplier}' and docstatus=1 order by modified desc limit {start}, {len}""".
@@ -130,38 +123,56 @@ def get_customers_suppliers(doctype, user):
suppliers = []
meta = frappe.get_meta(doctype)
customer_field_name = get_customer_field_name(doctype)
has_customer_field = meta.has_field(customer_field_name)
has_supplier_field = meta.has_field('supplier')
if has_common(["Supplier", "Customer"], frappe.get_roles(user)):
contacts = frappe.db.sql("""
select
select
`tabContact`.email_id,
`tabDynamic Link`.link_doctype,
`tabDynamic Link`.link_name
from
from
`tabContact`, `tabDynamic Link`
where
`tabContact`.name=`tabDynamic Link`.parent and `tabContact`.email_id =%s
""", user, as_dict=1)
customers = [c.link_name for c in contacts if c.link_doctype == 'Customer'] \
if meta.get_field("customer") else None
suppliers = [c.link_name for c in contacts if c.link_doctype == 'Supplier'] \
if meta.get_field("supplier") else None
customers = [c.link_name for c in contacts if c.link_doctype == 'Customer']
suppliers = [c.link_name for c in contacts if c.link_doctype == 'Supplier']
elif frappe.has_permission(doctype, 'read', user=user):
customers = [customer.name for customer in frappe.get_list("Customer")] \
if meta.get_field("customer") else None
suppliers = [supplier.name for supplier in frappe.get_list("Customer")] \
if meta.get_field("supplier") else None
customer_list = frappe.get_list("Customer")
customers = suppliers = [customer.name for customer in customer_list]
return customers, suppliers
return customers if has_customer_field else None, \
suppliers if has_supplier_field else None
def has_website_permission(doc, ptype, user, verbose=False):
doctype = doc.doctype
customers, suppliers = get_customers_suppliers(doctype, user)
if customers:
return frappe.get_all(doctype, filters=[(doctype, "customer", "in", customers),
(doctype, "name", "=", doc.name)]) and True or False
return frappe.db.exists(doctype, get_customer_filter(doc, customers))
elif suppliers:
fieldname = 'suppliers' if doctype == 'Request for Quotation' else 'supplier'
return frappe.get_all(doctype, filters=[(doctype, fieldname, "in", suppliers),
(doctype, "name", "=", doc.name)]) and True or False
return frappe.db.exists(doctype, filters={
'name': doc.name,
fieldname: ["in", suppliers]
})
else:
return False
def get_customer_filter(doc, customers):
doctype = doc.doctype
filters = frappe._dict()
filters.name = doc.name
filters[get_customer_field_name(doctype)] = ['in', customers]
if doctype == 'Quotation':
filters.quotation_to = 'Customer'
return filters
def get_customer_field_name(doctype):
if doctype == 'Quotation':
return 'party_name'
else:
return 'customer'

File diff suppressed because it is too large Load Diff

View File

@@ -88,7 +88,7 @@ def get_status(start_date, end_date):
end_date = getdate(end_date)
now_date = getdate(nowdate())
return "Active" if start_date < now_date < end_date else "Inactive"
return "Active" if start_date <= now_date <= end_date else "Inactive"
def update_status_for_contracts():

View File

@@ -1460,7 +1460,7 @@
"has_web_view": 0,
"hide_heading": 0,
"hide_toolbar": 0,
"icon": "fa fa-info-sign",
"icon": "fa fa-info-circle",
"idx": 195,
"image_view": 0,
"in_create": 0,
@@ -1468,7 +1468,7 @@
"issingle": 0,
"istable": 0,
"max_attachments": 0,
"modified": "2019-05-17 19:03:32.740910",
"modified": "2019-10-22 11:11:32.740910",
"modified_by": "Administrator",
"module": "CRM",
"name": "Opportunity",

View File

@@ -7,8 +7,7 @@
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"letter_head": "",
"modified": "2017-04-17 00:20:27.248275",
"modified": "2019-04-17 00:20:27.248275",
"modified_by": "Administrator",
"module": "CRM",
"name": "Campaign Efficiency",

View File

@@ -6,8 +6,7 @@
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"letter_head": "",
"modified": "2018-09-17 14:40:52.035394",
"modified": "2019-09-19 14:40:52.035394",
"modified_by": "Administrator",
"module": "CRM",
"name": "Lead Conversion Time",

View File

@@ -67,7 +67,7 @@ def get_communication_details(filters):
communication_count = None
communication_list = []
opportunities = frappe.db.get_values('Opportunity', {'opportunity_from': 'Lead'},\
['name', 'customer_name', 'lead', 'contact_email'], as_dict=1)
['name', 'customer_name', 'contact_email'], as_dict=1)
for d in opportunities:
invoice = frappe.db.sql('''

View File

@@ -7,8 +7,7 @@
"doctype": "Report",
"idx": 0,
"is_standard": "Yes",
"letter_head": "Shishuvan Secondary School",
"modified": "2018-02-08 15:11:35.339434",
"modified": "2019-02-08 15:11:35.339434",
"modified_by": "Administrator",
"module": "Education",
"name": "Final Assessment Grades",

View File

@@ -234,14 +234,17 @@ def get_order_taxes(shopify_order, shopify_settings):
return taxes
def update_taxes_with_shipping_lines(taxes, shipping_lines, shopify_settings):
"""Shipping lines represents the shipping details,
each such shipping detail consists of a list of tax_lines"""
for shipping_charge in shipping_lines:
taxes.append({
"charge_type": _("Actual"),
"account_head": get_tax_account_head(shipping_charge),
"description": shipping_charge["title"],
"tax_amount": shipping_charge["price"],
"cost_center": shopify_settings.cost_center
})
for tax in shipping_charge.get("tax_lines"):
taxes.append({
"charge_type": _("Actual"),
"account_head": get_tax_account_head(tax),
"description": tax["title"],
"tax_amount": tax["price"],
"cost_center": shopify_settings.cost_center
})
return taxes

View File

@@ -62,7 +62,8 @@ def _order(*args, **kwargs):
item_woo_com_id = item.get("product_id")
if frappe.get_value("Item",{"woocommerce_id": item_woo_com_id}):
if frappe.get_value("Item",{"woocommerce_id": item_woo_com_id}) or\
frappe.get_value("Item",{"item_name": item.get('name')}):
#Edit
link_item(item,1)
else:

View File

@@ -114,10 +114,11 @@ def add_account_subtype(account_subtype):
@frappe.whitelist()
def sync_transactions(bank, bank_account):
last_sync_date = frappe.db.get_value("Bank Account", bank_account, "last_integration_date")
if last_sync_date:
start_date = formatdate(last_sync_date, "YYYY-MM-dd")
'''Sync transactions based on the last integration date as the start date, after sync is completed
add the transaction date of the oldest transaction as the last integration date'''
last_transaction_date = frappe.db.get_value("Bank Account", bank_account, "last_integration_date")
if last_transaction_date:
start_date = formatdate(last_transaction_date, "YYYY-MM-dd")
else:
start_date = formatdate(add_months(today(), -12), "YYYY-MM-dd")
end_date = formatdate(today(), "YYYY-MM-dd")
@@ -125,13 +126,17 @@ def sync_transactions(bank, bank_account):
try:
transactions = get_transactions(bank=bank, bank_account=bank_account, start_date=start_date, end_date=end_date)
result = []
if transactions:
for transaction in transactions:
result.append(new_bank_transaction(transaction))
for transaction in reversed(transactions):
result += new_bank_transaction(transaction)
frappe.db.set_value("Bank Account", bank_account, "last_integration_date", getdate(end_date))
if result:
last_transaction_date = frappe.db.get_value('Bank Transaction', result.pop(), 'date')
frappe.logger().info("Plaid added {} new Bank Transactions from '{}' between {} and {}".format(
len(result), bank_account, start_date, end_date))
frappe.db.set_value("Bank Account", bank_account, "last_integration_date", last_transaction_date)
return result
except Exception:
frappe.log_error(frappe.get_traceback(), _("Plaid transactions sync error"))

View File

@@ -20,6 +20,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "status",
"fieldtype": "Select",
"hidden": 1,
@@ -54,6 +55,7 @@
"collapsible": 1,
"collapsible_depends_on": "eval:doc.client_id && doc.client_secret && doc.redirect_url",
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "application_settings",
"fieldtype": "Section Break",
"hidden": 0,
@@ -87,6 +89,7 @@
"collapsible": 0,
"columns": 0,
"default": "",
"fetch_if_empty": 0,
"fieldname": "client_id",
"fieldtype": "Data",
"hidden": 0,
@@ -120,6 +123,7 @@
"collapsible": 0,
"columns": 0,
"default": "",
"fetch_if_empty": 0,
"fieldname": "redirect_url",
"fieldtype": "Data",
"hidden": 0,
@@ -153,6 +157,7 @@
"collapsible": 0,
"columns": 0,
"default": "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer",
"fetch_if_empty": 0,
"fieldname": "token_endpoint",
"fieldtype": "Data",
"hidden": 0,
@@ -185,6 +190,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "application_column_break",
"fieldtype": "Column Break",
"hidden": 0,
@@ -217,6 +223,7 @@
"collapsible": 0,
"columns": 0,
"default": "",
"fetch_if_empty": 0,
"fieldname": "client_secret",
"fieldtype": "Data",
"hidden": 0,
@@ -250,6 +257,7 @@
"collapsible": 0,
"columns": 0,
"default": "com.intuit.quickbooks.accounting",
"fetch_if_empty": 0,
"fieldname": "scope",
"fieldtype": "Data",
"hidden": 0,
@@ -284,6 +292,7 @@
"collapsible": 0,
"columns": 0,
"default": "https://quickbooks.api.intuit.com/v3",
"fetch_if_empty": 0,
"fieldname": "api_endpoint",
"fieldtype": "Data",
"hidden": 0,
@@ -316,6 +325,7 @@
"bold": 0,
"collapsible": 1,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "authorization_settings",
"fieldtype": "Section Break",
"hidden": 0,
@@ -349,6 +359,7 @@
"collapsible": 0,
"columns": 0,
"default": "https://appcenter.intuit.com/connect/oauth2",
"fetch_if_empty": 0,
"fieldname": "authorization_endpoint",
"fieldtype": "Data",
"hidden": 0,
@@ -381,6 +392,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "refresh_token",
"fieldtype": "Small Text",
"hidden": 1,
@@ -413,6 +425,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "code",
"fieldtype": "Data",
"hidden": 1,
@@ -445,6 +458,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "authorization_column_break",
"fieldtype": "Column Break",
"hidden": 0,
@@ -476,6 +490,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "authorization_url",
"fieldtype": "Data",
"hidden": 0,
@@ -508,6 +523,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "access_token",
"fieldtype": "Small Text",
"hidden": 1,
@@ -540,6 +556,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "quickbooks_company_id",
"fieldtype": "Data",
"hidden": 1,
@@ -572,6 +589,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "company_settings",
"fieldtype": "Section Break",
"hidden": 1,
@@ -604,6 +622,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "company",
"fieldtype": "Link",
"hidden": 0,
@@ -637,6 +656,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "default_shipping_account",
"fieldtype": "Link",
"hidden": 1,
@@ -670,6 +690,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "default_warehouse",
"fieldtype": "Link",
"hidden": 1,
@@ -703,6 +724,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "company_column_break",
"fieldtype": "Column Break",
"hidden": 0,
@@ -734,6 +756,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "default_cost_center",
"fieldtype": "Link",
"hidden": 1,
@@ -767,6 +790,7 @@
"bold": 0,
"collapsible": 0,
"columns": 0,
"fetch_if_empty": 0,
"fieldname": "undeposited_funds_account",
"fieldtype": "Link",
"hidden": 1,
@@ -804,7 +828,7 @@
"issingle": 1,
"istable": 0,
"max_attachments": 0,
"modified": "2018-10-17 03:12:53.506229",
"modified": "2019-08-07 05:53:00.920316",
"modified_by": "Administrator",
"module": "ERPNext Integrations",
"name": "QuickBooks Migrator",
@@ -834,7 +858,7 @@
"quick_entry": 1,
"read_only": 0,
"read_only_onload": 0,
"show_name_in_global_search": 1,
"show_name_in_global_search": 0,
"sort_field": "modified",
"sort_order": "DESC",
"track_changes": 0,

View File

@@ -8,6 +8,7 @@ import json
from frappe import _
from frappe.model.document import Document
from frappe.utils import get_request_session
from requests.exceptions import HTTPError
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
from erpnext.erpnext_integrations.utils import get_webhook_address
from erpnext.erpnext_integrations.doctype.shopify_log.shopify_log import make_shopify_log
@@ -39,24 +40,28 @@ class ShopifySettings(Document):
def register_webhooks(self):
webhooks = ["orders/create", "orders/paid", "orders/fulfilled"]
url = get_shopify_url('admin/webhooks.json', self)
url = get_shopify_url('admin/api/2020-04/webhooks.json', self)
created_webhooks = [d.method for d in self.webhooks]
for method in webhooks:
if method in created_webhooks:
continue
session = get_request_session()
try:
d = session.post(url, data=json.dumps({
res = session.post(url, data=json.dumps({
"webhook": {
"topic": method,
"address": get_webhook_address(connector_name='shopify_connection', method='store_request_data'),
"format": "json"
}
}), headers=get_header(self))
d.raise_for_status()
self.update_webhook_table(method, d.json())
res.raise_for_status()
self.update_webhook_table(method, res.json())
except HTTPError as e:
error_message = res.json().get('errors', e)
make_shopify_log(status="Warning", exception=error_message, rollback=True)
except Exception as e:
make_shopify_log(status="Warning", message=e.message, exception=False)
@@ -65,13 +70,18 @@ class ShopifySettings(Document):
deleted_webhooks = []
for d in self.webhooks:
url = get_shopify_url('admin/webhooks/{0}.json'.format(d.webhook_id), self)
url = get_shopify_url('admin/api/2020-04/webhooks/{0}.json'.format(d.webhook_id), self)
try:
res = session.delete(url, headers=get_header(self))
res.raise_for_status()
deleted_webhooks.append(d)
except HTTPError as e:
error_message = res.json().get('errors', e)
make_shopify_log(status="Warning", exception=error_message, rollback=True)
except Exception as e:
frappe.log_error(message=frappe.get_traceback(), title=e.message[:140])
frappe.log_error(message=e, title='Shopify Webhooks Issue')
for d in deleted_webhooks:
self.remove(d)
@@ -144,4 +154,3 @@ def setup_custom_fields():
}
create_custom_fields(custom_fields)

View File

@@ -7,7 +7,7 @@ from erpnext.erpnext_integrations.doctype.shopify_settings.shopify_settings impo
shopify_variants_attr_list = ["option1", "option2", "option3"]
def sync_item_from_shopify(shopify_settings, item):
url = get_shopify_url("/admin/products/{0}.json".format(item.get("product_id")), shopify_settings)
url = get_shopify_url("admin/api/2020-04/products/{0}.json".format(item.get("product_id")), shopify_settings)
session = get_request_session()
try:

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,7 @@ from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.model.document import Document
from frappe.utils import cint, cstr, getdate
from frappe.utils import cint, cstr, getdate, flt
import dateutil
from frappe.model.naming import set_name_by_naming_series
from erpnext.healthcare.doctype.healthcare_settings.healthcare_settings import get_receivable_account,get_income_account,send_registration_sms
@@ -64,7 +64,7 @@ class Patient(Document):
def invoice_patient_registration(self):
frappe.db.set_value("Patient", self.name, "disabled", 0)
send_registration_sms(self)
if(frappe.get_value("Healthcare Settings", None, "registration_fee")>0):
if(flt(frappe.get_value("Healthcare Settings", None, "registration_fee"))>0):
company = frappe.defaults.get_user_default('company')
if not company:
company = frappe.db.get_value("Global Defaults", None, "default_company")

View File

@@ -386,5 +386,5 @@ def get_procedure_prescribed(patient):
return frappe.db.sql("""select pp.name, pp.procedure, pp.parent, ct.practitioner,
ct.encounter_date, pp.practitioner, pp.date, pp.department
from `tabPatient Encounter` ct, `tabProcedure Prescription` pp
where ct.patient='{0}' and pp.parent=ct.name and pp.appointment_booked=0
order by ct.creation desc""".format(patient))
where ct.patient=%(patient)s and pp.parent=ct.name and pp.appointment_booked=0
order by ct.creation desc""", {"patient": patient})

View File

@@ -233,7 +233,6 @@ scheduler_events = {
],
"daily": [
"erpnext.stock.reorder_item.reorder_item",
"erpnext.setup.doctype.email_digest.email_digest.send",
"erpnext.support.doctype.issue.issue.auto_close_tickets",
"erpnext.crm.doctype.opportunity.opportunity.auto_close_opportunity",
"erpnext.controllers.accounts_controller.update_invoice_status",
@@ -252,6 +251,7 @@ scheduler_events = {
"erpnext.projects.doctype.project.project.send_project_status_email_to_users"
],
"daily_long": [
"erpnext.setup.doctype.email_digest.email_digest.send",
"erpnext.manufacturing.doctype.bom_update_tool.bom_update_tool.update_latest_price_in_all_boms"
],
"monthly_long": [

View File

@@ -48,12 +48,17 @@ def get_abbreviated_name(name, company):
@frappe.whitelist()
def get_children(doctype, parent=None, company=None, is_root=False):
condition = ''
var_dict = {
"name": get_root_of("Department"),
"parent": parent,
"company": company,
}
if company == parent:
condition = "name='{0}'".format(get_root_of("Department"))
condition = "name=%(name)s"
elif company:
condition = "parent_department='{0}' and company='{1}'".format(parent, company)
condition = "parent_department=%(parent)s and company=%(company)s"
else:
condition = "parent_department = '{0}'".format(parent)
condition = "parent_department = %(parent)s"
return frappe.db.sql("""
select
@@ -62,7 +67,7 @@ def get_children(doctype, parent=None, company=None, is_root=False):
from `tab{doctype}`
where
{condition}
order by name""".format(doctype=doctype, condition=condition), as_dict=1)
order by name""".format(doctype=doctype, condition=condition), var_dict, as_dict=1)
@frappe.whitelist()
def add_node():

View File

@@ -157,10 +157,11 @@ class Employee(NestedSet):
def validate_status(self):
if self.status == 'Left':
reports_to = frappe.db.get_all('Employee',
filters={'reports_to': self.name}
filters={'reports_to': self.name, 'status': "Active"},
fields = ['name','employee_name']
)
if reports_to:
link_to_employees = [frappe.utils.get_link_to_form('Employee', employee.name) for employee in reports_to]
link_to_employees = [frappe.utils.get_link_to_form('Employee', employee.name, label=employee.employee_name) for employee in reports_to]
throw(_("Employee status cannot be set to 'Left' as following employees are currently reporting to this employee:&nbsp;")
+ ', '.join(link_to_employees), EmployeeLeftValidationError)
if not self.relieving_date:

View File

@@ -64,13 +64,20 @@ class EmployeeAdvance(Document):
def update_claimed_amount(self):
claimed_amount = frappe.db.sql("""
select sum(ifnull(allocated_amount, 0))
from `tabExpense Claim Advance`
where employee_advance = %s and docstatus=1 and allocated_amount > 0
SELECT sum(ifnull(allocated_amount, 0))
FROM `tabExpense Claim Advance` eca, `tabExpense Claim` ec
WHERE
eca.employee_advance = %s
AND ec.approval_status="Approved"
AND ec.name = eca.parent
AND ec.docstatus=1
AND eca.allocated_amount > 0
""", self.name)[0][0] or 0
if claimed_amount:
frappe.db.set_value("Employee Advance", self.name, "claimed_amount", flt(claimed_amount))
frappe.db.set_value("Employee Advance", self.name, "claimed_amount", flt(claimed_amount))
self.reload()
self.set_status()
frappe.db.set_value("Employee Advance", self.name, "status", self.status)
@frappe.whitelist()
def get_due_advance_amount(employee, posting_date):

View File

@@ -7,6 +7,14 @@ frappe.ui.form.on('Employee Onboarding', {
frm.add_fetch("employee_onboarding_template", "department", "department");
frm.add_fetch("employee_onboarding_template", "designation", "designation");
frm.add_fetch("employee_onboarding_template", "employee_grade", "employee_grade");
frm.set_query('job_offer', function () {
return {
filters: {
'job_applicant': frm.doc.job_applicant
}
};
});
},
refresh: function(frm) {

View File

@@ -178,7 +178,7 @@ frappe.ui.form.on("Expense Claim", {
refresh: function(frm) {
frm.trigger("toggle_fields");
if(frm.doc.docstatus == 1) {
if(frm.doc.docstatus === 1 && frm.doc.approval_status !== "Rejected") {
frm.add_custom_button(__('Accounting Ledger'), function() {
frappe.route_options = {
voucher_no: frm.doc.name,
@@ -188,8 +188,7 @@ frappe.ui.form.on("Expense Claim", {
frappe.set_route("query-report", "General Ledger");
}, __("View"));
}
if (frm.doc.docstatus===1
if (frm.doc.docstatus===1 && !cint(frm.doc.is_paid)
&& (cint(frm.doc.total_amount_reimbursed) < cint(frm.doc.total_sanctioned_amount))
&& frappe.model.can_create("Payment Entry")) {
frm.add_custom_button(__('Payment'),

View File

@@ -13,6 +13,7 @@ class Loan(AccountsController):
def validate(self):
validate_repayment_method(self.repayment_method, self.loan_amount, self.monthly_repayment_amount, self.repayment_periods)
self.set_missing_fields()
self.validate_loan_application()
self.make_repayment_schedule()
self.set_repayment_period()
self.calculate_totals()
@@ -33,6 +34,13 @@ class Loan(AccountsController):
if self.status == "Repaid/Closed":
self.total_amount_paid = self.total_payment
def validate_loan_application(self):
if self.loan_application:
loan = frappe.db.get_value("Loan", {"loan_application": self.loan_application}, "name")
if loan and loan != self.name:
frappe.throw(_("Loan {0} already created for Loan Application {1}").format(frappe.bold(loan),
frappe.bold(self.loan_application)))
def make_jv_entry(self):
self.check_permission('write')
@@ -116,6 +124,7 @@ def update_disbursement_status(doc):
""", (doc.payment_account, doc.name), as_dict=1)[0]
disbursement_date = None
status = ''
if not disbursement or disbursement.disbursed_amount == 0:
status = "Sanctioned"
elif disbursement.disbursed_amount == doc.loan_amount:

View File

@@ -23,20 +23,25 @@ frappe.ui.form.on('Loan Application', {
},
add_toolbar_buttons: function(frm) {
if (frm.doc.status == "Approved") {
frm.add_custom_button(__('Create Loan'), function() {
frappe.call({
method: "erpnext.hr.doctype.loan_application.loan_application.make_loan",
args: {
"source_name": frm.doc.name
},
callback: function(r) {
if(!r.exc) {
var doc = frappe.model.sync(r.message);
frappe.set_route("Form", r.message.doctype, r.message.name);
}
}
});
}).addClass("btn-primary");
// show create loan button if loan not created against loan aplication
frappe.db.get_value("Loan", {"loan_application": frm.doc.name}, "name", (r) => {
if (!r) {
frm.add_custom_button(__('Create Loan'), function() {
frappe.call({
method: "erpnext.hr.doctype.loan_application.loan_application.make_loan",
args: {
"source_name": frm.doc.name
},
callback: function(r) {
if(!r.exc) {
var doc = frappe.model.sync(r.message);
frappe.set_route("Form", r.message.doctype, r.message.name);
}
}
});
}).addClass("btn-primary");
}
});
}
}
});

View File

@@ -30,11 +30,11 @@ class LoanApplication(Document):
monthly_interest_rate = flt(self.rate_of_interest) / (12 *100)
if monthly_interest_rate:
min_repayment_amount = self.loan_amount*monthly_interest_rate
if self.repayment_amount - min_repayment_amount < 0:
if (self.repayment_amount - min_repayment_amount) <= 0:
frappe.throw(_("Repayment Amount must be greater than " \
+ str(flt(min_repayment_amount, 2))))
self.repayment_periods = math.ceil(math.log(self.repayment_amount) -
math.log(self.repayment_amount - min_repayment_amount) /(math.log(1 + monthly_interest_rate)))
self.repayment_periods = math.ceil((math.log(self.repayment_amount) -
math.log(self.repayment_amount - min_repayment_amount)) /(math.log(1 + monthly_interest_rate)))
else:
self.repayment_periods = self.loan_amount / self.repayment_amount
@@ -58,10 +58,13 @@ def make_loan(source_name, target_doc = None):
doclist = get_mapped_doc("Loan Application", source_name, {
"Loan Application": {
"doctype": "Loan",
"field_map": {
"repayment_amount": "monthly_repayment_amount"
},
"validation": {
"docstatus": ["=", 1]
}
}
}, target_doc)
return doclist
return doclist

View File

@@ -69,7 +69,7 @@ frappe.ui.form.on('Payroll Entry', {
},
add_context_buttons: function(frm) {
if(frm.doc.salary_slips_submitted) {
if(frm.doc.salary_slips_submitted || (frm.doc.__onload && frm.doc.__onload.submitted_ss)) {
frm.events.add_bank_entry_button(frm);
} else if(frm.doc.salary_slips_created) {
frm.add_custom_button(__("Submit Salary Slip"), function() {

View File

@@ -13,14 +13,13 @@ from erpnext.hr.doctype.employee.employee import get_holiday_list_for_employee
class PayrollEntry(Document):
def onload(self):
if not self.docstatus==1:
if not self.docstatus==1 or self.salary_slips_submitted:
return
# check if salary slips were manually submitted
entries = frappe.db.count("Salary Slip", {'payroll_entry': self.name, 'docstatus': 1}, ['name'])
if cint(entries) == len(self.employees) and not self.salary_slips_submitted:
self.db_set("salary_slips_submitted", 1)
self.reload()
if cint(entries) == len(self.employees):
self.set_onload("submitted_ss", True)
def on_submit(self):
self.create_salary_slips()
@@ -423,7 +422,6 @@ def get_start_end_dates(payroll_frequency, start_date=None, company=None):
'start_date': start_date, 'end_date': end_date
})
def get_frequency_kwargs(frequency_name):
frequency_dict = {
'monthly': {'months': 1},

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