Compare commits

...

1051 Commits

Author SHA1 Message Date
Saqib Ansari
aa8b241d5a chore: update CODEOWNERS
[skip ci]
2023-04-11 15:46:47 +05:30
rohitwaghchaure
2f9856436e Merge pull request #34808 from rohitwaghchaure/fixed-reposting-issue-for-stock-reco
fix: reposting record not created for backdated stock reconciliation
2023-04-11 15:02:18 +05:30
Rohit Waghchaure
6851b5ba97 fix: reposting record not created for backdated stock reco 2023-04-11 14:35:33 +05:30
Anand Baburajan
c957a5cd2e fix: provide filter by depreciable assets in fixed asset register (#34803) 2023-04-11 12:16:07 +05:30
Raffael Meyer
934e1b4e6a fix: add german translation of "Partly Paid" (#34776) 2023-04-09 19:04:57 +05:30
Deepesh Garg
6f6928fa7b fix: Item tax validity comparison fixes (#34784)
fix: Item tax validity comparsion fixes
2023-04-09 09:38:13 +05:30
Sagar Sharma
02d5d43eb1 Merge pull request #34632 from s-aga-r/refactor-batch
refactor: rewrite `batch.py` queries in `QB`
2023-04-08 13:11:01 +05:30
Sagar Sharma
299fb2619f Merge branch 'develop' into refactor-batch 2023-04-08 11:15:08 +05:30
s-aga-r
4a9a5f03fc Merge branch 'develop' into refactor-batch 2023-04-08 11:06:27 +05:30
Sagar Sharma
ba553dba62 Merge pull request #34739 from resilient-tech/item-tax-override-remove
chore!: remove override to set item tax based on HSN code
2023-04-07 21:39:08 +05:30
Anand Baburajan
29e025c012 Merge pull request #34778 from AnandBaburajan/asset_value_adjust_get_depr_amt
fix: improper usage of get_depreciation_amount in asset_value_adjustment
2023-04-07 16:48:06 +05:30
Anand Baburajan
90dee8b663 Merge pull request #34735 from ihosseinu/asset_movement_translation_fix
'Make Asset Movement' button translation fix in asset_list.js
2023-04-07 15:30:26 +05:30
Anand Baburajan
edaa0bc3f7 Merge branch 'develop' into asset_value_adjust_get_depr_amt 2023-04-07 15:24:37 +05:30
anandbaburajan
984905109d fix: improper usage of get_depreciation_amount in asset_value_adjustment 2023-04-07 15:23:02 +05:30
Sagar Sharma
45291aaa43 Merge pull request #34760 from s-aga-r/FIX-ISS-22-23-06306
fix: validate `Received Qty` for Internal Purchase Receipt
2023-04-06 16:25:29 +05:30
Sagar Sharma
f7fb001126 Merge branch 'develop' into FIX-ISS-22-23-06306 2023-04-06 15:59:32 +05:30
s-aga-r
a575bd50ef test: add test cases for internal PR received qty 2023-04-06 15:26:19 +05:30
rohitwaghchaure
7d2d2dd0af Merge pull request #34768 from rohitwaghchaure/fixed-ux-issue
fix: UX for stock entry, bom and work order
2023-04-06 14:16:20 +05:30
Sagar Sharma
7934441f3c Merge pull request #34769 from s-aga-r/FIX-ISS-22-23-06397
fix: Subcontracting Receipt incorrect `status`
2023-04-06 13:56:23 +05:30
Sagar Sharma
cfd4da3e8a Merge branch 'develop' into FIX-ISS-22-23-06397 2023-04-06 13:23:39 +05:30
s-aga-r
a55b818119 fix: Subcontracting Receipt incorrect status 2023-04-06 13:20:33 +05:30
Rohit Waghchaure
82a136f991 fix: UX for stock entry, bom and work order 2023-04-06 12:51:32 +05:30
Deepesh Garg
91a26608ee fix: Unable to create payment request against purchase invoice (#34762) 2023-04-06 12:40:58 +05:30
s-aga-r
0d1df26b88 chore: add Delivery Note Item in Purchase Receipt Status Updater 2023-04-06 09:31:46 +05:30
s-aga-r
bc39dfab5d feat: add Received Qty field in Delivery Note Item 2023-04-06 09:29:44 +05:30
rohitwaghchaure
8503d581a5 Merge pull request #34743 from rohitwaghchaure/fix-incorrect-balance-qty
fix: incorrect stock balance quantity for batch item
2023-04-05 23:21:44 +05:30
Lucky-Tsuma
9bf87d708e fix: payment entry is already created on posawesome. (#34712) 2023-04-05 13:52:00 +05:30
Deepesh Garg
56f5078357 fix: Shop by category fixes (#34688)
* fix: Shop by category fixes

* chore: Update tests
2023-04-05 12:43:32 +05:30
Ritwik Puri
f193393f57 fix!: require sender and message for contact us page (#34707)
* fix: require sender and message for contact us page

* refactor: dont override frappe.send_message from client side

used override_whitelisted_method hook for the same
2023-04-05 12:04:36 +05:30
Deepesh Garg
fd3fb64aa3 feat: Auto allocate advance payments only against orders (#34727)
feat: Auto allocate advance payments only againt orders
2023-04-05 12:02:44 +05:30
Anand Baburajan
869289a6e4 Merge pull request #34745 from frappe/mergify/bp/develop/pr-34737
fix: don't include cancelled JVs in assdeprledger report (backport #34737)
2023-04-05 11:59:52 +05:30
Raffael Meyer
e6a9b6ee95 feat: remove deprecated get_customer_list (#34624)
feat: remove deprecated method

get_customer_list
2023-04-05 11:56:15 +05:30
Anand Baburajan
5cc3c08059 fix: asset monthly WDV and DD schedule and refactor [develop] (#34646)
* fix: monthly wdv and dd schedule

* chore: minor rename

* chore: fix tests
2023-04-05 11:46:31 +05:30
anandbaburajan
1101b7bfdf fix: don't include cancelled JVs in assdeprledger report
(cherry picked from commit 3896d41e95)
2023-04-05 06:04:43 +00:00
Rohit Waghchaure
ef4bd77196 fix: incorrect stock balance quantity for batch item 2023-04-05 00:02:45 +05:30
Sagar Vora
9c66ebb108 chore!: remove override to set item tax based on HSN code 2023-04-04 18:02:55 +05:30
Hossein Yousefian
b70615ef18 'Make Asset Movement' button translation fix 2023-04-04 14:49:43 +03:30
rohitwaghchaure
12325cb685 Merge pull request #31271 from dj12djdjs/fix-reserve-qty
fix(stock): don't reserve qty on sales return.
2023-04-04 06:21:30 +05:30
rohitwaghchaure
682730a498 Merge pull request #34715 from rohitwaghchaure/fixed-bom-update-log
fix: bom update log not working for large batch size
2023-04-03 15:49:25 +05:30
Sagar Sharma
5f2874743b Merge pull request #34713 from s-aga-r/FIX-ISS-22-23-06369
fix: consider qty field precision
2023-04-03 15:36:19 +05:30
Rohit Waghchaure
d56070301c fix: bom update log not working for large batch size 2023-04-03 14:47:58 +05:30
Sagar Sharma
4571bd58d2 Merge branch 'develop' into FIX-ISS-22-23-06369 2023-04-03 14:26:09 +05:30
s-aga-r
6ec7590c21 fix: consider qty field precision 2023-04-03 14:08:11 +05:30
ruthra kumar
1eefd517d1 Merge pull request #34694 from ruthra-kumar/recalculate_difference_on_allocation_change
fix(ui): recalculate difference amount on allocation change
2023-04-03 10:03:01 +05:30
Deepesh Garg
4c61ee30bb fix: Multiple issues in purchase invoice submission (#34600)
* fix: Multiple issues in purchase invoice submission

* fix: Base grand total calculation

* chore: Calculate base grand total separately only in multi currency docs

* fix: Add gl entry for round off
2023-04-02 09:35:27 +05:30
Kitti U. @ Ecosoft
74b29eb5e2 fix: Bank clearance for case loan (disburstment/repayment) (#34586) 2023-04-01 18:50:30 +05:30
Deepesh Garg
1e3eb4deed Merge pull request #34287 from frappe/early-payment-loss
fix: Allocate tax loss to tax account head on early payment discount
2023-04-01 18:49:02 +05:30
Sagar Sharma
7c7cfc4373 Merge pull request #34656 from s-aga-r/FIX-33830
fix: BOM Update Cost, when no actual qty
2023-04-01 16:24:01 +05:30
ruthra kumar
32a4ca6b6c fix: recalculate difference amount on allocation change 2023-04-01 09:17:33 +05:30
Sagar Sharma
2c2b5a5b6e Merge branch 'develop' into FIX-33830 2023-03-31 14:14:17 +05:30
ruthra kumar
770640f4e1 Merge pull request #34679 from ruthra-kumar/fix_credit_note_with_positive_total
fix: enclose ternary operator in parentheses
2023-03-31 13:31:16 +05:30
Sagar Sharma
b4ace345f9 Merge branch 'develop' into FIX-33830 2023-03-31 12:35:45 +05:30
Sagar Sharma
e56d40f48a Merge pull request #34677 from s-aga-r/FIX-34240
chore: make `Production Plan Item Reference` table hidden in Production Plan
2023-03-31 12:34:59 +05:30
Sagar Sharma
d0d3961a1e Merge branch 'develop' into FIX-34240 2023-03-31 12:34:27 +05:30
ruthra kumar
986daa6578 fix: enclose ternary operator in parentheses 2023-03-31 12:09:57 +05:30
Deepesh Garg
576575c227 fix: Column value mismatch in COA blank template (#34658) 2023-03-31 11:10:50 +05:30
s-aga-r
706be2a415 chore: make Production Plan Item Reference table hidden in Production Plan 2023-03-31 10:32:49 +05:30
Deepesh Garg
7c42b72ee7 fix: Total debit and credit while importing via Data Import (#34659) 2023-03-30 17:28:19 +05:30
rohitwaghchaure
edb0666b2c Merge branch 'develop' into fix-reserve-qty 2023-03-30 17:26:32 +05:30
rohitwaghchaure
c4a0842a4f Merge pull request #34636 from rohitwaghchaure/posting-time-issue
fix: posting time issue
2023-03-30 17:25:47 +05:30
rohitwaghchaure
06eac67796 Merge pull request #34664 from rohitwaghchaure/fixed-incorrect-balance-qty-stock-ledger
fix: incorrect balance qty in the stock ledger report
2023-03-30 17:25:35 +05:30
Richard Case
ddb17a8880 fix: plaid log_error syntax issue (#34642) 2023-03-30 16:45:56 +05:30
Anand Baburajan
d999dea3e4 chore: improve asset depr posting failure msg (#34661)
* chore: improve asset depr posting error msg

* chore: add period

* chore: improve msg
2023-03-30 16:38:37 +05:30
Rohit Waghchaure
345e6facbe fix: posting time issue 2023-03-30 16:34:34 +05:30
Rohit Waghchaure
cbdaab940d fix: incorrect balance qty in the stock ledger report 2023-03-30 16:20:42 +05:30
Anand Baburajan
a7a45fbe59 Merge pull request #34660 from AnandBaburajan/asset_improvements_30_mar_develop
chore: auto fill asset name and available for use date
2023-03-30 16:07:59 +05:30
anandbaburajan
af3e807607 chore: auto fill asset name and available for use date 2023-03-30 15:28:59 +05:30
Sagar Sharma
b6c0a675f9 Merge branch 'develop' into FIX-33830 2023-03-30 13:49:41 +05:30
s-aga-r
a4112c75c5 fix: BOM Update Cost, when no actual qty 2023-03-30 13:46:50 +05:30
rohitwaghchaure
448afa2b7c Merge pull request #34648 from rohitwaghchaure/fixed-serial-no-qty-issue-in-stock-reco
fix: serial no with zero quantity issue in stock reco
2023-03-30 13:04:48 +05:30
Anand Baburajan
0ac81e3793 Merge pull request #34649 from AnandBaburajan/asset_value_adjustment_asset_name
fix: incorrect arg name in asset value adjustment
2023-03-30 13:03:18 +05:30
Anand Baburajan
33ff1b5a72 Merge branch 'develop' into asset_value_adjustment_asset_name 2023-03-30 12:53:06 +05:30
anandbaburajan
2b0470d1f5 fix: incorrect arg name in asset value adjustment 2023-03-30 12:50:33 +05:30
Rohit Waghchaure
17131e5a02 fix: serial no with zero quantity issue in stock reco 2023-03-30 11:47:32 +05:30
Komal-Saraf0609
ad11934d39 fix: enabling lead even after "Opportunity" created against it (#34627)
* fix: enabling lead even after "Opportunity" created against it

* chore: Linting Issues

---------

Co-authored-by: Komal Saraf <komal@frappe.io>
Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-03-30 08:03:55 +05:30
Anand Baburajan
7c5720ddb3 Merge pull request #34641 from frappe/mergify/bp/develop/pr-34607
Asset maintenance task add dropdown "3 Yearly" (backport #34607)
2023-03-29 18:15:17 +05:30
Bevan Tony Medrano
ddc3050e99 Asset maintenance task add dropdown "3 Yearly" (#34607)
* feat(asset_maintenance.json):Add 3 yearly in periodicity dropdown

* add server side implications for 3 yearly

(cherry picked from commit 625b8e8005)
2023-03-29 12:16:03 +00:00
Raffael Meyer
f5453adf4d fix: switch to supported update_linked_doctypes (#34625) 2023-03-29 16:59:10 +05:30
Komal-Saraf0609
d0660ad222 fix: lost opportunity report issue (#34626)
* fix: lost opportunity report issue

* chore: Linting Issues

---------

Co-authored-by: Komal Saraf <komal@frappe.io>
Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-03-29 16:51:18 +05:30
s-aga-r
517b5f8567 refactor: rewrite batch.py queries in QB 2023-03-29 14:02:09 +05:30
Sagar Sharma
867d898304 fix: zero rm-cost for batch rm item in SCR (#34616)
fix: `0` rm-cost for batch rm item in SCR
2023-03-28 18:45:16 +05:30
rohitwaghchaure
738dd57904 Merge pull request #34461 from s-aga-r/FIX-ISS-22-23-05936
fix: incorrect `Opening Value` in `Stock Balance` report
2023-03-28 17:40:25 +05:30
akiratfli
07c9b99072 fix: bad strings format for update-translations (#34592)
Co-authored-by: justin.li <justin.li@lungteh.com>
2023-03-28 17:20:54 +05:30
ruthra kumar
7c13533f30 Merge pull request #34608 from ruthra-kumar/reconciliation_debit_credit_mismatch
chore: removing redundant validation
2023-03-28 16:54:37 +05:30
Vishal Dhayagude
7aafc90d58 fix: Tax Category not able to set hence it calculating zero tax for item whoes tax template set (#34525)
* fix: Tax Category not able to set hence it calculating zero tax for item whoes tax template set

* fix: minor change added
2023-03-28 16:48:03 +05:30
Devin Slauenwhite
393bc25e2d fix: don't get zero value entries for exchange rate calculation (#34475)
* fix: multiply None by float

* chore: remove debug
2023-03-28 16:34:24 +05:30
Deepesh Garg
50c1172f29 fix: Party Name in SOA print when viewed from Customer/Supplier master (#34597)
fix: Party Name in SOA print when viewd from Customer/Supplier master
2023-03-28 15:36:16 +05:30
Deepesh Garg
12ad2aa2e5 fix: Percentage billing in Sales Order (#34606) 2023-03-28 15:33:59 +05:30
Marica
dae40dfbb4 Merge branch 'develop' into early-payment-loss 2023-03-28 15:01:28 +05:30
ruthra kumar
d52f7e2820 fix: removing redundant validation 2023-03-28 13:44:23 +05:30
Devin Slauenwhite
3c553b0938 feat: don't reserve qty on sales return 2023-03-27 10:37:54 -04:00
Devin Slauenwhite
17f8080168 chore: revert get_reserve_qty method 2023-03-27 10:37:04 -04:00
marination
216a46bd66 feat: Make Tax loss booking optional
- Checkbox in Accounts Settings
- Apply checkbox in PE deductions setting logic
- Adjust tests
2023-03-27 16:11:00 +05:30
=
1142e69d1a Merge branch 'develop' into fix-reserve-qty
# Conflicts:
#	erpnext/selling/doctype/selling_settings/selling_settings.json
2023-03-24 23:50:50 -04:00
ruthra kumar
f7780cdb58 refactor: additional filters and columns in Payment Ledger report (#34577)
1. 'Party type' and 'Party' filters have been added
2. checkbox to include Amount in Acccount Currency
3. Grouping vouchers on Party
4. Replaced Company with Posting Date
2023-03-24 12:25:03 +05:30
Raffael Meyer
8c7fa5712b feat: deprecate get_customer_list (#34563) 2023-03-24 12:13:53 +05:30
Shram Kadia
0cd870fc29 fix: get batch_no. for item automatically (#34473)
* fix: default pos conversion factor set to 1

* fix:get-batch-no-of-item-automatically
2023-03-24 12:05:26 +05:30
Nabin Hait
282a0ee2ce Merge pull request #34257 from deepeshgarg007/opening_entry
feat: Closing balance for period closing and reporting
2023-03-24 11:59:00 +05:30
rohitwaghchaure
980cca1772 Merge pull request #34573 from rohitwaghchaure/timer-buttons-not-working
fix: Time buttons not working in the job card
2023-03-23 22:35:43 +05:30
Rohit Waghchaure
34c190b7d6 fix: Time button not working in the job card 2023-03-23 22:32:11 +05:30
Anand Baburajan
e6b0196493 fix: recalculate wdv rate after asset repair [develop] (#34570)
fix: recalculate wdv rate after asset repair
2023-03-23 21:10:22 +05:30
Deepesh Garg
87108be11a fix: Sales person variance report without item group (#34552)
fix: Sales person variance report witout item group
2023-03-23 19:19:46 +05:30
Deepesh Garg
8ce1da111e chore: Improve validation message 2023-03-23 19:14:12 +05:30
Deepesh Garg
f8cff09129 fix: CS financial statement param 2023-03-23 19:12:42 +05:30
Deepesh Garg
b7dcf27b01 fix: Partial trial balance view 2023-03-23 19:06:07 +05:30
Deepesh Garg
30eb6c8512 fix: Validation for cancelation 2023-03-23 19:05:25 +05:30
Deepesh Garg
3fd95200da chore: Fix Typo 2023-03-23 19:04:48 +05:30
Deepesh Garg
528ab503f2 fix: Don't validate if no GL Entry exists 2023-03-23 15:32:24 +05:30
Raffael Meyer
f7bf1b8a0c fix: unset address and contact on trash (#34495)
* fix(Customer): unset address and contact on trash

* fix(Supplier): unset address and contact on trash

---------

Co-authored-by: Sagar Sharma <sagarsharma.s312@gmail.com>
2023-03-23 11:52:32 +05:30
Shariq Ansari
829bc6a51f Merge pull request #34547 from shariquerik/crm-comments-overlap-fix 2023-03-22 12:02:10 +05:30
Shariq Ansari
24a0833980 Merge branch 'develop' into crm-comments-overlap-fix 2023-03-22 11:40:58 +05:30
Shariq Ansari
76cea7dd6a fix: Note username overlapping with note content(CRM) 2023-03-22 11:28:58 +05:30
Anand Baburajan
6d6a7bcb50 fix: incorrect depr schedules after asset repair [develop] (#34544)
* fix: backport missing changes from #30838

* fix: incorrect schedule after repair
2023-03-21 20:07:27 +05:30
Deepesh Garg
fa991b2dcf Merge branch 'develop' of https://github.com/frappe/erpnext into opening_entry 2023-03-21 17:00:08 +05:30
Deepesh Garg
f9397a87ac fix: Ignore opening entries if PCV posted 2023-03-21 16:59:22 +05:30
Richard Case
d8e73b63f3 fix: remove non-maintained Tax Detail report and missing workspace links (#34192)
* fix: remove non-maintained Tax Detail report and missing workspace links

* patch: delete report "Tax Detail"

---------

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2023-03-21 16:23:27 +05:30
Raffael Meyer
79911734e9 fix: translations and UX in alternative item mapping (#34433)
* fix: disable deletion in alternative item mapping

* feat: german translations

* fix: make string translatable

---------

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-03-21 16:20:11 +05:30
Raffael Meyer
0df3a1a3af fix: remove unused translation (#34519) 2023-03-21 16:18:45 +05:30
Shram Kadia
08fc686513 fix: default pos conversion factor set to 1 (#34437) 2023-03-21 15:51:18 +05:30
Raffael Meyer
59c2e7ec3e fix: german translations (#34312)
fix: some german translations
2023-03-21 15:48:27 +05:30
Vishal Dhayagude
6966fa4d88 fix: POS not picking up pos profile company address instead fetch any random company address (#34521) 2023-03-21 15:43:57 +05:30
Deepesh Garg
ee6c107d58 fix(client): Amount calculation for 0 qty debit notes (#34455)
fix(client): Amount calculaton for 0 qty debit notes

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2023-03-21 15:38:39 +05:30
ruthra kumar
2d19cee713 Merge pull request #34501 from ruthra-kumar/incorrect_currency_symbol_for_bank_accounts_in_reconciliation_tool
refactor(bank reconciliation tool): currency symbol fix and concurrent usage
2023-03-21 15:30:50 +05:30
rohitwaghchaure
af5f59e856 Merge pull request #34528 from rohitwaghchaure/no-stock-still-option-to-add-in-cart
fix: E-commerce issue with Item Variants
2023-03-21 14:46:20 +05:30
Rohit Waghchaure
aaa4d1eb55 fix: E-commerce issue with Item Variants 2023-03-21 14:44:12 +05:30
Marica
c5da7f5fb1 Merge branch 'develop' into early-payment-loss 2023-03-21 11:40:30 +05:30
Ankush Menat
109a9f1390 perf: index against_sales_invoice field on DN items (#34509)
This is used on Sales invoice dashboard and takes a lot of time to load
as db size increases.

Results:

Before: ~10-20 seconds to load dashboard
After: few milliseconds because of index

[skip ci]
2023-03-20 14:46:26 +05:30
Sagar Sharma
4c35a37eaa Merge branch 'develop' into FIX-ISS-22-23-05936 2023-03-20 10:28:10 +05:30
Deepesh Garg
44053db010 chore: Remove unnecessary list comprehension 2023-03-19 19:46:01 +05:30
Deepesh Garg
fc86a8568f fix: Supplier RFQ email link (#34338) 2023-03-19 18:09:18 +05:30
Raffael Meyer
d791dc11a3 fix: patch depends on Currency Exchange Settings (#34494) 2023-03-19 18:05:12 +05:30
ruthra kumar
1eea585d29 refactor: allow for concurrent use of reconciliation tool
1. set default filter dates a period of one month from current date
2023-03-19 14:20:03 +05:30
ruthra kumar
2d14d92b32 fix: incorrect currency symbol in Bank Reconciliation tool 2023-03-19 12:47:29 +05:30
Deepesh Garg
0aadb680eb chore: Add missing validations 2023-03-19 12:46:42 +05:30
Deepesh Garg
00fe3042b2 chore: Simplify query 2023-03-18 20:05:43 +05:30
Deepesh Garg
7f11373b58 fix: Account sub query 2023-03-17 16:59:23 +05:30
Deepesh Garg
d11a64ddcb Merge branch 'develop' of https://github.com/frappe/erpnext into opening_entry 2023-03-17 16:50:49 +05:30
Sagar Sharma
a07a5572ea Merge branch 'develop' into FIX-ISS-22-23-05936 2023-03-17 16:18:54 +05:30
Danny
ca10e2bb9f fix: bad strings format for command get-untraslated (#34361)
* fix: bad string foramt

* fix: bad string format

* fix: pre-commit format

---------

Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2023-03-17 16:13:32 +05:30
Sagar Sharma
2ac079210d Merge pull request #34440 from s-aga-r/FIX-34354
chore: `Allow Zero Valuation Rate` msg in SE
2023-03-17 16:02:52 +05:30
Sagar Sharma
f2c78163bf Merge branch 'develop' into FIX-34354 2023-03-17 15:58:46 +05:30
Deepesh Garg
d8ece86463 fix: Update account number from parent company (#34474) 2023-03-17 15:55:11 +05:30
Deepesh Garg
7b630217bd fix: Multiple accounting dimension filtering in AR/AP reports (#34464)
Co-authored-by: Anand Baburajan <anandbaburajan@gmail.com>
2023-03-17 15:51:33 +05:30
Deepesh Garg
be723bb9d4 chore: Update user manual link (#34478) 2023-03-17 15:39:33 +05:30
ruthra kumar
6b0dc62681 Merge pull request #34456 from ruthra-kumar/gp_return_invoice_issue
fix: Gross Profit reports Invoices with -ve qty for Invoices with Cr Notes
2023-03-17 15:26:41 +05:30
Sagar Sharma
0753aa5ab7 Merge branch 'develop' into FIX-34354 2023-03-17 15:16:41 +05:30
Sagar Sharma
3fb64cacfc Merge branch 'develop' into FIX-ISS-22-23-05936 2023-03-17 15:03:44 +05:30
ruthra kumar
6e0f1c6ad6 Merge pull request #34466 from ruthra-kumar/difference_amount_issue_while_reconciling
fix: unwanted difference amt while reconciling vouchers from base currency account
2023-03-17 14:51:41 +05:30
ruthra kumar
cc61daeec4 test: Gross Profit report output for Cr notes
2 New test cases added.
1. Standalone Cr notes will be reported as normal Invoices
2. Cr notes against an Invoice will not overallocate qty if there are
multiple instances of same item
2023-03-17 14:48:57 +05:30
ruthra kumar
d0715a82eb refactor: Ignore linked Cr Notes in Report output 2023-03-17 14:48:57 +05:30
ruthra kumar
e2f19c6a14 fix: Overallocation of 'qty' from Cr Notes to Parent Invoice
Cr Notes 'qty' are overallocated to parent invoice, when there are
mulitple instances of same item in Invoice.
2023-03-17 14:48:57 +05:30
ruthra kumar
ec075122b6 refactor: difference amt validation for same currency accounts 2023-03-17 14:14:17 +05:30
ruthra kumar
861387f164 test: difference amount should not be calculated for base currency 2023-03-17 14:14:17 +05:30
ruthra kumar
48fae0c1ce fix: difference amount calculation for company currency accounts 2023-03-17 14:14:17 +05:30
Sagar Sharma
db1f2d6279 Merge branch 'develop' into FIX-ISS-22-23-05936 2023-03-17 13:54:13 +05:30
s-aga-r
b04a101c11 fix: incorrect Opening Value in Stock Balance report 2023-03-17 13:53:49 +05:30
rohitwaghchaure
1d767d58f2 Merge pull request #34279 from s-aga-r/fix-22-23-05686
fix: `Blanket Order`
2023-03-17 10:14:37 +05:30
rohitwaghchaure
558db13040 Merge branch 'develop' into fix-22-23-05686 2023-03-17 08:57:37 +05:30
Anand Baburajan
563ee92043 Merge branch 'develop' into FIX-34354 2023-03-16 18:22:13 +05:30
Anand Baburajan
b6b8392413 fix: refactor asset depr schedule and remove unnecessary depr method (#34434)
* fix: remove depr method from depr schedule and refactor assetdeprsch

* chore: use assetdeprsch's depr method, not deprschedule's

* fix: use default 0 value for NDB and OAD

* chore: fix rounded numbers

* chore: correct rounding in test_website_item_price_for_logged_in_user
2023-03-16 18:18:47 +05:30
mergify[bot]
bd2dd7dfac ci: use version specific payments repo (backport #34468) (#34470)
ci: use version specific payments repo (#34468)

ci: use version-14 branch of payments repo for v14 erpnext
(cherry picked from commit befd1a0f91)

Co-authored-by: Ritwik Puri <ritwikpuri5678@gmail.com>
2023-03-16 16:20:21 +05:30
Sagar Sharma
357d4994e4 Merge branch 'develop' into fix-22-23-05686 2023-03-14 20:59:38 +05:30
Sagar Sharma
ea269c6a1c Merge branch 'develop' into FIX-34354 2023-03-14 20:59:27 +05:30
Ankush Menat
f1752fcd7a Merge pull request #34116 from prateekkaramchandani/develop
fix: use max function to get default company address
2023-03-14 19:38:26 +05:30
Ankush Menat
e0042972c8 test: add test for primary address sorting 2023-03-14 18:50:19 +05:30
Sagar Sharma
e9f5ea6ede Merge pull request #34415 from s-aga-r/FIX-ISS-22-23-05720
fix: operation time for multi-level BOM in WO
2023-03-14 18:47:40 +05:30
Ankush Menat
55489d0056 Merge branch 'develop' into prateekkaramchandani/develop 2023-03-14 18:39:06 +05:30
Sagar Sharma
e0b288fbf0 Merge branch 'develop' into FIX-34354 2023-03-14 18:19:38 +05:30
Sagar Sharma
258242f8d7 Merge branch 'develop' into FIX-ISS-22-23-05720 2023-03-14 18:17:48 +05:30
Ankush Menat
f95ad039e4 test: add timeout to all BOM related tests (#34446)
* Revert "chore: remove failing test (#34444)"

This reverts commit b89ecd482d.

* test: add timeout to bom tests
2023-03-14 18:10:17 +05:30
Deepesh Garg
b89ecd482d chore: remove failing test (#34444) 2023-03-14 15:53:03 +05:30
Johannes Obermeier
70c78d0d67 fix: incorrect EAN validation, EAN can be an EAN8, EAN12 or EAN13 code (#34250)
Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-03-14 15:27:05 +05:30
s-aga-r
22ad9a1903 chore: Allow Zero Valuation Rate msg in SE 2023-03-14 14:33:03 +05:30
marination
d6d0163514 fix: Provision to apply early payment discount if payment is recorded late
- Party could have paid on time but payment is recorded late
- Prompt for reference date so that discount is applied while mapping
- Prompt only if discount in payment schedule of valid doctypes
- test: Reference date and impact on PE
- `make_payment_entry` (JS) must be able to access `this`
2023-03-14 14:22:39 +05:30
Sagar Sharma
0dbf4557db Merge branch 'develop' into FIX-ISS-22-23-05720 2023-03-14 13:19:58 +05:30
Sagar Sharma
185a8910d2 Merge branch 'develop' into fix-22-23-05686 2023-03-14 13:19:46 +05:30
Anand Baburajan
fe672aa9b1 Merge pull request #34392 from AnandBaburajan/patch_assdeprsch_gpa_ndb
fix: add missing patch for new fields added in #34214
2023-03-14 12:18:29 +05:30
Sagar Sharma
6c88fb7a8d Merge branch 'develop' into fix-22-23-05686 2023-03-14 11:44:39 +05:30
Sagar Sharma
48a88b7064 Merge branch 'develop' into FIX-ISS-22-23-05720 2023-03-14 11:44:33 +05:30
Anand Baburajan
72e9c05f46 Merge branch 'develop' into patch_assdeprsch_gpa_ndb 2023-03-13 21:31:57 +05:30
Deepesh Garg
c6999fc687 fix: Total row in trail balance report (#34395)
* fix: Total row in trail balance report

* fix: Calculate total after preparing opening and closing
2023-03-13 21:18:30 +05:30
Deepesh Garg
91f7b0bbd6 Merge branch 'develop' of https://github.com/frappe/erpnext into opening_entry 2023-03-13 20:52:48 +05:30
Deepesh Garg
76775a3e49 fix: Update patch 2023-03-13 20:51:35 +05:30
Deepesh Garg
492e994c22 Merge branch 'develop' into patch_assdeprsch_gpa_ndb 2023-03-13 19:37:55 +05:30
Deepesh Garg
fa776d2987 fix: Use customer name instead of name(id) in PSOA (#34412) 2023-03-13 19:37:09 +05:30
Deepesh Garg
d8e54a21fb test: Update values in Sales Invoice tests (#34419) 2023-03-13 19:34:15 +05:30
Sagar Vora
5c06620f97 fix: set tax category from address before executing get_regional_address_details (#34372) 2023-03-13 19:32:50 +05:30
HENRY Florian
d267111e13 chore: fix french translation (#34381)
chore: update french translation
2023-03-13 19:03:16 +05:30
Ernesto Ruiz
796ad01b87 fix: Add translate function to Delayed Tasks Summary Report (#34411)
fix: Add translate function to Delayed Tasks Summary Report
2023-03-13 19:02:34 +05:30
Deepesh Garg
c8cc3fc65f chore: Move source and campaign to additional info section (#34414) 2023-03-13 18:37:19 +05:30
Deepesh Garg
4416ddc4af fix: Linked invoice cancellation issue via timesheet (#34337) 2023-03-13 18:36:43 +05:30
Sagar Sharma
4a05f3736f Merge branch 'develop' into FIX-ISS-22-23-05720 2023-03-13 17:50:09 +05:30
s-aga-r
66f650061d test: add test cases for Over Order Allowance against Blanket Order 2023-03-13 17:21:07 +05:30
Sagar Sharma
9d49afebde Merge branch 'develop' into fix-22-23-05686 2023-03-13 17:05:27 +05:30
Marica
e1d28063b0 Merge branch 'develop' into early-payment-loss 2023-03-13 16:23:24 +05:30
Anand Baburajan
b8703b0b9b Merge branch 'develop' into patch_assdeprsch_gpa_ndb 2023-03-13 15:35:08 +05:30
Ankush Menat
b8a61be080 test: fix hypothesis tests (#34416) 2023-03-13 15:16:30 +05:30
Anand Baburajan
097be8bdea Merge branch 'develop' into patch_assdeprsch_gpa_ndb 2023-03-13 15:08:08 +05:30
Sagar Sharma
f2e9a67f37 Merge branch 'develop' into FIX-ISS-22-23-05720 2023-03-13 14:32:26 +05:30
s-aga-r
442ee3adba fix: operation time for multi-level BOM in WO 2023-03-13 14:31:38 +05:30
Ankush Menat
5bf6fb43a9 chore: fix broken TR translations 2023-03-13 14:12:12 +05:30
Deepesh Garg
7101e7acc4 fix: Filtering issue in payment entry (#34332) 2023-03-13 14:05:13 +05:30
marination
caa1a3dccf fix: Handle rounding more gracefully
- Round off pending discount loss to avoid miniscule losses rounded to 0.0 that are added in deductions
- Use base amounts to calculate base losses instead of using conversion factor which increases rounding error
- Round of total base loss instead of individual income and tax losses to reduce rounding error
- Use default round off account for pending rounding loss in deductions
2023-03-13 14:01:31 +05:30
ruthra kumar
9f26e3c27a Merge pull request #34408 from ruthra-kumar/delete_remarks_migration_patch
chore: delete remarks migration patch
2023-03-13 09:14:21 +05:30
ruthra kumar
da37573b73 chore: delete remarks migration patch
Versions higher than V14.18.2, 'remarks' will be moved in 'migrate_gl_to_payment_ledger'
2023-03-12 13:41:50 +05:30
ruthra kumar
e371bb6672 Merge pull request #34387 from ruthra-kumar/refactor_gl_migration_patch
refactor(patch): remove inner join to improve SQL performance
2023-03-11 21:00:19 +05:30
Mehmet Demirel
fa6d37542b chore: Improve Turkish language translation
chore: Improve Turkish language translation
2023-03-11 19:18:44 +05:30
Deepesh Garg
aae53bb910 fix: Error in consolidated financial statement (#34330) 2023-03-11 19:06:56 +05:30
ruthra kumar
a9989b12d5 Merge pull request #34370 from ruthra-kumar/fix_flaky_test_in_payment_terms_report
fix(test): flaky test case in Payment terms report
2023-03-11 14:17:45 +05:30
ruthra kumar
9d0a1149d8 chore: remove remarks migrations patch from patches.txt
'Remarks' field is moved in migrate_gl_to_payment_ledger patch itself
from versions highers than v14.18.2. Removing it from patches.txt
2023-03-11 13:17:52 +05:30
ruthra kumar
a43304b01b Merge branch 'develop' into fix_flaky_test_in_payment_terms_report 2023-03-11 13:05:38 +05:30
ruthra kumar
7fcd74ed03 fix(test): flaky test case in Payment terms report 2023-03-10 21:23:12 +05:30
ruthra kumar
1744f1d4e4 refactor: add remarks to column as well 2023-03-10 15:57:11 +05:30
Anand Baburajan
6308b509fc Merge branch 'develop' into patch_assdeprsch_gpa_ndb 2023-03-10 15:25:39 +05:30
Deepesh Garg
0157fa15eb chore: Use account closing balance in set gl entries 2023-03-10 13:02:01 +05:30
Sagar Sharma
430378bb6e Merge pull request #34383 from s-aga-r/FIX-ISS-22-23-05836
fix: filters not getting applied on `Web Form`
2023-03-10 00:24:08 +05:30
s-aga-r
9c1e566394 fix: filters not getting applied on Web Form 2023-03-09 22:12:35 +05:30
ruthra kumar
f9cfabf78e refactor(patch): remove inner join to improve SQL performance 2023-03-09 19:42:28 +05:30
Marica
ae0a8a6529 Merge branch 'develop' into early-payment-loss 2023-03-09 18:36:36 +05:30
anandbaburajan
ebb6953274 fix: add missing patch for new fields added in #34214 2023-03-09 17:55:27 +05:30
marination
9abf0ef615 test: Multi currency SI with multi-currency accounting and single currency accounting + Early payment discount 2023-03-09 16:32:36 +05:30
marination
b09c2381ca fix: Multi-currency SI with base currency PE
- Return total discount loss in base currency
- Allocate payment based on terms: Set allocated amount in references table in base currency if accounting is in that currency
- Allocate payment based on terms: While back updating set paid amount (payment schedule) in transaction currency always
- minor: discount msgprint in correct currency
2023-03-09 16:25:45 +05:30
Deepesh Garg
9a8f8e8b7d Revert "fix: Default sales team not getting set" (#34376)
Revert "fix: Default sales team not getting set (#34284)"

This reverts commit 7d0199d743.
2023-03-09 15:36:52 +05:30
Sagar Sharma
428274df56 Merge pull request #34360 from s-aga-r/FIX-ITEM-ALTERNATIVE-ERR-MSG
chore: `Alternative Item Code` error msg
2023-03-09 10:07:35 +05:30
Sagar Sharma
87b5734f5b Merge branch 'develop' into FIX-ITEM-ALTERNATIVE-ERR-MSG 2023-03-09 10:07:24 +05:30
Sagar Sharma
c312692af5 Merge pull request #34362 from s-aga-r/FIX-ISS-22-23-05697
fix: `required_qty` get reset to `1` for Alternative Item in WO
2023-03-09 10:05:08 +05:30
s-aga-r
046834a97a fix: required_qty get reset to 1 for Alternative Item in WO 2023-03-08 20:40:03 +05:30
s-aga-r
baef5ae1ef chore: Alternative Item Code error msg 2023-03-08 20:07:30 +05:30
Deepesh Garg
719ac5c8f3 Merge branch 'develop' of https://github.com/frappe/erpnext into opening_entry 2023-03-08 18:35:26 +05:30
Deepesh Garg
f0267feca8 test: Add test case for closing balance 2023-03-08 18:34:07 +05:30
marination
761f68d7bf fix: Paid amount must be discounted considering accounting currency
- Accounting is in the same currency if party currency and company currency is the same
- If accounting is in the same currency, paid and recvd amount is in the base currency
- Then, discount amount must also be in the base currency as it is deducted from paid amount
- Received amount must be in base currency if not multi currency
- cleanup: Deductions setting broken into smaller functions
2023-03-08 17:20:48 +05:30
Sagar Sharma
a1ad9587fa Merge pull request #34352 from s-aga-r/FIX-ISS-22-23-05722
fix: `BOM Stock Report`
2023-03-08 16:26:19 +05:30
Sagar Sharma
2d66384cbc Merge branch 'develop' into FIX-ISS-22-23-05722 2023-03-08 16:25:42 +05:30
Ankush Menat
52ab11389b chore: drop hypothesis dependency
pinned in framework now
2023-03-08 15:31:25 +05:30
Deepesh Garg
3249a79f07 chore: rename Closing Balance to Account Closing Balance 2023-03-08 15:15:33 +05:30
s-aga-r
b53dcb04ed test: add test cases for BOM Stock Report 2023-03-08 14:00:06 +05:30
s-aga-r
a65b80911b fix: BOM Stock Report 2023-03-08 12:59:50 +05:30
Sagar Sharma
ba1162ff3f Merge pull request #34313 from s-aga-r/fix-22-23-05690
perf: Stock Entry (Material Transfer)
2023-03-07 18:03:08 +05:30
Sagar Sharma
336e0b9666 Merge branch 'develop' into fix-22-23-05690 2023-03-07 18:02:52 +05:30
Solufyin
71de72bdd0 fix: Set contact filter link in Opportunity (#34325)
Co-authored-by: Nihantra C. Patel <n.patel.serpentcs@gmail.com>
2023-03-07 17:44:31 +05:30
Devin Slauenwhite
6de826b8c4 fix: exchange rate revaluation errors (#33947)
* fix: set new balance for non-positive balances

* fix: don't add debit: 0, credit: 0 entries to journal entry.

* fix: add journal entry difference to unbooked gain/loss of exchange.

* chore: linter

* chore: remove invlaid TODO. [skip-ci]
2023-03-07 17:16:15 +05:30
Sagar Sharma
4d1f620d75 Merge branch 'develop' into fix-22-23-05690 2023-03-07 16:35:17 +05:30
Marica
2feb27e399 fix(minor): Dirty the form after clicking on Get advances button in Invoices (#34323)
fix(minor): Dirty form after clicking on Get advances button
2023-03-07 15:45:18 +05:30
Sagar Sharma
4cd16a839b Merge branch 'develop' into fix-22-23-05690 2023-03-07 14:55:46 +05:30
marination
f02fc8acf0 fix: Don't add to deductions if amount is 0
- misc: better docstring
2023-03-07 12:12:45 +05:30
Deepesh Garg
10632d75b0 fix: Do not calculate commission post submit (#34267)
* fix: Do not calculate commision post submit

* chore: Update condition to match server side logic

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>

---------

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2023-03-07 11:44:09 +05:30
Deepesh Garg
7d0199d743 fix: Default sales team not getting set (#34284) 2023-03-07 11:41:26 +05:30
Deepesh Garg
ea8e23384d fix: Payment Request against sales order with disabled rounded total (#34281)
* fix: Payment Request against sales order with disabled rounded total

* chore: Do not consider advance amount
2023-03-07 11:40:29 +05:30
Sagar Sharma
3952b3373d Merge branch 'develop' into fix-22-23-05690 2023-03-07 10:50:39 +05:30
s-aga-r
8ad9e99cea perf: update_completed_qty() in material_request.py 2023-03-07 10:50:11 +05:30
Raffael Meyer
ee12313dbb Merge branch 'develop' into early-payment-loss 2023-03-06 21:41:54 +01:00
s-aga-r
de18f98c5c perf: Stock Entry (Material Transfer) 2023-03-06 23:41:04 +05:30
rohitwaghchaure
53efc8ff77 Merge pull request #34305 from rohitwaghchaure/fixed-not-able-to-complete-bom-update-log
fix: BOM Update log not completed
2023-03-06 23:23:58 +05:30
Sagar Sharma
04b346865f Merge pull request #34301 from barredterra/rename-timezone-utils
refactor: use renamed timezone utils
2023-03-06 22:22:23 +05:30
marination
7f2e7badff fix: Set deduction amount in company currency on Doctype
- Even via JS, deductions amount is always in company currency
- Since there is nothing dynamic about this field, set it in the doctype spec itself
- fixed: Inconsistency between label currency and field currency formatted value
2023-03-06 17:45:26 +05:30
Deepesh Garg
5dabc98ba5 chore: Add index to period closing voucher column 2023-03-06 16:56:33 +05:30
Marica
b5f80815a1 Merge branch 'develop' into early-payment-loss 2023-03-06 15:50:50 +05:30
marination
c217bb2018 test: PE from SI with early payment discount amount & PE assertions in discount % test 2023-03-06 15:02:32 +05:30
marination
2ae5834290 fix: Back update discounted amount in Invoice based on discount type
- Discount value was always trated as a percentage on back updation
2023-03-06 14:54:46 +05:30
Rohit Waghchaure
2f157fa5d3 fix: BOM Update log not completed 2023-03-06 12:09:50 +05:30
Deepesh Garg
310f71c313 test: Add static posting dates to tests 2023-03-05 18:43:26 +05:30
Deepesh Garg
4a2046dfb6 fix: Aggregation with previous closing balance 2023-03-05 18:18:33 +05:30
barredterra
502a37a864 refactor: use renamed timezone utils
https://github.com/frappe/frappe/pull/20253
2023-03-04 19:31:40 +01:00
Sagar Sharma
ac87fca0d7 Merge pull request #34299 from s-aga-r/fix-22-23-05638-2
fix: Stock Reconciliation `actual_qty`
2023-03-04 17:35:05 +05:30
s-aga-r
70de444b7b fix: Stock Reconciliation actual_qty 2023-03-04 17:06:07 +05:30
Sagar Sharma
0185096e09 Merge pull request #34298 from s-aga-r/fix-22-23-05638-1
fix: update inventory dimensions before returning sle
2023-03-04 16:50:55 +05:30
s-aga-r
e6a02719f7 fix: update inventory dimensions before returning sle 2023-03-04 16:02:50 +05:30
Sagar Sharma
31ed4ef6d6 Merge pull request #34293 from s-aga-r/fix-22-23-05638
fix: `Inventory Dimension` for `Stock Reconciliation`
2023-03-04 02:13:13 +05:30
s-aga-r
0e1b7760a8 fix: Inventory Dimension for Stock Reconciliation 2023-03-04 01:05:55 +05:30
s-aga-r
53701c37b1 feat: consider over_order_allowance while validating sales order qty 2023-03-03 22:03:54 +05:30
Devin Slauenwhite
6190b4cf63 chore: revert unrelated changes. 2023-03-03 09:42:29 -05:00
marination
dc2998f544 fix: Set deductions in base currency
- Use field precision to get more accurate values
2023-03-03 17:24:43 +05:30
marination
75ec0a0a85 fix: Recalculate difference amount after setting deductions 2023-03-03 14:13:27 +05:30
Marica
619824855a Merge branch 'develop' into early-payment-loss 2023-03-03 13:53:57 +05:30
marination
768c3a4927 fix: Taxes aren't discounted on early payment discount
- Deductions in payment entry must be split into income loss and tax loss
- Compute total discount in percentage, makes discounting different amounts proportionately easier
2023-03-03 13:21:38 +05:30
s-aga-r
d7da8928ac feat: add field Over Order Allowance (%) in Selling Settings 2023-03-03 11:19:28 +05:30
s-aga-r
8bcbc45add feat: consider over_order_allowance while validating order qty 2023-03-03 11:11:33 +05:30
s-aga-r
fc1088d9c4 fix: don't map item row having 0 qty 2023-03-03 10:50:17 +05:30
s-aga-r
f3993783a3 refactor: rewrite blanket_order.py queries in QB 2023-03-02 17:18:46 +05:30
s-aga-r
f5937f46cb feat: add field Over Order Allowance (%) in Buying Settings 2023-03-02 17:04:09 +05:30
Deepesh Garg
c3d455653e Merge pull request #34258 from deepeshgarg007/finance_book_read_only
chore: Make finance book read only
2023-03-02 16:47:47 +05:30
Deepesh Garg
c089c4156c chore: Minor fixes 2023-03-02 16:46:56 +05:30
s-aga-r
abf9a28d6a fix: hide + button based on Blanket Order Type 2023-03-02 16:40:37 +05:30
Sagar Sharma
6e43d6882a Merge pull request #34117 from s-aga-r/github-issue-33344
refactor: rewrite `get_item_details.py` queries in `QB`
2023-03-02 14:54:58 +05:30
Sagar Sharma
90e1a6bc16 Merge branch 'develop' into github-issue-33344 2023-03-02 14:24:14 +05:30
Sagar Sharma
310643d215 Merge pull request #34273 from s-aga-r/fix-return-pr-serial-no
fix: `rejected_serial_no` not getting copied from PR to PR(Return)
2023-03-02 12:37:39 +05:30
Sagar Sharma
6e9d001819 Merge branch 'develop' into github-issue-33344 2023-03-02 12:09:21 +05:30
Sagar Sharma
50dc0c18eb Merge branch 'develop' into fix-return-pr-serial-no 2023-03-02 12:06:59 +05:30
Ankush Menat
330ae419be fix!: Parse float as per number format in quality inspection (#34259)
fix: Parse float as per number format in quality inspection
2023-03-02 11:59:18 +05:30
s-aga-r
cb0b6de4b9 fix: Serial No is mandatory even if the qty is 0 2023-03-02 11:27:46 +05:30
s-aga-r
a9f0a11ce6 fix: rejected_serial_no not getting copied from PR to PR(Return) 2023-03-02 11:15:46 +05:30
Deepesh Garg
790f0aac12 Merge branch 'develop' into finance_book_read_only 2023-03-02 10:01:12 +05:30
Deepesh Garg
6a89cb98ce Merge pull request #33874 from frappe/alternative-items-quotation
feat: Support for Alternative Items in Quotation
2023-03-01 17:41:24 +05:30
Suraj Shetty
e0a3869874 Merge pull request #34266 from frappe/mergify/bp/develop/pr-34265 2023-03-01 17:06:20 +05:30
Suraj Shetty
6cd2ba5c66 fix: Resolve conflicts
(cherry picked from commit f6469d8398)
2023-03-01 10:59:21 +00:00
Suraj Shetty
46a722d51c fix: Wrap unexpectedly long text in remark
(cherry picked from commit ba66a6714c)

# Conflicts:
#	erpnext/accounts/report/general_ledger/general_ledger.html
(cherry picked from commit b13bf1ebc5)
2023-03-01 10:59:20 +00:00
rohitwaghchaure
32b64db4d6 Merge pull request #34254 from rohitwaghchaure/fixed-subcontracting-stock-availability-issue
fix: consumed qty validation for subcontracting receipt
2023-03-01 15:15:25 +05:30
Deepesh Garg
28dd1a25cb chore: Make finance book read only 2023-03-01 15:00:24 +05:30
Deepesh Garg
e5f603c9d9 perf: Apply closing balance in Trial Balance report 2023-03-01 14:48:06 +05:30
Deepesh Garg
95c9aafda9 fix: Update patch to generate closing balance entries 2023-03-01 14:45:22 +05:30
Deepesh Garg
436fc03eda fix: Closing balance entries for period closing voucher 2023-03-01 14:43:49 +05:30
Rohit Waghchaure
b38fe24090 fix: consumed qty validation for subcontracting receipt 2023-03-01 11:42:59 +05:30
rohitwaghchaure
d88fe27be6 Merge pull request #34195 from HarryPaulo/fix-performance-on-insert-item
fix: Performance improvement when adding a new item
2023-03-01 10:10:14 +05:30
ruthra kumar
f9c75055bb Merge pull request #34241 from ruthra-kumar/bug_on_credit_note_creation
fix: pos return throwing amount greater than grand total
2023-02-28 18:22:30 +05:30
rohitwaghchaure
9bb8e351a2 Merge pull request #34243 from rohitwaghchaure/fixed-date-filters-in-subcontracting-reports
fix: default date in Subcontracting reports
2023-02-28 18:01:14 +05:30
Rohit Waghchaure
dfddc4efc3 fix: default date in Subcontracting reports 2023-02-28 17:53:51 +05:30
rohitwaghchaure
2942cee31e Merge pull request #34235 from rohitwaghchaure/feat-adjust-valuation-rate
feat: adjust purchase receipt valuation rate as per purchase invoice rate
2023-02-28 17:34:53 +05:30
ruthra kumar
35c70f39fa fix: pos return throwing amount greater than grand total 2023-02-28 16:21:32 +05:30
Rohit Waghchaure
a8445da02a fix: labels name 2023-02-28 15:47:45 +05:30
s-aga-r
58c027d4cc chore: Linters 2023-02-28 15:14:34 +05:30
Sagar Sharma
26ced47ddc Merge branch 'develop' into github-issue-33344 2023-02-28 15:01:40 +05:30
Rohit Waghchaure
8e86553717 test: added test cases 2023-02-28 14:57:04 +05:30
Sagar Sharma
90abb28cb4 Merge pull request #34060 from vishdha/pos_fix
fix: multiple Point of Sale conversion issue resolved
2023-02-28 14:55:14 +05:30
Rohit Waghchaure
eab775ef32 feat: adjust purchase receipt valuation rate as per purchase invoice rate 2023-02-28 14:11:55 +05:30
Vishal
f18ae5856f chore: minor changes in pos_controller 2023-02-28 11:03:26 +05:30
Vishal
a51bec0269 chore: minor change 2023-02-28 11:03:26 +05:30
Vishal
3ebe7d861d chore: minor changes added to code 2023-02-28 11:03:26 +05:30
Vishal
1de531e56e fix: multiple pos conversion issue resolved 2023-02-28 11:03:25 +05:30
Sagar Sharma
ecd15d2a65 Merge branch 'develop' into github-issue-33344 2023-02-27 16:57:29 +05:30
Sagar Sharma
9cd42dd60f Merge pull request #34225 from s-aga-r/fix-mr-warehouse
fix: set `from_warehouse` and `to_warehouse` while mapping SE
2023-02-27 12:52:53 +05:30
s-aga-r
c09a61f360 fix: set from_warehouse and to_warehouse while mapping SE 2023-02-27 12:26:21 +05:30
Sagar Sharma
c22641092a Merge pull request #34212 from brian-pond/patch-2
fix: Remove missing DocField in fetch_from
2023-02-27 11:52:53 +05:30
Sagar Sharma
bda1340760 Merge branch 'develop' into patch-2 2023-02-27 11:52:31 +05:30
Patrick Eissler
bbb6a62a7d chore: add german translations (#34167)
* chore: add german translations

* Apply suggestions from code review

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>

---------

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
2023-02-27 11:49:22 +05:30
Deepesh Garg
81651beaef Merge pull request #34174 from Vishnu7025/coa-import-currency
fix: currency in coa import
2023-02-27 11:48:44 +05:30
Sagar Sharma
5dcb723fc7 Merge branch 'develop' into patch-2 2023-02-27 11:14:14 +05:30
Brian Pond
83f3e317e1 fix: Remove missing DocField in fetch_from 2023-02-27 11:10:51 +05:30
ruthra kumar
d8b9b83b11 Merge pull request #34207 from ruthra-kumar/fix_permisssion_error_on_work_order_creation
fix: permission error while calling get_work_order_items
2023-02-27 09:55:18 +05:30
Anand Baburajan
9e38b61bd9 Merge pull request #34214 from AnandBaburajan/manual_asset_schedule_develop
fix: asset manual depr schedule [develop]
2023-02-26 20:22:34 +05:30
Anand Baburajan
feeebb1262 Merge branch 'develop' into manual_asset_schedule_develop 2023-02-26 17:40:26 +05:30
anandbaburajan
dd74839eba fix: manual depr schedule 2023-02-26 17:36:36 +05:30
Deepesh Garg
a663df376c fix: Add patch to update closing balances 2023-02-26 16:06:04 +05:30
Deepesh Garg
6607c8bd82 fix: Order by issue in aggregation query 2023-02-26 16:05:26 +05:30
Deepesh Garg
f92c63fb10 feat: Add validations against period closing voucher 2023-02-26 15:53:33 +05:30
Deepesh Garg
7fa7d6b5e4 chore: Rewrite query using query builder 2023-02-26 15:52:30 +05:30
Deepesh Garg
e18336ebe7 feat: Add views in standard filter 2023-02-26 15:47:29 +05:30
rohitwaghchaure
6d6e3cb015 Merge pull request #34206 from rohitwaghchaure/fixed-trivial-error-for-stock-reposting
fix: not able to repost gl entries
2023-02-24 21:10:27 +05:30
ruthra kumar
a11d3327df fix(test): use standalone method to fetch work orders from SO 2023-02-24 21:07:49 +05:30
ruthra kumar
b6bad728cd fix: permission error while calling get_work_order_items 2023-02-24 21:07:46 +05:30
Rohit Waghchaure
7d10dd9ea8 fix: not able to repost gl entries 2023-02-24 17:50:00 +05:30
Deepesh Garg
e26c6dc76b Revert "fix: Concurrency issues in Sales and Purchase returns" (#34202)
Revert "fix: Concurrency issues in Sales and Purchase returns (#34019)"

This reverts commit a67284e96d.
2023-02-24 15:28:14 +05:30
rohitwaghchaure
04f00fd141 Merge pull request #34199 from rohitwaghchaure/fixed-converison-factor-issue
fix: conversion factor not set
2023-02-24 14:56:36 +05:30
Rohit Waghchaure
8e46aebc50 fix: conversion factor not set 2023-02-24 14:49:05 +05:30
HarryPaulo
49af5ba434 fix: Performance improvement when adding a new item 2023-02-23 18:11:09 -03:00
rohitwaghchaure
670304fe8e Merge pull request #34189 from rohitwaghchaure/prevent-to-make-item-price-for-template
fix: user shouldn't able to make item price for item template
2023-02-23 20:47:48 +05:30
Rohit Waghchaure
6417ae0ee8 fix: user shouldn't able to make item price for item template 2023-02-23 20:21:50 +05:30
Deepesh Garg
c3f39c3f32 feat: Cascade closing balances on PCV submit 2023-02-23 16:48:54 +05:30
Deepesh Garg
36c08d0835 fix: Add patches to create accounting dimension in Closing Balance 2023-02-23 16:46:37 +05:30
Marica
47ab26dedf Merge branch 'develop' into alternative-items-quotation 2023-02-23 13:20:28 +05:30
ruthra kumar
a9920715ab Merge pull request #34022 from ruthra-kumar/gross_profit_memory_and_performance_issue
perf: Gross Profit report will fetch SLE's on demand and memoize
2023-02-23 11:55:08 +05:30
ruthra kumar
88d888d9d0 refactor: use docstatus from Delivery Note Item 2023-02-23 11:15:56 +05:30
rohitwaghchaure
3692f960d0 Merge pull request #34172 from rohitwaghchaure/zero-rate-adjust-issue-using-lcv
fix: zero division error while making LCV
2023-02-23 10:54:01 +05:30
ruthra kumar
60d3b86cdb Merge pull request #34176 from ruthra-kumar/fix_ui_freeze_on_sales_invoice
fix: ui freeze on item selection in sales invoice
2023-02-23 10:35:50 +05:30
rohitwaghchaure
e3de216e06 Merge pull request #34173 from rohitwaghchaure/incorrect-color-showing-in-report
fix: incorrect color in the BOM Stock Report
2023-02-23 09:54:45 +05:30
ruthra kumar
6412583e98 fix: ui freeze on item selection in sales invoice 2023-02-23 09:36:21 +05:30
vishnu
e3c000d0be chore: change column label 2023-02-22 10:53:11 +00:00
vishnu
19c0b7a523 fix: currency in coa import 2023-02-22 10:24:45 +00:00
Rohit Waghchaure
a8f03ebf7f fix: incorrect color in the BOM Stock Report 2023-02-22 14:24:18 +05:30
Rohit Waghchaure
80e94a08cf fix: zero division error while making LCV 2023-02-22 13:29:06 +05:30
Deepesh Garg
b44a19bd1a feat: Introduce opening entry for reporting 2023-02-21 23:20:28 +05:30
Sankara Subramanian V
a06d24037b refactor(region): Splitting of KSA(Saudi Arabia) Regional logic from ERPNext (#33895)
* feat: remove KSA regional code
2023-02-21 22:52:20 +05:30
ruthra kumar
dde5bbc9a7 Merge pull request #34102 from ruthra-kumar/pos_validate_for_duplicate_pos_invoices
fix: check for duplicate pos invoices in closing entry
2023-02-21 18:45:39 +05:30
ruthra kumar
47add0b751 fix: check for duplicate in pos closing and pos merge log entry 2023-02-21 17:50:57 +05:30
Deepesh Garg
9739d8b52a feat: Introduce opening entry for reporting 2023-02-21 15:23:29 +05:30
ruthra kumar
4a7b1de2d8 refactor: clear records in batches in 'Transaction Deletion Record' (#34109)
refactor: clear records in batches
2023-02-21 14:27:00 +05:30
Deepesh Garg
c88444a6c4 fix: Filters in item-wise sales history report (#34145) 2023-02-21 14:26:33 +05:30
Anand Baburajan
5e3a034395 Merge pull request #34153 from AnandBaburajan/fixed_asset_report_fiscal_year
fix: fiscal year error for existing assets in fixed asset register
2023-02-21 13:54:54 +05:30
Anand Baburajan
b781c57dcb Merge branch 'develop' into fixed_asset_report_fiscal_year 2023-02-21 13:10:07 +05:30
Himanshu Shivhare
3547252217 Telegram Group link updated (#34149) 2023-02-21 13:05:03 +05:30
anandbaburajan
76861eb332 fix: fiscal year error for existing assets in fixed asset register 2023-02-21 13:04:01 +05:30
Deepesh Garg
35cdd996a9 fix: Use normal rounding for Tax Withholding Category (#34114) 2023-02-21 12:57:49 +05:30
Sagar Sharma
18541ef54f Merge pull request #34138 from s-aga-r/dn-create-button
fix(ux): `ReferenceError: me is not defined` Delivery Note
2023-02-21 10:26:52 +05:30
Sagar Sharma
77b88961f6 Merge branch 'develop' into dn-create-button 2023-02-21 10:19:09 +05:30
marination
6b789e2f04 fix: Map only non alternative items from Quotation in Sales Invoice
- Since there's no item selection, only Quotation selection :/
2023-02-20 21:17:43 +05:30
marination
19456127cf fix: Handle Get Items From in Sales Order
- Map all non alternatives from Quotation to SO if no selected items
- Show disclaimer mentioning that Qtns with alternatives must be mapped to SO from the Qtn form
2023-02-20 20:52:14 +05:30
Ankush Menat
3f2a2c96f6 fix: Add company perm check unconditionally (#34142)
fix: Add employee perm check unconditionally
2023-02-20 16:07:27 +05:30
rohitwaghchaure
5e9620b98b Merge pull request #34139 from rohitwaghchaure/fixed-stock-ledger-report-filter-issue
fix: inventory dimension filter not overriding with existing filter for stock ledger report
2023-02-20 13:04:23 +05:30
rohitwaghchaure
3871cf7110 Merge pull request #33722 from rohitwaghchaure/purchase-invoice-performance-issue
fix: purchase invoice performance issue
2023-02-20 12:47:07 +05:30
Rohit Waghchaure
0e388ba872 fix: inventory dimension filter not overriding with existing filter for stock ledger report 2023-02-20 12:28:43 +05:30
Marica
3c96791d52 fix: Use block variable
Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-02-20 12:04:14 +05:30
s-aga-r
44ee9f0f19 chore: Linters 2023-02-20 11:43:32 +05:30
s-aga-r
1b010add26 fix(ux): ReferenceError: me is not defined Delivery Note 2023-02-20 11:17:49 +05:30
Sagar Sharma
5bbe8728ad Merge branch 'develop' into github-issue-33344 2023-02-20 10:47:29 +05:30
Ankush Menat
e84e778151 chore(deps): remove stdnum dependency (#34137) 2023-02-20 10:40:07 +05:30
Sagar Sharma
c8fda8a8b5 Merge branch 'develop' into github-issue-33344 2023-02-19 22:17:20 +05:30
Alirio Castro
183e42af1a fix: differency entry journal debit/credit missing (#34104)
* fix: difference entry journal is wrong

* fix: difference entry journal is wrong
2023-02-19 19:40:13 +05:30
Johannes Obermeier
2a90fad710 feat(stock): Support more barcodes in an item by validate the barcode with the barcodenumber module (#33863)
feat(stock): Support more barcodes in an article by validate the barcode with the barcodenumber module
2023-02-19 19:36:05 +05:30
Sagar Sharma
2959285c82 Merge branch 'develop' into fix-reserve-qty 2023-02-19 12:45:39 +05:30
Sagar Sharma
f342e6e765 Merge branch 'develop' into github-issue-33344 2023-02-19 12:17:33 +05:30
ruthra kumar
c722f2819c fix: ignore repost payment ledger on basic documents cancellation (#34054)
fix: ignore repost payment ledger on cancel/delete of Inv/Pay/JE's

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-02-19 12:09:10 +05:30
Sagar Sharma
2c3973156f Merge branch 'develop' into github-issue-33344 2023-02-19 12:07:27 +05:30
Richard Case
5b7d23de15 feat: bank reconciliation and plaid changes (#33986)
fix: plaid link refresh: update account ids
fix: plaid transactions for credit cards & add accounts on link refresh if they don't exist
fix: bank reconciliation amount matching
fix: bank reconciliation dialog usability
feat: rewrite bank transaction reconciliation to allow multiple transactions to reconcile against vouchers before clearance
fix: matching transaction amounts and race condition bug
fix: ensure there is a reference number in plaid transactions and other tweaks
feat: add references to Payroll Entry Bank Journal Entry
feat: only clear Voucher once all Bank GLEs are allocated to Bank Transactions
fix: strange type error
feat: add payment method field to bank and plaid transactions and prepopulate relevant bank reconciliation new voucher fields
feat: bank reconciliation - allow bank transactions to reconcile against themselves for when there are banking amendments
fix: bank transaction self-reconcile bug and tidy
fix: bank reconciliation datatable index update
2023-02-19 11:50:17 +05:30
Shadrak Gurupnor
5e48e61c66 fix: forced delete linked desktop_icons (#34107)
* fix: forced delete linked desktop_icons

* fix: black
2023-02-19 11:42:35 +05:30
ruthra kumar
9c6466f15b fix: consider rounded total amount while making payment request (#34110) 2023-02-19 11:41:41 +05:30
Devin Slauenwhite
c76352340d fix(ux): customer/supplier docfield layout (#34125) 2023-02-19 11:37:32 +05:30
Sagar Sharma
07c6ad6fa7 Merge branch 'develop' into github-issue-33344 2023-02-18 22:24:06 +05:30
s-aga-r
35489fbbf9 refactor: remove method get_serial_no_batchwise from get_item_details.py 2023-02-18 22:23:53 +05:30
s-aga-r
6b144baa69 refactor: rewrite get_item_details.py queries in QB 2023-02-18 22:23:51 +05:30
rohitwaghchaure
dff9655d46 Merge pull request #34115 from rohitwaghchaure/fixed-convert-to-item-warehouse-reposting
feat: provision to convert transaction based reposting to item wareho…
2023-02-18 21:31:20 +05:30
Rohit Waghchaure
f1383b5ef9 feat: provision to convert transaction based reposting to item warehouse based reposting 2023-02-18 19:31:48 +05:30
Sagar Sharma
c292859b59 Merge pull request #34077 from s-aga-r/purchase-order-portal
fix: show Purchase Order Portal `Pay` button based on configuration
2023-02-18 12:20:14 +05:30
Sagar Sharma
91983fb4c6 Merge branch 'develop' into purchase-order-portal 2023-02-17 23:17:00 +05:30
Prateek
b93c18bd4a fix: use max function to get default company address 2023-02-17 20:25:13 +05:30
rohitwaghchaure
47264481ad Merge branch 'develop' into purchase-invoice-performance-issue 2023-02-17 16:41:30 +05:30
Anand Baburajan
6b3028d77b Merge pull request #34111 from AnandBaburajan/asset_fixes_17_feb
fix: repair status after deletion, asset status after manual depr entry and other misc bugs [develop]
2023-02-17 16:12:45 +05:30
anandbaburajan
e50bd52162 fix: asset repair status after deletion and asset status after manual depr entry 2023-02-17 15:32:24 +05:30
Sagar Sharma
ee21850ab8 Merge branch 'develop' into purchase-order-portal 2023-02-17 11:08:30 +05:30
ruthra kumar
067f8c1d70 Merge pull request #34071 from ruthra-kumar/rearrange_patch_order
patch: reorder migration patches
2023-02-17 11:04:36 +05:30
ruthra kumar
68202639f5 chore: reorder migration patches.
'migrate_gl_to_payment_ledger',
'migrate_remarks_from_gl_to_payment_ledger' should always run last.
2023-02-17 10:03:55 +05:30
rohitwaghchaure
4399a93843 Merge pull request #34091 from rohitwaghchaure/fixed-incorrect-consumed-qty-in-scr
fix: incorrect consumed qty in subcontracting receipt
2023-02-16 15:10:14 +05:30
Sagar Sharma
758f47bba6 Merge branch 'develop' into purchase-order-portal 2023-02-16 14:28:05 +05:30
Sagar Sharma
70660191a6 Merge pull request #34090 from frappe/update-codeowners
chore: update `CODEOWNERS`
2023-02-16 14:26:11 +05:30
Sagar Sharma
320f3c1ac0 Merge branch 'develop' into update-codeowners 2023-02-16 14:25:19 +05:30
Rohit Waghchaure
156e45970a fix: incorrect consumed qty in subcontracting receipt 2023-02-16 14:17:27 +05:30
HENRY Florian
4d79ffe3c6 fix: rename duplicate field name with same type into a DocType to avoid import Error (#34053)
* fix: Delivery Note field label set

* fix: Item field label de-duplicate name

* fix: Payment Entry field label de-duplicate name

* fix: Pruicing Rule field label de-duplicate name

* fix: Project field label de-duplicate name

* fix: Timesheet field label de-duplicate name

* Update erpnext/accounts/doctype/pricing_rule/pricing_rule.json

Co-authored-by: Sagar Sharma <sagarsharma.s312@gmail.com>

* Update erpnext/stock/doctype/item/item.json

Co-authored-by: Sagar Sharma <sagarsharma.s312@gmail.com>

---------

Co-authored-by: Sagar Sharma <sagarsharma.s312@gmail.com>
2023-02-16 13:46:19 +05:30
Sagar Sharma
e8b8c51d82 chore: update CODEOWNERS 2023-02-16 12:55:37 +05:30
Sagar Sharma
fb5796f18f Merge branch 'develop' into purchase-order-portal 2023-02-16 11:22:19 +05:30
Tej Pochiraju
3f44ef8790 Allow bulk import of party specific item (#34083)
Co-authored-by: Tej Pochiraju <tej@iotready.co>
2023-02-16 10:11:25 +05:30
Sagar Sharma
471e77b931 Merge branch 'develop' into purchase-order-portal 2023-02-15 22:46:07 +05:30
Sagar Sharma
c793fb0bb4 Merge pull request #34086 from s-aga-r/github-issue-33930
fix: create `Delivery Trip` from `Delivery Note` list
2023-02-15 22:40:10 +05:30
Sagar Sharma
c7a540b7e2 Merge branch 'develop' into github-issue-33930 2023-02-15 22:37:52 +05:30
s-aga-r
6c6195bae6 fix: create Delivery Trip from Delivery Note list 2023-02-15 22:37:26 +05:30
Sagar Sharma
421e34a181 Merge branch 'develop' into purchase-order-portal 2023-02-15 21:49:09 +05:30
Sagar Sharma
0cd69c8700 Merge pull request #34080 from s-aga-r/batch-no-item
chore: copy `item_code` to `Batch` while creating new batch from SCR
2023-02-15 21:39:07 +05:30
Sagar Sharma
46afbb594a Merge branch 'develop' into fix-reserve-qty 2023-02-15 21:37:30 +05:30
s-aga-r
ffa9c6e4d9 chore: copy item_code to Batch while creating new batch from SCR 2023-02-15 20:16:42 +05:30
s-aga-r
20bdc63b03 fix: show Purchase Order Portal Pay button based on configuration 2023-02-15 20:14:39 +05:30
marination
74fab53e28 test: Alternative items in Quotation
- Taxes and totals, mapping, back updation
2023-02-15 15:04:00 +05:30
s-aga-r
d7ef5ad955 chore: add field show_pay_button in Buying Settings 2023-02-15 14:43:10 +05:30
Marica
22010b745c Merge branch 'develop' into alternative-items-quotation 2023-02-15 13:05:33 +05:30
Anand Baburajan
d3ff289779 Merge pull request #34057 from AnandBaburajan/more_asset_bug_fixes
fix: manual depr entries in asset_depreciations_and_balances report and some misc bugs [develop]
2023-02-15 12:32:35 +05:30
Anand Baburajan
ef56060166 Merge branch 'develop' into more_asset_bug_fixes 2023-02-15 11:55:55 +05:30
rohitwaghchaure
2e43123048 Merge pull request #34065 from barredterra/print-rfq-letterhead
fix: change parameter name for letter head
2023-02-15 09:31:06 +05:30
rohitwaghchaure
9235918d65 Merge pull request #34061 from rohitwaghchaure/feat-allow-to-make-in-transit-entry
feat: allow to make in transit transfer entry from material request
2023-02-15 09:08:22 +05:30
barredterra
d5b7f2e49e test: download RFQ PDF 2023-02-14 19:53:39 +01:00
barredterra
8e40c04494 refactor: download RFQ PDF 2023-02-14 19:53:03 +01:00
barredterra
f7fd30fecf fix: change parameter name for letter head
To match changes from https://github.com/frappe/frappe/pull/19627
2023-02-14 19:34:36 +01:00
Devin Slauenwhite
4c5147f270 Merge: upstream/develop into pps190/fix-reserve-qty 2023-02-14 10:22:54 -05:00
Deepesh Garg
e69ea17f59 Merge pull request #34059 from AnandBaburajan/anand_codeowners
chore: add anand to asset's codeowner
2023-02-14 19:38:46 +05:30
Rohit Waghchaure
5b6128848f feat: allow to make in transit transfer entry from material request 2023-02-14 19:03:07 +05:30
Anand Baburajan
662ebc5c1a Merge branch 'develop' into anand_codeowners 2023-02-14 18:35:05 +05:30
anandbaburajan
d003370f61 chore: add anand to asset's codeowner 2023-02-14 18:33:01 +05:30
Anand Baburajan
b0cfb0b9ef Merge branch 'develop' into more_asset_bug_fixes 2023-02-14 18:09:55 +05:30
anandbaburajan
a58f2e6c03 fix: asset_depreciation_and_balances report doesn't reflect manual depr entries 2023-02-14 17:59:16 +05:30
Sagar Sharma
75d9dbca43 Merge pull request #34018 from s-aga-r/fix-so-on-hold
fix: update `reserved_qty` when `Sales Order` marked as `Hold`
2023-02-14 16:51:56 +05:30
Sagar Sharma
447310702e Merge branch 'develop' into fix-so-on-hold 2023-02-14 16:01:43 +05:30
ruthra kumar
960085853a Merge pull request #34048 from ruthra-kumar/typeerror_on_sales_order_analysis
fix: typeerror on Sales Order analysis report
2023-02-14 15:27:36 +05:30
Sagar Sharma
5faa62c49a Merge branch 'develop' into fix-so-on-hold 2023-02-14 11:06:10 +05:30
rohitwaghchaure
3014bac293 Merge pull request #34046 from FHenry/dev_fix_BOM_import
fix: BOM import failed as use same label field for Raw Materials Item table and Scrap Item table
2023-02-14 10:29:26 +05:30
ruthra kumar
00a4191966 fix: typeerror on Sales Order analysis report 2023-02-14 09:56:35 +05:30
Raffael Meyer
770369e5c1 feat: translate fixtures during runtime, not installation (#33996)
feat: install untranslated fixtures from files

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-02-14 09:29:27 +05:30
Florian HENRY
86be259341 fix: BOM import failed as importer use same label field for Raw MaterialsItem table and Scrap Item table 2023-02-13 23:34:20 +01:00
Deepesh Garg
ce748cec3a fix: Amount validation in Payment Request against Purchase Order (#34042) 2023-02-13 20:56:24 +05:30
HENRY Florian
231fe4156f fix: should never get cutomer price on purchase document (#34002)
* fix: never get cutomer price on purchase document

chores: syntax

chore: typo in stock_entry get_uom_details (#33998)

fix: typo in stock_entry get_uom_details

chores: syntax

* feat: add test for get_item_detail price list oriented

* feat: add test for get_item_detail price price oriented

* feat: add test for get_item_detail price price oriented

* chore: clean test code
2023-02-13 20:56:05 +05:30
anandbaburajan
c533213225 fix: opening_accumulated_depreciation and precision in charts 2023-02-13 17:22:31 +05:30
Deepesh Garg
ff4ae69408 Merge branch 'develop' into gross_profit_memory_and_performance_issue 2023-02-13 15:38:26 +05:30
Deepesh Garg
a67284e96d fix: Concurrency issues in Sales and Purchase returns (#34019) 2023-02-13 15:31:19 +05:30
ruthra kumar
4f2553e7b6 Merge pull request #34021 from ruthra-kumar/handle_rare_cases_of_null_in_outstanding_calculation
fix: rare instances of IntegrityError while cancelling journals against cr note
2023-02-13 14:14:27 +05:30
ruthra kumar
192a3395a5 refactor: filter only immediate upcoming payment term for each SO (#33923)
* fix: ignore closed or 'on hold' orders

* refactor: filter immediate upcoming term

---------

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-02-13 12:36:40 +05:30
ruthra kumar
e5a2b15fba fix: unwanted difference amount calculation on cr note and invoice with same currency (#34020)
* fix: incorrect difference amount while reconiling cr/dr notes

* fix(test): catch incorrect difference amount calculation

Fixed issues where difference amount was calculated for Cr Notes and Invoices of
the same currency.

---------

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-02-13 12:08:42 +05:30
Deepesh Garg
148703bfc2 feat: Setting to allow Sales Order creation against expired quotation (#33952)
* feat: Setting to allow Sales Order creation against expired quotation

* chore: linting issues
2023-02-13 10:27:55 +05:30
Deepesh Garg
ce3f9622cb Merge branch 'develop' into handle_rare_cases_of_null_in_outstanding_calculation 2023-02-13 09:42:41 +05:30
Deepesh Garg
48bb2c942b fix: Ignore Payment Ledger Entry on dunning cancel (#34025)
* fix: Ignore Payment Ledger Entry on dunning cancel

* chore: fix translation issue
2023-02-13 09:42:01 +05:30
Deepesh Garg
77f6789706 Revert "Update tr.csv" (#34026)
Revert "chore: Update Turkish translations (#33985)"

This reverts commit 6ea0a69d3a.
2023-02-13 08:27:00 +05:30
ruthra kumar
3e5691072a perf: fetch SLE's on demand and memoize 2023-02-12 14:49:17 +05:30
ruthra kumar
b9a7ff7c3d fix: IntegrityError while cancelling journals against cr note 2023-02-12 14:06:40 +05:30
Sagar Sharma
b0e8dfc1d0 Merge branch 'develop' into fix-so-on-hold 2023-02-12 13:37:21 +05:30
s-aga-r
d76759e066 fix: update reserved_qty when Sales Order marked as Hold 2023-02-12 13:36:26 +05:30
Richard Case
a0eb5e5535 fix: add payment hook to point of sale JS (#33988) 2023-02-11 20:40:15 +05:30
Raffael Meyer
e4953df4a3 fix: set per_billed based on hours when amounts are zero (#33984)
* fix: set per_billed based on hours when amounts are zero

* test: calculate_percentage_billed
2023-02-11 12:36:37 +05:30
Sabu Siyad
0df28c7174 fix(ecommerce): throw invalid doctype error in shop by category (#33901)
Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-02-11 12:28:42 +05:30
Raffael Meyer
201573ab9a refactor: install fixtures (#33964)
* refactor: install fixtures

* style: disable semgrep for install_defaults signature
2023-02-11 12:15:42 +05:30
Deepesh Garg
0efdc6c13a fix: Ignore mandatory fields while creating tax templates for new companies (#34005) 2023-02-11 12:03:48 +05:30
Raffael Meyer
ab7293bcd3 fix: list view for Terms and Conditions (#33925)
Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-02-11 11:09:26 +05:30
Mehmet Demirel
6ea0a69d3a chore: Update Turkish translations (#33985)
We are the ERPNext Turkey Team.
We request confirmation of the translation we sent.
2023-02-11 09:27:18 +05:30
Deepesh Garg
e478a5d0ce feat: Add filters in Loan Interest Report (#33907) 2023-02-11 09:17:24 +05:30
Deepesh Garg
47c91324b1 fix: Amount for debit and credit notes with 0 qty line items (#33902) 2023-02-11 09:04:00 +05:30
Akshay
185c543b73 chore: typo in stock_entry get_uom_details (#33998)
fix: typo in stock_entry get_uom_details
2023-02-10 20:42:35 +05:30
Devin Slauenwhite
5685492995 Merge remote-tracking branch 'frappe/develop' into fix-reserve-qty
# Conflicts:
#	erpnext/stock/doctype/delivery_note/test_delivery_note.py
2023-02-09 13:48:18 -05:00
Marica
bd7e6e264e Merge branch 'develop' into alternative-items-quotation 2023-02-06 16:30:10 +05:30
marination
db2076db69 refactor: Order based alternative items mapping
- Alternatives must be followed by a non-alternative item row
- On submit, store non-alternative rows in hidden checkbox to avoid recomputation
- Check for valid/mappable rows by row name
- UI: Select from table rows.Add single row for original/alternative item in dialog
- UI: Indicator for alternative items in dialog grid
- UI: Indicator legend and description of table
- DB: Added check field 'Has Alternative Item' not to be confused with 'Has Alternative' in Mfg
2023-02-06 16:25:38 +05:30
Anand Baburajan
9d8502f406 Merge pull request #33946 from AnandBaburajan/cancel_pi_cancelled_asset
fix: allow cancelling purchase invoice if linked asset is already cancelled
2023-02-05 16:06:47 +05:30
Anand Baburajan
4686217d3c Merge branch 'develop' into cancel_pi_cancelled_asset 2023-02-05 14:14:39 +05:30
Anand Baburajan
52435a3a7f Merge pull request #33945 from AnandBaburajan/manual_depr_entries_develop
fix: manual depr entry not updating asset value [develop]
2023-02-05 14:14:19 +05:30
Anand Baburajan
396f455258 Merge branch 'develop' into cancel_pi_cancelled_asset 2023-02-05 12:19:21 +05:30
Anand Baburajan
f8270f093d Merge branch 'develop' into manual_depr_entries_develop 2023-02-05 12:19:17 +05:30
Sagar Sharma
05f4b659a7 Merge pull request #33942 from s-aga-r/github-issue-33533
fix: stock entry from item dashboard (stock levels)
2023-02-05 09:46:27 +05:30
Sagar Sharma
2fedd595a9 Merge branch 'develop' into github-issue-33533 2023-02-05 09:36:17 +05:30
rohitwaghchaure
0352e1dace Merge pull request #33936 from rohitwaghchaure/fixed-negative-stock-error
fix: negative stock error
2023-02-04 23:34:56 +05:30
rohitwaghchaure
7d794bc70a Merge branch 'develop' into fixed-negative-stock-error 2023-02-04 22:35:58 +05:30
rohitwaghchaure
67b5471d09 Merge pull request #33941 from developsessions/fix_wrong_due_date_calculation
fix: default due_date was wrong calculated on template "_Test Payment Term Template 1" (last day of next month)
2023-02-04 22:34:47 +05:30
Anand Baburajan
82490d9f55 Merge branch 'develop' into cancel_pi_cancelled_asset 2023-02-04 18:37:34 +05:30
Anand Baburajan
07a0e2b7a8 Merge branch 'develop' into manual_depr_entries_develop 2023-02-04 18:37:34 +05:30
developsessions
c8cd351b39 style: apply results of lint run 2023-02-04 09:12:29 +01:00
Sagar Sharma
4a6a7593c3 Merge pull request #33940 from s-aga-r/github-issue-33917
chore: report `Warehouse wise Item Balance Age and Value`
2023-02-04 13:27:57 +05:30
anandbaburajan
3380dc5dea chore: use continue, not break 2023-02-04 11:20:26 +05:30
developsessions
9d0096ad9e fix: failed test, convert date time to string 2023-02-03 21:21:43 +01:00
Anand Baburajan
bb424965ee Merge branch 'develop' into cancel_pi_cancelled_asset 2023-02-03 23:00:40 +05:30
anandbaburajan
b961321de5 fix: allow PI cancel if linked asset is cancelled 2023-02-03 22:58:22 +05:30
Anand Baburajan
2d54bf7199 Merge branch 'develop' into manual_depr_entries_develop 2023-02-03 22:25:27 +05:30
anandbaburajan
e5da0d7a63 chore: refactor AssetDepreciationSchedule 2023-02-03 22:23:17 +05:30
anandbaburajan
8ea9e4576f fix: asset value for manual depr entries 2023-02-03 21:43:18 +05:30
developsessions
be1f941996 fix: Add missing 1 required positional argument: 'bill_date' 2023-02-03 14:50:44 +01:00
developsessions
c80aaad437 style: lint wrong from position 2023-02-03 13:55:36 +01:00
s-aga-r
dc0ddf8d7e fix: stock entry from item dashboard (stock levels) 2023-02-03 18:08:34 +05:30
developsessions
ce8a1086a7 fix: default due_date was wrong calculated on template "_Test Payment Term Template 1" (last day of next month) 2023-02-03 11:30:29 +01:00
s-aga-r
56356ffbb9 chore: add Item Name column in Warehouse wise Item Balance Age and Value report 2023-02-03 12:28:23 +05:30
s-aga-r
d7a665cb84 chore: column width in Warehouse wise Item Balance Age and Value report 2023-02-03 12:27:41 +05:30
Rohit Waghchaure
9ae7578b07 test: test case 2023-02-02 18:54:42 +05:30
Rohit Waghchaure
6d513e2519 fix: negative stock error 2023-02-02 18:40:15 +05:30
marination
03321f5f13 chore: Code simplification
- Map is not required, avoid filter multiple times, use single loop instead
- Better variable name
- Reduce LOC
2023-02-02 17:28:03 +05:30
ruthra kumar
2afed7de03 Merge pull request #33777 from ruthra-kumar/performance_tuning_remarks_migration
fix(patch): reduce memory usage while migrating remarks
2023-02-02 16:47:15 +05:30
ruthra kumar
ae2de3cf65 Merge pull request #33776 from ruthra-kumar/performance_tuning_ple_migration
patch: reduce memory usage by paging through records
2023-02-02 16:46:51 +05:30
ruthra kumar
cb1d33880c Merge pull request #33502 from ruthra-kumar/toggle_account_balances_in_coa
feat: Toggle display of Account Balance in Chart of Accounts
2023-02-02 09:20:53 +05:30
marination
b3fe7c6dad fix: Consider only ordered alternative/original item for Quotation status
- The original and its alternatives make a set of items where one is chosen
- While setting order status of Quotation, check if the chosen item from the set is fully ordered or not
- Filter out unselected items from the set
- Create a map containing the set of items and if they were ordered or not for ease of grouping
- The simple items will work as it used to
2023-02-01 19:10:32 +05:30
Gokulnath
2884738864 feat: adding warehouse filter for gross profit report (#33397)
Co-authored-by: ruthra kumar <ruthra@erpnext.com>
2023-02-01 18:44:15 +05:30
rohitwaghchaure
fbf2d7e9de Merge pull request #33918 from rohitwaghchaure/fixed-incorrect-bin-qty
fix: incorrect actual qty in Bin
2023-02-01 18:22:56 +05:30
Rohit Waghchaure
f8c852c54c fix: incorrect actual qty in Bin 2023-02-01 17:49:11 +05:30
Suraj Shetty
ffeda0cee9 Merge pull request #33916 from gavindsouza/permlevel-apis 2023-02-01 16:24:18 +05:30
Dany Robert
2cc7239dd5 fix: currency formatting in item-wise sales history (#33903)
* fix(item-sales-history): currency formatting

* chore: linting issues

* fix: convert raw sql to qb
2023-02-01 14:27:38 +05:30
Gavin D'souza
940ad6e3f2 fix: Fetch all fields via get_returned_qty_map_for_row 2023-02-01 13:53:37 +05:30
Gavin D'souza
3518d4be1d fix(update_billing_percentage): Remove permlevel checks on aggregated
value
2023-02-01 12:58:08 +05:30
Raffael Meyer
3c7b460fd8 fix: german chart of accounts "SKR03" (#33909)
* fix: german chart of accounts "SKR03"

- Added some missing account types and tax rates
- Added some missing accounts

* style: convert indentation to tabs

* fix: space before percentage sign

* feat: add some expense accounts

* refactor: replace unicode characters with utf-8

for better readability

* revert: add back groups for Bank and Cash accounts

Removed in 7d0d9c6900
2023-02-01 12:16:58 +05:30
Deepesh Garg
103a91ed40 Merge pull request #33808 from dj12djdjs/fix-lead-rename
fix: make title field readonly
2023-02-01 12:02:33 +05:30
ruthra kumar
9bb64107c5 perf: reduce memory usage while migrating remarks
Page through records using primary key
2023-02-01 10:44:44 +05:30
Sagar Sharma
4ac983cf09 Merge pull request #33910 from barredterra/named-place-rfq
feat: add incoterm named place to RFQ
2023-02-01 10:40:09 +05:30
barredterra
7156184933 feat: add incoterm named place to RFQ 2023-01-31 23:23:54 +01:00
marination
ece6358e60 fix: Iterate over list instead of map's output and formatting 2023-01-31 17:13:05 +05:30
rohitwaghchaure
c5834c1db4 Merge pull request #33896 from rohitwaghchaure/inventory-dimension-mandatory
feat: mandatory and mandatory depends on in inventory dimension
2023-01-31 15:18:14 +05:30
rohitwaghchaure
ddb4396117 Merge pull request #33715 from s-aga-r/fix-pick-list
fix: consider existing pick-list
2023-01-31 15:10:19 +05:30
Sagar Sharma
510b265830 Merge pull request #33869 from s-aga-r/fix-mr-rate-and-amount
fix: `amount` in `Material Request`
2023-01-31 14:44:11 +05:30
s-aga-r
6ffdeb1af8 fix: consider stock_qty if picked_qty is zero 2023-01-31 14:39:57 +05:30
Rohit Waghchaure
22d0e1373b test: added test case 2023-01-31 14:36:26 +05:30
Rohit Waghchaure
423f2b5627 feat: mandatory and mandatory depends on in inventory dimension 2023-01-31 14:19:14 +05:30
Sagar Sharma
6e015fd6be Merge branch 'develop' into fix-mr-rate-and-amount 2023-01-31 12:39:38 +05:30
Sagar Sharma
57c8871de0 Merge branch 'develop' into fix-pick-list 2023-01-31 12:38:58 +05:30
s-aga-r
0b76a26c8a refactor: test_consider_existing_pick_list() 2023-01-31 12:38:30 +05:30
s-aga-r
bb7fe795fe test: add test cases 2023-01-31 12:38:30 +05:30
s-aga-r
207eeefc85 fix(test): test_pick_list_for_items_with_multiple_UOM() 2023-01-31 12:38:30 +05:30
s-aga-r
5138ef0160 fix: pymysql.err.ProgrammingError 2023-01-31 12:38:30 +05:30
s-aga-r
7b3d496ce0 fix: get_picked_items_details 2023-01-31 12:38:30 +05:30
s-aga-r
be41052dc8 chore: add status field in Pick List 2023-01-31 12:38:30 +05:30
s-aga-r
b642718f08 fix: consider existing pick list 2023-01-31 12:38:27 +05:30
Deepesh Garg
b3dc22de29 fix: Fetch commission rate from sales partner (#33851) 2023-01-31 09:33:33 +05:30
Anand Baburajan
e007776ada Merge pull request #33888 from barredterra/get-asset-value
fix(Fixed Asset Register): error when selecting more than one fiscal …
2023-01-31 01:59:53 +05:30
barredterra
11d165c274 fix(Fixed Asset Register): error when selecting more than one fiscal year 2023-01-30 19:33:58 +01:00
Anand Baburajan
3e5f8323b0 Merge pull request #33883 from AnandBaburajan/fix_disposal_was_made_on_original_schedule_date_develop
fix: disposal_was_made_on_original_schedule_date [develop]
2023-01-30 22:02:34 +05:30
Anand Baburajan
5d8a2d3d3b Merge branch 'develop' into fix_disposal_was_made_on_original_schedule_date_develop 2023-01-30 21:36:51 +05:30
anandbaburajan
f487660a7c fix: disposal_was_made_on_original_schedule_date 2023-01-30 21:33:43 +05:30
Deepesh Garg
2bad86d8d8 ci: bump isort to 5.12.0 (#33875)
[skip ci]
2023-01-30 19:58:23 +05:30
ruthra kumar
5f38859a04 Merge pull request #33736 from rtdany10/gp-report-fix
fix(gp): fetch buying amount from dn related to so
2023-01-30 19:04:36 +05:30
marination
fa9b327501 chore: Validate 'alternative_to' field values, must be a valid non-alterntaive item from table 2023-01-30 16:27:01 +05:30
Dany Robert
d69c839369 chore: linting issues 2023-01-30 10:45:08 +00:00
Dany Robert
1f6ab86a65 feat(gp): test for inv and dn related via so 2023-01-30 10:35:43 +00:00
marination
94cacb60de feat: Filter rows to be mapped on server side mapping function
- Pass dialog selections to `make_sales_order`
- Map either original item or its alternative depending on mapping
- Only qty check for simple rows (without alternatives and not an alternative itself)
2023-01-30 13:54:30 +05:30
s-aga-r
6b781d78e0 fix: amount in Material Request 2023-01-30 12:51:59 +05:30
Deepesh Garg
a34a1f8fd2 fix: Amount validation in Payment Request against Purchase Order (#33855)
fix: Amount validation in Payment Request againt Purchase Order
2023-01-29 21:34:28 +05:30
Deepesh Garg
faecf3ee40 fix: Lead to customer creation (#33859) 2023-01-29 21:25:49 +05:30
Deepesh Garg
6674edface chore: Resize numeric and date columns (#33858) 2023-01-29 21:25:26 +05:30
Deepesh Garg
428b099f63 fix: Ignore linked JE on JE cancellation (#33852) 2023-01-29 17:27:17 +05:30
Deepesh Garg
8abe0ce0ec fix: Currency symbol for tax withholding net total field (#33850)
* fix: Currency symbol for tax withholding net total field

* chore: Update display depends on property
2023-01-29 17:08:11 +05:30
ruthra kumar
fee0ca8cd9 perf: reduce memory usage by paging through records
While migrating GL entries to Payment Ledger, page through records using
primary key to reduce memory usage.
2023-01-29 15:27:01 +05:30
ruthra kumar
f270880735 fix: double salutation on quotation print (#33834)
'lead_name' always has salutation.
2023-01-28 18:17:25 +05:30
rohitwaghchaure
1b967f37af Merge pull request #33845 from rohitwaghchaure/fixed-item-not-fetching-issue
fix: item rate not fetching
2023-01-28 15:10:02 +05:30
Rohit Waghchaure
0d7f98b496 fix: item rate not fetching 2023-01-28 13:51:33 +05:30
Sabu Siyad
9e50aa4833 feat(pos): multiple item prices (#33005)
* fix(pos): multiple item prices

feat: show uom with product price
feat: multiple item (variant) depending on uom

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* feat(pos): consider uom for new item

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* feat(pos): uom based stock display

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* use `//` instead of `math.floor()`

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* feat(pos): uom by barcode

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* fix: replace `is not` with `!=`

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* fix(pos): barcode_info `next()`: fallback

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* chore: format

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* Update erpnext/selling/page/point_of_sale/point_of_sale.py

Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>

* chore: un-ignore unused local variable

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

---------

Signed-off-by: Sabu Siyad <hello@ssiyad.com>
Co-authored-by: Raffael Meyer <14891507+barredterra@users.noreply.github.com>
Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-01-28 10:28:31 +05:30
Deepesh Garg
3598bcc9a8 chore: ERPNext setup wizard cleanup (#33675)
* chore: ERPNext setup wizard cleanup

* chore: Remove default website

* chore: Remove flaky tests

* chore: remove unwanted tests
2023-01-27 14:40:39 +05:30
ruthra kumar
9dd6b3c950 Merge pull request #33835 from ruthra-kumar/skip_validation_on_migration
fix(patch): validation error on cost center allocation migration
2023-01-27 13:40:57 +05:30
ruthra kumar
de10f2dc00 fix(patch): validation error on cost center allocation migration
If Distributed cost centers have GL postings on patch run date,
patch failes with valiation error.
2023-01-27 12:08:18 +05:30
Sagar Vora
7e592fdf91 Merge pull request #33831 from dj12djdjs/perf-update-items
perf: show update items dialog
2023-01-27 04:17:39 +00:00
Devin Slauenwhite
a835c1a418 perf: show update items dialog 2023-01-26 16:53:05 -05:00
ruthra kumar
b31b850db8 fix: disfuctional cost center filter on Journal Entries (#33815)
* fix: missing cost_center filter for journal entries

* test: cost center filter on invoices, journals and payments
2023-01-26 20:26:21 +05:30
Vishal Dhayagude
c5ca8d74c4 fix: GST Category validation broken for pos unregistered customer who dont have address. (#33800)
* fix: GST Category validation is given for pos customer
2023-01-26 15:45:51 +05:30
marination
cef7dfd0b4 feat: Dialog to select alternative item before creating Sales order
- Users can leave the row blank in the dialog if original item is to be used
- Else users can select an alternative item against an original item
- In the document, users must check `Is Alternative Item` if needed and also specify which item it is an altenrative to since there are no documented mappings
2023-01-26 14:36:25 +05:30
Anand Baburajan
e3d7426b47 Merge pull request #33823 from AnandBaburajan/fix_translations
chore: remove broken translation
2023-01-26 13:46:16 +05:30
anandbaburajan
21f425660d chore: remove broken translation 2023-01-26 13:15:01 +05:30
Ankush Menat
33b68552f1 chore: remove broken translation
[skip ci]
2023-01-25 17:35:23 +05:30
Daizy Modi
da323cbb40 fix: use correct filter name in item_query (#33814) 2023-01-25 17:32:21 +05:30
Sagar Vora
41222659a6 Merge pull request #33813 from frappe/revert-33810-fix-quality-inspection-item-query
Revert "fix: use correct filter name in reports"
2023-01-25 10:50:03 +00:00
Sagar Vora
1f4fa90072 Revert "fix: use correct filter name in reports" 2023-01-25 16:19:44 +05:30
Sagar Sharma
58dab9ca8b Merge pull request #33810 from resilient-tech/fix-quality-inspection-item-query
fix: use correct filter name in reports
2023-01-25 16:15:04 +05:30
Daizy Modi
def759d2e8 fix: use correct filter name in reports 2023-01-25 15:57:39 +05:30
marination
f19eadab9a feat: Consider filtered items table in JS for totals computation
- Set `_items` as filtered rows if quotation else the entire table. Set at entry point of JS API
- Use `_items` instead of `items` to compute taxes and charges. Exclude alternative item rows
2023-01-25 13:10:03 +05:30
Devin Slauenwhite
a504b6eac0 fix: make title field readonly 2023-01-24 18:21:08 -05:00
Rohit Waghchaure
92d857d49c fix: purchase invoice performance issue 2023-01-24 22:32:07 +05:30
marination
91982d1e4f feat: Filter out alternative item rows in taxes and totals for Quotation
- Added a Quotation Item field `is_alternative_item`
- Use filtered rows for taxes and totals computation
2023-01-24 18:03:53 +05:30
Sagar Sharma
a067a904a1 Merge pull request #33797 from s-aga-r/fix-sco-status
chore: add `Cancelled` status in SCO `get_indicator`
2023-01-24 17:33:57 +05:30
Sagar Sharma
302f5e27be Merge branch 'develop' into fix-sco-status 2023-01-24 17:33:29 +05:30
s-aga-r
840f9d8987 chore: add Cancelled status in SCO get_indicator 2023-01-24 17:30:37 +05:30
rohitwaghchaure
a5478c61af Merge pull request #33790 from rohitwaghchaure/fix-web-supplier-quotation-from-rfq
fix: web supplier quotation
2023-01-24 15:59:04 +05:30
Raffael Meyer
d155042edd ci: documentation helper (#33757)
refactor: documentation helper
2023-01-24 11:42:44 +05:30
Anand Baburajan
84383087a6 Merge pull request #33789 from AnandBaburajan/update_translations_for_naming_series_error
chore: update translations for naming series error
2023-01-23 18:48:10 +05:30
anandbaburajan
4e6066f929 chore: fix translations 2023-01-23 18:07:32 +05:30
Rohit Waghchaure
7800db7c0d fix: web supplier quotation 2023-01-23 14:21:10 +05:30
Anand Baburajan
7d5df407c4 Merge branch 'develop' into update_translations_for_naming_series_error 2023-01-23 14:19:39 +05:30
anandbaburajan
bbde1f611f chore: update translations for naming series error 2023-01-23 14:16:31 +05:30
s-aga-r
9ae3a54ce9 chore: add method get_picked_items_details() 2023-01-23 13:48:26 +05:30
rohitwaghchaure
b3a216c8f8 Merge pull request #33785 from rohitwaghchaure/get-items-from-transit-entry
feat: get items from Transit Stock Entry
2023-01-23 13:03:53 +05:30
Rohit Waghchaure
62141b0b63 feat: get items from Transit Stock Entry 2023-01-23 12:01:57 +05:30
Deepesh Garg
a3c89dbc10 fix: TDS deduction in payment entry (#33747)
* fix: TDS deduction in payment entry
2023-01-22 23:31:19 +05:30
Xeanth
ad437fd5ba fix: use stock qty to calculate POS reserved stock (#33735) 2023-01-22 18:06:01 +05:30
Sagar Sharma
7a8e89e2fe fix: incorrect rate and amount in MR Item (#33547)
* fix: incorrect `rate` and `amount` in MR Item
2023-01-22 18:04:13 +05:30
Sagar Sharma
72be489b1b Merge pull request #33778 from barredterra/fix-missing-const
fix: missing constant definition
2023-01-21 22:28:49 +05:30
barredterra
547d37b1db fix: missing constant definition 2023-01-21 17:29:43 +01:00
Deepesh Garg
2f03dcb5b6 fix: Better budget exceeding validation messages (#33713)
* fix: Better budget exceeding validation messages

* chore: remove unwanted changes
2023-01-21 19:33:14 +05:30
s-aga-r
57c3216683 refactor: rewrite get_available_item_locations_for_serial_and_batched_item query in QB 2023-01-21 14:05:51 +05:30
s-aga-r
5b76e8b193 refactor: rewrite get_available_item_locations_for_serialized_item query in QB 2023-01-21 12:45:39 +05:30
s-aga-r
58dd40a2d7 refactor: rewrite get_available_item_locations_for_other_item query in QB 2023-01-21 12:15:45 +05:30
rohitwaghchaure
f6f64083d2 Merge pull request #33760 from rohitwaghchaure/allow-to-change-bom-in-sco
fix: not able to change default BOM in the Subcontracting Order
2023-01-21 12:04:50 +05:30
s-aga-r
29bf787313 refactor: rewrite get_picked_items_qty query in QB 2023-01-21 11:28:23 +05:30
ruthra kumar
4cbd63edbf Merge pull request #33710 from openrefactory/patch-2
Removed an unnecessary check in code which always evaluates to true
2023-01-21 09:41:04 +05:30
OpenRefactory, Inc
49aed7ff69 fix: removed an unnecessary check which always evaluates to true 2023-01-21 06:15:51 +05:30
Kevin Shenk
e4bceaaf66 feat: Copy project_name, from_time, to_time from timesheet details to sales invoice (#33726)
feat: Copy project_name, from_time, to_time from timesheet details to sales invoice
2023-01-20 23:39:37 +05:30
rohitwaghchaure
b5d2eedfa6 Merge pull request #33759 from rohitwaghchaure/fix-incorrect-actual-qty-for-packed-item
fix: incorrect actual qty for the packed item
2023-01-20 23:38:55 +05:30
Rohit Waghchaure
11b2994fe8 fix: not able to change default BOM in the Subcontracting Order 2023-01-20 23:27:49 +05:30
Rohit Waghchaure
02566a02a8 fix: incorrect actual qty for the packed item 2023-01-20 22:38:56 +05:30
rohitwaghchaure
d462d6de06 Merge pull request #33595 from vishdha/fg_based_operating_cost
feat: Add operating cost based on bom quanity without creating job card
2023-01-20 22:36:42 +05:30
Dany Robert
1e607cd4c7 fix(pricing rule): free item duplication (#33746)
* fix(pricing rule): free item duplication

* chore: linting issues
2023-01-20 20:07:37 +05:30
Deepesh Garg
597ef0a498 fix: Short closed order, receipt and delivery note status on cancellation (#33743)
* fix: Short closed order, receipt and delivery note status on cancellation

* test: Cancelled status check
2023-01-20 19:44:06 +05:30
Raffael Meyer
327b6fdb32 fix: calculate correct amount for qty == 0 (#33739) 2023-01-20 19:35:27 +05:30
Sabu Siyad
a94aa7a79f fix(ecommerce): breadcrumb: fallback to /all-products (#33718)
* fix(ecommerce): breadcrumb: fallback to `/all-products`

* fix(item_group): use `==` instead of `is`

* test(ecommerce): breadcrumb
2023-01-20 18:54:54 +05:30
Ritwik Puri
17045f88a1 fix: use hash based naming for tax withheld vouchers child table (#33643) 2023-01-20 18:53:39 +05:30
Dany Robert
ef90e24931 chore: linting issue 2023-01-19 10:25:05 +00:00
Dany Robert
e8e20da78e fix(gp): fetch buying amount from dn related to so 2023-01-19 09:39:43 +00:00
Sagar Sharma
cceb7922a5 Merge pull request #33619 from SvbZ3r0/item-attr-abbr-lowercase
fix: rewrite logic for duplicate check in Item Attribute
2023-01-19 13:27:43 +05:30
Sagar Sharma
333b2ae721 Merge branch 'develop' into item-attr-abbr-lowercase 2023-01-19 12:55:49 +05:30
rohitwaghchaure
ecfebfec57 Merge pull request #33723 from rohitwaghchaure/fixed-issue-of-item-variant
fix: don't add template item in sales/purchase transaction
2023-01-19 12:42:04 +05:30
Rohit Waghchaure
2c83fff1a1 fix: don't add template item in sales/purchase transaction 2023-01-18 23:29:06 +05:30
mergify[bot]
cc569c955f fix: the frappe throw message is corrected in the group task validation (#33698)
fix: the frappe throw message is corrected in the group task validation (#33698)
2023-01-18 21:47:22 +05:30
Sagar Sharma
60ec7b6cde Merge pull request #33679 from s-aga-r/refactor/qb/pick-list
refactor: rewrite `pick_list.py` queries in `QB`
2023-01-18 18:26:08 +05:30
Sagar Sharma
f89600aee8 Merge branch 'develop' into refactor/qb/pick-list 2023-01-18 17:52:23 +05:30
rohitwaghchaure
e8e21ea4b4 Merge pull request #33712 from rohitwaghchaure/fixed-stock_rbnb-error
fix: local variable 'stock_rbnb' referenced before assignment
2023-01-18 16:36:20 +05:30
Rohit Waghchaure
1de4742ffb fix: local variable 'stock_rbnb' referenced before assignment 2023-01-18 15:29:39 +05:30
Sagar Sharma
eee3bd2554 Merge branch 'develop' into refactor/qb/pick-list 2023-01-18 13:01:59 +05:30
Deepesh Garg
6b31c27ed6 fix: Missing constructor args in Bank Reco Tool (#33705) 2023-01-17 21:03:39 +05:30
Vishal Dhayagude
c4e1f81ccb Merge branch 'develop' into fg_based_operating_cost 2023-01-17 19:20:37 +05:30
Anand Baburajan
d7522d40c0 Merge pull request #33682 from AnandBaburajan/misc_asset_fixes
fix: handle asset depr entries posting failure, improve asset depr schedules cancel notes and fix asset repair link [develop]
2023-01-17 16:23:13 +05:30
Anand Baburajan
c6cfacc108 Merge branch 'develop' into misc_asset_fixes 2023-01-17 15:09:37 +05:30
Deepesh Garg
db9beb3cdd fix: Rate from LDC in TDS reports (#33699) 2023-01-17 14:53:24 +05:30
Vishal Dhayagude
f19212b5c8 Merge branch 'develop' into fg_based_operating_cost 2023-01-17 13:02:25 +05:30
Anand Baburajan
d97127bff2 Merge branch 'develop' into misc_asset_fixes 2023-01-17 12:57:22 +05:30
Sagar Sharma
eeed3356ac Merge branch 'develop' into refactor/qb/pick-list 2023-01-17 12:25:16 +05:30
rohitwaghchaure
e68c4e2aa8 Merge pull request #33695 from rohitwaghchaure/fixed-patch-item_reposting_for_incorrect_sl_and_gl
fix: patch item_reposting_for_incorrect_sl_and_gl
2023-01-17 12:18:34 +05:30
Rohit Waghchaure
dbde3a3421 fix: patch item_reposting_for_incorrect_sl_and_gl 2023-01-17 11:47:53 +05:30
Anand Baburajan
c4d3a8a600 Merge branch 'develop' into misc_asset_fixes 2023-01-17 11:21:37 +05:30
Ankush Menat
64f7f3a12a chore: ignore b028
Very high false positive count. 

[skip ci]
2023-01-17 10:56:55 +05:30
Sagar Sharma
1cadef97bf Merge branch 'develop' into refactor/qb/pick-list 2023-01-17 10:08:36 +05:30
Sagar Sharma
e4cf0dced8 Merge pull request #33690 from FHenry/dev_fix_bug_from_78b438f6
fix: Sales Order Connections Tabs do not show linked Material Request or "+" button  (intoduce by #33304)
2023-01-17 10:05:49 +05:30
Sagar Sharma
fb950417bf Merge branch 'develop' into dev_fix_bug_from_78b438f6 2023-01-17 10:04:36 +05:30
Deepesh Garg
0639d9e32a chore: Typo in payment reconciliation (#33686) 2023-01-17 08:06:30 +05:30
Florian HENRY
e19161a8ee fix: Sales ORder Connections on Material Request 2023-01-16 21:03:18 +01:00
anandbaburajan
86cf5c89ab chore: add depr_entry_posting_status in create_asset 2023-01-17 00:31:21 +05:30
anandbaburajan
cebb5a42f4 chore: add missing /n in options 2023-01-17 00:26:50 +05:30
Anand Baburajan
011a4c3faa Merge branch 'develop' into misc_asset_fixes 2023-01-17 00:00:09 +05:30
rohitwaghchaure
66ae807a26 Merge pull request #33684 from rohitwaghchaure/fixed-work-order-summary
feat: [minor] date type based on filter in Work Order Summary report
2023-01-16 23:57:43 +05:30
rohitwaghchaure
d6915df81d Merge pull request #33680 from rohitwaghchaure/refactor-picked-qty
refactor: picked qty in sales order item
2023-01-16 23:31:56 +05:30
Rohit Waghchaure
20c8873208 feat: provision to select date type based on filter 2023-01-16 23:29:13 +05:30
anandbaburajan
a144bb01fe chore: stying 2023-01-16 23:26:39 +05:30
Anand Baburajan
070bc8badd Merge branch 'develop' into misc_asset_fixes 2023-01-16 23:23:12 +05:30
anandbaburajan
cf7b43f7f0 fix: handle_post_depr_entries_fail, show error alert and send email 2023-01-16 23:10:02 +05:30
Rohit Waghchaure
1bcff80074 refactor: picked qty in sales order item 2023-01-16 22:50:16 +05:30
Devin Slauenwhite
179a31ed5e feat: disable currency exchange api. (#33593) 2023-01-16 21:00:10 +05:30
anandbaburajan
61cd0f98fd fix: asset repair link and improve notes 2023-01-16 18:35:40 +05:30
Vishal Dhayagude
352dcddd6d Merge branch 'develop' into fg_based_operating_cost 2023-01-16 17:02:31 +05:30
Sagar Sharma
1769b45d4b Merge branch 'develop' into refactor/qb/pick-list 2023-01-16 16:04:20 +05:30
s-aga-r
0ed6552655 refactor: rewrite pick_list.py queries in QB 2023-01-16 16:01:40 +05:30
Deepesh Garg
adaeba1554 fix(minor): Label updates in Statement of Accounts (#33639)
fix(minor): Label updates in Satement of Accounts
2023-01-16 14:03:54 +05:30
Deepesh Garg
312625fdc5 fix: Patch to update reference_due_date in Journal Entry (#33616) 2023-01-16 13:43:54 +05:30
ruthra kumar
9627b46ee7 Merge pull request #33661 from ruthra-kumar/performance_tuning_payment_reconciliation
perf: improve reconciliation performance for JE with 100s of accounts
2023-01-16 09:41:17 +05:30
ruthra kumar
56aa1866da Merge pull request #33663 from ruthra-kumar/attribute_error_on_repost_ple_tool
fix: attribute error while submitting Repost PLE
2023-01-16 09:36:22 +05:30
Raffael Meyer
dceef0397a fix: allow to create sales order from expired quotation (#33582) 2023-01-16 08:53:37 +05:30
ruthra kumar
be382054e5 revert: Reverting changes done on 33495 (#33662)
'ordered_qty' will not be fetched from `tabBin`
2023-01-16 08:50:39 +05:30
ruthra kumar
333907b7a5 Revert "fix: Updating SO throws ordered_qty not allowed to change after submission" (#33646) 2023-01-16 08:49:35 +05:30
Poruri Sai Rahul
8119442c94 FIX: Remove usage of "six.string_types" (#33603)
FIX: Remove usage of six.string_types

six is no longer a dependency
2023-01-16 08:47:28 +05:30
Sagar Sharma
9069643504 Merge pull request #33664 from s-aga-r/so-ref-in-pick-list
chore: `Sales Order` link in `Pick List`
2023-01-15 23:04:53 +05:30
s-aga-r
b3759890d7 chore: Sales Order link in Pick List 2023-01-15 21:09:16 +05:30
ruthra kumar
2c50f43cdd fix: attribute error while submitting Repost PLE 2023-01-15 20:51:54 +05:30
ruthra kumar
828eaf0930 fix: minor filter issue while reconciliation tool from bench console 2023-01-15 18:09:51 +05:30
ruthra kumar
11cf694d9a perf: improve reconciliation speed on JE's with 1000's of rows
1. No need to keep old PLE's on reconciliation.
2. Added Validation to catch debit-credit mismatch on JE's
3. Only update outstanding amount for newly reconciled invoices
2023-01-15 17:51:46 +05:30
Raffael Meyer
67cf7e1728 refactor: use DocStatus (#33594) 2023-01-15 17:34:16 +05:30
Deepesh Garg
906ad10d16 fix: Return against internal purchase invoice (#33635) 2023-01-15 17:32:57 +05:30
Sagar Sharma
bfbc9ea8f7 Merge branch 'develop' into item-attr-abbr-lowercase 2023-01-15 12:55:01 +05:30
Sagar Sharma
242af681e9 Merge pull request #33651 from s-aga-r/fix/scr-rm-cost
fix: zero rm-cost in SCR
2023-01-14 23:11:12 +05:30
s-aga-r
f70d757b82 fix: zero rm-cost in SCR 2023-01-14 22:24:18 +05:30
Vishal Dhayagude
736afdf85a Merge branch 'develop' into fg_based_operating_cost 2023-01-13 10:03:50 +05:30
Sagar Sharma
c2b3843d80 Merge branch 'develop' into item-attr-abbr-lowercase 2023-01-13 00:49:21 +05:30
Ritwik Puri
e22d56484d chore: reuse doc object in test_pick_list_grouping_before_print (#33636) 2023-01-13 00:47:26 +05:30
Deepesh Garg
75e52d7108 Merge pull request #33608 from AnandBaburajan/fixed_asset_report_asset_value
fix: asset value in fixed asset register
2023-01-12 22:59:19 +05:30
Vishal Dhayagude
d63258ee9b Merge branch 'develop' into fg_based_operating_cost 2023-01-12 21:50:09 +05:30
unknown
2ca4d3fb71 fix: linting 2023-01-12 20:53:12 +05:30
Anand Baburajan
29088e1777 Merge branch 'develop' into fixed_asset_report_asset_value 2023-01-12 20:30:39 +05:30
Deepesh Garg
afb33f2049 Merge pull request #33271 from sonali8848/filters-on-bank-reconciliation
feat: Date filters on bank reconciliation tool
2023-01-12 18:25:05 +05:30
Deepesh Garg
232726288a chore: fix fieldnames and order 2023-01-12 17:44:07 +05:30
Ritwik Puri
cfb0bb1eaa fix: only group similar items in print format if group_same_items is checked in pick list (#33627)
* fix: only group similar items if group same items is checked in pick list

* test: non grouping of locations if group_same_items is false

Co-authored-by: Sagar Sharma <sagarsharma.s312@gmail.com>
2023-01-12 17:25:50 +05:30
ruthra kumar
e0db2670f9 Merge pull request #33622 from ruthra-kumar/fix_change_after_submission_on_error
fix: Updating SO throws ordered_qty not allowed to change after submission
2023-01-12 16:59:08 +05:30
Sagar Sharma
9ed7594f6e Merge pull request #33621 from s-aga-r/fix/validation/scr
chore: subcontracting validations
2023-01-12 15:49:30 +05:30
s-aga-r
b26e96cdf4 chore: linter 2023-01-12 13:41:53 +05:30
ruthra kumar
391f42db04 fix: Updating SO throws ordered_qty not allowed to change after submission 2023-01-12 13:18:03 +05:30
s-aga-r
f028bd6e69 fix: validate accepted and rejected qty in SCR Item 2023-01-12 12:23:40 +05:30
s-aga-r
a0e2a93f3f chore: update error msgs for Subcontracted PO 2023-01-12 12:09:28 +05:30
s-aga-r
6878f40d1d chore: add row-index in error msgs 2023-01-12 12:02:00 +05:30
s-aga-r
434aa594d5 fix: ZeroDivisionError: float division by zero in SCR 2023-01-12 11:49:41 +05:30
unknown
974e12c837 fix: rewrite logic for duplicate check in Item Attribute
Previously, Item Attribute values were not checked for case-insensitive duplicates, and Item tttribute abbreviations were forced to be uppercase. This commit fixes both problems.
2023-01-12 07:44:57 +05:30
Vishal Dhayagude
467a2b5cb4 Merge branch 'develop' into fg_based_operating_cost 2023-01-11 22:30:36 +05:30
Vishal
694fc3e20c fix: hide with_operation on selection on fg_based and vice versa 2023-01-11 18:52:30 +05:30
Sagar Sharma
9ab8aa49d6 Merge pull request #33611 from vorasmit/fix-diff-value
fix: better comparision of `difference_value` of Stock and Account
2023-01-11 14:52:49 +05:30
Sagar Sharma
887ae1c434 Merge branch 'develop' into fix-diff-value 2023-01-11 14:01:51 +05:30
Smit Vora
be05aea101 fix: better comparision of difference value between stock and account 2023-01-11 05:21:13 +00:00
anandbaburajan
aa1f2a7297 fix: asset value in fixed asset register 2023-01-10 23:05:09 +05:30
Smit Vora
e0f5ecdad6 fix: RFQ emails not sent with pdf attachment (#33604) 2023-01-10 20:40:12 +05:30
vr-greycube
0f0bc9a462 fix: customer selection not mandatory in purchase invoice to fetch item details (#33572) 2023-01-10 20:26:33 +05:30
Deepesh Garg
0ed938a490 fix: Incorrect exchange rate in payment entries (#33481)
* fix: Incorrect exchange rate in payment entries

* test: Update failing tests
2023-01-10 20:12:43 +05:30
rohitwaghchaure
58ad79d3e8 Merge pull request #33597 from rohitwaghchaure/do-not-check-other-warehouse
fix: don't check other warehouse ledgers to calculate valuation rate
2023-01-10 13:56:10 +05:30
Vishal
a5cbdea8e4 fix: minor change in bom.js added 2023-01-10 13:40:12 +05:30
Rohit Waghchaure
ef2bf3c223 fix: don't check other warehouse ledgers to calculate valuation rate 2023-01-10 10:59:50 +05:30
Ankush Menat
8a56df695d perf: Drop name part from posting sort index (#33551) 2023-01-10 09:54:15 +05:30
Vishal
c51f9e0a97 fix: bom.json updated 2023-01-10 07:48:26 +05:30
Vishal
f0c0a64984 fix: test case added for FG_BASED OPERTING COST 2023-01-10 07:43:28 +05:30
Vishal
ddc0127e05 fix: minor changes added 2023-01-10 07:43:20 +05:30
Vishal
b559245f2a feat: Add operating cost based on bom quanity without creating job card 2023-01-10 07:43:05 +05:30
rohitwaghchaure
59628f1934 Merge pull request #33579 from rtdany10/repack-fg-valuation
fix(stock entry): wrong valuation rate in repack
2023-01-10 00:05:56 +05:30
rohitwaghchaure
a424acbfcb Merge pull request #33577 from rohitwaghchaure/timeout-error-in-purchase-invoice
fix: Timeout error while saving the purchase invoice
2023-01-10 00:01:15 +05:30
rohitwaghchaure
461f8e7724 Merge pull request #33590 from frappe/revert-33347-fb_based_operating_cost
Revert "feat: Add operating cost based on bom quanity without creating job card"
2023-01-09 23:37:13 +05:30
rohitwaghchaure
d749c4aac2 Revert "feat: Add operating cost based on bom quanity without creating job card" 2023-01-09 23:35:11 +05:30
rohitwaghchaure
20fe1875e7 Merge pull request #33347 from vishdha/fb_based_operating_cost
feat: Add operating cost based on bom quanity without creating job card
2023-01-09 23:31:41 +05:30
Gughan Ravikumar
4d5067d6d4 fix(accounts): currency fields no longer read as strings by validation function in Payment Entry (#33535)
explicitly cast paid_amount and received_amount to float in the Payment Entry set_unallocated_amount validation function
2023-01-09 23:26:51 +05:30
ruthra kumar
bbe5e5d9d6 fix: incorrect warehouse and selling amount on bundled products (#33549) 2023-01-09 22:45:43 +05:30
Sagar Sharma
dd3b79b511 Merge pull request #33562 from ruthra-kumar/better_handling_of_duplicate_bundle_items
fix: better handling of duplicate bundle items
2023-01-09 22:22:57 +05:30
Devin Slauenwhite
6bc8bb26b6 fix: customer/supplier quick entry dialog (#33496)
* fix: readonly primary contact fields.

* refactor: supplier and customer quick entry form into common class.
2023-01-09 22:22:19 +05:30
Rohit Waghchaure
ec171fc7c1 test: test case to check disable last purchase rate 2023-01-09 22:02:35 +05:30
Sagar Sharma
b56c1ed050 Merge branch 'develop' into better_handling_of_duplicate_bundle_items 2023-01-09 21:48:48 +05:30
Deepesh Garg
f056e9e7c5 Merge pull request #33569 from ruthra-kumar/patch_to_update_new_je_type_in_property_setters
chore: patch property setters for JE with new entry type
2023-01-09 21:01:52 +05:30
Deepesh Garg
57a6c37833 Merge pull request #32933 from AnandBaburajan/asset_depreciation_schedule
feat: separating depreciation schedule from assets into a new doc
2023-01-09 20:47:21 +05:30
sonali
917b2190aa fix: remove comments 2023-01-09 20:07:24 +05:30
Ankush Menat
fa4af2acce refactor: convert heatmap queries to QB (#33581)
Uses new `UnixTimestamp` function, don't backport.
2023-01-09 20:00:22 +05:30
safvanhuzain
99f5e869e0 fix(stock entry): wrong valuation rate in repack 2023-01-09 13:11:37 +00:00
Rohit Waghchaure
05df8579cd fix: linters issue 2023-01-09 18:23:22 +05:30
Ankush Menat
7ee151880a chore: instructions for running forks
[skip ci]
2023-01-09 18:16:04 +05:30
Rohit Waghchaure
d1d4671320 feat: provision to disable get last purchase rate
fix: set_incoming_rate condition
2023-01-09 18:10:52 +05:30
Rohit Waghchaure
7249657d15 fix: timeout error in the Purchase Invoice 2023-01-09 14:53:54 +05:30
Raffael Meyer
a4cb0313dd chore: test with wkhtmltopdf 12.6 (#33554)
* chore: test with latest wkhtmltopdf

* chore: check ubuntu version

* ci: check exit code of wk install

Co-authored-by: Ankush Menat <ankush@frappe.io>
2023-01-09 14:27:34 +05:30
Ankush Menat
1ad1fc4c7d ci: bump node in release workflow (#33574)
[skip ci]
2023-01-09 13:25:05 +05:30
rohitwaghchaure
44f32f7521 Merge pull request #33568 from dj12djdjs/fix-manufacture-field-tab
fix: move `include_item_in_manufacturing` into Manufacturing Tab
2023-01-09 12:25:40 +05:30
ruthra kumar
789e448f0e chore: patch property setters for JE with new entry type 2023-01-08 11:45:28 +05:30
Devin Slauenwhite
e35ac4f8c2 fix: move include in manufacturing to manufacturing tab. 2023-01-07 22:47:16 -05:00
ruthra kumar
c717e87c9e fix: better handling of duplicate bundle items 2023-01-06 16:21:20 +05:30
rohitwaghchaure
525f054c45 Merge pull request #33548 from rohitwaghchaure/fixed-incorrect-wo-status
fix: incorrect status in the work order
2023-01-06 16:01:24 +05:30
Rohit Waghchaure
b0baba84a0 fix: incorrect status in the work order 2023-01-06 14:16:18 +05:30
ruthra kumar
51b082b43c Merge pull request #33537 from ruthra-kumar/sales_partner_in_ar_report
refactor: Sales Partner column in AR and AR Summary Report
2023-01-06 12:07:50 +05:30
Vishal
9fd1c3efb4 fix: test case added for FG_BASED OPERTING COST 2023-01-06 11:18:09 +05:30
Vishal
6b0b49ee3f fix: minor changes added 2023-01-06 11:18:09 +05:30
Vishal
bc1bdd58a7 feat: Add operating cost based on bom quanity without creating job card 2023-01-06 11:18:09 +05:30
Sagar Sharma
f4a1218467 Merge pull request #33543 from s-aga-r/fix/github-issue/25162
chore: enable `No Copy` attribute for `route` in Item Group
2023-01-05 13:01:53 +05:30
s-aga-r
348dc32514 chore: enable No Copy attribute for route in Item Group 2023-01-05 12:25:50 +05:30
rohitwaghchaure
f6da6ece0a Merge pull request #33524 from rohitwaghchaure/revamp-process-loss-feature
refactor: revamp process loss feature & added tab breaks
2023-01-04 18:37:32 +05:30
ruthra kumar
ee94127974 refactor: Sales Partner column in AR and AR Summary Report 2023-01-04 12:41:02 +05:30
Anand Baburajan
3ae47ab32c Merge branch 'develop' into asset_depreciation_schedule 2023-01-04 01:34:08 +05:30
Rohit Waghchaure
524c0994e0 test: test cases for process loss 2023-01-03 22:47:52 +05:30
Deepesh Garg
c78399c618 fix: Missing opening entry in general ledger (#33519) 2023-01-03 21:35:03 +05:30
Deepesh Garg
1a83a67d41 fix: Get payment entry button not visible in Bank Clearance doc (#33518) 2023-01-03 21:34:49 +05:30
Rohit Waghchaure
ae039777f9 refactor: revamp process loss feature & added tab breaks 2023-01-03 19:05:41 +05:30
Sagar Sharma
d3b74a868e Merge pull request #33514 from s-aga-r/fix/github-issue/33417
fix: set `supplier` details while mapping SE(Send to Subcontractor)
2023-01-03 18:57:07 +05:30
Sagar Sharma
9f59330fa9 Merge branch 'develop' into fix/github-issue/33417 2023-01-03 18:51:27 +05:30
Deepesh Garg
a3ab8f973a fix: Deferred revenue date comparison (#33515) 2023-01-03 17:51:41 +05:30
s-aga-r
751bdc98ed fix: set supplier details while mapping SE(Send to Subcontractor) 2023-01-03 17:17:23 +05:30
Deepesh Garg
9a3d947e89 fix: Exchange gain and loss booking on multi-currency invoice reconciliation (#32900)
* fix: Exchange gain and loss booking on multi-curreny invoice reconciliation

* test: Update test cases

* chore: Ignore SQL linting rule

* chore: Joural Entry for exchange gainand loss booking

* chore: Journal entry for exchange gain loss booking

* test: Update test case

* chore: Default exchange gain and loss account
2023-01-03 16:55:15 +05:30
Anand Baburajan
d9c91fa072 Merge branch 'develop' into asset_depreciation_schedule 2023-01-03 15:10:07 +05:30
anandbaburajan
312e51e8b2 chore: more refactoring and adding test for duplicate asset_depr_schedule 2023-01-03 15:09:04 +05:30
anandbaburajan
adb5249257 chore: fix patch 2023-01-03 13:03:12 +05:30
anandbaburajan
7b6b5ae581 chore: refactoring patch 2023-01-03 09:21:08 +05:30
anandbaburajan
e501c2db51 chore: just testing patch 2023-01-02 21:11:53 +05:30
anandbaburajan
357c17214d chore: fix patch 2023-01-02 20:32:29 +05:30
rohitwaghchaure
6a4e25c1f9 Merge pull request #33504 from rohitwaghchaure/fixed-incorrect-picked-qty-in-so
fix: [concurrency issue] incorrect picked qty in sales order
2023-01-02 20:12:05 +05:30
Rohit Waghchaure
aba83849a6 fix: [concurrency issue] incorrect picked qty in sales order 2023-01-02 18:46:22 +05:30
Anand Baburajan
72c2e77a5d Merge branch 'develop' into asset_depreciation_schedule 2023-01-02 17:07:58 +05:30
anandbaburajan
7c5168eeac chore: allow manually submitting and cancelling asset depr schedule 2023-01-02 17:07:28 +05:30
ruthra kumar
914b23038c refactor: Exchange rate revaluation to handle accounts with zero account balance (#33165)
* refactor: new type for JE - Exchange Gain or Loss

* refactor: skip few validations for Exchanage Gain Or Loss type Jour

* refactor: ERR create 2 journals for handling zero and non-zero compa

1. Additional check box accounts table to identify accounts with zero balance
2. Accounts with zero balance only in either of the 2 currencies will be handled on separate Journal

* refactor: skips few validation for allowing 0 debit/credit

* fix: General Ledger presentaion currency

* test: fix test case in general ledger

* test: fix failing test case in AR report
2023-01-02 14:33:14 +05:30
ruthra kumar
23fbe86d51 refactor: Show Balance in COA based on Accounts Settings 2023-01-02 13:32:01 +05:30
ruthra kumar
1b78fae6fc refactor: show balance checkbox in Accounts Settings 2023-01-02 13:31:56 +05:30
Sagar Sharma
a7b011aed4 Merge pull request #33444 from s-aga-r/fix/github-issue/28766
fix: consider child nodes while getting bin details
2023-01-02 11:54:11 +05:30
s-aga-r
b425c2d7ec Merge branch 'develop' into fix/github-issue/28766 2023-01-02 11:25:13 +05:30
Sagar Sharma
332f4dc028 Merge pull request #33495 from dj12djdjs/fix-get-bin-details
fix(stock): missing ordered_qty in get_bin_details
2023-01-02 11:14:25 +05:30
Sagar Sharma
e18b0ebe92 Merge branch 'develop' into fix-get-bin-details 2023-01-02 10:49:29 +05:30
Sagar Sharma
49025db33c Merge branch 'develop' into fix/github-issue/28766 2023-01-02 10:27:42 +05:30
Marc de Lima Lucio
3c393bfdc5 fix: javascript: execution blocked by undefined route options (#33405)
Some implementations of DocField.get_route_options_for_new_doc() returned no value instead of an empty object in some cases, which caused a JavaScript error.
2023-01-02 09:18:25 +05:30
MOHAMMED NIYAS
d054f37602 feat: calculate hours (#33464)
* feat: calculate hours

* chore: Linting Issues

Co-authored-by: Deepesh Garg <deepeshgarg6@gmail.com>
2023-01-02 09:17:14 +05:30
Sabu Siyad
010718ffed fix(ecommerce/cart): explicitly set frappe.boot (#33431)
related: https://github.com/frappe/frappe/pull/18323

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

Signed-off-by: Sabu Siyad <hello@ssiyad.com>
2023-01-02 08:58:01 +05:30
Devin Slauenwhite
98c39c4f5f fix: update payment entry references (#33274)
* fix: set_amounts after deductions and losses are set

* test: difference_amount changes after update_references_in_payment_entry

* chore: linter

* fix: use kwargs instad of destructing a dict

[skip ci]

* fix(test): test payment entry difference_amount after payment reconciliation.
2023-01-02 08:55:12 +05:30
Sagar Sharma
7126d71627 Merge branch 'develop' into fix/github-issue/28766 2023-01-01 22:24:47 +05:30
Devin Slauenwhite
239a5f8bf4 test: get_item_details contains bin details 2022-12-31 15:41:07 -05:00
Devin Slauenwhite
8d62cdfd5f fix: add missing 'ordered_qty' to get_bin_details 2022-12-31 14:14:25 -05:00
Deepesh Garg
bb4725e9b5 Merge pull request #33488 from deepeshgarg007/multi_currency_bank_reco
fix: Multi-currency issues in Bank Reconciliation Tool
2022-12-31 13:05:25 +05:30
sonali
12822f7c36 fix: data format 2022-12-30 14:06:19 +05:30
Deepesh Garg
ad53ecf2b4 fix: Multi-currency issues in Bank Recociliation Tool 2022-12-30 13:37:41 +05:30
Gughan Ravikumar
48a9cd5ef3 fix: allow arbitary items in Quotation ans Sales Order (#33430)
fix: allow arbitary items in Quotation ans Sales Order
2022-12-30 13:16:40 +05:30
rohitwaghchaure
6e1ef4f3d0 Merge pull request #33487 from frappe/revert-33387-audit-to-fix-incorrect-valuation-entry
Revert "fix: daily scheduler to identify and fix stock transfer entries having incorrect valuation"
2022-12-30 12:58:36 +05:30
rohitwaghchaure
728dc1acf4 Revert "fix: daily scheduler to identify and fix stock transfer entries having incorrect valuation" 2022-12-30 11:08:31 +05:30
Sagar Sharma
d072169340 Merge branch 'develop' into fix/github-issue/28766 2022-12-29 16:38:53 +05:30
s-aga-r
c3911a592a chore: use frappe.qb instead of frappe.db.get_value 2022-12-29 16:38:08 +05:30
sonali
d65243eb65 feat: consolidated auto bank reconciliation
Added a button of Auto Reconcile, to reconcile the bank entries as per the matching reference number with the bank transaction and count of transactions reconciled message will be pop up on clicking the auto reconcile button.
2022-12-29 16:12:25 +05:30
s-aga-r
c716dcc01e fix: consider child nodes while getting bin details 2022-12-29 14:01:42 +05:30
sonali
c764f14f53 fix: pre-commit 2022-12-29 13:58:06 +05:30
sonali
35c29e0226 fix: pre-commit 2022-12-29 13:45:15 +05:30
sonali
f1810803e1 fix: passing from_date and to_date filters in test cases
passing from_date and to_date filters in  test_linked_payments and test_debit_credit_output  for unit testing
2022-12-29 13:07:06 +05:30
sonali
e2614b8a21 fix: pre-commit 2022-12-29 12:21:45 +05:30
sonali
3aaa2f5326 fix: filtered as per reference date
On bank reconciliation, transactions will be filtered as per date selected in 'from_date' and 'to_date' fields , In dialog, all the bank entries will  be fetched as per the posting date selected and if filtered by reference date checkbox is tick then then there will be two fields 'from_reference_date' and 'to_reference_date' then all bank entries in dialog box came as per reference date, selected. And by default journal entry checkbox is tick.
Also sorted the bank transactions and bank entries as per ascending order date wise.
2022-12-29 12:05:22 +05:30
Deepesh Garg
617518389a fix: Conversion factor error for invoices without item code (petty expenses) (#32714)
* fix: Set default uom conversion factor to 1 for invoices

* chore: set default conversion_factor as 1

* chore: remove print statements
2022-12-29 10:41:36 +05:30
Devin Slauenwhite
123920d0bc feat: add after_refresh hook to item dashboard (#33372)
* fix: return promise

* fix: use after_refresh hook instead of promise

# Because there is already a before_refresh hook. I think it makes sense to do the same for after.
2022-12-29 10:31:26 +05:30
ruthra kumar
e3a0ce5d63 fix: use base_net_amount in case of missing stock qty (#33457) 2022-12-29 09:34:51 +05:30
Dany Robert
cabaed9ed2 fix(pricing rule): consider child tables in condition (#33469) 2022-12-28 18:05:55 +05:30
Sagar Sharma
882f92e732 Merge pull request #33465 from s-aga-r/fix/po-fg_item_qty
fix: `fg_item_qty` in non-subcontracted PO
2022-12-28 15:27:18 +05:30
s-aga-r
6f5824cb21 fix: fg_item_qty in non-subcontracted PO 2022-12-28 14:54:24 +05:30
Sabu Siyad
8e271fd719 feat(exotel): make use of CustomField in API (#33338)
* feat(exotel): pass kwargs for `make_a_call`

https://developer.exotel.com/api/make-a-call-api#call-agent

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

* feat(exotel): map custom field to doctype

Signed-off-by: Sabu Siyad <hello@ssiyad.com>

Signed-off-by: Sabu Siyad <hello@ssiyad.com>
2022-12-28 08:11:28 +05:30
Deepesh Garg
0b75aa5390 fix: Default dimensions on fetching items from BOM (#33439) 2022-12-27 17:53:43 +05:30
Deepesh Garg
d2686ce75b fix: Multiple rows for same warehouse and batches in pick list (#33456) 2022-12-27 17:42:03 +05:30
anandbaburajan
21a09e7431 chore: fix patch 2022-12-27 15:01:42 +05:30
anandbaburajan
97f8588202 chore: fix patch name 2022-12-27 15:01:42 +05:30
anandbaburajan
c916fb0513 chore: fix module name in patch 2022-12-27 15:01:42 +05:30
anandbaburajan
57adcc0b03 chore: move patch to pre_model_sync 2022-12-27 15:01:42 +05:30
Anand Baburajan
4ff70db668 Merge branch 'develop' into asset_depreciation_schedule 2022-12-27 14:59:53 +05:30
Deepesh Garg
8263bf9a9a fix: Random behaviour while picking items using picklist (#33449) 2022-12-27 14:59:35 +05:30
anandbaburajan
482116db9f chore: improve tests 2022-12-27 10:03:32 +05:30
Anand Baburajan
6f4e6fafa9 Merge branch 'develop' into asset_depreciation_schedule 2022-12-27 07:36:32 +05:30
anandbaburajan
6d791cabd1 chore: more refactoring 2022-12-26 22:13:11 +05:30
Sagar Sharma
4e2291f52d Merge pull request #33437 from s-aga-r/fix/purchase-order/shipping_address
fix: `shipping_address` in PO for non-drop ship item
2022-12-26 21:20:40 +05:30
Sagar Sharma
88edc412e3 Merge branch 'develop' into fix/purchase-order/shipping_address 2022-12-26 12:00:39 +05:30
s-aga-r
67a7ccf3ce fix: shipping_address for non-drop shipping item 2022-12-26 11:57:50 +05:30
Deepesh Garg
8a79efab4b feat: Accounting Dimension updation in Payment Request and Entry (#33411) 2022-12-26 10:17:55 +05:30
Solufyin
7d9f3f23dd fix: Customer Primary Contact (#33424)
Co-authored-by: Nihantra C. Patel <n.patel.serpentcs@gmail.com>
2022-12-26 10:15:10 +05:30
Sagar Sharma
9858d9d968 Merge pull request #33434 from s-aga-r/fix/github-issue/32258
fix(ux): `shipping_address` in PO
2022-12-25 19:40:09 +05:30
Sagar Sharma
32f3b64a89 Merge branch 'develop' into fix/github-issue/32258 2022-12-25 18:12:08 +05:30
s-aga-r
7e1b6b3c2a fix: shipping_address in PO 2022-12-25 18:11:05 +05:30
anandbaburajan
03662be150 chore: have different functions for draft. active and cancelled depr schedules 2022-12-25 18:05:19 +05:30
ruthra kumar
a31e9d3afe Merge pull request #33402 from ruthra-kumar/hidden_party_detail_fields_for_better_handling_of_permission
refactor: Customer and Supplier Ledger summary will have hidden fields for better handling of user permission
2022-12-25 16:25:13 +05:30
ruthra kumar
a0484793c3 refactor: remove unrelated filters from Supplier Ledger Summary
Territory, Sales Partner and Sales Person doesn't belong in Supplier Ledger
2022-12-24 11:20:39 +05:30
ruthra kumar
89229d9b58 refactor: additional doctype access for 'Accounts User' role 2022-12-24 11:20:16 +05:30
ruthra kumar
bfa511cb65 refactor: hidden columns to help framework handle user permissions
'territory', 'supplier_group', 'customer_group' have been added as
hidden columns to handle 'user permission' based access control.
2022-12-24 10:30:30 +05:30
rohitwaghchaure
40769998c6 Merge pull request #33422 from rohitwaghchaure/fix-stock-entry-submit-performance
fix: timeout error while submitting stock entry
2022-12-23 13:57:25 +05:30
Rohit Waghchaure
a05c47e499 fix: timeout error while submitting stock entry
Co-authored-by: Ankush Menat <ankush@frappe.io>
2022-12-23 13:27:32 +05:30
Anand Baburajan
b17ef34042 Merge branch 'develop' into asset_depreciation_schedule 2022-12-23 11:02:17 +05:30
anandbaburajan
3e91a56c19 chore: only consider draft and active assets in patch, and allow asset depr schedule to be manually createed 2022-12-23 11:01:18 +05:30
ruthra kumar
8f5d8e0474 Merge pull request #33418 from ruthra-kumar/filter_issue_in_ar_ap_report
fix: payment terms and sales partner filter issue in Accounts Receivable report
2022-12-22 11:38:38 +05:30
ruthra kumar
13c4420f42 fix: payment terms and sales partner filter issue in AR/AP report 2022-12-22 10:55:22 +05:30
ruthra kumar
a26698b88e Merge pull request #33410 from ruthra-kumar/typeerror_on_gp_due_to_incorrect_warehouse_on_bundle_item
fix: TypeError on GP Report due to multiple warehouse on bundled items
2022-12-21 18:26:40 +05:30
ruthra kumar
5918bb03f7 test: type error on bundled products with different warehouses 2022-12-21 17:47:57 +05:30
ruthra kumar
e684eb32d0 fix: typerror on multi warehouse in Packed Items
DN(with bundled item with varying warehouses)-> Sales Invoice.
2022-12-21 15:09:55 +05:30
sonali
81e5f71172 fix: json issue 2022-12-21 14:02:54 +05:30
sonali
6b5276398e fix: linters 2022-12-21 13:32:16 +05:30
sonali
645869e6ff feat: added arguments of posting date and reference date 2022-12-21 12:58:30 +05:30
Deepesh Garg
76be35aaea Merge pull request #33406 from deepeshgarg007/cs_cf_report_fix
fix: Consolidated financial report
2022-12-20 18:22:58 +05:30
Deepesh Garg
ade83cbab3 fix: Consolidated financial report 2022-12-20 16:45:46 +05:30
Deepesh Garg
1da14b7ec3 Merge pull request #33393 from deepeshgarg007/cc_filter_cashflow_report
fix: Cost center filter not working in cash flow report
2022-12-20 13:46:18 +05:30
Deepesh Garg
925387550e Merge pull request #33350 from deepeshgarg007/payment_request_flow
fix: Payment Request flow fixes from Order to Payment Entry
2022-12-20 13:45:15 +05:30
Deepesh Garg
9dc1509cf5 Merge pull request #33358 from resilient-tech/fix-appointment-booking
fix: remove unnecessary permissions from Appointment and Appointment Booking Settings
2022-12-20 11:53:01 +05:30
Deepesh Garg
31c95deb88 chore: More fixes 2022-12-20 11:45:51 +05:30
Deepesh Garg
ff48634cbb Merge branch 'develop' of https://github.com/frappe/erpnext into payment_request_flow 2022-12-20 11:42:15 +05:30
Deepesh Garg
e25b98b620 chore: Update test case 2022-12-20 11:42:05 +05:30
Deepesh Garg
068df9f815 chore: remove print statement 2022-12-20 10:00:53 +05:30
Deepesh Garg
d0dbfec052 fix: Cost center filter not working in cash flow report 2022-12-20 09:59:27 +05:30
rohitwaghchaure
1ed2230e4c Merge pull request #33382 from rohitwaghchaure/fixed-pick-list-issue
fix: unsupported operand type(s) for +=: 'int' and 'NoneType'
2022-12-20 09:47:29 +05:30
rohitwaghchaure
eaa04abe29 Merge pull request #33387 from rohitwaghchaure/audit-to-fix-incorrect-valuation-entry
fix: daily scheduler to identify and fix stock transfer entries having incorrect valuation
2022-12-20 09:46:57 +05:30
Rohit Waghchaure
f31612376a test: added test case to validate audit for incorrect entries 2022-12-20 00:14:41 +05:30
Rohit Waghchaure
b1721b79ce fix: daily scheduler to identify and fix stock transfer entries having incorrect valuation 2022-12-19 23:33:44 +05:30
rohitwaghchaure
7851adb49a Merge branch 'develop' into fixed-pick-list-issue 2022-12-19 23:32:06 +05:30
Ankush Menat
88ce11f03d fix: incorrect type hints (#33381)
[skip ci]
2022-12-19 16:44:19 +05:30
Rohit Waghchaure
2b4eae5f84 fix: unsupported operand type(s) for +=: 'int' and 'NoneType' 2022-12-19 16:24:55 +05:30
ruthra kumar
65db9cea25 Merge pull request #33380 from ruthra-kumar/err_for_invoices_should_reflect_in_ar_ap_report
fix: ERR journals should reported in AR/AP
2022-12-18 16:56:19 +05:30
ruthra kumar
2ed86760d7 test: err for party should be in AR/AP report 2022-12-18 12:40:07 +05:30
ruthra kumar
b09eade3e4 fix: ERR journals reported in AR/AP
Exchange Rate Revaluation on Receivable/Payable will included in AR/AP report
2022-12-18 06:38:23 +05:30
Gokulnath
4ecce242a8 feat: adding warehouse filter for sales order ananlysis report
feat: adding warehouse filter for sales order ananlysis report
2022-12-17 20:06:01 +05:30
Deepesh Garg
67c5cec505 Merge pull request #33339 from barredterra/print-rfq
feat: more control when printing RFQ
2022-12-17 19:24:57 +05:30
Deepesh Garg
6238fd9d9e Merge pull request #33341 from deepeshgarg007/tds_tcs_cost_center
fix: Cost Center for tax withholding invoices
2022-12-17 19:24:34 +05:30
sonali
05b6fce03d feat:filters on bank reconciliation
Added date filters on bank transactions, payment entries and journal entries and sorted list as per date in ascending order.
2022-12-17 17:15:28 +05:30
Deepesh Garg
2a4eec245a Merge pull request #33371 from deepeshgarg007/coa_import_srbnb
fix: Unable to import COA through importer
2022-12-17 16:42:37 +05:30
Deepesh Garg
3b66920342 fix: Unable to import COA through importer 2022-12-16 19:22:29 +05:30
Deepesh Garg
0e86cde64b Merge branch 'develop' into print-rfq 2022-12-16 16:24:02 +05:30
Sagar Vora
9989ccbb02 Merge branch 'develop' into fix-appointment-booking 2022-12-16 10:36:48 +00:00
Daizy Modi
4802d719ed fix: removed unused data and minor changes 2022-12-16 15:55:58 +05:30
Deepesh Garg
6bb22c703a Merge pull request #33275 from pps190/fix-pe-exchange-rate-precision
fix: use highest precision for exchange rate.
2022-12-16 15:43:36 +05:30
Deepesh Garg
50421b3d7d Merge pull request #33315 from kunhimohamed/develop
fix: at create_customer_or_supplier on session creation
2022-12-16 15:04:10 +05:30
Deepesh Garg
b76129c1dd Merge pull request #33286 from artykbasar/develop
Subscription Cost center value is fixed to Default value(Bug fix)
2022-12-16 14:44:51 +05:30
Deepesh Garg
5c50a46822 Merge pull request #33362 from deepeshgarg007/exclude_gst_credential_doctype
feat: Ignore company related doctype for other apps via hooks
2022-12-16 11:07:47 +05:30
Daizy Modi
5d0b5c8d2a fix: pass necessary params instead of args 2022-12-16 10:10:29 +05:30
Deepesh Garg
1a40c04b72 feat: Ignore company related doctype for other apps via hooks 2022-12-15 18:51:58 +05:30
sonali
408c89df03 Feat:Filter on Payment Entries and Journal Entries
Applying filters on Payement entries and Journal Entries as per reference  date and posting date
2022-12-15 18:09:57 +05:30
rohitwaghchaure
da1d8da355 Merge pull request #33355 from rohitwaghchaure/fixed-do-not-show-disabled-items
fix: disabled items showing in the report Itemwise Recommended Reorder Level
2022-12-15 18:08:05 +05:30
Daizy Modi
56f3ac15d8 fix: removed unncessary changes 2022-12-15 18:02:14 +05:30
Daizy Modi
4bfe2ea572 fix: agent assignment and permissions for appointment 2022-12-15 17:19:28 +05:30
rohitwaghchaure
5cbbb5d59e Merge pull request #33354 from rohitwaghchaure/fixed-trivial-discount-amount
fix: unsupported operand type(s) for +: 'int' and 'NoneType'
2022-12-15 17:13:46 +05:30
Rohit Waghchaure
ae31ff1c48 fix: disabled items showing in the report 'Itemwise Recommended Reorder Level
'
2022-12-15 17:05:53 +05:30
Daizy Modi
ac51c27500 fix: fetch required details from appointment booking settings 2022-12-15 16:53:51 +05:30
Daizy Modi
9a00b3bdbd fix: format html of verify email for book appointment 2022-12-15 16:51:00 +05:30
Daizy Modi
25f0b26a17 fix: remove unused page book-appointment 2022-12-15 16:49:23 +05:30
Daizy Modi
ae8dd2b2bc fix: remove guest role from doctype 2022-12-15 16:47:27 +05:30
Rohit Waghchaure
0f28074e5a fix: unsupported operand type(s) for +: 'int' and 'NoneType' 2022-12-15 16:36:47 +05:30
ruthra kumar
d126c8030f Merge pull request #33335 from ruthra-kumar/cost_center_issue_on_reconciliation_tool
fix: paid invoices shows up as outstanding when 'cost_center' filter is applied
2022-12-15 15:02:18 +05:30
Anand Baburajan
246d3b4075 Merge branch 'develop' into asset_depreciation_schedule 2022-12-15 14:51:16 +05:30
anandbaburajan
a358f77774 chore: move patch to post_model_sync and cancel asset depr schedule properly 2022-12-15 14:50:53 +05:30
ruthra kumar
a998a8a2da test: cost center should not affect outstanding calculation 2022-12-15 14:28:33 +05:30
Deepesh Garg
dc178984ae fix: Payment Request flow fixes from Order to Payment Entry 2022-12-15 14:15:20 +05:30
anandbaburajan
7f5e761c04 chore: add patch 2022-12-15 13:13:04 +05:30
Deepesh Garg
3a8a467edb Merge pull request #33329 from Gokulnath17/Gokulnath_dev
fix: in gross profit report
2022-12-15 12:01:39 +05:30
Deepesh Garg
076527bb88 Merge pull request #33305 from barredterra/incoterm-named-place
feat: incoterm named place
2022-12-15 11:59:54 +05:30
Gokulnath
f73976946e Merge branch 'develop' into Gokulnath_dev 2022-12-15 10:22:49 +05:30
Deepesh Garg
b7e9942681 Merge pull request #33309 from barredterra/refactor-validate_payment_against_negative_invoice
refactor: translatable strings and guard clause
2022-12-15 09:21:16 +05:30
rohitwaghchaure
9bf2682218 Merge pull request #33323 from barredterra/translate-overbilling-overdelivery
fix: translatability of warning on overbilling/-receipt/-delivery
2022-12-14 23:24:11 +05:30
Deepesh Garg
26277cfcf3 chore: resolve errors in test 2022-12-14 21:22:48 +05:30
Deepesh Garg
973ef33eb5 fix: Cost Center for tax withholding invoices 2022-12-14 20:41:00 +05:30
barredterra
8717148d9b feat: improve visibility of default values 2022-12-14 14:14:29 +01:00
barredterra
ce9626fead feat: more control when printing RFQ 2022-12-14 14:03:36 +01:00
Gokulnath
d404a03947 Merge branch 'develop' into Gokulnath_dev 2022-12-14 17:38:16 +05:30
ruthra kumar
8eb93004f7 fix: cost_center filter fix for 'Get Outstanding Invoice' in PE 2022-12-14 16:16:22 +05:30
rohitwaghchaure
21c86bd649 Merge pull request #33332 from rohitwaghchaure/fixed--missing-required-argument
fix: get_serial_nos_for_fg() missing 1 required positional argument: …
2022-12-14 16:06:58 +05:30
ruthra kumar
6d9d730759 fix: cost_center filter gives incorrect output
filtering on cost center gives invoices that are reconciled as having outstanding
2022-12-14 16:05:15 +05:30
Rohit Waghchaure
410a58b3de fix: get_serial_nos_for_fg() missing 1 required positional argument: 'args' 2022-12-14 15:33:36 +05:30
anandbaburajan
c99cd74a15 chore: handle some error cases and add empty patch file 2022-12-14 13:20:37 +05:30
Gokulnath17
41fc3be339 fixes in gross profit report 2022-12-14 10:46:11 +05:30
barredterra
36997d9788 fix: translation for warning on Overbilling/-receipt/-delivery 2022-12-13 18:59:20 +01:00
Anand Baburajan
8534390627 Merge branch 'develop' into asset_depreciation_schedule 2022-12-13 20:49:06 +05:30
anandbaburajan
22ef342ac7 chore: fixing all tests 2022-12-13 20:48:34 +05:30
anandbaburajan
df134c7c5b chore: fixing some tests 2022-12-13 16:26:33 +05:30
Nabin Hait
5ea6080a6e Merge pull request #33300 from ssiyad/fix/pos/var_typo
fix(pos): variable typo: `s_pos` -> `is_pos`
2022-12-13 15:21:10 +05:30
Nabin Hait
c5d21b2461 Merge pull request #33314 from s-aga-r/fix/github-issue/33145
fix: `Enough Parts to Build` in `BOM Stock Report`
2022-12-13 15:19:40 +05:30
ruthra kumar
3db446e35a Merge pull request #33313 from nabinhait/tax_detail_report_perm_issue
fix: Permission issue in Tax Detail report
2022-12-13 14:27:08 +05:30
kunhi
15e3b7f218 fix: at create_customer_or_supplier on session creation 2022-12-13 12:41:56 +04:00
Sagar Sharma
f9be137cf5 Merge branch 'develop' into fix/github-issue/33145 2022-12-13 14:00:51 +05:30
s-aga-r
723c64ba73 fix: Enough Parts to Build in BOM Stock Report 2022-12-13 14:00:30 +05:30
Nabin Hait
aa787e4030 fix: Permission issue in Tax Detail report 2022-12-13 13:16:24 +05:30
ruthra kumar
2175784142 Merge pull request #33303 from ruthra-kumar/incorrect_balance_on_consolidate_balance_sheet
fix: incorrect balance on parent company on consolidate Balance sheet due to key mismatch
2022-12-13 09:04:13 +05:30
barredterra
f20370c5ef refactor: translatable strings and guard clause 2022-12-13 02:36:12 +01:00
Sagar Sharma
cb49055aac Merge pull request #33304 from s-aga-r/fix/so-mr-ref
fix: `Material Request` reference in internal `Sales Order`
2022-12-12 23:48:14 +05:30
barredterra
e057e1dfe7 feat: incoterm named place 2022-12-12 18:49:47 +01:00
s-aga-r
78b438f6cf fix: Material Request reference in internal Sales Order 2022-12-12 22:41:44 +05:30
ruthra kumar
7b3316dc31 fix: incorrect balance on parent company due to key mismatch 2022-12-12 20:15:59 +05:30
rohitwaghchaure
5c0f8ee44f Merge pull request #33281 from tundebabzy/29976
fix: maintain same rate throughout sales cycle doesn't work with quotation
2022-12-12 19:30:50 +05:30
Sabu Siyad
7d64bf78cf fix(pos): variable typo: s_pos -> is_pos
Signed-off-by: Sabu Siyad <hello@ssiyad.com>
2022-12-12 19:09:00 +05:30
anandbaburajan
28d4942d6c chore: fix validation and add make_schedules_editable 2022-12-12 18:19:34 +05:30
anandbaburajan
1ea2ba0dea chore: handle some cases where asset_depr_schedule doesn't exist 2022-12-12 17:31:57 +05:30
anandbaburajan
b997d1eb87 chore: add some validation, shorten some function names 2022-12-12 17:25:38 +05:30
Anand Baburajan
f8cd424559 Merge branch 'develop' into asset_depreciation_schedule 2022-12-12 16:36:59 +05:30
anandbaburajan
ec6505d747 chore: fix some bugs, refactor some functions, add proper notes 2022-12-12 16:35:30 +05:30
Ankush Menat
915e0347b0 chore!: remove activity log feeds (#33294)
- This contains little to no information and practically no one uses this.
- Also causes a lot of problem by adding way too many feeds in activity
  log to the point where activity page doesn't even load.
2022-12-12 15:14:30 +05:30
Ankush Menat
593626f502 perf: add indexes on payment entry reference (#33288)
Adds index on:
1. reference doctype
2. reference name

*Why not composite index?*

There are three type of queries on this doctype

- filtering ref_doctype - doctype index helps here
- filtering ref_name - name index helps here
- filtering both - name index helps here too. Since it has sufficiently
  high cardinality. Composite index wont help in case where ref_doctype
  isn't specfied.


[skip ci]
2022-12-12 12:58:11 +05:30
artykbasar
d512919f5c Subscription Cost center value is fixed to Default value(Bug fix)
self.cost_center 
    field value keeps changing to 
erpnext.get_default_cost_center(self.get("company"))
    after validation of this doctype. This overrides the user input.
simple fix, it will first check if the field is empty, if empty then puts the company's default cost center value if not then user input will be saved.
2022-12-11 08:51:34 +00:00
Tunde Akinyanmi
71aa8c5e1c test: refactor test case 2022-12-09 14:33:54 +01:00
Tunde Akinyanmi
d193a14b8f test: ensure test case sets Selling Settings 2022-12-09 14:09:54 +01:00
anandbaburajan
90e1b9cb3d chore: fix asset depr schedule notes 2022-12-09 16:18:40 +05:30
Tunde Akinyanmi
97ddfcfc7c fix: Maintain Same Rate Throughout Sales Cycle doesn't work
Issue #29976 was partly fixed by #32923 but the problem still persists.
The reason is because an incorrect fieldname was passed to the
`validate_rate_with_reference_doc` method.
This commit fixes it
2022-12-09 10:29:28 +01:00
anandbaburajan
16365bfca3 chore: more refactoring 2022-12-09 14:04:00 +05:30
Devin Slauenwhite
2010b1b6e8 fix: use highest precision for exchange rate. 2022-12-08 16:26:07 -05:00
anandbaburajan
77dc8e7966 fix: bug in posting depr entries 2022-12-09 00:58:19 +05:30
anandbaburajan
187e1a324a chore: refactor get_depreciable_assets 2022-12-09 00:46:56 +05:30
anandbaburajan
1fd73af744 chore: refactor some reports 2022-12-09 00:28:34 +05:30
Deepesh Garg
7d41f63c5d Merge pull request #33200 from deepeshgarg007/buying_selling_pricing_rule
fix: Buying and selling check in pricing rule
2022-12-08 18:41:42 +05:30
Deepesh Garg
6db52930d8 Merge branch 'develop' of https://github.com/frappe/erpnext into buying_selling_pricing_rule 2022-12-08 18:05:05 +05:30
Deepesh Garg
6192af5cf0 chore: Update tests 2022-12-08 18:04:40 +05:30
sonali
8e7c8a6482 Update bank_reconciliation_tool.json
Adding fields in bank reconciliation tool
2022-12-08 17:23:08 +05:30
Deepesh Garg
b179514273 Merge pull request #33269 from ssiyad/fix/ecommerce/breadcrumb_base
fix(ecommerce): remove query parameters from referrer
2022-12-08 17:11:34 +05:30
Sabu Siyad
b6bd408f19 fix(ecommerce): remove query parameters from referer
inclusion of query parameters results in logic failure

example:
- logic check if referrer is `all-products`
- `http://shop.example/all-products` -> `all-products`, valid outcome
- `http://shop.example/all-products?start=1` -> `all-products?start=1`,
  invalid outcome

Signed-off-by: Sabu Siyad <hello@ssiyad.com>
2022-12-08 16:47:50 +05:30
Ankush Menat
0b86b1baca refactor: make payments app a soft dependency (#33245)
refactor: make payment app a soft dependency
2022-12-08 16:40:13 +05:30
sonali
447272aa4d Filters on Bank Reconciliation
Applying date filter on transactions and all the bank entries and also gives the filter the bank entries as per reference date. Sorted all transactions and entries as per date in ascending order. Also added posting date columns in all bank entries and default checkbox tick of journal entry, hide the sales invoice and purchase invoice checkbox.
2022-12-08 16:11:53 +05:30
Deepesh Garg
07c815f35f Merge pull request #33253 from niyazrazak/patch-9
feat: get lead with WhatsApp number
2022-12-08 15:19:13 +05:30
Deepesh Garg
81397ba3ee Merge pull request #33227 from barredterra/add-translation-variable-order
fix: add translation variable index
2022-12-08 15:17:07 +05:30
Deepesh Garg
f0487451f7 Merge pull request #33248 from QwQuan/develop
fix: bugs in zh.csv
2022-12-08 15:16:16 +05:30
sonali
e5a1189bec Update bank_reconciliation_tool.py
Applying date filter on transactions and all the bank entries and also gives the filter the bank entries as per reference date. Sorted all transactions and entries as per date in ascending order.
Also added posting date columns in all bank entries and default checkbox tick of journal entry, hide the sales invoice and purchase invoice checkbox.
2022-12-08 13:29:28 +05:30
ruthra kumar
1d37c0143f Merge pull request #33254 from ruthra-kumar/index_error_on_customer_master
fix: index error on customer master
2022-12-08 12:52:20 +05:30
rohitwaghchaure
e451639ff6 Merge pull request #33258 from rohitwaghchaure/fixed-total-value-in-report
fix: total value in Warehouse Wise Stock Balance
2022-12-08 10:13:03 +05:30
Rohit Waghchaure
f598da7c81 fix: total value in Warehouse Wise Stock Balance 2022-12-08 00:55:45 +05:30
rohitwaghchaure
5e0f4eb580 Merge pull request #33255 from s-aga-r/fix/github-issue/33212
fix: order status in `Production Planning Report`
2022-12-07 23:36:12 +05:30
s-aga-r
632c08f7e0 fix: order status in Production Planning Report 2022-12-07 22:47:06 +05:30
ruthra kumar
cf1e3dc8ea fix: index error on customer master 2022-12-07 20:48:40 +05:30
MOHAMMED NIYAS
922375c3ba feat: get lead with WhatsApp number 2022-12-07 17:50:35 +05:30
wuzhouquan
a6fe79f29d fix: bugs in zh.csv 2022-12-07 15:04:56 +08:00
anandbaburajan
18fc2b5695 chore: fix bug in unlink_asset_reference 2022-12-06 23:34:56 +05:30
anandbaburajan
2cd42dbb10 chore: refactor get_finance_book_value_map 2022-12-06 23:16:02 +05:30
anandbaburajan
e7d404a30a chore: remove finance_book and finance_book_id from depreciation_schedule and refactor some functions 2022-12-06 21:33:20 +05:30
anandbaburajan
8365d6bdb7 fix: refactor reverse_depreciation_entry_made_after_disposal and fix fb bug 2022-12-06 17:58:38 +05:30
rohitwaghchaure
7cbb6c4de9 Merge pull request #33236 from rohitwaghchaure/feat-warehouse-wise-stock-balance
feat: warehouse wise stock balance
2022-12-06 17:58:31 +05:30
Deepesh Garg
b602a0dcb0 Merge pull request #33146 from barredterra/reload-currency-exchange-settings
fix: reload Currency Exchange Settings in patch
2022-12-06 17:38:13 +05:30
Deepesh Garg
26adbc6282 Merge pull request #33192 from deepeshgarg007/bundle_item_rates
fix: Bundle item rates
2022-12-06 17:37:22 +05:30
Rohit Waghchaure
861aa9e08a feat: warehouse wise stock balance 2022-12-06 17:33:33 +05:30
Deepesh Garg
c1a82dc9e5 Merge pull request #33191 from ruthra-kumar/key_error_in_profit_loss_report
fix: key error while filtering on date range and reporting on foreign currency
2022-12-06 15:42:53 +05:30
Deepesh Garg
b1242bc56c chore: Update tests 2022-12-06 15:14:48 +05:30
Deepesh Garg
af1f98e188 Merge branch 'develop' of https://github.com/frappe/erpnext into bundle_item_rates 2022-12-06 13:59:51 +05:30
Deepesh Garg
e5566b31d5 chore: Consider bundle qty as well 2022-12-06 13:59:45 +05:30
ruthra kumar
a6794c3606 fix: key error on p/l and balance sheet reports on foreign currency 2022-12-06 13:48:46 +05:30
ruthra kumar
19db7e2989 fix: replace sql code with fields list in get_cached_value 2022-12-06 13:48:46 +05:30
Ankush Menat
d23b5d8f2f ci: use mariadb 10.6 (#33220)
https://github.com/frappe/frappe/pull/19116

[skip ci]
2022-12-06 12:58:07 +05:30
Deepesh Garg
01b84a9751 Merge pull request #33194 from barredterra/validate-employee-dates
refactor: validate dates in Employee
2022-12-06 12:34:15 +05:30
Deepesh Garg
3aa6f97420 Merge pull request #33216 from barredterra/validate-accounts-dates
refactor: validate dates in accounts module
2022-12-06 12:33:01 +05:30
Deepesh Garg
63393fa503 Merge pull request #33217 from barredterra/validate-project-dates
refactor: validate dates in project and task
2022-12-06 12:32:27 +05:30
Deepesh Garg
921f8edde8 Merge pull request #33219 from deepeshgarg007/internal_transfer_editable_rate
fix: Allow item rate updates for non-stock invoices
2022-12-06 09:12:24 +05:30
Raffael Meyer
5853b80d25 Merge branch 'develop' into reload-currency-exchange-settings 2022-12-06 02:30:55 +01:00
Raffael Meyer
8301d3b13f Merge branch 'develop' into key_error_in_profit_loss_report 2022-12-06 02:08:47 +01:00
barredterra
a26a29f33b fix: incorrect dates in test records 2022-12-05 19:49:05 +01:00
Raffael Meyer
a074ffa880 Merge branch 'develop' into validate-employee-dates 2022-12-05 19:38:01 +01:00
Raffael Meyer
e526a0e282 Merge branch 'develop' into validate-accounts-dates 2022-12-05 19:37:54 +01:00
Raffael Meyer
a2abc879c9 Merge branch 'develop' into validate-project-dates 2022-12-05 19:37:46 +01:00
barredterra
748c74ba52 fix: add translation variable order 2022-12-05 18:24:22 +01:00
rohitwaghchaure
d0478ec3b8 Merge pull request #33224 from rohitwaghchaure/fixed-partial-work-order-incorrect-batch-picked
fix: non empty FG batch picked while completing work order
2022-12-05 20:29:19 +05:30
Rohit Waghchaure
713330cbf6 fix: non empty FG batch picked while completing work order 2022-12-05 18:15:13 +05:30
Deepesh Garg
0581000990 fix: Remove free items 2022-12-05 17:59:02 +05:30
ruthra kumar
7bd0e977bf Merge pull request #33222 from ruthra-kumar/data_import_errors_for_sales_invoice
fix: data import mandatory account_head, charge_type
2022-12-05 16:56:43 +05:30
ruthra kumar
3814db02eb fix: data import mandatory account_head, charge_type 2022-12-05 16:24:05 +05:30
Deepesh Garg
55c0770280 Merge branch 'develop' of https://github.com/frappe/erpnext into buying_selling_pricing_rule 2022-12-05 15:43:29 +05:30
Deepesh Garg
5f821b93a5 chore: Add POS Invoices 2022-12-05 15:43:03 +05:30
Deepesh Garg
ef9d126254 fix: Allow item rate udpates for non-stock invoices 2022-12-05 10:17:19 +05:30
anandbaburajan
83ed93fbb6 chore: refactor chart and manual set_accumulated_depreciation logic 2022-12-04 22:45:48 +05:30
barredterra
31db0e7c79 refactor: validate parent_expected_end_date in Task 2022-12-04 15:28:38 +01:00
barredterra
2c4eb371a6 refactor: validate dates in project and task 2022-12-04 15:15:07 +01:00
anandbaburajan
ca8c827492 chore: refactor schedules in split_asset functions 2022-12-04 19:22:58 +05:30
barredterra
eb66b749b2 refactor: validate dates in accounts module 2022-12-04 14:41:21 +01:00
Raffael Meyer
083a954b5d Merge branch 'develop' into validate-employee-dates 2022-12-04 14:30:51 +01:00
Deepesh Garg
b741ae143c fix: Reapply pricing rule on qty change 2022-12-02 17:14:06 +05:30
Deepesh Garg
f54838ab56 fix: Buying and selling check in pricing rule 2022-12-01 21:23:17 +05:30
barredterra
03f7bfbbde refactor: validate dates 2022-12-01 12:42:03 +01:00
Deepesh Garg
826f45ad60 fix: Bundle item rates 2022-12-01 16:11:10 +05:30
anandbaburajan
f20238fa3e chore: use make_new_active_asset_depr_schedules_and_cancel_current_ones instead of prepare_depreciation_data 2022-12-01 15:15:32 +05:30
ruthra kumar
9b8d6fe411 fix: key error while filtering on date range and different currency 2022-12-01 13:38:41 +05:30
anandbaburajan
96ede2fcf9 fix: bug in new depr schedule 2022-11-29 21:30:23 +05:30
barredterra
06e094b5fc fix: reload currency exchange settings 2022-11-28 22:58:31 +01:00
anandbaburajan
7d09440579 chore: more refactoring 2022-11-25 16:49:44 +05:30
anandbaburajan
4e63ba3ac6 chore: refactor schedules in journal_entry 2022-11-24 17:39:11 +05:30
anandbaburajan
de17367a36 chore: finish refactoring asset_value_adjustment 2022-11-24 16:16:36 +05:30
anandbaburajan
f35b19eac3 chore: renaming functions and variables again, and continuing refactoring AssetValueAdjustment 2022-11-24 15:33:21 +05:30
anandbaburajan
7db66b0916 chore: renaming some functions and variables, and partially refactoring AssetValueAdjustment 2022-11-24 13:02:48 +05:30
anandbaburajan
76f28de7eb chore: refactor more functions to use new depr schedule 2022-11-23 18:04:54 +05:30
Anand Baburajan
a66d9f8e8e Merge branch 'develop' into asset_depreciation_schedule 2022-11-17 20:17:21 +05:30
anandbaburajan
417180e6ba chore: refactor some functions to use new depr schedule 2022-11-14 17:51:11 +05:30
anandbaburajan
ffd41703de chore: add expected_value_after_useful_life in depr schedule 2022-11-14 14:36:10 +05:30
anandbaburajan
ca3581d055 chore: rename modify_draft_asset_depreciation_schedules 2022-11-14 12:47:48 +05:30
anandbaburajan
ed495dc846 chore: add convert_draft_asset_depreciation_schedules_into_active and fix some bugs 2022-11-14 12:45:49 +05:30
anandbaburajan
97ab7bd36b chore: remove some unwanted functions in asset 2022-11-11 16:27:29 +05:30
anandbaburajan
710d2452b7 feat: moving, refactoring and adding functions to asset_depreciation_schedule 2022-11-11 16:20:17 +05:30
anandbaburajan
5a274176d3 feat: add asset_depreciation_schedule 2022-11-01 11:45:17 +05:30
Devin Slauenwhite
ef6480803b chore: linter 2022-06-22 14:10:40 -04:00
Devin Slauenwhite
199b8dd0e3 Merge branch 'develop' into fix-reserve-qty 2022-06-22 14:06:35 -04:00
Devin Slauenwhite
6a6c560375 fix: move to transcation settings 2022-06-17 15:51:11 -04:00
Devin Slauenwhite
179e2d2c74 fix: reset selling setting 2022-06-17 15:29:32 -04:00
Devin Slauenwhite
591b5917a9 fix: re-assign after append query 2022-06-17 15:27:17 -04:00
Devin Slauenwhite
0328874018 fix: syntax missing ) 2022-06-17 14:45:33 -04:00
Devin Slauenwhite
7855a59843 Merge branch 'develop' into fix-reserve-qty 2022-06-17 14:39:31 -04:00
Devin Slauenwhite
494bbf0124 feat: add checkbox to reserve qty on sales return 2022-06-17 13:51:33 -04:00
Devin Slauenwhite
f3f26d2c78 refactor: get_reserved_qty query builder 2022-06-17 11:51:21 -04:00
Devin Slauenwhite
d259c2e36a fix: merge conflict 2022-06-17 11:50:54 -04:00
Devin Slauenwhite
f39422afd8 Merge branch 'develop' into fix-reserve-qty
# Conflicts:
#	erpnext/patches.txt
#	erpnext/public/js/utils/barcode_scanner.js
#	erpnext/regional/report/gstr_1/gstr_1.py
#	erpnext/stock/doctype/delivery_note/test_delivery_note.py
2022-06-17 08:04:52 -04:00
Devin Slauenwhite
5a402a5709 fix(test): use unique item in sales order. 2022-05-17 13:32:26 -04:00
Devin Slauenwhite
4d0fa85a75 fix(test): update_prevdoc_status 2022-05-16 19:45:59 -04:00
Devin Slauenwhite
47f867b77a test: don't reserve qty on sales return. 2022-05-16 14:27:45 -04:00
Devin Slauenwhite
1d1c975da1 Merge remote-tracking branch 'upstream/develop' into fix-reserve-qty 2022-05-16 11:10:28 -04:00
Devin Slauenwhite
695e2bcfbc fix: don't reserve qty on sales return. 2022-05-14 11:06:24 -04:00
532 changed files with 19800 additions and 14412 deletions

View File

@@ -66,7 +66,8 @@ ignore =
F841,
E713,
E712,
B023
B023,
B028
max-line-length = 200

View File

@@ -3,52 +3,71 @@ import requests
from urllib.parse import urlparse
docs_repos = [
"frappe_docs",
"erpnext_documentation",
WEBSITE_REPOS = [
"erpnext_com",
"frappe_io",
]
DOCUMENTATION_DOMAINS = [
"docs.erpnext.com",
"frappeframework.com",
]
def uri_validator(x):
result = urlparse(x)
return all([result.scheme, result.netloc, result.path])
def docs_link_exists(body):
for line in body.splitlines():
for word in line.split():
if word.startswith('http') and uri_validator(word):
parsed_url = urlparse(word)
if parsed_url.netloc == "github.com":
parts = parsed_url.path.split('/')
if len(parts) == 5 and parts[1] == "frappe" and parts[2] in docs_repos:
return True
elif parsed_url.netloc == "docs.erpnext.com":
return True
def is_valid_url(url: str) -> bool:
parts = urlparse(url)
return all((parts.scheme, parts.netloc, parts.path))
def is_documentation_link(word: str) -> bool:
if not word.startswith("http") or not is_valid_url(word):
return False
parsed_url = urlparse(word)
if parsed_url.netloc in DOCUMENTATION_DOMAINS:
return True
if parsed_url.netloc == "github.com":
parts = parsed_url.path.split("/")
if len(parts) == 5 and parts[1] == "frappe" and parts[2] in WEBSITE_REPOS:
return True
return False
def contains_documentation_link(body: str) -> bool:
return any(
is_documentation_link(word)
for line in body.splitlines()
for word in line.split()
)
def check_pull_request(number: str) -> "tuple[int, str]":
response = requests.get(f"https://api.github.com/repos/frappe/erpnext/pulls/{number}")
if not response.ok:
return 1, "Pull Request Not Found! ⚠️"
payload = response.json()
title = (payload.get("title") or "").lower().strip()
head_sha = (payload.get("head") or {}).get("sha")
body = (payload.get("body") or "").lower()
if (
not title.startswith("feat")
or not head_sha
or "no-docs" in body
or "backport" in body
):
return 0, "Skipping documentation checks... 🏃"
if contains_documentation_link(body):
return 0, "Documentation Link Found. You're Awesome! 🎉"
return 1, "Documentation Link Not Found! ⚠️"
if __name__ == "__main__":
pr = sys.argv[1]
response = requests.get("https://api.github.com/repos/frappe/erpnext/pulls/{}".format(pr))
if response.ok:
payload = response.json()
title = (payload.get("title") or "").lower().strip()
head_sha = (payload.get("head") or {}).get("sha")
body = (payload.get("body") or "").lower()
if (title.startswith("feat")
and head_sha
and "no-docs" not in body
and "backport" not in body
):
if docs_link_exists(body):
print("Documentation Link Found. You're Awesome! 🎉")
else:
print("Documentation Link Not Found! ⚠️")
sys.exit(1)
else:
print("Skipping documentation checks... 🏃")
exit_code, message = check_pull_request(sys.argv[1])
print(message)
sys.exit(exit_code)

View File

@@ -8,8 +8,9 @@ sudo apt update && sudo apt install redis-server libcups2-dev
pip install frappe-bench
githubbranch=${GITHUB_BASE_REF:-${GITHUB_REF##*/}}
frappeuser=${FRAPPE_USER:-"frappe"}
frappebranch=${FRAPPE_BRANCH:-${GITHUB_BASE_REF:-${GITHUB_REF##*/}}}
frappebranch=${FRAPPE_BRANCH:-$githubbranch}
git clone "https://github.com/${frappeuser}/frappe" --branch "${frappebranch}" --depth 1
bench init --skip-assets --frappe-path ~/frappe --python "$(which python)" frappe-bench
@@ -24,15 +25,14 @@ fi
if [ "$DB" == "mariadb" ];then
mysql --host 127.0.0.1 --port 3306 -u root -e "SET GLOBAL character_set_server = 'utf8mb4'"
mysql --host 127.0.0.1 --port 3306 -u root -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'"
mysql --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL character_set_server = 'utf8mb4'"
mysql --host 127.0.0.1 --port 3306 -u root -proot -e "SET GLOBAL collation_server = 'utf8mb4_unicode_ci'"
mysql --host 127.0.0.1 --port 3306 -u root -e "CREATE USER 'test_frappe'@'localhost' IDENTIFIED BY 'test_frappe'"
mysql --host 127.0.0.1 --port 3306 -u root -e "CREATE DATABASE test_frappe"
mysql --host 127.0.0.1 --port 3306 -u root -e "GRANT ALL PRIVILEGES ON \`test_frappe\`.* TO 'test_frappe'@'localhost'"
mysql --host 127.0.0.1 --port 3306 -u root -proot -e "CREATE USER 'test_frappe'@'localhost' IDENTIFIED BY 'test_frappe'"
mysql --host 127.0.0.1 --port 3306 -u root -proot -e "CREATE DATABASE test_frappe"
mysql --host 127.0.0.1 --port 3306 -u root -proot -e "GRANT ALL PRIVILEGES ON \`test_frappe\`.* TO 'test_frappe'@'localhost'"
mysql --host 127.0.0.1 --port 3306 -u root -e "UPDATE mysql.user SET Password=PASSWORD('travis') WHERE User='root'"
mysql --host 127.0.0.1 --port 3306 -u root -e "FLUSH PRIVILEGES"
mysql --host 127.0.0.1 --port 3306 -u root -proot -e "FLUSH PRIVILEGES"
fi
if [ "$DB" == "postgres" ];then
@@ -42,12 +42,17 @@ fi
install_whktml() {
wget -O /tmp/wkhtmltox.tar.xz https://github.com/frappe/wkhtmltopdf/raw/master/wkhtmltox-0.12.3_linux-generic-amd64.tar.xz
tar -xf /tmp/wkhtmltox.tar.xz -C /tmp
sudo mv /tmp/wkhtmltox/bin/wkhtmltopdf /usr/local/bin/wkhtmltopdf
sudo chmod o+x /usr/local/bin/wkhtmltopdf
if [ "$(lsb_release -rs)" = "22.04" ]; then
wget -O /tmp/wkhtmltox.deb https://github.com/wkhtmltopdf/packaging/releases/download/0.12.6.1-2/wkhtmltox_0.12.6.1-2.jammy_amd64.deb
sudo apt install /tmp/wkhtmltox.deb
else
echo "Please update this script to support wkhtmltopdf for $(lsb_release -ds)"
exit 1
fi
}
install_whktml &
wkpid=$!
cd ~/frappe-bench || exit
@@ -56,11 +61,13 @@ sed -i 's/schedule:/# schedule:/g' Procfile
sed -i 's/socketio:/# socketio:/g' Procfile
sed -i 's/redis_socketio:/# redis_socketio:/g' Procfile
bench get-app payments
bench get-app payments --branch ${githubbranch%"-hotfix"}
bench get-app erpnext "${GITHUB_WORKSPACE}"
if [ "$TYPE" == "server" ]; then bench setup requirements --dev; fi
wait $wkpid
bench start &> bench_run_logs.txt &
CI=Yes bench build --app frappe &
bench --site test_site reinstall --yes

View File

@@ -9,8 +9,8 @@
"mail_password": "test",
"admin_password": "admin",
"root_login": "root",
"root_password": "travis",
"root_password": "root",
"host_name": "http://test_site:8000",
"install_apps": ["erpnext"],
"install_apps": ["payments", "erpnext"],
"throttle_user_limit": 100
}

View File

@@ -25,7 +25,7 @@ jobs:
mysql:
image: mariadb:10.3
env:
MYSQL_ALLOW_EMPTY_PASSWORD: YES
MARIADB_ROOT_PASSWORD: 'root'
ports:
- 3306:3306
options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3

View File

@@ -13,10 +13,10 @@ jobs:
with:
fetch-depth: 0
persist-credentials: false
- name: Setup Node.js v14
- name: Setup Node.js
uses: actions/setup-node@v2
with:
node-version: 14
node-version: 18
- name: Setup dependencies
run: |
npm install @semantic-release/git @semantic-release/exec --no-save
@@ -28,4 +28,4 @@ jobs:
GIT_AUTHOR_EMAIL: "developers@frappe.io"
GIT_COMMITTER_NAME: "Frappe PR Bot"
GIT_COMMITTER_EMAIL: "developers@frappe.io"
run: npx semantic-release
run: npx semantic-release

View File

@@ -7,7 +7,6 @@ on:
- '**.css'
- '**.md'
- '**.html'
- '**.csv'
push:
branches: [ develop ]
paths-ignore:
@@ -16,12 +15,12 @@ on:
workflow_dispatch:
inputs:
user:
description: 'user'
description: 'Frappe Framework repository user (add your username for forks)'
required: true
default: 'frappe'
type: string
branch:
description: 'Branch name'
description: 'Frappe Framework branch'
default: 'develop'
required: false
type: string
@@ -45,9 +44,9 @@ jobs:
services:
mysql:
image: mariadb:10.3
image: mariadb:10.6
env:
MYSQL_ALLOW_EMPTY_PASSWORD: YES
MARIADB_ROOT_PASSWORD: 'root'
ports:
- 3306:3306
options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3

View File

@@ -32,8 +32,8 @@ repos:
- id: black
additional_dependencies: ['click==8.0.4']
- repo: https://github.com/timothycrosley/isort
rev: 5.9.1
- repo: https://github.com/PyCQA/isort
rev: 5.12.0
hooks:
- id: isort
exclude: ".*setup.py$"

View File

@@ -3,26 +3,23 @@
# These owners will be the default owners for everything in
# the repo. Unless a later match takes precedence,
erpnext/accounts/ @nextchamp-saqib @deepeshgarg007 @ruthra-kumar
erpnext/assets/ @nextchamp-saqib @deepeshgarg007 @ruthra-kumar
erpnext/loan_management/ @nextchamp-saqib @deepeshgarg007
erpnext/regional @nextchamp-saqib @deepeshgarg007 @ruthra-kumar
erpnext/selling @nextchamp-saqib @deepeshgarg007 @ruthra-kumar
erpnext/support/ @nextchamp-saqib @deepeshgarg007
pos* @nextchamp-saqib
erpnext/accounts/ @deepeshgarg007 @ruthra-kumar
erpnext/assets/ @anandbaburajan @deepeshgarg007
erpnext/loan_management/ @deepeshgarg007
erpnext/regional @deepeshgarg007 @ruthra-kumar
erpnext/selling @deepeshgarg007 @ruthra-kumar
erpnext/support/ @deepeshgarg007
pos*
erpnext/buying/ @rohitwaghchaure @s-aga-r
erpnext/maintenance/ @rohitwaghchaure @s-aga-r
erpnext/manufacturing/ @rohitwaghchaure @s-aga-r
erpnext/quality_management/ @rohitwaghchaure @s-aga-r
erpnext/stock/ @rohitwaghchaure @s-aga-r
erpnext/subcontracting @rohitwaghchaure @s-aga-r
erpnext/crm/ @NagariaHussain
erpnext/education/ @rutwikhdev
erpnext/projects/ @ruchamahabal
erpnext/controllers/ @deepeshgarg007 @rohitwaghchaure
erpnext/patches/ @deepeshgarg007
erpnext/controllers/ @deepeshgarg007 @nextchamp-saqib @rohitwaghchaure
erpnext/patches/ @deepeshgarg007 @nextchamp-saqib
.github/ @ankush
.github/ @deepeshgarg007
pyproject.toml @ankush

View File

@@ -65,7 +65,7 @@ New passwords will be created for the ERPNext "Administrator" user, the MariaDB
1. [Frappe School](https://frappe.school) - Learn Frappe Framework and ERPNext from the various courses by the maintainers or from the community.
2. [Official documentation](https://docs.erpnext.com/) - Extensive documentation for ERPNext.
3. [Discussion Forum](https://discuss.erpnext.com/) - Engage with community of ERPNext users and service providers.
4. [Telegram Group](https://t.me/erpnexthelp) - Get instant help from huge community of users.
4. [Telegram Group](https://erpnext_public.t.me) - Get instant help from huge community of users.
## Contributing

View File

@@ -378,7 +378,7 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None):
return
# check if books nor frozen till endate:
if accounts_frozen_upto and (end_date) <= getdate(accounts_frozen_upto):
if accounts_frozen_upto and getdate(end_date) <= getdate(accounts_frozen_upto):
end_date = get_last_day(add_days(accounts_frozen_upto, 1))
if via_journal_entry:

View File

@@ -394,7 +394,13 @@ def update_account_number(name, account_name, account_number=None, from_descenda
if ancestors and not allow_independent_account_creation:
for ancestor in ancestors:
if frappe.db.get_value("Account", {"account_name": old_acc_name, "company": ancestor}, "name"):
old_name = frappe.db.get_value(
"Account",
{"account_number": old_acc_number, "account_name": old_acc_name, "company": ancestor},
"name",
)
if old_name:
# same account in parent company exists
allow_child_account_creation = _("Allow Account Creation Against Child Company")

View File

@@ -56,36 +56,41 @@ frappe.treeview_settings["Account"] = {
accounts = nodes;
}
const get_balances = frappe.call({
method: 'erpnext.accounts.utils.get_account_balances',
args: {
accounts: accounts,
company: cur_tree.args.company
},
});
frappe.db.get_single_value("Accounts Settings", "show_balance_in_coa").then((value) => {
if(value) {
get_balances.then(r => {
if (!r.message || r.message.length == 0) return;
const get_balances = frappe.call({
method: 'erpnext.accounts.utils.get_account_balances',
args: {
accounts: accounts,
company: cur_tree.args.company
},
});
for (let account of r.message) {
get_balances.then(r => {
if (!r.message || r.message.length == 0) return;
const node = cur_tree.nodes && cur_tree.nodes[account.value];
if (!node || node.is_root) continue;
for (let account of r.message) {
// show Dr if positive since balance is calculated as debit - credit else show Cr
const balance = account.balance_in_account_currency || account.balance;
const dr_or_cr = balance > 0 ? "Dr": "Cr";
const format = (value, currency) => format_currency(Math.abs(value), currency);
const node = cur_tree.nodes && cur_tree.nodes[account.value];
if (!node || node.is_root) continue;
if (account.balance!==undefined) {
node.parent && node.parent.find('.balance-area').remove();
$('<span class="balance-area pull-right">'
+ (account.balance_in_account_currency ?
(format(account.balance_in_account_currency, account.account_currency) + " / ") : "")
+ format(account.balance, account.company_currency)
+ " " + dr_or_cr
+ '</span>').insertBefore(node.$ul);
}
// show Dr if positive since balance is calculated as debit - credit else show Cr
const balance = account.balance_in_account_currency || account.balance;
const dr_or_cr = balance > 0 ? "Dr": "Cr";
const format = (value, currency) => format_currency(Math.abs(value), currency);
if (account.balance!==undefined) {
node.parent && node.parent.find('.balance-area').remove();
$('<span class="balance-area pull-right">'
+ (account.balance_in_account_currency ?
(format(account.balance_in_account_currency, account.account_currency) + " / ") : "")
+ format(account.balance, account.company_currency)
+ " " + dr_or_cr
+ '</span>').insertBefore(node.$ul);
}
}
});
}
});
},

View File

@@ -29,6 +29,7 @@ def create_charts(
"root_type",
"is_group",
"tax_rate",
"account_currency",
]:
account_number = cstr(child.get("account_number")).strip()
@@ -95,7 +96,17 @@ def identify_is_group(child):
is_group = child.get("is_group")
elif len(
set(child.keys())
- set(["account_name", "account_type", "root_type", "is_group", "tax_rate", "account_number"])
- set(
[
"account_name",
"account_type",
"root_type",
"is_group",
"tax_rate",
"account_number",
"account_currency",
]
)
):
is_group = 1
else:
@@ -185,6 +196,7 @@ def get_account_tree_from_existing_company(existing_company):
"root_type",
"tax_rate",
"account_number",
"account_currency",
],
order_by="lft, rgt",
)
@@ -267,6 +279,7 @@ def build_tree_from_json(chart_template, chart_data=None, from_coa_importer=Fals
"root_type",
"is_group",
"tax_rate",
"account_currency",
]:
continue

View File

@@ -1,38 +1,38 @@
{
"country_code": "de",
"name": "SKR03 mit Kontonummern",
"tree": {
"Aktiva": {
"is_group": 1,
"country_code": "de",
"name": "SKR03 mit Kontonummern",
"tree": {
"Aktiva": {
"is_group": 1,
"root_type": "Asset",
"A - Anlagevermögen": {
"is_group": 1,
"EDV-Software": {
"account_number": "0027",
"account_type": "Fixed Asset"
},
"Gesch\u00e4ftsausstattung": {
"account_number": "0410",
"account_type": "Fixed Asset"
},
"B\u00fcroeinrichtung": {
"account_number": "0420",
"account_type": "Fixed Asset"
},
"Darlehen": {
"account_number": "0565"
},
"Maschinen": {
"account_number": "0210",
"account_type": "Fixed Asset"
},
"Betriebsausstattung": {
"account_number": "0400",
"account_type": "Fixed Asset"
},
"Ladeneinrichtung": {
"account_number": "0430",
"account_type": "Fixed Asset"
"A - Anlagevermögen": {
"is_group": 1,
"EDV-Software": {
"account_number": "0027",
"account_type": "Fixed Asset"
},
"Geschäftsausstattung": {
"account_number": "0410",
"account_type": "Fixed Asset"
},
"Büroeinrichtung": {
"account_number": "0420",
"account_type": "Fixed Asset"
},
"Darlehen": {
"account_number": "0565"
},
"Maschinen": {
"account_number": "0210",
"account_type": "Fixed Asset"
},
"Betriebsausstattung": {
"account_number": "0400",
"account_type": "Fixed Asset"
},
"Ladeneinrichtung": {
"account_number": "0430",
"account_type": "Fixed Asset"
},
"Accumulated Depreciation": {
"account_type": "Accumulated Depreciation"
@@ -60,36 +60,46 @@
"Durchlaufende Posten": {
"account_number": "1590"
},
"Gewinnermittlung \u00a74/3 nicht Ergebniswirksam": {
"Verrechnungskonto Gewinnermittlung § 4 Abs. 3 EStG, nicht ergebniswirksam": {
"account_number": "1371"
},
"Abziehbare Vorsteuer": {
"account_type": "Tax",
"is_group": 1,
"Abziehbare Vorsteuer 7%": {
"account_number": "1571"
"Abziehbare Vorsteuer 7 %": {
"account_number": "1571",
"account_type": "Tax",
"tax_rate": 7.0
},
"Abziehbare Vorsteuer 19%": {
"account_number": "1576"
"Abziehbare Vorsteuer 19 %": {
"account_number": "1576",
"account_type": "Tax",
"tax_rate": 19.0
},
"Abziehbare Vorsteuer nach \u00a713b UStG 19%": {
"account_number": "1577"
},
"Leistungen \u00a713b UStG 19% Vorsteuer, 19% Umsatzsteuer": {
"account_number": "3120"
"Abziehbare Vorsteuer nach § 13b UStG 19 %": {
"account_number": "1577",
"account_type": "Tax",
"tax_rate": 19.0
}
}
},
"III. Wertpapiere": {
"is_group": 1
"is_group": 1,
"Anteile an verbundenen Unternehmen (Umlaufvermögen)": {
"account_number": "1340"
},
"Anteile an herrschender oder mit Mehrheit beteiligter Gesellschaft": {
"account_number": "1344"
},
"Sonstige Wertpapiere": {
"account_number": "1348"
}
},
"IV. Kassenbestand, Bundesbankguthaben, Guthaben bei Kreditinstituten und Schecks.": {
"is_group": 1,
"Kasse": {
"account_type": "Cash",
"is_group": 1,
"account_type": "Cash",
"Kasse": {
"is_group": 1,
"account_number": "1000",
"account_type": "Cash"
}
@@ -111,21 +121,21 @@
"C - Rechnungsabgrenzungsposten": {
"is_group": 1,
"Aktive Rechnungsabgrenzung": {
"account_number": "0980"
"account_number": "0980"
}
},
"D - Aktive latente Steuern": {
"is_group": 1,
"Aktive latente Steuern": {
"account_number": "0983"
"account_number": "0983"
}
},
"E - Aktiver Unterschiedsbetrag aus der Vermögensverrechnung": {
"is_group": 1
}
},
"Passiva": {
"is_group": 1,
},
"Passiva": {
"is_group": 1,
"root_type": "Liability",
"A. Eigenkapital": {
"is_group": 1,
@@ -200,26 +210,32 @@
},
"Umsatzsteuer": {
"is_group": 1,
"account_type": "Tax",
"Umsatzsteuer 7%": {
"account_number": "1771"
"Umsatzsteuer 7 %": {
"account_number": "1771",
"account_type": "Tax",
"tax_rate": 7.0
},
"Umsatzsteuer 19%": {
"account_number": "1776"
"Umsatzsteuer 19 %": {
"account_number": "1776",
"account_type": "Tax",
"tax_rate": 19.0
},
"Umsatzsteuer-Vorauszahlung": {
"account_number": "1780"
"account_number": "1780",
"account_type": "Tax"
},
"Umsatzsteuer-Vorauszahlung 1/11": {
"account_number": "1781"
},
"Umsatzsteuer \u00a7 13b UStG 19%": {
"account_number": "1787"
"Umsatzsteuer nach § 13b UStG 19 %": {
"account_number": "1787",
"account_type": "Tax",
"tax_rate": 19.0
},
"Umsatzsteuer Vorjahr": {
"account_number": "1790"
},
"Umsatzsteuer fr\u00fchere Jahre": {
"Umsatzsteuer frühere Jahre": {
"account_number": "1791"
}
}
@@ -234,44 +250,56 @@
"E. Passive latente Steuern": {
"is_group": 1
}
},
"Erl\u00f6se u. Ertr\u00e4ge 2/8": {
"is_group": 1,
"root_type": "Income",
"Erl\u00f6skonten 8": {
},
"Erlöse u. Erträge 2/8": {
"is_group": 1,
"root_type": "Income",
"Erlöskonten 8": {
"is_group": 1,
"Erl\u00f6se": {
"account_number": "8200",
"account_type": "Income Account"
},
"Erl\u00f6se USt. 19%": {
"account_number": "8400",
"account_type": "Income Account"
},
"Erl\u00f6se USt. 7%": {
"account_number": "8300",
"account_type": "Income Account"
}
},
"Ertragskonten 2": {
"is_group": 1,
"sonstige Zinsen und \u00e4hnliche Ertr\u00e4ge": {
"account_number": "2650",
"account_type": "Income Account"
},
"Au\u00dferordentliche Ertr\u00e4ge": {
"account_number": "2500",
"account_type": "Income Account"
},
"Sonstige Ertr\u00e4ge": {
"account_number": "2700",
"account_type": "Income Account"
}
}
},
"Aufwendungen 2/4": {
"is_group": 1,
"Erlöse": {
"account_number": "8200",
"account_type": "Income Account"
},
"Erlöse USt. 19 %": {
"account_number": "8400",
"account_type": "Income Account"
},
"Erlöse USt. 7 %": {
"account_number": "8300",
"account_type": "Income Account"
}
},
"Ertragskonten 2": {
"is_group": 1,
"sonstige Zinsen und ähnliche Erträge": {
"account_number": "2650",
"account_type": "Income Account"
},
"Außerordentliche Erträge": {
"account_number": "2500",
"account_type": "Income Account"
},
"Sonstige Erträge": {
"account_number": "2700",
"account_type": "Income Account"
}
}
},
"Aufwendungen 2/4": {
"is_group": 1,
"root_type": "Expense",
"Fremdleistungen": {
"account_number": "3100",
"account_type": "Expense Account"
},
"Fremdleistungen ohne Vorsteuer": {
"account_number": "3109",
"account_type": "Expense Account"
},
"Bauleistungen eines im Inland ansässigen Unternehmers 19 % Vorsteuer und 19 % Umsatzsteuer": {
"account_number": "3120",
"account_type": "Expense Account"
},
"Wareneingang": {
"account_number": "3200"
},
@@ -298,234 +326,234 @@
"Gegenkonto 4996-4998": {
"account_number": "4999"
},
"Abschreibungen": {
"is_group": 1,
"Abschreibungen": {
"is_group": 1,
"Abschreibungen auf Sachanlagen (ohne AfA auf Kfz und Gebäude)": {
"account_number": "4830",
"account_type": "Accumulated Depreciation"
"account_number": "4830",
"account_type": "Accumulated Depreciation"
},
"Abschreibungen auf Gebäude": {
"account_number": "4831",
"account_type": "Depreciation"
"account_number": "4831",
"account_type": "Depreciation"
},
"Abschreibungen auf Kfz": {
"account_number": "4832",
"account_type": "Depreciation"
"account_number": "4832",
"account_type": "Depreciation"
},
"Sofortabschreibung GWG": {
"account_number": "4855",
"account_type": "Expense Account"
"account_number": "4855",
"account_type": "Expense Account"
}
},
"Kfz-Kosten": {
"is_group": 1,
"Kfz-Steuer": {
"account_number": "4510",
"account_type": "Expense Account"
},
"Kfz-Versicherungen": {
"account_number": "4520",
"account_type": "Expense Account"
},
"laufende Kfz-Betriebskosten": {
"account_number": "4530",
"account_type": "Expense Account"
},
"Kfz-Reparaturen": {
"account_number": "4540",
"account_type": "Expense Account"
},
"Fremdfahrzeuge": {
"account_number": "4570",
"account_type": "Expense Account"
},
"sonstige Kfz-Kosten": {
"account_number": "4580",
"account_type": "Expense Account"
}
},
"Personalkosten": {
"is_group": 1,
"Geh\u00e4lter": {
"account_number": "4120",
"account_type": "Expense Account"
},
"gesetzliche soziale Aufwendungen": {
"account_number": "4130",
"account_type": "Expense Account"
},
"Aufwendungen f\u00fcr Altersvorsorge": {
"account_number": "4165",
"account_type": "Expense Account"
},
"Verm\u00f6genswirksame Leistungen": {
"account_number": "4170",
"account_type": "Expense Account"
},
"Aushilfsl\u00f6hne": {
"account_number": "4190",
"account_type": "Expense Account"
}
},
"Raumkosten": {
"is_group": 1,
"Miete und Nebenkosten": {
"account_number": "4210",
"account_type": "Expense Account"
},
"Gas, Wasser, Strom (Verwaltung, Vertrieb)": {
"account_number": "4240",
"account_type": "Expense Account"
},
"Reinigung": {
"account_number": "4250",
"account_type": "Expense Account"
}
},
"Reparatur/Instandhaltung": {
"is_group": 1,
"Reparatur u. Instandh. von Anlagen/Maschinen u. Betriebs- u. Gesch\u00e4ftsausst.": {
"account_number": "4805",
"account_type": "Expense Account"
}
},
"Versicherungsbeitr\u00e4ge": {
"is_group": 1,
"Versicherungen": {
"account_number": "4360",
"account_type": "Expense Account"
},
"Beitr\u00e4ge": {
"account_number": "4380",
"account_type": "Expense Account"
},
"sonstige Ausgaben": {
"account_number": "4390",
"account_type": "Expense Account"
},
"steuerlich abzugsf\u00e4hige Versp\u00e4tungszuschl\u00e4ge und Zwangsgelder": {
"account_number": "4396",
"account_type": "Expense Account"
}
},
"Werbe-/Reisekosten": {
"is_group": 1,
"Werbekosten": {
"account_number": "4610",
"account_type": "Expense Account"
},
"Aufmerksamkeiten": {
"account_number": "4653",
"account_type": "Expense Account"
},
"nicht abzugsf\u00e4hige Betriebsausg. aus Werbe-, Repr\u00e4s.- u. Reisekosten": {
"account_number": "4665",
"account_type": "Expense Account"
},
"Reisekosten Unternehmer": {
"account_number": "4670",
"account_type": "Expense Account"
}
},
"verschiedene Kosten": {
"is_group": 1,
"Porto": {
"account_number": "4910",
"account_type": "Expense Account"
},
"Telekom": {
"account_number": "4920",
"account_type": "Expense Account"
},
"Mobilfunk D2": {
"account_number": "4921",
"account_type": "Expense Account"
},
"Internet": {
"account_number": "4922",
"account_type": "Expense Account"
},
"B\u00fcrobedarf": {
"account_number": "4930",
"account_type": "Expense Account"
},
"Zeitschriften, B\u00fccher": {
"account_number": "4940",
"account_type": "Expense Account"
},
"Fortbildungskosten": {
"account_number": "4945",
"account_type": "Expense Account"
},
"Buchf\u00fchrungskosten": {
"account_number": "4955",
"account_type": "Expense Account"
},
"Abschlu\u00df- u. Pr\u00fcfungskosten": {
"account_number": "4957",
"account_type": "Expense Account"
},
"Nebenkosten des Geldverkehrs": {
"account_number": "4970",
"account_type": "Expense Account"
},
"Werkzeuge und Kleinger\u00e4te": {
"account_number": "4985",
"account_type": "Expense Account"
}
},
"Zinsaufwendungen": {
"is_group": 1,
"Zinsaufwendungen f\u00fcr kurzfristige Verbindlichkeiten": {
"account_number": "2110",
"account_type": "Expense Account"
},
"Zinsaufwendungen f\u00fcr KFZ Finanzierung": {
"account_number": "2121",
"account_type": "Expense Account"
}
}
},
"Anfangsbestand 9": {
"is_group": 1,
"root_type": "Equity",
"Saldenvortragskonten": {
"is_group": 1,
"Saldenvortrag Sachkonten": {
"account_number": "9000"
},
"Saldenvortr\u00e4ge Debitoren": {
"account_number": "9008"
},
"Saldenvortr\u00e4ge Kreditoren": {
"account_number": "9009"
}
}
},
"Privatkonten 1": {
"is_group": 1,
"root_type": "Equity",
"Privatentnahmen/-einlagen": {
"is_group": 1,
"Privatentnahme allgemein": {
"account_number": "1800"
},
"Privatsteuern": {
"account_number": "1810"
},
"Sonderausgaben beschr\u00e4nkt abzugsf\u00e4hig": {
"account_number": "1820"
},
"Sonderausgaben unbeschr\u00e4nkt abzugsf\u00e4hig": {
"account_number": "1830"
},
"Au\u00dfergew\u00f6hnliche Belastungen": {
"account_number": "1850"
},
"Privateinlagen": {
"account_number": "1890"
}
}
}
}
},
"Kfz-Kosten": {
"is_group": 1,
"Kfz-Steuer": {
"account_number": "4510",
"account_type": "Expense Account"
},
"Kfz-Versicherungen": {
"account_number": "4520",
"account_type": "Expense Account"
},
"laufende Kfz-Betriebskosten": {
"account_number": "4530",
"account_type": "Expense Account"
},
"Kfz-Reparaturen": {
"account_number": "4540",
"account_type": "Expense Account"
},
"Fremdfahrzeuge": {
"account_number": "4570",
"account_type": "Expense Account"
},
"sonstige Kfz-Kosten": {
"account_number": "4580",
"account_type": "Expense Account"
}
},
"Personalkosten": {
"is_group": 1,
"Gehälter": {
"account_number": "4120",
"account_type": "Expense Account"
},
"gesetzliche soziale Aufwendungen": {
"account_number": "4130",
"account_type": "Expense Account"
},
"Aufwendungen für Altersvorsorge": {
"account_number": "4165",
"account_type": "Expense Account"
},
"Vermögenswirksame Leistungen": {
"account_number": "4170",
"account_type": "Expense Account"
},
"Aushilfslöhne": {
"account_number": "4190",
"account_type": "Expense Account"
}
},
"Raumkosten": {
"is_group": 1,
"Miete und Nebenkosten": {
"account_number": "4210",
"account_type": "Expense Account"
},
"Gas, Wasser, Strom (Verwaltung, Vertrieb)": {
"account_number": "4240",
"account_type": "Expense Account"
},
"Reinigung": {
"account_number": "4250",
"account_type": "Expense Account"
}
},
"Reparatur/Instandhaltung": {
"is_group": 1,
"Reparaturen und Instandhaltungen von anderen Anlagen und Betriebs- und Geschäftsausstattung": {
"account_number": "4805",
"account_type": "Expense Account"
}
},
"Versicherungsbeiträge": {
"is_group": 1,
"Versicherungen": {
"account_number": "4360",
"account_type": "Expense Account"
},
"Beiträge": {
"account_number": "4380",
"account_type": "Expense Account"
},
"sonstige Ausgaben": {
"account_number": "4390",
"account_type": "Expense Account"
},
"steuerlich abzugsfähige Verspätungszuschläge und Zwangsgelder": {
"account_number": "4396",
"account_type": "Expense Account"
}
},
"Werbe-/Reisekosten": {
"is_group": 1,
"Werbekosten": {
"account_number": "4610",
"account_type": "Expense Account"
},
"Aufmerksamkeiten": {
"account_number": "4653",
"account_type": "Expense Account"
},
"nicht abzugsfähige Betriebsausg. aus Werbe-, Repräs.- u. Reisekosten": {
"account_number": "4665",
"account_type": "Expense Account"
},
"Reisekosten Unternehmer": {
"account_number": "4670",
"account_type": "Expense Account"
}
},
"verschiedene Kosten": {
"is_group": 1,
"Porto": {
"account_number": "4910",
"account_type": "Expense Account"
},
"Telekom": {
"account_number": "4920",
"account_type": "Expense Account"
},
"Mobilfunk D2": {
"account_number": "4921",
"account_type": "Expense Account"
},
"Internet": {
"account_number": "4922",
"account_type": "Expense Account"
},
"Bürobedarf": {
"account_number": "4930",
"account_type": "Expense Account"
},
"Zeitschriften, Bücher": {
"account_number": "4940",
"account_type": "Expense Account"
},
"Fortbildungskosten": {
"account_number": "4945",
"account_type": "Expense Account"
},
"Buchführungskosten": {
"account_number": "4955",
"account_type": "Expense Account"
},
"Abschluß- u. Prüfungskosten": {
"account_number": "4957",
"account_type": "Expense Account"
},
"Nebenkosten des Geldverkehrs": {
"account_number": "4970",
"account_type": "Expense Account"
},
"Werkzeuge und Kleingeräte": {
"account_number": "4985",
"account_type": "Expense Account"
}
},
"Zinsaufwendungen": {
"is_group": 1,
"Zinsaufwendungen für kurzfristige Verbindlichkeiten": {
"account_number": "2110",
"account_type": "Expense Account"
},
"Zinsaufwendungen für KFZ Finanzierung": {
"account_number": "2121",
"account_type": "Expense Account"
}
}
},
"Anfangsbestand 9": {
"is_group": 1,
"root_type": "Equity",
"Saldenvortragskonten": {
"is_group": 1,
"Saldenvortrag Sachkonten": {
"account_number": "9000"
},
"Saldenvorträge Debitoren": {
"account_number": "9008"
},
"Saldenvorträge Kreditoren": {
"account_number": "9009"
}
}
},
"Privatkonten 1": {
"is_group": 1,
"root_type": "Equity",
"Privatentnahmen/-einlagen": {
"is_group": 1,
"Privatentnahme allgemein": {
"account_number": "1800"
},
"Privatsteuern": {
"account_number": "1810"
},
"Sonderausgaben beschränkt abzugsfähig": {
"account_number": "1820"
},
"Sonderausgaben unbeschränkt abzugsfähig": {
"account_number": "1830"
},
"Außergewöhnliche Belastungen": {
"account_number": "1850"
},
"Privateinlagen": {
"account_number": "1890"
}
}
}
}
}

View File

@@ -0,0 +1,8 @@
// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
// frappe.ui.form.on("Account Closing Balance", {
// refresh(frm) {
// },
// });

View File

@@ -0,0 +1,164 @@
{
"actions": [],
"creation": "2023-02-21 15:20:59.586811",
"default_view": "List",
"doctype": "DocType",
"document_type": "Document",
"engine": "InnoDB",
"field_order": [
"closing_date",
"account",
"cost_center",
"debit",
"credit",
"account_currency",
"debit_in_account_currency",
"credit_in_account_currency",
"project",
"company",
"finance_book",
"period_closing_voucher",
"is_period_closing_voucher_entry"
],
"fields": [
{
"fieldname": "closing_date",
"fieldtype": "Date",
"in_filter": 1,
"in_list_view": 1,
"label": "Closing Date",
"oldfieldname": "posting_date",
"oldfieldtype": "Date",
"search_index": 1
},
{
"fieldname": "account",
"fieldtype": "Link",
"in_filter": 1,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Account",
"oldfieldname": "account",
"oldfieldtype": "Link",
"options": "Account",
"search_index": 1
},
{
"fieldname": "cost_center",
"fieldtype": "Link",
"in_filter": 1,
"in_list_view": 1,
"label": "Cost Center",
"oldfieldname": "cost_center",
"oldfieldtype": "Link",
"options": "Cost Center"
},
{
"fieldname": "debit",
"fieldtype": "Currency",
"label": "Debit Amount",
"oldfieldname": "debit",
"oldfieldtype": "Currency",
"options": "Company:company:default_currency"
},
{
"fieldname": "credit",
"fieldtype": "Currency",
"label": "Credit Amount",
"oldfieldname": "credit",
"oldfieldtype": "Currency",
"options": "Company:company:default_currency"
},
{
"fieldname": "account_currency",
"fieldtype": "Link",
"label": "Account Currency",
"options": "Currency"
},
{
"fieldname": "debit_in_account_currency",
"fieldtype": "Currency",
"label": "Debit Amount in Account Currency",
"options": "account_currency"
},
{
"fieldname": "credit_in_account_currency",
"fieldtype": "Currency",
"label": "Credit Amount in Account Currency",
"options": "account_currency"
},
{
"fieldname": "project",
"fieldtype": "Link",
"label": "Project",
"options": "Project"
},
{
"fieldname": "company",
"fieldtype": "Link",
"in_filter": 1,
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Company",
"oldfieldname": "company",
"oldfieldtype": "Link",
"options": "Company",
"search_index": 1
},
{
"fieldname": "finance_book",
"fieldtype": "Link",
"label": "Finance Book",
"options": "Finance Book"
},
{
"fieldname": "period_closing_voucher",
"fieldtype": "Link",
"in_standard_filter": 1,
"label": "Period Closing Voucher",
"options": "Period Closing Voucher",
"search_index": 1
},
{
"default": "0",
"fieldname": "is_period_closing_voucher_entry",
"fieldtype": "Check",
"label": "Is Period Closing Voucher Entry"
}
],
"icon": "fa fa-list",
"in_create": 1,
"links": [],
"modified": "2023-03-06 08:56:36.393237",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Account Closing Balance",
"owner": "Administrator",
"permissions": [
{
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts User"
},
{
"email": 1,
"export": 1,
"print": 1,
"read": 1,
"report": 1,
"role": "Accounts Manager"
},
{
"export": 1,
"read": 1,
"report": 1,
"role": "Auditor"
}
],
"sort_field": "modified",
"sort_order": "DESC",
"states": []
}

View File

@@ -0,0 +1,127 @@
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
import frappe
from frappe.model.document import Document
from frappe.utils import cint, cstr
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_accounting_dimensions,
)
class AccountClosingBalance(Document):
pass
def make_closing_entries(closing_entries, voucher_name):
accounting_dimensions = get_accounting_dimensions()
company = closing_entries[0].get("company")
closing_date = closing_entries[0].get("closing_date")
previous_closing_entries = get_previous_closing_entries(
company, closing_date, accounting_dimensions
)
combined_entries = closing_entries + previous_closing_entries
merged_entries = aggregate_with_last_account_closing_balance(
combined_entries, accounting_dimensions
)
for key, value in merged_entries.items():
cle = frappe.new_doc("Account Closing Balance")
cle.update(value)
cle.update(value["dimensions"])
cle.update(
{
"period_closing_voucher": voucher_name,
"closing_date": closing_date,
}
)
cle.submit()
def aggregate_with_last_account_closing_balance(entries, accounting_dimensions):
merged_entries = {}
for entry in entries:
key, key_values = generate_key(entry, accounting_dimensions)
merged_entries.setdefault(
key,
{
"debit": 0,
"credit": 0,
"debit_in_account_currency": 0,
"credit_in_account_currency": 0,
},
)
merged_entries[key]["dimensions"] = key_values
merged_entries[key]["debit"] += entry.get("debit")
merged_entries[key]["credit"] += entry.get("credit")
merged_entries[key]["debit_in_account_currency"] += entry.get("debit_in_account_currency")
merged_entries[key]["credit_in_account_currency"] += entry.get("credit_in_account_currency")
return merged_entries
def generate_key(entry, accounting_dimensions):
key = [
cstr(entry.get("account")),
cstr(entry.get("account_currency")),
cstr(entry.get("cost_center")),
cstr(entry.get("project")),
cstr(entry.get("finance_book")),
cint(entry.get("is_period_closing_voucher_entry")),
]
key_values = {
"company": cstr(entry.get("company")),
"account": cstr(entry.get("account")),
"account_currency": cstr(entry.get("account_currency")),
"cost_center": cstr(entry.get("cost_center")),
"project": cstr(entry.get("project")),
"finance_book": cstr(entry.get("finance_book")),
"is_period_closing_voucher_entry": cint(entry.get("is_period_closing_voucher_entry")),
}
for dimension in accounting_dimensions:
key.append(cstr(entry.get(dimension)))
key_values[dimension] = cstr(entry.get(dimension))
return tuple(key), key_values
def get_previous_closing_entries(company, closing_date, accounting_dimensions):
entries = []
last_period_closing_voucher = frappe.db.get_all(
"Period Closing Voucher",
filters={"docstatus": 1, "company": company, "posting_date": ("<", closing_date)},
fields=["name"],
order_by="posting_date desc",
limit=1,
)
if last_period_closing_voucher:
account_closing_balance = frappe.qb.DocType("Account Closing Balance")
query = frappe.qb.from_(account_closing_balance).select(
account_closing_balance.company,
account_closing_balance.account,
account_closing_balance.account_currency,
account_closing_balance.debit,
account_closing_balance.credit,
account_closing_balance.debit_in_account_currency,
account_closing_balance.credit_in_account_currency,
account_closing_balance.cost_center,
account_closing_balance.project,
account_closing_balance.finance_book,
account_closing_balance.is_period_closing_voucher_entry,
)
for dimension in accounting_dimensions:
query = query.select(account_closing_balance[dimension])
query = query.where(
account_closing_balance.period_closing_voucher == last_period_closing_voucher[0].name
)
entries = query.run(as_dict=1)
return entries

View File

@@ -0,0 +1,9 @@
# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
# See license.txt
# import frappe
from frappe.tests.utils import FrappeTestCase
class TestAccountClosingBalance(FrappeTestCase):
pass

View File

@@ -31,6 +31,7 @@
"determine_address_tax_category_from",
"column_break_19",
"add_taxes_from_item_tax_template",
"book_tax_discount_loss",
"print_settings",
"show_inclusive_tax_in_print",
"column_break_12",
@@ -56,7 +57,9 @@
"acc_frozen_upto",
"column_break_25",
"frozen_accounts_modifier",
"report_settings_sb"
"report_settings_sb",
"tab_break_dpet",
"show_balance_in_coa"
],
"fields": [
{
@@ -347,6 +350,24 @@
"fieldname": "allow_multi_currency_invoices_against_single_party_account",
"fieldtype": "Check",
"label": "Allow multi-currency invoices against single party account "
},
{
"fieldname": "tab_break_dpet",
"fieldtype": "Tab Break",
"label": "Chart Of Accounts"
},
{
"default": "1",
"fieldname": "show_balance_in_coa",
"fieldtype": "Check",
"label": "Show Balances in Chart Of Accounts"
},
{
"default": "0",
"description": "Split Early Payment Discount Loss into Income and Tax Loss",
"fieldname": "book_tax_discount_loss",
"fieldtype": "Check",
"label": "Book Tax Loss on Early Payment Discount"
}
],
"icon": "icon-cog",
@@ -354,7 +375,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2022-11-27 21:49:52.538655",
"modified": "2023-03-28 09:50:20.375233",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Settings",

View File

@@ -118,6 +118,10 @@ erpnext.integrations.refreshPlaidLink = class refreshPlaidLink {
}
plaid_success(token, response) {
frappe.show_alert({ message: __('Plaid Link Updated'), indicator: 'green' });
frappe.xcall('erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.update_bank_account_ids', {
response: response,
}).then(() => {
frappe.show_alert({ message: __('Plaid Link Updated'), indicator: 'green' });
});
}
};

View File

@@ -37,14 +37,11 @@ frappe.ui.form.on("Bank Clearance", {
refresh: function(frm) {
frm.disable_save();
frm.add_custom_button(__('Get Payment Entries'), () =>
frm.trigger("get_payment_entries")
);
if (frm.doc.account && frm.doc.from_date && frm.doc.to_date) {
frm.add_custom_button(__('Get Payment Entries'), () =>
frm.trigger("get_payment_entries")
);
frm.change_custom_button_type('Get Payment Entries', null, 'primary');
}
frm.change_custom_button_type('Get Payment Entries', null, 'primary');
},
update_clearance_date: function(frm) {

View File

@@ -81,7 +81,7 @@ class BankClearance(Document):
loan_disbursement = frappe.qb.DocType("Loan Disbursement")
loan_disbursements = (
query = (
frappe.qb.from_(loan_disbursement)
.select(
ConstantColumn("Loan Disbursement").as_("payment_document"),
@@ -90,17 +90,22 @@ class BankClearance(Document):
ConstantColumn(0).as_("debit"),
loan_disbursement.reference_number.as_("cheque_number"),
loan_disbursement.reference_date.as_("cheque_date"),
loan_disbursement.clearance_date.as_("clearance_date"),
loan_disbursement.disbursement_date.as_("posting_date"),
loan_disbursement.applicant.as_("against_account"),
)
.where(loan_disbursement.docstatus == 1)
.where(loan_disbursement.disbursement_date >= self.from_date)
.where(loan_disbursement.disbursement_date <= self.to_date)
.where(loan_disbursement.clearance_date.isnull())
.where(loan_disbursement.disbursement_account.isin([self.bank_account, self.account]))
.orderby(loan_disbursement.disbursement_date)
.orderby(loan_disbursement.name, order=frappe.qb.desc)
).run(as_dict=1)
)
if not self.include_reconciled_entries:
query = query.where(loan_disbursement.clearance_date.isnull())
loan_disbursements = query.run(as_dict=1)
loan_repayment = frappe.qb.DocType("Loan Repayment")
@@ -113,16 +118,19 @@ class BankClearance(Document):
ConstantColumn(0).as_("credit"),
loan_repayment.reference_number.as_("cheque_number"),
loan_repayment.reference_date.as_("cheque_date"),
loan_repayment.clearance_date.as_("clearance_date"),
loan_repayment.applicant.as_("against_account"),
loan_repayment.posting_date,
)
.where(loan_repayment.docstatus == 1)
.where(loan_repayment.clearance_date.isnull())
.where(loan_repayment.posting_date >= self.from_date)
.where(loan_repayment.posting_date <= self.to_date)
.where(loan_repayment.payment_account.isin([self.bank_account, self.account]))
)
if not self.include_reconciled_entries:
query = query.where(loan_repayment.clearance_date.isnull())
if frappe.db.has_column("Loan Repayment", "repay_from_salary"):
query = query.where((loan_repayment.repay_from_salary == 0))

View File

@@ -18,16 +18,30 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
},
onload: function (frm) {
// Set default filter dates
today = frappe.datetime.get_today()
frm.doc.bank_statement_from_date = frappe.datetime.add_months(today, -1);
frm.doc.bank_statement_to_date = today;
frm.trigger('bank_account');
},
filter_by_reference_date: function (frm) {
if (frm.doc.filter_by_reference_date) {
frm.set_value("bank_statement_from_date", "");
frm.set_value("bank_statement_to_date", "");
} else {
frm.set_value("from_reference_date", "");
frm.set_value("to_reference_date", "");
}
},
refresh: function (frm) {
frm.disable_save();
frappe.require("bank-reconciliation-tool.bundle.js", () =>
frm.trigger("make_reconciliation_tool")
);
frm.upload_statement_button = frm.page.set_secondary_action(
__("Upload Bank Statement"),
() =>
frm.add_custom_button(__("Upload Bank Statement"), () =>
frappe.call({
method:
"erpnext.accounts.doctype.bank_statement_import.bank_statement_import.upload_bank_statement",
@@ -49,10 +63,26 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
},
})
);
},
after_save: function (frm) {
frm.trigger("make_reconciliation_tool");
frm.add_custom_button(__('Auto Reconcile'), function() {
frappe.call({
method: "erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool.auto_reconcile_vouchers",
args: {
bank_account: frm.doc.bank_account,
from_date: frm.doc.bank_statement_from_date,
to_date: frm.doc.bank_statement_to_date,
filter_by_reference_date: frm.doc.filter_by_reference_date,
from_reference_date: frm.doc.from_reference_date,
to_reference_date: frm.doc.to_reference_date,
},
})
});
frm.add_custom_button(__('Get Unreconciled Entries'), function() {
frm.trigger("make_reconciliation_tool");
});
frm.change_custom_button_type('Get Unreconciled Entries', null, 'primary');
},
bank_account: function (frm) {
@@ -66,7 +96,7 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
r.account,
"account_currency",
(r) => {
frm.currency = r.account_currency;
frm.doc.account_currency = r.account_currency;
frm.trigger("render_chart");
}
);
@@ -132,19 +162,19 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
}
},
render_chart: frappe.utils.debounce((frm) => {
render_chart(frm) {
frm.cards_manager = new erpnext.accounts.bank_reconciliation.NumberCardManager(
{
$reconciliation_tool_cards: frm.get_field(
"reconciliation_tool_cards"
).$wrapper,
bank_statement_closing_balance:
frm.doc.bank_statement_closing_balance,
frm.doc.bank_statement_closing_balance,
cleared_balance: frm.cleared_balance,
currency: frm.currency,
currency: frm.doc.account_currency,
}
);
}, 500),
},
render(frm) {
if (frm.doc.bank_account) {
@@ -160,6 +190,9 @@ frappe.ui.form.on("Bank Reconciliation Tool", {
).$wrapper,
bank_statement_from_date: frm.doc.bank_statement_from_date,
bank_statement_to_date: frm.doc.bank_statement_to_date,
filter_by_reference_date: frm.doc.filter_by_reference_date,
from_reference_date: frm.doc.from_reference_date,
to_reference_date: frm.doc.to_reference_date,
bank_statement_closing_balance:
frm.doc.bank_statement_closing_balance,
cards_manager: frm.cards_manager,

View File

@@ -10,7 +10,11 @@
"column_break_1",
"bank_statement_from_date",
"bank_statement_to_date",
"from_reference_date",
"to_reference_date",
"filter_by_reference_date",
"column_break_2",
"account_currency",
"account_opening_balance",
"bank_statement_closing_balance",
"section_break_1",
@@ -36,13 +40,13 @@
"fieldtype": "Column Break"
},
{
"depends_on": "eval: doc.bank_account",
"depends_on": "eval: doc.bank_account && !doc.filter_by_reference_date",
"fieldname": "bank_statement_from_date",
"fieldtype": "Date",
"label": "From Date"
},
{
"depends_on": "eval: doc.bank_statement_from_date",
"depends_on": "eval: doc.bank_account && !doc.filter_by_reference_date",
"fieldname": "bank_statement_to_date",
"fieldtype": "Date",
"label": "To Date"
@@ -56,7 +60,7 @@
"fieldname": "account_opening_balance",
"fieldtype": "Currency",
"label": "Account Opening Balance",
"options": "Currency",
"options": "account_currency",
"read_only": 1
},
{
@@ -64,7 +68,7 @@
"fieldname": "bank_statement_closing_balance",
"fieldtype": "Currency",
"label": "Closing Balance",
"options": "Currency"
"options": "account_currency"
},
{
"fieldname": "section_break_1",
@@ -81,14 +85,40 @@
},
{
"fieldname": "no_bank_transactions",
"fieldtype": "HTML"
"fieldtype": "HTML",
"options": "<div class=\"text-muted text-center\">No Matching Bank Transactions Found</div>"
},
{
"depends_on": "eval:doc.filter_by_reference_date",
"fieldname": "from_reference_date",
"fieldtype": "Date",
"label": "From Reference Date"
},
{
"depends_on": "eval:doc.filter_by_reference_date",
"fieldname": "to_reference_date",
"fieldtype": "Date",
"label": "To Reference Date"
},
{
"default": "0",
"fieldname": "filter_by_reference_date",
"fieldtype": "Check",
"label": "Filter by Reference Date"
},
{
"fieldname": "account_currency",
"fieldtype": "Link",
"hidden": 1,
"label": "Account Currency",
"options": "Currency"
}
],
"hide_toolbar": 1,
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2021-04-21 11:13:49.831769",
"modified": "2023-03-07 11:02:24.535714",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Reconciliation Tool",
@@ -107,5 +137,6 @@
],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC"
}
"sort_order": "DESC",
"states": []
}

View File

@@ -8,9 +8,9 @@ import frappe
from frappe import _
from frappe.model.document import Document
from frappe.query_builder.custom import ConstantColumn
from frappe.utils import flt
from frappe.utils import cint, flt
from erpnext.accounts.doctype.bank_transaction.bank_transaction import get_paid_amount
from erpnext.accounts.doctype.bank_transaction.bank_transaction import get_total_allocated_amount
from erpnext.accounts.report.bank_reconciliation_statement.bank_reconciliation_statement import (
get_amounts_not_reflected_in_system,
get_entries,
@@ -28,7 +28,7 @@ def get_bank_transactions(bank_account, from_date=None, to_date=None):
filters = []
filters.append(["bank_account", "=", bank_account])
filters.append(["docstatus", "=", 1])
filters.append(["unallocated_amount", ">", 0])
filters.append(["unallocated_amount", ">", 0.0])
if to_date:
filters.append(["date", "<=", to_date])
if from_date:
@@ -50,6 +50,7 @@ def get_bank_transactions(bank_account, from_date=None, to_date=None):
"party",
],
filters=filters,
order_by="date",
)
return transactions
@@ -57,7 +58,7 @@ def get_bank_transactions(bank_account, from_date=None, to_date=None):
@frappe.whitelist()
def get_account_balance(bank_account, till_date):
# returns account balance till the specified date
account = frappe.get_cached_value("Bank Account", bank_account, "account")
account = frappe.db.get_value("Bank Account", bank_account, "account")
filters = frappe._dict(
{"account": account, "report_date": till_date, "include_pos_transactions": 1}
)
@@ -65,7 +66,7 @@ def get_account_balance(bank_account, till_date):
balance_as_per_system = get_balance_on(filters["account"], filters["report_date"])
total_debit, total_credit = 0, 0
total_debit, total_credit = 0.0, 0.0
for d in data:
total_debit += flt(d.debit)
total_credit += flt(d.credit)
@@ -130,10 +131,8 @@ def create_journal_entry_bts(
fieldname=["name", "deposit", "withdrawal", "bank_account"],
as_dict=True,
)[0]
company_account = frappe.get_cached_value(
"Bank Account", bank_transaction.bank_account, "account"
)
account_type = frappe.get_cached_value("Account", second_account, "account_type")
company_account = frappe.get_value("Bank Account", bank_transaction.bank_account, "account")
account_type = frappe.db.get_value("Account", second_account, "account_type")
if account_type in ["Receivable", "Payable"]:
if not (party_type and party):
frappe.throw(
@@ -146,10 +145,8 @@ def create_journal_entry_bts(
accounts.append(
{
"account": second_account,
"credit_in_account_currency": bank_transaction.deposit if bank_transaction.deposit > 0 else 0,
"debit_in_account_currency": bank_transaction.withdrawal
if bank_transaction.withdrawal > 0
else 0,
"credit_in_account_currency": bank_transaction.deposit,
"debit_in_account_currency": bank_transaction.withdrawal,
"party_type": party_type,
"party": party,
}
@@ -159,14 +156,12 @@ def create_journal_entry_bts(
{
"account": company_account,
"bank_account": bank_transaction.bank_account,
"credit_in_account_currency": bank_transaction.withdrawal
if bank_transaction.withdrawal > 0
else 0,
"debit_in_account_currency": bank_transaction.deposit if bank_transaction.deposit > 0 else 0,
"credit_in_account_currency": bank_transaction.withdrawal,
"debit_in_account_currency": bank_transaction.deposit,
}
)
company = frappe.get_cached_value("Account", company_account, "company")
company = frappe.get_value("Account", company_account, "company")
journal_entry_dict = {
"voucher_type": entry_type,
@@ -186,16 +181,22 @@ def create_journal_entry_bts(
journal_entry.insert()
journal_entry.submit()
if bank_transaction.deposit > 0:
if bank_transaction.deposit > 0.0:
paid_amount = bank_transaction.deposit
else:
paid_amount = bank_transaction.withdrawal
vouchers = json.dumps(
[{"payment_doctype": "Journal Entry", "payment_name": journal_entry.name, "amount": paid_amount}]
[
{
"payment_doctype": "Journal Entry",
"payment_name": journal_entry.name,
"amount": paid_amount,
}
]
)
return reconcile_vouchers(bank_transaction.name, vouchers)
return reconcile_vouchers(bank_transaction_name, vouchers)
@frappe.whitelist()
@@ -219,12 +220,10 @@ def create_payment_entry_bts(
as_dict=True,
)[0]
paid_amount = bank_transaction.unallocated_amount
payment_type = "Receive" if bank_transaction.deposit > 0 else "Pay"
payment_type = "Receive" if bank_transaction.deposit > 0.0 else "Pay"
company_account = frappe.get_cached_value(
"Bank Account", bank_transaction.bank_account, "account"
)
company = frappe.get_cached_value("Account", company_account, "company")
company_account = frappe.get_value("Bank Account", bank_transaction.bank_account, "account")
company = frappe.get_value("Account", company_account, "company")
payment_entry_dict = {
"company": company,
"payment_type": payment_type,
@@ -260,9 +259,89 @@ def create_payment_entry_bts(
payment_entry.submit()
vouchers = json.dumps(
[{"payment_doctype": "Payment Entry", "payment_name": payment_entry.name, "amount": paid_amount}]
[
{
"payment_doctype": "Payment Entry",
"payment_name": payment_entry.name,
"amount": paid_amount,
}
]
)
return reconcile_vouchers(bank_transaction.name, vouchers)
return reconcile_vouchers(bank_transaction_name, vouchers)
@frappe.whitelist()
def auto_reconcile_vouchers(
bank_account,
from_date=None,
to_date=None,
filter_by_reference_date=None,
from_reference_date=None,
to_reference_date=None,
):
frappe.flags.auto_reconcile_vouchers = True
document_types = ["payment_entry", "journal_entry"]
bank_transactions = get_bank_transactions(bank_account)
matched_transaction = []
for transaction in bank_transactions:
linked_payments = get_linked_payments(
transaction.name,
document_types,
from_date,
to_date,
filter_by_reference_date,
from_reference_date,
to_reference_date,
)
vouchers = []
for r in linked_payments:
vouchers.append(
{
"payment_doctype": r[1],
"payment_name": r[2],
"amount": r[4],
}
)
transaction = frappe.get_doc("Bank Transaction", transaction.name)
account = frappe.db.get_value("Bank Account", transaction.bank_account, "account")
matched_trans = 0
for voucher in vouchers:
gl_entry = frappe.db.get_value(
"GL Entry",
dict(
account=account, voucher_type=voucher["payment_doctype"], voucher_no=voucher["payment_name"]
),
["credit", "debit"],
as_dict=1,
)
gl_amount, transaction_amount = (
(gl_entry.credit, transaction.deposit)
if gl_entry.credit > 0
else (gl_entry.debit, transaction.withdrawal)
)
allocated_amount = gl_amount if gl_amount >= transaction_amount else transaction_amount
transaction.append(
"payment_entries",
{
"payment_document": voucher["payment_doctype"],
"payment_entry": voucher["payment_name"],
"allocated_amount": allocated_amount,
},
)
matched_transaction.append(str(transaction.name))
transaction.save()
transaction.update_allocations()
matched_transaction_len = len(set(matched_transaction))
if matched_transaction_len == 0:
frappe.msgprint(_("No matching references found for auto reconciliation"))
elif matched_transaction_len == 1:
frappe.msgprint(_("{0} transaction is reconcilied").format(matched_transaction_len))
else:
frappe.msgprint(_("{0} transactions are reconcilied").format(matched_transaction_len))
frappe.flags.auto_reconcile_vouchers = False
return frappe.get_doc("Bank Transaction", transaction.name)
@frappe.whitelist()
@@ -270,80 +349,88 @@ def reconcile_vouchers(bank_transaction_name, vouchers):
# updated clear date of all the vouchers based on the bank transaction
vouchers = json.loads(vouchers)
transaction = frappe.get_doc("Bank Transaction", bank_transaction_name)
company_account = frappe.get_cached_value("Bank Account", transaction.bank_account, "account")
if transaction.unallocated_amount == 0:
frappe.throw(_("This bank transaction is already fully reconciled"))
total_amount = 0
for voucher in vouchers:
voucher["payment_entry"] = frappe.get_doc(voucher["payment_doctype"], voucher["payment_name"])
total_amount += get_paid_amount(
frappe._dict(
{
"payment_document": voucher["payment_doctype"],
"payment_entry": voucher["payment_name"],
}
),
transaction.currency,
company_account,
)
if total_amount > transaction.unallocated_amount:
frappe.throw(
_(
"The sum total of amounts of all selected vouchers should be less than the unallocated amount of the bank transaction"
)
)
account = frappe.get_cached_value("Bank Account", transaction.bank_account, "account")
for voucher in vouchers:
gl_entry = frappe.db.get_value(
"GL Entry",
dict(
account=account, voucher_type=voucher["payment_doctype"], voucher_no=voucher["payment_name"]
),
["credit", "debit"],
as_dict=1,
)
gl_amount, transaction_amount = (
(gl_entry.credit, transaction.deposit)
if gl_entry.credit > 0
else (gl_entry.debit, transaction.withdrawal)
)
allocated_amount = gl_amount if gl_amount >= transaction_amount else transaction_amount
transaction.append(
"payment_entries",
{
"payment_document": voucher["payment_entry"].doctype,
"payment_entry": voucher["payment_entry"].name,
"allocated_amount": allocated_amount,
},
)
transaction.save()
transaction.update_allocations()
transaction.add_payment_entries(vouchers)
return frappe.get_doc("Bank Transaction", bank_transaction_name)
@frappe.whitelist()
def get_linked_payments(bank_transaction_name, document_types=None):
def get_linked_payments(
bank_transaction_name,
document_types=None,
from_date=None,
to_date=None,
filter_by_reference_date=None,
from_reference_date=None,
to_reference_date=None,
):
# get all matching payments for a bank transaction
transaction = frappe.get_doc("Bank Transaction", bank_transaction_name)
bank_account = frappe.db.get_values(
"Bank Account", transaction.bank_account, ["account", "company"], as_dict=True
)[0]
(account, company) = (bank_account.account, bank_account.company)
matching = check_matching(account, company, transaction, document_types)
return matching
(gl_account, company) = (bank_account.account, bank_account.company)
matching = check_matching(
gl_account,
company,
transaction,
document_types,
from_date,
to_date,
filter_by_reference_date,
from_reference_date,
to_reference_date,
)
return subtract_allocations(gl_account, matching)
def check_matching(bank_account, company, transaction, document_types):
def subtract_allocations(gl_account, vouchers):
"Look up & subtract any existing Bank Transaction allocations"
copied = []
for voucher in vouchers:
rows = get_total_allocated_amount(voucher[1], voucher[2])
amount = None
for row in rows:
if row["gl_account"] == gl_account:
amount = row["total"]
break
if amount:
l = list(voucher)
l[3] -= amount
copied.append(tuple(l))
else:
copied.append(voucher)
return copied
def check_matching(
bank_account,
company,
transaction,
document_types,
from_date,
to_date,
filter_by_reference_date,
from_reference_date,
to_reference_date,
):
exact_match = True if "exact_match" in document_types else False
# combine all types of vouchers
subquery = get_queries(bank_account, company, transaction, document_types)
subquery = get_queries(
bank_account,
company,
transaction,
document_types,
from_date,
to_date,
filter_by_reference_date,
from_reference_date,
to_reference_date,
exact_match,
)
filters = {
"amount": transaction.unallocated_amount,
"payment_type": "Receive" if transaction.deposit > 0 else "Pay",
"payment_type": "Receive" if transaction.deposit > 0.0 else "Pay",
"reference_no": transaction.reference_number,
"party_type": transaction.party_type,
"party": transaction.party,
@@ -352,7 +439,9 @@ def check_matching(bank_account, company, transaction, document_types):
matching_vouchers = []
matching_vouchers.extend(get_loan_vouchers(bank_account, transaction, document_types, filters))
matching_vouchers.extend(
get_loan_vouchers(bank_account, transaction, document_types, filters, exact_match)
)
for query in subquery:
matching_vouchers.extend(
@@ -361,14 +450,23 @@ def check_matching(bank_account, company, transaction, document_types):
filters,
)
)
return sorted(matching_vouchers, key=lambda x: x[0], reverse=True) if matching_vouchers else []
def get_queries(bank_account, company, transaction, document_types):
def get_queries(
bank_account,
company,
transaction,
document_types,
from_date,
to_date,
filter_by_reference_date,
from_reference_date,
to_reference_date,
exact_match,
):
# get queries to get matching vouchers
amount_condition = "=" if "exact_match" in document_types else "<="
account_from_to = "paid_to" if transaction.deposit > 0 else "paid_from"
account_from_to = "paid_to" if transaction.deposit > 0.0 else "paid_from"
queries = []
# get matching queries from all the apps
@@ -379,8 +477,13 @@ def get_queries(bank_account, company, transaction, document_types):
company,
transaction,
document_types,
amount_condition,
exact_match,
account_from_to,
from_date,
to_date,
filter_by_reference_date,
from_reference_date,
to_reference_date,
)
or []
)
@@ -389,43 +492,106 @@ def get_queries(bank_account, company, transaction, document_types):
def get_matching_queries(
bank_account, company, transaction, document_types, amount_condition, account_from_to
bank_account,
company,
transaction,
document_types,
exact_match,
account_from_to,
from_date,
to_date,
filter_by_reference_date,
from_reference_date,
to_reference_date,
):
queries = []
if "payment_entry" in document_types:
pe_amount_matching = get_pe_matching_query(amount_condition, account_from_to, transaction)
queries.extend([pe_amount_matching])
query = get_pe_matching_query(
exact_match,
account_from_to,
transaction,
from_date,
to_date,
filter_by_reference_date,
from_reference_date,
to_reference_date,
)
queries.append(query)
if "journal_entry" in document_types:
je_amount_matching = get_je_matching_query(amount_condition, transaction)
queries.extend([je_amount_matching])
query = get_je_matching_query(
exact_match,
transaction,
from_date,
to_date,
filter_by_reference_date,
from_reference_date,
to_reference_date,
)
queries.append(query)
if transaction.deposit > 0 and "sales_invoice" in document_types:
si_amount_matching = get_si_matching_query(amount_condition)
queries.extend([si_amount_matching])
if transaction.deposit > 0.0 and "sales_invoice" in document_types:
query = get_si_matching_query(exact_match)
queries.append(query)
if transaction.withdrawal > 0:
if transaction.withdrawal > 0.0:
if "purchase_invoice" in document_types:
pi_amount_matching = get_pi_matching_query(amount_condition)
queries.extend([pi_amount_matching])
query = get_pi_matching_query(exact_match)
queries.append(query)
if "bank_transaction" in document_types:
query = get_bt_matching_query(exact_match, transaction)
queries.append(query)
return queries
def get_loan_vouchers(bank_account, transaction, document_types, filters):
def get_loan_vouchers(bank_account, transaction, document_types, filters, exact_match):
vouchers = []
amount_condition = True if "exact_match" in document_types else False
if transaction.withdrawal > 0 and "loan_disbursement" in document_types:
vouchers.extend(get_ld_matching_query(bank_account, amount_condition, filters))
if transaction.withdrawal > 0.0 and "loan_disbursement" in document_types:
vouchers.extend(get_ld_matching_query(bank_account, exact_match, filters))
if transaction.deposit > 0 and "loan_repayment" in document_types:
vouchers.extend(get_lr_matching_query(bank_account, amount_condition, filters))
if transaction.deposit > 0.0 and "loan_repayment" in document_types:
vouchers.extend(get_lr_matching_query(bank_account, exact_match, filters))
return vouchers
def get_ld_matching_query(bank_account, amount_condition, filters):
def get_bt_matching_query(exact_match, transaction):
# get matching bank transaction query
# find bank transactions in the same bank account with opposite sign
# same bank account must have same company and currency
field = "deposit" if transaction.withdrawal > 0.0 else "withdrawal"
return f"""
SELECT
(CASE WHEN reference_number = %(reference_no)s THEN 1 ELSE 0 END
+ CASE WHEN {field} = %(amount)s THEN 1 ELSE 0 END
+ CASE WHEN ( party_type = %(party_type)s AND party = %(party)s ) THEN 1 ELSE 0 END
+ CASE WHEN unallocated_amount = %(amount)s THEN 1 ELSE 0 END
+ 1) AS rank,
'Bank Transaction' AS doctype,
name,
unallocated_amount AS paid_amount,
reference_number AS reference_no,
date AS reference_date,
party,
party_type,
date AS posting_date,
currency
FROM
`tabBank Transaction`
WHERE
status != 'Reconciled'
AND name != '{transaction.name}'
AND bank_account = '{transaction.bank_account}'
AND {field} {'= %(amount)s' if exact_match else '> 0.0'}
"""
def get_ld_matching_query(bank_account, exact_match, filters):
loan_disbursement = frappe.qb.DocType("Loan Disbursement")
matching_reference = loan_disbursement.reference_number == filters.get("reference_number")
matching_party = loan_disbursement.applicant_type == filters.get(
@@ -453,17 +619,17 @@ def get_ld_matching_query(bank_account, amount_condition, filters):
.where(loan_disbursement.disbursement_account == bank_account)
)
if amount_condition:
if exact_match:
query.where(loan_disbursement.disbursed_amount == filters.get("amount"))
else:
query.where(loan_disbursement.disbursed_amount <= filters.get("amount"))
query.where(loan_disbursement.disbursed_amount > 0.0)
vouchers = query.run(as_list=True)
return vouchers
def get_lr_matching_query(bank_account, amount_condition, filters):
def get_lr_matching_query(bank_account, exact_match, filters):
loan_repayment = frappe.qb.DocType("Loan Repayment")
matching_reference = loan_repayment.reference_number == filters.get("reference_number")
matching_party = loan_repayment.applicant_type == filters.get(
@@ -494,88 +660,129 @@ def get_lr_matching_query(bank_account, amount_condition, filters):
if frappe.db.has_column("Loan Repayment", "repay_from_salary"):
query = query.where((loan_repayment.repay_from_salary == 0))
if amount_condition:
if exact_match:
query.where(loan_repayment.amount_paid == filters.get("amount"))
else:
query.where(loan_repayment.amount_paid <= filters.get("amount"))
query.where(loan_repayment.amount_paid > 0.0)
vouchers = query.run()
return vouchers
def get_pe_matching_query(amount_condition, account_from_to, transaction):
def get_pe_matching_query(
exact_match,
account_from_to,
transaction,
from_date,
to_date,
filter_by_reference_date,
from_reference_date,
to_reference_date,
):
# get matching payment entries query
if transaction.deposit > 0:
if transaction.deposit > 0.0:
currency_field = "paid_to_account_currency as currency"
else:
currency_field = "paid_from_account_currency as currency"
filter_by_date = f"AND posting_date between '{from_date}' and '{to_date}'"
order_by = " posting_date"
filter_by_reference_no = ""
if cint(filter_by_reference_date):
filter_by_date = f"AND reference_date between '{from_reference_date}' and '{to_reference_date}'"
order_by = " reference_date"
if frappe.flags.auto_reconcile_vouchers == True:
filter_by_reference_no = f"AND reference_no = '{transaction.reference_number}'"
return f"""
SELECT
(CASE WHEN reference_no=%(reference_no)s THEN 1 ELSE 0 END
+ CASE WHEN (party_type = %(party_type)s AND party = %(party)s ) THEN 1 ELSE 0 END
+ 1 ) AS rank,
'Payment Entry' as doctype,
name,
paid_amount,
reference_no,
reference_date,
party,
party_type,
posting_date,
{currency_field}
FROM
`tabPayment Entry`
WHERE
paid_amount {amount_condition} %(amount)s
AND docstatus = 1
AND payment_type IN (%(payment_type)s, 'Internal Transfer')
AND ifnull(clearance_date, '') = ""
AND {account_from_to} = %(bank_account)s
SELECT
(CASE WHEN reference_no=%(reference_no)s THEN 1 ELSE 0 END
+ CASE WHEN (party_type = %(party_type)s AND party = %(party)s ) THEN 1 ELSE 0 END
+ CASE WHEN paid_amount = %(amount)s THEN 1 ELSE 0 END
+ 1 ) AS rank,
'Payment Entry' as doctype,
name,
paid_amount,
reference_no,
reference_date,
party,
party_type,
posting_date,
{currency_field}
FROM
`tabPayment Entry`
WHERE
docstatus = 1
AND payment_type IN (%(payment_type)s, 'Internal Transfer')
AND ifnull(clearance_date, '') = ""
AND {account_from_to} = %(bank_account)s
AND paid_amount {'= %(amount)s' if exact_match else '> 0.0'}
{filter_by_date}
{filter_by_reference_no}
order by{order_by}
"""
def get_je_matching_query(amount_condition, transaction):
def get_je_matching_query(
exact_match,
transaction,
from_date,
to_date,
filter_by_reference_date,
from_reference_date,
to_reference_date,
):
# get matching journal entry query
# We have mapping at the bank level
# So one bank could have both types of bank accounts like asset and liability
# So cr_or_dr should be judged only on basis of withdrawal and deposit and not account type
cr_or_dr = "credit" if transaction.withdrawal > 0 else "debit"
cr_or_dr = "credit" if transaction.withdrawal > 0.0 else "debit"
filter_by_date = f"AND je.posting_date between '{from_date}' and '{to_date}'"
order_by = " je.posting_date"
filter_by_reference_no = ""
if cint(filter_by_reference_date):
filter_by_date = f"AND je.cheque_date between '{from_reference_date}' and '{to_reference_date}'"
order_by = " je.cheque_date"
if frappe.flags.auto_reconcile_vouchers == True:
filter_by_reference_no = f"AND je.cheque_no = '{transaction.reference_number}'"
return f"""
SELECT
(CASE WHEN je.cheque_no=%(reference_no)s THEN 1 ELSE 0 END
+ CASE WHEN jea.{cr_or_dr}_in_account_currency = %(amount)s THEN 1 ELSE 0 END
+ 1) AS rank ,
'Journal Entry' as doctype,
'Journal Entry' AS doctype,
je.name,
jea.{cr_or_dr}_in_account_currency as paid_amount,
je.cheque_no as reference_no,
je.cheque_date as reference_date,
je.pay_to_recd_from as party,
jea.{cr_or_dr}_in_account_currency AS paid_amount,
je.cheque_no AS reference_no,
je.cheque_date AS reference_date,
je.pay_to_recd_from AS party,
jea.party_type,
je.posting_date,
jea.account_currency as currency
jea.account_currency AS currency
FROM
`tabJournal Entry Account` as jea
`tabJournal Entry Account` AS jea
JOIN
`tabJournal Entry` as je
`tabJournal Entry` AS je
ON
jea.parent = je.name
WHERE
(je.clearance_date is null or je.clearance_date='0000-00-00')
je.docstatus = 1
AND je.voucher_type NOT IN ('Opening Entry')
AND (je.clearance_date IS NULL OR je.clearance_date='0000-00-00')
AND jea.account = %(bank_account)s
AND jea.{cr_or_dr}_in_account_currency {amount_condition} %(amount)s
AND jea.{cr_or_dr}_in_account_currency {'= %(amount)s' if exact_match else '> 0.0'}
AND je.docstatus = 1
{filter_by_date}
{filter_by_reference_no}
order by {order_by}
"""
def get_si_matching_query(amount_condition):
# get matchin sales invoice query
def get_si_matching_query(exact_match):
# get matching sales invoice query
return f"""
SELECT
( CASE WHEN si.customer = %(party)s THEN 1 ELSE 0 END
( CASE WHEN si.customer = %(party)s THEN 1 ELSE 0 END
+ CASE WHEN sip.amount = %(amount)s THEN 1 ELSE 0 END
+ 1 ) AS rank,
'Sales Invoice' as doctype,
si.name,
@@ -593,18 +800,20 @@ def get_si_matching_query(amount_condition):
`tabSales Invoice` as si
ON
sip.parent = si.name
WHERE (sip.clearance_date is null or sip.clearance_date='0000-00-00')
WHERE
si.docstatus = 1
AND (sip.clearance_date is null or sip.clearance_date='0000-00-00')
AND sip.account = %(bank_account)s
AND sip.amount {amount_condition} %(amount)s
AND si.docstatus = 1
AND sip.amount {'= %(amount)s' if exact_match else '> 0.0'}
"""
def get_pi_matching_query(amount_condition):
# get matching purchase invoice query
def get_pi_matching_query(exact_match):
# get matching purchase invoice query when they are also used as payment entries (is_paid)
return f"""
SELECT
( CASE WHEN supplier = %(party)s THEN 1 ELSE 0 END
+ CASE WHEN paid_amount = %(amount)s THEN 1 ELSE 0 END
+ 1 ) AS rank,
'Purchase Invoice' as doctype,
name,
@@ -618,9 +827,9 @@ def get_pi_matching_query(amount_condition):
FROM
`tabPurchase Invoice`
WHERE
paid_amount {amount_condition} %(amount)s
AND docstatus = 1
docstatus = 1
AND is_paid = 1
AND ifnull(clearance_date, '') = ""
AND cash_bank_account = %(bank_account)s
AND cash_bank_account = %(bank_account)s
AND paid_amount {'= %(amount)s' if exact_match else '> 0.0'}
"""

View File

@@ -12,8 +12,13 @@ frappe.ui.form.on("Bank Transaction", {
};
});
},
bank_account: function(frm) {
refresh(frm) {
frm.add_custom_button(__('Unreconcile Transaction'), () => {
frm.call('remove_payment_entries')
.then( () => frm.refresh() );
});
},
bank_account: function (frm) {
set_bank_statement_filter(frm);
},
@@ -34,6 +39,7 @@ frappe.ui.form.on("Bank Transaction", {
"Journal Entry",
"Sales Invoice",
"Purchase Invoice",
"Bank Transaction",
];
}
});
@@ -49,7 +55,7 @@ const update_clearance_date = (frm, cdt, cdn) => {
frappe
.xcall(
"erpnext.accounts.doctype.bank_transaction.bank_transaction.unclear_reference_payment",
{ doctype: cdt, docname: cdn }
{ doctype: cdt, docname: cdn, bt_name: frm.doc.name }
)
.then((e) => {
if (e == "success") {

View File

@@ -20,9 +20,11 @@
"currency",
"section_break_10",
"description",
"section_break_14",
"reference_number",
"column_break_10",
"transaction_id",
"transaction_type",
"section_break_14",
"payment_entries",
"section_break_18",
"allocated_amount",
@@ -190,11 +192,21 @@
"label": "Withdrawal",
"oldfieldname": "credit",
"options": "currency"
},
{
"fieldname": "column_break_10",
"fieldtype": "Column Break"
},
{
"fieldname": "transaction_type",
"fieldtype": "Data",
"label": "Transaction Type",
"length": 50
}
],
"is_submittable": 1,
"links": [],
"modified": "2022-03-21 19:05:04.208222",
"modified": "2022-05-29 18:36:50.475964",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Bank Transaction",
@@ -248,4 +260,4 @@
"states": [],
"title_field": "bank_account",
"track_changes": 1
}
}

View File

@@ -1,9 +1,6 @@
# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from functools import reduce
import frappe
from frappe.utils import flt
@@ -18,72 +15,135 @@ class BankTransaction(StatusUpdater):
self.clear_linked_payment_entries()
self.set_status()
_saving_flag = False
# nosemgrep: frappe-semgrep-rules.rules.frappe-modifying-but-not-comitting
def on_update_after_submit(self):
self.update_allocations()
self.clear_linked_payment_entries()
self.set_status(update=True)
"Run on save(). Avoid recursion caused by multiple saves"
if not self._saving_flag:
self._saving_flag = True
self.clear_linked_payment_entries()
self.update_allocations()
self._saving_flag = False
def on_cancel(self):
self.clear_linked_payment_entries(for_cancel=True)
self.set_status(update=True)
def update_allocations(self):
"The doctype does not allow modifications after submission, so write to the db direct"
if self.payment_entries:
allocated_amount = reduce(
lambda x, y: flt(x) + flt(y), [x.allocated_amount for x in self.payment_entries]
)
allocated_amount = sum(p.allocated_amount for p in self.payment_entries)
else:
allocated_amount = 0
allocated_amount = 0.0
if allocated_amount:
frappe.db.set_value(self.doctype, self.name, "allocated_amount", flt(allocated_amount))
frappe.db.set_value(
self.doctype,
self.name,
"unallocated_amount",
abs(flt(self.withdrawal) - flt(self.deposit)) - flt(allocated_amount),
)
amount = abs(flt(self.withdrawal) - flt(self.deposit))
self.db_set("allocated_amount", flt(allocated_amount))
self.db_set("unallocated_amount", amount - flt(allocated_amount))
self.reload()
self.set_status(update=True)
else:
frappe.db.set_value(self.doctype, self.name, "allocated_amount", 0)
frappe.db.set_value(
self.doctype, self.name, "unallocated_amount", abs(flt(self.withdrawal) - flt(self.deposit))
)
def add_payment_entries(self, vouchers):
"Add the vouchers with zero allocation. Save() will perform the allocations and clearance"
if 0.0 >= self.unallocated_amount:
frappe.throw(frappe._("Bank Transaction {0} is already fully reconciled").format(self.name))
amount = self.deposit or self.withdrawal
if amount == self.allocated_amount:
frappe.db.set_value(self.doctype, self.name, "status", "Reconciled")
added = False
for voucher in vouchers:
# Can't add same voucher twice
found = False
for pe in self.payment_entries:
if (
pe.payment_document == voucher["payment_doctype"]
and pe.payment_entry == voucher["payment_name"]
):
found = True
if not found:
pe = {
"payment_document": voucher["payment_doctype"],
"payment_entry": voucher["payment_name"],
"allocated_amount": 0.0, # Temporary
}
child = self.append("payment_entries", pe)
added = True
# runs on_update_after_submit
if added:
self.save()
def allocate_payment_entries(self):
"""Refactored from bank reconciliation tool.
Non-zero allocations must be amended/cleared manually
Get the bank transaction amount (b) and remove as we allocate
For each payment_entry if allocated_amount == 0:
- get the amount already allocated against all transactions (t), need latest date
- get the voucher amount (from gl) (v)
- allocate (a = v - t)
- a = 0: should already be cleared, so clear & remove payment_entry
- 0 < a <= u: allocate a & clear
- 0 < a, a > u: allocate u
- 0 > a: Error: already over-allocated
- clear means: set the latest transaction date as clearance date
"""
gl_bank_account = frappe.db.get_value("Bank Account", self.bank_account, "account")
remaining_amount = self.unallocated_amount
for payment_entry in self.payment_entries:
if payment_entry.allocated_amount == 0.0:
unallocated_amount, should_clear, latest_transaction = get_clearance_details(
self, payment_entry
)
if 0.0 == unallocated_amount:
if should_clear:
latest_transaction.clear_linked_payment_entry(payment_entry)
self.db_delete_payment_entry(payment_entry)
elif remaining_amount <= 0.0:
self.db_delete_payment_entry(payment_entry)
elif 0.0 < unallocated_amount and unallocated_amount <= remaining_amount:
payment_entry.db_set("allocated_amount", unallocated_amount)
remaining_amount -= unallocated_amount
if should_clear:
latest_transaction.clear_linked_payment_entry(payment_entry)
elif 0.0 < unallocated_amount and unallocated_amount > remaining_amount:
payment_entry.db_set("allocated_amount", remaining_amount)
remaining_amount = 0.0
elif 0.0 > unallocated_amount:
self.db_delete_payment_entry(payment_entry)
frappe.throw(frappe._("Voucher {0} is over-allocated by {1}").format(unallocated_amount))
self.reload()
def clear_linked_payment_entries(self, for_cancel=False):
def db_delete_payment_entry(self, payment_entry):
frappe.db.delete("Bank Transaction Payments", {"name": payment_entry.name})
@frappe.whitelist()
def remove_payment_entries(self):
for payment_entry in self.payment_entries:
if payment_entry.payment_document == "Sales Invoice":
self.clear_sales_invoice(payment_entry, for_cancel=for_cancel)
elif payment_entry.payment_document in get_doctypes_for_bank_reconciliation():
self.clear_simple_entry(payment_entry, for_cancel=for_cancel)
self.remove_payment_entry(payment_entry)
# runs on_update_after_submit
self.save()
def clear_simple_entry(self, payment_entry, for_cancel=False):
if payment_entry.payment_document == "Payment Entry":
if (
frappe.db.get_value("Payment Entry", payment_entry.payment_entry, "payment_type")
== "Internal Transfer"
):
if len(get_reconciled_bank_transactions(payment_entry)) < 2:
return
def remove_payment_entry(self, payment_entry):
"Clear payment entry and clearance"
self.clear_linked_payment_entry(payment_entry, for_cancel=True)
self.remove(payment_entry)
clearance_date = self.date if not for_cancel else None
frappe.db.set_value(
payment_entry.payment_document, payment_entry.payment_entry, "clearance_date", clearance_date
)
def clear_linked_payment_entries(self, for_cancel=False):
if for_cancel:
for payment_entry in self.payment_entries:
self.clear_linked_payment_entry(payment_entry, for_cancel)
else:
self.allocate_payment_entries()
def clear_sales_invoice(self, payment_entry, for_cancel=False):
clearance_date = self.date if not for_cancel else None
frappe.db.set_value(
"Sales Invoice Payment",
dict(parenttype=payment_entry.payment_document, parent=payment_entry.payment_entry),
"clearance_date",
clearance_date,
def clear_linked_payment_entry(self, payment_entry, for_cancel=False):
clearance_date = None if for_cancel else self.date
set_voucher_clearance(
payment_entry.payment_document, payment_entry.payment_entry, clearance_date, self
)
@@ -93,38 +153,114 @@ def get_doctypes_for_bank_reconciliation():
return frappe.get_hooks("bank_reconciliation_doctypes")
def get_reconciled_bank_transactions(payment_entry):
reconciled_bank_transactions = frappe.get_all(
"Bank Transaction Payments",
filters={"payment_entry": payment_entry.payment_entry},
fields=["parent"],
def get_clearance_details(transaction, payment_entry):
"""
There should only be one bank gle for a voucher.
Could be none for a Bank Transaction.
But if a JE, could affect two banks.
Should only clear the voucher if all bank gles are allocated.
"""
gl_bank_account = frappe.db.get_value("Bank Account", transaction.bank_account, "account")
gles = get_related_bank_gl_entries(payment_entry.payment_document, payment_entry.payment_entry)
bt_allocations = get_total_allocated_amount(
payment_entry.payment_document, payment_entry.payment_entry
)
return reconciled_bank_transactions
unallocated_amount = min(
transaction.unallocated_amount,
get_paid_amount(payment_entry, transaction.currency, gl_bank_account),
)
unmatched_gles = len(gles)
latest_transaction = transaction
for gle in gles:
if gle["gl_account"] == gl_bank_account:
if gle["amount"] <= 0.0:
frappe.throw(
frappe._("Voucher {0} value is broken: {1}").format(
payment_entry.payment_entry, gle["amount"]
)
)
unmatched_gles -= 1
unallocated_amount = gle["amount"]
for a in bt_allocations:
if a["gl_account"] == gle["gl_account"]:
unallocated_amount = gle["amount"] - a["total"]
if frappe.utils.getdate(transaction.date) < a["latest_date"]:
latest_transaction = frappe.get_doc("Bank Transaction", a["latest_name"])
else:
# Must be a Journal Entry affecting more than one bank
for a in bt_allocations:
if a["gl_account"] == gle["gl_account"] and a["total"] == gle["amount"]:
unmatched_gles -= 1
return unallocated_amount, unmatched_gles == 0, latest_transaction
def get_total_allocated_amount(payment_entry):
return frappe.db.sql(
def get_related_bank_gl_entries(doctype, docname):
# nosemgrep: frappe-semgrep-rules.rules.frappe-using-db-sql
result = frappe.db.sql(
"""
SELECT
SUM(btp.allocated_amount) as allocated_amount,
bt.name
ABS(gle.credit_in_account_currency - gle.debit_in_account_currency) AS amount,
gle.account AS gl_account
FROM
`tabBank Transaction Payments` as btp
`tabGL Entry` gle
LEFT JOIN
`tabBank Transaction` bt ON bt.name=btp.parent
`tabAccount` ac ON ac.name=gle.account
WHERE
btp.payment_document = %s
AND
btp.payment_entry = %s
AND
bt.docstatus = 1""",
(payment_entry.payment_document, payment_entry.payment_entry),
ac.account_type = 'Bank'
AND gle.voucher_type = %(doctype)s
AND gle.voucher_no = %(docname)s
AND is_cancelled = 0
""",
dict(doctype=doctype, docname=docname),
as_dict=True,
)
return result
def get_paid_amount(payment_entry, currency, bank_account):
def get_total_allocated_amount(doctype, docname):
"""
Gets the sum of allocations for a voucher on each bank GL account
along with the latest bank transaction name & date
NOTE: query may also include just saved vouchers/payments but with zero allocated_amount
"""
# nosemgrep: frappe-semgrep-rules.rules.frappe-using-db-sql
result = frappe.db.sql(
"""
SELECT total, latest_name, latest_date, gl_account FROM (
SELECT
ROW_NUMBER() OVER w AS rownum,
SUM(btp.allocated_amount) OVER(PARTITION BY ba.account) AS total,
FIRST_VALUE(bt.name) OVER w AS latest_name,
FIRST_VALUE(bt.date) OVER w AS latest_date,
ba.account AS gl_account
FROM
`tabBank Transaction Payments` btp
LEFT JOIN `tabBank Transaction` bt ON bt.name=btp.parent
LEFT JOIN `tabBank Account` ba ON ba.name=bt.bank_account
WHERE
btp.payment_document = %(doctype)s
AND btp.payment_entry = %(docname)s
AND bt.docstatus = 1
WINDOW w AS (PARTITION BY ba.account ORDER BY bt.date desc)
) temp
WHERE
rownum = 1
""",
dict(doctype=doctype, docname=docname),
as_dict=True,
)
for row in result:
# Why is this *sometimes* a byte string?
if isinstance(row["latest_name"], bytes):
row["latest_name"] = row["latest_name"].decode()
row["latest_date"] = frappe.utils.getdate(row["latest_date"])
return result
def get_paid_amount(payment_entry, currency, gl_bank_account):
if payment_entry.payment_document in ["Payment Entry", "Sales Invoice", "Purchase Invoice"]:
paid_amount_field = "paid_amount"
@@ -137,7 +273,7 @@ def get_paid_amount(payment_entry, currency, bank_account):
)
elif doc.payment_type == "Pay":
paid_amount_field = (
"paid_amount" if doc.paid_to_account_currency == currency else "base_paid_amount"
"paid_amount" if doc.paid_from_account_currency == currency else "base_paid_amount"
)
return frappe.db.get_value(
@@ -147,7 +283,7 @@ def get_paid_amount(payment_entry, currency, bank_account):
elif payment_entry.payment_document == "Journal Entry":
return frappe.db.get_value(
"Journal Entry Account",
{"parent": payment_entry.payment_entry, "account": bank_account},
{"parent": payment_entry.payment_entry, "account": gl_bank_account},
"sum(credit_in_account_currency)",
)
@@ -166,6 +302,12 @@ def get_paid_amount(payment_entry, currency, bank_account):
payment_entry.payment_document, payment_entry.payment_entry, "amount_paid"
)
elif payment_entry.payment_document == "Bank Transaction":
dep, wth = frappe.db.get_value(
"Bank Transaction", payment_entry.payment_entry, ("deposit", "withdrawal")
)
return abs(flt(wth) - flt(dep))
else:
frappe.throw(
"Please reconcile {0}: {1} manually".format(
@@ -174,18 +316,55 @@ def get_paid_amount(payment_entry, currency, bank_account):
)
@frappe.whitelist()
def unclear_reference_payment(doctype, docname):
if frappe.db.exists(doctype, docname):
doc = frappe.get_doc(doctype, docname)
if doctype == "Sales Invoice":
frappe.db.set_value(
"Sales Invoice Payment",
dict(parenttype=doc.payment_document, parent=doc.payment_entry),
"clearance_date",
None,
)
else:
frappe.db.set_value(doc.payment_document, doc.payment_entry, "clearance_date", None)
def set_voucher_clearance(doctype, docname, clearance_date, self):
if doctype in [
"Payment Entry",
"Journal Entry",
"Purchase Invoice",
"Expense Claim",
"Loan Repayment",
"Loan Disbursement",
]:
if (
doctype == "Payment Entry"
and frappe.db.get_value("Payment Entry", docname, "payment_type") == "Internal Transfer"
and len(get_reconciled_bank_transactions(doctype, docname)) < 2
):
return
frappe.db.set_value(doctype, docname, "clearance_date", clearance_date)
return doc.payment_entry
elif doctype == "Sales Invoice":
frappe.db.set_value(
"Sales Invoice Payment",
dict(parenttype=doctype, parent=docname),
"clearance_date",
clearance_date,
)
elif doctype == "Bank Transaction":
# For when a second bank transaction has fixed another, e.g. refund
bt = frappe.get_doc(doctype, docname)
if clearance_date:
vouchers = [{"payment_doctype": "Bank Transaction", "payment_name": self.name}]
bt.add_payment_entries(vouchers)
else:
for pe in bt.payment_entries:
if pe.payment_document == self.doctype and pe.payment_entry == self.name:
bt.remove(pe)
bt.save()
break
def get_reconciled_bank_transactions(doctype, docname):
return frappe.get_all(
"Bank Transaction Payments",
filters={"payment_document": doctype, "payment_entry": docname},
pluck="parent",
)
@frappe.whitelist()
def unclear_reference_payment(doctype, docname, bt_name):
bt = frappe.get_doc("Bank Transaction", bt_name)
set_voucher_clearance(doctype, docname, None, bt)
return docname

View File

@@ -5,6 +5,7 @@ import json
import unittest
import frappe
from frappe import utils
from frappe.tests.utils import FrappeTestCase
from erpnext.accounts.doctype.bank_reconciliation_tool.bank_reconciliation_tool import (
@@ -40,7 +41,12 @@ class TestBankTransaction(FrappeTestCase):
"Bank Transaction",
dict(description="Re 95282925234 FE/000002917 AT171513000281183046 Conrad Electronic"),
)
linked_payments = get_linked_payments(bank_transaction.name, ["payment_entry", "exact_match"])
linked_payments = get_linked_payments(
bank_transaction.name,
["payment_entry", "exact_match"],
from_date=bank_transaction.date,
to_date=utils.today(),
)
self.assertTrue(linked_payments[0][6] == "Conrad Electronic")
# This test validates a simple reconciliation leading to the clearance of the bank transaction and the payment
@@ -81,7 +87,12 @@ class TestBankTransaction(FrappeTestCase):
"Bank Transaction",
dict(description="Auszahlung Karte MC/000002916 AUTOMAT 698769 K002 27.10. 14:07"),
)
linked_payments = get_linked_payments(bank_transaction.name, ["payment_entry", "exact_match"])
linked_payments = get_linked_payments(
bank_transaction.name,
["payment_entry", "exact_match"],
from_date=bank_transaction.date,
to_date=utils.today(),
)
self.assertTrue(linked_payments[0][3])
# Check error if already reconciled

View File

@@ -184,6 +184,11 @@ def validate_budget_records(args, budget_records, expense_amount):
amount = expense_amount or get_amount(args, budget)
yearly_action, monthly_action = get_actions(args, budget)
if yearly_action in ("Stop", "Warn"):
compare_expense_with_budget(
args, flt(budget.budget_amount), _("Annual"), yearly_action, budget.budget_against, amount
)
if monthly_action in ["Stop", "Warn"]:
budget_amount = get_accumulated_monthly_budget(
budget.monthly_distribution, args.posting_date, args.fiscal_year, budget.budget_amount
@@ -195,28 +200,28 @@ def validate_budget_records(args, budget_records, expense_amount):
args, budget_amount, _("Accumulated Monthly"), monthly_action, budget.budget_against, amount
)
if (
yearly_action in ("Stop", "Warn")
and monthly_action != "Stop"
and yearly_action != monthly_action
):
compare_expense_with_budget(
args, flt(budget.budget_amount), _("Annual"), yearly_action, budget.budget_against, amount
)
def compare_expense_with_budget(args, budget_amount, action_for, action, budget_against, amount=0):
actual_expense = amount or get_actual_expense(args)
if actual_expense > budget_amount:
diff = actual_expense - budget_amount
actual_expense = get_actual_expense(args)
total_expense = actual_expense + amount
if total_expense > budget_amount:
if actual_expense > budget_amount:
error_tense = _("is already")
diff = actual_expense - budget_amount
else:
error_tense = _("will be")
diff = total_expense - budget_amount
currency = frappe.get_cached_value("Company", args.company, "default_currency")
msg = _("{0} Budget for Account {1} against {2} {3} is {4}. It will exceed by {5}").format(
msg = _("{0} Budget for Account {1} against {2} {3} is {4}. It {5} exceed by {6}").format(
_(action_for),
frappe.bold(args.account),
args.budget_against_field,
frappe.unscrub(args.budget_against_field),
frappe.bold(budget_against),
frappe.bold(fmt_money(budget_amount, currency=currency)),
error_tense,
frappe.bold(fmt_money(diff, currency=currency)),
)
@@ -227,9 +232,9 @@ def compare_expense_with_budget(args, budget_amount, action_for, action, budget_
action = "Warn"
if action == "Stop":
frappe.throw(msg, BudgetError)
frappe.throw(msg, BudgetError, title=_("Budget Exceeded"))
else:
frappe.msgprint(msg, indicator="orange")
frappe.msgprint(msg, indicator="orange", title=_("Budget Exceeded"))
def get_actions(args, budget):
@@ -351,7 +356,9 @@ def get_actual_expense(args):
"""
select sum(gle.debit) - sum(gle.credit)
from `tabGL Entry` gle
where gle.account=%(account)s
where
is_cancelled = 0
and gle.account=%(account)s
{condition1}
and gle.fiscal_year=%(fiscal_year)s
and gle.company=%(company)s

View File

@@ -36,7 +36,7 @@ def validate_columns(data):
no_of_columns = max([len(d) for d in data])
if no_of_columns > 7:
if no_of_columns > 8:
frappe.throw(
_("More columns found than expected. Please compare the uploaded file with standard template"),
title=(_("Wrong Template")),
@@ -233,6 +233,7 @@ def build_forest(data):
is_group,
account_type,
root_type,
account_currency,
) = i
if not account_name:
@@ -253,6 +254,8 @@ def build_forest(data):
charts_map[account_name]["account_type"] = account_type
if root_type:
charts_map[account_name]["root_type"] = root_type
if account_currency:
charts_map[account_name]["account_currency"] = account_currency
path = return_parent(data, account_name)[::-1]
paths.append(path) # List of path is created
line_no += 1
@@ -315,20 +318,21 @@ def get_template(template_type):
"Is Group",
"Account Type",
"Root Type",
"Account Currency",
]
writer = UnicodeWriter()
writer.writerow(fields)
if template_type == "Blank Template":
for root_type in get_root_types():
writer.writerow(["", "", "", 1, "", root_type])
writer.writerow(["", "", "", "", 1, "", root_type])
for account in get_mandatory_group_accounts():
writer.writerow(["", "", "", 1, account, "Asset"])
writer.writerow(["", "", "", "", 1, account, "Asset"])
for account_type in get_mandatory_account_types():
writer.writerow(
["", "", "", 0, account_type.get("account_type"), account_type.get("root_type")]
["", "", "", "", 0, account_type.get("account_type"), account_type.get("root_type")]
)
else:
writer = get_sample_template(writer)
@@ -485,6 +489,10 @@ def set_default_accounts(company):
"default_payable_account": frappe.db.get_value(
"Account", {"company": company.name, "account_type": "Payable", "is_group": 0}
),
"default_provisional_account": frappe.db.get_value(
"Account",
{"company": company.name, "account_type": "Service Received But Not Billed", "is_group": 0},
),
}
)

View File

@@ -28,9 +28,14 @@ class InvalidDateError(frappe.ValidationError):
class CostCenterAllocation(Document):
def __init__(self, *args, **kwargs):
super(CostCenterAllocation, self).__init__(*args, **kwargs)
self._skip_from_date_validation = False
def validate(self):
self.validate_total_allocation_percentage()
self.validate_from_date_based_on_existing_gle()
if not self._skip_from_date_validation:
self.validate_from_date_based_on_existing_gle()
self.validate_backdated_allocation()
self.validate_main_cost_center()
self.validate_child_cost_centers()

View File

@@ -6,6 +6,7 @@
"engine": "InnoDB",
"field_order": [
"api_details_section",
"disabled",
"service_provider",
"api_endpoint",
"url",
@@ -77,12 +78,18 @@
"label": "Service Provider",
"options": "frankfurter.app\nexchangerate.host\nCustom",
"reqd": 1
},
{
"default": "0",
"fieldname": "disabled",
"fieldtype": "Check",
"label": "Disabled"
}
],
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
"modified": "2022-01-10 15:51:14.521174",
"modified": "2023-01-09 12:19:03.955906",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Currency Exchange Settings",

View File

@@ -40,7 +40,7 @@ class Dunning(AccountsController):
def on_cancel(self):
if self.dunning_amount:
self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry")
self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Payment Ledger Entry")
make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
def make_gl_entries(self):

View File

@@ -26,7 +26,7 @@ frappe.ui.form.on('Exchange Rate Revaluation', {
doc: frm.doc,
callback: function(r) {
if (r.message) {
frm.add_custom_button(__('Journal Entry'), function() {
frm.add_custom_button(__('Journal Entries'), function() {
return frm.events.make_jv(frm);
}, __('Create'));
}
@@ -35,10 +35,11 @@ frappe.ui.form.on('Exchange Rate Revaluation', {
}
},
get_entries: function(frm) {
get_entries: function(frm, account) {
frappe.call({
method: "get_accounts_data",
doc: cur_frm.doc,
account: account,
callback: function(r){
frappe.model.clear_table(frm.doc, "accounts");
if(r.message) {
@@ -57,7 +58,6 @@ frappe.ui.form.on('Exchange Rate Revaluation', {
let total_gain_loss = 0;
frm.doc.accounts.forEach((d) => {
d.gain_loss = flt(d.new_balance_in_base_currency, precision("new_balance_in_base_currency", d)) - flt(d.balance_in_base_currency, precision("balance_in_base_currency", d));
total_gain_loss += flt(d.gain_loss, precision("gain_loss", d));
});
@@ -66,13 +66,19 @@ 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_entry",
method: "make_jv_entries",
doc: frm.doc,
freeze: true,
freeze_message: "Making Journal Entries...",
callback: function(r){
if (r.message) {
var doc = frappe.model.sync(r.message)[0];
frappe.set_route("Form", doc.doctype, doc.name);
let response = r.message;
if(response['revaluation_jv'] || response['zero_balance_jv']) {
frappe.msgprint(__("Journals have been created"));
}
}
}
});

View File

@@ -14,6 +14,9 @@
"get_entries",
"accounts",
"section_break_6",
"gain_loss_unbooked",
"gain_loss_booked",
"column_break_10",
"total_gain_loss",
"amended_from"
],
@@ -59,13 +62,6 @@
"fieldname": "section_break_6",
"fieldtype": "Section Break"
},
{
"fieldname": "total_gain_loss",
"fieldtype": "Currency",
"label": "Total Gain/Loss",
"options": "Company:company:default_currency",
"read_only": 1
},
{
"fieldname": "amended_from",
"fieldtype": "Link",
@@ -74,11 +70,37 @@
"options": "Exchange Rate Revaluation",
"print_hide": 1,
"read_only": 1
},
{
"fieldname": "gain_loss_unbooked",
"fieldtype": "Currency",
"label": "Gain/Loss from Revaluation",
"options": "Company:company:default_currency",
"read_only": 1
},
{
"description": "Gain/Loss accumulated in foreign currency account. Accounts with '0' balance in either Base or Account currency",
"fieldname": "gain_loss_booked",
"fieldtype": "Currency",
"label": "Gain/Loss already booked",
"options": "Company:company:default_currency",
"read_only": 1
},
{
"fieldname": "total_gain_loss",
"fieldtype": "Currency",
"label": "Total Gain/Loss",
"options": "Company:company:default_currency",
"read_only": 1
},
{
"fieldname": "column_break_10",
"fieldtype": "Column Break"
}
],
"is_submittable": 1,
"links": [],
"modified": "2022-11-17 10:28:03.911554",
"modified": "2022-12-29 19:38:24.416529",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Exchange Rate Revaluation",

View File

@@ -3,10 +3,12 @@
import frappe
from frappe import _
from frappe import _, qb
from frappe.model.document import Document
from frappe.model.meta import get_field_precision
from frappe.utils import flt
from frappe.query_builder import Criterion, Order
from frappe.query_builder.functions import NullIf, Sum
from frappe.utils import flt, get_link_to_form
import erpnext
from erpnext.accounts.doctype.journal_entry.journal_entry import get_balance_on
@@ -19,11 +21,25 @@ class ExchangeRateRevaluation(Document):
def set_total_gain_loss(self):
total_gain_loss = 0
gain_loss_booked = 0
gain_loss_unbooked = 0
for d in self.accounts:
d.gain_loss = flt(
d.new_balance_in_base_currency, d.precision("new_balance_in_base_currency")
) - flt(d.balance_in_base_currency, d.precision("balance_in_base_currency"))
if not d.zero_balance:
d.gain_loss = flt(
d.new_balance_in_base_currency, d.precision("new_balance_in_base_currency")
) - flt(d.balance_in_base_currency, d.precision("balance_in_base_currency"))
if d.zero_balance:
gain_loss_booked += flt(d.gain_loss, d.precision("gain_loss"))
else:
gain_loss_unbooked += flt(d.gain_loss, d.precision("gain_loss"))
total_gain_loss += flt(d.gain_loss, d.precision("gain_loss"))
self.gain_loss_booked = gain_loss_booked
self.gain_loss_unbooked = gain_loss_unbooked
self.total_gain_loss = flt(total_gain_loss, self.precision("total_gain_loss"))
def validate_mandatory(self):
@@ -35,98 +51,205 @@ class ExchangeRateRevaluation(Document):
@frappe.whitelist()
def check_journal_entry_condition(self):
total_debit = frappe.db.get_value(
"Journal Entry Account",
{"reference_type": "Exchange Rate Revaluation", "reference_name": self.name, "docstatus": 1},
"sum(debit) as sum",
exchange_gain_loss_account = self.get_for_unrealized_gain_loss_account()
jea = qb.DocType("Journal Entry Account")
journals = (
qb.from_(jea)
.select(jea.parent)
.distinct()
.where(
(jea.reference_type == "Exchange Rate Revaluation")
& (jea.reference_name == self.name)
& (jea.docstatus == 1)
)
.run()
)
total_amt = 0
for d in self.accounts:
total_amt = total_amt + d.new_balance_in_base_currency
if journals:
gle = qb.DocType("GL Entry")
total_amt = (
qb.from_(gle)
.select((Sum(gle.credit) - Sum(gle.debit)).as_("total_amount"))
.where(
(gle.voucher_type == "Journal Entry")
& (gle.voucher_no.isin(journals))
& (gle.account == exchange_gain_loss_account)
& (gle.is_cancelled == 0)
)
.run()
)
if total_amt != total_debit:
return True
if total_amt and total_amt[0][0] != self.total_gain_loss:
return True
else:
return False
return False
return True
@frappe.whitelist()
def get_accounts_data(self, account=None):
accounts = []
def get_accounts_data(self):
self.validate_mandatory()
company_currency = erpnext.get_company_currency(self.company)
account_details = self.get_account_balance_from_gle(
company=self.company, posting_date=self.posting_date, account=None, party_type=None, party=None
)
accounts_with_new_balance = self.calculate_new_account_balance(
self.company, self.posting_date, account_details
)
if not accounts_with_new_balance:
self.throw_invalid_response_message(account_details)
return accounts_with_new_balance
@staticmethod
def get_account_balance_from_gle(company, posting_date, account, party_type, party):
account_details = []
if company and posting_date:
company_currency = erpnext.get_company_currency(company)
acc = qb.DocType("Account")
if account:
accounts = [account]
else:
res = (
qb.from_(acc)
.select(acc.name)
.where(
(acc.is_group == 0)
& (acc.report_type == "Balance Sheet")
& (acc.root_type.isin(["Asset", "Liability", "Equity"]))
& (acc.account_type != "Stock")
& (acc.company == company)
& (acc.account_currency != company_currency)
)
.orderby(acc.name)
.run(as_list=True)
)
accounts = [x[0] for x in res]
if accounts:
having_clause = (qb.Field("balance") != qb.Field("balance_in_account_currency")) & (
(qb.Field("balance_in_account_currency") != 0) | (qb.Field("balance") != 0)
)
gle = qb.DocType("GL Entry")
# conditions
conditions = []
conditions.append(gle.account.isin(accounts))
conditions.append(gle.posting_date.lte(posting_date))
conditions.append(gle.is_cancelled == 0)
if party_type:
conditions.append(gle.party_type == party_type)
if party:
conditions.append(gle.party == party)
account_details = (
qb.from_(gle)
.select(
gle.account,
gle.party_type,
gle.party,
gle.account_currency,
(Sum(gle.debit_in_account_currency) - Sum(gle.credit_in_account_currency)).as_(
"balance_in_account_currency"
),
(Sum(gle.debit) - Sum(gle.credit)).as_("balance"),
(Sum(gle.debit) - Sum(gle.credit) == 0)
^ (Sum(gle.debit_in_account_currency) - Sum(gle.credit_in_account_currency) == 0).as_(
"zero_balance"
),
)
.where(Criterion.all(conditions))
.groupby(gle.account, NullIf(gle.party_type, ""), NullIf(gle.party, ""))
.having(having_clause)
.orderby(gle.account)
.run(as_dict=True)
)
return account_details
@staticmethod
def calculate_new_account_balance(company, posting_date, account_details):
accounts = []
company_currency = erpnext.get_company_currency(company)
precision = get_field_precision(
frappe.get_meta("Exchange Rate Revaluation Account").get_field("new_balance_in_base_currency"),
company_currency,
)
account_details = self.get_accounts_from_gle()
for d in account_details:
current_exchange_rate = (
d.balance / d.balance_in_account_currency if d.balance_in_account_currency else 0
)
new_exchange_rate = get_exchange_rate(d.account_currency, company_currency, self.posting_date)
new_balance_in_base_currency = flt(d.balance_in_account_currency * new_exchange_rate)
gain_loss = flt(new_balance_in_base_currency, precision) - flt(d.balance, precision)
if gain_loss:
accounts.append(
{
"account": d.account,
"party_type": d.party_type,
"party": d.party,
"account_currency": d.account_currency,
"balance_in_base_currency": d.balance,
"balance_in_account_currency": d.balance_in_account_currency,
"current_exchange_rate": current_exchange_rate,
"new_exchange_rate": new_exchange_rate,
"new_balance_in_base_currency": new_balance_in_base_currency,
}
if account_details:
# Handle Accounts with balance in both Account/Base Currency
for d in [x for x in account_details if not x.zero_balance]:
current_exchange_rate = (
d.balance / d.balance_in_account_currency if d.balance_in_account_currency else 0
)
new_exchange_rate = get_exchange_rate(d.account_currency, company_currency, posting_date)
new_balance_in_base_currency = flt(d.balance_in_account_currency * new_exchange_rate)
gain_loss = flt(new_balance_in_base_currency, precision) - flt(d.balance, precision)
if gain_loss:
accounts.append(
{
"account": d.account,
"party_type": d.party_type,
"party": d.party,
"account_currency": d.account_currency,
"balance_in_base_currency": d.balance,
"balance_in_account_currency": d.balance_in_account_currency,
"zero_balance": d.zero_balance,
"current_exchange_rate": current_exchange_rate,
"new_exchange_rate": new_exchange_rate,
"new_balance_in_base_currency": new_balance_in_base_currency,
"new_balance_in_account_currency": d.balance_in_account_currency,
"gain_loss": gain_loss,
}
)
if not accounts:
self.throw_invalid_response_message(account_details)
# Handle Accounts with '0' balance in Account/Base Currency
for d in [x for x in account_details if x.zero_balance]:
if d.balance != 0:
current_exchange_rate = new_exchange_rate = 0
new_balance_in_account_currency = 0 # this will be '0'
new_balance_in_base_currency = 0 # this will be '0'
gain_loss = flt(new_balance_in_base_currency, precision) - flt(d.balance, precision)
else:
new_exchange_rate = 0
new_balance_in_base_currency = 0
new_balance_in_account_currency = 0
current_exchange_rate = calculate_exchange_rate_using_last_gle(
company, d.account, d.party_type, d.party
)
gain_loss = new_balance_in_account_currency - (
current_exchange_rate * d.balance_in_account_currency
)
if gain_loss:
accounts.append(
{
"account": d.account,
"party_type": d.party_type,
"party": d.party,
"account_currency": d.account_currency,
"balance_in_base_currency": d.balance,
"balance_in_account_currency": d.balance_in_account_currency,
"zero_balance": d.zero_balance,
"current_exchange_rate": current_exchange_rate,
"new_exchange_rate": new_exchange_rate,
"new_balance_in_base_currency": new_balance_in_base_currency,
"new_balance_in_account_currency": new_balance_in_account_currency,
"gain_loss": gain_loss,
}
)
return accounts
def get_accounts_from_gle(self):
company_currency = erpnext.get_company_currency(self.company)
accounts = frappe.db.sql_list(
"""
select name
from tabAccount
where is_group = 0
and report_type = 'Balance Sheet'
and root_type in ('Asset', 'Liability', 'Equity')
and account_type != 'Stock'
and company=%s
and account_currency != %s
order by name""",
(self.company, company_currency),
)
account_details = []
if accounts:
account_details = frappe.db.sql(
"""
select
account, party_type, party, account_currency,
sum(debit_in_account_currency) - sum(credit_in_account_currency) as balance_in_account_currency,
sum(debit) - sum(credit) as balance
from `tabGL Entry`
where account in (%s)
and posting_date <= %s
and is_cancelled = 0
group by account, NULLIF(party_type,''), NULLIF(party,'')
having sum(debit) != sum(credit)
order by account
"""
% (", ".join(["%s"] * len(accounts)), "%s"),
tuple(accounts + [self.posting_date]),
as_dict=1,
)
return account_details
def throw_invalid_response_message(self, account_details):
if account_details:
message = _("No outstanding invoices require exchange rate revaluation")
@@ -134,11 +257,7 @@ class ExchangeRateRevaluation(Document):
message = _("No outstanding invoices found")
frappe.msgprint(message)
@frappe.whitelist()
def make_jv_entry(self):
if self.total_gain_loss == 0:
return
def get_for_unrealized_gain_loss_account(self):
unrealized_exchange_gain_loss_account = frappe.get_cached_value(
"Company", self.company, "unrealized_exchange_gain_loss_account"
)
@@ -146,6 +265,130 @@ class ExchangeRateRevaluation(Document):
frappe.throw(
_("Please set Unrealized Exchange Gain/Loss Account in Company {0}").format(self.company)
)
return unrealized_exchange_gain_loss_account
@frappe.whitelist()
def make_jv_entries(self):
zero_balance_jv = self.make_jv_for_zero_balance()
if zero_balance_jv:
frappe.msgprint(
f"Zero Balance Journal: {get_link_to_form('Journal Entry', zero_balance_jv.name)}"
)
revaluation_jv = self.make_jv_for_revaluation()
if revaluation_jv:
frappe.msgprint(
f"Revaluation Journal: {get_link_to_form('Journal Entry', revaluation_jv.name)}"
)
return {
"revaluation_jv": revaluation_jv.name if revaluation_jv else None,
"zero_balance_jv": zero_balance_jv.name if zero_balance_jv else None,
}
def make_jv_for_zero_balance(self):
if self.gain_loss_booked == 0:
return
accounts = [x for x in self.accounts if x.zero_balance]
if not accounts:
return
unrealized_exchange_gain_loss_account = self.get_for_unrealized_gain_loss_account()
journal_entry = frappe.new_doc("Journal Entry")
journal_entry.voucher_type = "Exchange Gain Or Loss"
journal_entry.company = self.company
journal_entry.posting_date = self.posting_date
journal_entry.multi_currency = 1
journal_entry_accounts = []
for d in accounts:
journal_account = frappe._dict(
{
"account": d.get("account"),
"party_type": d.get("party_type"),
"party": d.get("party"),
"account_currency": d.get("account_currency"),
"balance": flt(
d.get("balance_in_account_currency"), d.precision("balance_in_account_currency")
),
"exchange_rate": 0,
"cost_center": erpnext.get_default_cost_center(self.company),
"reference_type": "Exchange Rate Revaluation",
"reference_name": self.name,
}
)
# Account Currency has balance
if d.get("balance_in_account_currency") and not d.get("new_balance_in_account_currency"):
dr_or_cr = (
"credit_in_account_currency"
if d.get("balance_in_account_currency") > 0
else "debit_in_account_currency"
)
reverse_dr_or_cr = (
"debit_in_account_currency"
if dr_or_cr == "credit_in_account_currency"
else "credit_in_account_currency"
)
journal_account.update(
{
dr_or_cr: flt(
abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")
),
reverse_dr_or_cr: 0,
"debit": 0,
"credit": 0,
}
)
elif d.get("balance_in_base_currency") and not d.get("new_balance_in_base_currency"):
# Base currency has balance
dr_or_cr = "credit" if d.get("balance_in_base_currency") > 0 else "debit"
reverse_dr_or_cr = "debit" if dr_or_cr == "credit" else "credit"
journal_account.update(
{
dr_or_cr: flt(
abs(d.get("balance_in_base_currency")), d.precision("balance_in_base_currency")
),
reverse_dr_or_cr: 0,
"debit_in_account_currency": 0,
"credit_in_account_currency": 0,
}
)
journal_entry_accounts.append(journal_account)
journal_entry_accounts.append(
{
"account": unrealized_exchange_gain_loss_account,
"balance": get_balance_on(unrealized_exchange_gain_loss_account),
"debit": abs(self.gain_loss_booked) if self.gain_loss_booked < 0 else 0,
"credit": abs(self.gain_loss_booked) if self.gain_loss_booked > 0 else 0,
"debit_in_account_currency": abs(self.gain_loss_booked) if self.gain_loss_booked < 0 else 0,
"credit_in_account_currency": self.gain_loss_booked if self.gain_loss_booked > 0 else 0,
"cost_center": erpnext.get_default_cost_center(self.company),
"exchange_rate": 1,
"reference_type": "Exchange Rate Revaluation",
"reference_name": self.name,
}
)
journal_entry.set("accounts", journal_entry_accounts)
journal_entry.set_total_debit_credit()
journal_entry.save()
return journal_entry
def make_jv_for_revaluation(self):
if self.gain_loss_unbooked == 0:
return
accounts = [x for x in self.accounts if not x.zero_balance]
if not accounts:
return
unrealized_exchange_gain_loss_account = self.get_for_unrealized_gain_loss_account()
journal_entry = frappe.new_doc("Journal Entry")
journal_entry.voucher_type = "Exchange Rate Revaluation"
@@ -154,7 +397,10 @@ class ExchangeRateRevaluation(Document):
journal_entry.multi_currency = 1
journal_entry_accounts = []
for d in self.accounts:
for d in accounts:
if not flt(d.get("balance_in_account_currency"), d.precision("balance_in_account_currency")):
continue
dr_or_cr = (
"debit_in_account_currency"
if d.get("balance_in_account_currency") > 0
@@ -179,6 +425,7 @@ class ExchangeRateRevaluation(Document):
dr_or_cr: flt(
abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")
),
"cost_center": erpnext.get_default_cost_center(self.company),
"exchange_rate": flt(d.get("new_exchange_rate"), d.precision("new_exchange_rate")),
"reference_type": "Exchange Rate Revaluation",
"reference_name": self.name,
@@ -196,63 +443,122 @@ class ExchangeRateRevaluation(Document):
reverse_dr_or_cr: flt(
abs(d.get("balance_in_account_currency")), d.precision("balance_in_account_currency")
),
"cost_center": erpnext.get_default_cost_center(self.company),
"exchange_rate": flt(d.get("current_exchange_rate"), d.precision("current_exchange_rate")),
"reference_type": "Exchange Rate Revaluation",
"reference_name": self.name,
}
)
journal_entry_accounts.append(
{
"account": unrealized_exchange_gain_loss_account,
"balance": get_balance_on(unrealized_exchange_gain_loss_account),
"debit_in_account_currency": abs(self.total_gain_loss) if self.total_gain_loss < 0 else 0,
"credit_in_account_currency": self.total_gain_loss if self.total_gain_loss > 0 else 0,
"exchange_rate": 1,
"reference_type": "Exchange Rate Revaluation",
"reference_name": self.name,
}
)
journal_entry.set("accounts", journal_entry_accounts)
journal_entry.set_amounts_in_company_currency()
journal_entry.set_total_debit_credit()
return journal_entry.as_dict()
self.gain_loss_unbooked += journal_entry.difference - self.gain_loss_unbooked
journal_entry.append(
"accounts",
{
"account": unrealized_exchange_gain_loss_account,
"balance": get_balance_on(unrealized_exchange_gain_loss_account),
"debit_in_account_currency": abs(self.gain_loss_unbooked)
if self.gain_loss_unbooked < 0
else 0,
"credit_in_account_currency": self.gain_loss_unbooked if self.gain_loss_unbooked > 0 else 0,
"cost_center": erpnext.get_default_cost_center(self.company),
"exchange_rate": 1,
"reference_type": "Exchange Rate Revaluation",
"reference_name": self.name,
},
)
journal_entry.set_amounts_in_company_currency()
journal_entry.set_total_debit_credit()
journal_entry.save()
return journal_entry
def calculate_exchange_rate_using_last_gle(company, account, party_type, party):
"""
Use last GL entry to calculate exchange rate
"""
last_exchange_rate = None
if company and account:
gl = qb.DocType("GL Entry")
# build conditions
conditions = []
conditions.append(gl.company == company)
conditions.append(gl.account == account)
conditions.append(gl.is_cancelled == 0)
conditions.append((gl.debit > 0) | (gl.credit > 0))
conditions.append((gl.debit_in_account_currency > 0) | (gl.credit_in_account_currency > 0))
if party_type:
conditions.append(gl.party_type == party_type)
if party:
conditions.append(gl.party == party)
voucher_type, voucher_no = (
qb.from_(gl)
.select(gl.voucher_type, gl.voucher_no)
.where(Criterion.all(conditions))
.orderby(gl.posting_date, order=Order.desc)
.limit(1)
.run()[0]
)
last_exchange_rate = (
qb.from_(gl)
.select((gl.debit - gl.credit) / (gl.debit_in_account_currency - gl.credit_in_account_currency))
.where(
(gl.voucher_type == voucher_type) & (gl.voucher_no == voucher_no) & (gl.account == account)
)
.orderby(gl.posting_date, order=Order.desc)
.limit(1)
.run()[0][0]
)
return last_exchange_rate
@frappe.whitelist()
def get_account_details(account, company, posting_date, party_type=None, party=None):
def get_account_details(company, posting_date, account, party_type=None, party=None):
if not (company and posting_date):
frappe.throw(_("Company and Posting Date is mandatory"))
account_currency, account_type = frappe.get_cached_value(
"Account", account, ["account_currency", "account_type"]
)
if account_type in ["Receivable", "Payable"] and not (party_type and party):
frappe.throw(_("Party Type and Party is mandatory for {0} account").format(account_type))
account_details = {}
company_currency = erpnext.get_company_currency(company)
balance = get_balance_on(
account, date=posting_date, party_type=party_type, party=party, in_account_currency=False
)
account_details = {
"account_currency": account_currency,
}
account_balance = ExchangeRateRevaluation.get_account_balance_from_gle(
company=company, posting_date=posting_date, account=account, party_type=party_type, party=party
)
if balance:
balance_in_account_currency = get_balance_on(
account, date=posting_date, party_type=party_type, party=party
if account_balance and (
account_balance[0].balance or account_balance[0].balance_in_account_currency
):
account_with_new_balance = ExchangeRateRevaluation.calculate_new_account_balance(
company, posting_date, account_balance
)
current_exchange_rate = (
balance / balance_in_account_currency if balance_in_account_currency else 0
)
new_exchange_rate = get_exchange_rate(account_currency, company_currency, posting_date)
new_balance_in_base_currency = balance_in_account_currency * new_exchange_rate
account_details = account_details.update(
row = account_with_new_balance[0]
account_details.update(
{
"balance_in_base_currency": balance,
"balance_in_account_currency": balance_in_account_currency,
"current_exchange_rate": current_exchange_rate,
"new_exchange_rate": new_exchange_rate,
"new_balance_in_base_currency": new_balance_in_base_currency,
"balance_in_base_currency": row["balance_in_base_currency"],
"balance_in_account_currency": row["balance_in_account_currency"],
"current_exchange_rate": row["current_exchange_rate"],
"new_exchange_rate": row["new_exchange_rate"],
"new_balance_in_base_currency": row["new_balance_in_base_currency"],
"new_balance_in_account_currency": row["new_balance_in_account_currency"],
"zero_balance": row["zero_balance"],
"gain_loss": row["gain_loss"],
}
)

View File

@@ -10,14 +10,21 @@
"party",
"column_break_2",
"account_currency",
"account_balances",
"balance_in_account_currency",
"column_break_46yz",
"new_balance_in_account_currency",
"balances",
"current_exchange_rate",
"balance_in_base_currency",
"column_break_9",
"column_break_xown",
"new_exchange_rate",
"column_break_9",
"balance_in_base_currency",
"column_break_ukce",
"new_balance_in_base_currency",
"gain_loss"
"section_break_ngrs",
"gain_loss",
"zero_balance"
],
"fields": [
{
@@ -78,7 +85,7 @@
},
{
"fieldname": "column_break_9",
"fieldtype": "Column Break"
"fieldtype": "Section Break"
},
{
"fieldname": "new_exchange_rate",
@@ -102,11 +109,45 @@
"label": "Gain/Loss",
"options": "Company:company:default_currency",
"read_only": 1
},
{
"default": "0",
"description": "This Account has '0' balance in either Base Currency or Account Currency",
"fieldname": "zero_balance",
"fieldtype": "Check",
"label": "Zero Balance"
},
{
"fieldname": "new_balance_in_account_currency",
"fieldtype": "Currency",
"label": "New Balance In Account Currency",
"options": "account_currency",
"read_only": 1
},
{
"fieldname": "account_balances",
"fieldtype": "Section Break"
},
{
"fieldname": "column_break_46yz",
"fieldtype": "Column Break"
},
{
"fieldname": "column_break_xown",
"fieldtype": "Column Break"
},
{
"fieldname": "column_break_ukce",
"fieldtype": "Column Break"
},
{
"fieldname": "section_break_ngrs",
"fieldtype": "Section Break"
}
],
"istable": 1,
"links": [],
"modified": "2022-11-17 10:26:18.302728",
"modified": "2022-12-29 19:38:52.915295",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Exchange Rate Revaluation Account",

View File

@@ -9,10 +9,6 @@ from frappe.model.document import Document
from frappe.utils import add_days, add_years, cstr, getdate
class FiscalYearIncorrectDate(frappe.ValidationError):
pass
class FiscalYear(Document):
@frappe.whitelist()
def set_as_default(self):
@@ -53,23 +49,18 @@ class FiscalYear(Document):
)
def validate_dates(self):
self.validate_from_to_dates("year_start_date", "year_end_date")
if self.is_short_year:
# Fiscal Year can be shorter than one year, in some jurisdictions
# under certain circumstances. For example, in the USA and Germany.
return
if getdate(self.year_start_date) > getdate(self.year_end_date):
frappe.throw(
_("Fiscal Year Start Date should be one year earlier than Fiscal Year End Date"),
FiscalYearIncorrectDate,
)
date = getdate(self.year_start_date) + relativedelta(years=1) - relativedelta(days=1)
if getdate(self.year_end_date) != date:
frappe.throw(
_("Fiscal Year End Date should be one year after Fiscal Year Start Date"),
FiscalYearIncorrectDate,
frappe.exceptions.InvalidDates,
)
def on_update(self):
@@ -169,5 +160,6 @@ def auto_create_fiscal_year():
def get_from_and_to_date(fiscal_year):
fields = ["year_start_date as from_date", "year_end_date as to_date"]
return frappe.get_cached_value("Fiscal Year", fiscal_year, fields, as_dict=1)
fields = ["year_start_date", "year_end_date"]
cached_results = frappe.get_cached_value("Fiscal Year", fiscal_year, fields, as_dict=1)
return dict(from_date=cached_results.year_start_date, to_date=cached_results.year_end_date)

View File

@@ -7,8 +7,6 @@ import unittest
import frappe
from frappe.utils import now_datetime
from erpnext.accounts.doctype.fiscal_year.fiscal_year import FiscalYearIncorrectDate
test_ignore = ["Company"]
@@ -26,7 +24,7 @@ class TestFiscalYear(unittest.TestCase):
}
)
self.assertRaises(FiscalYearIncorrectDate, fy.insert)
self.assertRaises(frappe.exceptions.InvalidDates, fy.insert)
def test_record_generator():
@@ -35,8 +33,8 @@ def test_record_generator():
"doctype": "Fiscal Year",
"year": "_Test Short Fiscal Year 2011",
"is_short_year": 1,
"year_end_date": "2011-04-01",
"year_start_date": "2011-12-31",
"year_start_date": "2011-04-01",
"year_end_date": "2011-12-31",
}
]

View File

@@ -95,7 +95,15 @@ class GLEntry(Document):
)
# Zero value transaction is not allowed
if not (flt(self.debit, self.precision("debit")) or flt(self.credit, self.precision("credit"))):
if not (
flt(self.debit, self.precision("debit"))
or flt(self.credit, self.precision("credit"))
or (
self.voucher_type == "Journal Entry"
and frappe.get_cached_value("Journal Entry", self.voucher_no, "voucher_type")
== "Exchange Gain Or Loss"
)
):
frappe.throw(
_("{0} {1}: Either debit or credit amount is required for {2}").format(
self.voucher_type, self.voucher_no, self.account

View File

@@ -8,7 +8,7 @@ frappe.provide("erpnext.journal_entry");
frappe.ui.form.on("Journal Entry", {
setup: function(frm) {
frm.add_fetch("bank_account", "account", "account");
frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice'];
frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', 'Journal Entry', "Repost Payment Ledger"];
},
refresh: function(frm) {

View File

@@ -88,7 +88,7 @@
"label": "Entry Type",
"oldfieldname": "voucher_type",
"oldfieldtype": "Select",
"options": "Journal Entry\nInter Company Journal Entry\nBank Entry\nCash Entry\nCredit Card Entry\nDebit Note\nCredit Note\nContra Entry\nExcise Entry\nWrite Off Entry\nOpening Entry\nDepreciation Entry\nExchange Rate Revaluation\nDeferred Revenue\nDeferred Expense",
"options": "Journal Entry\nInter Company Journal Entry\nBank Entry\nCash Entry\nCredit Card Entry\nDebit Note\nCredit Note\nContra Entry\nExcise Entry\nWrite Off Entry\nOpening Entry\nDepreciation Entry\nExchange Rate Revaluation\nExchange Gain Or Loss\nDeferred Revenue\nDeferred Expense",
"reqd": 1,
"search_index": 1
},
@@ -539,7 +539,7 @@
"idx": 176,
"is_submittable": 1,
"links": [],
"modified": "2022-06-23 22:01:32.348337",
"modified": "2023-03-01 14:58:59.286591",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry",

View File

@@ -6,7 +6,7 @@ import json
import frappe
from frappe import _, msgprint, scrub
from frappe.utils import cint, cstr, flt, fmt_money, formatdate, get_link_to_form, nowdate
from frappe.utils import cstr, flt, fmt_money, formatdate, get_link_to_form, nowdate
import erpnext
from erpnext.accounts.deferred_revenue import get_deferred_booking_accounts
@@ -23,6 +23,9 @@ from erpnext.accounts.utils import (
get_stock_accounts,
get_stock_and_account_balance,
)
from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_schedule import (
get_depr_schedule,
)
from erpnext.controllers.accounts_controller import AccountsController
@@ -34,9 +37,6 @@ class JournalEntry(AccountsController):
def __init__(self, *args, **kwargs):
super(JournalEntry, self).__init__(*args, **kwargs)
def get_feed(self):
return self.voucher_type
def validate(self):
if self.voucher_type == "Opening Entry":
self.is_opening = "Yes"
@@ -51,7 +51,7 @@ class JournalEntry(AccountsController):
self.validate_multi_currency()
self.set_amounts_in_company_currency()
self.validate_debit_credit_amount()
self.set_total_debit_credit()
# Do not validate while importing via data import
if not frappe.flags.in_import:
self.validate_total_debit_and_credit()
@@ -81,6 +81,7 @@ class JournalEntry(AccountsController):
self.check_credit_limit()
self.make_gl_entries()
self.update_advance_paid()
self.update_asset_value()
self.update_inter_company_jv()
self.update_invoice_discounting()
@@ -88,7 +89,13 @@ class JournalEntry(AccountsController):
from erpnext.accounts.utils import unlink_ref_doc_from_payment_entries
unlink_ref_doc_from_payment_entries(self)
self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Payment Ledger Entry")
self.ignore_linked_doctypes = (
"GL Entry",
"Stock Ledger Entry",
"Payment Ledger Entry",
"Repost Payment Ledger",
"Repost Payment Ledger Items",
)
self.make_gl_entries(1)
self.update_advance_paid()
self.unlink_advance_entry_reference()
@@ -225,6 +232,29 @@ class JournalEntry(AccountsController):
for d in to_remove:
self.remove(d)
def update_asset_value(self):
if self.voucher_type != "Depreciation Entry":
return
processed_assets = []
for d in self.get("accounts"):
if (
d.reference_type == "Asset" and d.reference_name and d.reference_name not in processed_assets
):
processed_assets.append(d.reference_name)
asset = frappe.get_doc("Asset", d.reference_name)
if asset.calculate_depreciation:
continue
depr_value = d.debit or d.credit
asset.db_set("value_after_depreciation", asset.value_after_depreciation - depr_value)
asset.set_status()
def update_inter_company_jv(self):
if (
self.voucher_type == "Inter Company Journal Entry"
@@ -283,19 +313,45 @@ class JournalEntry(AccountsController):
d.db_update()
def unlink_asset_reference(self):
if self.voucher_type != "Depreciation Entry":
return
processed_assets = []
for d in self.get("accounts"):
if d.reference_type == "Asset" and d.reference_name:
if (
d.reference_type == "Asset" and d.reference_name and d.reference_name not in processed_assets
):
processed_assets.append(d.reference_name)
asset = frappe.get_doc("Asset", d.reference_name)
for s in asset.get("schedules"):
if s.journal_entry == self.name:
s.db_set("journal_entry", None)
idx = cint(s.finance_book_id) or 1
finance_books = asset.get("finance_books")[idx - 1]
finance_books.value_after_depreciation += s.depreciation_amount
finance_books.db_update()
if asset.calculate_depreciation:
je_found = False
asset.set_status()
for row in asset.get("finance_books"):
if je_found:
break
depr_schedule = get_depr_schedule(asset.name, "Active", row.finance_book)
for s in depr_schedule or []:
if s.journal_entry == self.name:
s.db_set("journal_entry", None)
row.value_after_depreciation += s.depreciation_amount
row.db_update()
asset.set_status()
je_found = True
break
else:
depr_value = d.debit or d.credit
asset.db_set("value_after_depreciation", asset.value_after_depreciation + depr_value)
asset.set_status()
def unlink_inter_company_jv(self):
if (
@@ -592,28 +648,29 @@ class JournalEntry(AccountsController):
d.against_account = frappe.db.get_value(d.reference_type, d.reference_name, field)
else:
for d in self.get("accounts"):
if flt(d.debit > 0):
if flt(d.debit) > 0:
accounts_debited.append(d.party or d.account)
if flt(d.credit) > 0:
accounts_credited.append(d.party or d.account)
for d in self.get("accounts"):
if flt(d.debit > 0):
if flt(d.debit) > 0:
d.against_account = ", ".join(list(set(accounts_credited)))
if flt(d.credit > 0):
if flt(d.credit) > 0:
d.against_account = ", ".join(list(set(accounts_debited)))
def validate_debit_credit_amount(self):
for d in self.get("accounts"):
if not flt(d.debit) and not flt(d.credit):
frappe.throw(_("Row {0}: Both Debit and Credit values cannot be zero").format(d.idx))
if not (self.voucher_type == "Exchange Gain Or Loss" and self.multi_currency):
for d in self.get("accounts"):
if not flt(d.debit) and not flt(d.credit):
frappe.throw(_("Row {0}: Both Debit and Credit values cannot be zero").format(d.idx))
def validate_total_debit_and_credit(self):
self.set_total_debit_credit()
if self.difference:
frappe.throw(
_("Total Debit must be equal to Total Credit. The difference is {0}").format(self.difference)
)
if not (self.voucher_type == "Exchange Gain Or Loss" and self.multi_currency):
if self.difference:
frappe.throw(
_("Total Debit must be equal to Total Credit. The difference is {0}").format(self.difference)
)
def set_total_debit_credit(self):
self.total_debit, self.total_credit, self.difference = 0, 0, 0
@@ -651,16 +708,17 @@ class JournalEntry(AccountsController):
self.set_exchange_rate()
def set_amounts_in_company_currency(self):
for d in self.get("accounts"):
d.debit_in_account_currency = flt(
d.debit_in_account_currency, d.precision("debit_in_account_currency")
)
d.credit_in_account_currency = flt(
d.credit_in_account_currency, d.precision("credit_in_account_currency")
)
if not (self.voucher_type == "Exchange Gain Or Loss" and self.multi_currency):
for d in self.get("accounts"):
d.debit_in_account_currency = flt(
d.debit_in_account_currency, d.precision("debit_in_account_currency")
)
d.credit_in_account_currency = flt(
d.credit_in_account_currency, d.precision("credit_in_account_currency")
)
d.debit = flt(d.debit_in_account_currency * flt(d.exchange_rate), d.precision("debit"))
d.credit = flt(d.credit_in_account_currency * flt(d.exchange_rate), d.precision("credit"))
d.debit = flt(d.debit_in_account_currency * flt(d.exchange_rate), d.precision("debit"))
d.credit = flt(d.credit_in_account_currency * flt(d.exchange_rate), d.precision("credit"))
def set_exchange_rate(self):
for d in self.get("accounts"):
@@ -759,7 +817,7 @@ class JournalEntry(AccountsController):
pay_to_recd_from = d.party
if pay_to_recd_from and pay_to_recd_from == d.party:
party_amount += d.debit_in_account_currency or d.credit_in_account_currency
party_amount += flt(d.debit_in_account_currency) or flt(d.credit_in_account_currency)
party_account_currency = d.account_currency
elif frappe.get_cached_value("Account", d.account, "account_type") in ["Bank", "Cash"]:
@@ -789,7 +847,7 @@ class JournalEntry(AccountsController):
def build_gl_map(self):
gl_map = []
for d in self.get("accounts"):
if d.debit or d.credit:
if d.debit or d.credit or (self.voucher_type == "Exchange Gain Or Loss"):
r = [d.user_remark, self.remark]
r = [x for x in r if x]
remarks = "\n".join(r)
@@ -837,7 +895,7 @@ class JournalEntry(AccountsController):
make_gl_entries(gl_map, cancel=cancel, adv_adj=adv_adj, update_outstanding=update_outstanding)
@frappe.whitelist()
def get_balance(self):
def get_balance(self, difference_account=None):
if not self.get("accounts"):
msgprint(_("'Entries' cannot be empty"), raise_exception=True)
else:
@@ -852,7 +910,13 @@ class JournalEntry(AccountsController):
blank_row = d
if not blank_row:
blank_row = self.append("accounts", {})
blank_row = self.append(
"accounts",
{
"account": difference_account,
"cost_center": erpnext.get_default_cost_center(self.company),
},
)
blank_row.exchange_rate = 1
if diff > 0:

View File

@@ -7,7 +7,7 @@ cur_frm.cscript.tax_table = "Advance Taxes and Charges";
frappe.ui.form.on('Payment Entry', {
onload: function(frm) {
frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice'];
frm.ignore_doctypes_on_cancel_all = ['Sales Invoice', 'Purchase Invoice', "Repost Payment Ledger"];
if(frm.doc.__islocal) {
if (!frm.doc.paid_from) frm.set_value("paid_from_account_currency", null);
@@ -217,7 +217,6 @@ frappe.ui.form.on('Payment Entry', {
frm.toggle_display("set_exchange_gain_loss",
frm.doc.paid_amount && frm.doc.received_amount && frm.doc.difference_amount);
frm.refresh_fields();
},
set_dynamic_labels: function(frm) {
@@ -245,8 +244,6 @@ frappe.ui.form.on('Payment Entry', {
frm.set_currency_labels(["total_amount", "outstanding_amount", "allocated_amount"],
party_account_currency, "references");
frm.set_currency_labels(["amount"], company_currency, "deductions");
cur_frm.set_df_property("source_exchange_rate", "description",
("1 " + frm.doc.paid_from_account_currency + " = [?] " + company_currency));

View File

@@ -239,7 +239,7 @@
"depends_on": "paid_from",
"fieldname": "paid_from_account_currency",
"fieldtype": "Link",
"label": "Account Currency",
"label": "Account Currency (From)",
"options": "Currency",
"print_hide": 1,
"read_only": 1,
@@ -249,7 +249,7 @@
"depends_on": "paid_from",
"fieldname": "paid_from_account_balance",
"fieldtype": "Currency",
"label": "Account Balance",
"label": "Account Balance (From)",
"options": "paid_from_account_currency",
"print_hide": 1,
"read_only": 1
@@ -272,7 +272,7 @@
"depends_on": "paid_to",
"fieldname": "paid_to_account_currency",
"fieldtype": "Link",
"label": "Account Currency",
"label": "Account Currency (To)",
"options": "Currency",
"print_hide": 1,
"read_only": 1,
@@ -282,7 +282,7 @@
"depends_on": "paid_to",
"fieldname": "paid_to_account_balance",
"fieldtype": "Currency",
"label": "Account Balance",
"label": "Account Balance (To)",
"options": "paid_to_account_currency",
"print_hide": 1,
"read_only": 1
@@ -304,7 +304,8 @@
{
"fieldname": "source_exchange_rate",
"fieldtype": "Float",
"label": "Exchange Rate",
"label": "Source Exchange Rate",
"precision": "9",
"print_hide": 1,
"reqd": 1
},
@@ -333,7 +334,8 @@
{
"fieldname": "target_exchange_rate",
"fieldtype": "Float",
"label": "Exchange Rate",
"label": "Target Exchange Rate",
"precision": "9",
"print_hide": 1,
"reqd": 1
},
@@ -631,14 +633,14 @@
"depends_on": "eval:doc.party_type == 'Supplier'",
"fieldname": "purchase_taxes_and_charges_template",
"fieldtype": "Link",
"label": "Taxes and Charges Template",
"label": "Purchase Taxes and Charges Template",
"options": "Purchase Taxes and Charges Template"
},
{
"depends_on": "eval: doc.party_type == 'Customer'",
"fieldname": "sales_taxes_and_charges_template",
"fieldtype": "Link",
"label": "Taxes and Charges Template",
"label": "Sales Taxes and Charges Template",
"options": "Sales Taxes and Charges Template"
},
{
@@ -731,7 +733,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2022-02-23 20:08:39.559814",
"modified": "2023-02-14 04:52:30.478523",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Entry",

View File

@@ -92,7 +92,13 @@ class PaymentEntry(AccountsController):
self.set_status()
def on_cancel(self):
self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Payment Ledger Entry")
self.ignore_linked_doctypes = (
"GL Entry",
"Stock Ledger Entry",
"Payment Ledger Entry",
"Repost Payment Ledger",
"Repost Payment Ledger Items",
)
self.make_gl_entries(cancel=1)
self.update_outstanding_amounts()
self.update_advance_paid()
@@ -247,7 +253,7 @@ class PaymentEntry(AccountsController):
self.set_target_exchange_rate(ref_doc)
def set_source_exchange_rate(self, ref_doc=None):
if self.paid_from and not self.source_exchange_rate:
if self.paid_from:
if self.paid_from_account_currency == self.company_currency:
self.source_exchange_rate = 1
else:
@@ -410,7 +416,7 @@ class PaymentEntry(AccountsController):
for ref in self.get("references"):
if ref.payment_term and ref.reference_name:
key = (ref.payment_term, ref.reference_name)
key = (ref.payment_term, ref.reference_name, ref.reference_doctype)
invoice_payment_amount_map.setdefault(key, 0.0)
invoice_payment_amount_map[key] += ref.allocated_amount
@@ -418,20 +424,37 @@ class PaymentEntry(AccountsController):
payment_schedule = frappe.get_all(
"Payment Schedule",
filters={"parent": ref.reference_name},
fields=["paid_amount", "payment_amount", "payment_term", "discount", "outstanding"],
fields=[
"paid_amount",
"payment_amount",
"payment_term",
"discount",
"outstanding",
"discount_type",
],
)
for term in payment_schedule:
invoice_key = (term.payment_term, ref.reference_name)
invoice_key = (term.payment_term, ref.reference_name, ref.reference_doctype)
invoice_paid_amount_map.setdefault(invoice_key, {})
invoice_paid_amount_map[invoice_key]["outstanding"] = term.outstanding
invoice_paid_amount_map[invoice_key]["discounted_amt"] = ref.total_amount * (
term.discount / 100
)
if not (term.discount_type and term.discount):
continue
if term.discount_type == "Percentage":
invoice_paid_amount_map[invoice_key]["discounted_amt"] = ref.total_amount * (
term.discount / 100
)
else:
invoice_paid_amount_map[invoice_key]["discounted_amt"] = term.discount
for idx, (key, allocated_amount) in enumerate(invoice_payment_amount_map.items(), 1):
if not invoice_paid_amount_map.get(key):
frappe.throw(_("Payment term {0} not used in {1}").format(key[0], key[1]))
allocated_amount = self.get_allocated_amount_in_transaction_currency(
allocated_amount, key[2], key[1]
)
outstanding = flt(invoice_paid_amount_map.get(key, {}).get("outstanding"))
discounted_amt = flt(invoice_paid_amount_map.get(key, {}).get("discounted_amt"))
@@ -466,6 +489,33 @@ class PaymentEntry(AccountsController):
(allocated_amount - discounted_amt, discounted_amt, allocated_amount, key[1], key[0]),
)
def get_allocated_amount_in_transaction_currency(
self, allocated_amount, reference_doctype, reference_docname
):
"""
Payment Entry could be in base currency while reference's payment schedule
is always in transaction currency.
E.g.
* SI with base=INR and currency=USD
* SI with payment schedule in USD
* PE in INR (accounting done in base currency)
"""
ref_currency, ref_exchange_rate = frappe.db.get_value(
reference_doctype, reference_docname, ["currency", "conversion_rate"]
)
is_single_currency = self.paid_from_account_currency == self.paid_to_account_currency
# PE in different currency
reference_is_multi_currency = self.paid_from_account_currency != ref_currency
if not (is_single_currency and reference_is_multi_currency):
return allocated_amount
allocated_amount = flt(
allocated_amount / ref_exchange_rate, self.precision("total_allocated_amount")
)
return allocated_amount
def set_status(self):
if self.docstatus == 2:
self.status = "Cancelled"
@@ -622,7 +672,7 @@ class PaymentEntry(AccountsController):
self.payment_type == "Receive"
and self.base_total_allocated_amount < self.base_received_amount + total_deductions
and self.total_allocated_amount
< self.paid_amount + (total_deductions / self.source_exchange_rate)
< flt(self.paid_amount) + (total_deductions / self.source_exchange_rate)
):
self.unallocated_amount = (
self.base_received_amount + total_deductions - self.base_total_allocated_amount
@@ -632,7 +682,7 @@ class PaymentEntry(AccountsController):
self.payment_type == "Pay"
and self.base_total_allocated_amount < (self.base_paid_amount - total_deductions)
and self.total_allocated_amount
< self.received_amount + (total_deductions / self.target_exchange_rate)
< flt(self.received_amount) + (total_deductions / self.target_exchange_rate)
):
self.unallocated_amount = (
self.base_paid_amount - (total_deductions + self.base_total_allocated_amount)
@@ -684,35 +734,34 @@ class PaymentEntry(AccountsController):
)
def validate_payment_against_negative_invoice(self):
if (self.payment_type == "Pay" and self.party_type == "Customer") or (
self.payment_type == "Receive" and self.party_type == "Supplier"
if (self.payment_type != "Pay" or self.party_type != "Customer") and (
self.payment_type != "Receive" or self.party_type != "Supplier"
):
return
total_negative_outstanding = sum(
abs(flt(d.outstanding_amount)) for d in self.get("references") if flt(d.outstanding_amount) < 0
total_negative_outstanding = sum(
abs(flt(d.outstanding_amount)) for d in self.get("references") if flt(d.outstanding_amount) < 0
)
paid_amount = self.paid_amount if self.payment_type == "Receive" else self.received_amount
additional_charges = sum(flt(d.amount) for d in self.deductions)
if not total_negative_outstanding:
if self.party_type == "Customer":
msg = _("Cannot pay to Customer without any negative outstanding invoice")
else:
msg = _("Cannot receive from Supplier without any negative outstanding invoice")
frappe.throw(msg, InvalidPaymentEntry)
elif paid_amount - additional_charges > total_negative_outstanding:
frappe.throw(
_("Paid Amount cannot be greater than total negative outstanding amount {0}").format(
total_negative_outstanding
),
InvalidPaymentEntry,
)
paid_amount = self.paid_amount if self.payment_type == "Receive" else self.received_amount
additional_charges = sum([flt(d.amount) for d in self.deductions])
if not total_negative_outstanding:
frappe.throw(
_("Cannot {0} {1} {2} without any negative outstanding invoice").format(
_(self.payment_type),
(_("to") if self.party_type == "Customer" else _("from")),
self.party_type,
),
InvalidPaymentEntry,
)
elif paid_amount - additional_charges > total_negative_outstanding:
frappe.throw(
_("Paid Amount cannot be greater than total negative outstanding amount {0}").format(
total_negative_outstanding
),
InvalidPaymentEntry,
)
def set_title(self):
if frappe.flags.in_import and self.title:
# do not set title dynamically if title exists during data import.
@@ -1188,6 +1237,7 @@ def get_outstanding_reference_documents(args):
ple = qb.DocType("Payment Ledger Entry")
common_filter = []
accounting_dimensions_filter = []
posting_and_due_date = []
# confirm that Supplier is not blocked
@@ -1217,7 +1267,7 @@ def get_outstanding_reference_documents(args):
# Add cost center condition
if args.get("cost_center"):
condition += " and cost_center='%s'" % args.get("cost_center")
common_filter.append(ple.cost_center == args.get("cost_center"))
accounting_dimensions_filter.append(ple.cost_center == args.get("cost_center"))
date_fields_dict = {
"posting_date": ["from_posting_date", "to_posting_date"],
@@ -1243,6 +1293,7 @@ def get_outstanding_reference_documents(args):
posting_date=posting_and_due_date,
min_outstanding=args.get("outstanding_amt_greater_than"),
max_outstanding=args.get("outstanding_amt_less_than"),
accounting_dimensions=accounting_dimensions_filter,
)
outstanding_invoices = split_invoices_based_on_payment_terms(outstanding_invoices)
@@ -1635,11 +1686,18 @@ def get_reference_details(reference_doctype, reference_name, party_account_curre
@frappe.whitelist()
def get_payment_entry(
dt, dn, party_amount=None, bank_account=None, bank_amount=None, party_type=None, payment_type=None
dt,
dn,
party_amount=None,
bank_account=None,
bank_amount=None,
party_type=None,
payment_type=None,
reference_date=None,
):
reference_doc = None
doc = frappe.get_doc(dt, dn)
if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) > 0:
if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) >= 99.99:
frappe.throw(_("Can only make payment against unbilled {0}").format(dt))
if not party_type:
@@ -1662,8 +1720,9 @@ def get_payment_entry(
dt, party_account_currency, bank, outstanding_amount, payment_type, bank_amount, doc
)
paid_amount, received_amount, discount_amount = apply_early_payment_discount(
paid_amount, received_amount, doc
reference_date = getdate(reference_date)
paid_amount, received_amount, discount_amount, valid_discounts = apply_early_payment_discount(
paid_amount, received_amount, doc, party_account_currency, reference_date
)
pe = frappe.new_doc("Payment Entry")
@@ -1671,6 +1730,7 @@ def get_payment_entry(
pe.company = doc.company
pe.cost_center = doc.get("cost_center")
pe.posting_date = nowdate()
pe.reference_date = reference_date
pe.mode_of_payment = doc.get("mode_of_payment")
pe.party_type = party_type
pe.party = doc.get(scrub(party_type))
@@ -1711,7 +1771,7 @@ def get_payment_entry(
):
for reference in get_reference_as_per_payment_terms(
doc.payment_schedule, dt, dn, doc, grand_total, outstanding_amount
doc.payment_schedule, dt, dn, doc, grand_total, outstanding_amount, party_account_currency
):
pe.append("references", reference)
else:
@@ -1757,23 +1817,38 @@ def get_payment_entry(
pe.setup_party_account_field()
pe.set_missing_values()
update_accounting_dimensions(pe, doc)
if party_account and bank:
pe.set_exchange_rate(ref_doc=reference_doc)
pe.set_amounts()
if discount_amount:
pe.set_gain_or_loss(
account_details={
"account": frappe.get_cached_value("Company", pe.company, "default_discount_account"),
"cost_center": pe.cost_center
or frappe.get_cached_value("Company", pe.company, "cost_center"),
"amount": discount_amount * (-1 if payment_type == "Pay" else 1),
}
base_total_discount_loss = 0
if frappe.db.get_single_value("Accounts Settings", "book_tax_discount_loss"):
base_total_discount_loss = split_early_payment_discount_loss(pe, doc, valid_discounts)
set_pending_discount_loss(
pe, doc, discount_amount, base_total_discount_loss, party_account_currency
)
pe.set_difference_amount()
pe.set_difference_amount()
return pe
def update_accounting_dimensions(pe, doc):
"""
Updates accounting dimensions in Payment Entry based on the accounting dimensions in the reference document
"""
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_accounting_dimensions,
)
for dimension in get_accounting_dimensions():
pe.set(dimension, doc.get(dimension))
def get_bank_cash_account(doc, bank_account):
bank = get_default_bank_cash_account(
doc.company, "Bank", mode_of_payment=doc.get("mode_of_payment"), account=bank_account
@@ -1868,20 +1943,28 @@ def set_paid_amount_and_received_amount(
return paid_amount, received_amount
def apply_early_payment_discount(paid_amount, received_amount, doc):
def apply_early_payment_discount(
paid_amount, received_amount, doc, party_account_currency, reference_date
):
total_discount = 0
valid_discounts = []
eligible_for_payments = ["Sales Order", "Sales Invoice", "Purchase Order", "Purchase Invoice"]
has_payment_schedule = hasattr(doc, "payment_schedule") and doc.payment_schedule
is_multi_currency = party_account_currency != doc.company_currency
if doc.doctype in eligible_for_payments and has_payment_schedule:
for term in doc.payment_schedule:
if not term.discounted_amount and term.discount and getdate(nowdate()) <= term.discount_date:
if not term.discounted_amount and term.discount and reference_date <= term.discount_date:
if term.discount_type == "Percentage":
discount_amount = flt(doc.get("grand_total")) * (term.discount / 100)
grand_total = doc.get("grand_total") if is_multi_currency else doc.get("base_grand_total")
discount_amount = flt(grand_total) * (term.discount / 100)
else:
discount_amount = term.discount
discount_amount_in_foreign_currency = discount_amount * doc.get("conversion_rate", 1)
# if accounting is done in the same currency, paid_amount = received_amount
conversion_rate = doc.get("conversion_rate", 1) if is_multi_currency else 1
discount_amount_in_foreign_currency = discount_amount * conversion_rate
if doc.doctype == "Sales Invoice":
paid_amount -= discount_amount
@@ -1890,23 +1973,151 @@ def apply_early_payment_discount(paid_amount, received_amount, doc):
received_amount -= discount_amount
paid_amount -= discount_amount_in_foreign_currency
valid_discounts.append({"type": term.discount_type, "discount": term.discount})
total_discount += discount_amount
if total_discount:
money = frappe.utils.fmt_money(total_discount, currency=doc.get("currency"))
currency = doc.get("currency") if is_multi_currency else doc.company_currency
money = frappe.utils.fmt_money(total_discount, currency=currency)
frappe.msgprint(_("Discount of {} applied as per Payment Term").format(money), alert=1)
return paid_amount, received_amount, total_discount
return paid_amount, received_amount, total_discount, valid_discounts
def set_pending_discount_loss(
pe, doc, discount_amount, base_total_discount_loss, party_account_currency
):
# If multi-currency, get base discount amount to adjust with base currency deductions/losses
if party_account_currency != doc.company_currency:
discount_amount = discount_amount * doc.get("conversion_rate", 1)
# Avoid considering miniscule losses
discount_amount = flt(discount_amount - base_total_discount_loss, doc.precision("grand_total"))
# Set base discount amount (discount loss/pending rounding loss) in deductions
if discount_amount > 0.0:
positive_negative = -1 if pe.payment_type == "Pay" else 1
# If tax loss booking is enabled, pending loss will be rounding loss.
# Otherwise it will be the total discount loss.
book_tax_loss = frappe.db.get_single_value("Accounts Settings", "book_tax_discount_loss")
account_type = "round_off_account" if book_tax_loss else "default_discount_account"
pe.set_gain_or_loss(
account_details={
"account": frappe.get_cached_value("Company", pe.company, account_type),
"cost_center": pe.cost_center or frappe.get_cached_value("Company", pe.company, "cost_center"),
"amount": discount_amount * positive_negative,
}
)
def split_early_payment_discount_loss(pe, doc, valid_discounts) -> float:
"""Split early payment discount into Income Loss & Tax Loss."""
total_discount_percent = get_total_discount_percent(doc, valid_discounts)
if not total_discount_percent:
return 0.0
base_loss_on_income = add_income_discount_loss(pe, doc, total_discount_percent)
base_loss_on_taxes = add_tax_discount_loss(pe, doc, total_discount_percent)
# Round off total loss rather than individual losses to reduce rounding error
return flt(base_loss_on_income + base_loss_on_taxes, doc.precision("grand_total"))
def get_total_discount_percent(doc, valid_discounts) -> float:
"""Get total percentage and amount discount applied as a percentage."""
total_discount_percent = (
sum(
discount.get("discount") for discount in valid_discounts if discount.get("type") == "Percentage"
)
or 0.0
)
# Operate in percentages only as it makes the income & tax split easier
total_discount_amount = (
sum(discount.get("discount") for discount in valid_discounts if discount.get("type") == "Amount")
or 0.0
)
if total_discount_amount:
discount_percentage = (total_discount_amount / doc.get("grand_total")) * 100
total_discount_percent += discount_percentage
return total_discount_percent
return total_discount_percent
def add_income_discount_loss(pe, doc, total_discount_percent) -> float:
"""Add loss on income discount in base currency."""
precision = doc.precision("total")
base_loss_on_income = doc.get("base_total") * (total_discount_percent / 100)
pe.append(
"deductions",
{
"account": frappe.get_cached_value("Company", pe.company, "default_discount_account"),
"cost_center": pe.cost_center or frappe.get_cached_value("Company", pe.company, "cost_center"),
"amount": flt(base_loss_on_income, precision),
},
)
return base_loss_on_income # Return loss without rounding
def add_tax_discount_loss(pe, doc, total_discount_percentage) -> float:
"""Add loss on tax discount in base currency."""
tax_discount_loss = {}
base_total_tax_loss = 0
precision = doc.precision("tax_amount_after_discount_amount", "taxes")
# The same account head could be used more than once
for tax in doc.get("taxes", []):
base_tax_loss = tax.get("base_tax_amount_after_discount_amount") * (
total_discount_percentage / 100
)
account = tax.get("account_head")
if not tax_discount_loss.get(account):
tax_discount_loss[account] = base_tax_loss
else:
tax_discount_loss[account] += base_tax_loss
for account, loss in tax_discount_loss.items():
base_total_tax_loss += loss
if loss == 0.0:
continue
pe.append(
"deductions",
{
"account": account,
"cost_center": pe.cost_center or frappe.get_cached_value("Company", pe.company, "cost_center"),
"amount": flt(loss, precision),
},
)
return base_total_tax_loss # Return loss without rounding
def get_reference_as_per_payment_terms(
payment_schedule, dt, dn, doc, grand_total, outstanding_amount
payment_schedule, dt, dn, doc, grand_total, outstanding_amount, party_account_currency
):
references = []
is_multi_currency_acc = (doc.currency != doc.company_currency) and (
party_account_currency != doc.company_currency
)
for payment_term in payment_schedule:
payment_term_outstanding = flt(
payment_term.payment_amount - payment_term.paid_amount, payment_term.precision("payment_amount")
)
if not is_multi_currency_acc:
# If accounting is done in company currency for multi-currency transaction
payment_term_outstanding = flt(
payment_term_outstanding * doc.get("conversion_rate"), payment_term.precision("payment_amount")
)
if payment_term_outstanding:
references.append(

View File

@@ -5,7 +5,7 @@ import unittest
import frappe
from frappe import qb
from frappe.tests.utils import FrappeTestCase
from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import flt, nowdate
from erpnext.accounts.doctype.payment_entry.payment_entry import (
@@ -256,10 +256,25 @@ class TestPaymentEntry(FrappeTestCase):
},
)
si.save()
si.submit()
frappe.db.set_single_value("Accounts Settings", "book_tax_discount_loss", 1)
pe_with_tax_loss = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Cash - _TC")
self.assertEqual(pe_with_tax_loss.references[0].payment_term, "30 Credit Days with 10% Discount")
self.assertEqual(pe_with_tax_loss.references[0].allocated_amount, 236.0)
self.assertEqual(pe_with_tax_loss.paid_amount, 212.4)
self.assertEqual(pe_with_tax_loss.deductions[0].amount, 20.0) # Loss on Income
self.assertEqual(pe_with_tax_loss.deductions[1].amount, 3.6) # Loss on Tax
self.assertEqual(pe_with_tax_loss.deductions[1].account, "_Test Account Service Tax - _TC")
frappe.db.set_single_value("Accounts Settings", "book_tax_discount_loss", 0)
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Cash - _TC")
self.assertEqual(pe.references[0].allocated_amount, 236.0)
self.assertEqual(pe.paid_amount, 212.4)
self.assertEqual(pe.deductions[0].amount, 23.6)
pe.submit()
si.load_from_db()
@@ -269,6 +284,190 @@ class TestPaymentEntry(FrappeTestCase):
self.assertEqual(si.payment_schedule[0].outstanding, 0)
self.assertEqual(si.payment_schedule[0].discounted_amount, 23.6)
def test_payment_entry_against_payment_terms_with_discount_amount(self):
si = create_sales_invoice(do_not_save=1, qty=1, rate=200)
si.payment_terms_template = "Test Discount Amount Template"
create_payment_terms_template_with_discount(
name="30 Credit Days with Rs.50 Discount",
discount_type="Amount",
discount=50,
template_name="Test Discount Amount Template",
)
frappe.db.set_value("Company", si.company, "default_discount_account", "Write Off - _TC")
si.append(
"taxes",
{
"charge_type": "On Net Total",
"account_head": "_Test Account Service Tax - _TC",
"cost_center": "_Test Cost Center - _TC",
"description": "Service Tax",
"rate": 18,
},
)
si.save()
si.submit()
# Set reference date past discount cut off date
pe_1 = get_payment_entry(
"Sales Invoice",
si.name,
bank_account="_Test Cash - _TC",
reference_date=frappe.utils.add_days(si.posting_date, 2),
)
self.assertEqual(pe_1.paid_amount, 236.0) # discount not applied
# Test if tax loss is booked on enabling configuration
frappe.db.set_single_value("Accounts Settings", "book_tax_discount_loss", 1)
pe_with_tax_loss = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Cash - _TC")
self.assertEqual(pe_with_tax_loss.deductions[0].amount, 42.37) # Loss on Income
self.assertEqual(pe_with_tax_loss.deductions[1].amount, 7.63) # Loss on Tax
self.assertEqual(pe_with_tax_loss.deductions[1].account, "_Test Account Service Tax - _TC")
frappe.db.set_single_value("Accounts Settings", "book_tax_discount_loss", 0)
pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Cash - _TC")
self.assertEqual(pe.references[0].allocated_amount, 236.0)
self.assertEqual(pe.paid_amount, 186)
self.assertEqual(pe.deductions[0].amount, 50.0)
pe.submit()
si.load_from_db()
self.assertEqual(si.payment_schedule[0].payment_amount, 236.0)
self.assertEqual(si.payment_schedule[0].paid_amount, 186)
self.assertEqual(si.payment_schedule[0].outstanding, 0)
self.assertEqual(si.payment_schedule[0].discounted_amount, 50)
@change_settings(
"Accounts Settings",
{
"allow_multi_currency_invoices_against_single_party_account": 1,
"book_tax_discount_loss": 1,
},
)
def test_payment_entry_multicurrency_si_with_base_currency_accounting_early_payment_discount(
self,
):
"""
1. Multi-currency SI with single currency accounting (company currency)
2. PE with early payment discount
3. Test if Paid Amount is calculated in company currency
4. Test if deductions are calculated in company currency
SI is in USD to document agreed amounts that are in USD, but the accounting is in base currency.
"""
si = create_sales_invoice(
customer="_Test Customer",
currency="USD",
conversion_rate=50,
do_not_save=1,
)
create_payment_terms_template_with_discount()
si.payment_terms_template = "Test Discount Template"
frappe.db.set_value("Company", si.company, "default_discount_account", "Write Off - _TC")
si.save()
si.submit()
pe = get_payment_entry(
"Sales Invoice",
si.name,
bank_account="_Test Bank - _TC",
)
pe.reference_no = si.name
pe.reference_date = nowdate()
# Early payment discount loss on income
self.assertEqual(pe.paid_amount, 4500.0) # Amount in company currency
self.assertEqual(pe.received_amount, 4500.0)
self.assertEqual(pe.deductions[0].amount, 500.0)
self.assertEqual(pe.deductions[0].account, "Write Off - _TC")
self.assertEqual(pe.difference_amount, 0.0)
pe.insert()
pe.submit()
expected_gle = dict(
(d[0], d)
for d in [
["Debtors - _TC", 0, 5000, si.name],
["_Test Bank - _TC", 4500, 0, None],
["Write Off - _TC", 500.0, 0, None],
]
)
self.validate_gl_entries(pe.name, expected_gle)
outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"))
self.assertEqual(outstanding_amount, 0)
def test_payment_entry_multicurrency_accounting_si_with_early_payment_discount(self):
"""
1. Multi-currency SI with multi-currency accounting
2. PE with early payment discount and also exchange loss
3. Test if Paid Amount is calculated in transaction currency
4. Test if deductions are calculated in base/company currency
5. Test if exchange loss is reflected in difference
"""
si = create_sales_invoice(
customer="_Test Customer USD",
debit_to="_Test Receivable USD - _TC",
currency="USD",
conversion_rate=50,
do_not_save=1,
)
create_payment_terms_template_with_discount()
si.payment_terms_template = "Test Discount Template"
frappe.db.set_value("Company", si.company, "default_discount_account", "Write Off - _TC")
si.save()
si.submit()
pe = get_payment_entry(
"Sales Invoice", si.name, bank_account="_Test Bank - _TC", bank_amount=4700
)
pe.reference_no = si.name
pe.reference_date = nowdate()
# Early payment discount loss on income
self.assertEqual(pe.paid_amount, 90.0)
self.assertEqual(pe.received_amount, 4200.0) # 5000 - 500 (discount) - 300 (exchange loss)
self.assertEqual(pe.deductions[0].amount, 500.0)
self.assertEqual(pe.deductions[0].account, "Write Off - _TC")
# Exchange loss
self.assertEqual(pe.difference_amount, 300.0)
pe.append(
"deductions",
{
"account": "_Test Exchange Gain/Loss - _TC",
"cost_center": "_Test Cost Center - _TC",
"amount": 300.0,
},
)
pe.insert()
pe.submit()
self.assertEqual(pe.difference_amount, 0.0)
expected_gle = dict(
(d[0], d)
for d in [
["_Test Receivable USD - _TC", 0, 5000, si.name],
["_Test Bank - _TC", 4200, 0, None],
["Write Off - _TC", 500.0, 0, None],
["_Test Exchange Gain/Loss - _TC", 300.0, 0, None],
]
)
self.validate_gl_entries(pe.name, expected_gle)
outstanding_amount = flt(frappe.db.get_value("Sales Invoice", si.name, "outstanding_amount"))
self.assertEqual(outstanding_amount, 0)
def test_payment_against_purchase_invoice_to_check_status(self):
pi = make_purchase_invoice(
supplier="_Test Supplier USD",
@@ -839,24 +1038,27 @@ def create_payment_terms_template():
).insert()
def create_payment_terms_template_with_discount():
def create_payment_terms_template_with_discount(
name=None, discount_type=None, discount=None, template_name=None
):
create_payment_term(name or "30 Credit Days with 10% Discount")
template_name = template_name or "Test Discount Template"
create_payment_term("30 Credit Days with 10% Discount")
if not frappe.db.exists("Payment Terms Template", "Test Discount Template"):
payment_term_template = frappe.get_doc(
if not frappe.db.exists("Payment Terms Template", template_name):
frappe.get_doc(
{
"doctype": "Payment Terms Template",
"template_name": "Test Discount Template",
"template_name": template_name,
"allocate_payment_based_on_payment_terms": 1,
"terms": [
{
"doctype": "Payment Terms Template Detail",
"payment_term": "30 Credit Days with 10% Discount",
"payment_term": name or "30 Credit Days with 10% Discount",
"invoice_portion": 100,
"credit_days_based_on": "Day(s) after invoice date",
"credit_days": 2,
"discount": 10,
"discount_type": discount_type or "Percentage",
"discount": discount or 10,
"discount_validity_based_on": "Day(s) after invoice date",
"discount_validity": 1,
}

View File

@@ -3,6 +3,7 @@
"creation": "2016-06-15 15:56:30.815503",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"account",
"cost_center",
@@ -17,9 +18,7 @@
"in_list_view": 1,
"label": "Account",
"options": "Account",
"reqd": 1,
"show_days": 1,
"show_seconds": 1
"reqd": 1
},
{
"fieldname": "cost_center",
@@ -28,37 +27,30 @@
"label": "Cost Center",
"options": "Cost Center",
"print_hide": 1,
"reqd": 1,
"show_days": 1,
"show_seconds": 1
"reqd": 1
},
{
"fieldname": "amount",
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Amount",
"reqd": 1,
"show_days": 1,
"show_seconds": 1
"label": "Amount (Company Currency)",
"options": "Company:company:default_currency",
"reqd": 1
},
{
"fieldname": "column_break_2",
"fieldtype": "Column Break",
"show_days": 1,
"show_seconds": 1
"fieldtype": "Column Break"
},
{
"fieldname": "description",
"fieldtype": "Small Text",
"label": "Description",
"show_days": 1,
"show_seconds": 1
"label": "Description"
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2020-09-12 20:38:08.110674",
"modified": "2023-03-06 07:11:57.739619",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Entry Deduction",
@@ -66,5 +58,6 @@
"permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC"
"sort_order": "DESC",
"states": []
}

View File

@@ -25,7 +25,8 @@
"in_list_view": 1,
"label": "Type",
"options": "DocType",
"reqd": 1
"reqd": 1,
"search_index": 1
},
{
"columns": 2,
@@ -35,7 +36,8 @@
"in_list_view": 1,
"label": "Name",
"options": "reference_doctype",
"reqd": 1
"reqd": 1,
"search_index": 1
},
{
"fieldname": "due_date",
@@ -104,7 +106,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2021-09-26 17:06:55.597389",
"modified": "2022-12-12 12:31:44.919895",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Entry Reference",
@@ -113,5 +115,6 @@
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}

View File

@@ -3,6 +3,7 @@
frappe.ui.form.on('Payment Gateway Account', {
refresh(frm) {
erpnext.utils.check_payments_app();
if(!frm.doc.__islocal) {
frm.set_df_property('payment_gateway', 'read_only', 1);
}

View File

@@ -170,7 +170,7 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo
}
reconcile() {
var show_dialog = this.frm.doc.allocation.filter(d => d.difference_amount && !d.difference_account);
var show_dialog = this.frm.doc.allocation.filter(d => d.difference_amount);
if (show_dialog && show_dialog.length) {
@@ -179,8 +179,12 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo
title: __("Select Difference Account"),
fields: [
{
fieldname: "allocation", fieldtype: "Table", label: __("Allocation"),
data: this.data, in_place_edit: true,
fieldname: "allocation",
fieldtype: "Table",
label: __("Allocation"),
data: this.data,
in_place_edit: true,
cannot_add_rows: true,
get_data: () => {
return this.data;
},
@@ -218,6 +222,10 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo
read_only: 1
}]
},
{
fieldtype: 'HTML',
options: "<b> New Journal Entry will be posted for the difference amount </b>"
}
],
primary_action: () => {
const args = dialog.get_values()["allocation"];
@@ -234,7 +242,7 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo
});
this.frm.doc.allocation.forEach(d => {
if (d.difference_amount && !d.difference_account) {
if (d.difference_amount) {
dialog.fields_dict.allocation.df.data.push({
'docname': d.name,
'reference_name': d.reference_name,
@@ -264,4 +272,32 @@ erpnext.accounts.PaymentReconciliationController = class PaymentReconciliationCo
}
};
frappe.ui.form.on('Payment Reconciliation Allocation', {
allocated_amount: function(frm, cdt, cdn) {
let row = locals[cdt][cdn];
// filter invoice
let invoice = frm.doc.invoices.filter((x) => (x.invoice_number == row.invoice_number));
// filter payment
let payment = frm.doc.payments.filter((x) => (x.reference_name == row.reference_name));
frm.call({
doc: frm.doc,
method: 'calculate_difference_on_allocation_change',
args: {
payment_entry: payment,
invoice: invoice,
allocated_amount: row.allocated_amount
},
callback: (r) => {
if (r.message) {
row.difference_amount = r.message;
frm.refresh();
}
}
});
}
});
extend_cscript(cur_frm.cscript, new erpnext.accounts.PaymentReconciliationController({frm: cur_frm}));

View File

@@ -14,7 +14,6 @@ from erpnext.accounts.utils import (
QueryPaymentLedger,
get_outstanding_invoices,
reconcile_against_document,
update_reference_in_payment_entry,
)
from erpnext.controllers.accounts_controller import get_advance_payment_entries
@@ -23,6 +22,7 @@ class PaymentReconciliation(Document):
def __init__(self, *args, **kwargs):
super(PaymentReconciliation, self).__init__(*args, **kwargs)
self.common_filter_conditions = []
self.accounting_dimension_filter_conditions = []
self.ple_posting_date_filter = []
@frappe.whitelist()
@@ -69,6 +69,10 @@ class PaymentReconciliation(Document):
def get_jv_entries(self):
condition = self.get_conditions()
if self.get("cost_center"):
condition += f" and t2.cost_center = '{self.cost_center}' "
dr_or_cr = (
"credit_in_account_currency"
if erpnext.get_party_account_type(self.party_type) == "Receivable"
@@ -79,12 +83,13 @@ class PaymentReconciliation(Document):
"t2.against_account like %(bank_cash_account)s" if self.bank_cash_account else "1=1"
)
# nosemgrep
journal_entries = frappe.db.sql(
"""
select
"Journal Entry" as reference_type, t1.name as reference_name,
t1.posting_date, t1.remark as remarks, t2.name as reference_row,
{dr_or_cr} as amount, t2.is_advance,
{dr_or_cr} as amount, t2.is_advance, t2.exchange_rate,
t2.account_currency as currency
from
`tabJournal Entry` t1, `tabJournal Entry Account` t2
@@ -193,6 +198,7 @@ class PaymentReconciliation(Document):
posting_date=self.ple_posting_date_filter,
min_outstanding=self.minimum_invoice_amount if self.minimum_invoice_amount else None,
max_outstanding=self.maximum_invoice_amount if self.maximum_invoice_amount else None,
accounting_dimensions=self.accounting_dimension_filter_conditions,
)
if self.invoice_limit:
@@ -213,26 +219,38 @@ class PaymentReconciliation(Document):
inv.currency = entry.get("currency")
inv.outstanding_amount = flt(entry.get("outstanding_amount"))
def get_difference_amount(self, allocated_entry):
if allocated_entry.get("reference_type") != "Payment Entry":
return
def get_difference_amount(self, payment_entry, invoice, allocated_amount):
difference_amount = 0
if frappe.get_cached_value(
"Account", self.receivable_payable_account, "account_currency"
) != frappe.get_cached_value("Company", self.company, "default_currency"):
if invoice.get("exchange_rate") and payment_entry.get("exchange_rate", 1) != invoice.get(
"exchange_rate", 1
):
allocated_amount_in_ref_rate = payment_entry.get("exchange_rate", 1) * allocated_amount
allocated_amount_in_inv_rate = invoice.get("exchange_rate", 1) * allocated_amount
difference_amount = allocated_amount_in_ref_rate - allocated_amount_in_inv_rate
dr_or_cr = (
"credit_in_account_currency"
if erpnext.get_party_account_type(self.party_type) == "Receivable"
else "debit_in_account_currency"
return difference_amount
@frappe.whitelist()
def calculate_difference_on_allocation_change(self, payment_entry, invoice, allocated_amount):
invoice_exchange_map = self.get_invoice_exchange_map(invoice, payment_entry)
invoice[0]["exchange_rate"] = invoice_exchange_map.get(invoice[0].get("invoice_number"))
new_difference_amount = self.get_difference_amount(
payment_entry[0], invoice[0], allocated_amount
)
row = self.get_payment_details(allocated_entry, dr_or_cr)
doc = frappe.get_doc(allocated_entry.reference_type, allocated_entry.reference_name)
update_reference_in_payment_entry(row, doc, do_not_save=True)
return doc.difference_amount
return new_difference_amount
@frappe.whitelist()
def allocate_entries(self, args):
self.validate_entries()
invoice_exchange_map = self.get_invoice_exchange_map(args.get("invoices"), args.get("payments"))
default_exchange_gain_loss_account = frappe.get_cached_value(
"Company", self.company, "exchange_gain_loss_account"
)
entries = []
for pay in args.get("payments"):
pay.update({"unreconciled_amount": pay.get("amount")})
@@ -246,7 +264,13 @@ class PaymentReconciliation(Document):
inv["outstanding_amount"] = flt(inv.get("outstanding_amount")) - flt(pay.get("amount"))
pay["amount"] = 0
res.difference_amount = self.get_difference_amount(res)
inv["exchange_rate"] = invoice_exchange_map.get(inv.get("invoice_number"))
if pay.get("reference_type") in ["Sales Invoice", "Purchase Invoice"]:
pay["exchange_rate"] = invoice_exchange_map.get(pay.get("reference_name"))
res.difference_amount = self.get_difference_amount(pay, inv, res["allocated_amount"])
res.difference_account = default_exchange_gain_loss_account
res.exchange_rate = inv.get("exchange_rate")
if pay.get("amount") == 0:
entries.append(res)
@@ -276,6 +300,7 @@ class PaymentReconciliation(Document):
"amount": pay.get("amount"),
"allocated_amount": allocated_amount,
"difference_amount": pay.get("difference_amount"),
"currency": inv.get("currency"),
}
)
@@ -298,7 +323,11 @@ class PaymentReconciliation(Document):
else:
reconciled_entry = entry_list
reconciled_entry.append(self.get_payment_details(row, dr_or_cr))
payment_details = self.get_payment_details(row, dr_or_cr)
reconciled_entry.append(payment_details)
if payment_details.difference_amount:
self.make_difference_entry(payment_details)
if entry_list:
reconcile_against_document(entry_list)
@@ -309,6 +338,57 @@ class PaymentReconciliation(Document):
msgprint(_("Successfully Reconciled"))
self.get_unreconciled_entries()
def make_difference_entry(self, row):
journal_entry = frappe.new_doc("Journal Entry")
journal_entry.voucher_type = "Exchange Gain Or Loss"
journal_entry.company = self.company
journal_entry.posting_date = nowdate()
journal_entry.multi_currency = 1
party_account_currency = frappe.get_cached_value(
"Account", self.receivable_payable_account, "account_currency"
)
difference_account_currency = frappe.get_cached_value(
"Account", row.difference_account, "account_currency"
)
# Account Currency has balance
dr_or_cr = "debit" if self.party_type == "Customer" else "credit"
reverse_dr_or_cr = "debit" if dr_or_cr == "credit" else "credit"
journal_account = frappe._dict(
{
"account": self.receivable_payable_account,
"party_type": self.party_type,
"party": self.party,
"account_currency": party_account_currency,
"exchange_rate": 0,
"cost_center": erpnext.get_default_cost_center(self.company),
"reference_type": row.against_voucher_type,
"reference_name": row.against_voucher,
dr_or_cr: flt(row.difference_amount),
dr_or_cr + "_in_account_currency": 0,
}
)
journal_entry.append("accounts", journal_account)
journal_account = frappe._dict(
{
"account": row.difference_account,
"account_currency": difference_account_currency,
"exchange_rate": 1,
"cost_center": erpnext.get_default_cost_center(self.company),
reverse_dr_or_cr + "_in_account_currency": flt(row.difference_amount),
reverse_dr_or_cr: flt(row.difference_amount),
}
)
journal_entry.append("accounts", journal_account)
journal_entry.save()
journal_entry.submit()
def get_payment_details(self, row, dr_or_cr):
return frappe._dict(
{
@@ -318,6 +398,7 @@ class PaymentReconciliation(Document):
"against_voucher_type": row.get("invoice_type"),
"against_voucher": row.get("invoice_number"),
"account": self.receivable_payable_account,
"exchange_rate": row.get("exchange_rate"),
"party_type": self.party_type,
"party": self.party,
"is_advance": row.get("is_advance"),
@@ -342,6 +423,49 @@ class PaymentReconciliation(Document):
if not self.get("payments"):
frappe.throw(_("No records found in the Payments table"))
def get_invoice_exchange_map(self, invoices, payments):
sales_invoices = [
d.get("invoice_number") for d in invoices if d.get("invoice_type") == "Sales Invoice"
]
sales_invoices.extend(
[d.get("reference_name") for d in payments if d.get("reference_type") == "Sales Invoice"]
)
purchase_invoices = [
d.get("invoice_number") for d in invoices if d.get("invoice_type") == "Purchase Invoice"
]
purchase_invoices.extend(
[d.get("reference_name") for d in payments if d.get("reference_type") == "Purchase Invoice"]
)
invoice_exchange_map = frappe._dict()
if sales_invoices:
sales_invoice_map = frappe._dict(
frappe.db.get_all(
"Sales Invoice",
filters={"name": ("in", sales_invoices)},
fields=["name", "conversion_rate"],
as_list=1,
)
)
invoice_exchange_map.update(sales_invoice_map)
if purchase_invoices:
purchase_invoice_map = frappe._dict(
frappe.db.get_all(
"Purchase Invoice",
filters={"name": ("in", purchase_invoices)},
fields=["name", "conversion_rate"],
as_list=1,
)
)
invoice_exchange_map.update(purchase_invoice_map)
return invoice_exchange_map
def validate_allocation(self):
unreconciled_invoices = frappe._dict()
@@ -375,13 +499,14 @@ class PaymentReconciliation(Document):
def build_qb_filter_conditions(self, get_invoices=False, get_return_invoices=False):
self.common_filter_conditions.clear()
self.accounting_dimension_filter_conditions.clear()
self.ple_posting_date_filter.clear()
ple = qb.DocType("Payment Ledger Entry")
self.common_filter_conditions.append(ple.company == self.company)
if self.get("cost_center") and (get_invoices or get_return_invoices):
self.common_filter_conditions.append(ple.cost_center == self.cost_center)
self.accounting_dimension_filter_conditions.append(ple.cost_center == self.cost_center)
if get_invoices:
if self.from_invoice_date:

View File

@@ -5,9 +5,11 @@ import unittest
import frappe
from frappe import qb
from frappe.tests.utils import FrappeTestCase
from frappe.utils import add_days, nowdate
from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import add_days, flt, nowdate
from erpnext import get_default_cost_center
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.party import get_party_account
@@ -20,6 +22,7 @@ class TestPaymentReconciliation(FrappeTestCase):
self.create_item()
self.create_customer()
self.create_account()
self.create_cost_center()
self.clear_old_entries()
def tearDown(self):
@@ -72,33 +75,11 @@ class TestPaymentReconciliation(FrappeTestCase):
self.item = item if isinstance(item, str) else item.item_code
def create_customer(self):
if frappe.db.exists("Customer", "_Test PR Customer"):
self.customer = "_Test PR Customer"
else:
customer = frappe.new_doc("Customer")
customer.customer_name = "_Test PR Customer"
customer.type = "Individual"
customer.save()
self.customer = customer.name
if frappe.db.exists("Customer", "_Test PR Customer 2"):
self.customer2 = "_Test PR Customer 2"
else:
customer = frappe.new_doc("Customer")
customer.customer_name = "_Test PR Customer 2"
customer.type = "Individual"
customer.save()
self.customer2 = customer.name
if frappe.db.exists("Customer", "_Test PR Customer 3"):
self.customer3 = "_Test PR Customer 3"
else:
customer = frappe.new_doc("Customer")
customer.customer_name = "_Test PR Customer 3"
customer.type = "Individual"
customer.default_currency = "EUR"
customer.save()
self.customer3 = customer.name
self.customer = make_customer("_Test PR Customer")
self.customer2 = make_customer("_Test PR Customer 2")
self.customer3 = make_customer("_Test PR Customer 3", "EUR")
self.customer4 = make_customer("_Test PR Customer 4", "EUR")
self.customer5 = make_customer("_Test PR Customer 5", "EUR")
def create_account(self):
account_name = "Debtors EUR"
@@ -216,6 +197,22 @@ class TestPaymentReconciliation(FrappeTestCase):
)
return je
def create_cost_center(self):
# Setup cost center
cc_name = "Sub"
self.main_cc = frappe.get_doc("Cost Center", get_default_cost_center(self.company))
cc_exists = frappe.db.get_list("Cost Center", filters={"cost_center_name": cc_name})
if cc_exists:
self.sub_cc = frappe.get_doc("Cost Center", cc_exists[0].name)
else:
sub_cc = frappe.new_doc("Cost Center")
sub_cc.cost_center_name = "Sub"
sub_cc.parent_cost_center = self.main_cc.parent_cost_center
sub_cc.company = self.main_cc.company
self.sub_cc = sub_cc.save()
def test_filter_min_max(self):
# check filter condition minimum and maximum amount
self.create_sales_invoice(qty=1, rate=300)
@@ -352,6 +349,11 @@ class TestPaymentReconciliation(FrappeTestCase):
invoices = [x.as_dict() for x in pr.get("invoices")]
payments = [x.as_dict() for x in pr.get("payments")]
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
# Difference amount should not be calculated for base currency accounts
for row in pr.allocation:
self.assertEqual(flt(row.get("difference_amount")), 0.0)
pr.reconcile()
si.reload()
@@ -393,6 +395,11 @@ class TestPaymentReconciliation(FrappeTestCase):
invoices = [x.as_dict() for x in pr.get("invoices")]
payments = [x.as_dict() for x in pr.get("payments")]
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
# Difference amount should not be calculated for base currency accounts
for row in pr.allocation:
self.assertEqual(flt(row.get("difference_amount")), 0.0)
pr.reconcile()
# check PR tool output
@@ -417,6 +424,11 @@ class TestPaymentReconciliation(FrappeTestCase):
invoices = [x.as_dict() for x in pr.get("invoices")]
payments = [x.as_dict() for x in pr.get("payments")]
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
# Difference amount should not be calculated for base currency accounts
for row in pr.allocation:
self.assertEqual(flt(row.get("difference_amount")), 0.0)
pr.reconcile()
# assert outstanding
@@ -453,6 +465,11 @@ class TestPaymentReconciliation(FrappeTestCase):
invoices = [x.as_dict() for x in pr.get("invoices")]
payments = [x.as_dict() for x in pr.get("payments")]
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
# Difference amount should not be calculated for base currency accounts
for row in pr.allocation:
self.assertEqual(flt(row.get("difference_amount")), 0.0)
pr.reconcile()
self.assertEqual(pr.get("invoices"), [])
@@ -476,6 +493,11 @@ class TestPaymentReconciliation(FrappeTestCase):
invoices = [x.as_dict() for x in pr.get("invoices")]
payments = [x.as_dict() for x in pr.get("payments")]
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
# Cr Note and Invoice are of the same currency. There shouldn't any difference amount.
for row in pr.allocation:
self.assertEqual(flt(row.get("difference_amount")), 0.0)
pr.reconcile()
pr.get_unreconciled_entries()
@@ -509,6 +531,11 @@ class TestPaymentReconciliation(FrappeTestCase):
payments = [x.as_dict() for x in pr.get("payments")]
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
pr.allocation[0].allocated_amount = allocated_amount
# Cr Note and Invoice are of the same currency. There shouldn't any difference amount.
for row in pr.allocation:
self.assertEqual(flt(row.get("difference_amount")), 0.0)
pr.reconcile()
# assert outstanding
@@ -578,3 +605,301 @@ class TestPaymentReconciliation(FrappeTestCase):
self.assertEqual(len(pr.payments), 1)
self.assertEqual(pr.payments[0].amount, amount)
self.assertEqual(pr.payments[0].currency, "EUR")
def test_difference_amount_via_journal_entry(self):
# Make Sale Invoice
si = self.create_sales_invoice(
qty=1, rate=100, posting_date=nowdate(), do_not_save=True, do_not_submit=True
)
si.customer = self.customer4
si.currency = "EUR"
si.conversion_rate = 85
si.debit_to = self.debtors_eur
si.save().submit()
# Make payment using Journal Entry
je1 = self.create_journal_entry("HDFC - _PR", self.debtors_eur, 100, nowdate())
je1.multi_currency = 1
je1.accounts[0].exchange_rate = 1
je1.accounts[0].credit_in_account_currency = 0
je1.accounts[0].credit = 0
je1.accounts[0].debit_in_account_currency = 8000
je1.accounts[0].debit = 8000
je1.accounts[1].party_type = "Customer"
je1.accounts[1].party = self.customer4
je1.accounts[1].exchange_rate = 80
je1.accounts[1].credit_in_account_currency = 100
je1.accounts[1].credit = 8000
je1.accounts[1].debit_in_account_currency = 0
je1.accounts[1].debit = 0
je1.save()
je1.submit()
je2 = self.create_journal_entry("HDFC - _PR", self.debtors_eur, 200, nowdate())
je2.multi_currency = 1
je2.accounts[0].exchange_rate = 1
je2.accounts[0].credit_in_account_currency = 0
je2.accounts[0].credit = 0
je2.accounts[0].debit_in_account_currency = 16000
je2.accounts[0].debit = 16000
je2.accounts[1].party_type = "Customer"
je2.accounts[1].party = self.customer4
je2.accounts[1].exchange_rate = 80
je2.accounts[1].credit_in_account_currency = 200
je1.accounts[1].credit = 16000
je1.accounts[1].debit_in_account_currency = 0
je1.accounts[1].debit = 0
je2.save()
je2.submit()
pr = self.create_payment_reconciliation()
pr.party = self.customer4
pr.receivable_payable_account = self.debtors_eur
pr.get_unreconciled_entries()
self.assertEqual(len(pr.invoices), 1)
self.assertEqual(len(pr.payments), 2)
# Test exact payment allocation
invoices = [x.as_dict() for x in pr.invoices]
payments = [pr.payments[0].as_dict()]
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
self.assertEqual(pr.allocation[0].allocated_amount, 100)
self.assertEqual(pr.allocation[0].difference_amount, -500)
# Test partial payment allocation (with excess payment entry)
pr.set("allocation", [])
pr.get_unreconciled_entries()
invoices = [x.as_dict() for x in pr.invoices]
payments = [pr.payments[1].as_dict()]
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
pr.allocation[0].difference_account = "Exchange Gain/Loss - _PR"
self.assertEqual(pr.allocation[0].allocated_amount, 100)
self.assertEqual(pr.allocation[0].difference_amount, -500)
# Check if difference journal entry gets generated for difference amount after reconciliation
pr.reconcile()
total_debit_amount = frappe.db.get_all(
"Journal Entry Account",
{"account": self.debtors_eur, "docstatus": 1, "reference_name": si.name},
"sum(debit) as amount",
group_by="reference_name",
)[0].amount
self.assertEqual(flt(total_debit_amount, 2), -500)
def test_difference_amount_via_payment_entry(self):
# Make Sale Invoice
si = self.create_sales_invoice(
qty=1, rate=100, posting_date=nowdate(), do_not_save=True, do_not_submit=True
)
si.customer = self.customer5
si.currency = "EUR"
si.conversion_rate = 85
si.debit_to = self.debtors_eur
si.save().submit()
# Make payment using Payment Entry
pe1 = create_payment_entry(
company=self.company,
payment_type="Receive",
party_type="Customer",
party=self.customer5,
paid_from=self.debtors_eur,
paid_to=self.bank,
paid_amount=100,
)
pe1.source_exchange_rate = 80
pe1.received_amount = 8000
pe1.save()
pe1.submit()
pe2 = create_payment_entry(
company=self.company,
payment_type="Receive",
party_type="Customer",
party=self.customer5,
paid_from=self.debtors_eur,
paid_to=self.bank,
paid_amount=200,
)
pe2.source_exchange_rate = 80
pe2.received_amount = 16000
pe2.save()
pe2.submit()
pr = self.create_payment_reconciliation()
pr.party = self.customer5
pr.receivable_payable_account = self.debtors_eur
pr.get_unreconciled_entries()
self.assertEqual(len(pr.invoices), 1)
self.assertEqual(len(pr.payments), 2)
invoices = [x.as_dict() for x in pr.invoices]
payments = [pr.payments[0].as_dict()]
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
self.assertEqual(pr.allocation[0].allocated_amount, 100)
self.assertEqual(pr.allocation[0].difference_amount, -500)
pr.set("allocation", [])
pr.get_unreconciled_entries()
invoices = [x.as_dict() for x in pr.invoices]
payments = [pr.payments[1].as_dict()]
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
self.assertEqual(pr.allocation[0].allocated_amount, 100)
self.assertEqual(pr.allocation[0].difference_amount, -500)
def test_differing_cost_center_on_invoice_and_payment(self):
"""
Cost Center filter should not affect outstanding amount calculation
"""
si = self.create_sales_invoice(qty=1, rate=100, do_not_submit=True)
si.cost_center = self.main_cc.name
si.submit()
pr = get_payment_entry(si.doctype, si.name)
pr.cost_center = self.sub_cc.name
pr = pr.save().submit()
pr = self.create_payment_reconciliation()
pr.cost_center = self.main_cc.name
pr.get_unreconciled_entries()
# check PR tool output
self.assertEqual(len(pr.get("invoices")), 0)
self.assertEqual(len(pr.get("payments")), 0)
def test_cost_center_filter_on_vouchers(self):
"""
Test Cost Center filter is applied on Invoices, Payment Entries and Journals
"""
transaction_date = nowdate()
rate = 100
# 'Main - PR' Cost Center
si1 = self.create_sales_invoice(
qty=1, rate=rate, posting_date=transaction_date, do_not_submit=True
)
si1.cost_center = self.main_cc.name
si1.submit()
pe1 = self.create_payment_entry(posting_date=transaction_date, amount=rate)
pe1.cost_center = self.main_cc.name
pe1 = pe1.save().submit()
je1 = self.create_journal_entry(self.bank, self.debit_to, 100, transaction_date)
je1.accounts[0].cost_center = self.main_cc.name
je1.accounts[1].cost_center = self.main_cc.name
je1.accounts[1].party_type = "Customer"
je1.accounts[1].party = self.customer
je1 = je1.save().submit()
# 'Sub - PR' Cost Center
si2 = self.create_sales_invoice(
qty=1, rate=rate, posting_date=transaction_date, do_not_submit=True
)
si2.cost_center = self.sub_cc.name
si2.submit()
pe2 = self.create_payment_entry(posting_date=transaction_date, amount=rate)
pe2.cost_center = self.sub_cc.name
pe2 = pe2.save().submit()
je2 = self.create_journal_entry(self.bank, self.debit_to, 100, transaction_date)
je2.accounts[0].cost_center = self.sub_cc.name
je2.accounts[1].cost_center = self.sub_cc.name
je2.accounts[1].party_type = "Customer"
je2.accounts[1].party = self.customer
je2 = je2.save().submit()
pr = self.create_payment_reconciliation()
pr.cost_center = self.main_cc.name
pr.get_unreconciled_entries()
# check PR tool output
self.assertEqual(len(pr.get("invoices")), 1)
self.assertEqual(pr.get("invoices")[0].get("invoice_number"), si1.name)
self.assertEqual(len(pr.get("payments")), 2)
payment_vouchers = [x.get("reference_name") for x in pr.get("payments")]
self.assertCountEqual(payment_vouchers, [pe1.name, je1.name])
# Change cost center
pr.cost_center = self.sub_cc.name
pr.get_unreconciled_entries()
# check PR tool output
self.assertEqual(len(pr.get("invoices")), 1)
self.assertEqual(pr.get("invoices")[0].get("invoice_number"), si2.name)
self.assertEqual(len(pr.get("payments")), 2)
payment_vouchers = [x.get("reference_name") for x in pr.get("payments")]
self.assertCountEqual(payment_vouchers, [je2.name, pe2.name])
@change_settings(
"Accounts Settings",
{
"allow_multi_currency_invoices_against_single_party_account": 1,
},
)
def test_no_difference_amount_for_base_currency_accounts(self):
# Make Sale Invoice
si = self.create_sales_invoice(
qty=1, rate=1, posting_date=nowdate(), do_not_save=True, do_not_submit=True
)
si.customer = self.customer
si.currency = "EUR"
si.conversion_rate = 85
si.debit_to = self.debit_to
si.save().submit()
# Make payment using Payment Entry
pe1 = create_payment_entry(
company=self.company,
payment_type="Receive",
party_type="Customer",
party=self.customer,
paid_from=self.debit_to,
paid_to=self.bank,
paid_amount=100,
)
pe1.save()
pe1.submit()
pr = self.create_payment_reconciliation()
pr.party = self.customer
pr.receivable_payable_account = self.debit_to
pr.get_unreconciled_entries()
self.assertEqual(len(pr.invoices), 1)
self.assertEqual(len(pr.payments), 1)
invoices = [x.as_dict() for x in pr.invoices]
payments = [pr.payments[0].as_dict()]
pr.allocate_entries(frappe._dict({"invoices": invoices, "payments": payments}))
self.assertEqual(pr.allocation[0].allocated_amount, 85)
self.assertEqual(pr.allocation[0].difference_amount, 0)
def make_customer(customer_name, currency=None):
if not frappe.db.exists("Customer", customer_name):
customer = frappe.new_doc("Customer")
customer.customer_name = customer_name
customer.type = "Individual"
if currency:
customer.default_currency = currency
customer.save()
return customer.name
else:
return customer_name

View File

@@ -20,7 +20,9 @@
"section_break_5",
"difference_amount",
"column_break_7",
"difference_account"
"difference_account",
"exchange_rate",
"currency"
],
"fields": [
{
@@ -37,7 +39,7 @@
"fieldtype": "Currency",
"in_list_view": 1,
"label": "Allocated Amount",
"options": "Currency",
"options": "currency",
"reqd": 1
},
{
@@ -112,7 +114,7 @@
"fieldtype": "Currency",
"hidden": 1,
"label": "Unreconciled Amount",
"options": "Currency",
"options": "currency",
"read_only": 1
},
{
@@ -120,7 +122,7 @@
"fieldtype": "Currency",
"hidden": 1,
"label": "Amount",
"options": "Currency",
"options": "currency",
"read_only": 1
},
{
@@ -129,11 +131,24 @@
"hidden": 1,
"label": "Reference Row",
"read_only": 1
},
{
"fieldname": "currency",
"fieldtype": "Link",
"hidden": 1,
"label": "Currency",
"options": "Currency"
},
{
"fieldname": "exchange_rate",
"fieldtype": "Float",
"label": "Exchange Rate",
"read_only": 1
}
],
"istable": 1,
"links": [],
"modified": "2021-10-06 11:48:59.616562",
"modified": "2022-12-24 21:01:14.882747",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Reconciliation Allocation",
@@ -141,5 +156,6 @@
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}

View File

@@ -11,7 +11,8 @@
"col_break1",
"amount",
"outstanding_amount",
"currency"
"currency",
"exchange_rate"
],
"fields": [
{
@@ -62,11 +63,17 @@
"hidden": 1,
"label": "Currency",
"options": "Currency"
},
{
"fieldname": "exchange_rate",
"fieldtype": "Float",
"hidden": 1,
"label": "Exchange Rate"
}
],
"istable": 1,
"links": [],
"modified": "2021-08-24 22:42:40.923179",
"modified": "2022-11-08 18:18:02.502149",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Reconciliation Invoice",
@@ -75,5 +82,6 @@
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}

View File

@@ -15,7 +15,8 @@
"difference_amount",
"sec_break1",
"remark",
"currency"
"currency",
"exchange_rate"
],
"fields": [
{
@@ -91,11 +92,17 @@
"label": "Difference Amount",
"options": "currency",
"read_only": 1
},
{
"fieldname": "exchange_rate",
"fieldtype": "Float",
"hidden": 1,
"label": "Exchange Rate"
}
],
"istable": 1,
"links": [],
"modified": "2021-08-30 10:51:48.140062",
"modified": "2022-11-08 18:18:36.268760",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Reconciliation Payment",
@@ -103,5 +110,6 @@
"permissions": [],
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC"
"sort_order": "DESC",
"states": []
}

View File

@@ -42,7 +42,7 @@ frappe.ui.form.on("Payment Request", "refresh", function(frm) {
});
}
if(!frm.doc.payment_gateway_account && frm.doc.status == "Initiated") {
if((!frm.doc.payment_gateway_account || frm.doc.payment_request_type == "Outward") && frm.doc.status == "Initiated") {
frm.add_custom_button(__('Create Payment Entry'), function(){
frappe.call({
method: "erpnext.accounts.doctype.payment_request.payment_request.make_payment_entry",

View File

@@ -32,6 +32,10 @@
"iban",
"branch_code",
"swift_number",
"accounting_dimensions_section",
"cost_center",
"dimension_col_break",
"project",
"recipient_and_message",
"print_format",
"email_to",
@@ -362,13 +366,35 @@
"label": "Payment Channel",
"options": "\nEmail\nPhone",
"read_only": 1
},
{
"collapsible": 1,
"fieldname": "accounting_dimensions_section",
"fieldtype": "Section Break",
"label": "Accounting Dimensions"
},
{
"fieldname": "cost_center",
"fieldtype": "Link",
"label": "Cost Center",
"options": "Cost Center"
},
{
"fieldname": "dimension_col_break",
"fieldtype": "Column Break"
},
{
"fieldname": "project",
"fieldtype": "Link",
"label": "Project",
"options": "Project"
}
],
"in_create": 1,
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
"modified": "2022-09-30 16:19:43.680025",
"modified": "2022-12-21 16:56:40.115737",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Request",

View File

@@ -9,8 +9,10 @@ from frappe import _
from frappe.model.document import Document
from frappe.utils import flt, get_url, nowdate
from frappe.utils.background_jobs import enqueue
from payments.utils import get_payment_gateway_controller
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_accounting_dimensions,
)
from erpnext.accounts.doctype.payment_entry.payment_entry import (
get_company_defaults,
get_payment_entry,
@@ -19,6 +21,14 @@ from erpnext.accounts.doctype.subscription_plan.subscription_plan import get_pla
from erpnext.accounts.party import get_party_account, get_party_bank_account
from erpnext.accounts.utils import get_account_currency
from erpnext.erpnext_integrations.stripe_integration import create_stripe_subscription
from erpnext.utilities import payment_app_import_guard
def _get_payment_gateway_controller(*args, **kwargs):
with payment_app_import_guard():
from payments.utils import get_payment_gateway_controller
return get_payment_gateway_controller(*args, **kwargs)
class PaymentRequest(Document):
@@ -35,21 +45,20 @@ class PaymentRequest(Document):
frappe.throw(_("To create a Payment Request reference document is required"))
def validate_payment_request_amount(self):
existing_payment_request_amount = get_existing_payment_request_amount(
self.reference_doctype, self.reference_name
existing_payment_request_amount = flt(
get_existing_payment_request_amount(self.reference_doctype, self.reference_name)
)
if existing_payment_request_amount:
ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
if hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") != "Shopping Cart":
ref_amount = get_amount(ref_doc, self.payment_account)
ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
if not hasattr(ref_doc, "order_type") or getattr(ref_doc, "order_type") != "Shopping Cart":
ref_amount = get_amount(ref_doc, self.payment_account)
if existing_payment_request_amount + flt(self.grand_total) > ref_amount:
frappe.throw(
_("Total Payment Request amount cannot be greater than {0} amount").format(
self.reference_doctype
)
if existing_payment_request_amount + flt(self.grand_total) > ref_amount:
frappe.throw(
_("Total Payment Request amount cannot be greater than {0} amount").format(
self.reference_doctype
)
)
def validate_currency(self):
ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
@@ -107,7 +116,7 @@ class PaymentRequest(Document):
self.request_phone_payment()
def request_phone_payment(self):
controller = get_payment_gateway_controller(self.payment_gateway)
controller = _get_payment_gateway_controller(self.payment_gateway)
request_amount = self.get_request_amount()
payment_record = dict(
@@ -156,7 +165,7 @@ class PaymentRequest(Document):
def payment_gateway_validation(self):
try:
controller = get_payment_gateway_controller(self.payment_gateway)
controller = _get_payment_gateway_controller(self.payment_gateway)
if hasattr(controller, "on_payment_request_submission"):
return controller.on_payment_request_submission(self)
else:
@@ -189,7 +198,7 @@ class PaymentRequest(Document):
)
data.update({"company": frappe.defaults.get_defaults().company})
controller = get_payment_gateway_controller(self.payment_gateway)
controller = _get_payment_gateway_controller(self.payment_gateway)
controller.validate_transaction_currency(self.currency)
if hasattr(controller, "validate_minimum_transaction_amount"):
@@ -254,6 +263,7 @@ class PaymentRequest(Document):
payment_entry.update(
{
"mode_of_payment": self.mode_of_payment,
"reference_no": self.name,
"reference_date": nowdate(),
"remarks": "Payment Entry against {0} {1} via Payment Request {2}".format(
@@ -262,6 +272,17 @@ class PaymentRequest(Document):
}
)
# Update dimensions
payment_entry.update(
{
"cost_center": self.get("cost_center"),
"project": self.get("project"),
}
)
for dimension in get_accounting_dimensions():
payment_entry.update({dimension: self.get(dimension)})
if payment_entry.difference_amount:
company_details = get_company_defaults(ref_doc.company)
@@ -403,25 +424,22 @@ def make_payment_request(**args):
else ""
)
existing_payment_request = None
if args.order_type == "Shopping Cart":
existing_payment_request = frappe.db.get_value(
"Payment Request",
{"reference_doctype": args.dt, "reference_name": args.dn, "docstatus": ("!=", 2)},
)
draft_payment_request = frappe.db.get_value(
"Payment Request",
{"reference_doctype": args.dt, "reference_name": args.dn, "docstatus": 0},
)
if existing_payment_request:
existing_payment_request_amount = get_existing_payment_request_amount(args.dt, args.dn)
if existing_payment_request_amount:
grand_total -= existing_payment_request_amount
if draft_payment_request:
frappe.db.set_value(
"Payment Request", existing_payment_request, "grand_total", grand_total, update_modified=False
"Payment Request", draft_payment_request, "grand_total", grand_total, update_modified=False
)
pr = frappe.get_doc("Payment Request", existing_payment_request)
pr = frappe.get_doc("Payment Request", draft_payment_request)
else:
if args.order_type != "Shopping Cart":
existing_payment_request_amount = get_existing_payment_request_amount(args.dt, args.dn)
if existing_payment_request_amount:
grand_total -= existing_payment_request_amount
pr = frappe.new_doc("Payment Request")
pr.update(
{
@@ -444,6 +462,17 @@ def make_payment_request(**args):
}
)
# Update dimensions
pr.update(
{
"cost_center": ref_doc.get("cost_center"),
"project": ref_doc.get("project"),
}
)
for dimension in get_accounting_dimensions():
pr.update({dimension: ref_doc.get(dimension)})
if args.order_type == "Shopping Cart" or args.mute_email:
pr.flags.mute_email = True
@@ -466,26 +495,28 @@ def get_amount(ref_doc, payment_account=None):
"""get amount based on doctype"""
dt = ref_doc.doctype
if dt in ["Sales Order", "Purchase Order"]:
grand_total = flt(ref_doc.grand_total) - flt(ref_doc.advance_paid)
grand_total = flt(ref_doc.rounded_total) or flt(ref_doc.grand_total)
elif dt in ["Sales Invoice", "Purchase Invoice"]:
if ref_doc.party_account_currency == ref_doc.currency:
grand_total = flt(ref_doc.outstanding_amount)
else:
grand_total = flt(ref_doc.outstanding_amount) / ref_doc.conversion_rate
if not ref_doc.get("is_pos"):
if ref_doc.party_account_currency == ref_doc.currency:
grand_total = flt(ref_doc.outstanding_amount)
else:
grand_total = flt(ref_doc.outstanding_amount) / ref_doc.conversion_rate
elif dt == "Sales Invoice":
for pay in ref_doc.payments:
if pay.type == "Phone" and pay.account == payment_account:
grand_total = pay.amount
break
elif dt == "POS Invoice":
for pay in ref_doc.payments:
if pay.type == "Phone" and pay.account == payment_account:
grand_total = pay.amount
break
elif dt == "Fees":
grand_total = ref_doc.outstanding_amount
if grand_total > 0:
return grand_total
else:
frappe.throw(_("Payment Entry is already created"))

View File

@@ -6,6 +6,7 @@ import unittest
import frappe
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.selling.doctype.sales_order.test_sales_order import make_sales_order
from erpnext.setup.utils import get_exchange_rate
@@ -45,7 +46,10 @@ class TestPaymentRequest(unittest.TestCase):
frappe.get_doc(method).insert(ignore_permissions=True)
def test_payment_request_linkings(self):
so_inr = make_sales_order(currency="INR")
so_inr = make_sales_order(currency="INR", do_not_save=True)
so_inr.disable_rounded_total = 1
so_inr.save()
pr = make_payment_request(
dt="Sales Order",
dn=so_inr.name,
@@ -71,6 +75,29 @@ class TestPaymentRequest(unittest.TestCase):
self.assertEqual(pr.reference_name, si_usd.name)
self.assertEqual(pr.currency, "USD")
def test_payment_entry_against_purchase_invoice(self):
si_usd = make_purchase_invoice(
customer="_Test Supplier USD",
debit_to="_Test Payable USD - _TC",
currency="USD",
conversion_rate=50,
)
pr = make_payment_request(
dt="Purchase Invoice",
dn=si_usd.name,
recipient_id="user@example.com",
mute_email=1,
payment_gateway_account="_Test Gateway - USD",
submit_doc=1,
return_doc=1,
)
pe = pr.create_payment_entry()
pr.load_from_db()
self.assertEqual(pr.status, "Paid")
def test_payment_entry(self):
frappe.db.set_value(
"Company", "_Test Company", "exchange_gain_loss_account", "_Test Exchange Gain/Loss - _TC"

View File

@@ -4,12 +4,13 @@
import frappe
from frappe import _
from frappe.utils import flt
from frappe.query_builder.functions import Sum
from frappe.utils import add_days, flt
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_accounting_dimensions,
)
from erpnext.accounts.utils import get_account_currency
from erpnext.accounts.utils import get_account_currency, get_fiscal_year, validate_fiscal_year
from erpnext.controllers.accounts_controller import AccountsController
@@ -20,9 +21,17 @@ class PeriodClosingVoucher(AccountsController):
def on_submit(self):
self.db_set("gle_processing_status", "In Progress")
self.make_gl_entries()
get_opening_entries = False
if not frappe.db.exists(
"Period Closing Voucher", {"company": self.company, "docstatus": 1, "name": ("!=", self.name)}
):
get_opening_entries = True
self.make_gl_entries(get_opening_entries=get_opening_entries)
def on_cancel(self):
self.validate_future_closing_vouchers()
self.db_set("gle_processing_status", "In Progress")
self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry")
gle_count = frappe.db.count(
@@ -42,6 +51,25 @@ class PeriodClosingVoucher(AccountsController):
else:
make_reverse_gl_entries(voucher_type="Period Closing Voucher", voucher_no=self.name)
self.delete_closing_entries()
def validate_future_closing_vouchers(self):
if frappe.db.exists(
"Period Closing Voucher",
{"posting_date": (">", self.posting_date), "docstatus": 1, "company": self.company},
):
frappe.throw(
_(
"You can not cancel this Period Closing Voucher, please cancel the future Period Closing Vouchers first"
)
)
def delete_closing_entries(self):
closing_balance = frappe.qb.DocType("Account Closing Balance")
frappe.qb.from_(closing_balance).delete().where(
closing_balance.period_closing_voucher == self.name
).run()
def validate_account_head(self):
closing_account_type = frappe.get_cached_value("Account", self.closing_account_head, "root_type")
@@ -56,8 +84,6 @@ class PeriodClosingVoucher(AccountsController):
frappe.throw(_("Currency of the Closing Account must be {0}").format(company_currency))
def validate_posting_date(self):
from erpnext.accounts.utils import get_fiscal_year, validate_fiscal_year
validate_fiscal_year(
self.posting_date, self.fiscal_year, self.company, label=_("Posting Date"), doc=self
)
@@ -66,6 +92,8 @@ class PeriodClosingVoucher(AccountsController):
self.posting_date, self.fiscal_year, company=self.company
)[1]
self.check_if_previous_year_closed()
pce = frappe.db.sql(
"""select name from `tabPeriod Closing Voucher`
where posting_date > %s and fiscal_year = %s and docstatus = 1 and company = %s""",
@@ -78,28 +106,64 @@ class PeriodClosingVoucher(AccountsController):
)
)
def make_gl_entries(self):
def check_if_previous_year_closed(self):
last_year_closing = add_days(self.year_start_date, -1)
previous_fiscal_year = get_fiscal_year(last_year_closing, company=self.company, boolean=True)
if previous_fiscal_year and not frappe.db.exists(
"GL Entry", {"posting_date": ("<=", last_year_closing), "company": self.company}
):
return
if previous_fiscal_year and not frappe.db.exists(
"Period Closing Voucher",
{"posting_date": ("<=", last_year_closing), "docstatus": 1, "company": self.company},
):
frappe.throw(_("Previous Year is not closed, please close it first"))
def make_gl_entries(self, get_opening_entries=False):
gl_entries = self.get_gl_entries()
closing_entries = self.get_grouped_gl_entries(get_opening_entries=get_opening_entries)
if gl_entries:
if len(gl_entries) > 5000:
frappe.enqueue(process_gl_entries, gl_entries=gl_entries, queue="long")
frappe.enqueue(
process_gl_entries,
gl_entries=gl_entries,
closing_entries=closing_entries,
voucher_name=self.name,
queue="long",
)
frappe.msgprint(
_("The GL Entries will be processed in the background, it can take a few minutes."),
alert=True,
)
else:
process_gl_entries(gl_entries)
process_gl_entries(gl_entries, closing_entries, voucher_name=self.name)
def get_grouped_gl_entries(self, get_opening_entries=False):
closing_entries = []
for acc in self.get_balances_based_on_dimensions(
group_by_account=True, for_aggregation=True, get_opening_entries=get_opening_entries
):
closing_entries.append(self.get_closing_entries(acc))
return closing_entries
def get_gl_entries(self):
gl_entries = []
# pl account
for acc in self.get_pl_balances_based_on_dimensions(group_by_account=True):
for acc in self.get_balances_based_on_dimensions(
group_by_account=True, report_type="Profit and Loss"
):
if flt(acc.bal_in_company_currency):
gl_entries.append(self.get_gle_for_pl_account(acc))
# closing liability account
for acc in self.get_pl_balances_based_on_dimensions(group_by_account=False):
for acc in self.get_balances_based_on_dimensions(
group_by_account=False, report_type="Profit and Loss"
):
if flt(acc.bal_in_company_currency):
gl_entries.append(self.get_gle_for_closing_account(acc))
@@ -108,6 +172,8 @@ class PeriodClosingVoucher(AccountsController):
def get_gle_for_pl_account(self, acc):
gl_entry = self.get_gl_dict(
{
"company": self.company,
"closing_date": self.posting_date,
"account": acc.account,
"cost_center": acc.cost_center,
"finance_book": acc.finance_book,
@@ -120,6 +186,7 @@ class PeriodClosingVoucher(AccountsController):
if flt(acc.bal_in_account_currency) > 0
else 0,
"credit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) > 0 else 0,
"is_period_closing_voucher_entry": 1,
},
item=acc,
)
@@ -129,6 +196,8 @@ class PeriodClosingVoucher(AccountsController):
def get_gle_for_closing_account(self, acc):
gl_entry = self.get_gl_dict(
{
"company": self.company,
"closing_date": self.posting_date,
"account": self.closing_account_head,
"cost_center": acc.cost_center,
"finance_book": acc.finance_book,
@@ -141,12 +210,36 @@ class PeriodClosingVoucher(AccountsController):
if flt(acc.bal_in_account_currency) < 0
else 0,
"credit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) < 0 else 0,
"is_period_closing_voucher_entry": 1,
},
item=acc,
)
self.update_default_dimensions(gl_entry, acc)
return gl_entry
def get_closing_entries(self, acc):
closing_entry = self.get_gl_dict(
{
"company": self.company,
"closing_date": self.posting_date,
"period_closing_voucher": self.name,
"account": acc.account,
"cost_center": acc.cost_center,
"finance_book": acc.finance_book,
"account_currency": acc.account_currency,
"debit_in_account_currency": flt(acc.debit_in_account_currency),
"debit": flt(acc.debit),
"credit_in_account_currency": flt(acc.credit_in_account_currency),
"credit": flt(acc.credit),
},
item=acc,
)
for dimension in self.accounting_dimensions:
closing_entry.update({dimension: acc.get(dimension)})
return closing_entry
def update_default_dimensions(self, gl_entry, acc):
if not self.accounting_dimensions:
self.accounting_dimensions = get_accounting_dimensions()
@@ -154,47 +247,88 @@ class PeriodClosingVoucher(AccountsController):
for dimension in self.accounting_dimensions:
gl_entry.update({dimension: acc.get(dimension)})
def get_pl_balances_based_on_dimensions(self, group_by_account=False):
def get_balances_based_on_dimensions(
self, group_by_account=False, report_type=None, for_aggregation=False, get_opening_entries=False
):
"""Get balance for dimension-wise pl accounts"""
dimension_fields = ["t1.cost_center", "t1.finance_book"]
qb_dimension_fields = ["cost_center", "finance_book", "project"]
self.accounting_dimensions = get_accounting_dimensions()
for dimension in self.accounting_dimensions:
dimension_fields.append("t1.{0}".format(dimension))
qb_dimension_fields.append(dimension)
if group_by_account:
dimension_fields.append("t1.account")
qb_dimension_fields.append("account")
return frappe.db.sql(
"""
select
t2.account_currency,
{dimension_fields},
sum(t1.debit_in_account_currency) - sum(t1.credit_in_account_currency) as bal_in_account_currency,
sum(t1.debit) - sum(t1.credit) as bal_in_company_currency
from `tabGL Entry` t1, `tabAccount` t2
where
t1.is_cancelled = 0
and t1.account = t2.name
and t2.report_type = 'Profit and Loss'
and t2.docstatus < 2
and t2.company = %s
and t1.posting_date between %s and %s
group by {dimension_fields}
""".format(
dimension_fields=", ".join(dimension_fields)
),
(self.company, self.get("year_start_date"), self.posting_date),
as_dict=1,
account_filters = {
"company": self.company,
"is_group": 0,
}
if report_type:
account_filters.update({"report_type": report_type})
accounts = frappe.get_all("Account", filters=account_filters, pluck="name")
gl_entry = frappe.qb.DocType("GL Entry")
query = frappe.qb.from_(gl_entry).select(gl_entry.account, gl_entry.account_currency)
if not for_aggregation:
query = query.select(
(Sum(gl_entry.debit_in_account_currency) - Sum(gl_entry.credit_in_account_currency)).as_(
"bal_in_account_currency"
),
(Sum(gl_entry.debit) - Sum(gl_entry.credit)).as_("bal_in_company_currency"),
)
else:
query = query.select(
(Sum(gl_entry.debit_in_account_currency)).as_("debit_in_account_currency"),
(Sum(gl_entry.credit_in_account_currency)).as_("credit_in_account_currency"),
(Sum(gl_entry.debit)).as_("debit"),
(Sum(gl_entry.credit)).as_("credit"),
)
for dimension in qb_dimension_fields:
query = query.select(gl_entry[dimension])
query = query.where(
(gl_entry.company == self.company)
& (gl_entry.is_cancelled == 0)
& (gl_entry.account.isin(accounts))
)
if get_opening_entries:
query = query.where(
gl_entry.posting_date.between(self.get("year_start_date"), self.posting_date)
| gl_entry.is_opening
== "Yes"
)
else:
query = query.where(
gl_entry.posting_date.between(self.get("year_start_date"), self.posting_date)
& gl_entry.is_opening
== "No"
)
def process_gl_entries(gl_entries):
if for_aggregation:
query = query.where(gl_entry.voucher_type != "Period Closing Voucher")
for dimension in qb_dimension_fields:
query = query.groupby(gl_entry[dimension])
return query.run(as_dict=1)
def process_gl_entries(gl_entries, closing_entries, voucher_name=None):
from erpnext.accounts.doctype.account_closing_balance.account_closing_balance import (
make_closing_entries,
)
from erpnext.accounts.general_ledger import make_gl_entries
try:
make_gl_entries(gl_entries, merge_entries=False)
make_closing_entries(gl_entries + closing_entries, voucher_name=voucher_name)
frappe.db.set_value(
"Period Closing Voucher", gl_entries[0].get("voucher_no"), "gle_processing_status", "Completed"
)

View File

@@ -16,16 +16,17 @@ from erpnext.accounts.utils import get_fiscal_year, now
class TestPeriodClosingVoucher(unittest.TestCase):
def test_closing_entry(self):
frappe.db.sql("delete from `tabGL Entry` where company='Test PCV Company'")
frappe.db.sql("delete from `tabPeriod Closing Voucher` where company='Test PCV Company'")
company = create_company()
cost_center = create_cost_center("Test Cost Center 1")
jv1 = make_journal_entry(
posting_date="2021-03-15",
amount=400,
account1="Cash - TPC",
account2="Sales - TPC",
cost_center=cost_center,
posting_date=now(),
save=False,
)
jv1.company = company
@@ -33,18 +34,18 @@ class TestPeriodClosingVoucher(unittest.TestCase):
jv1.submit()
jv2 = make_journal_entry(
posting_date="2021-03-15",
amount=600,
account1="Cost of Goods Sold - TPC",
account2="Cash - TPC",
cost_center=cost_center,
posting_date=now(),
save=False,
)
jv2.company = company
jv2.save()
jv2.submit()
pcv = self.make_period_closing_voucher()
pcv = self.make_period_closing_voucher(posting_date="2021-03-31")
surplus_account = pcv.closing_account_head
expected_gle = (
@@ -65,6 +66,7 @@ class TestPeriodClosingVoucher(unittest.TestCase):
def test_cost_center_wise_posting(self):
frappe.db.sql("delete from `tabGL Entry` where company='Test PCV Company'")
frappe.db.sql("delete from `tabPeriod Closing Voucher` where company='Test PCV Company'")
company = create_company()
surplus_account = create_account()
@@ -81,6 +83,7 @@ class TestPeriodClosingVoucher(unittest.TestCase):
debit_to="Debtors - TPC",
currency="USD",
customer="_Test Customer USD",
posting_date="2021-03-15",
)
create_sales_invoice(
company=company,
@@ -91,9 +94,10 @@ class TestPeriodClosingVoucher(unittest.TestCase):
debit_to="Debtors - TPC",
currency="USD",
customer="_Test Customer USD",
posting_date="2021-03-15",
)
pcv = self.make_period_closing_voucher(submit=False)
pcv = self.make_period_closing_voucher(posting_date="2021-03-31", submit=False)
pcv.save()
pcv.submit()
surplus_account = pcv.closing_account_head
@@ -128,12 +132,13 @@ class TestPeriodClosingVoucher(unittest.TestCase):
def test_period_closing_with_finance_book_entries(self):
frappe.db.sql("delete from `tabGL Entry` where company='Test PCV Company'")
frappe.db.sql("delete from `tabPeriod Closing Voucher` where company='Test PCV Company'")
company = create_company()
surplus_account = create_account()
cost_center = create_cost_center("Test Cost Center 1")
si = create_sales_invoice(
create_sales_invoice(
company=company,
income_account="Sales - TPC",
expense_account="Cost of Goods Sold - TPC",
@@ -142,6 +147,7 @@ class TestPeriodClosingVoucher(unittest.TestCase):
debit_to="Debtors - TPC",
currency="USD",
customer="_Test Customer USD",
posting_date="2021-03-15",
)
jv = make_journal_entry(
@@ -149,14 +155,14 @@ class TestPeriodClosingVoucher(unittest.TestCase):
account2="Sales - TPC",
amount=400,
cost_center=cost_center,
posting_date=now(),
posting_date="2021-03-15",
)
jv.company = company
jv.finance_book = create_finance_book().name
jv.save()
jv.submit()
pcv = self.make_period_closing_voucher()
pcv = self.make_period_closing_voucher(posting_date="2021-03-31")
surplus_account = pcv.closing_account_head
expected_gle = (
@@ -177,14 +183,130 @@ class TestPeriodClosingVoucher(unittest.TestCase):
self.assertSequenceEqual(pcv_gle, expected_gle)
def make_period_closing_voucher(self, submit=True):
def test_gl_entries_restrictions(self):
frappe.db.sql("delete from `tabGL Entry` where company='Test PCV Company'")
frappe.db.sql("delete from `tabPeriod Closing Voucher` where company='Test PCV Company'")
company = create_company()
cost_center = create_cost_center("Test Cost Center 1")
self.make_period_closing_voucher(posting_date="2021-03-31")
jv1 = make_journal_entry(
posting_date="2021-03-15",
amount=400,
account1="Cash - TPC",
account2="Sales - TPC",
cost_center=cost_center,
save=False,
)
jv1.company = company
jv1.save()
self.assertRaises(frappe.ValidationError, jv1.submit)
def test_closing_balance_with_dimensions(self):
frappe.db.sql("delete from `tabGL Entry` where company='Test PCV Company'")
frappe.db.sql("delete from `tabPeriod Closing Voucher` where company='Test PCV Company'")
frappe.db.sql("delete from `tabAccount Closing Balance` where company='Test PCV Company'")
company = create_company()
cost_center1 = create_cost_center("Test Cost Center 1")
cost_center2 = create_cost_center("Test Cost Center 2")
jv1 = make_journal_entry(
posting_date="2021-03-15",
amount=400,
account1="Cash - TPC",
account2="Sales - TPC",
cost_center=cost_center1,
save=False,
)
jv1.company = company
jv1.save()
jv1.submit()
jv2 = make_journal_entry(
posting_date="2021-03-15",
amount=200,
account1="Cash - TPC",
account2="Sales - TPC",
cost_center=cost_center2,
save=False,
)
jv2.company = company
jv2.save()
jv2.submit()
pcv1 = self.make_period_closing_voucher(posting_date="2021-03-31")
closing_balance = frappe.db.get_value(
"Account Closing Balance",
{
"account": "Sales - TPC",
"cost_center": cost_center1,
"period_closing_voucher": pcv1.name,
"is_period_closing_voucher_entry": 0,
},
["credit", "credit_in_account_currency"],
as_dict=1,
)
self.assertEqual(closing_balance.credit, 400)
self.assertEqual(closing_balance.credit_in_account_currency, 400)
jv3 = make_journal_entry(
posting_date="2022-03-15",
amount=300,
account1="Cash - TPC",
account2="Sales - TPC",
cost_center=cost_center2,
save=False,
)
jv3.company = company
jv3.save()
jv3.submit()
pcv2 = self.make_period_closing_voucher(posting_date="2022-03-31")
cc1_closing_balance = frappe.db.get_value(
"Account Closing Balance",
{
"account": "Sales - TPC",
"cost_center": cost_center1,
"period_closing_voucher": pcv2.name,
"is_period_closing_voucher_entry": 0,
},
["credit", "credit_in_account_currency"],
as_dict=1,
)
cc2_closing_balance = frappe.db.get_value(
"Account Closing Balance",
{
"account": "Sales - TPC",
"cost_center": cost_center2,
"period_closing_voucher": pcv2.name,
"is_period_closing_voucher_entry": 0,
},
["credit", "credit_in_account_currency"],
as_dict=1,
)
self.assertEqual(cc1_closing_balance.credit, 400)
self.assertEqual(cc1_closing_balance.credit_in_account_currency, 400)
self.assertEqual(cc2_closing_balance.credit, 500)
self.assertEqual(cc2_closing_balance.credit_in_account_currency, 500)
def make_period_closing_voucher(self, posting_date=None, submit=True):
surplus_account = create_account()
cost_center = create_cost_center("Test Cost Center 1")
pcv = frappe.get_doc(
{
"doctype": "Period Closing Voucher",
"transaction_date": today(),
"posting_date": today(),
"transaction_date": posting_date or today(),
"posting_date": posting_date or today(),
"company": "Test PCV Company",
"fiscal_year": get_fiscal_year(today(), company="Test PCV Company")[0],
"cost_center": cost_center,

View File

@@ -21,8 +21,24 @@ class POSClosingEntry(StatusUpdater):
if frappe.db.get_value("POS Opening Entry", self.pos_opening_entry, "status") != "Open":
frappe.throw(_("Selected POS Opening Entry should be open."), title=_("Invalid Opening Entry"))
self.validate_duplicate_pos_invoices()
self.validate_pos_invoices()
def validate_duplicate_pos_invoices(self):
pos_occurences = {}
for idx, inv in enumerate(self.pos_transactions, 1):
pos_occurences.setdefault(inv.pos_invoice, []).append(idx)
error_list = []
for key, value in pos_occurences.items():
if len(value) > 1:
error_list.append(
_("{} is added multiple times on rows: {}".format(frappe.bold(key), frappe.bold(value)))
)
if error_list:
frappe.throw(error_list, title=_("Duplicate POS Invoices found"), as_list=True)
def validate_pos_invoices(self):
invalid_rows = []
for d in self.pos_transactions:

View File

@@ -112,7 +112,8 @@ erpnext.selling.POSInvoiceController = class POSInvoiceController extends erpnex
party_type: "Customer",
account: this.frm.doc.debit_to,
price_list: this.frm.doc.selling_price_list,
pos_profile: pos_profile
pos_profile: pos_profile,
company_address: this.frm.doc.company_address
}, () => {
this.apply_pricing_rule();
});

View File

@@ -161,7 +161,7 @@ class POSInvoice(SalesInvoice):
bold_item_name = frappe.bold(item.item_name)
bold_extra_batch_qty_needed = frappe.bold(
abs(available_batch_qty - reserved_batch_qty - item.qty)
abs(available_batch_qty - reserved_batch_qty - item.stock_qty)
)
bold_invalid_batch_no = frappe.bold(item.batch_no)
@@ -172,7 +172,7 @@ class POSInvoice(SalesInvoice):
).format(item.idx, bold_invalid_batch_no, bold_item_name),
title=_("Item Unavailable"),
)
elif (available_batch_qty - reserved_batch_qty - item.qty) < 0:
elif (available_batch_qty - reserved_batch_qty - item.stock_qty) < 0:
frappe.throw(
_(
"Row #{}: Batch No. {} of item {} has less than required stock available, {} more required"
@@ -246,7 +246,7 @@ class POSInvoice(SalesInvoice):
),
title=_("Item Unavailable"),
)
elif is_stock_item and flt(available_stock) < flt(d.qty):
elif is_stock_item and flt(available_stock) < flt(d.stock_qty):
frappe.throw(
_(
"Row #{}: Stock quantity not enough for Item Code: {} under warehouse {}. Available quantity {}."
@@ -651,7 +651,7 @@ def get_bundle_availability(bundle_item_code, warehouse):
item_pos_reserved_qty = get_pos_reserved_qty(item.item_code, warehouse)
available_qty = item_bin_qty - item_pos_reserved_qty
max_available_bundles = available_qty / item.qty
max_available_bundles = available_qty / item.stock_qty
if bundle_bin_qty > max_available_bundles and frappe.get_value(
"Item", item.item_code, "is_stock_item"
):
@@ -675,7 +675,7 @@ def get_bin_qty(item_code, warehouse):
def get_pos_reserved_qty(item_code, warehouse):
reserved_qty = frappe.db.sql(
"""select sum(p_item.qty) as qty
"""select sum(p_item.stock_qty) as qty
from `tabPOS Invoice` p, `tabPOS Invoice Item` p_item
where p.name = p_item.parent
and ifnull(p.consolidated_invoice, '') = ''

View File

@@ -17,6 +17,22 @@ class POSInvoiceMergeLog(Document):
def validate(self):
self.validate_customer()
self.validate_pos_invoice_status()
self.validate_duplicate_pos_invoices()
def validate_duplicate_pos_invoices(self):
pos_occurences = {}
for idx, inv in enumerate(self.pos_invoices, 1):
pos_occurences.setdefault(inv.pos_invoice, []).append(idx)
error_list = []
for key, value in pos_occurences.items():
if len(value) > 1:
error_list.append(
_("{} is added multiple times on rows: {}".format(frappe.bold(key), frappe.bold(value)))
)
if error_list:
frappe.throw(error_list, title=_("Duplicate POS Invoices found"), as_list=True)
def validate_customer(self):
if self.merge_invoices_based_on == "Customer Group":
@@ -425,6 +441,8 @@ def create_merge_logs(invoice_by_customer, closing_entry=None):
if closing_entry:
closing_entry.set_status(update=True, status="Failed")
if type(error_message) == list:
error_message = frappe.json.dumps(error_message)
closing_entry.db_set("error_message", error_message)
raise

View File

@@ -472,7 +472,7 @@
"description": "If rate is zero them item will be treated as \"Free Item\"",
"fieldname": "free_item_rate",
"fieldtype": "Currency",
"label": "Rate"
"label": "Free Item Rate"
},
{
"collapsible": 1,
@@ -608,7 +608,7 @@
"icon": "fa fa-gift",
"idx": 1,
"links": [],
"modified": "2022-10-13 19:05:35.056304",
"modified": "2023-02-14 04:53:34.887358",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Pricing Rule",

View File

@@ -10,7 +10,7 @@ import re
import frappe
from frappe import _, throw
from frappe.model.document import Document
from frappe.utils import cint, flt, getdate
from frappe.utils import cint, flt
apply_on_dict = {"Item Code": "items", "Item Group": "item_groups", "Brand": "brands"}
@@ -184,8 +184,7 @@ class PricingRule(Document):
if self.is_cumulative and not (self.valid_from and self.valid_upto):
frappe.throw(_("Valid from and valid upto fields are mandatory for the cumulative"))
if self.valid_from and self.valid_upto and getdate(self.valid_from) > getdate(self.valid_upto):
frappe.throw(_("Valid from date must be less than valid upto date"))
self.validate_from_to_dates("valid_from", "valid_upto")
def validate_condition(self):
if (
@@ -256,7 +255,7 @@ def apply_pricing_rule(args, doc=None):
for item in item_list:
args_copy = copy.deepcopy(args)
args_copy.update(item)
data = get_pricing_rule_for_item(args_copy, item.get("price_list_rate"), doc=doc)
data = get_pricing_rule_for_item(args_copy, doc=doc)
out.append(data)
if (
@@ -293,7 +292,7 @@ def update_pricing_rule_uom(pricing_rule, args):
pricing_rule.uom = row.uom
def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=False):
def get_pricing_rule_for_item(args, doc=None, for_validate=False):
from erpnext.accounts.doctype.pricing_rule.utils import (
get_applied_pricing_rules,
get_pricing_rule_items,

View File

@@ -1123,7 +1123,7 @@ def make_pricing_rule(**args):
"apply_on": args.apply_on or "Item Code",
"applicable_for": args.applicable_for,
"selling": args.selling or 0,
"currency": "USD",
"currency": "INR",
"apply_discount_on_rate": args.apply_discount_on_rate or 0,
"buying": args.buying or 0,
"min_qty": args.min_qty or 0.0,

View File

@@ -250,6 +250,22 @@ def get_other_conditions(conditions, values, args):
and ifnull(`tabPricing Rule`.valid_upto, '2500-12-31')"""
values["transaction_date"] = args.get("transaction_date")
if args.get("doctype") in [
"Quotation",
"Quotation Item",
"Sales Order",
"Sales Order Item",
"Delivery Note",
"Delivery Note Item",
"Sales Invoice",
"Sales Invoice Item",
"POS Invoice",
"POS Invoice Item",
]:
conditions += """ and ifnull(`tabPricing Rule`.selling, 0) = 1"""
else:
conditions += """ and ifnull(`tabPricing Rule`.buying, 0) = 1"""
return conditions
@@ -669,13 +685,23 @@ def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None):
item_details.free_item_data.append(free_item_data_args)
def apply_pricing_rule_for_free_items(doc, pricing_rule_args, set_missing_values=False):
def apply_pricing_rule_for_free_items(doc, pricing_rule_args):
if pricing_rule_args:
items = tuple((d.item_code, d.pricing_rules) for d in doc.items if d.is_free_item)
args = {(d["item_code"], d["pricing_rules"]): d for d in pricing_rule_args}
for args in pricing_rule_args:
if not items or (args.get("item_code"), args.get("pricing_rules")) not in items:
doc.append("items", args)
for item in doc.items:
if not item.is_free_item:
continue
free_item_data = args.get((item.item_code, item.pricing_rules))
if free_item_data:
free_item_data.pop("item_name")
free_item_data.pop("description")
item.update(free_item_data)
args.pop((item.item_code, item.pricing_rules))
for free_item in args.values():
doc.append("items", free_item)
def get_pricing_rule_items(pr_doc, other_items=False) -> list:

View File

@@ -15,7 +15,7 @@
</div>
<h2 class="text-center">{{ _("STATEMENTS OF ACCOUNTS") }}</h2>
<div>
<h5 style="float: left;">{{ _("Customer: ") }} <b>{{filters.party[0] }}</b></h5>
<h5 style="float: left;">{{ _("Customer: ") }} <b>{{filters.party_name[0] }}</b></h5>
<h5 style="float: right;">
{{ _("Date: ") }}
<b>{{ frappe.format(filters.from_date, 'Date')}}
@@ -49,7 +49,6 @@
<br>
{% endif %}
{{ _("Against") }}: {{ row.against }}
<br>{{ _("Remarks") }}: {{ row.remarks }}
{% if row.bill_no %}
<br>{{ _("Supplier Invoice No") }}: {{ row.bill_no }}

View File

@@ -24,7 +24,7 @@ from erpnext.accounts.report.general_ledger.general_ledger import execute as get
class ProcessStatementOfAccounts(Document):
def validate(self):
if not self.subject:
self.subject = "Statement Of Accounts for {{ customer.name }}"
self.subject = "Statement Of Accounts for {{ customer.customer_name }}"
if not self.body:
self.body = "Hello {{ customer.name }},<br>PFA your Statement Of Accounts from {{ doc.from_date }} to {{ doc.to_date }}."
@@ -87,6 +87,7 @@ def get_report_pdf(doc, consolidated=True):
"account": [doc.account] if doc.account else None,
"party_type": "Customer",
"party": [entry.customer],
"party_name": [entry.customer_name] if entry.customer_name else None,
"presentation_currency": presentation_currency,
"group_by": doc.group_by,
"currency": doc.currency,
@@ -156,7 +157,7 @@ def get_customers_based_on_territory_or_customer_group(customer_collection, coll
]
return frappe.get_list(
"Customer",
fields=["name", "email_id"],
fields=["name", "customer_name", "email_id"],
filters=[[fields_dict[customer_collection], "IN", selected]],
)
@@ -179,7 +180,7 @@ def get_customers_based_on_sales_person(sales_person):
if sales_person_records.get("Customer"):
return frappe.get_list(
"Customer",
fields=["name", "email_id"],
fields=["name", "customer_name", "email_id"],
filters=[["name", "in", list(sales_person_records["Customer"])]],
)
else:
@@ -228,7 +229,7 @@ def fetch_customers(customer_collection, collection_name, primary_mandatory):
if customer_collection == "Sales Partner":
customers = frappe.get_list(
"Customer",
fields=["name", "email_id"],
fields=["name", "customer_name", "email_id"],
filters=[["default_sales_partner", "=", collection_name]],
)
else:
@@ -245,7 +246,12 @@ def fetch_customers(customer_collection, collection_name, primary_mandatory):
continue
customer_list.append(
{"name": customer.name, "primary_email": primary_email, "billing_email": billing_email}
{
"name": customer.name,
"customer_name": customer.customer_name,
"primary_email": primary_email,
"billing_email": billing_email,
}
)
return customer_list

View File

@@ -1,12 +1,12 @@
{
"actions": [],
"allow_workflow": 1,
"creation": "2020-08-03 16:35:21.852178",
"doctype": "DocType",
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
"customer",
"customer_name",
"billing_email",
"primary_email"
],
@@ -30,11 +30,18 @@
"fieldtype": "Read Only",
"in_list_view": 1,
"label": "Billing Email"
},
{
"fetch_from": "customer.customer_name",
"fieldname": "customer_name",
"fieldtype": "Data",
"label": "Customer Name",
"read_only": 1
}
],
"istable": 1,
"links": [],
"modified": "2020-08-03 22:55:38.875601",
"modified": "2023-03-13 00:12:34.508086",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Process Statement Of Accounts Customer",
@@ -43,5 +50,6 @@
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"track_changes": 1
}

View File

@@ -31,7 +31,7 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
super.onload();
// Ignore linked advances
this.frm.ignore_doctypes_on_cancel_all = ['Journal Entry', 'Payment Entry', 'Purchase Invoice'];
this.frm.ignore_doctypes_on_cancel_all = ['Journal Entry', 'Payment Entry', 'Purchase Invoice', "Repost Payment Ledger"];
if(!this.frm.doc.__islocal) {
// show credit_to in print format
@@ -82,7 +82,11 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
if(doc.docstatus == 1 && doc.outstanding_amount != 0
&& !(doc.is_return && doc.return_against) && !doc.on_hold) {
this.frm.add_custom_button(__('Payment'), this.make_payment_entry, __('Create'));
this.frm.add_custom_button(
__('Payment'),
() => this.make_payment_entry(),
__('Create')
);
cur_frm.page.set_inner_btn_group_as_primary(__('Create'));
}

View File

@@ -64,12 +64,13 @@
"tax_withholding_net_total",
"base_tax_withholding_net_total",
"taxes_section",
"tax_category",
"taxes_and_charges",
"column_break_58",
"tax_category",
"column_break_49",
"shipping_rule",
"column_break_49",
"incoterm",
"named_place",
"section_break_51",
"taxes",
"totals",
@@ -117,6 +118,7 @@
"paid_amount",
"advances_section",
"allocate_advances_automatically",
"only_include_allocated_payments",
"get_advances",
"advances",
"advance_tax",
@@ -1425,6 +1427,7 @@
},
{
"default": "0",
"depends_on": "apply_tds",
"fieldname": "tax_withholding_net_total",
"fieldtype": "Currency",
"hidden": 1,
@@ -1434,12 +1437,13 @@
"read_only": 1
},
{
"depends_on": "apply_tds",
"fieldname": "base_tax_withholding_net_total",
"fieldtype": "Currency",
"hidden": 1,
"label": "Base Tax Withholding Net Total",
"no_copy": 1,
"options": "currency",
"options": "Company:company:default_currency",
"print_hide": 1,
"read_only": 1
},
@@ -1541,17 +1545,30 @@
"fieldtype": "Link",
"label": "Incoterm",
"options": "Incoterm"
},
{
"depends_on": "incoterm",
"fieldname": "named_place",
"fieldtype": "Data",
"label": "Named Place"
},
{
"default": "0",
"depends_on": "allocate_advances_automatically",
"description": "Advance payments allocated against orders will only be fetched",
"fieldname": "only_include_allocated_payments",
"fieldtype": "Check",
"label": "Only Include Allocated Payments"
}
],
"icon": "fa fa-file-text",
"idx": 204,
"is_submittable": 1,
"links": [],
"modified": "2022-11-25 12:44:29.935567",
"modified": "2023-04-03 22:57:14.074982",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice",
"name_case": "Title Case",
"naming_rule": "By \"Naming Series\" field",
"owner": "Administrator",
"permissions": [

View File

@@ -5,6 +5,7 @@
import frappe
from frappe import _, throw
from frappe.model.mapper import get_mapped_doc
from frappe.query_builder.functions import Sum
from frappe.utils import cint, cstr, flt, formatdate, get_link_to_form, getdate, nowdate
import erpnext
@@ -116,7 +117,7 @@ class PurchaseInvoice(BuyingController):
self.validate_expense_account()
self.set_against_expense_account()
self.validate_write_off_account()
self.validate_multiple_billing("Purchase Receipt", "pr_detail", "amount", "items")
self.validate_multiple_billing("Purchase Receipt", "pr_detail", "amount")
self.create_remarks()
self.set_status()
self.validate_purchase_receipt_if_update_stock()
@@ -231,7 +232,9 @@ class PurchaseInvoice(BuyingController):
)
if (
cint(frappe.db.get_single_value("Buying Settings", "maintain_same_rate")) and not self.is_return
cint(frappe.get_cached_value("Buying Settings", "None", "maintain_same_rate"))
and not self.is_return
and not self.is_internal_supplier
):
self.validate_rate_with_reference_doc(
[
@@ -578,6 +581,7 @@ class PurchaseInvoice(BuyingController):
self.make_supplier_gl_entry(gl_entries)
self.make_item_gl_entries(gl_entries)
self.make_precision_loss_gl_entry(gl_entries)
if self.check_asset_cwip_enabled():
self.get_asset_gl_entry(gl_entries)
@@ -972,6 +976,28 @@ class PurchaseInvoice(BuyingController):
item.item_tax_amount, item.precision("item_tax_amount")
)
def make_precision_loss_gl_entry(self, gl_entries):
round_off_account, round_off_cost_center = get_round_off_account_and_cost_center(
self.company, "Purchase Invoice", self.name
)
precision_loss = self.get("base_net_total") - flt(
self.get("net_total") * self.conversion_rate, self.precision("net_total")
)
if precision_loss:
gl_entries.append(
self.get_gl_dict(
{
"account": round_off_account,
"against": self.supplier,
"credit": precision_loss,
"cost_center": self.cost_center or round_off_cost_center,
"remarks": _("Net total calculation precision loss"),
}
)
)
def get_asset_gl_entry(self, gl_entries):
arbnb_account = self.get_company_default("asset_received_but_not_billed")
eiiav_account = self.get_company_default("expenses_included_in_asset_valuation")
@@ -1414,6 +1440,8 @@ class PurchaseInvoice(BuyingController):
"GL Entry",
"Stock Ledger Entry",
"Repost Item Valuation",
"Repost Payment Ledger",
"Repost Payment Ledger Items",
"Payment Ledger Entry",
"Tax Withheld Vouchers",
)
@@ -1461,19 +1489,16 @@ class PurchaseInvoice(BuyingController):
def update_billing_status_in_pr(self, update_modified=True):
updated_pr = []
po_details = []
pr_details_billed_amt = self.get_pr_details_billed_amt()
for d in self.get("items"):
if d.pr_detail:
billed_amt = frappe.db.sql(
"""select sum(amount) from `tabPurchase Invoice Item`
where pr_detail=%s and docstatus=1""",
d.pr_detail,
)
billed_amt = billed_amt and billed_amt[0][0] or 0
frappe.db.set_value(
"Purchase Receipt Item",
d.pr_detail,
"billed_amt",
billed_amt,
flt(pr_details_billed_amt.get(d.pr_detail)),
update_modified=update_modified,
)
updated_pr.append(d.purchase_receipt)
@@ -1483,11 +1508,35 @@ class PurchaseInvoice(BuyingController):
if po_details:
updated_pr += update_billed_amount_based_on_po(po_details, update_modified)
adjust_incoming_rate = frappe.db.get_single_value(
"Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate"
)
for pr in set(updated_pr):
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import update_billing_percentage
pr_doc = frappe.get_doc("Purchase Receipt", pr)
update_billing_percentage(pr_doc, update_modified=update_modified)
update_billing_percentage(
pr_doc, update_modified=update_modified, adjust_incoming_rate=adjust_incoming_rate
)
def get_pr_details_billed_amt(self):
# Get billed amount based on purchase receipt item reference (pr_detail) in purchase invoice
pr_details_billed_amt = {}
pr_details = [d.get("pr_detail") for d in self.get("items") if d.get("pr_detail")]
if pr_details:
doctype = frappe.qb.DocType("Purchase Invoice Item")
query = (
frappe.qb.from_(doctype)
.select(doctype.pr_detail, Sum(doctype.amount))
.where(doctype.pr_detail.isin(pr_details) & doctype.docstatus == 1)
.groupby(doctype.pr_detail)
)
pr_details_billed_amt = frappe._dict(query.run(as_list=1))
return pr_details_billed_amt
def on_recurring(self, reference_doc, auto_repeat_doc):
self.due_date = None

View File

@@ -1523,6 +1523,94 @@ class TestPurchaseInvoice(unittest.TestCase, StockTestMixin):
company.enable_provisional_accounting_for_non_stock_items = 0
company.save()
def test_adjust_incoming_rate(self):
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=1, rate=100)
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, 100)
pi = create_purchase_invoice_from_receipt(pr.name)
for row in pi.items:
row.rate = 150
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, 150)
# Reduce the cost of the item
pr = make_purchase_receipt(qty=1, rate=100)
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, 100)
pi = create_purchase_invoice_from_receipt(pr.name)
for row in pi.items:
row.rate = 50
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, 50)
frappe.db.set_single_value(
"Buying Settings", "set_landed_cost_based_on_purchase_invoice_rate", 0
)
# Don't adjust incoming rate
pr = make_purchase_receipt(qty=1, rate=100)
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, 100)
pi = create_purchase_invoice_from_receipt(pr.name)
for row in pi.items:
row.rate = 50
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, 100)
frappe.db.set_single_value("Buying Settings", "maintain_same_rate", 1)
def test_item_less_defaults(self):
pi = frappe.new_doc("Purchase Invoice")

View File

@@ -7,7 +7,6 @@ import frappe
from frappe import _, qb
from frappe.model.document import Document
from frappe.query_builder.custom import ConstantColumn
from frappe.utils.background_jobs import is_job_queued
from erpnext.accounts.utils import _delete_pl_entries, create_payment_ledger_entry
@@ -27,7 +26,7 @@ def start_payment_ledger_repost(docname=None):
"""
if docname:
repost_doc = frappe.get_doc("Repost Payment Ledger", docname)
if repost_doc.docstatus == 1 and repost_doc.repost_status in ["Queued", "Failed"]:
if repost_doc.docstatus.is_submitted() and repost_doc.repost_status in ["Queued", "Failed"]:
try:
for entry in repost_doc.repost_vouchers:
doc = frappe.get_doc(entry.voucher_type, entry.voucher_no)
@@ -102,10 +101,9 @@ def execute_repost_payment_ledger(docname):
job_name = "payment_ledger_repost_" + docname
if not is_job_queued(job_name):
frappe.enqueue(
method="erpnext.accounts.doctype.repost_payment_ledger.repost_payment_ledger.start_payment_ledger_repost",
docname=docname,
is_async=True,
job_name=job_name,
)
frappe.enqueue(
method="erpnext.accounts.doctype.repost_payment_ledger.repost_payment_ledger.start_payment_ledger_repost",
docname=docname,
is_async=True,
job_name=job_name,
)

View File

@@ -34,7 +34,7 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e
super.onload();
this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice', 'Timesheet', 'POS Invoice Merge Log',
'POS Closing Entry', 'Journal Entry', 'Payment Entry'];
'POS Closing Entry', 'Journal Entry', 'Payment Entry', "Repost Payment Ledger"];
if(!this.frm.doc.__islocal && !this.frm.doc.customer && this.frm.doc.debit_to) {
// show debit_to in print format
@@ -93,9 +93,12 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e
if (doc.docstatus == 1 && doc.outstanding_amount!=0
&& !(cint(doc.is_return) && doc.return_against)) {
cur_frm.add_custom_button(__('Payment'),
this.make_payment_entry, __('Create'));
cur_frm.page.set_inner_btn_group_as_primary(__('Create'));
this.frm.add_custom_button(
__('Payment'),
() => this.make_payment_entry(),
__('Create')
);
this.frm.page.set_inner_btn_group_as_primary(__('Create'));
}
if(doc.docstatus==1 && !doc.is_return) {

View File

@@ -32,9 +32,6 @@
"cost_center",
"dimension_col_break",
"project",
"column_break_27",
"campaign",
"source",
"currency_and_price_list",
"currency",
"conversion_rate",
@@ -61,12 +58,13 @@
"total",
"net_total",
"taxes_section",
"tax_category",
"taxes_and_charges",
"column_break_38",
"shipping_rule",
"incoterm",
"column_break_55",
"tax_category",
"incoterm",
"named_place",
"section_break_40",
"taxes",
"section_break_43",
@@ -122,6 +120,7 @@
"account_for_change_amount",
"advances_section",
"allocate_advances_automatically",
"only_include_allocated_payments",
"get_advances",
"advances",
"write_off_section",
@@ -202,7 +201,9 @@
"more_information",
"status",
"inter_company_invoice_reference",
"campaign",
"represents_company",
"source",
"customer_group",
"col_break23",
"is_internal_customer",
@@ -921,6 +922,7 @@
"fieldtype": "Table",
"hide_days": 1,
"hide_seconds": 1,
"label": "Sales Taxes and Charges",
"oldfieldname": "other_charges",
"oldfieldtype": "Table",
"options": "Sales Taxes and Charges"
@@ -1774,6 +1776,8 @@
"width": "50%"
},
{
"fetch_from": "sales_partner.commission_rate",
"fetch_if_empty": 1,
"fieldname": "commission_rate",
"fieldtype": "Float",
"hide_days": 1,
@@ -2079,10 +2083,6 @@
"fieldname": "company_addr_col_break",
"fieldtype": "Column Break"
},
{
"fieldname": "column_break_27",
"fieldtype": "Column Break"
},
{
"fieldname": "column_break_52",
"fieldtype": "Column Break"
@@ -2121,6 +2121,20 @@
"fieldtype": "Link",
"label": "Incoterm",
"options": "Incoterm"
},
{
"depends_on": "incoterm",
"fieldname": "named_place",
"fieldtype": "Data",
"label": "Named Place"
},
{
"default": "0",
"depends_on": "allocate_advances_automatically",
"description": "Advance payments allocated against orders will only be fetched",
"fieldname": "only_include_allocated_payments",
"fieldtype": "Check",
"label": "Only Include Allocated Payments"
}
],
"icon": "fa fa-file-text",
@@ -2133,11 +2147,10 @@
"link_fieldname": "consolidated_invoice"
}
],
"modified": "2022-11-17 17:17:10.883487",
"modified": "2023-04-03 22:55:14.206473",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",
"name_case": "Title Case",
"naming_rule": "By \"Naming Series\" field",
"owner": "Administrator",
"permissions": [

View File

@@ -145,7 +145,7 @@ class SalesInvoice(SellingController):
self.set_against_income_account()
self.validate_time_sheets_are_submitted()
self.validate_multiple_billing("Delivery Note", "dn_detail", "amount", "items")
self.validate_multiple_billing("Delivery Note", "dn_detail", "amount")
if not self.is_return:
self.validate_serial_numbers()
else:
@@ -397,6 +397,8 @@ class SalesInvoice(SellingController):
"GL Entry",
"Stock Ledger Entry",
"Repost Item Valuation",
"Repost Payment Ledger",
"Repost Payment Ledger Items",
"Payment Ledger Entry",
)
@@ -1185,11 +1187,24 @@ class SalesInvoice(SellingController):
if asset.calculate_depreciation:
posting_date = frappe.db.get_value("Sales Invoice", self.return_against, "posting_date")
reverse_depreciation_entry_made_after_disposal(asset, posting_date)
reset_depreciation_schedule(asset, self.posting_date)
notes = _(
"This schedule was created when Asset {0} was returned after being sold through Sales Invoice {1}."
).format(
get_link_to_form(asset.doctype, asset.name),
get_link_to_form(self.doctype, self.get("name")),
)
reset_depreciation_schedule(asset, self.posting_date, notes)
asset.reload()
else:
if asset.calculate_depreciation:
depreciate_asset(asset, self.posting_date)
notes = _(
"This schedule was created when Asset {0} was sold through Sales Invoice {1}."
).format(
get_link_to_form(asset.doctype, asset.name),
get_link_to_form(self.doctype, self.get("name")),
)
depreciate_asset(asset, self.posting_date, notes)
asset.reload()
fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(

View File

@@ -21,6 +21,9 @@ from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_comp
from erpnext.accounts.utils import PaymentEntryUnlinkError
from erpnext.assets.doctype.asset.depreciation import post_depreciation_entries
from erpnext.assets.doctype.asset.test_asset import create_asset, create_asset_data
from erpnext.assets.doctype.asset_depreciation_schedule.asset_depreciation_schedule import (
get_depr_schedule,
)
from erpnext.controllers.accounts_controller import update_invoice_status
from erpnext.controllers.taxes_and_totals import get_itemised_tax_breakup_data
from erpnext.exceptions import InvalidAccountCurrency, InvalidCurrency
@@ -263,16 +266,16 @@ class TestSalesInvoice(unittest.TestCase):
"_Test Account Education Cess - _TC": [3, 1618, 0.06, 32.36],
"_Test Account S&H Education Cess - _TC": [1.5, 1619.5, 0.03, 32.39],
"_Test Account CST - _TC": [32.5, 1652, 0.65, 33.04],
"_Test Account VAT - _TC": [156.5, 1808.5, 3.13, 36.17],
"_Test Account Discount - _TC": [-181.0, 1627.5, -3.62, 32.55],
"_Test Account VAT - _TC": [156.0, 1808.0, 3.12, 36.16],
"_Test Account Discount - _TC": [-181.0, 1627.0, -3.62, 32.54],
}
for d in si.get("taxes"):
for i, k in enumerate(expected_values["keys"]):
self.assertEqual(d.get(k), expected_values[d.account_head][i])
self.assertEqual(si.base_grand_total, 1627.5)
self.assertEqual(si.grand_total, 32.55)
self.assertEqual(si.base_grand_total, 1627.0)
self.assertEqual(si.grand_total, 32.54)
def test_sales_invoice_with_discount_and_inclusive_tax(self):
si = create_sales_invoice(qty=100, rate=50, do_not_save=True)
@@ -398,10 +401,10 @@ class TestSalesInvoice(unittest.TestCase):
"_Test Account S&H Education Cess - _TC": [1.4, 1.30, 1297.67],
"_Test Account CST - _TC": [27.88, 25.95, 1323.62],
"_Test Account VAT - _TC": [156.25, 145.43, 1469.05],
"_Test Account Customs Duty - _TC": [125, 116.35, 1585.40],
"_Test Account Shipping Charges - _TC": [100, 100, 1685.40],
"_Test Account Discount - _TC": [-180.33, -168.54, 1516.86],
"_Test Account Service Tax - _TC": [-18.03, -16.85, 1500.01],
"_Test Account Customs Duty - _TC": [125, 116.34, 1585.39],
"_Test Account Shipping Charges - _TC": [100, 100, 1685.39],
"_Test Account Discount - _TC": [-180.33, -168.54, 1516.85],
"_Test Account Service Tax - _TC": [-18.03, -16.85, 1500.00],
}
for d in si.get("taxes"):
@@ -410,7 +413,7 @@ class TestSalesInvoice(unittest.TestCase):
self.assertEqual(si.base_grand_total, 1500)
self.assertEqual(si.grand_total, 1500)
self.assertEqual(si.rounding_adjustment, -0.01)
self.assertEqual(si.rounding_adjustment, 0.0)
def test_discount_amount_gl_entry(self):
frappe.db.set_value("Company", "_Test Company", "round_off_account", "Round Off - _TC")
@@ -451,7 +454,7 @@ class TestSalesInvoice(unittest.TestCase):
[test_records[3]["taxes"][2]["account_head"], 0.0, 1.30],
[test_records[3]["taxes"][3]["account_head"], 0.0, 25.95],
[test_records[3]["taxes"][4]["account_head"], 0.0, 145.43],
[test_records[3]["taxes"][5]["account_head"], 0.0, 116.35],
[test_records[3]["taxes"][5]["account_head"], 0.0, 116.34],
[test_records[3]["taxes"][6]["account_head"], 0.0, 100],
[test_records[3]["taxes"][7]["account_head"], 168.54, 0.0],
["_Test Account Service Tax - _TC", 16.85, 0.0],
@@ -1166,6 +1169,46 @@ class TestSalesInvoice(unittest.TestCase):
frappe.db.sql("delete from `tabPOS Profile`")
def test_bin_details_of_packed_item(self):
from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
from erpnext.stock.doctype.item.test_item import make_item
# test Update Items with product bundle
if not frappe.db.exists("Item", "_Test Product Bundle Item New"):
bundle_item = make_item("_Test Product Bundle Item New", {"is_stock_item": 0})
bundle_item.append(
"item_defaults", {"company": "_Test Company", "default_warehouse": "_Test Warehouse - _TC"}
)
bundle_item.save(ignore_permissions=True)
make_item("_Packed Item New 1", {"is_stock_item": 1})
make_product_bundle("_Test Product Bundle Item New", ["_Packed Item New 1"], 2)
si = create_sales_invoice(
item_code="_Test Product Bundle Item New",
update_stock=1,
warehouse="_Test Warehouse - _TC",
transaction_date=add_days(nowdate(), -1),
do_not_submit=1,
)
make_stock_entry(item="_Packed Item New 1", target="_Test Warehouse - _TC", qty=120, rate=100)
bin_details = frappe.db.get_value(
"Bin",
{"item_code": "_Packed Item New 1", "warehouse": "_Test Warehouse - _TC"},
["actual_qty", "projected_qty", "ordered_qty"],
as_dict=1,
)
si.transaction_date = nowdate()
si.save()
packed_item = si.packed_items[0]
self.assertEqual(flt(bin_details.actual_qty), flt(packed_item.actual_qty))
self.assertEqual(flt(bin_details.projected_qty), flt(packed_item.projected_qty))
self.assertEqual(flt(bin_details.ordered_qty), flt(packed_item.ordered_qty))
def test_pos_si_without_payment(self):
make_pos_profile()
@@ -1571,7 +1614,7 @@ class TestSalesInvoice(unittest.TestCase):
"_Test Account Education Cess - _TC": [1.4, 1.4, 1.4],
"_Test Account S&H Education Cess - _TC": [0.7, 0.7, 0.7],
"_Test Account CST - _TC": [17.19, 17.19, 17.19],
"_Test Account VAT - _TC": [78.13, 78.13, 78.13],
"_Test Account VAT - _TC": [78.12, 78.12, 78.12],
"_Test Account Discount - _TC": [-95.49, -95.49, -95.49],
}
@@ -1580,9 +1623,9 @@ class TestSalesInvoice(unittest.TestCase):
if expected_values.get(d.account_head):
self.assertEqual(d.get(k), expected_values[d.account_head][i])
self.assertEqual(si.total_taxes_and_charges, 234.43)
self.assertEqual(si.base_grand_total, 859.43)
self.assertEqual(si.grand_total, 859.43)
self.assertEqual(si.total_taxes_and_charges, 234.42)
self.assertEqual(si.base_grand_total, 859.42)
self.assertEqual(si.grand_total, 859.42)
def test_multi_currency_gle(self):
si = create_sales_invoice(
@@ -1942,17 +1985,17 @@ class TestSalesInvoice(unittest.TestCase):
)
si.save()
si.submit()
self.assertEqual(si.net_total, 19453.13)
self.assertEqual(si.net_total, 19453.12)
self.assertEqual(si.grand_total, 24900)
self.assertEqual(si.total_taxes_and_charges, 5446.88)
self.assertEqual(si.rounding_adjustment, -0.01)
self.assertEqual(si.rounding_adjustment, 0.0)
expected_values = dict(
(d[0], d)
for d in [
[si.debit_to, 24900, 0.0],
["_Test Account Service Tax - _TC", 0.0, 5446.88],
["Sales - _TC", 0.0, 19453.13],
["Sales - _TC", 0.0, 19453.12],
["Round Off - _TC", 0.01, 0.0],
]
)
@@ -2774,7 +2817,7 @@ class TestSalesInvoice(unittest.TestCase):
["2021-09-30", 5041.1, 26407.22],
]
for i, schedule in enumerate(asset.schedules):
for i, schedule in enumerate(get_depr_schedule(asset.name, "Active")):
self.assertEqual(getdate(expected_values[i][0]), schedule.schedule_date)
self.assertEqual(expected_values[i][1], schedule.depreciation_amount)
self.assertEqual(expected_values[i][2], schedule.accumulated_depreciation_amount)
@@ -2805,7 +2848,7 @@ class TestSalesInvoice(unittest.TestCase):
expected_values = [["2020-12-31", 30000, 30000], ["2021-12-31", 30000, 60000]]
for i, schedule in enumerate(asset.schedules):
for i, schedule in enumerate(get_depr_schedule(asset.name, "Active")):
self.assertEqual(getdate(expected_values[i][0]), schedule.schedule_date)
self.assertEqual(expected_values[i][1], schedule.depreciation_amount)
self.assertEqual(expected_values[i][2], schedule.accumulated_depreciation_amount)
@@ -2834,7 +2877,7 @@ class TestSalesInvoice(unittest.TestCase):
["2025-06-06", 18633.88, 100000.0, False],
]
for i, schedule in enumerate(asset.schedules):
for i, schedule in enumerate(get_depr_schedule(asset.name, "Active")):
self.assertEqual(getdate(expected_values[i][0]), schedule.schedule_date)
self.assertEqual(expected_values[i][1], schedule.depreciation_amount)
self.assertEqual(expected_values[i][2], schedule.accumulated_depreciation_amount)

View File

@@ -890,7 +890,7 @@
"idx": 1,
"istable": 1,
"links": [],
"modified": "2022-11-02 12:53:12.693217",
"modified": "2022-12-28 16:17:33.484531",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Item",

View File

@@ -280,7 +280,8 @@ class Subscription(Document):
self.validate_plans_billing_cycle(self.get_billing_cycle_and_interval())
self.validate_end_date()
self.validate_to_follow_calendar_months()
self.cost_center = erpnext.get_default_cost_center(self.get("company"))
if not self.cost_center:
self.cost_center = erpnext.get_default_cost_center(self.get("company"))
def validate_trial_period(self):
"""

View File

@@ -5,5 +5,9 @@ frappe.ui.form.on('Subscription Plan', {
price_determination: function(frm) {
frm.toggle_reqd("cost", frm.doc.price_determination === 'Fixed rate');
frm.toggle_reqd("price_list", frm.doc.price_determination === 'Based on price list');
}
},
subscription_plan: function (frm) {
erpnext.utils.check_payments_app();
},
});

View File

@@ -32,7 +32,7 @@ class TaxRule(Document):
def validate(self):
self.validate_tax_template()
self.validate_date()
self.validate_from_to_dates("from_date", "to_date")
self.validate_filters()
self.validate_use_for_shopping_cart()
@@ -51,10 +51,6 @@ class TaxRule(Document):
if not (self.sales_tax_template or self.purchase_tax_template):
frappe.throw(_("Tax Template is mandatory."))
def validate_date(self):
if self.from_date and self.to_date and self.from_date > self.to_date:
frappe.throw(_("From Date cannot be greater than To Date"))
def validate_filters(self):
filters = {
"tax_type": self.tax_type,

View File

@@ -1,6 +1,6 @@
{
"actions": [],
"autoname": "autoincrement",
"autoname": "hash",
"creation": "2022-09-13 16:18:59.404842",
"doctype": "DocType",
"editable_grid": 1,
@@ -36,11 +36,11 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
"modified": "2022-09-13 23:40:41.479208",
"modified": "2023-01-13 13:40:41.479208",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Tax Withheld Vouchers",
"naming_rule": "Autoincrement",
"naming_rule": "Random",
"owner": "Administrator",
"permissions": [],
"sort_field": "modified",

View File

@@ -121,12 +121,24 @@ def get_party_tax_withholding_details(inv, tax_withholding_category=None):
else:
tax_row = get_tax_row_for_tcs(inv, tax_details, tax_amount, tax_deducted)
cost_center = get_cost_center(inv)
tax_row.update({"cost_center": cost_center})
if inv.doctype == "Purchase Invoice":
return tax_row, tax_deducted_on_advances, voucher_wise_amount
else:
return tax_row
def get_cost_center(inv):
cost_center = frappe.get_cached_value("Company", inv.company, "cost_center")
if len(inv.get("taxes", [])) > 0:
cost_center = inv.get("taxes")[0].cost_center
return cost_center
def get_tax_withholding_details(tax_withholding_category, posting_date, company):
tax_withholding = frappe.get_doc("Tax Withholding Category", tax_withholding_category)
@@ -247,9 +259,7 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N
if tax_deducted:
net_total = inv.tax_withholding_net_total
if ldc:
tax_amount = get_tds_amount_from_ldc(
ldc, parties, pan_no, tax_details, posting_date, net_total
)
tax_amount = get_tds_amount_from_ldc(ldc, parties, tax_details, posting_date, net_total)
else:
tax_amount = net_total * tax_details.rate / 100 if net_total > 0 else 0
@@ -268,7 +278,7 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N
tax_amount = get_tcs_amount(parties, inv, tax_details, vouchers, advance_vouchers)
if cint(tax_details.round_off_tax_amount):
tax_amount = round(tax_amount)
tax_amount = normal_round(tax_amount)
return tax_amount, tax_deducted, tax_deducted_on_advances, voucher_wise_amount
@@ -400,12 +410,26 @@ def get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers):
tds_amount = 0
invoice_filters = {"name": ("in", vouchers), "docstatus": 1, "apply_tds": 1}
## for TDS to be deducted on advances
payment_entry_filters = {
"party_type": "Supplier",
"party": ("in", parties),
"docstatus": 1,
"apply_tax_withholding_amount": 1,
"unallocated_amount": (">", 0),
"posting_date": ["between", (tax_details.from_date, tax_details.to_date)],
"tax_withholding_category": tax_details.get("tax_withholding_category"),
}
field = "sum(tax_withholding_net_total)"
if cint(tax_details.consider_party_ledger_amount):
invoice_filters.pop("apply_tds", None)
field = "sum(grand_total)"
payment_entry_filters.pop("apply_tax_withholding_amount", None)
payment_entry_filters.pop("tax_withholding_category", None)
supp_credit_amt = frappe.db.get_value("Purchase Invoice", invoice_filters, field) or 0.0
supp_jv_credit_amt = (
@@ -417,14 +441,28 @@ def get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers):
"party": ("in", parties),
"reference_type": ("!=", "Purchase Invoice"),
},
"sum(credit_in_account_currency)",
"sum(credit_in_account_currency - debit_in_account_currency)",
)
or 0.0
)
# Get Amount via payment entry
payment_entry_amounts = frappe.db.get_all(
"Payment Entry",
filters=payment_entry_filters,
fields=["sum(unallocated_amount) as amount", "payment_type"],
group_by="payment_type",
)
supp_credit_amt += supp_jv_credit_amt
supp_credit_amt += inv.tax_withholding_net_total
for type in payment_entry_amounts:
if type.payment_type == "Pay":
supp_credit_amt += type.amount
else:
supp_credit_amt -= type.amount
threshold = tax_details.get("threshold", 0)
cumulative_threshold = tax_details.get("cumulative_threshold", 0)
@@ -526,7 +564,7 @@ def get_invoice_total_without_tcs(inv, tax_details):
return inv.grand_total - tcs_tax_row_amount
def get_tds_amount_from_ldc(ldc, parties, pan_no, tax_details, posting_date, net_total):
def get_tds_amount_from_ldc(ldc, parties, tax_details, posting_date, net_total):
tds_amount = 0
limit_consumed = frappe.db.get_value(
"Purchase Invoice",
@@ -565,3 +603,20 @@ def is_valid_certificate(
valid = True
return valid
def normal_round(number):
"""
Rounds a number to the nearest integer.
:param number: The number to round.
"""
decimal_part = number - int(number)
if decimal_part >= 0.5:
decimal_part = 1
else:
decimal_part = 0
number = int(number) + decimal_part
return number

View File

@@ -16,7 +16,7 @@ class TestTaxWithholdingCategory(unittest.TestCase):
def setUpClass(self):
# create relevant supplier, etc
create_records()
create_tax_with_holding_category()
create_tax_withholding_category_records()
def tearDown(self):
cancel_invoices()
@@ -38,7 +38,7 @@ class TestTaxWithholdingCategory(unittest.TestCase):
pi = create_purchase_invoice(supplier="Test TDS Supplier")
pi.submit()
# assert equal tax deduction on total invoice amount uptil now
# assert equal tax deduction on total invoice amount until now
self.assertEqual(pi.taxes_and_charges_deducted, 3000)
self.assertEqual(pi.grand_total, 7000)
invoices.append(pi)
@@ -47,7 +47,7 @@ class TestTaxWithholdingCategory(unittest.TestCase):
pi = create_purchase_invoice(supplier="Test TDS Supplier", rate=5000)
pi.submit()
# assert equal tax deduction on total invoice amount uptil now
# assert equal tax deduction on total invoice amount until now
self.assertEqual(pi.taxes_and_charges_deducted, 500)
invoices.append(pi)
@@ -130,7 +130,7 @@ class TestTaxWithholdingCategory(unittest.TestCase):
invoices.append(si)
# create another invoice whose total when added to previously created invoice,
# surpasses cumulative threshhold
# surpasses cumulative threshold
si = create_sales_invoice(customer="Test TCS Customer", rate=12000)
si.submit()
@@ -329,6 +329,38 @@ class TestTaxWithholdingCategory(unittest.TestCase):
for d in reversed(invoices):
d.cancel()
def test_tax_withholding_via_payment_entry_for_advances(self):
frappe.db.set_value(
"Supplier", "Test TDS Supplier7", "tax_withholding_category", "Advance TDS Category"
)
# create payment entry
pe1 = create_payment_entry(
payment_type="Pay", party_type="Supplier", party="Test TDS Supplier7", paid_amount=4000
)
pe1.submit()
self.assertFalse(pe1.get("taxes"))
pe2 = create_payment_entry(
payment_type="Pay", party_type="Supplier", party="Test TDS Supplier7", paid_amount=4000
)
pe2.submit()
self.assertFalse(pe2.get("taxes"))
pe3 = create_payment_entry(
payment_type="Pay", party_type="Supplier", party="Test TDS Supplier7", paid_amount=4000
)
pe3.apply_tax_withholding_amount = 1
pe3.save()
pe3.submit()
self.assertEquals(pe3.get("taxes")[0].tax_amount, 1200)
pe1.cancel()
pe2.cancel()
pe3.cancel()
def cancel_invoices():
purchase_invoices = frappe.get_all(
@@ -450,6 +482,32 @@ def create_sales_invoice(**args):
return si
def create_payment_entry(**args):
# return payment entry doc object
args = frappe._dict(args)
pe = frappe.get_doc(
{
"doctype": "Payment Entry",
"posting_date": today(),
"payment_type": args.payment_type,
"party_type": args.party_type,
"party": args.party,
"company": "_Test Company",
"paid_from": "Cash - _TC",
"paid_to": "Creditors - _TC",
"paid_amount": args.paid_amount or 10000,
"received_amount": args.paid_amount or 10000,
"reference_no": args.reference_no or "12345",
"reference_date": today(),
"paid_from_account_currency": "INR",
"paid_to_account_currency": "INR",
}
)
pe.save()
return pe
def create_records():
# create a new suppliers
for name in [
@@ -460,6 +518,7 @@ def create_records():
"Test TDS Supplier4",
"Test TDS Supplier5",
"Test TDS Supplier6",
"Test TDS Supplier7",
]:
if frappe.db.exists("Supplier", name):
continue
@@ -530,142 +589,129 @@ def create_records():
).insert()
def create_tax_with_holding_category():
def create_tax_withholding_category_records():
fiscal_year = get_fiscal_year(today(), company="_Test Company")
from_date = fiscal_year[1]
to_date = fiscal_year[2]
# Cumulative threshold
if not frappe.db.exists("Tax Withholding Category", "Cumulative Threshold TDS"):
frappe.get_doc(
{
"doctype": "Tax Withholding Category",
"name": "Cumulative Threshold TDS",
"category_name": "10% TDS",
"rates": [
{
"from_date": fiscal_year[1],
"to_date": fiscal_year[2],
"tax_withholding_rate": 10,
"single_threshold": 0,
"cumulative_threshold": 30000.00,
}
],
"accounts": [{"company": "_Test Company", "account": "TDS - _TC"}],
}
).insert()
create_tax_withholding_category(
category_name="Cumulative Threshold TDS",
rate=10,
from_date=from_date,
to_date=to_date,
account="TDS - _TC",
single_threshold=0,
cumulative_threshold=30000.00,
)
if not frappe.db.exists("Tax Withholding Category", "Cumulative Threshold TCS"):
frappe.get_doc(
{
"doctype": "Tax Withholding Category",
"name": "Cumulative Threshold TCS",
"category_name": "10% TCS",
"rates": [
{
"from_date": fiscal_year[1],
"to_date": fiscal_year[2],
"tax_withholding_rate": 10,
"single_threshold": 0,
"cumulative_threshold": 30000.00,
}
],
"accounts": [{"company": "_Test Company", "account": "TCS - _TC"}],
}
).insert()
# Category for TCS
create_tax_withholding_category(
category_name="Cumulative Threshold TCS",
rate=10,
from_date=from_date,
to_date=to_date,
account="TCS - _TC",
single_threshold=0,
cumulative_threshold=30000.00,
)
# Single thresold
if not frappe.db.exists("Tax Withholding Category", "Single Threshold TDS"):
frappe.get_doc(
{
"doctype": "Tax Withholding Category",
"name": "Single Threshold TDS",
"category_name": "10% TDS",
"rates": [
{
"from_date": fiscal_year[1],
"to_date": fiscal_year[2],
"tax_withholding_rate": 10,
"single_threshold": 20000.00,
"cumulative_threshold": 0,
}
],
"accounts": [{"company": "_Test Company", "account": "TDS - _TC"}],
}
).insert()
# Single threshold
create_tax_withholding_category(
category_name="Single Threshold TDS",
rate=10,
from_date=from_date,
to_date=to_date,
account="TDS - _TC",
single_threshold=20000,
cumulative_threshold=0,
)
if not frappe.db.exists("Tax Withholding Category", "New TDS Category"):
frappe.get_doc(
{
"doctype": "Tax Withholding Category",
"name": "New TDS Category",
"category_name": "New TDS Category",
"round_off_tax_amount": 1,
"consider_party_ledger_amount": 1,
"tax_on_excess_amount": 1,
"rates": [
{
"from_date": fiscal_year[1],
"to_date": fiscal_year[2],
"tax_withholding_rate": 10,
"single_threshold": 0,
"cumulative_threshold": 30000,
}
],
"accounts": [{"company": "_Test Company", "account": "TDS - _TC"}],
}
).insert()
create_tax_withholding_category(
category_name="New TDS Category",
rate=10,
from_date=from_date,
to_date=to_date,
account="TDS - _TC",
single_threshold=0,
cumulative_threshold=30000,
round_off_tax_amount=1,
consider_party_ledger_amount=1,
tax_on_excess_amount=1,
)
if not frappe.db.exists("Tax Withholding Category", "Test Service Category"):
frappe.get_doc(
{
"doctype": "Tax Withholding Category",
"name": "Test Service Category",
"category_name": "Test Service Category",
"rates": [
{
"from_date": fiscal_year[1],
"to_date": fiscal_year[2],
"tax_withholding_rate": 10,
"single_threshold": 2000,
"cumulative_threshold": 2000,
}
],
"accounts": [{"company": "_Test Company", "account": "TDS - _TC"}],
}
).insert()
create_tax_withholding_category(
category_name="Test Service Category",
rate=10,
from_date=from_date,
to_date=to_date,
account="TDS - _TC",
single_threshold=2000,
cumulative_threshold=2000,
)
if not frappe.db.exists("Tax Withholding Category", "Test Goods Category"):
frappe.get_doc(
{
"doctype": "Tax Withholding Category",
"name": "Test Goods Category",
"category_name": "Test Goods Category",
"rates": [
{
"from_date": fiscal_year[1],
"to_date": fiscal_year[2],
"tax_withholding_rate": 10,
"single_threshold": 2000,
"cumulative_threshold": 2000,
}
],
"accounts": [{"company": "_Test Company", "account": "TDS - _TC"}],
}
).insert()
create_tax_withholding_category(
category_name="Test Goods Category",
rate=10,
from_date=from_date,
to_date=to_date,
account="TDS - _TC",
single_threshold=2000,
cumulative_threshold=2000,
)
if not frappe.db.exists("Tax Withholding Category", "Test Multi Invoice Category"):
create_tax_withholding_category(
category_name="Test Multi Invoice Category",
rate=10,
from_date=from_date,
to_date=to_date,
account="TDS - _TC",
single_threshold=5000,
cumulative_threshold=10000,
)
create_tax_withholding_category(
category_name="Advance TDS Category",
rate=10,
from_date=from_date,
to_date=to_date,
account="TDS - _TC",
single_threshold=5000,
cumulative_threshold=10000,
consider_party_ledger_amount=1,
)
def create_tax_withholding_category(
category_name,
rate,
from_date,
to_date,
account,
single_threshold=0,
cumulative_threshold=0,
round_off_tax_amount=0,
consider_party_ledger_amount=0,
tax_on_excess_amount=0,
):
if not frappe.db.exists("Tax Withholding Category", category_name):
frappe.get_doc(
{
"doctype": "Tax Withholding Category",
"name": "Test Multi Invoice Category",
"category_name": "Test Multi Invoice Category",
"name": category_name,
"category_name": category_name,
"round_off_tax_amount": round_off_tax_amount,
"consider_party_ledger_amount": consider_party_ledger_amount,
"tax_on_excess_amount": tax_on_excess_amount,
"rates": [
{
"from_date": fiscal_year[1],
"to_date": fiscal_year[2],
"tax_withholding_rate": 10,
"single_threshold": 5000,
"cumulative_threshold": 10000,
"from_date": from_date,
"to_date": to_date,
"tax_withholding_rate": rate,
"single_threshold": single_threshold,
"cumulative_threshold": cumulative_threshold,
}
],
"accounts": [{"company": "_Test Company", "account": "TDS - _TC"}],
"accounts": [{"company": "_Test Company", "account": account}],
}
).insert()

View File

@@ -199,7 +199,14 @@ def merge_similar_entries(gl_map, precision=None):
# filter zero debit and credit entries
merged_gl_map = filter(
lambda x: flt(x.debit, precision) != 0 or flt(x.credit, precision) != 0, merged_gl_map
lambda x: flt(x.debit, precision) != 0
or flt(x.credit, precision) != 0
or (
x.voucher_type == "Journal Entry"
and frappe.get_cached_value("Journal Entry", x.voucher_no, "voucher_type")
== "Exchange Gain Or Loss"
),
merged_gl_map,
)
merged_gl_map = list(merged_gl_map)
@@ -293,6 +300,9 @@ def save_entries(gl_map, adv_adj, update_outstanding, from_repost=False):
if gl_map:
check_freezing_date(gl_map[0]["posting_date"], adv_adj)
is_opening = any(d.get("is_opening") == "Yes" for d in gl_map)
if gl_map[0]["voucher_type"] != "Period Closing Voucher":
validate_against_pcv(is_opening, gl_map[0]["posting_date"], gl_map[0]["company"])
for entry in gl_map:
make_entry(entry, adv_adj, update_outstanding, from_repost)
@@ -350,15 +360,26 @@ def process_debit_credit_difference(gl_map):
allowance = get_debit_credit_allowance(voucher_type, precision)
debit_credit_diff = get_debit_credit_difference(gl_map, precision)
if abs(debit_credit_diff) > allowance:
raise_debit_credit_not_equal_error(debit_credit_diff, voucher_type, voucher_no)
if not (
voucher_type == "Journal Entry"
and frappe.get_cached_value("Journal Entry", voucher_no, "voucher_type")
== "Exchange Gain Or Loss"
):
raise_debit_credit_not_equal_error(debit_credit_diff, voucher_type, voucher_no)
elif abs(debit_credit_diff) >= (1.0 / (10**precision)):
make_round_off_gle(gl_map, debit_credit_diff, precision)
debit_credit_diff = get_debit_credit_difference(gl_map, precision)
if abs(debit_credit_diff) > allowance:
raise_debit_credit_not_equal_error(debit_credit_diff, voucher_type, voucher_no)
if not (
voucher_type == "Journal Entry"
and frappe.get_cached_value("Journal Entry", voucher_no, "voucher_type")
== "Exchange Gain Or Loss"
):
raise_debit_credit_not_equal_error(debit_credit_diff, voucher_type, voucher_no)
def get_debit_credit_difference(gl_map, precision):
@@ -501,6 +522,9 @@ def make_reverse_gl_entries(
)
validate_accounting_period(gl_entries)
check_freezing_date(gl_entries[0]["posting_date"], adv_adj)
is_opening = any(d.get("is_opening") == "Yes" for d in gl_entries)
validate_against_pcv(is_opening, gl_entries[0]["posting_date"], gl_entries[0]["company"])
set_as_cancel(gl_entries[0]["voucher_type"], gl_entries[0]["voucher_no"])
for entry in gl_entries:
@@ -548,6 +572,28 @@ def check_freezing_date(posting_date, adv_adj=False):
)
def validate_against_pcv(is_opening, posting_date, company):
if is_opening and frappe.db.exists(
"Period Closing Voucher", {"docstatus": 1, "company": company}
):
frappe.throw(
_("Opening Entry can not be created after Period Closing Voucher is created."),
title=_("Invalid Opening Entry"),
)
last_pcv_date = frappe.db.get_value(
"Period Closing Voucher", {"docstatus": 1, "company": company}, "max(posting_date)"
)
if last_pcv_date and getdate(posting_date) <= getdate(last_pcv_date):
message = _("Books have been closed till the period ending on {0}").format(
formatdate(last_pcv_date)
)
message += "</br >"
message += _("You cannot create/amend any accounting entries till this date.")
frappe.throw(message, title=_("Period Closed"))
def set_as_cancel(voucher_type, voucher_no):
"""
Set is_cancelled=1 in all original gl entries for the voucher

View File

@@ -32,6 +32,16 @@ from erpnext import get_company_currency
from erpnext.accounts.utils import get_fiscal_year
from erpnext.exceptions import InvalidAccountCurrency, PartyDisabled, PartyFrozen
PURCHASE_TRANSACTION_TYPES = {"Purchase Order", "Purchase Receipt", "Purchase Invoice"}
SALES_TRANSACTION_TYPES = {
"Quotation",
"Sales Order",
"Delivery Note",
"Sales Invoice",
"POS Invoice",
}
TRANSACTION_TYPES = PURCHASE_TRANSACTION_TYPES | SALES_TRANSACTION_TYPES
class DuplicatePartyAccountError(frappe.ValidationError):
pass
@@ -124,12 +134,6 @@ def _get_party_details(
set_other_values(party_details, party, party_type)
set_price_list(party_details, party, party_type, price_list, pos_profile)
party_details["tax_category"] = get_address_tax_category(
party.get("tax_category"),
party_address,
shipping_address if party_type != "Supplier" else party_address,
)
tax_template = set_taxes(
party.name,
party_type,
@@ -170,6 +174,9 @@ def _get_party_details(
party_type, party.name, "tax_withholding_category"
)
if not party_details.get("tax_category") and pos_profile:
party_details["tax_category"] = frappe.get_value("POS Profile", pos_profile, "tax_category")
return party_details
@@ -211,14 +218,10 @@ def set_address_details(
else:
party_details.update(get_company_address(company))
if doctype and doctype in ["Delivery Note", "Sales Invoice", "Sales Order", "Quotation"]:
if party_details.company_address:
party_details.update(
get_fetch_values(doctype, "company_address", party_details.company_address)
)
get_regional_address_details(party_details, doctype, company)
if doctype in SALES_TRANSACTION_TYPES and party_details.company_address:
party_details.update(get_fetch_values(doctype, "company_address", party_details.company_address))
elif doctype and doctype in ["Purchase Invoice", "Purchase Order", "Purchase Receipt"]:
if doctype in PURCHASE_TRANSACTION_TYPES:
if shipping_address:
party_details.update(
shipping_address=shipping_address,
@@ -244,9 +247,21 @@ def set_address_details(
**get_fetch_values(doctype, "shipping_address", party_details.billing_address)
)
party_address, shipping_address = (
party_details.get(billing_address_field),
party_details.shipping_address_name,
)
party_details["tax_category"] = get_address_tax_category(
party.get("tax_category"),
party_address,
shipping_address if party_type != "Supplier" else party_address,
)
if doctype in TRANSACTION_TYPES:
get_regional_address_details(party_details, doctype, company)
return party_details.get(billing_address_field), party_details.shipping_address_name
return party_address, shipping_address
@erpnext.allow_regional
@@ -544,7 +559,7 @@ def get_due_date_from_template(template_name, posting_date, bill_date):
elif term.due_date_based_on == "Day(s) after the end of the invoice month":
due_date = max(due_date, add_days(get_last_day(due_date), term.credit_days))
else:
due_date = max(due_date, add_months(get_last_day(due_date), term.credit_months))
due_date = max(due_date, get_last_day(add_months(due_date, term.credit_months)))
return due_date

View File

@@ -99,6 +99,9 @@ class ReceivablePayableReport(object):
# Get return entries
self.get_return_entries()
# Get Exchange Rate Revaluations
self.get_exchange_rate_revaluations()
self.data = []
for ple in self.ple_entries:
@@ -251,7 +254,8 @@ class ReceivablePayableReport(object):
row.invoice_grand_total = row.invoiced
if (abs(row.outstanding) > 1.0 / 10**self.currency_precision) and (
abs(row.outstanding_in_account_currency) > 1.0 / 10**self.currency_precision
(abs(row.outstanding_in_account_currency) > 1.0 / 10**self.currency_precision)
or (row.voucher_no in self.err_journals)
):
# non-zero oustanding, we must consider this row
@@ -794,19 +798,19 @@ class ReceivablePayableReport(object):
if self.filters.get("payment_terms_template"):
self.qb_selection_filter.append(
self.ple.party_isin(
qb.from_(self.customer).where(
self.customer.payment_terms == self.filters.get("payment_terms_template")
)
self.ple.party.isin(
qb.from_(self.customer)
.select(self.customer.name)
.where(self.customer.payment_terms == self.filters.get("payment_terms_template"))
)
)
if self.filters.get("sales_partner"):
self.qb_selection_filter.append(
self.ple.party_isin(
qb.from_(self.customer).where(
self.customer.default_sales_partner == self.filters.get("payment_terms_template")
)
self.ple.party.isin(
qb.from_(self.customer)
.select(self.customer.name)
.where(self.customer.default_sales_partner == self.filters.get("sales_partner"))
)
)
@@ -855,7 +859,7 @@ class ReceivablePayableReport(object):
)
else:
self.qb_selection_filter.append(
self.ple[dimension.fieldname] == self.filters[dimension.fieldname]
self.ple[dimension.fieldname].isin(self.filters[dimension.fieldname])
)
def is_invoice(self, ple):
@@ -865,10 +869,15 @@ class ReceivablePayableReport(object):
def get_party_details(self, party):
if not party in self.party_details:
if self.party_type == "Customer":
fields = ["customer_name", "territory", "customer_group", "customer_primary_contact"]
if self.filters.get("sales_partner"):
fields.append("default_sales_partner")
self.party_details[party] = frappe.db.get_value(
"Customer",
party,
["customer_name", "territory", "customer_group", "customer_primary_contact"],
fields,
as_dict=True,
)
else:
@@ -969,6 +978,9 @@ class ReceivablePayableReport(object):
if self.filters.show_sales_person:
self.add_column(label=_("Sales Person"), fieldname="sales_person", fieldtype="Data")
if self.filters.sales_partner:
self.add_column(label=_("Sales Partner"), fieldname="default_sales_partner", fieldtype="Data")
if self.filters.party_type == "Supplier":
self.add_column(
label=_("Supplier Group"),
@@ -1028,3 +1040,17 @@ class ReceivablePayableReport(object):
"data": {"labels": self.ageing_column_labels, "datasets": rows},
"type": "percentage",
}
def get_exchange_rate_revaluations(self):
je = qb.DocType("Journal Entry")
results = (
qb.from_(je)
.select(je.name)
.where(
(je.company == self.filters.company)
& (je.posting_date.lte(self.filters.report_date))
& (je.voucher_type == "Exchange Rate Revaluation")
)
.run()
)
self.err_journals = [x[0] for x in results] if results else []

View File

@@ -1,9 +1,10 @@
import unittest
import frappe
from frappe.tests.utils import FrappeTestCase
from frappe.utils import add_days, getdate, today
from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import add_days, flt, getdate, today
from erpnext import get_default_cost_center
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.report.accounts_receivable.accounts_receivable import execute
@@ -17,10 +18,37 @@ class TestAccountsReceivable(FrappeTestCase):
frappe.db.sql("delete from `tabPayment Entry` where company='_Test Company 2'")
frappe.db.sql("delete from `tabGL Entry` where company='_Test Company 2'")
frappe.db.sql("delete from `tabPayment Ledger Entry` where company='_Test Company 2'")
frappe.db.sql("delete from `tabJournal Entry` where company='_Test Company 2'")
frappe.db.sql("delete from `tabExchange Rate Revaluation` where company='_Test Company 2'")
self.create_usd_account()
def tearDown(self):
frappe.db.rollback()
def create_usd_account(self):
name = "Debtors USD"
exists = frappe.db.get_list(
"Account", filters={"company": "_Test Company 2", "account_name": "Debtors USD"}
)
if exists:
self.debtors_usd = exists[0].name
else:
debtors = frappe.get_doc(
"Account",
frappe.db.get_list(
"Account", filters={"company": "_Test Company 2", "account_name": "Debtors"}
)[0].name,
)
debtors_usd = frappe.new_doc("Account")
debtors_usd.company = debtors.company
debtors_usd.account_name = "Debtors USD"
debtors_usd.account_currency = "USD"
debtors_usd.parent_account = debtors.parent_account
debtors_usd.account_type = debtors.account_type
self.debtors_usd = debtors_usd.save().name
def test_accounts_receivable(self):
filters = {
"company": "_Test Company 2",
@@ -33,7 +61,7 @@ class TestAccountsReceivable(FrappeTestCase):
}
# check invoice grand total and invoiced column's value for 3 payment terms
name = make_sales_invoice()
name = make_sales_invoice().name
report = execute(filters)
expected_data = [[100, 30], [100, 50], [100, 20]]
@@ -118,8 +146,72 @@ class TestAccountsReceivable(FrappeTestCase):
],
)
@change_settings(
"Accounts Settings", {"allow_multi_currency_invoices_against_single_party_account": 1}
)
def test_exchange_revaluation_for_party(self):
"""
Exchange Revaluation for party on Receivable/Payable shoule be included
"""
def make_sales_invoice():
company = "_Test Company 2"
customer = "_Test Customer 2"
# Using Exchange Gain/Loss account for unrealized as well.
company_doc = frappe.get_doc("Company", company)
company_doc.unrealized_exchange_gain_loss_account = company_doc.exchange_gain_loss_account
company_doc.save()
si = make_sales_invoice(no_payment_schedule=True, do_not_submit=True)
si.currency = "USD"
si.conversion_rate = 0.90
si.debit_to = self.debtors_usd
si = si.save().submit()
# Exchange Revaluation
err = frappe.new_doc("Exchange Rate Revaluation")
err.company = company
err.posting_date = today()
accounts = err.get_accounts_data()
err.extend("accounts", accounts)
err.accounts[0].new_exchange_rate = 0.95
row = err.accounts[0]
row.new_balance_in_base_currency = flt(
row.new_exchange_rate * flt(row.balance_in_account_currency)
)
row.gain_loss = row.new_balance_in_base_currency - flt(row.balance_in_base_currency)
err.set_total_gain_loss()
err = err.save().submit()
# Submit JV for ERR
err_journals = err.make_jv_entries()
je = frappe.get_doc("Journal Entry", err_journals.get("revaluation_jv"))
je = je.submit()
filters = {
"company": company,
"report_date": today(),
"range1": 30,
"range2": 60,
"range3": 90,
"range4": 120,
}
report = execute(filters)
expected_data_for_err = [0, -5, 0, 5]
row = [x for x in report[1] if x.voucher_type == je.doctype and x.voucher_no == je.name][0]
self.assertEqual(
expected_data_for_err,
[
row.invoiced,
row.paid,
row.credit_note,
row.outstanding,
],
)
def make_sales_invoice(no_payment_schedule=False, do_not_submit=False):
frappe.set_user("Administrator")
si = create_sales_invoice(
@@ -134,22 +226,26 @@ def make_sales_invoice():
do_not_save=1,
)
si.append(
"payment_schedule",
dict(due_date=getdate(add_days(today(), 30)), invoice_portion=30.00, payment_amount=30),
)
si.append(
"payment_schedule",
dict(due_date=getdate(add_days(today(), 60)), invoice_portion=50.00, payment_amount=50),
)
si.append(
"payment_schedule",
dict(due_date=getdate(add_days(today(), 90)), invoice_portion=20.00, payment_amount=20),
)
if not no_payment_schedule:
si.append(
"payment_schedule",
dict(due_date=getdate(add_days(today(), 30)), invoice_portion=30.00, payment_amount=30),
)
si.append(
"payment_schedule",
dict(due_date=getdate(add_days(today(), 60)), invoice_portion=50.00, payment_amount=50),
)
si.append(
"payment_schedule",
dict(due_date=getdate(add_days(today(), 90)), invoice_portion=20.00, payment_amount=20),
)
si.submit()
si = si.save()
return si.name
if not do_not_submit:
si = si.submit()
return si
def make_payment(docname):

View File

@@ -121,6 +121,9 @@ class AccountsReceivableSummary(ReceivablePayableReport):
if row.sales_person:
self.party_total[row.party].sales_person.append(row.sales_person)
if self.filters.sales_partner:
self.party_total[row.party]["default_sales_partner"] = row.get("default_sales_partner")
def get_columns(self):
self.columns = []
self.add_column(
@@ -160,6 +163,10 @@ class AccountsReceivableSummary(ReceivablePayableReport):
)
if self.filters.show_sales_person:
self.add_column(label=_("Sales Person"), fieldname="sales_person", fieldtype="Data")
if self.filters.sales_partner:
self.add_column(label=_("Sales Partner"), fieldname="default_sales_partner", fieldtype="Data")
else:
self.add_column(
label=_("Supplier Group"),

View File

@@ -25,6 +25,7 @@ def get_data(filters):
["posting_date", "<=", filters.get("to_date")],
["against_voucher_type", "=", "Asset"],
["account", "in", depreciation_accounts],
["is_cancelled", "=", 0],
]
if filters.get("asset"):

View File

@@ -131,8 +131,36 @@ def get_assets(filters):
else
0
end), 0) as depreciation_amount_during_the_period
from `tabAsset` a, `tabDepreciation Schedule` ds
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and a.name = ds.parent and ifnull(ds.journal_entry, '') != ''
from `tabAsset` a, `tabAsset Depreciation Schedule` ads, `tabDepreciation Schedule` ds
where a.docstatus=1 and a.company=%(company)s and a.purchase_date <= %(to_date)s and ads.asset = a.name and ads.docstatus=1 and ads.name = ds.parent and ifnull(ds.journal_entry, '') != ''
group by a.asset_category
union
SELECT a.asset_category,
ifnull(sum(case when gle.posting_date < %(from_date)s and (ifnull(a.disposal_date, 0) = 0 or a.disposal_date >= %(from_date)s) then
gle.debit
else
0
end), 0) as accumulated_depreciation_as_on_from_date,
ifnull(sum(case when ifnull(a.disposal_date, 0) != 0 and a.disposal_date >= %(from_date)s
and a.disposal_date <= %(to_date)s and gle.posting_date <= a.disposal_date then
gle.debit
else
0
end), 0) as depreciation_eliminated_during_the_period,
ifnull(sum(case when gle.posting_date >= %(from_date)s and gle.posting_date <= %(to_date)s
and (ifnull(a.disposal_date, 0) = 0 or gle.posting_date <= a.disposal_date) then
gle.debit
else
0
end), 0) as depreciation_amount_during_the_period
from `tabGL Entry` gle
join `tabAsset` a on
gle.against_voucher = a.name
join `tabAsset Category Account` aca on
aca.parent = a.asset_category and aca.company_name = %(company)s
join `tabCompany` company on
company.name = %(company)s
where a.docstatus=1 and a.company=%(company)s and a.calculate_depreciation=0 and a.purchase_date <= %(to_date)s and gle.debit != 0 and gle.is_cancelled = 0 and gle.account = ifnull(aca.depreciation_expense_account, company.depreciation_expense_account)
group by a.asset_category
union
SELECT a.asset_category,

View File

@@ -25,6 +25,8 @@ def execute(filters=None):
company=filters.company,
)
filters.period_start_date = period_list[0]["year_start_date"]
currency = filters.presentation_currency or frappe.get_cached_value(
"Company", filters.company, "default_currency"
)
@@ -96,7 +98,7 @@ def execute(filters=None):
chart = get_chart_data(filters, columns, asset, liability, equity)
report_summary = get_report_summary(
period_list, asset, liability, equity, provisional_profit_loss, total_credit, currency, filters
period_list, asset, liability, equity, provisional_profit_loss, currency, filters
)
return columns, data, message, chart, report_summary
@@ -174,7 +176,6 @@ def get_report_summary(
liability,
equity,
provisional_profit_loss,
total_credit,
currency,
filters,
consolidated=False,

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