Compare commits

...

470 Commits

Author SHA1 Message Date
frappe-pr-bot
af918cb401 chore: update POT file 2024-09-08 09:35:06 +00:00
rohitwaghchaure
5ff87edc85 fix: incorrect qty after transaction in SLE (#43103) 2024-09-07 16:38:04 +05:30
David Arnold
a1af6af121 Merge pull request #43100 from frappe/fix/lp-duing-refdoc-save-oversight
fix: oversight
2024-09-06 22:35:58 +02:00
David
8e2f1e3033 fix: oversight 2024-09-06 22:17:33 +02:00
Khushi Rawat
32749954de Merge pull request #43098 from khushi8112/reorder-expected-value-validation
fix(minor): reorder expected value validation
2024-09-07 00:13:20 +05:30
Khushi Rawat
0a6bf1559b fix(minor): reorder expected value validation 2024-09-06 23:46:29 +05:30
ruthra kumar
f9136c5d87 Merge pull request #43077 from aerele/common_party_jv
fix: cancel common party advance jv while canceling the invoice
2024-09-06 20:18:55 +05:30
David Arnold
c6e2470c92 Merge pull request #42135 from blaggacao/feat/lpe-discretionary
feat(lpe): add discretionary reason
2024-09-06 15:47:52 +02:00
David
b276b1d823 fix: format collection table properly 2024-09-06 15:30:08 +02:00
David (aider)
c638e57be4 feat: add test cases for loyalty point entry 2024-09-06 15:29:44 +02:00
David
586422424a feat(lpe): add distretionary reason 2024-09-06 13:13:25 +02:00
Smit Vora
816aae968e Merge pull request #43013 from ljain112/fix-process-payment-recp
fix: `default_advance_account` field in Process Payment Reconciliation
2024-09-06 15:16:29 +05:30
Rushabh Mehta
87662403d9 Merge pull request #43090 from rmehta/logo-2024
fix(logo): updated logo
2024-09-06 14:35:50 +05:30
Rushabh Mehta
8a71b54572 fix(logo): updated logo 2024-09-06 14:14:15 +05:30
ruthra kumar
75cf3e245f Merge pull request #42588 from ruthra-kumar/round_off_account_for_opening
refactor: separate round off account for opening
2024-09-06 10:10:23 +05:30
David Arnold
6652806e7a Merge pull request #43084 from blaggacao/payments/pr-6
fix: unhide payment url in pr
2024-09-06 00:52:53 +02:00
David
19c6bf2f92 fix: unhide payment url in pr 2024-09-06 00:35:17 +02:00
David Arnold
aa54516444 Merge pull request #41718 from blaggacao/fix/lp-on-si
fix: loyalty points application on si
2024-09-06 00:10:31 +02:00
David
74fb070959 test: loyalty points redemption from shopping cart 2024-09-05 23:53:30 +02:00
David Arnold
7ca9ec14a4 Merge pull request #42071 from blaggacao/fix/coupon-code-limits
fix: coupon code limits and add external plaftorm code
2024-09-05 23:42:30 +02:00
David (aider)
3413bad51d test: add test case for coupon without maximum use 2024-09-05 23:25:08 +02:00
David
70ed75a397 fix: coupon code limits and add external plaftorm code 2024-09-05 22:52:49 +02:00
venkat102
8c6e3f3c12 test: add unit test for canceling the common party advance jv created from sales invoice 2024-09-05 23:30:35 +05:30
David Arnold
873571272d Merge pull request #41719 from blaggacao/fix/lp-during-refdoc-save
fix: lp during ref_doc save event
2024-09-05 16:50:45 +02:00
David
48b5567af4 fix: loyalty points application on si 2024-09-05 16:39:52 +02:00
David
340e35f209 fix: lp during ref_doc save event 2024-09-05 16:32:56 +02:00
David Arnold
2ae1704d11 Merge pull request #41871 from blaggacao/perf/reduce-critical-path
perf: hot path in page load
2024-09-05 15:50:23 +02:00
venkat102
6a928b92df fix: cancel common party advance jv while canceling the invoice 2024-09-05 19:13:31 +05:30
David Arnold
e93ff9b267 Merge pull request #41564 from blaggacao/fix/loyalty
fix: tiered loyalty program
2024-09-05 15:02:04 +02:00
David (aider)
efd8f1e978 feat: Add validation to ensure lowest tier has min_spent of 0 in LoyaltyProgram 2024-09-05 14:30:40 +02:00
David
4bd26b845e fix: test ensures min spent respected 2024-09-05 14:29:10 +02:00
David
ad02e4112f fix: loyalty test case -> validate against actual database state 2024-09-05 14:08:29 +02:00
David
c914ea4adc test: add tests for loyalty tier selection 2024-09-05 14:08:22 +02:00
David
81dc5872c4 fix: tiered loyalty program 2024-09-05 14:08:22 +02:00
David
618e737b0b fix: add lp collection min to list 2024-09-05 14:08:21 +02:00
David Arnold
b38b8ded7b Merge pull request #43017 from blaggacao/fix/status-updater-notifications
fix: updater tigger notifications on status updates & perf
2024-09-05 13:54:35 +02:00
ruthra kumar
613830bfba Merge pull request #43070 from ruthra-kumar/handle_emp_advance_as_separate_unit
fix: AP filter to simulate employee advance as a ledger impacting voucher
2024-09-05 17:12:30 +05:30
David
d72d858efa fix: Update status field in parent transaction 2024-09-05 13:00:18 +02:00
David
00b70d7654 chore: calculate in python code 2024-09-05 13:00:17 +02:00
David
5b3e0c5543 chore: refactor set status into a getter & setter (future perf) 2024-09-05 12:58:51 +02:00
ruthra kumar
257e13c299 refactor: filter to toggle employee advance scenario in AP 2024-09-05 16:12:52 +05:30
ruthra kumar
eedf22b07a refactor: Handle Emp Advance as separate row in AP report 2024-09-05 16:01:26 +05:30
rohitwaghchaure
c13a147df1 fix: disabled batches showing in the list (#43024) 2024-09-05 15:39:15 +05:30
rohitwaghchaure
a8055a6da9 fix: auto reorder material request mail issue (#43066)
fix: auto reorder matreial request mail issue
2024-09-05 15:39:03 +05:30
David Arnold
5f25cde788 Merge pull request #41721 from blaggacao/fix/distributed-discounts
fix: distributed discounts on si
2024-09-05 12:07:18 +02:00
David (aider)
a464bd861b feat: add unit tests for distributed_discount_amount 2024-09-05 11:50:24 +02:00
David
0bab6f34c1 fix: distributed discounts on si 2024-09-05 11:49:24 +02:00
David Arnold
146ce59b87 Merge pull request #43036 from blaggacao/chore/ignore-aider-files
chore: ignore aider files
2024-09-05 11:43:32 +02:00
David
6fdb8659cc chore: ignore aider files 2024-09-05 11:26:26 +02:00
ruthra kumar
ef0682a216 Merge pull request #43065 from Ninad1306/merge_taxes_fix
fix: Reset Value Conditionally Based on Merge Taxes
2024-09-05 14:32:41 +05:30
Ninad1306
9d899f3c4a fix: reset value conditionally based on merge taxes 2024-09-05 13:29:22 +05:30
Smit Vora
5442487b6c Merge pull request #42925 from Ninad1306/mapping_docs_fix
fix: Replace `add_if_empty` with `reset_value` flag
2024-09-04 13:13:57 +05:30
Lakshit Jain
38f925b376 fix: adjust price insertion logic for internal suppliers/customers (#42988)
* fix: adjust price insertion logic for internal suppliers/customers

* refactor: correct indentation, specify conditions within function

* fix: typo

---------

Co-authored-by: Smit Vora <smitvora203@gmail.com>
2024-09-04 12:56:04 +05:30
Khushi Rawat
4da289ec61 Merge pull request #42926 from khushi8112/asset-value-through-landed-cost-voucher
fix: link Purchase Invoice and Receipt Items to Asset
2024-09-04 00:49:46 +05:30
ruthra kumar
c4ed04cb31 refactor: fetch advance account on party seleection 2024-09-03 17:27:50 +05:30
ruthra kumar
b7525ca4e5 Merge pull request #43030 from ruthra-kumar/default_currency_typeerror_on_payment_entry
fix: typerror on default_currency
2024-09-03 16:40:08 +05:30
Nihantra C. Patel
890016695f Merge pull request #43029 from Nihantra-Patel/fix_company_filter
fix: move setup_queries to refresh event for company
2024-09-03 16:05:09 +05:30
Nihantra C. Patel
80ace72541 fix: company accounts setup_queries 2024-09-03 15:54:42 +05:30
ruthra kumar
4a7cc4da87 fix: typerror on default_currency 2024-09-03 15:54:39 +05:30
ruthra kumar
fb6b960806 Merge pull request #42974 from Vishnu7025/report_fix
fix: retain date filter when redirecting in Profit and Loss report
2024-09-03 15:43:01 +05:30
ruthra kumar
cf11ac87fb refactor(test): filter for active ledger entries 2024-09-03 15:05:15 +05:30
ruthra kumar
b7edc6dea9 test: opening purchase invoice with rounding adjustment 2024-09-03 15:05:15 +05:30
ruthra kumar
a5d6a25a96 refactor: handle opening round off on purchase invoice 2024-09-03 15:05:15 +05:30
ruthra kumar
193ea9ad8f refactor: filter on account_type 2024-09-03 15:05:14 +05:30
ruthra kumar
79267358d0 test: opening round off with inclusive tax 2024-09-03 15:05:14 +05:30
ruthra kumar
5021c7ca2c test: rounding adjustment validation and posting 2024-09-03 15:05:14 +05:30
ruthra kumar
96e3c2ad10 refactor: handle opening round off from sales invoice 2024-09-03 15:05:14 +05:30
ruthra kumar
fc46ebcd7c chore: default should return 3 elements 2024-09-03 15:05:14 +05:30
ruthra kumar
88e68168e3 refactor: use separate round off for opening entries 2024-09-03 15:05:14 +05:30
ruthra kumar
a5b228549c feat: round off for opening entries 2024-09-03 15:05:14 +05:30
Khushi Rawat
2fdc56cebf Merge branch 'develop' of https://github.com/frappe/erpnext into asset-value-through-landed-cost-voucher 2024-09-03 10:15:29 +05:30
Khushi Rawat
07339e2ddf Merge pull request #43015 from khushi8112/test-case-fix-in-asset-capitalization
chore: test case failing issue
2024-09-03 03:38:29 +05:30
Khushi Rawat
418f7e97b9 chore: patch correction 2024-09-03 03:35:19 +05:30
Raffael Meyer
baff301e8a Merge pull request #43016 from frappe/l10n_develop 2024-09-02 20:09:43 +02:00
Frappe PR Bot
861edd3041 fix: Esperanto translations 2024-09-02 23:00:49 +05:30
Frappe PR Bot
77ac87494a fix: Swedish translations 2024-09-02 23:00:46 +05:30
Frappe PR Bot
0a5b21d002 fix: Spanish translations 2024-09-02 23:00:42 +05:30
Frappe PR Bot
c50aba8333 fix: Arabic translations 2024-09-02 23:00:39 +05:30
Frappe PR Bot
c595bf684c fix: Bosnian translations 2024-09-02 23:00:35 +05:30
Frappe PR Bot
f51e9df921 fix: Turkish translations 2024-09-02 23:00:32 +05:30
Frappe PR Bot
6ebd52bdfe fix: French translations 2024-09-02 23:00:29 +05:30
Frappe PR Bot
aa57a98872 fix: Persian translations 2024-09-02 23:00:25 +05:30
Frappe PR Bot
f9d1bdd9f9 fix: German translations 2024-09-02 23:00:21 +05:30
Khushi Rawat
0bdffdfa98 chore: test case failing issue 2024-09-02 16:50:28 +05:30
Khushi Rawat
f369b583b9 chore: resolved conflicts 2024-09-02 16:09:09 +05:30
rohitwaghchaure
f3b91d4d62 fix: validate component quantity according to BOM (#43011) 2024-09-02 16:05:43 +05:30
Khushi Rawat
a84a5b43e2 refactor: rename to in SLE query functions 2024-09-02 11:48:41 +05:30
Nihantra C. Patel
78be5fc276 Merge pull request #43008 from Nihantra-Patel/upd_develop_version
fix: update develop_version in hooks
2024-09-02 11:42:38 +05:30
Nihantra C. Patel
01b345e046 fix: update develop_version in hooks 2024-09-02 10:40:31 +05:30
Raffael Meyer
e21fcbae6a Merge pull request #42953 from frappe/l10n_develop 2024-09-01 22:48:59 +02:00
Raffael Meyer
a5314165c1 Merge pull request #43005 from frappe/pot_develop_2024-09-01 2024-09-01 22:47:44 +02:00
frappe-pr-bot
d45e6781c1 chore: update POT file 2024-09-01 09:35:05 +00:00
rahulgupta8848
d4fdada83c feat: account heads changed along with journal entry type and descrip… (#42845)
* feat: account heads changed along with journal entry type and description

* feat: added patch for difference_amount for asset value adjustment and refactor

---------

Co-authored-by: “rahulgupta8848” <“rahul.gupta@8848digital.com”>
2024-09-01 10:39:48 +05:30
Raffael Meyer
9b4bb30af2 Merge pull request #43003 from barredterra/toggle_naming 2024-08-31 19:21:16 +02:00
barredterra
ab2d7f7279 fix: show naming series only when needed 2024-08-31 19:14:45 +02:00
Shariq Ansari
9d4ad987e1 Merge pull request #42993 from shariquerik/apps-page-fix
fix: added app permission check for apps page
2024-08-30 17:34:30 +05:30
Shariq Ansari
1d9ed27a89 chore: linter fix 2024-08-30 16:57:17 +05:30
Shariq Ansari
e8f8fb8a8f fix: added app permission check for apps page 2024-08-30 16:51:26 +05:30
David Arnold
b01ac4d6fd Merge pull request #42975 from blaggacao/chore/fix-editorconfig-json
chore: fix editorconfig for json
2024-08-30 08:54:13 +02:00
Khushi Rawat
272f42eaf2 chore: linters/semgrep check 2024-08-30 00:40:01 +05:30
Khushi Rawat
3baffffd1b chore: resolved linter warnings with #nosemgrep 2024-08-30 00:30:47 +05:30
Khushi Rawat
83fe78bcda fix: improve asset item matching logic 2024-08-29 17:10:12 +05:30
ruthra kumar
4500f22671 Merge pull request #42983 from ruthra-kumar/link_utility_rpt_to_bank_reconciliation_statement
refactor: link utility report with bank reconciliation statement
2024-08-29 16:11:09 +05:30
ruthra kumar
00eac65712 refactor: link utility report with bank reconciliation statement 2024-08-29 16:01:54 +05:30
Khushi Rawat
5d99f17583 fix(capitalization): debit cwip account instead of fixed asset account (#42857)
* fix(capitalization): debit cwip account instead of fixed asset account

* fix: post entries for capitalized asset through background jobs

* chore: run pre-commit

* fix: correct GL entries posting for composite assets

* fix(minor): resolve failing check

* chore: update gl entry check logic

* chore: handle none values
2024-08-29 15:50:02 +05:30
ruthra kumar
ac01686ded Merge pull request #42664 from Nihantra-Patel/fix_payment_req_del
fix: add the company in payment request bcz delete company transactions
2024-08-29 15:15:59 +05:30
Nihantra C. Patel
e3008843d1 fix: link company when make payment request 2024-08-29 14:42:45 +05:30
ruthra kumar
735558d073 Merge pull request #42979 from ruthra-kumar/bank_clearance_with_better_transaction_model
refactor: better UX on Bank Clearance tool
2024-08-29 13:53:48 +05:30
ruthra kumar
6a06e26d04 refactor: better UX on Bank Clearance tool 2024-08-29 12:11:48 +05:30
Khushi Rawat
4e29654458 Merge pull request #42976 from khushi8112/capitalization-for-new-composite-asset
fix: don't allow capitalizing only service item for new composite asset
2024-08-29 02:12:13 +05:30
Khushi Rawat
f1d2138258 fix: don't allow capitalizing only service item for new composite asset 2024-08-29 01:51:45 +05:30
David
a07100d5b3 chore: fix editorconfig for json 2024-08-28 21:00:09 +02:00
Frappe PR Bot
d51f9ce815 fix: Swedish translations 2024-08-28 21:10:02 +05:30
Frappe PR Bot
c73ea6bad9 fix: Turkish translations 2024-08-28 21:09:58 +05:30
Frappe PR Bot
7c5b88a4b9 fix: German translations 2024-08-28 21:09:53 +05:30
vishnu
598e9c1390 fix: indentation 2024-08-28 15:12:22 +00:00
vishnu
bb29fc4c3d fix: retain date filter when redirecting in Profit and Loss report 2024-08-28 15:10:08 +00:00
ruthra kumar
df3267ad3c Merge pull request #42966 from ruthra-kumar/typeerror_on_PE
fix: typeerror on Payment Entry
2024-08-28 12:26:25 +05:30
ruthra kumar
e9cf8937cd fix: typeerror on Payment Entry 2024-08-28 12:21:22 +05:30
rohitwaghchaure
663a08e4cd feat: Disassembly Order (#42655) 2024-08-27 22:25:20 +05:30
Raffael Meyer
758d1606a9 Merge pull request #42955 from barredterra/fix-item-error 2024-08-27 17:44:09 +02:00
barredterra
c34737f4c6 fix(Item): error message on tax rate 2024-08-27 17:27:13 +02:00
Frappe PR Bot
193d13be0d fix: Turkish translations 2024-08-27 20:43:48 +05:30
Frappe PR Bot
5d0c25f5b5 fix: German translations 2024-08-27 20:43:44 +05:30
Raffael Meyer
1064d7636d Merge pull request #42681 from barredterra/psa-translatability 2024-08-27 16:55:51 +02:00
ruthra kumar
33e37cfbff Merge pull request #42951 from frappe/mergify/copy/develop/pr-42453
fix: Update get_amount to return currency precision grand total (copy #42453)
2024-08-27 17:38:59 +05:30
David Arnold
d3d0af3815 Merge pull request #41925 from blaggacao/fix/customer-mobile-email
fix: spec mobile and email fields for notifications
2024-08-27 13:42:40 +02:00
ruthra kumar
6310497403 chore: resolve conflict 2024-08-27 17:11:10 +05:30
Syed Mujeer Hashmi
f960aac639 fix: Update get_amount to return currency precision grand total
In case of multi-currency purchase invoice, we are getting the error "Total Payment Request amount cannot be greater than Purchase Invoice amount" because of rounding difference.

(cherry picked from commit 976abf7b3c)

# Conflicts:
#	erpnext/accounts/doctype/payment_request/payment_request.py
2024-08-27 11:39:32 +00:00
ljain112
143209f91a fix: default_advance_account field in Process Payment Reconciliation 2024-08-27 16:53:34 +05:30
ruthra kumar
ab2b2dc5d4 Merge pull request #41949 from Nihantra-Patel/refactor_item_wise_purchase_history_v15
refactor: item-wise purchase history (query to script report)
2024-08-27 16:44:45 +05:30
ruthra kumar
f88d5dcf5c Merge pull request #42936 from ruthra-kumar/fix_incorrect_attribute_on_payment_entry
fix: remove unnecessary condition on 'voucher_no'
2024-08-27 16:35:02 +05:30
ruthra kumar
520db864b5 Merge pull request #42923 from aerele/bank_reconciliation_statement
fix: get amount with taxes and charges from payment entry
2024-08-27 16:30:28 +05:30
Khushi Rawat
62873309e5 Merge pull request #42939 from khushi8112/depreciation-and-balances-report
feat: added finance book filter in depreciation and balances report
2024-08-27 16:22:47 +05:30
rohitwaghchaure
86d3a9ab03 fix: Cannot read properties of null (reading 'doctype') (#42941) 2024-08-27 16:21:36 +05:30
ruthra kumar
d855a8ec1c Merge pull request #42921 from aerele/party_naming_sequance
fix: make party naming sequential when naming_by set as auto name
2024-08-27 15:36:30 +05:30
Khushi Rawat
adf1e487e1 chore: resolved linter warnings with #nosemgrep 2024-08-27 15:27:37 +05:30
Khushi Rawat
45804c68f0 feat: added finance book filter in depreciation and balances report 2024-08-27 15:02:01 +05:30
ruthra kumar
fb32d2cafb fix: use of incorrect attribute 2024-08-27 14:46:10 +05:30
rohitwaghchaure
ca2fde891e fix: timeout while submitting stock entry (#42929) 2024-08-27 10:00:06 +05:30
Raffael Meyer
a132ca1857 Merge pull request #42841 from frappe/l10n_develop 2024-08-26 21:12:25 +02:00
Khushi Rawat
a1aaffbebe fix: link Purchase Invoice and Receipt Items to Asset 2024-08-26 23:33:10 +05:30
Ninad1306
8dcb1ab63b fix: update child table from last source_doc 2024-08-26 22:32:16 +05:30
Frappe PR Bot
6b88777829 fix: Persian translations 2024-08-26 20:30:01 +05:30
venkat102
b3a901b631 fix: get amount with taxes and charges from payment entry 2024-08-26 20:04:51 +05:30
venkat102
c9015f7c04 fix: make party naming sequential when naming_by set as auto name 2024-08-26 19:30:30 +05:30
rohitwaghchaure
27364b7e6b fix: same posting date and time, creation causing incorrect balance qty (#42904)
fix: same posting date and time, creation causing incorrect balance quantity
2024-08-26 18:19:25 +05:30
rohitwaghchaure
10434742e9 fix: unsupported operand type(s) for *: 'float' and 'NoneType' (#42916) 2024-08-26 17:59:14 +05:30
ruthra kumar
c64deac4d3 Merge pull request #42887 from ljain112/fix-dimension
fix: update dimesions in exchange_gain_loss jv based on base document
2024-08-26 17:30:03 +05:30
rohitwaghchaure
92bde71ab1 fix: Column 'valuation_rate' cannot be null (#42909) 2024-08-26 16:48:55 +05:30
Rushabh Mehta
0aae621d0e Merge pull request #42524 from blaggacao/fix/financial-statement-linking
fix: link cash flow rows and fix summary linking
2024-08-26 15:54:32 +05:30
ruthra kumar
94629e0b1b Merge pull request #42910 from ruthra-kumar/tools_for_bank_statement_analysis
feat: utility report to better diagnose incorrectly cleared Cheques and Deposits
2024-08-26 15:40:14 +05:30
ruthra kumar
2144e0337d refactor: build dictionary for Journal
remove redundant filter
2024-08-26 14:57:38 +05:30
ruthra kumar
06e5a3fe23 Merge pull request #42905 from ruthra-kumar/better_err_msg_clearance_tool
refactor: better err msg on clearance tool
2024-08-26 14:23:40 +05:30
ruthra kumar
74b36db24e chore: remove redundant column 2024-08-26 14:16:32 +05:30
ruthra kumar
092411b54f refactor: better err msg on clearance tool 2024-08-26 14:05:19 +05:30
Frappe PR Bot
ef7d9004d5 fix: Swedish translations 2024-08-25 20:33:55 +05:30
Frappe PR Bot
8bdb8e71d4 fix: Spanish translations 2024-08-25 20:33:52 +05:30
Frappe PR Bot
66d7b18204 fix: Persian translations 2024-08-24 20:03:57 +05:30
ruthra kumar
784dec24c8 refactor: build dict for payment entry 2024-08-24 08:16:10 +05:30
ruthra kumar
4cd023444a refactor: working state with minimum functions 2024-08-24 08:16:10 +05:30
ruthra kumar
ceaa1be729 refactor: barebones functions 2024-08-24 08:16:10 +05:30
ruthra kumar
28890fa833 feat: report to identify incorrectly cleared cheques 2024-08-24 08:16:10 +05:30
rohitwaghchaure
6721ae76de fix: LCV based on purchase invoice amount with multi-currency (#42890) 2024-08-23 22:34:38 +05:30
Frappe PR Bot
0d57a7d4a4 fix: Turkish translations 2024-08-23 19:35:37 +05:30
Smit Vora
18bc1df6b7 Merge pull request #42848 from ljain112/fix-oppurtunity
fix: do not copy date fields in opportunity doctype
2024-08-23 14:17:19 +05:30
Raffael Meyer
13eb9ea421 Merge pull request #42877 from barredterra/sb-in-setup-wizard 2024-08-22 17:20:31 +02:00
barredterra
fd51dd8999 fix: remove ugly column break in setup wizard 2024-08-22 17:16:33 +02:00
Raffael Meyer
cc4afc53e3 Merge pull request #42803 from barredterra/pseudo-translate-in-setup-wizard 2024-08-22 17:03:18 +02:00
rohitwaghchaure
ce7f6ee71c fix: incorrect in and out qty in the Batch-Wise Balance History (#42866) 2024-08-22 19:33:30 +05:30
rohitwaghchaure
63ca1025bc fix: not able to make stock entry against MR (#42874) 2024-08-22 19:33:14 +05:30
Raffael Meyer
f6371f4a0d Merge branch 'develop' into pseudo-translate-in-setup-wizard 2024-08-22 16:01:17 +02:00
Frappe PR Bot
b64f70d95a fix: Swedish translations 2024-08-22 18:43:20 +05:30
Frappe PR Bot
e7350f18f6 fix: Turkish translations 2024-08-22 18:43:16 +05:30
ruthra kumar
2ca0f416a9 Merge pull request #42867 from ruthra-kumar/respect_db_transaction_model
fix: call 'process' directly instead of creating 'process_subscription'
2024-08-22 17:22:06 +05:30
ruthra kumar
1efdc04e46 Merge pull request #42851 from ruthra-kumar/allow_equity_type_for_shareholders
refactor: Allow equity type Account in Payment Entry for shareholders
2024-08-22 17:09:45 +05:30
ruthra kumar
b4d22c2936 fix: call 'process' directly instead of creating 'process_subscripti
reason: 'process' follows simple DB transaction model.
2024-08-22 16:58:39 +05:30
ljain112
96df19149d fix: update dimesions in exchange_gain_loss jv based on base document 2024-08-22 11:50:19 +05:30
Khushi Rawat
930286e105 Merge pull request #42861 from khushi8112/slm-depreciation
fix(minor): calculation correction for annual depreciation
2024-08-22 11:17:14 +05:30
Khushi Rawat
f440243b75 fix: calculation correction for annual depreciation 2024-08-22 02:22:51 +05:30
Frappe PR Bot
28df8aa9cf fix: Turkish translations 2024-08-21 18:36:37 +05:30
rohitwaghchaure
fb846ffa12 fix: incorrect Received Qty Amount in Purchase Order Analysis (#42852) 2024-08-21 15:22:19 +05:30
rohitwaghchaure
5b9309cf34 fix: last purchase rate not updated from purchase invoice (#42847) 2024-08-21 14:52:24 +05:30
ruthra kumar
63ad9f4f86 refactor: filter shareholder on company 2024-08-21 14:19:30 +05:30
ruthra kumar
6cbf98294a refactor: allow equity types on Payment Entry 2024-08-21 14:13:04 +05:30
rohitwaghchaure
9c82c2b5d3 fix: custom stock entry type issue (#42835) 2024-08-21 12:11:22 +05:30
Frappe PR Bot
ee9c3e12b4 fix: Turkish translations 2024-08-20 18:33:35 +05:30
Nihantra C. Patel
a1183f0165 refactor: brand js and allow quick entry (#42829)
* refactor: brand js and allow quick entry

* refactor: brand js and allow quick entry --prettier

* refactor: brand js and allow quick entry --prettier
2024-08-20 14:42:32 +05:30
ljain112
74afa57a9f fix: do not copy date fields in opportunity doctype 2024-08-20 13:45:57 +05:30
Khushi Rawat
85641fd21c Merge pull request #42824 from khushi8112/asset-creation-timeout-error
perf: asset creation from purchase receipt
2024-08-20 13:41:46 +05:30
ruthra kumar
c039cfd5c3 Merge pull request #42379 from Nihantra-Patel/fix_so_prospect_dev_v15
fix: Create Sales Order from Quotation for Prospect
2024-08-20 10:40:44 +05:30
Nabin Hait
f48b3c41de chore: Deleted all the form tours (#42773) 2024-08-20 10:37:50 +05:30
Nabin Hait
b7f5012f15 fix: Removed quick entry for Brand (#42822) 2024-08-20 10:37:19 +05:30
Nabin Hait
0b7657ecf1 chore: Remove module onboarding records (#42790) 2024-08-20 10:36:49 +05:30
ruthra kumar
5c5e2c3ac2 Merge pull request #42731 from creative-paramu/bank_reconcilation_filter
fix: bank reconcilation tool cost center company filter adding
2024-08-20 10:13:19 +05:30
Parameshwari Palanisamy
6d19aae423 refactor: update dialog_manager.js 2024-08-20 10:09:25 +05:30
Raffael Meyer
3ddb18de41 Merge pull request #42797 from frappe/l10n_develop 2024-08-19 15:26:43 +02:00
Frappe PR Bot
7ceb294aab fix: Swedish translations 2024-08-19 18:15:49 +05:30
Frappe PR Bot
fbfc5b4b1d fix: Spanish translations 2024-08-19 18:15:45 +05:30
Frappe PR Bot
0448ea3daa fix: Arabic translations 2024-08-19 18:15:40 +05:30
Frappe PR Bot
9cfbd8c8e3 fix: Turkish translations 2024-08-19 18:15:32 +05:30
Frappe PR Bot
2238ca55a5 fix: French translations 2024-08-19 18:15:28 +05:30
Khushi Rawat
6e84fc5143 perf: asset creation from purchase receipt 2024-08-19 17:24:31 +05:30
rohitwaghchaure
5404b21c7d fix: dropping index to improve performance (#42820)
fix: droping index to improve peformance
2024-08-19 16:58:25 +05:30
rohitwaghchaure
1511280464 perf: data import for stock entries (#42711) 2024-08-19 12:04:47 +05:30
Frappe PR Bot
949c0cab72 fix: Swedish translations 2024-08-18 18:07:18 +05:30
Frappe PR Bot
4d41e1538d fix: Esperanto translations 2024-08-18 18:07:15 +05:30
Frappe PR Bot
5e8700f0b4 fix: Spanish translations 2024-08-18 18:07:11 +05:30
Frappe PR Bot
73c4d8a8e6 fix: Arabic translations 2024-08-18 18:07:08 +05:30
Frappe PR Bot
5ae756a93b fix: Bosnian translations 2024-08-18 18:07:05 +05:30
Frappe PR Bot
c9d474bc87 fix: Turkish translations 2024-08-18 18:07:01 +05:30
Frappe PR Bot
226095c6b8 fix: French translations 2024-08-18 18:06:58 +05:30
Frappe PR Bot
200a43e41f fix: Persian translations 2024-08-18 18:06:55 +05:30
Frappe PR Bot
834d8c64c2 fix: German translations 2024-08-18 18:06:51 +05:30
Raffael Meyer
d32fdaaff5 Merge pull request #42808 from frappe/pot_develop_2024-08-18
chore: update POT file
2024-08-18 13:52:29 +02:00
frappe-pr-bot
63b772a7a3 chore: update POT file 2024-08-18 09:36:00 +00:00
Frappe PR Bot
2142f8b4a3 fix: Turkish translations 2024-08-17 17:44:21 +05:30
Frappe PR Bot
fa20320f39 fix: Persian translations 2024-08-17 17:44:18 +05:30
Frappe PR Bot
0e52ef4fd9 fix: German translations 2024-08-17 17:44:14 +05:30
Raffael Meyer
5f3963b3e6 Merge pull request #42800 from barredterra/translatability-fixes 2024-08-17 02:19:43 +02:00
Raffael Meyer
4db7054542 Merge pull request #42802 from barredterra/extract-setup-wizard-data 2024-08-17 02:14:23 +02:00
barredterra
97c2853497 feat: enable "Translate Link Fields" for fixture doctypes 2024-08-17 02:02:20 +02:00
barredterra
48513b1087 refactor: remove useless line 2024-08-17 02:01:35 +02:00
barredterra
505cd95150 fix: remove pseudo-translation for UOM Category variable
Has no effect and even if it had, it's not really needed.
2024-08-17 02:00:34 +02:00
barredterra
c08cd938bc fix: add pseudo-translation for Project Type 2024-08-17 01:58:48 +02:00
barredterra
11f6e570a5 fix: add pseudo-translation for Stock Entry Type 2024-08-17 01:58:27 +02:00
barredterra
77e5787036 fix: use pseudo-translation while creating fixtures 2024-08-17 01:57:44 +02:00
barredterra
60aaeefd70 feat: add extractor for setup wizard txt files 2024-08-17 01:50:55 +02:00
barredterra
af0ae930ca fix: translatability of boldened text 2024-08-16 21:38:56 +02:00
Frappe PR Bot
9d4165b9f9 fix: Turkish translations 2024-08-16 17:35:39 +05:30
ruthra kumar
1bdeddcb04 Merge pull request #42791 from ruthra-kumar/refactor_pos_return_patch
fix(patch): Use sql to update 'against_voucher' rather than reposting
2024-08-16 15:31:22 +05:30
rohitwaghchaure
48c3b0d094 fix: Auto Create Serial and Batch Bundle For Outward (#42778) 2024-08-16 15:10:22 +05:30
ruthra kumar
1721175a20 chore: update patches.txt 2024-08-16 14:32:53 +05:30
ruthra kumar
13bb48434f fix(patch): replace repost with direct sql to update 'against_voucher 2024-08-16 14:31:13 +05:30
rohitwaghchaure
32c4fab14f fix: not able to create the batch (#42784) 2024-08-16 11:27:08 +05:30
ruthra kumar
c1a5a0510e Merge pull request #42774 from ruthra-kumar/loosen_account_currency_validation_on_groups
refactor: Loosen account currency validation on groups
2024-08-16 09:58:52 +05:30
Raffael Meyer
5011b76de9 Merge pull request #42713 from frappe/l10n_develop 2024-08-15 17:00:52 +02:00
Frappe PR Bot
4c0c0d3eb3 fix: Turkish translations 2024-08-15 16:53:43 +05:30
ruthra kumar
164498bafb refactor: allow foreign currency accounts in Supplier Group 2024-08-14 17:31:19 +05:30
ruthra kumar
066e935892 refactor: allow foreign currency accounts in customer group 2024-08-14 17:30:46 +05:30
Nihantra C. Patel
3dfee0ae43 Merge pull request #42769 from Nihantra-Patel/fix_rename_stop_warehouse
fix: disable rename from warehouse
2024-08-14 16:32:50 +05:30
Frappe PR Bot
85c47b9a5a fix: Spanish translations 2024-08-14 16:28:41 +05:30
Frappe PR Bot
6f8eb4d9ed fix: Turkish translations 2024-08-14 16:28:37 +05:30
Frappe PR Bot
3231bedfb3 fix: Persian translations 2024-08-14 16:28:33 +05:30
Frappe PR Bot
e79b57975e fix: German translations 2024-08-14 16:28:29 +05:30
Nihantra C. Patel
c1812f74e8 fix: disable rename from warehouse 2024-08-14 16:11:50 +05:30
rohitwaghchaure
0db82ec93a fix: ignore pricing rule while making DN from Pick List (#42763) 2024-08-14 15:22:19 +05:30
Khushi Rawat
ad4991dc47 Merge pull request #42765 from khushi8112/set-filters-in-asset-capitalization-fields
fix: set up filters for dimensions
2024-08-14 14:35:52 +05:30
Khushi Rawat
0d42793397 chore: linters check 2024-08-14 14:31:56 +05:30
Khushi Rawat
6fa29376a0 fix: removed extra filter condition 2024-08-14 14:26:47 +05:30
Khushi Rawat
2fd8de2f76 fix: set up filters for dimensions 2024-08-14 14:21:48 +05:30
Smit Vora
9e8f25d020 Merge pull request #42458 from Sanket322/currency_format_in_report
fix: use company default currency in report
2024-08-14 13:38:21 +05:30
ruthra kumar
144cad81a4 Merge pull request #42720 from ruthra-kumar/pos_return_with_self_flag
fix: incorrect 'against_voucher' for Pos return with 'Update Outstanding for Self' disabled.
2024-08-14 11:55:22 +05:30
ruthra kumar
da2286802a refactor: move patch to v14 and update patches.txt 2024-08-14 11:35:05 +05:30
ruthra kumar
3fb0858321 test: against_voucher for pos_returns without updating for self 2024-08-14 11:32:26 +05:30
Khushi Rawat
ad5aadde0f Merge pull request #42719 from khushi8112/recalculate-depreciation-correctly-after-asset-repair
fix: linkage between asset repair and asset value adjustment
2024-08-14 00:30:03 +05:30
Khushi Rawat
88a5824e31 test: new depreciation after cancelling asset repair 2024-08-14 00:06:09 +05:30
Khushi Rawat
c085b6159b fix: resolved conflict 2024-08-14 00:03:35 +05:30
rohitwaghchaure
8aadc18ee8 fix: duplicate labels in stock entry (#42756) 2024-08-13 19:32:15 +05:30
ruthra kumar
f6933111ac Merge pull request #40964 from Nihantra-Patel/fix_timesheet_to_invoice_pricelist
fix: price list when invoice created from timesheet
2024-08-13 17:10:49 +05:30
ruthra kumar
7eb811c71a Merge pull request #42221 from ruthra-kumar/fix_subscription_issue
fix: Force Fetching updates on old subscriptions
2024-08-13 16:22:23 +05:30
ruthra kumar
fd680380bb refactor: test for force-fetch on future subscription 2024-08-13 16:05:06 +05:30
Frappe PR Bot
54ed6a9868 fix: Turkish translations 2024-08-13 15:54:38 +05:30
Frappe PR Bot
f65292612b fix: Persian translations 2024-08-13 15:54:34 +05:30
Khushi Rawat
616ce2dbec Merge pull request #42689 from khushi8112/depreciation-amount-correction
fix: fetch month's last date to avoid miscalculation
2024-08-13 14:11:35 +05:30
ruthra kumar
af11480b7f Merge pull request #42726 from ljain112/sales-funnel
fix: text color in sales funnel report based on theme
2024-08-13 14:05:08 +05:30
ruthra kumar
44261223d5 Merge pull request #42590 from ljain112/coa
fix: error message in coa importer
2024-08-13 14:01:49 +05:30
ruthra kumar
d8b6767697 chore: minor naming change 2024-08-13 12:36:42 +05:30
ruthra kumar
3a11577411 refactor: don't process future subscriptions 2024-08-13 12:36:39 +05:30
ruthra kumar
1ef890db73 fix: force fetch updates for subcription 2024-08-13 12:26:59 +05:30
ruthra kumar
4dc0d3a003 refactor: update patches.txt 2024-08-13 11:58:57 +05:30
ruthra kumar
487d0a55f5 fix: patch to fix incorrect against_voucher references in ledger 2024-08-13 11:58:57 +05:30
ruthra kumar
2cd9b28e5b fix: ledger entries for pos return with update outstanding for self 2024-08-13 11:58:57 +05:30
ruthra kumar
6c2700cd8f Merge pull request #42733 from ruthra-kumar/default_ignore_in_soa
refactor: add a new filter flag in Process Statement of Accounts
2024-08-13 11:10:15 +05:30
ruthra kumar
90880c8c01 refactor: pass filter to General Ledger 2024-08-13 10:42:32 +05:30
ruthra kumar
0cf478c4c2 refactor: cr and dr note filter in Statement of Accounts 2024-08-13 10:41:31 +05:30
ruthra kumar
4cc5cd5a71 refactor: combine vouchers from both ignore 2024-08-13 10:37:55 +05:30
Khushi Rawat
245549951d Merge pull request #42729 from khushi8112/cancel-jl-entry-on-doctype-cancellation
fix: cancel Journal Entry on cancellation of asset value adjustment
2024-08-13 02:12:57 +05:30
Khushi Rawat
4b7f1f2d67 fix: cancel Journal Entry on cancellation of asset value adjustment 2024-08-13 01:48:36 +05:30
Shariq Ansari
18b4b09371 Merge pull request #42727 from shariquerik/include-erpnext
fix: include erpnext in apps page
2024-08-12 19:41:55 +05:30
Shariq Ansari
5280132423 chore: renamed include_as_app to include_in_apps_screen 2024-08-12 19:23:43 +05:30
Shariq Ansari
1d52ef7afe fix: include erpnext in apps page 2024-08-12 17:48:54 +05:30
rohitwaghchaure
4abdb28a1a fix: test case for work order (#42723) 2024-08-12 17:45:29 +05:30
ljain112
61bc0925d5 fix: text color in sales funnel report based on theme 2024-08-12 17:09:39 +05:30
ruthra kumar
0a4db47c69 Merge pull request #42672 from ruthra-kumar/correct_paid_and_received_amt_on_payment_request
fix: update 'Paid Amount' on forex payment request
2024-08-12 16:07:21 +05:30
Frappe PR Bot
f194615057 fix: Swedish translations 2024-08-12 15:55:38 +05:30
Frappe PR Bot
5a68e918cd fix: Persian translations 2024-08-12 15:55:33 +05:30
rohitwaghchaure
17ba0cff44 fix: currency changing while making PR from the PO (#42718) 2024-08-12 15:03:53 +05:30
Frappe PR Bot
61a57aaed5 fix: Swedish translations 2024-08-11 15:58:22 +05:30
rohitwaghchaure
deccb007c1 fix: warning message for negative stock (#42683) 2024-08-11 11:35:45 +05:30
Raffael Meyer
2a999e8f58 Merge pull request #42703 from frappe/l10n_develop
fix: sync translations from crowdin
2024-08-10 23:23:00 +02:00
rohitwaghchaure
bf39e3ab32 perf: data import with 99 stock entries (#42685)
perf: data import for stock entries
2024-08-10 22:34:58 +05:30
Frappe PR Bot
534243a4c8 fix: Swedish translations 2024-08-10 15:48:04 +05:30
Frappe PR Bot
d8de6cb708 fix: Spanish translations 2024-08-10 15:48:01 +05:30
Frappe PR Bot
14c40d17d3 fix: Arabic translations 2024-08-10 15:47:58 +05:30
Frappe PR Bot
aa652c8e15 fix: Bosnian translations 2024-08-10 15:47:55 +05:30
Frappe PR Bot
54d6d346c8 fix: Turkish translations 2024-08-10 15:47:52 +05:30
Frappe PR Bot
4070359174 fix: French translations 2024-08-10 15:47:49 +05:30
Frappe PR Bot
908ca511e7 fix: Persian translations 2024-08-10 15:47:46 +05:30
Frappe PR Bot
2ee70c3968 fix: German translations 2024-08-10 15:47:42 +05:30
Raffael Meyer
2c6938120e Merge pull request #42502 from frappe/l10n_develop 2024-08-09 15:12:26 +02:00
ruthra kumar
f913c0fde1 test: currency conversion on foreign currency account 2024-08-09 17:54:45 +05:30
ruthra kumar
d6d0a1b38d test: make use of test fixture 2024-08-09 16:10:05 +05:30
Frappe PR Bot
2028e52c33 fix: Swedish translations 2024-08-09 15:52:25 +05:30
rohitwaghchaure
b65072cd98 fix: delivery note creation issue (#42696) 2024-08-09 12:45:53 +05:30
ruthra kumar
2312757a1d Merge pull request #42428 from cogk/fix-sort-before-groupby
fix: Sort lists before calling itertools.groupby
2024-08-09 11:52:42 +05:30
ruthra kumar
1dcd1b2a23 Merge pull request #42691 from ruthra-kumar/typeerror_on_payment_entry
fix: typeerror on payment entry
2024-08-09 11:39:59 +05:30
ruthra kumar
9cdca0d662 fix: typeerror on payment entry 2024-08-09 11:34:05 +05:30
Khushi Rawat
70ff4e7644 fix: fetch months last date to avoid miscalculation 2024-08-09 03:21:09 +05:30
barredterra
1d6d383ec8 fix(Process Statement Of Accounts): translatable strings in print 2024-08-08 13:07:33 +02:00
barredterra
34089ba230 style(PSA): fix indentation 2024-08-08 12:40:23 +02:00
Raffael Meyer
38568a3e15 Merge pull request #42679 from barredterra/fix-translatable-string-jv 2024-08-08 12:09:02 +02:00
barredterra
8bd84e9d51 refactor(Exchange Rate Revaluation): remove unused variables 2024-08-08 12:05:25 +02:00
barredterra
dc29fb8759 fix(Exchange Rate Revaluation): translatable strings 2024-08-08 12:05:08 +02:00
Frappe PR Bot
29fe8377a1 fix: Swedish translations 2024-08-08 15:27:45 +05:30
ruthra kumar
657acab32f Merge pull request #42673 from ruthra-kumar/maintain_rate_on_quotation_to_sales_order
fix: Maintain same rate on qty change on Quotation to Sales Order
2024-08-08 13:32:52 +05:30
ruthra kumar
91ce9fce9b fix: Maintain same rate on qty change on Quotation to Sales Order 2024-08-08 11:58:58 +05:30
ruthra kumar
7b0dfb2a05 fix: update 'Paid Amount' on forex payment request 2024-08-08 10:37:57 +05:30
Raffael Meyer
755c162093 Merge pull request #42670 from barredterra/extract-incoterms 2024-08-07 20:45:57 +02:00
barredterra
e177ece008 fix: extract incoterm titles as translatable strings 2024-08-07 20:28:39 +02:00
Shariq Ansari
47c2127a31 Merge pull request #42596 from Didiman1998/bugfix/fix-opportunity-contact-address-bug
fix: changes in opportunity.py to show contacts and addresses created from opportunity
2024-08-07 19:58:36 +05:30
Nihantra Patel
12834ccf9a fix: add the company in payment request bcz delete company transactions 2024-08-07 16:48:29 +05:30
ruthra kumar
efadf94cf3 Merge pull request #42578 from ljain112/fix-dimensions
fix: dimensions in common party journal entry
2024-08-07 16:02:46 +05:30
Khushi Rawat
b1c23ba85c Merge pull request #42593 from khushi8112/disable-submit-button-for-composite-asset
fix: disable primary action button only when there are no active capitalization
2024-08-06 19:11:58 +05:30
Raffael Meyer
82b02d6c9b Merge pull request #42400 from barredterra/root-supplier-group 2024-08-06 14:46:02 +02:00
ruthra kumar
8040544216 test: dimension inheritance on Exc Gain/Loss JV on Common party 2024-08-06 17:46:19 +05:30
ruthra kumar
34ad9d33ff Merge pull request #41708 from Mutantpenguin/patch-1
refactor: use doctype File directly instead of save_file from file_manager.py
2024-08-06 14:34:07 +05:30
ruthra kumar
113362e2d9 Merge pull request #42592 from ljain112/tds-filters
fix: company filter for filtring tax withheld vouchers
2024-08-06 13:16:13 +05:30
ruthra kumar
165ec2ed53 Merge pull request #42545 from ljain112/fix-sales-funnel
fix: min height for rows in sales funnel
2024-08-06 13:01:41 +05:30
rohitwaghchaure
faff84c6e5 feat: expiry date column in Available Batch Report (#42628) 2024-08-06 12:20:41 +05:30
ruthra kumar
4a4e1d36f2 Merge pull request #42636 from ruthra-kumar/ignore_on_gl_should_pull_all_records
refactor: posting date should not be considered for ignore filters in GL
2024-08-06 10:54:54 +05:30
ruthra kumar
c930f8ba9d refactor: posting date is not considered for ignore filters in GL 2024-08-06 10:36:26 +05:30
Frappe PR Bot
42885a81f0 fix: Swedish translations 2024-08-06 03:08:20 +05:30
rohitwaghchaure
c8af544ef3 fix: do not update item price and last purchase rate for inter transf… (#42616)
fix: do not update item price and last purchase rate for inter transfer transaction
2024-08-05 20:20:36 +05:30
ruthra kumar
0f0b19688a Merge pull request #42555 from ruthra-kumar/sales_pipeline_analytics_filters
refactor: date filters should be mandatory in Sales Pipeline Analytics report
2024-08-05 15:07:59 +05:30
ruthra kumar
4253caf910 refactor(test): use test fixture and supply from and to dates 2024-08-05 14:47:09 +05:30
ruthra kumar
213b2ba942 refactor: consider empty-string as Not Assigned 2024-08-05 14:08:08 +05:30
ruthra kumar
e78e948b7c Merge pull request #42621 from ruthra-kumar/clear_data_on_gl_tests
refactor(test): clear data before GL report tests
2024-08-05 13:43:59 +05:30
ruthra kumar
56620785a0 refactor(test): clear old records on GL report tests 2024-08-05 13:26:03 +05:30
ruthra kumar
751a25c4b7 refactor: report columns should be based on from and to dates 2024-08-05 11:31:13 +05:30
ruthra kumar
05b92c5321 Merge pull request #42597 from ruthra-kumar/ignore_reconciliation_jv_on_general_ledger
refactor: filter to ignore system generated cr / dr reconciliation journals on general ledger
2024-08-05 11:12:56 +05:30
ruthra kumar
9ade269b7a refactor(test): filter and reconcile concerned vouchers 2024-08-05 10:46:35 +05:30
ruthra kumar
991069bfbc test: clear old data 2024-08-05 10:23:52 +05:30
ruthra kumar
3617b41b95 refactor: make 'from_date' and 'to_date' mandatory 2024-08-05 10:18:42 +05:30
ruthra kumar
03f3ab522f refactor: make use of date filters on ignore filterss 2024-08-05 10:01:09 +05:30
ruthra kumar
3ffac73598 test: ignore filter for system generated cr / dr note journals 2024-08-05 10:01:07 +05:30
Frappe PR Bot
f7c0ccce0a fix: Swedish translations 2024-08-05 03:03:25 +05:30
Frappe PR Bot
a1bcc6b418 fix: Esperanto translations 2024-08-05 03:03:22 +05:30
Raffael Meyer
6d2a36c4f9 Merge pull request #42612 from frappe/pot_develop_2024-08-04 2024-08-04 13:48:21 +02:00
frappe-pr-bot
5040e3ca7d chore: update POT file 2024-08-04 09:34:51 +00:00
rohitwaghchaure
da22a8df42 Merge pull request #42584 from rohitwaghchaure/fixed-support-19715
fix: all warehouse filter for the stock report
2024-08-02 18:55:42 +05:30
Dietmar Fischer
511a0b9f37 fix: pre-commit for better code formatting 2024-08-02 12:51:48 +02:00
Khushi Rawat
5d58eb67a6 fix: use get_last_day to get the correct date (#42564) 2024-08-02 16:21:21 +05:30
ruthra kumar
bb8c9b5a58 refactor: ignore system generated cr / dr notes on general ledger 2024-08-02 15:47:47 +05:30
ruthra kumar
59d5beee20 refactor: ignore filter in general ledger for cr / dr notes 2024-08-02 15:43:04 +05:30
Khushi Rawat
bb877f4a6b fix: disable primary action button only when there are no active capitalization 2024-08-02 13:37:59 +05:30
ljain112
cfe2ae604b fix: company filter for filtring tax withheld vouchers 2024-08-02 13:12:04 +05:30
ljain112
5d0a38dfc7 fix: error message in coa importer 2024-08-02 11:50:57 +05:30
David
41e395a7e3 fix: hot path in page load 2024-08-01 23:23:48 +02:00
Rohit Waghchaure
a4311e345d fix: all warehouse filter for the stock report 2024-08-01 23:10:46 +05:30
Dietmar Fischer
61576ca030 feat: changes in opportunity.py to show contacts and addresses from referenced and opportunities 2024-08-01 17:01:43 +02:00
Nabin Hait
281198456d fix: Discount and taxes in return document should follow the reference document (#41911)
* fix: Discount and taxes in return document should follow the reference document

* fix: Ignore Pricing rule on debit/credit note if created against PI/SI with test cases

* fix: linter issue
2024-08-01 14:51:01 +05:30
ljain112
ac629ede79 fix: dimensions in common party journal entry 2024-07-31 18:49:19 +05:30
ruthra kumar
fb2aa7d205 Merge pull request #42563 from ruthra-kumar/set_query_filter_on_pe_tax_templates
fix: set query filters for sales / purchase tax template on PE
2024-07-31 17:08:11 +05:30
ruthra kumar
9fe47ac101 fix: set query filters for sales / purchase tax template on PE 2024-07-31 16:42:33 +05:30
ruthra kumar
d811fdf675 Merge pull request #42558 from ruthra-kumar/fix_undefined_in_consolidate_report
fix: 'undefined' in PL and BS report summary on Consolidated report
2024-07-31 15:32:36 +05:30
ruthra kumar
dd5a5e4919 fix: 'undefined' in PL and BS report summary on Consolidated report 2024-07-31 14:40:55 +05:30
rohitwaghchaure
f620ef20ae fix: inter transfer delivery note issue with batch (#42552) 2024-07-31 14:17:22 +05:30
ruthra kumar
40c166a0a0 refactor: date filters should be explicit 2024-07-31 14:01:27 +05:30
Smit Vora
33a0a529a5 Merge pull request #42392 from ljain112/fix-asset
fix: allow sale of asset for internal transfer
2024-07-31 13:22:20 +05:30
Lakshit Jain
8624aeca54 fix: promotional scheme doctype fields in consitency with pricing rule (#42432)
* fix: add "round_free_qty" check box in promotional scheme

* fix: add `add_for_price_list` field

* fix: set_query in setup for promotional scheme

---------
2024-07-31 13:19:54 +05:30
Lakshit Jain
a694390a12 fix: reverse debit credit for party gl entry in payment entry based on negative amount (#42367)
* fix: do not absolute the amount for party gl entries

* fix: reverse debit credit for party gl entry based on negative amount

* refactor: reduce nesting of if condition

---------
2024-07-31 12:46:10 +05:30
Nabin Hait
4986f28a89 fix: set currency on change of company considering customer default currency (#42405) 2024-07-31 11:44:05 +05:30
Khushi Rawat
0e5e503b42 Merge pull request #42525 from khushi8112/depreciation-adjustment-for-existing-asset
fix: Adjust initial month's depreciation to end of depreciation period
2024-07-30 19:04:26 +05:30
ljain112
fd71d8af52 fix: min height for rows in sales funnel 2024-07-30 17:49:56 +05:30
rohitwaghchaure
1c7f7c8d1a perf: huge number of serial no creation (#42522) 2024-07-30 16:33:53 +05:30
ruthra kumar
93ee922c5f Merge pull request #42393 from blaggacao/payments/pr-5
refactor: explicate intent on make_payment_request interface
2024-07-30 16:31:53 +05:30
rohitwaghchaure
23fed831a0 fix: price_list_currency not found error (#42534) 2024-07-30 14:26:29 +05:30
rohitwaghchaure
0ecfa709d8 fix: warehouse filter in Product Bundle Balance (#42532) 2024-07-30 14:26:06 +05:30
rohitwaghchaure
c5d68333c9 fix: purchase return from rejected warehouse (#42531) 2024-07-30 14:24:35 +05:30
ruthra kumar
ff3a5058c1 Merge pull request #42476 from ruthra-kumar/configuration_to_control_payment_request_creation
refactor: checkbox to control Payment Request creation
2024-07-30 13:35:59 +05:30
ruthra kumar
5ec9df8d1c Merge pull request #42528 from ruthra-kumar/rename_button_on_payment_order
chore: button name should reflect what it creates
2024-07-30 11:59:46 +05:30
ruthra kumar
0b6e7f83cd chore: button name should reflect what it creates 2024-07-30 11:54:41 +05:30
Khushi Rawat
f0768010d9 fix(tests): added tests for usecase 2024-07-30 01:28:52 +05:30
Khushi Rawat
cbb749a3a5 fix: Adjust initial month's depreciation to end of depreciation period 2024-07-29 23:50:57 +05:30
David
b94af28587 fix: link cash flow rows and fix summary linking 2024-07-29 20:02:52 +02:00
rohitwaghchaure
8eff168d76 fix: Warranty Expiry Date not set in the serial number (#42513)
* fix: Warranty Expiry Date not set in the serial number

* chore: fix linters issue
2024-07-29 14:47:42 +05:30
rohitwaghchaure
25dac1f18e fix: builtins.KeyError: ('ABC', 'Store - CP') (#42505) 2024-07-29 09:49:53 +05:30
rohitwaghchaure
cb522f8f22 fix: performance issue for the report Purchase Order Analysis report (#42503) 2024-07-28 23:41:00 +05:30
Frappe PR Bot
0b27b19c35 fix: Swedish translations 2024-07-28 23:06:42 +05:30
Raffael Meyer
21d8b09f47 Merge pull request #42499 from frappe/l10n_develop
fix: sync translations from crowdin
2024-07-28 14:49:19 +02:00
Frappe PR Bot
e9c1a7e3e3 fix: Esperanto translations 2024-07-27 22:53:17 +05:30
Raffael Meyer
24b26627e7 Merge pull request #42498 from frappe/pot_develop_2024-07-27 2024-07-27 19:18:38 +02:00
frappe-pr-bot
9aaf6e47d5 chore: update POT file 2024-07-27 17:02:08 +00:00
Raffael Meyer
4931b74b3f Merge pull request #42466 from frappe/l10n_develop 2024-07-27 19:00:36 +02:00
Raffael Meyer
532bc163f6 Merge pull request #42457 from barredterra/review-translations 2024-07-27 18:59:26 +02:00
Raffael Meyer
4c3ec767ce Merge pull request #42418 from barredterra/si-queries 2024-07-27 18:58:27 +02:00
ruthra kumar
0d77e0b0cd Merge pull request #42477 from ruthra-kumar/incorrect_cost_center_on_AR_AP_report
fix: incorrect cost_center on AR/AP report
2024-07-26 19:42:56 +05:30
Ninad Parikh
723ac0ffc4 fix: Update Rate as per Valuation Rate for Internal Transfers only if Setting is Enabled (#42050)
* fix: update rate for internal transfers only if settings enabled

* fix: better naming

* fix: create field for storing incoming rate in purchase doctypes

* fix: use qty instead of qty_in_stock_uom

* fix: add description, refactor for readablility

* test: test case to validate internal transfers at arm's length price

* fix: minor fix

* fix: deletion of code not required

---------

Co-authored-by: Smit Vora <smitvora203@gmail.com>
2024-07-26 18:22:40 +05:30
ruthra kumar
9a0894fd65 test: invoice cost center reported in AR/AP report 2024-07-26 18:16:40 +05:30
Smit Vora
096ec2db6a Merge pull request #42470 from Ninad1306/field_changes_for_subcontracting
fix: Fields Alteration Related to Subcontracting
2024-07-26 16:24:47 +05:30
Smit Vora
3b481ae656 Merge pull request #42305 from ljain112/fix-tds-adv
fix: consider payment entries for checking if tds is deducted
2024-07-26 14:40:15 +05:30
Smit Vora
ad31b63537 Merge pull request #42444 from ljain112/parenttype
fix: parenttype in purchase and sales item query
2024-07-26 14:23:21 +05:30
ruthra kumar
3e19041fa3 fix: incorrect cost_center on AR/AP report 2024-07-26 08:00:13 +05:30
ruthra kumar
ce81fd9ba6 refactor: checkbox to control Payment Request creation 2024-07-26 06:52:55 +05:30
ruthra kumar
07281d30bb Merge pull request #42472 from ruthra-kumar/index_on_release_date
refactor: index on Purchase Invoice 'release_date'
2024-07-25 21:26:22 +05:30
ruthra kumar
764dd12b10 refactor: index on Purchase Invoice 'release_date' 2024-07-25 19:51:54 +05:30
Ninad1306
77590e6077 fix: fields alteration related to subcontracting 2024-07-25 18:05:31 +05:30
ljain112
40b59de4cd fix: consider payment entries for checking if tds is deducted 2024-07-25 17:26:52 +05:30
Ninad Parikh
302339998f fix: Fields Modification for Subcontracting DocTypes (#42383)
* fix: fields renaming and reordering for enhanced user experience

* fix: dashboard data for stock entry
2024-07-25 10:18:21 +05:30
rohitwaghchaure
0e817f42ef fix: dynamic condition in the pricing rule not working (#42467) 2024-07-25 10:15:05 +05:30
Frappe PR Bot
8c6d666163 fix: Swedish translations 2024-07-24 20:18:54 +05:30
Frappe PR Bot
4ab6134306 fix: Esperanto translations 2024-07-24 20:18:50 +05:30
ruthra kumar
e062ec38b0 Merge pull request #42462 from ruthra-kumar/monthly_err_revaluation
refactor: provision for re-evaluating Exchange Rates in monthly frequency
2024-07-24 18:44:04 +05:30
ruthra kumar
fc4e5f165c refactor: hooks for monthly re-evaluation jobs 2024-07-24 18:18:21 +05:30
ruthra kumar
ce2b9e0f1a refactor: provision for monthly re-evaluation 2024-07-24 18:18:13 +05:30
rohitwaghchaure
06e2d7265c fix: keyerror posting_time (#42452)
fix: keyerror: posting_time
2024-07-24 16:39:11 +05:30
rohitwaghchaure
9cd3374101 fix: incorrect current qty for the batch in stock reco (#42434) 2024-07-24 16:38:20 +05:30
Sanket322
2533808f1e fix: set proper currency format 2024-07-24 13:42:04 +05:30
barredterra
89c458e229 chore: set PR reviewer for translations 2024-07-24 09:59:46 +02:00
Raffael Meyer
ffc5bbfa4c Merge pull request #42319 from frappe/pot_develop_2024-07-14 2024-07-24 09:51:13 +02:00
Raffael Meyer
92a6fc8c2e Merge pull request #42206 from frappe/l10n_develop 2024-07-24 09:50:43 +02:00
ruthra kumar
8ccf8c0ea2 Merge pull request #42390 from ruthra-kumar/remove_stale_code_from_reposting
refactor: cleaning up stale code related to reposting
2024-07-24 12:57:51 +05:30
ljain112
972329cc16 fix: allow sale of asset for internal transfer 2024-07-23 17:44:37 +05:30
ljain112
35981b8730 fix: parenttype in item wise purchase and sales register 2024-07-23 17:36:33 +05:30
Khushi Rawat
6e1fc33d01 Merge pull request #42372 from khushi8112/correct-validation-for-depreciation-posting-date
fix: correct validation for depreciation posting date
2024-07-23 15:23:41 +05:30
Smit Vora
c8e85f4bb7 Merge pull request #42377 from vorasmit/ignore-duplicates
fix: ignore duplicates while creating default templates
2024-07-23 10:25:29 +05:30
Raffael Meyer
bf2c9a7f41 Merge pull request #42419 from barredterra/si-initial-value 2024-07-22 20:40:49 +02:00
rohitwaghchaure
be2648245b fix: qty in the 'Serial No Ledger' report (#42429) 2024-07-22 17:45:48 +05:30
Corentin Forler
45a6ecbd38 fix: Sort lists before calling itertools.groupby 2024-07-22 10:13:10 +02:00
barredterra
65f80abf2f fix: provide initial value for .reduce() call
Fixes the error "TypeError: Reduce of empty array with no initial value" (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Errors/Reduce_of_empty_array_with_no_initial_value#invalid_cases)
2024-07-20 00:44:31 +02:00
barredterra
9b463753b7 refactor(Sales Invoice): queries 2024-07-20 00:27:16 +02:00
Frappe PR Bot
9e93bc5819 fix: Swedish translations 2024-07-19 17:47:54 +05:30
barredterra
5d4578a172 refactor(Supplier Group): use frm.is_new() instead of frm.doc.__islocal 2024-07-18 21:00:42 +02:00
barredterra
45391c951b fix(Supplier Group): remove useless headline
Not sure what it was supposed to mean. It was only visible on root group, along with the other message.
2024-07-18 20:59:26 +02:00
barredterra
741fc54eca fix(Supplier Group): properly call frm.set_read_only() 2024-07-18 20:56:56 +02:00
David
6d83c11d8e refactor: explicate intent on make_payment_request interface 2024-07-18 15:18:27 +02:00
ruthra kumar
e71cb4eab7 refactor(test): remove assert on 'repost_required' 2024-07-18 15:39:30 +05:30
ruthra kumar
07fc952a43 refactor: remove attribute check on 'repost_required' 2024-07-18 15:38:15 +05:30
ruthra kumar
09f429ffba refactor: repost without checking on flag 2024-07-18 15:34:59 +05:30
ruthra kumar
fe46e1d089 chore: remove stale UI code related to repost 2024-07-18 15:34:02 +05:30
ruthra kumar
e81373bb6a chore: remove 'repost_required' from Journal Entry 2024-07-18 15:32:51 +05:30
ruthra kumar
a467888a67 chore: remove 'repost_required' from purchase invoice 2024-07-18 15:31:59 +05:30
ruthra kumar
06c5334f2a chore: remove stale 'repost_required' flag from sales invoice 2024-07-18 15:31:01 +05:30
ruthra kumar
f3fda9ce98 chore: remove stale code from sales invoice 2024-07-18 15:29:30 +05:30
Nihantra Patel
ee44022249 fix: update the testcase format 2024-07-18 12:26:23 +05:30
Nihantra Patel
29d50b770e fix: update the testcase format 2024-07-18 12:24:52 +05:30
Nihantra Patel
2f63fae31d fix: Create Sales Order from Quotation for Prospect 2024-07-18 12:14:53 +05:30
Smit Vora
cf55c2ab3d fix: ignore duplicates while creating default templates 2024-07-18 10:07:02 +05:30
Khushi Rawat
da4ed90a3e fix: correct validation for depreciation posting date 2024-07-17 19:19:31 +05:30
Frappe PR Bot
656e363aef fix: Swedish translations 2024-07-16 02:32:01 +05:30
frappe-pr-bot
372b5a4ca7 chore: update POT file 2024-07-14 09:35:44 +00:00
Frappe PR Bot
5e3359c5c5 fix: Swedish translations 2024-07-14 02:09:38 +05:30
Frappe PR Bot
62fc3bd586 fix: Swedish translations 2024-07-11 01:37:52 +05:30
Frappe PR Bot
e7ee508de4 fix: Esperanto translations 2024-07-09 00:53:26 +05:30
Frappe PR Bot
4b415987f9 fix: Swedish translations 2024-07-08 00:51:51 +05:30
Frappe PR Bot
cfda332faf fix: Swedish translations 2024-07-07 00:29:52 +05:30
creative-paramu
6e2ac09821 fix: bank reconcilation tool cost center company filter adding 2024-06-30 20:37:09 +05:30
Nihantra Patel
2851764ed6 refactor: item-wise purchase history (query to script report) --upd 2024-06-19 16:09:44 +05:30
Nihantra Patel
7bae18aba8 refactor: item-wise purchase history (query to script report) --prettier 2024-06-19 15:06:09 +05:30
Nihantra Patel
f740c94363 refactor: item-wise purchase history (query to script report) --prettier 2024-06-19 14:59:24 +05:30
Nihantra Patel
003a9608dc refactor: item-wise purchase history (query to script report) -- formatter 2024-06-19 14:57:02 +05:30
Nihantra Patel
49331e6109 refactor: item-wise purchase history (query to script report) 2024-06-19 14:37:29 +05:30
Nihantra Patel
5de91cf55e refactor: item-wise purchase history (query to script report) 2024-06-19 14:31:52 +05:30
David
18993a97ce fix: spec mobile and email fields for notifications 2024-06-17 10:28:38 +02:00
Markus Lobedann
0ba4bff943 Merge branch 'frappe:develop' into patch-1 2024-05-31 14:39:41 +02:00
Nihantra C. Patel
882227a460 fix: price list when invoice created from timesheet 2024-05-29 10:26:49 +05:30
Markus Lobedann
9d9331408f Merge branch 'frappe:develop' into patch-1 2024-04-18 14:51:05 +02:00
Nihantra C. Patel
39d6df7c7d fix: price list when invoice created from timesheet 2024-04-11 12:09:27 +05:30
Markus Lobedann
a2f878e1d1 fix: 🐛 use doctype File directly instead of save_file from file_manager.py
`save_file` will change the filename and not clean up after itself after a rollback
2024-04-04 10:31:34 +02:00
357 changed files with 180142 additions and 352228 deletions

View File

@@ -18,4 +18,4 @@ max_line_length = 110
[{*.json}]
insert_final_newline = false
indent_style = space
indent_size = 2
indent_size = 1

View File

@@ -37,4 +37,4 @@ gh auth setup-git
git push -u upstream "${branch_name}"
echo "Creating a PR..."
gh pr create --fill --base "${BASE_BRANCH}" --head "${branch_name}" -R frappe/erpnext
gh pr create --fill --base "${BASE_BRANCH}" --head "${branch_name}" --reviewer ${PR_REVIEWER} -R frappe/erpnext

View File

@@ -36,3 +36,4 @@ jobs:
env:
GH_TOKEN: ${{ secrets.RELEASE_TOKEN }}
BASE_BRANCH: ${{ matrix.branch }}
PR_REVIEWER: barredterra # change to your GitHub username if you copied this file

5
.gitignore vendored
View File

@@ -14,5 +14,8 @@ __pycache__
*~
.idea/
.vscode/
.helix/
node_modules/
.backportrc.json
.backportrc.json
# Aider AI Chat
.aider*

View File

@@ -1 +1,3 @@
**/setup/setup_wizard/data/uom_data.json,erpnext.gettext.extractors.uom_data.extract
**/setup/doctype/incoterm/incoterms.csv,erpnext.gettext.extractors.incoterms.extract
**/setup/setup_wizard/data/*.txt,erpnext.gettext.extractors.lines_from_txt_file.extract
1 **/setup/setup_wizard/data/uom_data.json erpnext.gettext.extractors.uom_data.extract
2 **/setup/doctype/incoterm/incoterms.csv erpnext.gettext.extractors.incoterms.extract
3 **/setup/setup_wizard/data/*.txt erpnext.gettext.extractors.lines_from_txt_file.extract

View File

@@ -4,5 +4,7 @@ files:
pull_request_title: "fix: sync translations from crowdin"
pull_request_labels:
- translation
pull_request_reviewers:
- barredterra # change to your GitHub username if you copied this file
commit_message: "fix: %language% translations"
append_commit_message: false

View File

@@ -2,6 +2,7 @@ import functools
import inspect
import frappe
from frappe.utils.user import is_website_user
__version__ = "16.0.0-dev"
@@ -149,3 +150,13 @@ def allow_regional(fn):
return frappe.get_attr(overrides[function_path][-1])(*args, **kwargs)
return caller
def check_app_permission():
if frappe.session.user == "Administrator":
return True
if is_website_user():
return False
return True

View File

@@ -124,7 +124,7 @@
"label": "Account Type",
"oldfieldname": "account_type",
"oldfieldtype": "Select",
"options": "\nAccumulated Depreciation\nAsset Received But Not Billed\nBank\nCash\nChargeable\nCapital Work in Progress\nCost of Goods Sold\nCurrent Asset\nCurrent Liability\nDepreciation\nDirect Expense\nDirect Income\nEquity\nExpense Account\nExpenses Included In Asset Valuation\nExpenses Included In Valuation\nFixed Asset\nIncome Account\nIndirect Expense\nIndirect Income\nLiability\nPayable\nReceivable\nRound Off\nStock\nStock Adjustment\nStock Received But Not Billed\nService Received But Not Billed\nTax\nTemporary",
"options": "\nAccumulated Depreciation\nAsset Received But Not Billed\nBank\nCash\nChargeable\nCapital Work in Progress\nCost of Goods Sold\nCurrent Asset\nCurrent Liability\nDepreciation\nDirect Expense\nDirect Income\nEquity\nExpense Account\nExpenses Included In Asset Valuation\nExpenses Included In Valuation\nFixed Asset\nIncome Account\nIndirect Expense\nIndirect Income\nLiability\nPayable\nReceivable\nRound Off\nRound Off for Opening\nStock\nStock Adjustment\nStock Received But Not Billed\nService Received But Not Billed\nTax\nTemporary",
"search_index": 1
},
{
@@ -194,7 +194,7 @@
"idx": 1,
"is_tree": 1,
"links": [],
"modified": "2024-06-27 16:23:04.444354",
"modified": "2024-08-19 15:19:11.095045",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Account",

View File

@@ -60,6 +60,7 @@ class Account(NestedSet):
"Payable",
"Receivable",
"Round Off",
"Round Off for Opening",
"Stock",
"Stock Adjustment",
"Stock Received But Not Billed",
@@ -200,7 +201,7 @@ class Account(NestedSet):
msg = _(
"There are ledger entries against this account. Changing {0} to non-{1} in live system will cause incorrect output in 'Accounts {2}' report"
).format(
frappe.bold("Account Type"), doc_before_save.account_type, doc_before_save.account_type
frappe.bold(_("Account Type")), doc_before_save.account_type, doc_before_save.account_type
)
frappe.msgprint(msg)
self.add_comment("Comment", msg)

View File

@@ -73,7 +73,9 @@
"remarks_section",
"general_ledger_remarks_length",
"column_break_lvjk",
"receivable_payable_remarks_length"
"receivable_payable_remarks_length",
"payment_request_settings",
"create_pr_in_draft_status"
],
"fields": [
{
@@ -475,6 +477,18 @@
"fieldname": "calculate_depr_using_total_days",
"fieldtype": "Check",
"label": "Calculate daily depreciation using total days in depreciation period"
},
{
"description": "Payment Request created from Sales Order or Purchase Order will be in Draft status. When disabled document will be in unsaved state.",
"fieldname": "payment_request_settings",
"fieldtype": "Tab Break",
"label": "Payment Request"
},
{
"default": "1",
"fieldname": "create_pr_in_draft_status",
"fieldtype": "Check",
"label": "Create in Draft Status"
}
],
"icon": "icon-cog",
@@ -482,7 +496,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2024-07-12 00:24:20.957726",
"modified": "2024-07-26 06:48:52.714630",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Settings",

View File

@@ -35,6 +35,7 @@ class AccountsSettings(Document):
book_tax_discount_loss: DF.Check
calculate_depr_using_total_days: DF.Check
check_supplier_invoice_uniqueness: DF.Check
create_pr_in_draft_status: DF.Check
credit_controller: DF.Link | None
delete_linked_ledger_entries: DF.Check
determine_address_tax_category_from: DF.Literal["Billing Address", "Shipping Address"]

View File

@@ -38,6 +38,11 @@ frappe.ui.form.on("Bank Clearance", {
frm.add_custom_button(__("Get Payment Entries"), () => frm.trigger("get_payment_entries"));
frm.change_custom_button_type(__("Get Payment Entries"), null, "primary");
if (frm.doc.payment_entries.length) {
frm.add_custom_button(__("Update Clearance Date"), () => frm.trigger("update_clearance_date"));
frm.change_custom_button_type(__("Get Payment Entries"), null, "default");
frm.change_custom_button_type(__("Update Clearance Date"), null, "primary");
}
},
update_clearance_date: function (frm) {
@@ -45,13 +50,7 @@ frappe.ui.form.on("Bank Clearance", {
method: "update_clearance_date",
doc: frm.doc,
callback: function (r, rt) {
frm.refresh_field("payment_entries");
frm.refresh_fields();
if (!frm.doc.payment_entries.length) {
frm.change_custom_button_type(__("Get Payment Entries"), null, "primary");
frm.change_custom_button_type(__("Update Clearance Date"), null, "default");
}
frm.refresh();
},
});
},
@@ -60,17 +59,8 @@ frappe.ui.form.on("Bank Clearance", {
return frappe.call({
method: "get_payment_entries",
doc: frm.doc,
callback: function (r, rt) {
frm.refresh_field("payment_entries");
if (frm.doc.payment_entries.length) {
frm.add_custom_button(__("Update Clearance Date"), () =>
frm.trigger("update_clearance_date")
);
frm.change_custom_button_type(__("Get Payment Entries"), null, "default");
frm.change_custom_button_type(__("Update Clearance Date"), null, "primary");
}
callback: function () {
frm.refresh();
},
});
},

View File

@@ -6,7 +6,7 @@ import frappe
from frappe import _, msgprint
from frappe.model.document import Document
from frappe.query_builder.custom import ConstantColumn
from frappe.utils import flt, fmt_money, getdate
from frappe.utils import flt, fmt_money, get_link_to_form, getdate
from pypika import Order
import erpnext
@@ -96,8 +96,11 @@ class BankClearance(Document):
if d.cheque_date and getdate(d.clearance_date) < getdate(d.cheque_date):
frappe.throw(
_("Row #{0}: Clearance date {1} cannot be before Cheque Date {2}").format(
d.idx, d.clearance_date, d.cheque_date
_("Row #{0}: For {1} Clearance date {2} cannot be before Cheque Date {3}").format(
d.idx,
get_link_to_form(d.payment_document, d.payment_entry),
d.clearance_date,
d.cheque_date,
)
)

View File

@@ -47,9 +47,11 @@ def validate_columns(data):
no_of_columns = max([len(d) for d in data])
if no_of_columns > 8:
if no_of_columns != 8:
frappe.throw(
_("More columns found than expected. Please compare the uploaded file with standard template"),
_(
"Columns are not according to template. Please compare the uploaded file with standard template"
),
title=(_("Wrong Template")),
)

View File

@@ -13,6 +13,7 @@
"customer",
"column_break_4",
"coupon_code",
"from_external_ecomm_platform",
"pricing_rule",
"uses",
"valid_from",
@@ -61,11 +62,12 @@
"unique": 1
},
{
"depends_on": "eval !doc.from_external_ecomm_platform",
"fieldname": "pricing_rule",
"fieldtype": "Link",
"label": "Pricing Rule",
"options": "Pricing Rule",
"reqd": 1
"mandatory_depends_on": "eval: !doc.from_external_ecomm_platform",
"options": "Pricing Rule"
},
{
"fieldname": "uses",
@@ -114,13 +116,20 @@
"options": "Coupon Code",
"print_hide": 1,
"read_only": 1
},
{
"default": "0",
"fieldname": "from_external_ecomm_platform",
"fieldtype": "Check",
"label": "From External Ecomm Platform"
}
],
"links": [],
"modified": "2024-03-27 13:06:47.220931",
"modified": "2024-06-28 06:17:01.833399",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Coupon Code",
"naming_rule": "By fieldname",
"owner": "Administrator",
"permissions": [
{
@@ -177,4 +186,4 @@
"states": [],
"title_field": "coupon_name",
"track_changes": 1
}
}

View File

@@ -23,8 +23,9 @@ class CouponCode(Document):
coupon_type: DF.Literal["Promotional", "Gift Card"]
customer: DF.Link | None
description: DF.TextEditor | None
from_external_ecomm_platform: DF.Check
maximum_use: DF.Int
pricing_rule: DF.Link
pricing_rule: DF.Link | None
used: DF.Int
valid_from: DF.Date | None
valid_upto: DF.Date | None

View File

@@ -142,3 +142,39 @@ class TestCouponCode(unittest.TestCase):
so.submit()
self.assertEqual(frappe.db.get_value("Coupon Code", "SAVE30", "used"), 1)
def test_coupon_without_max_use(self):
from erpnext.accounts.doctype.pricing_rule.utils import (
update_coupon_code_count,
validate_coupon_code,
)
coupon = frappe.get_doc(
{
"doctype": "Coupon Code",
"coupon_name": "_Test Coupon Without Max Use",
"coupon_code": "TESTUNLIMITED",
"from_external_ecomm_platform": 1, # avoids requirement for pricing rule
"valid_from": frappe.utils.nowdate(),
"maximum_use": 0,
"used": 0,
}
)
coupon.insert(ignore_permissions=True)
# Validate initial state
self.assertEqual(coupon.used, 0)
self.assertEqual(coupon.maximum_use, 0)
# Use coupon multiple times
for _ in range(5):
validate_coupon_code(coupon.name)
update_coupon_code_count(coupon.name, "used")
coupon.reload()
# Check that the coupon is still valid and usage count increased
self.assertEqual(coupon.used, 5)
validate_coupon_code(coupon.name) # This should not raise an error
# Clean up
coupon.delete()

View File

@@ -85,18 +85,16 @@ frappe.ui.form.on("Exchange Rate Revaluation", {
},
make_jv: function (frm) {
let revaluation_journal = null;
let zero_balance_journal = null;
frappe.call({
method: "make_jv_entries",
doc: frm.doc,
freeze: true,
freeze_message: "Making Journal Entries...",
freeze_message: __("Creating Journal Entries..."),
callback: function (r) {
if (r.message) {
let response = r.message;
if (response["revaluation_jv"] || response["zero_balance_jv"]) {
frappe.msgprint(__("Journals have been created"));
frappe.msgprint(__("Journal entries have been created"));
}
}
},

View File

@@ -25,30 +25,6 @@ frappe.ui.form.on("Journal Entry", {
refresh: function (frm) {
erpnext.toggle_naming_series();
if (frm.doc.repost_required && frm.doc.docstatus === 1) {
frm.set_intro(
__(
"Accounting entries for this Journal Entry need to be reposted. Please click on 'Repost' button to update."
)
);
frm.add_custom_button(__("Repost Accounting Entries"), () => {
frm.call({
doc: frm.doc,
method: "repost_accounting_entries",
freeze: true,
freeze_message: __("Reposting..."),
callback: (r) => {
if (!r.exc) {
frappe.msgprint(__("Accounting Entries are reposted."));
frm.refresh();
}
},
});
})
.removeClass("btn-default")
.addClass("btn-warning");
}
if (frm.doc.docstatus > 0) {
frm.add_custom_button(
__("Ledger"),

View File

@@ -64,8 +64,7 @@
"stock_entry",
"subscription_section",
"auto_repeat",
"amended_from",
"repost_required"
"amended_from"
],
"fields": [
{
@@ -544,15 +543,6 @@
"label": "Is System Generated",
"no_copy": 1,
"read_only": 1
},
{
"default": "0",
"fieldname": "repost_required",
"fieldtype": "Check",
"hidden": 1,
"label": "Repost Required",
"print_hide": 1,
"read_only": 1
}
],
"icon": "fa fa-file-text",
@@ -567,7 +557,7 @@
"table_fieldname": "payment_entries"
}
],
"modified": "2024-03-27 13:09:58.366953",
"modified": "2024-07-18 15:32:29.413598",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry",

View File

@@ -47,9 +47,7 @@ class JournalEntry(AccountsController):
if TYPE_CHECKING:
from frappe.types import DF
from erpnext.accounts.doctype.journal_entry_account.journal_entry_account import (
JournalEntryAccount,
)
from erpnext.accounts.doctype.journal_entry_account.journal_entry_account import JournalEntryAccount
accounts: DF.Table[JournalEntryAccount]
amended_from: DF.Link | None
@@ -197,14 +195,10 @@ class JournalEntry(AccountsController):
self.update_booked_depreciation()
def on_update_after_submit(self):
if hasattr(self, "repost_required"):
self.needs_repost = self.check_if_fields_updated(
fields_to_check=[], child_tables={"accounts": []}
)
if self.needs_repost:
self.validate_for_repost()
self.db_set("repost_required", self.needs_repost)
self.repost_accounting_entries()
self.needs_repost = self.check_if_fields_updated(fields_to_check=[], child_tables={"accounts": []})
if self.needs_repost:
self.validate_for_repost()
self.repost_accounting_entries()
def on_cancel(self):
# References for this Journal are removed on the `on_cancel` event in accounts_controller

View File

@@ -15,14 +15,16 @@
"purchase_amount",
"expiry_date",
"posting_date",
"company"
"company",
"discretionary_reason"
],
"fields": [
{
"fieldname": "loyalty_program",
"fieldtype": "Link",
"label": "Loyalty Program",
"options": "Loyalty Program"
"options": "Loyalty Program",
"reqd": 1
},
{
"fieldname": "loyalty_program_tier",
@@ -34,7 +36,8 @@
"fieldtype": "Link",
"in_list_view": 1,
"label": "Customer",
"options": "Customer"
"options": "Customer",
"reqd": 1
},
{
"fieldname": "redeem_against",
@@ -46,7 +49,8 @@
"fieldname": "loyalty_points",
"fieldtype": "Int",
"in_list_view": 1,
"label": "Loyalty Points"
"label": "Loyalty Points",
"reqd": 1
},
{
"fieldname": "purchase_amount",
@@ -57,24 +61,28 @@
"fieldname": "expiry_date",
"fieldtype": "Date",
"in_list_view": 1,
"label": "Expiry Date"
"label": "Expiry Date",
"reqd": 1
},
{
"fieldname": "posting_date",
"fieldtype": "Date",
"label": "Posting Date"
"label": "Posting Date",
"reqd": 1
},
{
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"options": "Company"
"options": "Company",
"reqd": 1
},
{
"fieldname": "invoice_type",
"fieldtype": "Link",
"label": "Invoice Type",
"options": "DocType"
"options": "DocType",
"reqd": 1
},
{
"fieldname": "invoice",
@@ -82,11 +90,16 @@
"in_list_view": 1,
"label": "Invoice",
"options": "invoice_type"
},
{
"fieldname": "discretionary_reason",
"fieldtype": "Data",
"label": "Discretionary Reason"
}
],
"in_create": 1,
"links": [],
"modified": "2024-03-27 13:10:03.015035",
"modified": "2024-07-01 08:51:13.927009",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Loyalty Point Entry",
@@ -123,4 +136,4 @@
"states": [],
"title_field": "customer",
"track_changes": 1
}
}

View File

@@ -18,15 +18,16 @@ class LoyaltyPointEntry(Document):
if TYPE_CHECKING:
from frappe.types import DF
company: DF.Link | None
customer: DF.Link | None
expiry_date: DF.Date | None
company: DF.Link
customer: DF.Link
discretionary_reason: DF.Data | None
expiry_date: DF.Date
invoice: DF.DynamicLink | None
invoice_type: DF.Link | None
invoice_type: DF.Link
loyalty_points: DF.Int
loyalty_program: DF.Link | None
loyalty_program: DF.Link
loyalty_program_tier: DF.Data | None
posting_date: DF.Date | None
posting_date: DF.Date
purchase_amount: DF.Currency
redeem_against: DF.Link | None
# end: auto-generated types

View File

@@ -3,6 +3,83 @@
import unittest
import frappe
from frappe.utils import today
from erpnext.accounts.doctype.loyalty_program.test_loyalty_program import create_records
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
class TestLoyaltyPointEntry(unittest.TestCase):
pass
@classmethod
def setUpClass(cls):
# Create test records
create_records()
cls.loyalty_program_name = "Test Single Loyalty"
cls.customer_name = "Test Loyalty Customer"
customer = frappe.get_doc("Customer", cls.customer_name)
customer.db_set("loyalty_program", cls.loyalty_program_name)
@classmethod
def tearDownClass(cls):
# Delete all Loyalty Point Entries
frappe.db.sql("DELETE FROM `tabLoyalty Point Entry` WHERE customer = %s", cls.customer_name)
frappe.db.sql("DELETE FROM `tabSales Invoice` WHERE customer = %s", cls.customer_name)
frappe.db.commit()
# cls.customer.delete()
def create_test_invoice(self, redeem=None):
if redeem:
si = create_sales_invoice(customer=self.customer_name, qty=1, rate=100, do_not_save=True)
si.redeem_loyalty_points = True
si.loyalty_points = redeem
return si.insert().submit()
else:
si = create_sales_invoice(customer=self.customer_name, qty=10, rate=1000, do_not_save=True)
return si.insert().submit()
def test_add_loyalty_points(self):
self.create_test_invoice()
doc = frappe.get_last_doc("Loyalty Point Entry")
self.assertEqual(doc.loyalty_points, 10)
def test_add_loyalty_points_with_discretionary_reason(self):
doc = frappe.get_doc(
{
"doctype": "Loyalty Point Entry",
"loyalty_program": "Test Single Loyalty",
"loyalty_program_tier": "Bronce",
"customer": self.customer_name,
"invoice_type": "Sales Invoice",
"loyalty_points": 75,
"expiry_date": today(),
"posting_date": today(),
"company": "_Test Company",
"discretionary_reason": "Customer Appreciation",
}
)
doc.insert(ignore_permissions=True)
self.assertEqual(doc.loyalty_points, 75)
self.assertEqual(doc.discretionary_reason, "Customer Appreciation")
# Verify the entry in the database
entry = frappe.get_doc("Loyalty Point Entry", doc.name)
self.assertEqual(entry.loyalty_points, 75)
self.assertEqual(entry.discretionary_reason, "Customer Appreciation")
def test_redeem_loyalty_points(self):
self.create_test_invoice(redeem=10)
doc = frappe.get_last_doc("Loyalty Point Entry")
self.assertEqual(doc.loyalty_points, -10)
# Check balance
balance = frappe.db.sql(
"""
SELECT SUM(loyalty_points)
FROM `tabLoyalty Point Entry`
WHERE customer = %s
""",
(self.customer_name,),
)[0][0]
self.assertEqual(balance, 75) # 85 added, 10 redeemed

View File

@@ -36,7 +36,17 @@ class LoyaltyProgram(Document):
to_date: DF.Date | None
# end: auto-generated types
pass
def validate(self):
self.validate_lowest_tier()
def validate_lowest_tier(self):
tiers = sorted(self.collection_rules, key=lambda x: x.min_spent)
if tiers and tiers[0].min_spent != 0:
frappe.throw(
_(
"The lowest tier must have a minimum spent amount of 0. Customers need to be part of a tier as soon as they are enrolled in the program."
)
)
def get_loyalty_details(
@@ -79,17 +89,17 @@ def get_loyalty_program_details_with_points(
):
lp_details = get_loyalty_program_details(customer, loyalty_program, company=company, silent=silent)
loyalty_program = frappe.get_doc("Loyalty Program", loyalty_program)
lp_details.update(
get_loyalty_details(customer, loyalty_program.name, expiry_date, company, include_expired_entry)
loyalty_details = get_loyalty_details(
customer, loyalty_program.name, expiry_date, company, include_expired_entry
)
lp_details.update(loyalty_details)
tier_spent_level = sorted(
[d.as_dict() for d in loyalty_program.collection_rules],
key=lambda rule: rule.min_spent,
reverse=True,
)
for i, d in enumerate(tier_spent_level):
if i == 0 or (lp_details.total_spent + current_transaction_amount) <= d.min_spent:
if i == 0 or (lp_details.total_spent + current_transaction_amount) >= d.min_spent:
lp_details.tier_name = d.tier_name
lp_details.collection_factor = d.collection_factor
else:
@@ -173,6 +183,8 @@ def validate_loyalty_points(ref_doc, points_to_redeem):
if not ref_doc.loyalty_amount and ref_doc.loyalty_amount != loyalty_amount:
ref_doc.loyalty_amount = loyalty_amount
if not ref_doc.loyalty_points and ref_doc.loyalty_points != points_to_redeem:
ref_doc.loyalty_points = points_to_redeem
if ref_doc.doctype == "Sales Invoice":
ref_doc.loyalty_program = loyalty_program

View File

@@ -7,6 +7,7 @@ import frappe
from frappe.utils import cint, flt, getdate, today
from erpnext.accounts.doctype.loyalty_program.loyalty_program import (
get_loyalty_details,
get_loyalty_program_details_with_points,
)
from erpnext.accounts.party import get_dashboard_info
@@ -38,6 +39,7 @@ class TestLoyaltyProgram(unittest.TestCase):
)
self.assertEqual(si_original.get("loyalty_program"), customer.loyalty_program)
self.assertEqual(lpe.get("loyalty_program_tier"), "Bronce") # is always in the first tier
self.assertEqual(lpe.get("loyalty_program_tier"), customer.loyalty_program_tier)
self.assertEqual(lpe.loyalty_points, earned_points)
@@ -79,6 +81,7 @@ class TestLoyaltyProgram(unittest.TestCase):
si_original = create_sales_invoice_record()
si_original.insert()
si_original.submit()
customer.reload()
earned_points = get_points_earned(si_original)
@@ -101,8 +104,8 @@ class TestLoyaltyProgram(unittest.TestCase):
si_redeem.loyalty_points = earned_points
si_redeem.insert()
si_redeem.submit()
customer.reload()
customer = frappe.get_doc("Customer", {"customer_name": "Test Loyalty Customer"})
earned_after_redemption = get_points_earned(si_redeem)
lpe_redeem = frappe.get_doc(
@@ -197,6 +200,70 @@ class TestLoyaltyProgram(unittest.TestCase):
for d in company_wise_info:
self.assertTrue(d.get("loyalty_points"))
@unittest.mock.patch("erpnext.accounts.doctype.loyalty_program.loyalty_program.get_loyalty_details")
def test_tier_selection(self, mock_get_loyalty_details):
# Create a new loyalty program with multiple tiers
loyalty_program = frappe.get_doc(
{
"doctype": "Loyalty Program",
"loyalty_program_name": "Test Tier Selection",
"auto_opt_in": 1,
"from_date": today(),
"loyalty_program_type": "Multiple Tier Program",
"conversion_factor": 1,
"expiry_duration": 10,
"company": "_Test Company",
"cost_center": "Main - _TC",
"expense_account": "Loyalty - _TC",
"collection_rules": [
{"tier_name": "Gold", "collection_factor": 1000, "min_spent": 20000},
{"tier_name": "Silver", "collection_factor": 1000, "min_spent": 10000},
{"tier_name": "Bronze", "collection_factor": 1000, "min_spent": 0},
],
}
)
loyalty_program.insert()
# Test cases with different total_spent and current_transaction_amount combinations
test_cases = [
(0, 6000, "Bronze"),
(0, 15000, "Silver"),
(0, 25000, "Gold"),
(4000, 500, "Bronze"),
(8000, 3000, "Silver"),
(18000, 3000, "Gold"),
(22000, 5000, "Gold"),
]
for total_spent, current_transaction_amount, expected_tier in test_cases:
with self.subTest(total_spent=total_spent, current_transaction_amount=current_transaction_amount):
# Mock the get_loyalty_details function to update the total_spent
def side_effect(*args, **kwargs):
result = get_loyalty_details(*args, **kwargs)
result.update({"total_spent": total_spent})
return result
mock_get_loyalty_details.side_effect = side_effect
lp_details = get_loyalty_program_details_with_points(
"Test Loyalty Customer",
loyalty_program=loyalty_program.name,
company="_Test Company",
current_transaction_amount=current_transaction_amount,
)
# Get the selected tier based on the current implementation
selected_tier = lp_details.tier_name
self.assertEqual(
selected_tier,
expected_tier,
f"Expected tier {expected_tier} for total_spent {total_spent} and current_transaction_amount {current_transaction_amount}, but got {selected_tier}",
)
# Clean up
loyalty_program.delete()
def get_points_earned(self):
def get_returned_amount():
@@ -285,7 +352,7 @@ def create_records():
"company": "_Test Company",
"cost_center": "Main - _TC",
"expense_account": "Loyalty - _TC",
"collection_rules": [{"tier_name": "Silver", "collection_factor": 1000, "min_spent": 1000}],
"collection_rules": [{"tier_name": "Bronce", "collection_factor": 1000, "min_spent": 0}],
}
).insert()
@@ -316,6 +383,7 @@ def create_records():
"cost_center": "Main - _TC",
"expense_account": "Loyalty - _TC",
"collection_rules": [
{"tier_name": "Bronze", "collection_factor": 1000, "min_spent": 0},
{"tier_name": "Silver", "collection_factor": 1000, "min_spent": 10000},
{"tier_name": "Gold", "collection_factor": 1000, "min_spent": 19000},
],

View File

@@ -11,6 +11,7 @@
],
"fields": [
{
"columns": 3,
"fieldname": "tier_name",
"fieldtype": "Data",
"in_list_view": 1,
@@ -18,8 +19,10 @@
"reqd": 1
},
{
"columns": 3,
"fieldname": "min_spent",
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Minimum Total Spent"
},
{
@@ -27,6 +30,7 @@
"fieldtype": "Column Break"
},
{
"columns": 3,
"description": "For how much spent = 1 Loyalty Point",
"fieldname": "collection_factor",
"fieldtype": "Currency",
@@ -37,7 +41,7 @@
],
"istable": 1,
"links": [],
"modified": "2024-03-27 13:10:03.536071",
"modified": "2024-09-06 09:26:03.323912",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Loyalty Program Collection",

View File

@@ -48,7 +48,7 @@
"idx": 1,
"index_web_pages_for_search": 1,
"links": [],
"modified": "2024-03-27 13:10:05.597711",
"modified": "2024-08-16 19:22:42.942264",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Mode of Payment",
@@ -74,5 +74,6 @@
"show_name_in_global_search": 1,
"sort_field": "creation",
"sort_order": "ASC",
"states": []
"states": [],
"translated_doctype": 1
}

View File

@@ -35,6 +35,11 @@ frappe.ui.form.on("Payment Entry", {
var account_types = ["Pay", "Internal Transfer"].includes(frm.doc.payment_type)
? ["Bank", "Cash"]
: [frappe.boot.party_account_types[frm.doc.party_type]];
if (frm.doc.party_type == "Shareholder") {
account_types.push("Equity");
}
return {
filters: {
account_type: ["in", account_types],
@@ -90,6 +95,9 @@ frappe.ui.form.on("Payment Entry", {
var account_types = ["Receive", "Internal Transfer"].includes(frm.doc.payment_type)
? ["Bank", "Cash"]
: [frappe.boot.party_account_types[frm.doc.party_type]];
if (frm.doc.party_type == "Shareholder") {
account_types.push("Equity");
}
return {
filters: {
account_type: ["in", account_types],
@@ -165,8 +173,25 @@ frappe.ui.form.on("Payment Entry", {
filters: filters,
};
});
},
frm.set_query("sales_taxes_and_charges_template", function () {
return {
filters: {
company: frm.doc.company,
disabled: false,
},
};
});
frm.set_query("purchase_taxes_and_charges_template", function () {
return {
filters: {
company: frm.doc.company,
disabled: false,
},
};
});
},
refresh: function (frm) {
erpnext.hide_company(frm);
frm.events.hide_unhide_fields(frm);
@@ -213,7 +238,7 @@ frappe.ui.form.on("Payment Entry", {
hide_unhide_fields: function (frm) {
var company_currency = frm.doc.company
? frappe.get_doc(":Company", frm.doc.company).default_currency
? frappe.get_doc(":Company", frm.doc.company)?.default_currency
: "";
frm.toggle_display(
@@ -280,7 +305,7 @@ frappe.ui.form.on("Payment Entry", {
set_dynamic_labels: function (frm) {
var company_currency = frm.doc.company
? frappe.get_doc(":Company", frm.doc.company).default_currency
? frappe.get_doc(":Company", frm.doc.company)?.default_currency
: "";
frm.set_currency_labels(
@@ -395,6 +420,12 @@ frappe.ui.form.on("Payment Entry", {
return {
query: "erpnext.controllers.queries.employee_query",
};
} else if (frm.doc.party_type == "Shareholder") {
return {
filters: {
company: frm.doc.company,
},
};
}
});
@@ -627,7 +658,7 @@ frappe.ui.form.on("Payment Entry", {
frm.set_value("source_exchange_rate", 1);
} else if (frm.doc.paid_from) {
if (["Internal Transfer", "Pay"].includes(frm.doc.payment_type)) {
let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
let company_currency = frappe.get_doc(":Company", frm.doc.company)?.default_currency;
frappe.call({
method: "erpnext.setup.utils.get_exchange_rate",
args: {

View File

@@ -37,7 +37,6 @@ from erpnext.accounts.utils import (
get_account_currency,
get_balance_on,
get_outstanding_invoices,
get_party_types_from_account_type,
)
from erpnext.controllers.accounts_controller import (
AccountsController,
@@ -1214,90 +1213,82 @@ class PaymentEntry(AccountsController):
self.make_advance_gl_entries(cancel=cancel)
def add_party_gl_entries(self, gl_entries):
if self.party_account:
if self.payment_type == "Receive":
against_account = self.paid_to
else:
against_account = self.paid_from
if not self.party_account:
return
party_gl_dict = self.get_gl_dict(
if self.payment_type == "Receive":
against_account = self.paid_to
else:
against_account = self.paid_from
party_account_type = frappe.db.get_value("Party Type", self.party_type, "account_type")
party_gl_dict = self.get_gl_dict(
{
"account": self.party_account,
"party_type": self.party_type,
"party": self.party,
"against": against_account,
"account_currency": self.party_account_currency,
"cost_center": self.cost_center,
},
item=self,
)
for d in self.get("references"):
# re-defining dr_or_cr for every reference in order to avoid the last value affecting calculation of reverse
dr_or_cr = "credit" if self.payment_type == "Receive" else "debit"
cost_center = self.cost_center
if d.reference_doctype == "Sales Invoice" and not cost_center:
cost_center = frappe.db.get_value(d.reference_doctype, d.reference_name, "cost_center")
gle = party_gl_dict.copy()
allocated_amount_in_company_currency = self.calculate_base_allocated_amount_for_reference(d)
if (
d.reference_doctype in ["Sales Invoice", "Purchase Invoice"]
and d.allocated_amount < 0
and (
(party_account_type == "Receivable" and self.payment_type == "Pay")
or (party_account_type == "Payable" and self.payment_type == "Receive")
)
):
# reversing dr_cr because because it will get reversed in gl processing due to negative amount
dr_or_cr = "debit" if dr_or_cr == "credit" else "credit"
gle.update(
{
"account": self.party_account,
"party_type": self.party_type,
"party": self.party,
"against": against_account,
"account_currency": self.party_account_currency,
"cost_center": self.cost_center,
},
item=self,
dr_or_cr: allocated_amount_in_company_currency,
dr_or_cr + "_in_account_currency": d.allocated_amount,
"against_voucher_type": d.reference_doctype,
"against_voucher": d.reference_name,
"cost_center": cost_center,
}
)
gl_entries.append(gle)
if self.unallocated_amount:
dr_or_cr = "credit" if self.payment_type == "Receive" else "debit"
exchange_rate = self.get_exchange_rate()
base_unallocated_amount = self.unallocated_amount * exchange_rate
gle = party_gl_dict.copy()
gle.update(
{
dr_or_cr + "_in_account_currency": self.unallocated_amount,
dr_or_cr: base_unallocated_amount,
}
)
dr_or_cr = "credit" if self.payment_type == "Receive" else "debit"
for d in self.get("references"):
# re-defining dr_or_cr for every reference in order to avoid the last value affecting calculation of reverse
dr_or_cr = "credit" if self.payment_type == "Receive" else "debit"
cost_center = self.cost_center
if d.reference_doctype == "Sales Invoice" and not cost_center:
cost_center = frappe.db.get_value(d.reference_doctype, d.reference_name, "cost_center")
gle = party_gl_dict.copy()
allocated_amount_in_company_currency = self.calculate_base_allocated_amount_for_reference(d)
reverse_dr_or_cr = 0
if d.reference_doctype in ["Sales Invoice", "Purchase Invoice"]:
is_return = frappe.db.get_value(d.reference_doctype, d.reference_name, "is_return")
payable_party_types = get_party_types_from_account_type("Payable")
receivable_party_types = get_party_types_from_account_type("Receivable")
if (
is_return
and self.party_type in receivable_party_types
and (self.payment_type == "Pay")
):
reverse_dr_or_cr = 1
elif (
is_return
and self.party_type in payable_party_types
and (self.payment_type == "Receive")
):
reverse_dr_or_cr = 1
if is_return and not reverse_dr_or_cr:
dr_or_cr = "debit" if dr_or_cr == "credit" else "credit"
if self.book_advance_payments_in_separate_party_account:
gle.update(
{
dr_or_cr: abs(allocated_amount_in_company_currency),
dr_or_cr + "_in_account_currency": abs(d.allocated_amount),
"against_voucher_type": d.reference_doctype,
"against_voucher": d.reference_name,
"cost_center": cost_center,
"against_voucher_type": "Payment Entry",
"against_voucher": self.name,
}
)
gl_entries.append(gle)
if self.unallocated_amount:
dr_or_cr = "credit" if self.payment_type == "Receive" else "debit"
exchange_rate = self.get_exchange_rate()
base_unallocated_amount = self.unallocated_amount * exchange_rate
gle = party_gl_dict.copy()
gle.update(
{
dr_or_cr + "_in_account_currency": self.unallocated_amount,
dr_or_cr: base_unallocated_amount,
}
)
if self.book_advance_payments_in_separate_party_account:
gle.update(
{
"against_voucher_type": "Payment Entry",
"against_voucher": self.name,
}
)
gl_entries.append(gle)
gl_entries.append(gle)
def make_advance_gl_entries(
self, entry: object | dict = None, cancel: bool = 0, update_outstanding: str = "Yes"
@@ -1847,7 +1838,7 @@ def get_outstanding_reference_documents(args, validate=False):
d["bill_no"] = frappe.db.get_value(d.voucher_type, d.voucher_no, "bill_no")
# Get negative outstanding sales /purchase invoices
if args.get("party_type") != "Employee" and not args.get("voucher_no"):
if args.get("party_type") != "Employee":
negative_outstanding_invoices = get_negative_outstanding_invoices(
args.get("party_type"),
args.get("party"),

View File

@@ -36,7 +36,7 @@ frappe.ui.form.on("Payment Order", {
// payment Entry
if (frm.doc.docstatus === 1 && frm.doc.payment_order_type === "Payment Request") {
frm.add_custom_button(__("Create Payment Entries"), function () {
frm.add_custom_button(__("Create Journal Entries"), function () {
frm.trigger("make_payment_records");
});
}

View File

@@ -10,6 +10,7 @@
"failed_reason",
"column_break_2",
"naming_series",
"company",
"mode_of_payment",
"party_details",
"party_type",
@@ -49,13 +50,14 @@
"message",
"message_examples",
"mute_email",
"payment_url",
"section_break_7",
"payment_gateway",
"payment_account",
"payment_channel",
"payment_order",
"amended_from"
"amended_from",
"column_break_pnyv",
"payment_url"
],
"fields": [
{
@@ -342,7 +344,7 @@
{
"fieldname": "payment_url",
"fieldtype": "Data",
"hidden": 1,
"label": "Payment URL",
"length": 500,
"options": "URL",
"read_only": 1
@@ -400,13 +402,24 @@
"no_copy": 1,
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "company",
"fieldtype": "Link",
"label": "Company",
"options": "Company",
"read_only": 1
},
{
"fieldname": "column_break_pnyv",
"fieldtype": "Column Break"
}
],
"in_create": 1,
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2024-06-20 13:54:55.245774",
"modified": "2024-08-07 16:39:54.288002",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Request",

View File

@@ -16,7 +16,7 @@ from erpnext.accounts.doctype.payment_entry.payment_entry import (
)
from erpnext.accounts.doctype.subscription_plan.subscription_plan import get_plan_rate
from erpnext.accounts.party import get_party_account, get_party_bank_account
from erpnext.accounts.utils import get_account_currency
from erpnext.accounts.utils import get_account_currency, get_currency_precision
from erpnext.utilities import payment_app_import_guard
@@ -85,6 +85,7 @@ class PaymentRequest(Document):
subscription_plans: DF.Table[SubscriptionPlanDetail]
swift_number: DF.ReadOnly | None
transaction_date: DF.Date | None
company: DF.Link | None
# end: auto-generated types
def validate(self):
@@ -331,6 +332,17 @@ class PaymentRequest(Document):
payment_entry.received_amount = amount
payment_entry.get("references")[0].allocated_amount = amount
# Update 'Paid Amount' on Forex transactions
if self.currency != ref_doc.company_currency:
if (
self.payment_request_type == "Outward"
and payment_entry.paid_from_account_currency == ref_doc.company_currency
and payment_entry.paid_from_account_currency != payment_entry.paid_to_account_currency
):
payment_entry.paid_amount = payment_entry.base_paid_amount = (
payment_entry.target_exchange_rate * payment_entry.received_amount
)
for dimension in get_accounting_dimensions():
payment_entry.update({dimension: self.get(dimension)})
@@ -420,7 +432,9 @@ def make_payment_request(**args):
"""Make payment request"""
args = frappe._dict(args)
if args.dt not in [
ref_doc = args.ref_doc or frappe.get_doc(args.dt, args.dn)
if ref_doc.doctype not in [
"Sales Order",
"Purchase Order",
"Sales Invoice",
@@ -428,22 +442,21 @@ def make_payment_request(**args):
"POS Invoice",
"Fees",
]:
frappe.throw(_("Payment Requests cannot be created against: {0}").format(frappe.bold(args.dt)))
frappe.throw(
_("Payment Requests cannot be created against: {0}").format(frappe.bold(ref_doc.doctype))
)
ref_doc = frappe.get_doc(args.dt, args.dn)
gateway_account = get_gateway_details(args) or frappe._dict()
grand_total = get_amount(ref_doc, gateway_account.get("payment_account"))
if not grand_total:
frappe.throw(_("Payment Entry is already created"))
if args.loyalty_points and args.dt == "Sales Order":
if args.loyalty_points and ref_doc.doctype == "Sales Order":
from erpnext.accounts.doctype.loyalty_program.loyalty_program import validate_loyalty_points
loyalty_amount = validate_loyalty_points(ref_doc, int(args.loyalty_points))
frappe.db.set_value(
"Sales Order", args.dn, "loyalty_points", int(args.loyalty_points), update_modified=False
)
frappe.db.set_value("Sales Order", args.dn, "loyalty_amount", loyalty_amount, update_modified=False)
loyalty_amount = validate_loyalty_points(ref_doc, int(args.loyalty_points)) # sets fields on ref_doc
ref_doc.db_update()
grand_total = grand_total - loyalty_amount
bank_account = (
@@ -452,10 +465,10 @@ def make_payment_request(**args):
draft_payment_request = frappe.db.get_value(
"Payment Request",
{"reference_doctype": args.dt, "reference_name": args.dn, "docstatus": 0},
{"reference_doctype": ref_doc.doctype, "reference_name": ref_doc.name, "docstatus": 0},
)
existing_payment_request_amount = get_existing_payment_request_amount(args.dt, args.dn)
existing_payment_request_amount = get_existing_payment_request_amount(ref_doc.doctype, ref_doc.name)
if existing_payment_request_amount:
grand_total -= existing_payment_request_amount
@@ -486,15 +499,21 @@ def make_payment_request(**args):
"email_to": args.recipient_id or ref_doc.owner,
"subject": _("Payment Request for {0}").format(args.dn),
"message": gateway_account.get("message") or get_dummy_message(ref_doc),
"reference_doctype": args.dt,
"reference_name": args.dn,
"reference_doctype": ref_doc.doctype,
"reference_name": ref_doc.name,
"company": ref_doc.get("company"),
"party_type": args.get("party_type") or "Customer",
"party": args.get("party") or ref_doc.get("customer"),
"bank_account": bank_account,
"make_sales_invoice": args.order_type == "Shopping Cart",
"mute_email": args.mute_email
or args.order_type == "Shopping Cart"
or gateway_account.get("payment_channel", "Email") != "Email",
"make_sales_invoice": (
args.make_sales_invoice # new standard
or args.order_type == "Shopping Cart" # compat for webshop app
),
"mute_email": (
args.mute_email # new standard
or args.order_type == "Shopping Cart" # compat for webshop app
or gateway_account.get("payment_channel", "Email") != "Email"
),
}
)
@@ -509,7 +528,8 @@ def make_payment_request(**args):
for dimension in get_accounting_dimensions():
pr.update({dimension: ref_doc.get(dimension)})
pr.insert(ignore_permissions=True)
if frappe.db.get_single_value("Accounts Settings", "create_pr_in_draft_status", cache=True):
pr.insert(ignore_permissions=True)
if args.submit_doc:
pr.submit()
@@ -549,7 +569,10 @@ def get_amount(ref_doc, payment_account=None):
elif dt == "Fees":
grand_total = ref_doc.outstanding_amount
return grand_total
if grand_total > 0:
return flt(grand_total, get_currency_precision())
else:
frappe.throw(_("Payment Entry is already created"))
def get_existing_payment_request_amount(ref_dt, ref_dn):

View File

@@ -5,10 +5,12 @@ import unittest
from unittest.mock import patch
import frappe
from frappe.tests.utils import FrappeTestCase
from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.buying.doctype.purchase_order.test_purchase_order import create_purchase_order
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
from erpnext.setup.utils import get_exchange_rate
@@ -53,7 +55,7 @@ payment_method = [
]
class TestPaymentRequest(unittest.TestCase):
class TestPaymentRequest(FrappeTestCase):
def setUp(self):
for payment_gateway in payment_gateways:
if not frappe.db.get_value("Payment Gateway", payment_gateway["gateway"], "name"):
@@ -86,6 +88,9 @@ class TestPaymentRequest(unittest.TestCase):
self._get_payment_gateway_controller = _get_payment_gateway_controller.start()
self.addCleanup(_get_payment_gateway_controller.stop)
def tearDown(self):
frappe.db.rollback()
def test_payment_request_linkings(self):
so_inr = make_sales_order(currency="INR", do_not_save=True)
so_inr.disable_rounded_total = 1
@@ -194,7 +199,8 @@ class TestPaymentRequest(unittest.TestCase):
dt="Sales Order",
dn=so.name,
payment_gateway_account="_Test Gateway - USD", # email channel
order_type="Shopping Cart",
make_sales_invoice=True,
mute_email=True,
submit_doc=True,
return_doc=True,
)
@@ -393,3 +399,19 @@ class TestPaymentRequest(unittest.TestCase):
# Try to make Payment Request more than SO amount, should give validation
pr2.grand_total = 900
self.assertRaises(frappe.ValidationError, pr2.save)
def test_conversion_on_foreign_currency_accounts(self):
po_doc = create_purchase_order(supplier="_Test Supplier USD", currency="USD", do_not_submit=1)
po_doc.conversion_rate = 80
po_doc.items[0].qty = 1
po_doc.items[0].rate = 10
po_doc.save().submit()
pr = make_payment_request(dt=po_doc.doctype, dn=po_doc.name, recipient_id="nabin@erpnext.com")
pr = frappe.get_doc(pr).save().submit()
pe = pr.create_payment_entry()
self.assertEqual(pe.base_paid_amount, 800)
self.assertEqual(pe.paid_amount, 800)
self.assertEqual(pe.base_received_amount, 800)
self.assertEqual(pe.received_amount, 10)

View File

@@ -87,19 +87,15 @@ class POSClosingEntry(StatusUpdater):
as_dict=1,
)[0]
if pos_invoice.consolidated_invoice:
invalid_row.setdefault("msg", []).append(
_("POS Invoice is {}").format(frappe.bold("already consolidated"))
)
invalid_row.setdefault("msg", []).append(_("POS Invoice is already consolidated"))
invalid_rows.append(invalid_row)
continue
if pos_invoice.pos_profile != self.pos_profile:
invalid_row.setdefault("msg", []).append(
_("POS Profile doesn't matches {}").format(frappe.bold(self.pos_profile))
_("POS Profile doesn't match {}").format(frappe.bold(self.pos_profile))
)
if pos_invoice.docstatus != 1:
invalid_row.setdefault("msg", []).append(
_("POS Invoice is not {}").format(frappe.bold("submitted"))
)
invalid_row.setdefault("msg", []).append(_("POS Invoice is not submitted"))
if pos_invoice.owner != self.user:
invalid_row.setdefault("msg", []).append(
_("POS Invoice isn't created by user {}").format(frappe.bold(self.owner))

View File

@@ -188,7 +188,7 @@ class POSInvoice(SalesInvoice):
def validate(self):
if not cint(self.is_pos):
frappe.throw(
_("POS Invoice should have {} field checked.").format(frappe.bold("Include Payment"))
_("POS Invoice should have the field {0} checked.").format(frappe.bold(_("Include Payment")))
)
# run on validate method of selling controller

View File

@@ -37,6 +37,7 @@
"column_break_19",
"discount_percentage",
"discount_amount",
"distributed_discount_amount",
"base_rate_with_margin",
"section_break1",
"rate",
@@ -847,6 +848,12 @@
{
"fieldname": "column_break_ciit",
"fieldtype": "Column Break"
},
{
"fieldname": "distributed_discount_amount",
"fieldtype": "Currency",
"label": "Distributed Discount Amount",
"options": "currency"
}
],
"istable": 1,

View File

@@ -39,6 +39,7 @@ class POSInvoiceItem(SalesInvoiceItem):
description: DF.TextEditor
discount_amount: DF.Currency
discount_percentage: DF.Percent
distributed_discount_amount: DF.Currency
dn_detail: DF.Data | None
enable_deferred_revenue: DF.Check
expense_account: DF.Link | None

View File

@@ -97,16 +97,15 @@ class POSInvoiceMergeLog(Document):
return_against_status = frappe.db.get_value("POS Invoice", return_against, "status")
if return_against_status != "Consolidated":
# if return entry is not getting merged in the current pos closing and if it is not consolidated
bold_unconsolidated = frappe.bold("not Consolidated")
msg = _("Row #{}: Original Invoice {} of return invoice {} is {}.").format(
d.idx, bold_return_against, bold_pos_invoice, bold_unconsolidated
)
msg = _(
"Row #{}: The original Invoice {} of return invoice {} is not consolidated."
).format(d.idx, bold_return_against, bold_pos_invoice)
msg += " "
msg += _(
"Original invoice should be consolidated before or along with the return invoice."
"The original invoice should be consolidated before or along with the return invoice."
)
msg += "<br><br>"
msg += _("You can add original invoice {} manually to proceed.").format(
msg += _("You can add the original invoice {} manually to proceed.").format(
bold_return_against
)
frappe.throw(msg)

View File

@@ -186,7 +186,8 @@ class PricingRule(Document):
if not self.priority:
throw(
_("As the field {0} is enabled, the field {1} is mandatory.").format(
frappe.bold("Apply Discount on Discounted Rate"), frappe.bold("Priority")
frappe.bold(_("Apply Discount on Discounted Rate")),
frappe.bold(_("Priority")),
)
)
@@ -194,7 +195,7 @@ class PricingRule(Document):
throw(
_(
"As the field {0} is enabled, the value of the field {1} should be more than 1."
).format(frappe.bold("Apply Discount on Discounted Rate"), frappe.bold("Priority"))
).format(frappe.bold(_("Apply Discount on Discounted Rate")), frappe.bold(_("Priority")))
)
def validate_applicable_for_selling_or_buying(self):

View File

@@ -6,7 +6,9 @@ import unittest
import frappe
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.controllers.sales_and_purchase_return import make_return_doc
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
from erpnext.stock.doctype.item.test_item import make_item
from erpnext.stock.get_item_details import get_item_details
@@ -1311,6 +1313,69 @@ class TestPricingRule(unittest.TestCase):
pricing_rule.is_recursive = True
self.assertRaises(frappe.ValidationError, pricing_rule.save)
def test_ignore_pricing_rule_for_credit_note(self):
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule")
pricing_rule = make_pricing_rule(
discount_percentage=20,
selling=1,
buying=1,
priority=1,
title="_Test Pricing Rule",
)
si = create_sales_invoice(do_not_submit=True, customer="_Test Customer 1", qty=1)
item = si.items[0]
si.submit()
self.assertEqual(item.discount_percentage, 20)
self.assertEqual(item.rate, 80)
# change discount on pricing rule
pricing_rule.discount_percentage = 30
pricing_rule.save()
credit_note = make_return_doc(si.doctype, si.name)
credit_note.save()
self.assertEqual(credit_note.ignore_pricing_rule, 1)
self.assertEqual(credit_note.pricing_rules, [])
self.assertEqual(credit_note.items[0].discount_percentage, 20)
self.assertEqual(credit_note.items[0].rate, 80)
self.assertEqual(credit_note.items[0].pricing_rules, None)
credit_note.delete()
si.cancel()
def test_ignore_pricing_rule_for_debit_note(self):
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule")
pricing_rule = make_pricing_rule(
discount_percentage=20,
buying=1,
priority=1,
title="_Test Pricing Rule",
)
pi = make_purchase_invoice(do_not_submit=True, supplier="_Test Supplier 1", qty=1)
item = pi.items[0]
pi.submit()
self.assertEqual(item.discount_percentage, 20)
self.assertEqual(item.rate, 40)
# change discount on pricing rule
pricing_rule.discount_percentage = 30
pricing_rule.save()
# create debit note from purchase invoice
debit_note = make_return_doc(pi.doctype, pi.name)
debit_note.save()
self.assertEqual(debit_note.ignore_pricing_rule, 1)
self.assertEqual(debit_note.pricing_rules, [])
self.assertEqual(debit_note.items[0].discount_percentage, 20)
self.assertEqual(debit_note.items[0].rate, 40)
self.assertEqual(debit_note.items[0].pricing_rules, None)
debit_note.delete()
pi.cancel()
test_dependencies = ["Campaign"]

View File

@@ -486,7 +486,7 @@ def get_qty_and_rate_for_other_item(doc, pr_doc, pricing_rules, row_item):
continue
stock_qty = row.get("qty") * (row.get("conversion_factor") or 1.0)
amount = stock_qty * (row.get("price_list_rate") or row.get("rate"))
amount = stock_qty * (flt(row.get("price_list_rate")) or flt(row.get("rate")))
pricing_rules = filter_pricing_rules_for_qty_amount(stock_qty, amount, pricing_rules, row)
if pricing_rules and pricing_rules[0]:
@@ -731,7 +731,7 @@ def validate_coupon_code(coupon_name):
elif coupon.valid_upto:
if coupon.valid_upto < getdate(today()):
frappe.throw(_("Sorry, this coupon code's validity has expired"))
elif coupon.used >= coupon.maximum_use:
elif coupon.maximum_use and coupon.used >= coupon.maximum_use:
frappe.throw(_("Sorry, this coupon code is no longer valid"))
@@ -739,7 +739,10 @@ def update_coupon_code_count(coupon_name, transaction_type):
coupon = frappe.get_doc("Coupon Code", coupon_name)
if coupon:
if transaction_type == "used":
if coupon.used < coupon.maximum_use:
if not coupon.maximum_use:
coupon.used = coupon.used + 1
coupon.save(ignore_permissions=True)
elif coupon.used < coupon.maximum_use:
coupon.used = coupon.used + 1
coupon.save(ignore_permissions=True)
else:

View File

@@ -20,6 +20,17 @@ frappe.ui.form.on("Process Payment Reconciliation", {
},
};
});
frm.set_query("default_advance_account", function (doc) {
return {
filters: {
company: doc.company,
is_group: 0,
account_type: doc.party_type == "Customer" ? "Receivable" : "Payable",
root_type: doc.party_type == "Customer" ? "Liability" : "Asset",
},
};
});
frm.set_query("cost_center", function (doc) {
return {
filters: {
@@ -102,6 +113,7 @@ frappe.ui.form.on("Process Payment Reconciliation", {
company(frm) {
frm.set_value("party", "");
frm.set_value("receivable_payable_account", "");
frm.set_value("default_advance_account", "");
},
party_type(frm) {
frm.set_value("party", "");
@@ -109,6 +121,7 @@ frappe.ui.form.on("Process Payment Reconciliation", {
party(frm) {
frm.set_value("receivable_payable_account", "");
frm.set_value("default_advance_account", "");
if (!frm.doc.receivable_payable_account && frm.doc.party_type && frm.doc.party) {
return frappe.call({
method: "erpnext.accounts.party.get_party_account",
@@ -116,10 +129,16 @@ frappe.ui.form.on("Process Payment Reconciliation", {
company: frm.doc.company,
party_type: frm.doc.party_type,
party: frm.doc.party,
include_advance: 1,
},
callback: (r) => {
if (!r.exc && r.message) {
frm.set_value("receivable_payable_account", r.message);
if (typeof r.message === "string") {
frm.set_value("receivable_payable_account", r.message);
} else if (Array.isArray(r.message)) {
frm.set_value("receivable_payable_account", r.message[0]);
frm.set_value("default_advance_account", r.message[1]);
}
}
frm.refresh();
},

View File

@@ -13,6 +13,7 @@
"column_break_io6c",
"party",
"receivable_payable_account",
"default_advance_account",
"filter_section",
"from_invoice_date",
"to_invoice_date",
@@ -141,12 +142,23 @@
{
"fieldname": "section_break_a8yx",
"fieldtype": "Section Break"
},
{
"depends_on": "eval:doc.party",
"description": "Only 'Payment Entries' made against this advance account are supported.",
"documentation_url": "https://docs.erpnext.com/docs/user/manual/en/advance-in-separate-party-account",
"fieldname": "default_advance_account",
"fieldtype": "Link",
"label": "Default Advance Account",
"mandatory_depends_on": "doc.party_type",
"options": "Account",
"reqd": 1
}
],
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2024-03-27 13:10:18.601732",
"modified": "2024-08-27 14:48:56.715320",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Process Payment Reconciliation",
@@ -180,4 +192,4 @@
"sort_order": "DESC",
"states": [],
"title_field": "company"
}
}

View File

@@ -23,6 +23,7 @@ class ProcessPaymentReconciliation(Document):
bank_cash_account: DF.Link | None
company: DF.Link
cost_center: DF.Link | None
default_advance_account: DF.Link
error_log: DF.LongText | None
from_invoice_date: DF.Date | None
from_payment_date: DF.Date | None
@@ -101,6 +102,7 @@ def get_pr_instance(doc: str):
"party_type",
"party",
"receivable_payable_account",
"default_advance_account",
"from_invoice_date",
"to_invoice_date",
"from_payment_date",

View File

@@ -13,7 +13,7 @@
</div>
{% endif %}
</div>
<h2 class="text-center">{{ _("STATEMENTS OF ACCOUNTS") }}</h2>
<h2 class="text-center">{{ _("STATEMENT OF ACCOUNTS") }}</h2>
<div>
{% if filters.party[0] == filters.party_name[0] %}
<h5 style="float: left;">{{ _("Customer: ") }} <b>{{ filters.party_name[0] }}</b></h5>
@@ -22,11 +22,11 @@
<h5 style="float: left; margin-left:15px">{{ _("Customer Name: ") }} <b>{{filters.party_name[0] }}</b></h5>
{% endif %}
<h5 style="float: right;">
{{ _("Date: ") }}
<b>{{ frappe.format(filters.from_date, 'Date')}}
{{ _("to") }}
{{ frappe.format(filters.to_date, 'Date')}}</b>
</h5>
{{ _("Date: {0} to {1}").format(
frappe.format(filters.from_date, "Date"),
frappe.format(filters.to_date, 'Date')
) }}
</h5>
</div>
<br>
@@ -54,7 +54,7 @@
<br>
{% endif %}
<br>{{ _("Remarks") }}: {{ row.remarks }}
<br>{{ _("Remarks:") }} {{ row.remarks }}
{% if row.bill_no %}
<br>{{ _("Supplier Invoice No") }}: {{ row.bill_no }}
{% endif %}
@@ -83,17 +83,20 @@
</table>
<br>
{% if ageing %}
<h4 class="text-center">{{ _("Ageing Report based on ") }} {{ ageing.ageing_based_on }}
{{ _("up to " ) }} {{ frappe.format(filters.to_date, 'Date')}}
<h4 class="text-center">
{{ _("Ageing Report based on {0} up to {1}").format(
ageing.ageing_based_on,
frappe.format(filters.to_date, "Date")
) }}
</h4>
<table class="table table-bordered">
<thead>
<tr>
<th style="width: 20%">0 - 30 Days</th>
<th style="width: 20%">30 - 60 Days</th>
<th style="width: 20%">60 - 90 Days</th>
<th style="width: 20%">90 - 120 Days</th>
<th style="width: 20%">Above 120 Days</th>
<th style="width: 20%">{{ _("0 - 30 Days") }}</th>
<th style="width: 20%">{{ _("30 - 60 Days") }}</th>
<th style="width: 20%">{{ _("60 - 90 Days") }}</th>
<th style="width: 20%">{{ _("90 - 120 Days") }}</th>
<th style="width: 20%">{{ _("Above 120 Days") }}</th>
</tr>
</thead>
<tbody>

View File

@@ -16,6 +16,7 @@
"cost_center",
"territory",
"ignore_exchange_rate_revaluation_journals",
"ignore_cr_dr_notes",
"column_break_14",
"to_date",
"finance_book",
@@ -383,10 +384,16 @@
"fieldname": "ignore_exchange_rate_revaluation_journals",
"fieldtype": "Check",
"label": "Ignore Exchange Rate Revaluation Journals"
},
{
"default": "0",
"fieldname": "ignore_cr_dr_notes",
"fieldtype": "Check",
"label": "Ignore System Generated Credit / Debit Notes"
}
],
"links": [],
"modified": "2024-03-27 13:10:19.075913",
"modified": "2024-08-13 10:41:18.381165",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Process Statement Of Accounts",

View File

@@ -54,6 +54,7 @@ class ProcessStatementOfAccounts(Document):
frequency: DF.Literal["Weekly", "Monthly", "Quarterly"]
from_date: DF.Date | None
group_by: DF.Literal["", "Group by Voucher", "Group by Voucher (Consolidated)"]
ignore_cr_dr_notes: DF.Check
ignore_exchange_rate_revaluation_journals: DF.Check
include_ageing: DF.Check
include_break: DF.Check
@@ -133,6 +134,9 @@ def get_statement_dict(doc, get_statement_dict=False):
if doc.ignore_exchange_rate_revaluation_journals:
filters.update({"ignore_err": True})
if doc.ignore_cr_dr_notes:
filters.update({"ignore_cr_dr_notes": True})
if doc.report == "General Ledger":
filters.update(get_gl_filters(doc, entry, tax_id, presentation_currency))
col, res = get_soa(filters)

View File

@@ -6,228 +6,302 @@
.print-format td {
vertical-align:middle !important;
}
</style>
</style>
<div id="header-html" class="hidden-pdf">
{% if letter_head.content %}
<div class="letter-head text-center">{{ letter_head.content }}</div>
<hr style="height:2px;border-width:0;color:black;background-color:black;">
{% endif %}
</div>
<div id="footer-html" class="visible-pdf">
{% if letter_head.footer %}
<div class="letter-head-footer">
<hr style="border-width:0;color:black;background-color:black;padding-bottom:2px;">
{{ letter_head.footer }}
</div>
{% endif %}
</div>
<h2 class="text-center" style="margin-top:0">{{ _(report.report_name) }}</h2>
<h4 class="text-center">
{{ filters.customer_name }}
</h4>
<h6 class="text-center">
{% if (filters.tax_id) %}
{{ _("Tax Id: ") }}{{ filters.tax_id }}
{% endif %}
</h6>
<h5 class="text-center">
{{ _(filters.ageing_based_on) }}
{{ _("Until") }}
{{ frappe.format(filters.report_date, 'Date') }}
</h5>
<div class="clearfix">
<div class="pull-left">
{% if(filters.payment_terms) %}
<strong>{{ _("Payment Terms") }}:</strong> {{ filters.payment_terms }}
{% endif %}
</div>
<div class="pull-right">
{% if(filters.credit_limit) %}
<strong>{{ _("Credit Limit") }}:</strong> {{ frappe.utils.fmt_money(filters.credit_limit) }}
{% endif %}
</div>
</div>
{% if(filters.show_future_payments) %}
{% set balance_row = data.slice(-1).pop() %}
{% for i in report.columns %}
{% if i.fieldname == 'age' %}
{% set elem = i %}
{% endif %}
{% endfor %}
{% set start = report.columns.findIndex(elem) %}
{% set range1 = report.columns[start].label %}
{% set range2 = report.columns[start+1].label %}
{% set range3 = report.columns[start+2].label %}
{% set range4 = report.columns[start+3].label %}
{% set range5 = report.columns[start+4].label %}
{% set range6 = report.columns[start+5].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>{{ _(range6) }}</th>
<th>{{ _("Total") }}</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{ _("Total Outstanding") }}</td>
<td class="text-right">
{{ format_number(balance_row["age"], null, 2) }}
</td>
<td class="text-right">
{{ frappe.utils.fmt_money(balance_row["range1"], data[data.length-1]["currency"]) }}
</td>
<td class="text-right">
{{ frappe.utils.fmt_money(balance_row["range2"], data[data.length-1]["currency"]) }}
</td>
<td class="text-right">
{{ frappe.utils.fmt_money(balance_row["range3"], data[data.length-1]["currency"]) }}
</td>
<td class="text-right">
{{ frappe.utils.fmt_money(balance_row["range4"], data[data.length-1]["currency"]) }}
</td>
<td class="text-right">
{{ frappe.utils.fmt_money(balance_row["range5"], data[data.length-1]["currency"]) }}
</td>
<td class="text-right">
{{ frappe.utils.fmt_money(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></td>
<td class="text-right">
{{ frappe.utils.fmt_money(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></th>
<th class="text-right">
{{ frappe.utils.fmt_money(flt(balance_row["outstanding"] - balance_row[("future_amount")]), data[data.length-1]["currency"]) }}</th>
</tr>
</tbody>
</table>
{% endif %}
<div id="header-html" class="hidden-pdf">
{% if letter_head.content %}
<div class="letter-head text-center">{{ letter_head.content }}</div>
<hr style="height:2px;border-width:0;color:black;background-color:black;">
{% endif %}
<table class="table table-bordered">
</div>
<div id="footer-html" class="visible-pdf">
{% if letter_head.footer %}
<div class="letter-head-footer">
<hr style="border-width:0;color:black;background-color:black;padding-bottom:2px;">
{{ letter_head.footer }}
</div>
{% endif %}
</div>
<h2 class="text-center" style="margin-top:0">{{ _(report.report_name) }}</h2>
<h4 class="text-center">
{{ filters.customer_name }}
</h4>
<h6 class="text-center">
{% if (filters.tax_id) %}
{{ _("Tax Id: {0}").format(filters.tax_id) }}
{% endif %}
</h6>
<h5 class="text-center">
{{ _("{0} until {1}").format(
_(filters.ageing_based_on),
frappe.format(filters.report_date, 'Date')
) }}
</h5>
<div class="clearfix">
<div class="pull-left">
{% if(filters.payment_terms) %}
<strong>{{ _("Payment Terms:") }}</strong> {{ filters.payment_terms }}
{% endif %}
</div>
<div class="pull-right">
{% if(filters.credit_limit) %}
<strong>{{ _("Credit Limit:") }}</strong> {{ frappe.utils.fmt_money(filters.credit_limit) }}
{% endif %}
</div>
</div>
{% if(filters.show_future_payments) %}
{% set balance_row = data.slice(-1).pop() %}
{% for i in report.columns %}
{% if i.fieldname == 'age' %}
{% set elem = i %}
{% endif %}
{% endfor %}
{% set start = report.columns.findIndex(elem) %}
{% set range1 = report.columns[start].label %}
{% set range2 = report.columns[start+1].label %}
{% set range3 = report.columns[start+2].label %}
{% set range4 = report.columns[start+3].label %}
{% set range5 = report.columns[start+4].label %}
{% set range6 = report.columns[start+5].label %}
{% if(balance_row) %}
<table class="table table-bordered table-condensed">
<caption class="text-right">{{ _("Amount in {0}").format(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>
{% if(report.report_name == "Accounts Receivable" or report.report_name == "Accounts Payable") %}
<th style="width: 10%">{{ _("Date") }}</th>
<th style="width: 4%">{{ _("Age (Days)") }}</th>
{% if(report.report_name == "Accounts Receivable" and filters.show_sales_person) %}
<th style="width: 14%">{{ _("Reference") }}</th>
<th style="width: 10%">{{ _("Sales Person") }}</th>
{% else %}
<th style="width: 24%">{{ _("Reference") }}</th>
{% endif %}
{% if not(filters.show_future_payments) %}
<th style="width: 20%">
{% if (filters.customer or filters.supplier or filters.customer_name) %}
{{ _("Remarks") }}
{% else %}
{{ _("Party") }}
{% endif %}
</th>
{% endif %}
<th style="width: 10%; text-align: right">{{ _("Invoiced Amount") }}</th>
{% if not(filters.show_future_payments) %}
<th style="width: 10%; text-align: right">{{ _("Paid Amount") }}</th>
<th style="width: 10%; text-align: right">
{% if report.report_name == "Accounts Receivable" %}
{{ _('Credit Note') }}
{% else %}
{{ _('Debit Note') }}
{% endif %}
</th>
{% endif %}
<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>
{% endif %}
<th style="width: 10%">{{ _("Future Payment Ref") }}</th>
<th style="width: 10%">{{ _("Future Payment Amount") }}</th>
<th style="width: 10%">{{ _("Remaining Balance") }}</th>
{% endif %}
{% else %}
<th style="width: 40%">
{% if (filters.customer or filters.supplier or filters.customer_name) %}
{{ _("Remarks")}}
{% else %}
{{ _("Party") }}
{% endif %}
</th>
<th style="width: 15%">{{ _("Total Invoiced Amount") }}</th>
<th style="width: 15%">{{ _("Total Paid Amount") }}</th>
<th style="width: 15%">
{% if report.report_name == "Accounts Receivable Summary" %}
{{ _('Credit Note Amount') }}
{% else %}
{{ _('Debit Note Amount') }}
{% endif %}
</th>
<th style="width: 15%">{{ _("Total Outstanding Amount") }}</th>
{% endif %}
<th>{{ _(" ") }}</th>
<th>{{ _(range1) }}</th>
<th>{{ _(range2) }}</th>
<th>{{ _(range3) }}</th>
<th>{{ _(range4) }}</th>
<th>{{ _(range5) }}</th>
<th>{{ _(range6) }}</th>
<th>{{ _("Total") }}</th>
</tr>
</thead>
<tbody>
{% for i in range(data|length) %}
<tr>
{% if(report.report_name == "Accounts Receivable" or report.report_name == "Accounts Payable") %}
{% if(data[i]["party"]) %}
<td>{{ (data[i]["posting_date"]) }}</td>
<td style="text-align: right">{{ data[i]["age"] }}</td>
<td>
{% if not(filters.show_future_payments) %}
{{ data[i]["voucher_type"] }}
<br>
{% endif %}
{{ data[i]["voucher_no"] }}
</td>
<tr>
<td>{{ _("Total Outstanding") }}</td>
<td class="text-right">
{{ format_number(balance_row["age"], null, 2) }}
</td>
<td class="text-right">
{{ frappe.utils.fmt_money(balance_row["range1"], data[data.length-1]["currency"]) }}
</td>
<td class="text-right">
{{ frappe.utils.fmt_money(balance_row["range2"], data[data.length-1]["currency"]) }}
</td>
<td class="text-right">
{{ frappe.utils.fmt_money(balance_row["range3"], data[data.length-1]["currency"]) }}
</td>
<td class="text-right">
{{ frappe.utils.fmt_money(balance_row["range4"], data[data.length-1]["currency"]) }}
</td>
<td class="text-right">
{{ frappe.utils.fmt_money(balance_row["range5"], data[data.length-1]["currency"]) }}
</td>
<td class="text-right">
{{ frappe.utils.fmt_money(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></td>
<td class="text-right">
{{ frappe.utils.fmt_money(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></th>
<th class="text-right">
{{ frappe.utils.fmt_money(flt(balance_row["outstanding"] - balance_row[("future_amount")]), data[data.length-1]["currency"]) }}</th>
</tr>
</tbody>
{% if(report.report_name == "Accounts Receivable" and filters.show_sales_person) %}
<td>{{ data[i]["sales_person"] }}</td>
</table>
{% endif %}
{% endif %}
<table class="table table-bordered">
<thead>
<tr>
{% if(report.report_name == "Accounts Receivable" or report.report_name == "Accounts Payable") %}
<th style="width: 10%">{{ _("Date") }}</th>
<th style="width: 4%">{{ _("Age (Days)") }}</th>
{% if(report.report_name == "Accounts Receivable" and filters.show_sales_person) %}
<th style="width: 14%">{{ _("Reference") }}</th>
<th style="width: 10%">{{ _("Sales Person") }}</th>
{% else %}
<th style="width: 24%">{{ _("Reference") }}</th>
{% endif %}
{% if not(filters.show_future_payments) %}
<th style="width: 20%">
{% if (filters.customer or filters.supplier or filters.customer_name) %}
{{ _("Remarks") }}
{% else %}
{{ _("Party") }}
{% endif %}
</th>
{% endif %}
<th style="width: 10%; text-align: right">{{ _("Invoiced Amount") }}</th>
{% if not(filters.show_future_payments) %}
<th style="width: 10%; text-align: right">{{ _("Paid Amount") }}</th>
<th style="width: 10%; text-align: right">
{% if report.report_name == "Accounts Receivable" %}
{{ _("Credit Note") }}
{% else %}
{{ _("Debit Note") }}
{% endif %}
</th>
{% endif %}
<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>
{% endif %}
<th style="width: 10%">{{ _("Future Payment Ref") }}</th>
<th style="width: 10%">{{ _("Future Payment Amount") }}</th>
<th style="width: 10%">{{ _("Remaining Balance") }}</th>
{% endif %}
{% else %}
<th style="width: 40%">
{% if (filters.customer or filters.supplier or filters.customer_name) %}
{{ _("Remarks")}}
{% else %}
{{ _("Party") }}
{% endif %}
</th>
<th style="width: 15%">{{ _("Total Invoiced Amount") }}</th>
<th style="width: 15%">{{ _("Total Paid Amount") }}</th>
<th style="width: 15%">
{% if report.report_name == "Accounts Receivable Summary" %}
{{ _("Credit Note Amount") }}
{% else %}
{{ _("Debit Note Amount") }}
{% endif %}
</th>
<th style="width: 15%">{{ _("Total Outstanding Amount") }}</th>
{% endif %}
</tr>
</thead>
<tbody>
{% for i in range(data|length) %}
<tr>
{% if(report.report_name == "Accounts Receivable" or report.report_name == "Accounts Payable") %}
{% if(data[i]["party"]) %}
<td>{{ (data[i]["posting_date"]) }}</td>
<td style="text-align: right">{{ data[i]["age"] }}</td>
<td>
{% if not(filters.show_future_payments) %}
{{ data[i]["voucher_type"] }}
<br>
{% endif %}
{{ data[i]["voucher_no"] }}
</td>
{% if not (filters.show_future_payments) %}
{% if(report.report_name == "Accounts Receivable" and filters.show_sales_person) %}
<td>{{ data[i]["sales_person"] }}</td>
{% endif %}
{% if not (filters.show_future_payments) %}
<td>
{% if(not(filters.customer or filters.supplier or filters.customer_name)) %}
{{ data[i]["party"] }}
{% if(data[i]["customer_name"] and data[i]["customer_name"] != data[i]["party"]) %}
<br> {{ data[i]["customer_name"] }}
{% elif(data[i]["supplier_name"] != data[i]["party"]) %}
<br> {{ data[i]["supplier_name"] }}
{% endif %}
{% endif %}
<div>
{% if data[i]["remarks"] %}
{{ _("Remarks") }}:
{{ data[i]["remarks"] }}
{% endif %}
</div>
</td>
{% endif %}
<td style="text-align: right">
{{ frappe.utils.fmt_money(data[i]["invoiced"], currency=data[i]["currency"]) }}</td>
{% if not(filters.show_future_payments) %}
<td style="text-align: right">
{{ frappe.utils.fmt_money(data[i]["paid"], currency=data[i]["currency"]) }}</td>
<td style="text-align: right">
{{ frappe.utils.fmt_money(data[i]["credit_note"], currency=data[i]["currency"]) }}</td>
{% endif %}
<td style="text-align: right">
{{ frappe.utils.fmt_money(data[i]["outstanding"], currency=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>
{% endif %}
<td style="text-align: right">{{ data[i]["future_ref"] }}</td>
<td style="text-align: right">{{ frappe.utils.fmt_money(data[i]["future_amount"], currency=data[i]["currency"]) }}</td>
<td style="text-align: right">{{ frappe.utils.fmt_money(data[i]["remaining_balance"], currency=data[i]["currency"]) }}</td>
{% endif %}
{% else %}
<td></td>
{% if not(filters.show_future_payments) %}
<td></td>
{% endif %}
{% if(report.report_name == "Accounts Receivable" and filters.show_sales_person) %}
<td></td>
{% endif %}
<td></td>
<td style="text-align: right"><b>{{ _("Total") }}</b></td>
<td style="text-align: right">
{{ frappe.utils.fmt_money(data[i]["invoiced"], data[i]["currency"]) }}</td>
{% if not(filters.show_future_payments) %}
<td style="text-align: right">
{{ frappe.utils.fmt_money(data[i]["paid"], currency=data[i]["currency"]) }}</td>
<td style="text-align: right">{{ frappe.utils.fmt_money(data[i]["credit_note"], currency=data[i]["currency"]) }} </td>
{% endif %}
<td style="text-align: right">
{{ frappe.utils.fmt_money(data[i]["outstanding"], currency=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>
{% endif %}
<td style="text-align: right">{{ data[i]["future_ref"] }}</td>
<td style="text-align: right">{{ frappe.utils.fmt_money(data[i]["future_amount"], currency=data[i]["currency"]) }}</td>
<td style="text-align: right">{{ frappe.utils.fmt_money(data[i]["remaining_balance"], currency=data[i]["currency"]) }}</td>
{% endif %}
{% endif %}
{% else %}
{% if(data[i]["party"] or "&nbsp;") %}
{% if not(data[i]["is_total_row"]) %}
<td>
{% if(not(filters.customer or filters.supplier or filters.customer_name)) %}
{% if(not(filters.customer | filters.supplier)) %}
{{ data[i]["party"] }}
{% if(data[i]["customer_name"] and data[i]["customer_name"] != data[i]["party"]) %}
<br> {{ data[i]["customer_name"] }}
@@ -235,132 +309,62 @@
<br> {{ data[i]["supplier_name"] }}
{% endif %}
{% endif %}
<div>
{% if data[i]["remarks"] %}
{{ _("Remarks") }}:
{{ data[i]["remarks"] }}
{% endif %}
</div>
<br>{{ _("Remarks") }}:
{{ data[i]["remarks"] }}
</td>
{% endif %}
<td style="text-align: right">
{{ frappe.utils.fmt_money(data[i]["invoiced"], currency=data[i]["currency"]) }}</td>
{% if not(filters.show_future_payments) %}
<td style="text-align: right">
{{ frappe.utils.fmt_money(data[i]["paid"], currency=data[i]["currency"]) }}</td>
<td style="text-align: right">
{{ frappe.utils.fmt_money(data[i]["credit_note"], currency=data[i]["currency"]) }}</td>
{% endif %}
<td style="text-align: right">
{{ frappe.utils.fmt_money(data[i]["outstanding"], currency=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>
{% endif %}
<td style="text-align: right">{{ data[i]["future_ref"] }}</td>
<td style="text-align: right">{{ frappe.utils.fmt_money(data[i]["future_amount"], currency=data[i]["currency"]) }}</td>
<td style="text-align: right">{{ frappe.utils.fmt_money(data[i]["remaining_balance"], currency=data[i]["currency"]) }}</td>
{% endif %}
{% else %}
<td></td>
{% if not(filters.show_future_payments) %}
<td></td>
{% endif %}
{% if(report.report_name == "Accounts Receivable" and filters.show_sales_person) %}
<td></td>
{% endif %}
<td></td>
<td style="text-align: right"><b>{{ _("Total") }}</b></td>
<td style="text-align: right">
{{ frappe.utils.fmt_money(data[i]["invoiced"], data[i]["currency"]) }}</td>
{% if not(filters.show_future_payments) %}
<td style="text-align: right">
{{ frappe.utils.fmt_money(data[i]["paid"], currency=data[i]["currency"]) }}</td>
<td style="text-align: right">{{ frappe.utils.fmt_money(data[i]["credit_note"], currency=data[i]["currency"]) }} </td>
{% endif %}
<td style="text-align: right">
{{ frappe.utils.fmt_money(data[i]["outstanding"], currency=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>
{% endif %}
<td style="text-align: right">{{ data[i]["future_ref"] }}</td>
<td style="text-align: right">{{ frappe.utils.fmt_money(data[i]["future_amount"], currency=data[i]["currency"]) }}</td>
<td style="text-align: right">{{ frappe.utils.fmt_money(data[i]["remaining_balance"], currency=data[i]["currency"]) }}</td>
{% endif %}
{% endif %}
{% else %}
{% if(data[i]["party"] or "&nbsp;") %}
{% if not(data[i]["is_total_row"]) %}
<td>
{% if(not(filters.customer | filters.supplier)) %}
{{ data[i]["party"] }}
{% if(data[i]["customer_name"] and data[i]["customer_name"] != data[i]["party"]) %}
<br> {{ data[i]["customer_name"] }}
{% elif(data[i]["supplier_name"] != data[i]["party"]) %}
<br> {{ data[i]["supplier_name"] }}
{% endif %}
{% endif %}
<br>{{ _("Remarks") }}:
{{ data[i]["remarks"] }}
</td>
{% else %}
<td><b>{{ _("Total") }}</b></td>
{% endif %}
<td style="text-align: right">{{ frappe.utils.fmt_money(data[i]["invoiced"], currency=data[i]["currency"]) }}</td>
<td style="text-align: right">{{ frappe.utils.fmt_money(data[i]["paid"], currency=data[i]["currency"]) }}</td>
<td style="text-align: right">{{ frappe.utils.fmt_money(data[i]["credit_note"], currency=data[i]["currency"]) }}</td>
<td style="text-align: right">{{ frappe.utils.fmt_money(data[i]["outstanding"], currency=data[i]["currency"]) }}</td>
<td><b>{{ _("Total") }}</b></td>
{% endif %}
<td style="text-align: right">{{ frappe.utils.fmt_money(data[i]["invoiced"], currency=data[i]["currency"]) }}</td>
<td style="text-align: right">{{ frappe.utils.fmt_money(data[i]["paid"], currency=data[i]["currency"]) }}</td>
<td style="text-align: right">{{ frappe.utils.fmt_money(data[i]["credit_note"], currency=data[i]["currency"]) }}</td>
<td style="text-align: right">{{ frappe.utils.fmt_money(data[i]["outstanding"], currency=data[i]["currency"]) }}</td>
{% endif %}
</tr>
{% endfor %}
<td></td>
<td></td>
<td></td>
<td></td>
<td style="text-align: right"><b>{{ frappe.utils.fmt_money(data|sum(attribute="invoiced"), currency=data[0]["currency"]) }}</b></td>
<td style="text-align: right"><b>{{ frappe.utils.fmt_money(data|sum(attribute="paid"), currency=data[0]["currency"]) }}</b></td>
<td style="text-align: right"><b>{{ frappe.utils.fmt_money(data|sum(attribute="credit_note"), currency=data[0]["currency"]) }}</b></td>
<td style="text-align: right"><b>{{ frappe.utils.fmt_money(data|sum(attribute="outstanding"), currency=data[0]["currency"]) }}</b></td>
</tbody>
</table>
<br>
{% if ageing %}
<h4 class="text-center">{{ _("Ageing Report based on ") }} {{ ageing.ageing_based_on }}
{{ _("up to " ) }} {{ frappe.format(filters.report_date, 'Date')}}
</h4>
<table class="table table-bordered">
<thead>
<tr>
<th style="width: 25%">0 - 30 Days</th>
<th style="width: 25%">30 - 60 Days</th>
<th style="width: 25%">60 - 90 Days</th>
<th style="width: 25%">90 - 120 Days</th>
<th style="width: 20%">Above 120 Days</th>
{% endif %}
</tr>
</thead>
<tbody>
<tr>
<td>{{ frappe.utils.fmt_money(ageing.range1, currency=data[0]["currency"]) }}</td>
<td>{{ frappe.utils.fmt_money(ageing.range2, currency=data[0]["currency"]) }}</td>
<td>{{ frappe.utils.fmt_money(ageing.range3, currency=data[0]["currency"]) }}</td>
<td>{{ frappe.utils.fmt_money(ageing.range4, currency=data[0]["currency"]) }}</td>
<td>{{ frappe.utils.fmt_money(ageing.range5, currency=filters.presentation_currency) }}</td>
</tr>
</tbody>
</table>
{% endif %}
{% if terms_and_conditions %}
<div>
{{ terms_and_conditions }}
</div>
{% endif %}
<p class="text-right text-muted">{{ _("Printed On ") }}{{ frappe.utils.now() }}</p>
{% endfor %}
<td></td>
<td></td>
<td></td>
<td></td>
<td style="text-align: right"><b>{{ frappe.utils.fmt_money(data|sum(attribute="invoiced"), currency=data[0]["currency"]) }}</b></td>
<td style="text-align: right"><b>{{ frappe.utils.fmt_money(data|sum(attribute="paid"), currency=data[0]["currency"]) }}</b></td>
<td style="text-align: right"><b>{{ frappe.utils.fmt_money(data|sum(attribute="credit_note"), currency=data[0]["currency"]) }}</b></td>
<td style="text-align: right"><b>{{ frappe.utils.fmt_money(data|sum(attribute="outstanding"), currency=data[0]["currency"]) }}</b></td>
</tbody>
</table>
<br>
{% if ageing %}
<h4 class="text-center">
{{ _("Ageing Report based on {0} up to {1}").format(
ageing.ageing_based_on,
frappe.format(filters.report_date, "Date")
) }}
</h4>
<table class="table table-bordered">
<thead>
<tr>
<th style="width: 25%">{{ _("0 - 30 Days") }}</th>
<th style="width: 25%">{{ _("30 - 60 Days") }}</th>
<th style="width: 25%">{{ _("60 - 90 Days") }}</th>
<th style="width: 25%">{{ _("90 - 120 Days") }}</th>
<th style="width: 20%">{{ _("Above 120 Days") }}</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{ frappe.utils.fmt_money(ageing.range1, currency=data[0]["currency"]) }}</td>
<td>{{ frappe.utils.fmt_money(ageing.range2, currency=data[0]["currency"]) }}</td>
<td>{{ frappe.utils.fmt_money(ageing.range3, currency=data[0]["currency"]) }}</td>
<td>{{ frappe.utils.fmt_money(ageing.range4, currency=data[0]["currency"]) }}</td>
<td>{{ frappe.utils.fmt_money(ageing.range5, currency=filters.presentation_currency) }}</td>
</tr>
</tbody>
</table>
{% endif %}
{% if terms_and_conditions %}
<div>
{{ terms_and_conditions }}
</div>
{% endif %}
<p class="text-right text-muted">{{ _("Printed on {0}").format(frappe.utils.now()) }}</p>

View File

@@ -2,6 +2,18 @@
// For license information, please see license.txt
frappe.ui.form.on("Promotional Scheme", {
setup: function (frm) {
frm.set_query("for_price_list", "price_discount_slabs", (doc) => {
return {
filters: {
selling: doc.selling,
buying: doc.buying,
currency: doc.currency,
},
};
});
},
refresh: function (frm) {
frm.trigger("set_options_for_applicable_for");
frm.trigger("toggle_reqd_apply_on");

View File

@@ -51,6 +51,7 @@ price_discount_fields = [
"discount_percentage",
"validate_applied_rule",
"apply_multiple_pricing_rules",
"for_price_list",
]
product_discount_fields = [
@@ -63,6 +64,7 @@ product_discount_fields = [
"recurse_for",
"apply_recursion_over",
"apply_multiple_pricing_rules",
"round_free_qty",
]

View File

@@ -21,6 +21,7 @@
"rate",
"discount_amount",
"discount_percentage",
"for_price_list",
"section_break_11",
"warehouse",
"threshold_percentage",
@@ -120,6 +121,13 @@
"fieldtype": "Float",
"label": "Discount Percentage"
},
{
"depends_on": "eval:doc.rate_or_discount!=\"Rate\"",
"fieldname": "for_price_list",
"fieldtype": "Link",
"label": "For Price List",
"options": "Price List"
},
{
"fieldname": "section_break_11",
"fieldtype": "Section Break"
@@ -169,7 +177,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2024-03-27 13:10:22.448265",
"modified": "2024-07-23 12:33:46.574950",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Promotional Scheme Price Discount",

View File

@@ -19,6 +19,7 @@ class PromotionalSchemePriceDiscount(Document):
disable: DF.Check
discount_amount: DF.Currency
discount_percentage: DF.Float
for_price_list: DF.Link | None
max_amount: DF.Currency
max_qty: DF.Float
min_amount: DF.Currency

View File

@@ -22,6 +22,7 @@
"column_break_9",
"free_item_uom",
"free_item_rate",
"round_free_qty",
"section_break_12",
"warehouse",
"threshold_percentage",
@@ -181,12 +182,18 @@
"fieldtype": "Float",
"label": "Apply Recursion Over (As Per Transaction UOM)",
"mandatory_depends_on": "is_recursive"
},
{
"default": "0",
"fieldname": "round_free_qty",
"fieldtype": "Check",
"label": "Round Free Qty"
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2024-03-27 13:10:22.605892",
"modified": "2024-07-22 17:25:07.880984",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Promotional Scheme Product Discount",
@@ -195,4 +202,4 @@
"sort_field": "creation",
"sort_order": "DESC",
"states": []
}
}

View File

@@ -53,6 +53,7 @@ class PromotionalSchemeProductDiscount(Document):
"20",
]
recurse_for: DF.Float
round_free_qty: DF.Check
rule_description: DF.SmallText
same_item: DF.Check
threshold_percentage: DF.Percent

View File

@@ -77,31 +77,6 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
erpnext.accounts.ledger_preview.show_stock_ledger_preview(this.frm);
}
if (this.frm.doc.repost_required && this.frm.doc.docstatus === 1) {
this.frm.set_intro(
__(
"Accounting entries for this invoice need to be reposted. Please click on 'Repost' button to update."
)
);
this.frm
.add_custom_button(__("Repost Accounting Entries"), () => {
this.frm.call({
doc: this.frm.doc,
method: "repost_accounting_entries",
freeze: true,
freeze_message: __("Reposting..."),
callback: (r) => {
if (!r.exc) {
frappe.msgprint(__("Accounting Entries are reposted."));
me.frm.refresh();
}
},
});
})
.removeClass("btn-default")
.addClass("btn-warning");
}
if (!doc.is_return && doc.docstatus == 1 && doc.outstanding_amount != 0) {
if (doc.on_hold) {
this.frm.add_custom_button(

View File

@@ -170,7 +170,6 @@
"against_expense_account",
"column_break_63",
"unrealized_profit_loss_account",
"repost_required",
"subscription_section",
"subscription",
"auto_repeat",
@@ -364,7 +363,8 @@
"description": "Once set, this invoice will be on hold till the set date",
"fieldname": "release_date",
"fieldtype": "Date",
"label": "Release Date"
"label": "Release Date",
"search_index": 1
},
{
"fieldname": "cb_17",
@@ -1604,15 +1604,6 @@
"fieldtype": "Check",
"label": "Use Company Default Round Off Cost Center"
},
{
"default": "0",
"fieldname": "repost_required",
"fieldtype": "Check",
"hidden": 1,
"label": "Repost Required",
"options": "Account",
"read_only": 1
},
{
"default": "0",
"fieldname": "use_transaction_date_exchange_rate",
@@ -1640,7 +1631,7 @@
"idx": 204,
"is_submittable": 1,
"links": [],
"modified": "2024-04-11 11:28:42.802211",
"modified": "2024-07-25 19:42:36.931278",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice",

View File

@@ -159,7 +159,6 @@ class PurchaseInvoice(BuyingController):
rejected_warehouse: DF.Link | None
release_date: DF.Date | None
remarks: DF.SmallText | None
repost_required: DF.Check
represents_company: DF.Link | None
return_against: DF.Link | None
rounded_total: DF.Currency
@@ -379,16 +378,16 @@ class PurchaseInvoice(BuyingController):
if account.report_type != "Balance Sheet":
frappe.throw(
_(
"Please ensure {} account is a Balance Sheet account. You can change the parent account to a Balance Sheet account or select a different account."
).format(frappe.bold("Credit To")),
"Please ensure that the {0} account is a Balance Sheet account. You can change the parent account to a Balance Sheet account or select a different account."
).format(frappe.bold(_("Credit To"))),
title=_("Invalid Account"),
)
if self.supplier and account.account_type != "Payable":
frappe.throw(
_(
"Please ensure {} account {} is a Payable account. Change the account type to Payable or select a different account."
).format(frappe.bold("Credit To"), frappe.bold(self.credit_to)),
"Please ensure that the {0} account {1} is a Payable account. You can change the account type to Payable or select a different account."
).format(frappe.bold(_("Credit To")), frappe.bold(self.credit_to)),
title=_("Invalid Account"),
)
@@ -636,7 +635,7 @@ class PurchaseInvoice(BuyingController):
"To submit the invoice without purchase order please set {0} as {1} in {2}"
).format(
frappe.bold(_("Purchase Order Required")),
frappe.bold("No"),
frappe.bold(_("No")),
get_link_to_form("Buying Settings", "Buying Settings", "Buying Settings"),
)
throw(msg, title=_("Mandatory Purchase Order"))
@@ -657,7 +656,7 @@ class PurchaseInvoice(BuyingController):
"To submit the invoice without purchase receipt please set {0} as {1} in {2}"
).format(
frappe.bold(_("Purchase Receipt Required")),
frappe.bold("No"),
frappe.bold(_("No")),
get_link_to_form("Buying Settings", "Buying Settings", "Buying Settings"),
)
throw(msg, title=_("Mandatory Purchase Receipt"))
@@ -797,19 +796,17 @@ class PurchaseInvoice(BuyingController):
self.process_common_party_accounting()
def on_update_after_submit(self):
if hasattr(self, "repost_required"):
fields_to_check = [
"cash_bank_account",
"write_off_account",
"unrealized_profit_loss_account",
"is_opening",
]
child_tables = {"items": ("expense_account",), "taxes": ("account_head",)}
self.needs_repost = self.check_if_fields_updated(fields_to_check, child_tables)
if self.needs_repost:
self.validate_for_repost()
self.db_set("repost_required", self.needs_repost)
self.repost_accounting_entries()
fields_to_check = [
"cash_bank_account",
"write_off_account",
"unrealized_profit_loss_account",
"is_opening",
]
child_tables = {"items": ("expense_account",), "taxes": ("account_head",)}
self.needs_repost = self.check_if_fields_updated(fields_to_check, child_tables)
if self.needs_repost:
self.validate_for_repost()
self.repost_accounting_entries()
def make_gl_entries(self, gl_entries=None, from_repost=False):
update_outstanding = "No" if (cint(self.is_paid) or self.write_off_account) else "Yes"
@@ -1270,7 +1267,11 @@ class PurchaseInvoice(BuyingController):
def update_gross_purchase_amount_for_linked_assets(self, item):
assets = frappe.db.get_all(
"Asset",
filters={"purchase_invoice": self.name, "item_code": item.item_code},
filters={
"purchase_invoice": self.name,
"item_code": item.item_code,
"purchase_invoice_item": ("in", [item.name, ""]),
},
fields=["name", "asset_quantity"],
)
for asset in assets:
@@ -1523,10 +1524,29 @@ class PurchaseInvoice(BuyingController):
# 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 not self.is_internal_transfer() and self.rounding_adjustment and self.base_rounding_adjustment:
round_off_account, round_off_cost_center = get_round_off_account_and_cost_center(
(
round_off_account,
round_off_cost_center,
round_off_for_opening,
) = get_round_off_account_and_cost_center(
self.company, "Purchase Invoice", self.name, self.use_company_roundoff_cost_center
)
if self.is_opening == "Yes" and self.rounding_adjustment:
if not round_off_for_opening:
frappe.throw(
_(
"Opening Invoice has rounding adjustment of {0}.<br><br> '{1}' account is required to post these values. Please set it in Company: {2}.<br><br> Or, '{3}' can be enabled to not post any rounding adjustment."
).format(
frappe.bold(self.rounding_adjustment),
frappe.bold("Round Off for Opening"),
get_link_to_form("Company", self.company),
frappe.bold("Disable Rounded Total"),
)
)
else:
round_off_account = round_off_for_opening
gl_entries.append(
self.get_gl_dict(
{
@@ -1710,6 +1730,9 @@ class PurchaseInvoice(BuyingController):
self.db_set("release_date", None)
def set_tax_withholding(self):
self.set("advance_tax", [])
self.set("tax_withheld_vouchers", [])
if not self.apply_tds:
return
@@ -1751,8 +1774,6 @@ class PurchaseInvoice(BuyingController):
self.remove(d)
## Add pending vouchers on which tax was withheld
self.set("tax_withheld_vouchers", [])
for voucher_no, voucher_details in voucher_wise_amount.items():
self.append(
"tax_withheld_vouchers",
@@ -1767,7 +1788,6 @@ class PurchaseInvoice(BuyingController):
self.calculate_taxes_and_totals()
def allocate_advance_tds(self, tax_withholding_details, advance_taxes):
self.set("advance_tax", [])
for tax in advance_taxes:
allocated_amount = 0
pending_amount = flt(tax.tax_amount - tax.allocated_amount)

View File

@@ -2024,8 +2024,6 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
["Service - _TC", 1000, 0.0, nowdate()],
]
check_gl_entries(self, pi.name, expected_gle, nowdate())
pi.load_from_db()
self.assertFalse(pi.repost_required)
@change_settings("Buying Settings", {"supplier_group": None})
def test_purchase_invoice_without_supplier_group(self):
@@ -2248,6 +2246,121 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
self.assertEqual(pi_expected_values[i][1], gle.debit)
self.assertEqual(pi_expected_values[i][2], gle.credit)
def test_adjust_incoming_rate_from_pi_with_multi_currency(self):
from erpnext.stock.doctype.landed_cost_voucher.test_landed_cost_voucher import (
make_landed_cost_voucher,
)
frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 0)
frappe.db.set_single_value("Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate", 1)
# Increase the cost of the item
pr = make_purchase_receipt(
qty=10, rate=1, currency="USD", do_not_save=1, supplier="_Test Supplier USD"
)
pr.conversion_rate = 6300
pr.plc_conversion_rate = 1
pr.save()
pr.submit()
self.assertEqual(pr.conversion_rate, 6300)
self.assertEqual(pr.plc_conversion_rate, 1)
self.assertEqual(pr.base_grand_total, 6300 * 10)
stock_value_difference = frappe.db.get_value(
"Stock Ledger Entry",
{"voucher_type": "Purchase Receipt", "voucher_no": pr.name},
"stock_value_difference",
)
self.assertEqual(stock_value_difference, 6300 * 10)
make_landed_cost_voucher(
company=pr.company,
receipt_document_type="Purchase Receipt",
receipt_document=pr.name,
charges=3000,
distribute_charges_based_on="Qty",
)
pi = create_purchase_invoice_from_receipt(pr.name)
for row in pi.items:
row.rate = 1.1
pi.save()
pi.submit()
stock_value_difference = frappe.db.get_value(
"Stock Ledger Entry",
{"voucher_type": "Purchase Receipt", "voucher_no": pr.name},
"stock_value_difference",
)
self.assertEqual(stock_value_difference, 7230 * 10)
frappe.db.set_single_value("Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate", 0)
frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 1)
def test_opening_invoice_rounding_adjustment_validation(self):
pi = make_purchase_invoice(do_not_save=1)
pi.items[0].rate = 99.98
pi.items[0].qty = 1
pi.items[0].expense_account = "Temporary Opening - _TC"
pi.is_opening = "Yes"
pi.save()
self.assertRaises(frappe.ValidationError, pi.submit)
def _create_opening_roundoff_account(self, company_name):
liability_root = frappe.db.get_all(
"Account",
filters={"company": company_name, "root_type": "Liability", "disabled": 0},
order_by="lft",
limit=1,
)[0]
# setup round off account
if acc := frappe.db.exists(
"Account",
{
"account_name": "Round Off for Opening",
"account_type": "Round Off for Opening",
"company": company_name,
},
):
frappe.db.set_value("Company", company_name, "round_off_for_opening", acc)
else:
acc = frappe.new_doc("Account")
acc.company = company_name
acc.parent_account = liability_root.name
acc.account_name = "Round Off for Opening"
acc.account_type = "Round Off for Opening"
acc.save()
frappe.db.set_value("Company", company_name, "round_off_for_opening", acc.name)
def test_ledger_entries_of_opening_invoice_with_rounding_adjustment(self):
pi = make_purchase_invoice(do_not_save=1)
pi.items[0].rate = 99.98
pi.items[0].qty = 1
pi.items[0].expense_account = "Temporary Opening - _TC"
pi.is_opening = "Yes"
pi.save()
self._create_opening_roundoff_account(pi.company)
pi.submit()
actual = frappe.db.get_all(
"GL Entry",
filters={"voucher_no": pi.name, "is_opening": "Yes", "is_cancelled": False},
fields=["account", "debit", "credit", "is_opening"],
order_by="account,debit",
)
expected = [
{"account": "Creditors - _TC", "debit": 0.0, "credit": 100.0, "is_opening": "Yes"},
{"account": "Round Off for Opening - _TC", "debit": 0.02, "credit": 0.0, "is_opening": "Yes"},
{"account": "Temporary Opening - _TC", "debit": 99.98, "credit": 0.0, "is_opening": "Yes"},
]
self.assertEqual(len(actual), 3)
self.assertEqual(expected, actual)
def set_advance_flag(company, flag, default_account):
frappe.db.set_value(

View File

@@ -38,6 +38,7 @@
"column_break_30",
"discount_percentage",
"discount_amount",
"distributed_discount_amount",
"base_rate_with_margin",
"sec_break2",
"rate",
@@ -57,6 +58,7 @@
"base_net_rate",
"base_net_amount",
"valuation_rate",
"sales_incoming_rate",
"item_tax_amount",
"landed_cost_voucher_amount",
"rm_supp_cost",
@@ -837,7 +839,7 @@
},
{
"collapsible": 1,
"collapsible_depends_on": "eval: doc.margin_type || doc.discount_amount",
"collapsible_depends_on": "eval: doc.margin_type || doc.discount_amount || doc.distributed_discount_amount",
"fieldname": "section_break_26",
"fieldtype": "Section Break",
"label": "Discount and Margin"
@@ -958,12 +960,28 @@
"print_hide": 1,
"read_only": 1,
"search_index": 1
},
{
"description": "Valuation rate for the item as per Sales Invoice (Only for Internal Transfers)",
"fieldname": "sales_incoming_rate",
"fieldtype": "Currency",
"hidden": 1,
"label": "Sales Incoming Rate",
"no_copy": 1,
"options": "Company:company:default_currency",
"print_hide": 1
},
{
"fieldname": "distributed_discount_amount",
"fieldtype": "Currency",
"label": "Distributed Discount Amount",
"options": "currency"
}
],
"idx": 1,
"istable": 1,
"links": [],
"modified": "2024-06-14 11:57:07.171700",
"modified": "2024-07-19 12:12:42.449298",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Item",

View File

@@ -34,6 +34,7 @@ class PurchaseInvoiceItem(Document):
description: DF.TextEditor | None
discount_amount: DF.Currency
discount_percentage: DF.Percent
distributed_discount_amount: DF.Currency
enable_deferred_expense: DF.Check
expense_account: DF.Link | None
from_warehouse: DF.Link | None
@@ -79,6 +80,7 @@ class PurchaseInvoiceItem(Document):
rejected_serial_no: DF.Text | None
rejected_warehouse: DF.Link | None
rm_supp_cost: DF.Currency
sales_incoming_rate: DF.Currency
sales_invoice_item: DF.Data | None
serial_and_batch_bundle: DF.Link | None
serial_no: DF.Text | None

View File

@@ -68,31 +68,6 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
this.frm.toggle_reqd("due_date", !this.frm.doc.is_return);
if (this.frm.doc.repost_required && this.frm.doc.docstatus === 1) {
this.frm.set_intro(
__(
"Accounting entries for this invoice needs to be reposted. Please click on 'Repost' button to update."
)
);
this.frm
.add_custom_button(__("Repost Accounting Entries"), () => {
this.frm.call({
doc: this.frm.doc,
method: "repost_accounting_entries",
freeze: true,
freeze_message: __("Reposting..."),
callback: (r) => {
if (!r.exc) {
frappe.msgprint(__("Accounting Entries are reposted"));
me.frm.refresh();
}
},
});
})
.removeClass("btn-default")
.addClass("btn-warning");
}
if (this.frm.doc.is_return) {
this.frm.return_print_format = "Sales Invoice Return";
}
@@ -161,7 +136,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends (
const payment_is_overdue = doc.payment_schedule
.map((row) => Date.parse(row.due_date) < Date.now())
.reduce((prev, current) => prev || current);
.reduce((prev, current) => prev || current, false);
if (payment_is_overdue) {
this.frm.add_custom_button(
@@ -596,49 +571,6 @@ cur_frm.cscript["Make Delivery Note"] = function () {
});
};
cur_frm.fields_dict.cash_bank_account.get_query = function (doc) {
return {
filters: [
["Account", "account_type", "in", ["Cash", "Bank"]],
["Account", "root_type", "=", "Asset"],
["Account", "is_group", "=", 0],
["Account", "company", "=", doc.company],
],
};
};
cur_frm.fields_dict.write_off_account.get_query = function (doc) {
return {
filters: {
report_type: "Profit and Loss",
is_group: 0,
company: doc.company,
},
};
};
// Write off cost center
//-----------------------
cur_frm.fields_dict.write_off_cost_center.get_query = function (doc) {
return {
filters: {
is_group: 0,
company: doc.company,
},
};
};
// Cost Center in Details Table
// -----------------------------
cur_frm.fields_dict["items"].grid.get_field("cost_center").get_query = function (doc) {
return {
filters: {
company: doc.company,
is_group: 0,
},
};
};
cur_frm.cscript.income_account = function (doc, cdt, cdn) {
erpnext.utils.copy_value_in_all_rows(doc, cdt, cdn, "items", "income_account");
};
@@ -651,28 +583,6 @@ cur_frm.cscript.cost_center = function (doc, cdt, cdn) {
erpnext.utils.copy_value_in_all_rows(doc, cdt, cdn, "items", "cost_center");
};
cur_frm.set_query("debit_to", function (doc) {
return {
filters: {
account_type: "Receivable",
is_group: 0,
company: doc.company,
},
};
});
cur_frm.set_query("asset", "items", function (doc, cdt, cdn) {
var d = locals[cdt][cdn];
return {
filters: [
["Asset", "item_code", "=", d.item_code],
["Asset", "docstatus", "=", 1],
["Asset", "status", "in", ["Submitted", "Partially Depreciated", "Fully Depreciated"]],
["Asset", "company", "=", doc.company],
],
};
});
frappe.ui.form.on("Sales Invoice", {
setup: function (frm) {
frm.add_fetch("customer", "tax_id", "tax_id");
@@ -682,71 +592,132 @@ frappe.ui.form.on("Sales Invoice", {
frm.set_df_property("packed_items", "cannot_add_rows", true);
frm.set_df_property("packed_items", "cannot_delete_rows", true);
frm.set_query("account_for_change_amount", function () {
frm.set_query("cash_bank_account", function (doc) {
return {
filters: [
["Account", "account_type", "in", ["Cash", "Bank"]],
["Account", "root_type", "=", "Asset"],
["Account", "is_group", "=", 0],
["Account", "company", "=", doc.company],
],
};
});
frm.set_query("write_off_account", function (doc) {
return {
filters: {
account_type: ["in", ["Cash", "Bank"]],
company: frm.doc.company,
report_type: "Profit and Loss",
is_group: 0,
company: doc.company,
},
};
});
frm.set_query("write_off_cost_center", function (doc) {
return {
filters: {
is_group: 0,
company: doc.company,
},
};
});
frm.set_query("cost_center", "items", function (doc) {
return {
filters: {
company: doc.company,
is_group: 0,
},
};
});
frm.set_query("unrealized_profit_loss_account", function () {
frm.set_query("debit_to", function (doc) {
return {
filters: {
company: frm.doc.company,
account_type: "Receivable",
is_group: 0,
company: doc.company,
},
};
});
frm.set_query("asset", "items", function (doc, cdt, cdn) {
const row = locals[cdt][cdn];
return {
filters: [
["Asset", "item_code", "=", row.item_code],
["Asset", "docstatus", "=", 1],
["Asset", "status", "in", ["Submitted", "Partially Depreciated", "Fully Depreciated"]],
["Asset", "company", "=", doc.company],
],
};
});
frm.set_query("account_for_change_amount", function (doc) {
return {
filters: {
account_type: ["in", ["Cash", "Bank"]],
company: doc.company,
is_group: 0,
},
};
});
frm.set_query("unrealized_profit_loss_account", function (doc) {
return {
filters: {
company: doc.company,
is_group: 0,
root_type: "Liability",
},
};
});
frm.set_query("adjustment_against", function () {
frm.set_query("adjustment_against", function (doc) {
return {
filters: {
company: frm.doc.company,
customer: frm.doc.customer,
company: doc.company,
customer: doc.customer,
docstatus: 1,
},
};
});
frm.set_query("additional_discount_account", function () {
frm.set_query("additional_discount_account", function (doc) {
return {
filters: {
company: frm.doc.company,
company: doc.company,
is_group: 0,
report_type: "Profit and Loss",
},
};
});
frm.set_query("income_account", "items", function () {
frm.set_query("income_account", "items", function (doc) {
return {
query: "erpnext.controllers.queries.get_income_account",
filters: {
company: frm.doc.company,
company: doc.company,
disabled: 0,
},
};
});
(frm.custom_make_buttons = {
frm.custom_make_buttons = {
"Delivery Note": "Delivery",
"Sales Invoice": "Return / Credit Note",
"Payment Request": "Payment Request",
"Payment Entry": "Payment",
}),
(frm.fields_dict["timesheets"].grid.get_field("time_sheet").get_query = function (doc, cdt, cdn) {
return {
query: "erpnext.projects.doctype.timesheet.timesheet.get_timesheet",
filters: { project: doc.project },
};
});
};
// discount account
frm.fields_dict["items"].grid.get_field("discount_account").get_query = function (doc) {
frm.set_query("time_sheet", "timesheets", function (doc, cdt, cdn) {
return {
query: "erpnext.projects.doctype.timesheet.timesheet.get_timesheet",
filters: { project: doc.project },
};
});
frm.set_query("discount_account", "items", function (doc) {
return {
filters: {
report_type: "Profit and Loss",
@@ -754,9 +725,9 @@ frappe.ui.form.on("Sales Invoice", {
is_group: 0,
},
};
};
});
frm.fields_dict["items"].grid.get_field("deferred_revenue_account").get_query = function (doc) {
frm.set_query("deferred_revenue_account", "items", function (doc) {
return {
filters: {
root_type: "Liability",
@@ -764,7 +735,7 @@ frappe.ui.form.on("Sales Invoice", {
is_group: 0,
},
};
};
});
frm.set_query("company_address", function (doc) {
if (!doc.company) {
@@ -793,25 +764,23 @@ frappe.ui.form.on("Sales Invoice", {
};
});
// set get_query for loyalty redemption account
frm.fields_dict["loyalty_redemption_account"].get_query = function () {
frm.set_query("loyalty_redemption_account", function () {
return {
filters: {
company: frm.doc.company,
is_group: 0,
},
};
};
});
// set get_query for loyalty redemption cost center
frm.fields_dict["loyalty_redemption_cost_center"].get_query = function () {
frm.set_query("loyalty_redemption_cost_center", function () {
return {
filters: {
company: frm.doc.company,
is_group: 0,
},
};
};
});
},
// When multiple companies are set up. in case company name is changed set default company address
company: function (frm) {

View File

@@ -215,7 +215,6 @@
"is_internal_customer",
"is_discounted",
"remarks",
"repost_required",
"connections_tab"
],
"fields": [
@@ -2128,15 +2127,6 @@
"label": "Write Off",
"width": "50%"
},
{
"default": "0",
"fieldname": "repost_required",
"fieldtype": "Check",
"hidden": 1,
"label": "Repost Required",
"no_copy": 1,
"read_only": 1
},
{
"fieldname": "incoterm",
"fieldtype": "Link",
@@ -2205,7 +2195,7 @@
"link_fieldname": "consolidated_invoice"
}
],
"modified": "2024-05-23 14:02:28.549041",
"modified": "2024-07-18 15:30:39.428519",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",

View File

@@ -166,7 +166,6 @@ class SalesInvoice(SellingController):
project: DF.Link | None
redeem_loyalty_points: DF.Check
remarks: DF.SmallText | None
repost_required: DF.Check
represents_company: DF.Link | None
return_against: DF.Link | None
rounded_total: DF.Currency
@@ -316,17 +315,6 @@ class SalesInvoice(SellingController):
if not self.is_opening:
self.is_opening = "No"
if self.redeem_loyalty_points:
lp = frappe.get_doc("Loyalty Program", self.loyalty_program)
self.loyalty_redemption_account = (
lp.expense_account if not self.loyalty_redemption_account else self.loyalty_redemption_account
)
self.loyalty_redemption_cost_center = (
lp.cost_center
if not self.loyalty_redemption_cost_center
else self.loyalty_redemption_cost_center
)
self.set_against_income_account()
self.validate_time_sheets_are_submitted()
self.validate_multiple_billing("Delivery Note", "dn_detail", "amount")
@@ -345,12 +333,7 @@ class SalesInvoice(SellingController):
if self.is_pos and self.is_return:
self.verify_payment_amount_is_negative()
if (
self.redeem_loyalty_points
and self.loyalty_program
and self.loyalty_points
and not self.is_consolidated
):
if self.redeem_loyalty_points and self.loyalty_points and not self.is_consolidated:
validate_loyalty_points(self, self.loyalty_points)
self.reset_default_field_value("set_warehouse", "items", "warehouse")
@@ -528,7 +511,7 @@ class SalesInvoice(SellingController):
)
if pos_closing_entry and pos_closing_entry[0]:
msg = _("To cancel a {} you need to cancel the POS Closing Entry {}.").format(
frappe.bold("Consolidated Sales Invoice"),
frappe.bold(_("Consolidated Sales Invoice")),
get_link_to_form("POS Closing Entry", pos_closing_entry[0]),
)
frappe.throw(msg, title=_("Not Allowed"))
@@ -569,7 +552,6 @@ class SalesInvoice(SellingController):
self.repost_future_sle_and_gle()
self.db_set("status", "Cancelled")
self.db_set("repost_required", 0)
if self.coupon_code:
update_coupon_code_count(self.coupon_code, "cancelled")
@@ -722,25 +704,23 @@ class SalesInvoice(SellingController):
data.sales_invoice = sales_invoice
def on_update_after_submit(self):
if hasattr(self, "repost_required"):
fields_to_check = [
"additional_discount_account",
"cash_bank_account",
"account_for_change_amount",
"write_off_account",
"loyalty_redemption_account",
"unrealized_profit_loss_account",
"is_opening",
]
child_tables = {
"items": ("income_account", "expense_account", "discount_account"),
"taxes": ("account_head",),
}
self.needs_repost = self.check_if_fields_updated(fields_to_check, child_tables)
if self.needs_repost:
self.validate_for_repost()
self.db_set("repost_required", self.needs_repost)
self.repost_accounting_entries()
fields_to_check = [
"additional_discount_account",
"cash_bank_account",
"account_for_change_amount",
"write_off_account",
"loyalty_redemption_account",
"unrealized_profit_loss_account",
"is_opening",
]
child_tables = {
"items": ("income_account", "expense_account", "discount_account"),
"taxes": ("account_head",),
}
self.needs_repost = self.check_if_fields_updated(fields_to_check, child_tables)
if self.needs_repost:
self.validate_for_repost()
self.repost_accounting_entries()
def set_paid_amount(self):
paid_amount = 0.0
@@ -878,7 +858,7 @@ class SalesInvoice(SellingController):
if account.report_type != "Balance Sheet":
msg = (
_("Please ensure {} account is a Balance Sheet account.").format(frappe.bold("Debit To"))
_("Please ensure {} account is a Balance Sheet account.").format(frappe.bold(_("Debit To")))
+ " "
)
msg += _(
@@ -889,7 +869,7 @@ class SalesInvoice(SellingController):
if self.customer and account.account_type != "Receivable":
msg = (
_("Please ensure {} account {} is a Receivable account.").format(
frappe.bold("Debit To"), frappe.bold(self.debit_to)
frappe.bold(_("Debit To")), frappe.bold(self.debit_to)
)
+ " "
)
@@ -1326,6 +1306,10 @@ class SalesInvoice(SellingController):
for item in self.get("items"):
if flt(item.base_net_amount, item.precision("base_net_amount")):
# Do not book income for transfer within same company
if self.is_internal_transfer():
continue
if item.is_fixed_asset:
asset = self.get_asset(item)
@@ -1384,37 +1368,33 @@ class SalesInvoice(SellingController):
self.set_asset_status(asset)
else:
# Do not book income for transfer within same company
if not self.is_internal_transfer():
income_account = (
item.income_account
if (not item.enable_deferred_revenue or self.is_return)
else item.deferred_revenue_account
)
income_account = (
item.income_account
if (not item.enable_deferred_revenue or self.is_return)
else item.deferred_revenue_account
)
amount, base_amount = self.get_amount_and_base_amount(
item, enable_discount_accounting
)
amount, base_amount = self.get_amount_and_base_amount(item, enable_discount_accounting)
account_currency = get_account_currency(income_account)
gl_entries.append(
self.get_gl_dict(
{
"account": income_account,
"against": self.customer,
"credit": flt(base_amount, item.precision("base_net_amount")),
"credit_in_account_currency": (
flt(base_amount, item.precision("base_net_amount"))
if account_currency == self.company_currency
else flt(amount, item.precision("net_amount"))
),
"cost_center": item.cost_center,
"project": item.project or self.project,
},
account_currency,
item=item,
)
account_currency = get_account_currency(income_account)
gl_entries.append(
self.get_gl_dict(
{
"account": income_account,
"against": self.customer,
"credit": flt(base_amount, item.precision("base_net_amount")),
"credit_in_account_currency": (
flt(base_amount, item.precision("base_net_amount"))
if account_currency == self.company_currency
else flt(amount, item.precision("net_amount"))
),
"cost_center": item.cost_center,
"project": item.project or self.project,
},
account_currency,
item=item,
)
)
# expense account gl entries
if cint(self.update_stock) and erpnext.is_perpetual_inventory_enabled(self.company):
@@ -1448,7 +1428,7 @@ class SalesInvoice(SellingController):
asset.set_status("Sold" if self.docstatus == 1 else None)
def make_loyalty_point_redemption_gle(self, gl_entries):
if cint(self.redeem_loyalty_points):
if cint(self.redeem_loyalty_points and self.loyalty_points and not self.is_consolidated):
gl_entries.append(
self.get_gl_dict(
{
@@ -1489,6 +1469,10 @@ class SalesInvoice(SellingController):
if skip_change_gl_entries and payment_mode.account == self.account_for_change_amount:
payment_mode.base_amount -= flt(self.change_amount)
against_voucher = self.name
if self.is_return and self.return_against and not self.update_outstanding_for_self:
against_voucher = self.return_against
if payment_mode.base_amount:
# POS, make payment entries
gl_entries.append(
@@ -1502,7 +1486,7 @@ class SalesInvoice(SellingController):
"credit_in_account_currency": payment_mode.base_amount
if self.party_account_currency == self.company_currency
else payment_mode.amount,
"against_voucher": self.name,
"against_voucher": against_voucher,
"against_voucher_type": self.doctype,
"cost_center": self.cost_center,
},
@@ -1626,10 +1610,29 @@ class SalesInvoice(SellingController):
and self.base_rounding_adjustment
and not self.is_internal_transfer()
):
round_off_account, round_off_cost_center = get_round_off_account_and_cost_center(
(
round_off_account,
round_off_cost_center,
round_off_for_opening,
) = get_round_off_account_and_cost_center(
self.company, "Sales Invoice", self.name, self.use_company_roundoff_cost_center
)
if self.is_opening == "Yes" and self.rounding_adjustment:
if not round_off_for_opening:
frappe.throw(
_(
"Opening Invoice has rounding adjustment of {0}.<br><br> '{1}' account is required to post these values. Please set it in Company: {2}.<br><br> Or, '{3}' can be enabled to not post any rounding adjustment."
).format(
frappe.bold(self.rounding_adjustment),
frappe.bold("Round Off for Opening"),
get_link_to_form("Company", self.company),
frappe.bold("Disable Rounded Total"),
)
)
else:
round_off_account = round_off_for_opening
gl_entries.append(
self.get_gl_dict(
{
@@ -1776,7 +1779,8 @@ class SalesInvoice(SellingController):
loyalty_program=self.loyalty_program,
include_expired_entry=True,
)
frappe.db.set_value("Customer", self.customer, "loyalty_program_tier", lp_details.tier_name)
customer = frappe.get_doc("Customer", self.customer)
customer.db_set("loyalty_program_tier", lp_details.tier_name)
def get_returned_amount(self):
from frappe.query_builder.functions import Sum
@@ -2081,7 +2085,7 @@ def make_delivery_note(source_name, target_doc=None):
"postprocess": update_item,
"condition": lambda doc: doc.delivered_by_supplier != 1,
},
"Sales Taxes and Charges": {"doctype": "Sales Taxes and Charges", "add_if_empty": True},
"Sales Taxes and Charges": {"doctype": "Sales Taxes and Charges", "reset_value": True},
"Sales Team": {
"doctype": "Sales Team",
"field_map": {"incentives": "incentives"},

View File

@@ -5,6 +5,7 @@ import copy
import json
import frappe
from frappe import qb
from frappe.model.dynamic_links import get_dynamic_link_map
from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import add_days, flt, getdate, nowdate, today
@@ -2965,9 +2966,6 @@ class TestSalesInvoice(FrappeTestCase):
check_gl_entries(self, si.name, expected_gle, add_days(nowdate(), -1))
si.load_from_db()
self.assertFalse(si.repost_required)
def test_asset_depreciation_on_sale_with_pro_rata(self):
"""
Tests if an Asset set to depreciate yearly on June 30, that gets sold on Sept 30, creates an additional depreciation entry on its date of sale.
@@ -3099,6 +3097,128 @@ class TestSalesInvoice(FrappeTestCase):
party_link.delete()
frappe.db.set_single_value("Accounts Settings", "enable_common_party_accounting", 0)
def test_sales_invoice_against_supplier_usd_with_dimensions(self):
from erpnext.accounts.doctype.opening_invoice_creation_tool.test_opening_invoice_creation_tool import (
make_customer,
)
from erpnext.accounts.doctype.party_link.party_link import create_party_link
from erpnext.buying.doctype.supplier.test_supplier import create_supplier
# create a customer
customer = make_customer(customer="_Test Common Supplier USD")
cust_doc = frappe.get_doc("Customer", customer)
cust_doc.default_currency = "USD"
cust_doc.save()
# create a supplier
supplier = create_supplier(supplier_name="_Test Common Supplier USD").name
supp_doc = frappe.get_doc("Supplier", supplier)
supp_doc.default_currency = "USD"
supp_doc.save()
# create a party link between customer & supplier
party_link = create_party_link("Supplier", supplier, customer)
# enable common party accounting
frappe.db.set_single_value("Accounts Settings", "enable_common_party_accounting", 1)
# create a dimension and make it mandatory
if not frappe.get_all("Accounting Dimension", filters={"document_type": "Department"}):
dim = frappe.get_doc(
{
"doctype": "Accounting Dimension",
"document_type": "Department",
"dimension_defaults": [{"company": "_Test Company", "mandatory_for_bs": True}],
}
)
dim.save()
else:
dim = frappe.get_doc(
"Accounting Dimension",
frappe.get_all("Accounting Dimension", filters={"document_type": "Department"})[0],
)
dim.disabled = False
dim.dimension_defaults = []
dim.append("dimension_defaults", {"company": "_Test Company", "mandatory_for_bs": True})
dim.save()
# create a sales invoice
si = create_sales_invoice(
customer=customer, parent_cost_center="_Test Cost Center - _TC", do_not_submit=True
)
si.department = "All Departments"
si.save().submit()
# check outstanding of sales invoice
si.reload()
self.assertEqual(si.status, "Paid")
self.assertEqual(flt(si.outstanding_amount), 0.0)
# check creation of journal entry
jv = frappe.get_all(
"Journal Entry Account",
{
"account": si.debit_to,
"party_type": "Customer",
"party": si.customer,
"reference_type": si.doctype,
"reference_name": si.name,
"department": "All Departments",
},
pluck="credit_in_account_currency",
)
self.assertTrue(jv)
self.assertEqual(jv[0], si.grand_total)
dim.disabled = True
dim.save()
party_link.delete()
frappe.db.set_single_value("Accounts Settings", "enable_common_party_accounting", 0)
def test_sales_invoice_cancel_with_common_party_advance_jv(self):
from erpnext.accounts.doctype.opening_invoice_creation_tool.test_opening_invoice_creation_tool import (
make_customer,
)
from erpnext.accounts.doctype.party_link.party_link import create_party_link
from erpnext.buying.doctype.supplier.test_supplier import create_supplier
# create a customer
customer = make_customer(customer="_Test Common Supplier")
# create a supplier
supplier = create_supplier(supplier_name="_Test Common Supplier").name
# create a party link between customer & supplier
party_link = create_party_link("Supplier", supplier, customer)
# enable common party accounting
frappe.db.set_single_value("Accounts Settings", "enable_common_party_accounting", 1)
# create a sales invoice
si = create_sales_invoice(customer=customer)
# check creation of journal entry
jv = frappe.db.get_value(
"Journal Entry Account",
filters={
"reference_type": si.doctype,
"reference_name": si.name,
"docstatus": 1,
},
fieldname="parent",
)
self.assertTrue(jv)
# cancel sales invoice
si.cancel()
# check cancellation of journal entry
jv_status = frappe.db.get_value("Journal Entry", jv, "docstatus")
self.assertEqual(jv_status, 2)
party_link.delete()
frappe.db.set_single_value("Accounts Settings", "enable_common_party_accounting", 0)
def test_payment_statuses(self):
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
@@ -3648,6 +3768,65 @@ class TestSalesInvoice(FrappeTestCase):
check_gl_entries(self, pe.name, expected_gle, nowdate(), voucher_type="Payment Entry")
set_advance_flag(company="_Test Company", flag=0, default_account="")
def test_loyalty_points_redemption_with_shopping_cart(self):
from erpnext.accounts.doctype.loyalty_program.test_loyalty_program import (
create_records,
create_sales_invoice_record,
)
from erpnext.selling.doctype.sales_order.sales_order import make_sales_invoice
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
# Set up loyalty program
create_records()
frappe.db.set_value("Customer", "Test Loyalty Customer", "loyalty_program", "Test Single Loyalty")
create_sales_invoice_record(10).insert().submit()
# Create a sales order
so = make_sales_order(qty=10, do_not_save=True, customer="Test Loyalty Customer")
so.name = "_T-Sales Order LP-0001"
so.order_type = "Shopping Cart"
so.loyalty_points = 50
so.loyalty_amount = 50
so.insert()
so.submit()
# Create sales invoice from the sales order
si = make_sales_invoice(so.name)
from frappe.model.trace import traced_field_context
with traced_field_context(si.__class__, "loyalty_program", forbidden_values=[None]):
si.insert()
si.submit()
# Check if loyalty points are applied correctly
self.assertEqual(si.loyalty_program, "Test Single Loyalty")
self.assertEqual(si.loyalty_points, 50)
self.assertEqual(si.loyalty_amount, 50)
# Check GL entries for loyalty points redemption
gl_entries = frappe.get_all(
"GL Entry",
filters={"voucher_type": "Sales Invoice", "voucher_no": si.name},
fields=["account", "debit", "credit"],
)
loyalty_account = frappe.db.get_value("Loyalty Program", "Test Single Loyalty", "expense_account")
expected_gl_entries = [
{"account": si.debit_to, "debit": si.grand_total, "credit": 0},
{"account": si.items[0].income_account, "debit": 0, "credit": si.net_total},
{"account": loyalty_account, "debit": 50, "credit": 0},
]
for entry in expected_gl_entries:
self.assertTrue(
any(
gl_entry.account == entry["account"]
and gl_entry.debit == entry["debit"]
and gl_entry.credit == entry["credit"]
for gl_entry in gl_entries
)
)
def test_pulling_advance_based_on_debit_to(self):
from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry
@@ -3774,6 +3953,142 @@ class TestSalesInvoice(FrappeTestCase):
]
self.assertEqual(expected, actual)
def test_pos_returns_without_update_outstanding_for_self(self):
from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_sales_return
pos_profile = make_pos_profile()
pos_profile.payments = []
pos_profile.append("payments", {"default": 1, "mode_of_payment": "Cash"})
pos_profile.save()
pos = create_sales_invoice(qty=10, do_not_save=True)
pos.is_pos = 1
pos.pos_profile = pos_profile.name
pos.append(
"payments", {"mode_of_payment": "Bank Draft", "account": "_Test Bank - _TC", "amount": 500}
)
pos.append("payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 500})
pos.save().submit()
pos_return = make_sales_return(pos.name)
pos_return.update_outstanding_for_self = False
pos_return.save().submit()
gle = qb.DocType("GL Entry")
res = (
qb.from_(gle)
.select(gle.against_voucher)
.distinct()
.where(
gle.is_cancelled.eq(0) & gle.voucher_no.eq(pos_return.name) & gle.against_voucher.notnull()
)
.run(as_list=1)
)
self.assertEqual(len(res), 1)
self.assertEqual(res[0][0], pos_return.return_against)
def test_validation_on_opening_invoice_with_rounding(self):
si = create_sales_invoice(qty=1, rate=99.98, do_not_submit=True)
si.is_opening = "Yes"
si.items[0].income_account = "Temporary Opening - _TC"
si.save()
self.assertRaises(frappe.ValidationError, si.submit)
def _create_opening_roundoff_account(self, company_name):
liability_root = frappe.db.get_all(
"Account",
filters={"company": company_name, "root_type": "Liability", "disabled": 0},
order_by="lft",
limit=1,
)[0]
# setup round off account
if acc := frappe.db.exists(
"Account",
{
"account_name": "Round Off for Opening",
"account_type": "Round Off for Opening",
"company": company_name,
},
):
frappe.db.set_value("Company", company_name, "round_off_for_opening", acc)
else:
acc = frappe.new_doc("Account")
acc.company = company_name
acc.parent_account = liability_root.name
acc.account_name = "Round Off for Opening"
acc.account_type = "Round Off for Opening"
acc.save()
frappe.db.set_value("Company", company_name, "round_off_for_opening", acc.name)
def test_opening_invoice_with_rounding_adjustment(self):
si = create_sales_invoice(qty=1, rate=99.98, do_not_submit=True)
si.is_opening = "Yes"
si.items[0].income_account = "Temporary Opening - _TC"
si.save()
self._create_opening_roundoff_account(si.company)
si.reload()
si.submit()
res = frappe.db.get_all(
"GL Entry",
filters={"voucher_no": si.name, "is_opening": "Yes"},
fields=["account", "debit", "credit", "is_opening"],
)
self.assertEqual(len(res), 3)
def _create_opening_invoice_with_inclusive_tax(self):
si = create_sales_invoice(qty=1, rate=90, do_not_submit=True)
si.is_opening = "Yes"
si.items[0].income_account = "Temporary Opening - _TC"
item_template = si.items[0].as_dict()
item_template.name = None
item_template.rate = 55
si.append("items", item_template)
si.append(
"taxes",
{
"charge_type": "On Net Total",
"account_head": "_Test Account Service Tax - _TC",
"cost_center": "_Test Cost Center - _TC",
"description": "Testing...",
"rate": 5,
"included_in_print_rate": True,
},
)
# there will be 0.01 precision loss between Dr and Cr
# caused by 'included_in_print_tax' option
si.save()
return si
def test_rounding_validation_for_opening_with_inclusive_tax(self):
si = self._create_opening_invoice_with_inclusive_tax()
# 'Round Off for Opening' not set in Company master
# Ledger level validation must be thrown
self.assertRaises(frappe.ValidationError, si.submit)
def test_ledger_entries_on_opening_invoice_with_rounding_loss_by_inclusive_tax(self):
si = self._create_opening_invoice_with_inclusive_tax()
# 'Round Off for Opening' is set in Company master
self._create_opening_roundoff_account(si.company)
si.submit()
actual = frappe.db.get_all(
"GL Entry",
filters={"voucher_no": si.name, "is_opening": "Yes", "is_cancelled": False},
fields=["account", "debit", "credit", "is_opening"],
order_by="account,debit",
)
expected = [
{"account": "_Test Account Service Tax - _TC", "debit": 0.0, "credit": 6.9, "is_opening": "Yes"},
{"account": "Debtors - _TC", "debit": 145.0, "credit": 0.0, "is_opening": "Yes"},
{"account": "Round Off for Opening - _TC", "debit": 0.0, "credit": 0.01, "is_opening": "Yes"},
{"account": "Temporary Opening - _TC", "debit": 0.0, "credit": 138.09, "is_opening": "Yes"},
]
self.assertEqual(len(actual), 4)
self.assertEqual(expected, actual)
def set_advance_flag(company, flag, default_account):
frappe.db.set_value(

View File

@@ -37,6 +37,7 @@
"column_break_19",
"discount_percentage",
"discount_amount",
"distributed_discount_amount",
"base_rate_with_margin",
"section_break1",
"rate",
@@ -253,7 +254,7 @@
},
{
"collapsible": 1,
"collapsible_depends_on": "eval: doc.margin_type || doc.discount_amount",
"collapsible_depends_on": "eval: doc.margin_type || doc.discount_amount || doc.distributed_discount_amount",
"fieldname": "discount_and_margin",
"fieldtype": "Section Break",
"label": "Discount and Margin"
@@ -922,12 +923,18 @@
{
"fieldname": "column_break_ytgd",
"fieldtype": "Column Break"
},
{
"fieldname": "distributed_discount_amount",
"fieldtype": "Currency",
"label": "Distributed Discount Amount",
"options": "currency"
}
],
"idx": 1,
"istable": 1,
"links": [],
"modified": "2024-05-23 16:36:18.970862",
"modified": "2024-06-02 06:14:40.009020",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Item",

View File

@@ -45,6 +45,7 @@ class SalesInvoiceItem(Document):
discount_account: DF.Link | None
discount_amount: DF.Currency
discount_percentage: DF.Percent
distributed_discount_amount: DF.Currency
dn_detail: DF.Data | None
enable_deferred_revenue: DF.Check
expense_account: DF.Link | None

View File

@@ -38,6 +38,12 @@ frappe.ui.form.on("Subscription", {
__("Actions")
);
frm.add_custom_button(
__("Force-Fetch Subscription Updates"),
() => frm.trigger("force_fetch_subscription_updates"),
__("Actions")
);
frm.add_custom_button(
__("Cancel Subscription"),
() => frm.trigger("cancel_this_subscription"),
@@ -82,4 +88,11 @@ frappe.ui.form.on("Subscription", {
}
});
},
force_fetch_subscription_updates: function (frm) {
frm.call("force_fetch_subscription_updates").then((r) => {
if (!r.exec) {
frm.reload_doc();
}
});
},
});

View File

@@ -717,6 +717,28 @@ class Subscription(Document):
self.update_subscription_period(posting_date or nowdate())
self.save()
@frappe.whitelist()
def force_fetch_subscription_updates(self):
"""
Process Subscription and create Invoices even if current date doesn't lie between current_invoice_start and currenct_invoice_end
It makes use of 'Proces Subscription' to force processing in a specific 'posting_date'
"""
# Don't process future subscriptions
if nowdate() < self.current_invoice_start:
frappe.msgprint(_("Subscription for Future dates cannot be processed."))
return
processing_date = None
if self.generate_invoice_at == "Beginning of the current subscription period":
processing_date = self.current_invoice_start
elif self.generate_invoice_at == "End of the current subscription period":
processing_date = self.current_invoice_end
elif self.generate_invoice_at == "Days before the current subscription period":
processing_date = add_days(self.current_invoice_start, -self.number_of_days)
self.process(posting_date=processing_date)
def is_prorate() -> int:
return cint(frappe.db.get_single_value("Subscription Settings", "prorate"))

View File

@@ -521,6 +521,18 @@ class TestSubscription(FrappeTestCase):
subscription.process(posting_date="2023-01-22")
self.assertEqual(len(subscription.invoices), 2)
def test_future_subscription(self):
"""Force-Fetch should not process future subscriptions"""
subscription = create_subscription(
start_date=add_months(nowdate(), 1),
submit_invoice=0,
generate_new_invoices_past_due_date=1,
party="_Test Subscription Customer John Doe",
)
subscription.force_fetch_subscription_updates()
subscription.reload()
self.assertEqual(len(subscription.invoices), 0)
def make_plans():
create_plan(plan_name="_Test Plan Name", cost=900, currency="INR")

View File

@@ -268,6 +268,11 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N
vouchers, voucher_wise_amount = get_invoice_vouchers(
parties, tax_details, inv.company, party_type=party_type
)
payment_entry_vouchers = get_payment_entry_vouchers(
parties, tax_details, inv.company, party_type=party_type
)
advance_vouchers = get_advance_vouchers(
parties,
company=inv.company,
@@ -275,7 +280,8 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N
to_date=tax_details.to_date,
party_type=party_type,
)
taxable_vouchers = vouchers + advance_vouchers
taxable_vouchers = vouchers + advance_vouchers + payment_entry_vouchers
tax_deducted_on_advances = 0
if inv.doctype == "Purchase Invoice":
@@ -369,12 +375,14 @@ def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"):
AND ja.party in %s
AND j.apply_tds = 1
AND j.tax_withholding_category = %s
AND j.company = %s
""",
(
tax_details.from_date,
tax_details.to_date,
tuple(parties),
tax_details.get("tax_withholding_category"),
company,
),
as_dict=1,
)
@@ -387,6 +395,20 @@ def get_invoice_vouchers(parties, tax_details, company, party_type="Supplier"):
return vouchers, voucher_wise_amount
def get_payment_entry_vouchers(parties, tax_details, company, party_type="Supplier"):
payment_entry_filters = {
"party_type": party_type,
"party": ("in", parties),
"docstatus": 1,
"apply_tax_withholding_amount": 1,
"posting_date": ["between", (tax_details.from_date, tax_details.to_date)],
"tax_withholding_category": tax_details.get("tax_withholding_category"),
"company": company,
}
return frappe.db.get_all("Payment Entry", filters=payment_entry_filters, pluck="name")
def get_advance_vouchers(parties, company=None, from_date=None, to_date=None, party_type="Supplier"):
"""
Use Payment Ledger to fetch unallocated Advance Payments
@@ -477,6 +499,7 @@ def get_tds_amount(ldc, parties, inv, tax_details, vouchers):
"unallocated_amount": (">", 0),
"posting_date": ["between", (tax_details.from_date, tax_details.to_date)],
"tax_withholding_category": tax_details.get("tax_withholding_category"),
"company": inv.company,
}
field = "sum(tax_withholding_net_total)"

View File

@@ -1,113 +0,0 @@
{
"creation": "2021-06-29 17:00:18.273054",
"docstatus": 0,
"doctype": "Form Tour",
"idx": 0,
"is_standard": 1,
"modified": "2024-01-24 02:20:26.145996",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Settings",
"owner": "Administrator",
"reference_doctype": "Accounts Settings",
"save_on_complete": 0,
"steps": [
{
"description": "The percentage by which you can overbill transactions. For example, if the order value is $100 for an Item and percentage here is set as 10% then you are allowed to bill for $110.",
"field": "",
"fieldname": "over_billing_allowance",
"fieldtype": "Currency",
"has_next_condition": 0,
"is_table_field": 0,
"label": "Over Billing Allowance (%)",
"parent_field": "",
"position": "Right",
"title": "Over Billing Allowance Percentage"
},
{
"description": "Select the role that is allowed to overbill a transactions.",
"field": "",
"fieldname": "role_allowed_to_over_bill",
"fieldtype": "Link",
"has_next_condition": 0,
"is_table_field": 0,
"label": "Role Allowed to Over Bill ",
"parent_field": "",
"position": "Right",
"title": "Role Allowed to Over Bill"
},
{
"description": "If checked, system will unlink the payment against the respective invoice.",
"field": "",
"fieldname": "unlink_payment_on_cancellation_of_invoice",
"fieldtype": "Check",
"has_next_condition": 0,
"is_table_field": 0,
"label": "Unlink Payment on Cancellation of Invoice",
"parent_field": "",
"position": "Bottom",
"title": "Unlink Payment on Cancellation of Invoice"
},
{
"description": "Similar to the previous option, this unlinks any advance payments made against Purchase/Sales Orders.",
"field": "",
"fieldname": "unlink_advance_payment_on_cancelation_of_order",
"fieldtype": "Check",
"has_next_condition": 0,
"is_table_field": 0,
"label": "Unlink Advance Payment on Cancellation of Order",
"parent_field": "",
"position": "Bottom",
"title": "Unlink Advance Payment on Cancellation of Order"
},
{
"description": "Tax category can be set on Addresses. An address can be Shipping or Billing address. Set which addres to select when applying Tax Category.",
"field": "",
"fieldname": "determine_address_tax_category_from",
"fieldtype": "Select",
"has_next_condition": 0,
"is_table_field": 0,
"label": "Determine Address Tax Category From",
"parent_field": "",
"position": "Right",
"title": "Determine Address Tax Category From"
},
{
"description": "Freeze accounting transactions up to specified date, nobody can make/modify entry except the specified Role.",
"field": "",
"fieldname": "acc_frozen_upto",
"fieldtype": "Date",
"has_next_condition": 0,
"is_table_field": 0,
"label": "Accounts Frozen Till Date",
"parent_field": "",
"position": "Right",
"title": "Accounts Frozen Up To"
},
{
"description": "Users with this Role are allowed to set frozen accounts and create/modify accounting entries against frozen accounts.",
"field": "",
"fieldname": "frozen_accounts_modifier",
"fieldtype": "Link",
"has_next_condition": 0,
"is_table_field": 0,
"label": "Role Allowed to Set Frozen Accounts and Edit Frozen Entries",
"parent_field": "",
"position": "Right",
"title": "Role Allowed to Set Frozen Accounts & Edit Frozen Entries"
},
{
"description": "Select the role that is allowed to submit transactions that exceed credit limits set. The credit limit can be set in the Customer form.",
"field": "",
"fieldname": "credit_controller",
"fieldtype": "Link",
"has_next_condition": 0,
"is_table_field": 0,
"label": "Credit Controller",
"parent_field": "",
"position": "Left",
"title": "Credit Controller"
}
],
"title": "Accounts Settings"
}

View File

@@ -1,96 +0,0 @@
{
"creation": "2021-06-29 16:31:48.558826",
"docstatus": 0,
"doctype": "Form Tour",
"idx": 0,
"is_standard": 1,
"modified": "2021-06-29 16:31:48.558826",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice",
"owner": "Administrator",
"reference_doctype": "Purchase Invoice",
"save_on_complete": 1,
"steps": [
{
"description": "Select Supplier",
"field": "",
"fieldname": "supplier",
"fieldtype": "Link",
"has_next_condition": 1,
"is_table_field": 0,
"label": "Supplier",
"next_step_condition": "supplier",
"parent_field": "",
"position": "Right",
"title": "Select Supplier"
},
{
"description": "Add items in the table",
"field": "",
"fieldname": "items",
"fieldtype": "Table",
"has_next_condition": 0,
"is_table_field": 0,
"label": "Items",
"parent_field": "",
"position": "Bottom",
"title": "List of Items"
},
{
"child_doctype": "Purchase Invoice Item",
"description": "Select an item",
"field": "",
"fieldname": "item_code",
"fieldtype": "Link",
"has_next_condition": 0,
"is_table_field": 1,
"label": "Item",
"parent_field": "",
"parent_fieldname": "items",
"position": "Right",
"title": "Select Item"
},
{
"child_doctype": "Purchase Invoice Item",
"description": "Enter the quantity",
"field": "",
"fieldname": "qty",
"fieldtype": "Float",
"has_next_condition": 0,
"is_table_field": 1,
"label": "Accepted Qty",
"parent_field": "",
"parent_fieldname": "items",
"position": "Right",
"title": "Enter Quantity"
},
{
"child_doctype": "Purchase Invoice Item",
"description": "Enter rate of the item",
"field": "",
"fieldname": "rate",
"fieldtype": "Currency",
"has_next_condition": 0,
"is_table_field": 1,
"label": "Rate",
"parent_field": "",
"parent_fieldname": "items",
"position": "Right",
"title": "Enter Rate"
},
{
"description": "You can add taxes here",
"field": "",
"fieldname": "taxes",
"fieldtype": "Table",
"has_next_condition": 0,
"is_table_field": 0,
"label": "Purchase Taxes and Charges",
"parent_field": "",
"position": "Bottom",
"title": "Select taxes"
}
],
"title": "Purchase Invoice"
}

View File

@@ -1,41 +0,0 @@
{
"creation": "2023-05-23 09:58:17.235916",
"docstatus": 0,
"doctype": "Form Tour",
"first_document": 0,
"idx": 0,
"include_name_field": 0,
"is_standard": 1,
"modified": "2023-05-23 13:10:56.227127",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",
"owner": "Administrator",
"reference_doctype": "Sales Invoice",
"save_on_complete": 1,
"steps": [
{
"description": "Select a customer for whom this invoice is being prepared.",
"fieldname": "customer",
"fieldtype": "Link",
"has_next_condition": 1,
"is_table_field": 0,
"label": "Customer",
"next_step_condition": "eval: doc.customer",
"position": "Right",
"title": "Select Customer"
},
{
"child_doctype": "Sales Invoice Item",
"description": "Select item that you have sold along with quantity and rate.",
"fieldname": "items",
"fieldtype": "Table",
"has_next_condition": 0,
"is_table_field": 0,
"parent_fieldname": "items",
"position": "Top",
"title": "Select Item"
}
],
"title": "Sales Invoice"
}

View File

@@ -1,67 +0,0 @@
{
"creation": "2021-08-24 12:28:18.044902",
"docstatus": 0,
"doctype": "Form Tour",
"first_document": 0,
"idx": 0,
"include_name_field": 0,
"is_standard": 1,
"modified": "2022-01-18 18:32:17.102330",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Taxes and Charges Template",
"owner": "Administrator",
"reference_doctype": "Sales Taxes and Charges Template",
"save_on_complete": 1,
"steps": [
{
"description": "A name by which you will identify this template. You can change this later.",
"field": "",
"fieldname": "title",
"fieldtype": "Data",
"has_next_condition": 0,
"is_table_field": 0,
"label": "Title",
"parent_field": "",
"position": "Bottom",
"title": "Title"
},
{
"description": "Company for which this tax template will be applicable",
"field": "",
"fieldname": "company",
"fieldtype": "Link",
"has_next_condition": 0,
"is_table_field": 0,
"label": "Company",
"parent_field": "",
"position": "Bottom",
"title": "Company"
},
{
"description": "Set this template as the default for all sales transactions",
"field": "",
"fieldname": "is_default",
"fieldtype": "Check",
"has_next_condition": 0,
"is_table_field": 0,
"label": "Default",
"parent_field": "",
"position": "Bottom",
"title": "Is this Default Tax Template?"
},
{
"description": "You can add a row for a tax rule here. These rules can be applied on the net total, or can be a flat amount.",
"field": "",
"fieldname": "taxes",
"fieldtype": "Table",
"has_next_condition": 0,
"is_table_field": 0,
"label": "Sales Taxes and Charges",
"parent_field": "",
"position": "Bottom",
"title": "Taxes Table"
}
],
"title": "Sales Taxes and Charges Template"
}

View File

@@ -7,7 +7,7 @@ import copy
import frappe
from frappe import _
from frappe.model.meta import get_field_precision
from frappe.utils import cint, flt, formatdate, getdate, now
from frappe.utils import cint, flt, formatdate, get_link_to_form, getdate, now
from frappe.utils.dashboard import cache_source
import erpnext
@@ -490,16 +490,36 @@ def raise_debit_credit_not_equal_error(debit_credit_diff, voucher_type, voucher_
)
def has_opening_entries(gl_map: list) -> bool:
for x in gl_map:
if x.is_opening == "Yes":
return True
return False
def make_round_off_gle(gl_map, debit_credit_diff, precision):
round_off_account, round_off_cost_center = get_round_off_account_and_cost_center(
round_off_account, round_off_cost_center, round_off_for_opening = get_round_off_account_and_cost_center(
gl_map[0].company, gl_map[0].voucher_type, gl_map[0].voucher_no
)
round_off_gle = frappe._dict()
round_off_account_exists = False
has_opening_entry = has_opening_entries(gl_map)
if has_opening_entry:
if not round_off_for_opening:
frappe.throw(
_("Please set '{0}' in Company: {1}").format(
frappe.bold("Round Off for Opening"), get_link_to_form("Company", gl_map[0].company)
)
)
account = round_off_for_opening
else:
account = round_off_account
if gl_map[0].voucher_type != "Period Closing Voucher":
for d in gl_map:
if d.account == round_off_account:
if d.account == account:
round_off_gle = d
if d.debit:
debit_credit_diff -= flt(d.debit) - flt(d.credit)
@@ -517,7 +537,7 @@ def make_round_off_gle(gl_map, debit_credit_diff, precision):
round_off_gle.update(
{
"account": round_off_account,
"account": account,
"debit_in_account_currency": abs(debit_credit_diff) if debit_credit_diff < 0 else 0,
"credit_in_account_currency": debit_credit_diff if debit_credit_diff > 0 else 0,
"debit": abs(debit_credit_diff) if debit_credit_diff < 0 else 0,
@@ -531,6 +551,9 @@ def make_round_off_gle(gl_map, debit_credit_diff, precision):
}
)
if has_opening_entry:
round_off_gle.update({"is_opening": "Yes"})
update_accounting_dimensions(round_off_gle)
if not round_off_account_exists:
gl_map.append(round_off_gle)
@@ -555,9 +578,9 @@ def update_accounting_dimensions(round_off_gle):
def get_round_off_account_and_cost_center(company, voucher_type, voucher_no, use_company_default=False):
round_off_account, round_off_cost_center = frappe.get_cached_value(
"Company", company, ["round_off_account", "round_off_cost_center"]
) or [None, None]
round_off_account, round_off_cost_center, round_off_for_opening = frappe.get_cached_value(
"Company", company, ["round_off_account", "round_off_cost_center", "round_off_for_opening"]
) or [None, None, None]
# Use expense account as fallback
if not round_off_account:
@@ -572,12 +595,20 @@ def get_round_off_account_and_cost_center(company, voucher_type, voucher_no, use
round_off_cost_center = parent_cost_center
if not round_off_account:
frappe.throw(_("Please mention Round Off Account in Company"))
frappe.throw(
_("Please mention '{0}' in Company: {1}").format(
frappe.bold("Round Off Account"), get_link_to_form("Company", company)
)
)
if not round_off_cost_center:
frappe.throw(_("Please mention Round Off Cost Center in Company"))
frappe.throw(
_("Please mention '{0}' in Company: {1}").format(
frappe.bold("Round Off Cost Center"), get_link_to_form("Company", company)
)
)
return round_off_account, round_off_cost_center
return round_off_account, round_off_cost_center, round_off_for_opening
def make_reverse_gl_entries(

View File

@@ -1,47 +0,0 @@
{
"allow_roles": [
{
"role": "Accounts Manager"
},
{
"role": "Accounts User"
}
],
"creation": "2020-05-13 19:03:32.564049",
"docstatus": 0,
"doctype": "Module Onboarding",
"documentation_url": "https://docs.erpnext.com/docs/user/manual/en/accounts",
"idx": 0,
"is_complete": 0,
"modified": "2022-06-14 17:38:24.967834",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts",
"owner": "Administrator",
"steps": [
{
"step": "Chart of Accounts"
},
{
"step": "Setup Taxes"
},
{
"step": "Accounts Settings"
},
{
"step": "Cost Centers for Report and Budgeting"
},
{
"step": "Create Your First Purchase Invoice"
},
{
"step": "Updating Opening Balances"
},
{
"step": "Financial Statements"
}
],
"subtitle": "Accounts, Invoices, Taxation, and more.",
"success_message": "The Accounts Module is all set up!",
"title": "Let's Set Up Your Accounts and Taxes."
}

View File

@@ -1,21 +0,0 @@
{
"action": "Show Form Tour",
"action_label": "Take a quick walk-through of Accounts Settings",
"creation": "2021-06-29 16:42:03.400731",
"description": "# Account Settings\n\nIn ERPNext, Accounting features are configurable as per your business needs. Accounts Settings is the place to define some of your accounting preferences like:\n\n - Credit Limit and over billing settings\n - Taxation preferences\n - Deferred accounting preferences\n",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
"is_single": 1,
"is_skipped": 0,
"modified": "2021-08-13 11:50:06.227835",
"modified_by": "Administrator",
"name": "Accounts Settings",
"owner": "Administrator",
"reference_document": "Accounts Settings",
"show_form_tour": 0,
"show_full_form": 0,
"title": "Accounts Settings",
"validate_action": 1
}

View File

@@ -1,26 +0,0 @@
{
"action": "Go to Page",
"action_label": "Learn more about Chart of Accounts",
"callback_message": "You can continue with the onboarding after exploring this page",
"callback_title": "Explore Chart of Accounts",
"creation": "2020-05-13 19:58:20.928127",
"description": "# Chart Of Accounts\n\nERPNext sets up a simple chart of accounts for each Company you create, but you can modify it according to business and legal requirements.",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"intro_video_url": "https://www.youtube.com/embed/AcfMCT7wLLo",
"is_complete": 0,
"is_single": 0,
"is_skipped": 0,
"modified": "2022-06-07 14:21:26.264769",
"modified_by": "Administrator",
"name": "Chart of Accounts",
"owner": "Administrator",
"path": "Tree/Account",
"reference_document": "Account",
"show_form_tour": 0,
"show_full_form": 0,
"title": "Review Chart of Accounts",
"validate_action": 0,
"video_url": "https://www.youtube.com/embed/AcfMCT7wLLo"
}

View File

@@ -1,20 +0,0 @@
{
"action": "Show Form Tour",
"creation": "2020-05-14 17:53:00.876946",
"description": "# Account Settings\n\nThis is a crucial piece of configuration. There are various account settings in ERPNext to restrict and configure actions in the Accounting module.\n\nThe following settings are avaialble for you to configure\n\n1. Account Freezing \n2. Credit and Overbilling\n3. Invoicing and Tax Automations\n4. Balance Sheet configurations\n\nThere's much more, you can check it all out in this step",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
"is_single": 1,
"is_skipped": 0,
"modified": "2020-10-19 14:40:55.584484",
"modified_by": "Administrator",
"name": "Configure Account Settings",
"owner": "Administrator",
"reference_document": "Accounts Settings",
"show_form_tour": 0,
"show_full_form": 1,
"title": "Configure Account Settings",
"validate_action": 1
}

View File

@@ -1,21 +0,0 @@
{
"action": "Go to Page",
"action_label": "View Cost Center Tree",
"creation": "2021-07-12 12:02:05.726608",
"description": "# Cost Centers for Budgeting and Analysis\n\nWhile your Books of Accounts are framed to fulfill statutory requirements, you can set up Cost Center and Accounting Dimensions to address your companies reporting and budgeting requirements.\n\nClick here to learn more about how <b>[Cost Center](https://docs.erpnext.com/docs/v13/user/manual/en/accounts/cost-center)</b> and <b> [Dimensions](https://docs.erpnext.com/docs/v13/user/manual/en/accounts/accounting-dimensions)</b> allow you to get advanced financial analytics reports from ERPNext.",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
"is_single": 0,
"is_skipped": 0,
"modified": "2021-08-13 11:55:08.510366",
"modified_by": "Administrator",
"name": "Cost Centers for Report and Budgeting",
"owner": "Administrator",
"path": "cost-center/view/tree",
"show_form_tour": 0,
"show_full_form": 0,
"title": "Cost Centers for Budgeting and Analysis",
"validate_action": 1
}

View File

@@ -1,20 +0,0 @@
{
"action": "Create Entry",
"creation": "2020-05-14 17:46:41.831517",
"description": "## Who is a Customer?\n\nA customer, who is sometimes known as a client, buyer, or purchaser is the one who receives goods, services, products, or ideas, from a seller for a monetary consideration.\n\nEvery customer needs to be assigned a unique id. Customer name itself can be the id or you can set a naming series for ids to be generated in Selling Settings.\n\nJust like the supplier, let's quickly create a customer.",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
"is_single": 0,
"is_skipped": 0,
"modified": "2020-10-30 15:28:46.659660",
"modified_by": "Administrator",
"name": "Create a Customer",
"owner": "Administrator",
"reference_document": "Customer",
"show_form_tour": 0,
"show_full_form": 0,
"title": "Create a Customer",
"validate_action": 1
}

View File

@@ -1,21 +0,0 @@
{
"action": "Create Entry",
"creation": "2020-05-12 18:16:06.624554",
"description": "## Products and Services\n\nDepending on the nature of your business, you might be selling products or services to your clients or even both. \nERPNext is optimized for itemized management of your sales and purchase.\n\nThe **Item Master** is where you can add all your sales items. If you are in services, you can create an Item for each service that you offer. If you run a manufacturing business, the same master is used for keeping a record of raw materials, sub-assemblies etc.\n\nCompleting the Item Master is very essential for the successful implementation of ERPNext. We have a brief video introducing the item master for you, you can watch it in the next step.",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"intro_video_url": "https://www.youtube.com/watch?v=Sl5UFA5H5EQ",
"is_complete": 0,
"is_single": 0,
"is_skipped": 0,
"modified": "2020-10-30 15:20:30.133495",
"modified_by": "Administrator",
"name": "Create a Product",
"owner": "Administrator",
"reference_document": "Item",
"show_form_tour": 0,
"show_full_form": 0,
"title": "Create a Sales Item",
"validate_action": 1
}

View File

@@ -1,20 +0,0 @@
{
"action": "Create Entry",
"creation": "2020-05-14 22:09:10.043554",
"description": "## Who is a Supplier?\n\nSuppliers are companies or individuals who provide you with products or services. ERPNext has comprehensive features for purchase cycles. \n\nLet's quickly create a supplier with the minimal details required. You need the name of the supplier, assign the supplier to a group, and select the type of the supplier, viz. Company or Individual.",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
"is_single": 0,
"is_skipped": 0,
"modified": "2020-10-30 15:26:48.315772",
"modified_by": "Administrator",
"name": "Create a Supplier",
"owner": "Administrator",
"reference_document": "Supplier",
"show_form_tour": 0,
"show_full_form": 0,
"title": "Create a Supplier",
"validate_action": 1
}

View File

@@ -1,21 +0,0 @@
{
"action": "Show Form Tour",
"action_label": "Let\u2019s create your first Purchase Invoice",
"creation": "2020-05-14 22:10:07.049704",
"description": "# Create your first Purchase Invoice\n\nA Purchase Invoice is a bill received from a Supplier for a product(s) or service(s) delivery to your company. You can track payables through Purchase Invoice and process Payment Entries against it.\n\nPurchase Invoices can also be created against a Purchase Order or Purchase Receipt.",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
"is_single": 0,
"is_skipped": 0,
"modified": "2021-08-13 11:56:11.677253",
"modified_by": "Administrator",
"name": "Create Your First Purchase Invoice",
"owner": "Administrator",
"reference_document": "Purchase Invoice",
"show_form_tour": 0,
"show_full_form": 1,
"title": "Create Your First Purchase Invoice ",
"validate_action": 1
}

View File

@@ -1,20 +0,0 @@
{
"action": "Create Entry",
"creation": "2020-05-14 17:48:21.019019",
"description": "# All about sales invoice\n\nA Sales Invoice is a bill that you send to your Customers against which the Customer makes the payment. Sales Invoice is an accounting transaction. On submission of Sales Invoice, the system updates the receivable and books income against a Customer Account.\n\nHere's the flow of how a sales invoice is generally created\n\n\n![Sales Flow](https://docs.erpnext.com/docs/assets/img/accounts/so-flow.png)",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
"is_single": 0,
"is_skipped": 0,
"modified": "2020-10-16 12:59:16.987507",
"modified_by": "Administrator",
"name": "Create Your First Sales Invoice",
"owner": "Administrator",
"reference_document": "Sales Invoice",
"show_form_tour": 0,
"show_full_form": 1,
"title": "Create Your First Sales Invoice ",
"validate_action": 1
}

View File

@@ -1,23 +0,0 @@
{
"action": "View Report",
"creation": "2021-07-12 12:08:47.026115",
"description": "# Financial Statements\n\nIn ERPNext, you can get crucial financial reports like [Balance Sheet] and [Profit and Loss] statements with a click of a button. You can run in the report for a different period and plot analytics charts premised on statement data. For more reports, check sections like Financial Statements, General Ledger, and Profitability reports.\n\n<b>[Check Accounting reports](https://docs.erpnext.com/docs/v13/user/manual/en/accounts/accounting-reports)</b>",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
"is_single": 0,
"is_skipped": 0,
"modified": "2021-08-13 11:59:18.767407",
"modified_by": "Administrator",
"name": "Financial Statements",
"owner": "Administrator",
"reference_report": "General Ledger",
"report_description": "General Ledger",
"report_reference_doctype": "GL Entry",
"report_type": "Script Report",
"show_form_tour": 0,
"show_full_form": 0,
"title": "Financial Statements",
"validate_action": 1
}

View File

@@ -1,21 +0,0 @@
{
"action": "Create Entry",
"action_label": "Manage Sales Tax Templates",
"creation": "2020-05-13 19:29:43.844463",
"description": "# Setting up Taxes\n\nERPNext lets you configure your taxes so that they are automatically applied in your buying and selling transactions. You can configure them globally or even on Items. ERPNext taxes are pre-configured for most regions.",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"is_complete": 0,
"is_single": 0,
"is_skipped": 0,
"modified": "2022-06-14 17:37:56.694261",
"modified_by": "Administrator",
"name": "Setup Taxes",
"owner": "Administrator",
"reference_document": "Sales Taxes and Charges Template",
"show_form_tour": 1,
"show_full_form": 1,
"title": "Setting up Taxes",
"validate_action": 0
}

View File

@@ -1,22 +0,0 @@
{
"action": "Watch Video",
"action_label": "Learn how to update opening balances",
"creation": "2021-07-12 11:53:50.525030",
"description": "# Updating Opening Balances\n\nOnce you close the financial statement in previous accounting software, you can update the same as opening in your ERPNext's Balance Sheet accounts. This will allow you to get complete financial statements from ERPNext in the coming years, and discontinue the parallel accounting system right away.",
"docstatus": 0,
"doctype": "Onboarding Step",
"idx": 0,
"intro_video_url": "https://www.youtube.com/embed/U5wPIvEn-0c",
"is_complete": 0,
"is_single": 0,
"is_skipped": 0,
"modified": "2021-08-13 11:56:45.483418",
"modified_by": "Administrator",
"name": "Updating Opening Balances",
"owner": "Administrator",
"show_form_tour": 0,
"show_full_form": 0,
"title": "Updating Opening Balances",
"validate_action": 1,
"video_url": "https://www.youtube.com/embed/U5wPIvEn-0c"
}

View File

@@ -162,6 +162,11 @@ frappe.query_reports["Accounts Payable"] = {
label: __("Group by Voucher"),
fieldtype: "Check",
},
{
fieldname: "handle_employee_advances",
label: __("Handle Employee Advances"),
fieldtype: "Check",
},
],
formatter: function (value, row, column, data, default_formatter) {

View File

@@ -112,6 +112,26 @@ class ReceivablePayableReport:
self.build_data()
def build_voucher_dict(self, ple):
return frappe._dict(
voucher_type=ple.voucher_type,
voucher_no=ple.voucher_no,
party=ple.party,
party_account=ple.account,
posting_date=ple.posting_date,
account_currency=ple.account_currency,
remarks=ple.remarks,
invoiced=0.0,
paid=0.0,
credit_note=0.0,
outstanding=0.0,
invoiced_in_account_currency=0.0,
paid_in_account_currency=0.0,
credit_note_in_account_currency=0.0,
outstanding_in_account_currency=0.0,
cost_center=ple.cost_center,
)
def init_voucher_balance(self):
# build all keys, since we want to exclude vouchers beyond the report date
for ple in self.ple_entries:
@@ -123,23 +143,8 @@ class ReceivablePayableReport:
key = (ple.account, ple.voucher_type, ple.voucher_no, ple.party)
if key not in self.voucher_balance:
self.voucher_balance[key] = frappe._dict(
voucher_type=ple.voucher_type,
voucher_no=ple.voucher_no,
party=ple.party,
party_account=ple.account,
posting_date=ple.posting_date,
account_currency=ple.account_currency,
remarks=ple.remarks,
invoiced=0.0,
paid=0.0,
credit_note=0.0,
outstanding=0.0,
invoiced_in_account_currency=0.0,
paid_in_account_currency=0.0,
credit_note_in_account_currency=0.0,
outstanding_in_account_currency=0.0,
)
self.voucher_balance[key] = self.build_voucher_dict(ple)
self.get_invoices(ple)
if self.filters.get("group_by_party"):
@@ -207,6 +212,18 @@ class ReceivablePayableReport:
row = self.voucher_balance.get(key)
# Build and use a separate row for Employee Advances.
# This allows Payments or Journals made against Emp Advance to be processed.
if (
not row
and ple.against_voucher_type == "Employee Advance"
and self.filters.handle_employee_advances
):
_d = self.build_voucher_dict(ple)
_d.voucher_type = ple.against_voucher_type
_d.voucher_no = ple.against_voucher_no
row = self.voucher_balance[key] = _d
if not row:
# no invoice, this is an invoice / stand-alone payment / credit note
if self.filters.get("ignore_accounts"):
@@ -253,7 +270,7 @@ class ReceivablePayableReport:
row.paid -= amount
row.paid_in_account_currency -= amount_in_account_currency
if ple.cost_center:
if not row.cost_center and ple.cost_center:
row.cost_center = str(ple.cost_center)
def update_sub_total_row(self, row, party):

View File

@@ -53,11 +53,13 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
si = si.submit()
return si
def create_payment_entry(self, docname):
def create_payment_entry(self, docname, do_not_submit=False):
pe = get_payment_entry("Sales Invoice", docname, bank_account=self.cash, party_amount=40)
pe.paid_from = self.debit_to
pe.insert()
pe.submit()
if not do_not_submit:
pe.submit()
return pe
def create_credit_note(self, docname, do_not_submit=False):
credit_note = create_sales_invoice(
@@ -984,3 +986,40 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase):
expected_data_after_payment,
[row.invoice_grand_total, row.invoiced, row.paid, row.outstanding],
)
def test_cost_center_on_report_output(self):
filters = {
"company": self.company,
"report_date": today(),
"range1": 30,
"range2": 60,
"range3": 90,
"range4": 120,
}
# check invoice grand total and invoiced column's value for 3 payment terms
si = self.create_sales_invoice(no_payment_schedule=True, do_not_submit=True)
si.cost_center = self.cost_center
si.save().submit()
new_cc = frappe.get_doc(
{
"doctype": "Cost Center",
"cost_center_name": "East Wing",
"parent_cost_center": self.company + " - " + self.company_abbr,
"company": self.company,
}
)
new_cc.save()
# check invoice grand total, invoiced, paid and outstanding column's value after payment
pe = self.create_payment_entry(si.name, do_not_submit=True)
pe.cost_center = new_cc.name
pe.save().submit()
report = execute(filters)
expected_data_after_payment = [si.name, si.cost_center, 60]
self.assertEqual(len(report[1]), 1)
row = report[1][0]
self.assertEqual(expected_data_after_payment, [row.voucher_no, row.cost_center, row.outstanding])

View File

@@ -46,5 +46,11 @@ frappe.query_reports["Asset Depreciations and Balances"] = {
options: "Asset",
depends_on: "eval: doc.group_by == 'Asset'",
},
{
fieldname: "finance_book",
label: __("Finance Book"),
fieldtype: "Link",
options: "Finance Book",
},
],
};

View File

@@ -69,6 +69,9 @@ def get_asset_categories_for_grouped_by_category(filters):
condition = ""
if filters.get("asset_category"):
condition += " and asset_category = %(asset_category)s"
if filters.get("finance_book"):
condition += " and exists (select 1 from `tabAsset Depreciation Schedule` ads where ads.asset = a.name and ads.finance_book = %(finance_book)s)"
# nosemgrep
return frappe.db.sql(
f"""
@@ -119,6 +122,7 @@ def get_asset_categories_for_grouped_by_category(filters):
"from_date": filters.from_date,
"company": filters.company,
"asset_category": filters.get("asset_category"),
"finance_book": filters.get("finance_book"),
},
as_dict=1,
)
@@ -128,6 +132,10 @@ def get_asset_details_for_grouped_by_category(filters):
condition = ""
if filters.get("asset"):
condition += " and name = %(asset)s"
if filters.get("finance_book"):
condition += " and exists (select 1 from `tabAsset Depreciation Schedule` ads where ads.asset = `tabAsset`.name and ads.finance_book = %(finance_book)s)"
# nosemgrep
return frappe.db.sql(
f"""
SELECT name,
@@ -176,6 +184,7 @@ def get_asset_details_for_grouped_by_category(filters):
"from_date": filters.from_date,
"company": filters.company,
"asset": filters.get("asset"),
"finance_book": filters.get("finance_book"),
},
as_dict=1,
)

View File

@@ -46,4 +46,20 @@ frappe.query_reports["Bank Reconciliation Statement"] = {
fieldtype: "Check",
},
],
formatter: function (value, row, column, data, default_formatter, filter) {
if (column.fieldname == "payment_entry" && value == "Cheques and Deposits incorrectly cleared") {
column.link_onclick =
"frappe.query_reports['Bank Reconciliation Statement'].open_utility_report()";
}
return default_formatter(value, row, column, data);
},
open_utility_report: function () {
frappe.route_options = {
company: frappe.query_report.get_filter_value("company"),
account: frappe.query_report.get_filter_value("account"),
report_date: frappe.query_report.get_filter_value("report_date"),
};
frappe.open_in_new_tab = true;
frappe.set_route("query-report", "Cheques and Deposits Incorrectly cleared");
},
};

View File

@@ -154,8 +154,8 @@ def get_payment_entries(filters):
select
"Payment Entry" as payment_document, name as payment_entry,
reference_no, reference_date as ref_date,
if(paid_to=%(account)s, received_amount, 0) as debit,
if(paid_from=%(account)s, paid_amount, 0) as credit,
if(paid_to=%(account)s, received_amount_after_tax, 0) as debit,
if(paid_from=%(account)s, paid_amount_after_tax, 0) as credit,
posting_date, ifnull(party,if(paid_from=%(account)s,paid_to,paid_from)) as against_account, clearance_date,
if(paid_to=%(account)s, paid_to_account_currency, paid_from_account_currency) as account_currency
from `tabPayment Entry`

View File

@@ -1,7 +1,10 @@
// Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.query_reports["Cash Flow"] = $.extend({}, erpnext.financial_statements);
frappe.query_reports["Cash Flow"] = $.extend(erpnext.financial_statements, {
name_field: "section",
parent_field: "parent_section",
});
erpnext.utils.add_dimensions("Cash Flow", 10);

View File

@@ -30,7 +30,7 @@ def execute(filters=None):
company=filters.company,
)
cash_flow_accounts = get_cash_flow_accounts()
cash_flow_sections = get_cash_flow_accounts()
# compute net profit / loss
income = get_data(
@@ -60,14 +60,14 @@ def execute(filters=None):
summary_data = {}
company_currency = frappe.get_cached_value("Company", filters.company, "default_currency")
for cash_flow_account in cash_flow_accounts:
for cash_flow_section in cash_flow_sections:
section_data = []
data.append(
{
"account_name": cash_flow_account["section_header"],
"parent_account": None,
"section_name": "'" + cash_flow_section["section_header"] + "'",
"parent_section": None,
"indent": 0.0,
"account": cash_flow_account["section_header"],
"section": cash_flow_section["section_header"],
}
)
@@ -75,31 +75,40 @@ def execute(filters=None):
# add first net income in operations section
if net_profit_loss:
net_profit_loss.update(
{"indent": 1, "parent_account": cash_flow_accounts[0]["section_header"]}
{"indent": 1, "parent_section": cash_flow_sections[0]["section_header"]}
)
data.append(net_profit_loss)
section_data.append(net_profit_loss)
for account in cash_flow_account["account_types"]:
account_data = get_account_type_based_data(
filters.company, account["account_type"], period_list, filters.accumulated_values, filters
for row in cash_flow_section["account_types"]:
row_data = get_account_type_based_data(
filters.company, row["account_type"], period_list, filters.accumulated_values, filters
)
account_data.update(
accounts = frappe.get_all(
"Account",
filters={
"account_type": row["account_type"],
"is_group": 0,
},
pluck="name",
)
row_data.update(
{
"account_name": account["label"],
"account": account["label"],
"section_name": row["label"],
"section": row["label"],
"indent": 1,
"parent_account": cash_flow_account["section_header"],
"accounts": accounts,
"parent_section": cash_flow_section["section_header"],
"currency": company_currency,
}
)
data.append(account_data)
section_data.append(account_data)
data.append(row_data)
section_data.append(row_data)
add_total_row_account(
data,
section_data,
cash_flow_account["section_footer"],
cash_flow_section["section_footer"],
period_list,
company_currency,
summary_data,
@@ -109,7 +118,7 @@ def execute(filters=None):
add_total_row_account(
data, data, _("Net Change in Cash"), period_list, company_currency, summary_data, filters
)
columns = get_columns(filters.periodicity, period_list, filters.accumulated_values, filters.company)
columns = get_columns(filters.periodicity, period_list, filters.accumulated_values, filters.company, True)
chart = get_chart_data(columns, data)
@@ -217,8 +226,8 @@ def get_start_date(period, accumulated_values, company):
def add_total_row_account(out, data, label, period_list, currency, summary_data, filters, consolidated=False):
total_row = {
"account_name": "'" + _("{0}").format(label) + "'",
"account": "'" + _("{0}").format(label) + "'",
"section_name": "'" + _("{0}").format(label) + "'",
"section": "'" + _("{0}").format(label) + "'",
"currency": currency,
}
@@ -229,7 +238,7 @@ def add_total_row_account(out, data, label, period_list, currency, summary_data,
period_list = get_filtered_list_for_consolidated_report(filters, period_list)
for row in data:
if row.get("parent_account"):
if row.get("parent_section"):
for period in period_list:
key = period if consolidated else period["key"]
total_row.setdefault(key, 0.0)
@@ -254,13 +263,14 @@ def get_report_summary(summary_data, currency):
def get_chart_data(columns, data):
labels = [d.get("label") for d in columns[2:]]
print(data)
datasets = [
{
"name": account.get("account").replace("'", ""),
"values": [account.get(d.get("fieldname")) for d in columns[2:]],
"name": section.get("section").replace("'", ""),
"values": [section.get(d.get("fieldname")) for d in columns[2:]],
}
for account in data
if account.get("parent_account") is None and account.get("currency")
for section in data
if section.get("parent_section") is None and section.get("currency")
]
datasets = datasets[:-1]

View File

@@ -0,0 +1,44 @@
// Copyright (c) 2024, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
frappe.query_reports["Cheques and Deposits Incorrectly cleared"] = {
filters: [
{
fieldname: "company",
label: __("Company"),
fieldtype: "Link",
options: "Company",
reqd: 1,
default: frappe.defaults.get_user_default("Company"),
},
{
fieldname: "account",
label: __("Bank Account"),
fieldtype: "Link",
options: "Account",
default: frappe.defaults.get_user_default("Company")
? locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"]
: "",
reqd: 1,
get_query: function () {
var company = frappe.query_report.get_filter_value("company");
return {
query: "erpnext.controllers.queries.get_account_list",
filters: [
["Account", "account_type", "in", "Bank, Cash"],
["Account", "is_group", "=", 0],
["Account", "disabled", "=", 0],
["Account", "company", "=", company],
],
};
},
},
{
fieldname: "report_date",
label: __("Date"),
fieldtype: "Date",
default: frappe.datetime.get_today(),
reqd: 1,
},
],
};

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